From f007dcf6d8c1767064a49b750537745d217277e3 Mon Sep 17 00:00:00 2001 From: William Petit Date: Thu, 6 Mar 2025 16:01:46 +0100 Subject: [PATCH] feat: add authentication success/failure log outputs see https://forge.cadoles.com/CNOUS/mse/issues/4707 --- composer.json | 1 + composer.lock | 269 ++++++++++++++++++++- config/bundles.php | 1 + config/packages/monolog.yaml | 6 + config/services.yaml | 3 + docker-compose.yml | 5 +- src/Security/SQLLoginUserAuthenticator.php | 17 +- symfony.lock | 12 + 8 files changed, 307 insertions(+), 7 deletions(-) create mode 100644 config/packages/monolog.yaml diff --git a/composer.json b/composer.json index 107e546..95a7059 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "symfony/form": "5.4.*", "symfony/framework-bundle": "5.4.*", "symfony/http-client": "5.4.*", + "symfony/monolog-bundle": "^3.10", "symfony/rate-limiter": "5.4.*", "symfony/runtime": "5.4.*", "symfony/security-bundle": "5.4.*", diff --git a/composer.lock b/composer.lock index 50ea1e2..e682eae 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "203398b3a4f3ff689ff3341c02460f23", + "content-hash": "9daf21762ed80ef11e74a53f5d27119f", "packages": [ { "name": "clue/stream-filter", @@ -589,6 +589,108 @@ }, "time": "2024-03-08T09:58:59+00:00" }, + { + "name": "monolog/monolog", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.38 || ^9.6.19", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-11-12T12:43:37+00:00" + }, { "name": "php-http/client-common", "version": "2.7.2", @@ -3566,6 +3668,171 @@ ], "time": "2024-05-31T14:33:22+00:00" }, + { + "name": "symfony/monolog-bridge", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bridge.git", + "reference": "cf7d75d4d64a41fbb1c0e92301bec404134fa84b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/cf7d75d4d64a41fbb1c0e92301bec404134fa84b", + "reference": "cf7d75d4d64a41fbb1c0e92301bec404134fa84b", + "shasum": "" + }, + "require": { + "monolog/monolog": "^1.25.1|^2", + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-kernel": "^5.3|^6.0", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3" + }, + "conflict": { + "symfony/console": "<4.4", + "symfony/http-foundation": "<5.3" + }, + "require-dev": { + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/mailer": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0", + "symfony/security-core": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", + "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", + "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Monolog\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Monolog with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/monolog-bridge/tree/v5.4.45" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-10T06:37:45+00:00" + }, + { + "name": "symfony/monolog-bundle", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "shasum": "" + }, + "require": { + "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", + "php": ">=7.2.5", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony MonologBundle", + "homepage": "https://symfony.com", + "keywords": [ + "log", + "logging" + ], + "support": { + "issues": "https://github.com/symfony/monolog-bundle/issues", + "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-06T17:08:13+00:00" + }, { "name": "symfony/options-resolver", "version": "v5.4.40", diff --git a/config/bundles.php b/config/bundles.php index 01d71ee..6feef2d 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -8,4 +8,5 @@ return [ Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], Sentry\SentryBundle\SentryBundle::class => ['all' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], + Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], ]; diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml new file mode 100644 index 0000000..ddf803d --- /dev/null +++ b/config/packages/monolog.yaml @@ -0,0 +1,6 @@ +monolog: + handlers: + stdout: + type: stream + path: "php://stdout" + level: "%env(string:LOGGER_LEVEL)%" diff --git a/config/services.yaml b/config/services.yaml index 30bc817..7d0cf5d 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -29,6 +29,9 @@ parameters: env(PEPPER): ~ pepper: '%env(resolve:PEPPER)%' + + env(LOGGER_LEVEL): "info" + services: # default configuration for services in *this* file _defaults: diff --git a/docker-compose.yml b/docker-compose.yml index 59f5c34..209c42c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: ports: - 8082:8071 volumes: - - .:/app + - .:/app tmpfs: - /var/www/var/logs:uid=${FIXUID:-1000},gid=${FIXGID:-1000} - /var/www/var/cache:uid=${FIXUID:-1000},gid=${FIXGID:-1000} @@ -40,6 +40,9 @@ services: - HASH_ALGO_LEGACY="sha256" - SECURITY_PATTERN=password,salt,pepper - CADDY_HTTP_PORT=8071 + - LOGGER_LEVEL=info + - PHP_FPM_DISPLAY_ERRORS=on + - PHP_FPM_CATCH_WORKERS_OUTPUT=1 oidc-test: image: bornholm/oidc-test:v0.0.0-1-g936a77e diff --git a/src/Security/SQLLoginUserAuthenticator.php b/src/Security/SQLLoginUserAuthenticator.php index 71848b2..46b9ccd 100644 --- a/src/Security/SQLLoginUserAuthenticator.php +++ b/src/Security/SQLLoginUserAuthenticator.php @@ -11,6 +11,7 @@ use App\SQLLogin\Exception\InvalidSQLPasswordException; use App\SQLLogin\Exception\SecurityPatternConfigurationException; use App\SQLLogin\SQLLoginRequest; use PDOException; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -33,12 +34,14 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator string $baseUrl, private SQLLoginService $sqlLoginService, private PasswordEncoder $passwordHasher, - private SQLLoginRequest $sqlLoginRequest + private SQLLoginRequest $sqlLoginRequest, + private LoggerInterface $logger ) { $this->baseUrl = $baseUrl; $this->sqlLoginService = $sqlLoginService; $this->passwordHasher = $passwordHasher; $this->sqlLoginRequest = $sqlLoginRequest; + $this->logger = $logger; } /** @@ -53,14 +56,14 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse { - return new RedirectResponse($this->baseUrl.'/connect/login-accept'); + return new RedirectResponse($this->baseUrl . '/connect/login-accept'); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): RedirectResponse { $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception); - return new RedirectResponse($this->baseUrl.'/login'); + return new RedirectResponse($this->baseUrl . '/login'); } public function authenticate(Request $request): SelfValidatingPassport @@ -74,8 +77,9 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator $datas = $this->sqlLoginService->fetchPasswordAndDatas($login); } catch (EmptyResultException $e) { $session->set(self::ERROR_LOGIN, true); + $this->logger->warning("authentication failed", ['username' => $login, "remote_address" => $request->getClientIp()]); throw new AuthenticationException(); - } catch (DataToFetchConfigurationException|PDOException $e) { + } catch (DataToFetchConfigurationException | PDOException $e) { \Sentry\captureException($e); $session->set(self::TECHNICAL_ERROR, true); throw new AuthenticationException(); @@ -95,6 +99,7 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator $this->passwordHasher->verify($remoteHashedPassword, $plaintextPassword, $remoteSalt); } catch (InvalidSQLPasswordException $e) { $session->set(self::ERROR_LOGIN, true); + $this->logger->warning("authentication failed", ['username' => $login, "remote_address" => $request->getClientIp()]); throw new AuthenticationException(); } catch (SecurityPatternConfigurationException $e) { \Sentry\captureException($e); @@ -111,11 +116,13 @@ class SQLLoginUserAuthenticator extends AbstractLoginFormAuthenticator } $passport->setAttribute('attributes', $user->getAttributes()); + $this->logger->warning("authentication succeeded", ['username' => $login, "remote_address" => $request->getClientIp()]); + return $passport; } protected function getLoginUrl(Request $request): string { - return $this->baseUrl.'/login'; + return $this->baseUrl . '/login'; } } diff --git a/symfony.lock b/symfony.lock index 7447713..98af439 100644 --- a/symfony.lock +++ b/symfony.lock @@ -114,6 +114,18 @@ "config/packages/lock.yaml" ] }, + "symfony/monolog-bundle": { + "version": "3.10", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.7", + "ref": "aff23899c4440dd995907613c1dd709b6f59503f" + }, + "files": [ + "config/packages/monolog.yaml" + ] + }, "symfony/routing": { "version": "5.4", "recipe": {