Skip to content
Commits on Source (8)
......@@ -17,4 +17,25 @@ dbp_relay_core:
docs_title: 'Relay API Gateway'
# The description text of the API docs page (supports markdown)
docs_description: '*part of the [Digital Blueprint](https://gitlab.tugraz.at/dbp) project*'
```
\ No newline at end of file
```
## Configuration
To handle locking you need to set an environment variable `LOCK_DSN` in your `.env` file or by any other means.
The usual way would be to use [Redis](https://redis.io/) for locking.
Example:
```dotenv
LOCK_DSN=redis://redis:6379/
```
For projects that also use the [Symfony Messenger](https://symfony.com/doc/current/components/messenger.html) you also
need to set an environment variable `MESSENGER_TRANSPORT_DSN` in your `.env` file or by any other means.
[Redis](https://redis.io/) is also the best way for this.
Example:
```dotenv
MESSENGER_TRANSPORT_DSN=redis://redis:6379/local-messages/symfony/consumer?auto_setup=true&serializer=1&stream_max_entries=0&dbindex=0
```
This diff is collapsed.
......@@ -2,12 +2,12 @@
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Service;
namespace Dbp\Relay\CoreBundle\Logging;
use Dbp\Relay\CoreBundle\API\UserSessionInterface;
use Dbp\Relay\CoreBundle\Helpers\Tools;
use Dbp\Relay\CoreBundle\Helpers\Tools as CoreTools;
class LoggingProcessor
final class LoggingProcessor
{
private $userDataProvider;
......@@ -16,10 +16,27 @@ class LoggingProcessor
$this->userDataProvider = $userDataProvider;
}
private function maskUserId(array &$record)
{
try {
$userId = $this->userDataProvider->getUserIdentifier();
} catch (\Throwable $error) {
// pre-auth
$userId = null;
}
if ($userId !== null) {
Tools::maskValues($record, [$userId], '*****');
}
}
public function __invoke(array $record)
{
// Try to avoid information leaks (users should still not log sensitive information though...)
$record['message'] = Tools::filterErrorMessage($record['message']);
$record['message'] = CoreTools::filterErrorMessage($record['message']);
// Mask the user identifier
$this->maskUserId($record);
// Add some default context (session ID etc)
$loggingId = $this->userDataProvider->getSessionLoggingId();
......
<?php
declare(strict_types=1);
namespace Dbp\Relay\CoreBundle\Logging;
final class Tools
{
/**
* Try to mask a list of values in a monolog logging record.
*
* @param array $record The monolog record to mask
* @param array $values The values to mask
* @param string $replacement The replacement string used for masking
*/
public static function maskValues(array &$record, array $values, string $replacement)
{
$maskValues = function (string $input) use ($values, $replacement): string {
$output = $input;
foreach ($values as $value) {
// Don't mask values contained in words, otherwise if the outer masked word is known the
// value could be derived.
$output = preg_replace(
'/(^|[^\w])('.preg_quote($value).')($|[^\w])/',
'$1'.$replacement.'$3',
$output);
}
return $output;
};
if (isset($record['message'])) {
$record['message'] = $maskValues($record['message']);
}
foreach ($record['extra'] ?? [] as $key => $value) {
if (is_string($value)) {
$record['extra'][$key] = $maskValues($value);
}
}
foreach ($record['context'] ?? [] as $key => $value) {
if (is_string($value)) {
$record['context'][$key] = $maskValues($value);
}
}
}
}
......@@ -4,7 +4,7 @@ services:
autoconfigure: false
tags: [{'name' : 'serializer.normalizer', priority: '-700'}]
Dbp\Relay\CoreBundle\Service\LoggingProcessor:
Dbp\Relay\CoreBundle\Logging\LoggingProcessor:
autowire: true
autoconfigure: true
tags:
......
......@@ -2,7 +2,9 @@
declare(strict_types=1);
use Dbp\Relay\CoreBundle\Service\LoggingProcessor;
namespace Dbp\Relay\CoreBundle\Tests\Logging;
use Dbp\Relay\CoreBundle\Logging\LoggingProcessor;
use Dbp\Relay\CoreBundle\TestUtils\TestUserSession;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
......@@ -10,10 +12,32 @@ class LoggingProcessorTest extends WebTestCase
{
public function testFilter()
{
$processor = new LoggingProcessor(new TestUserSession());
$processor = new LoggingProcessor(new TestUserSession('some-random-user-id'));
$record = ['message' => 'http://foo.bar?token=secret'];
$record = $processor->__invoke($record);
$this->assertSame(['message' => 'http://foo.bar?token=hidden', 'context' => ['dbp-id' => 'logging-id']], $record);
}
public function testMaskUserId()
{
$processor = new LoggingProcessor(new TestUserSession('some-random-user-id'));
$record = [
'message' => 'hello some-random-user-id!',
'extra' => ['foo' => 'some-random-user-id'],
'context' => ['foo' => 'some-random-user-id'],
];
$record = $processor->__invoke($record);
$this->assertSame([
'message' => 'hello *****!',
'extra' => ['foo' => '*****'],
'context' => ['foo' => '*****', 'dbp-id' => 'logging-id'], ], $record);
// Don't mask when contained in a word
$processor = new LoggingProcessor(new TestUserSession('log'));
$record = ['message' => 'logging log'];
$record = $processor->__invoke($record);
$this->assertSame(['message' => 'logging *****', 'context' => ['dbp-id' => 'logging-id']], $record);
}
}