Skip to content

Commit

Permalink
feat(logging): obfuscate authorization header in logs
Browse files Browse the repository at this point in the history
  • Loading branch information
EdieLemoine committed Dec 17, 2024
1 parent e61fc9d commit 4d2e20d
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 8 deletions.
35 changes: 27 additions & 8 deletions src/Api/Service/AbstractApiService.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,7 @@ public function doRequest(
'body' => $request->getBody(),
];

$logContext = [
'request' => [
'uri' => $uri,
'method' => $method,
'headers' => $options['headers'],
'body' => $options['body'] ? json_decode($options['body'], true) : null,
],
];
$logContext = $this->createLogContext($uri, $method, $options);

try {
$response = $this->clientAdapter->doRequest($method, $uri, $options);
Expand Down Expand Up @@ -128,4 +121,30 @@ protected function buildUri(RequestInterface $request): string

return $url;
}

/**
* @param string $uri
* @param string $method
* @param array $options
*
* @return array[]
*/
private function createLogContext(string $uri, string $method, array $options): array
{
$headers = array_combine(array_map('strtolower', array_keys($options['headers'])), $options['headers']);

// Obfuscate the authorization header if present
if (isset($headers['authorization'])) {
$headers['authorization'] = '***';
}

return [
'request' => [
'uri' => $uri,
'method' => $method,
'headers' => $headers,
'body' => $options['body'] ? json_decode($options['body'], true) : null,
],
];
}
}
51 changes: 51 additions & 0 deletions tests/Unit/Api/Service/AbstractApiServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@
namespace MyParcelNL\Pdk\Api\Service;

use MyParcelNL\Pdk\Account\Repository\ShopRepository;
use MyParcelNL\Pdk\Api\Contract\ApiServiceInterface;
use MyParcelNL\Pdk\Api\Exception\ApiException;
use MyParcelNL\Pdk\Api\Request\Request;
use MyParcelNL\Pdk\Base\Support\Collection;
use MyParcelNL\Pdk\Facade\Pdk;
use MyParcelNL\Pdk\Logger\Contract\PdkLoggerInterface;
use MyParcelNL\Pdk\Shipment\Model\Shipment;
use MyParcelNL\Pdk\Shipment\Repository\ShipmentRepository;
use MyParcelNL\Pdk\Tests\Api\Response\ExampleErrorNotFoundResponse;
use MyParcelNL\Pdk\Tests\Api\Response\ExampleErrorResponse;
use MyParcelNL\Pdk\Tests\Api\Response\ExampleErrorUnprocessableEntityResponse;
use MyParcelNL\Pdk\Tests\Api\Response\ExampleGetShipmentsResponse;
use MyParcelNL\Pdk\Tests\Bootstrap\MockApi;
use MyParcelNL\Pdk\Tests\Bootstrap\TestBootstrapper;
use MyParcelNL\Pdk\Tests\Mocks\MockApiResponse;
use MyParcelNL\Pdk\Tests\Uses\UsesMockPdkInstance;
use MyParcelNL\Sdk\src\Support\Arr;
use Psr\Log\LogLevel;
use function MyParcelNL\Pdk\Tests\usesShared;

usesShared(new UsesMockPdkInstance());
Expand Down Expand Up @@ -49,3 +56,47 @@
->and($shipments->first())
->toBeInstanceOf(Shipment::class);
});

it('creates log context with obfuscated authorization header', function () {
TestBootstrapper::hasApiKey();

MockApi::enqueue(new ExampleGetShipmentsResponse());

/** @var \MyParcelNL\Pdk\Tests\Bootstrap\MockLogger $logger */
$logger = Pdk::get(PdkLoggerInterface::class);

/** @var \MyParcelNL\Pdk\Api\Contract\ApiServiceInterface $apiService */
$apiService = Pdk::get(ApiServiceInterface::class);

$request = new Request([
'headers' => [
'Authorization' => 'bearer this-is-some-fake-value',
'Content-Type' => 'application/json',
],
'uri' => 'test',
'method' => 'POST',
'parameters' => [],
'body' => json_encode(['test' => 'test']),
]);

$apiService->doRequest($request, MockApiResponse::class);

$lastLog = Arr::last($logger->getLogs());

expect($lastLog['level'])
->toBe(LogLevel::DEBUG)
->and($lastLog['message'])
->toBe('[PDK]: Successfully sent request')
->and(array_keys($lastLog['context']))
->toEqual(['request', 'response'])
->and(array_keys($lastLog['context']['request']))
->toEqual(['uri', 'method', 'headers', 'body'])
->and(array_keys($lastLog['context']['response']))
->toEqual(['code', 'body'])
// Expect header keys to be normalized and authorization header to be hidden
->and($lastLog['context']['request']['headers'])
->toBe([
'authorization' => '***',
'content-type' => 'application/json',
]);
});

0 comments on commit 4d2e20d

Please sign in to comment.