feat: proper logging to dedicated audit channel
This commit is contained in:
@@ -2,7 +2,7 @@ monolog:
|
||||
channels:
|
||||
- deprecation
|
||||
- bpn
|
||||
- myep
|
||||
- audit
|
||||
|
||||
handlers:
|
||||
main:
|
||||
@@ -11,7 +11,7 @@ monolog:
|
||||
handler: exceptions
|
||||
excluded_http_codes: [404, 405]
|
||||
buffer_size: 50
|
||||
channels: ["!bpn", "!myep"]
|
||||
channels: ["!bpn", "!audit"]
|
||||
exceptions:
|
||||
type: rotating_file
|
||||
path: "%kernel.logs_dir%/framework.%kernel.environment%.log"
|
||||
@@ -23,14 +23,14 @@ monolog:
|
||||
path: '%kernel.logs_dir%/bpn.%kernel.environment%.log'
|
||||
max_files: 5
|
||||
level: info
|
||||
myep:
|
||||
channels: [ "myep" ]
|
||||
audit:
|
||||
channels: [ "audit" ]
|
||||
type: rotating_file
|
||||
path: '%kernel.logs_dir%/myep.%kernel.environment%.log'
|
||||
path: '%kernel.logs_dir%/audit.%kernel.environment%.log'
|
||||
max_files: 5
|
||||
level: info
|
||||
database:
|
||||
channels: ["myep"]
|
||||
channels: ["audit"]
|
||||
type: service
|
||||
id: App\Logging\DatabaseHandler
|
||||
console:
|
||||
@@ -38,6 +38,18 @@ monolog:
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!bpn", "!doctrine", "!console"]
|
||||
|
||||
when@dev:
|
||||
monolog:
|
||||
handlers:
|
||||
# Single catch-all file so everything is visible in one place during
|
||||
# development. The handlers above are error-level or channel-bound,
|
||||
# which makes ordinary app output invisible locally.
|
||||
dev:
|
||||
type: stream
|
||||
path: '%kernel.logs_dir%/%kernel.environment%.log'
|
||||
level: debug
|
||||
channels: ["!event", "!doctrine"]
|
||||
|
||||
when@test:
|
||||
monolog:
|
||||
handlers:
|
||||
@@ -61,7 +73,7 @@ when@prod:
|
||||
handler: exceptions
|
||||
excluded_http_codes: [404, 405]
|
||||
buffer_size: 50
|
||||
channels: ["!bpn", "!myep"]
|
||||
channels: ["!bpn", "!audit"]
|
||||
exceptions:
|
||||
type: rotating_file
|
||||
path: "%kernel.logs_dir%/framework.%kernel.environment%.log"
|
||||
@@ -73,17 +85,17 @@ when@prod:
|
||||
path: '%kernel.logs_dir%/bpn.%kernel.environment%.log'
|
||||
max_files: 5
|
||||
level: info
|
||||
myep:
|
||||
channels: [ "myep" ]
|
||||
audit:
|
||||
channels: [ "audit" ]
|
||||
type: rotating_file
|
||||
path: '%kernel.logs_dir%/myep.%kernel.environment%.log'
|
||||
path: '%kernel.logs_dir%/audit.%kernel.environment%.log'
|
||||
max_files: 5
|
||||
level: info
|
||||
database:
|
||||
channels: ["myep"]
|
||||
channels: ["audit"]
|
||||
type: service
|
||||
id: App\Logging\DatabaseHandler
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine", "!bpn", "!myep"]
|
||||
channels: ["!event", "!doctrine", "!bpn", "!audit"]
|
||||
|
||||
@@ -43,7 +43,10 @@ services:
|
||||
autoconfigure: true
|
||||
bind:
|
||||
$tempDir: '%kernel.project_dir%/temp'
|
||||
$logger: '@monolog.logger.myep'
|
||||
# Application audit channel, not a MyE&P one: this is what feeds the
|
||||
# log table via App\Logging\DatabaseHandler. Unbinding a service from
|
||||
# it drops its records from that table.
|
||||
$logger: '@monolog.logger.audit'
|
||||
$houses: '%houses%'
|
||||
$xmlExport: '@xml_export.storage'
|
||||
$xmlDump: '@xml_dump.storage'
|
||||
|
||||
@@ -49,6 +49,9 @@ class MyEpAuthenticator extends AbstractAuthenticator
|
||||
try {
|
||||
$accessToken = $this->client->fetchAccessToken($request);
|
||||
} catch (AuthorizationRequestException|IdentityProviderException $e) {
|
||||
$this->logger->error('Login via MyE&P failed due to unobtainable access token', [
|
||||
'exception' => $e,
|
||||
]);
|
||||
throw new CustomUserMessageAuthenticationException('Invalid token');
|
||||
}
|
||||
|
||||
@@ -110,11 +113,21 @@ class MyEpAuthenticator extends AbstractAuthenticator
|
||||
{
|
||||
// User is expected to have at least one role
|
||||
if (false === isset($userinfo['roles']) || 0 === count($userinfo['roles'])) {
|
||||
// Claim keys only, the payload itself carries the full profile
|
||||
$this->logger->warning('Login via MyE&P failed due to missing roles claim', [
|
||||
'claims' => array_keys($userinfo),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// User is expected to have at least one of the roles teamer, manager, house manager or admin
|
||||
if ([] === array_intersect(self::ELIGIBLE_ROLES, $userinfo['roles'])) {
|
||||
$this->logger->warning('Login via MyE&P failed due to lack of an eligible role', [
|
||||
'roles' => $userinfo['roles'],
|
||||
'eligible_roles' => self::ELIGIBLE_ROLES,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use League\OAuth2\Client\Provider\AbstractProvider;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use League\OAuth2\Client\Provider\GenericProvider;
|
||||
use League\OAuth2\Client\Token\AccessTokenInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
@@ -14,8 +15,11 @@ class MyEpClient
|
||||
{
|
||||
private array $config;
|
||||
|
||||
public function __construct(private readonly UrlGeneratorInterface $urlGenerator, array $options)
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UrlGeneratorInterface $urlGenerator,
|
||||
private readonly LoggerInterface $logger,
|
||||
array $options,
|
||||
) {
|
||||
$this->config = $this->resolveConfig($options);
|
||||
}
|
||||
|
||||
@@ -26,6 +30,7 @@ class MyEpClient
|
||||
public function fetchAccessToken(Request $request): AccessTokenInterface
|
||||
{
|
||||
if (null === $code = $request->query->get('code')) {
|
||||
$this->logger->error('OAuth2 login request missing code');
|
||||
throw new AuthorizationRequestException('Missing code', 400, $request);
|
||||
}
|
||||
|
||||
@@ -36,6 +41,7 @@ class MyEpClient
|
||||
|| $request->query->get('state') !== $session->get('oauth2state')
|
||||
) {
|
||||
$session->remove('oauth2state');
|
||||
$this->logger->error('OAuth2 login request missing state or mismatch');
|
||||
throw new AuthorizationRequestException('Missing state or mismatch', 400, $request);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user