diff --git a/src/API/UserSessionInterface.php b/src/API/UserSessionInterface.php index ce3e3df3264c2d58765dd696d0b3fb89c234974f..8bc5b1b62dbe32cf32a0fa18779a010b7434a5d5 100644 --- a/src/API/UserSessionInterface.php +++ b/src/API/UserSessionInterface.php @@ -6,58 +6,37 @@ namespace Dbp\Relay\CoreBundle\API; interface UserSessionInterface { - /** - * This gets called with the active JWT before any of the other methods are called. - */ - public function setSessionToken(?array $jwt): void; - /** * The unique identifier of the authenticated user. Or null in case it is called * before the user is known or if the user is a system. - * - * Can be derived from the session token for example. */ public function getUserIdentifier(): ?string; - /** - * Returns a list of Symfony user roles, like ['ROLE_FOOBAR']. - * - * Can be derived from the session token for example. - */ - public function getUserRoles(): array; - /** * Returns an ID represents a "session" of a user which can be used for logging. It should not be possible to * figure out which user is behind the ID based on the ID itself and the ID should change regularly. * This is useful for connecting various requests together for logging while not exposing details about the user. - * - * Can be derived from long running session IDs embedded in the token for example. - * - * Return null in case no logging ID exists */ - public function getSessionLoggingId(): ?string; + public function getSessionLoggingId(): string; + + /** + * @deprecated + */ + public function getUserRoles(): array; /** * Returns a unique caching key that can be used to cache metadata related to the current user session like * any user metadata, authorization related information etc. * It should not be possible to figure out which user is behind the ID based on the ID itself and the ID should * change regularly (after a logout/login or a key refresh for example). - * - * For example a hashed version of the token. - * - * Return null in case no appropriate cache key exists to disable any caching. */ - public function getSessionCacheKey(): ?string; + public function getSessionCacheKey(): string; /** - * Should return the duration the session is valid (as a whole, not from now) in seconds. + * Returns the duration the session is valid (as a whole, not from now) in seconds. * After the specified amount of time has passed the logging ID and the caching key should have changed. * * This is mostly useful for limiting the cache. - * - * For example the lifespan of the token. - * - * Return <0 in case that information isn't available. */ public function getSessionTTL(): int; } diff --git a/src/Auth/ProxyAuthenticator.php b/src/Auth/ProxyAuthenticator.php index 7fdafe8433cc8c410a2c0766ebd1e20b0a253d21..eb80e473f49e0d4a3bdc1d040e451fa2a7d61b3d 100644 --- a/src/Auth/ProxyAuthenticator.php +++ b/src/Auth/ProxyAuthenticator.php @@ -19,9 +19,15 @@ class ProxyAuthenticator extends AbstractAuthenticator */ private $authenticators; - public function __construct() + /** + * @var UserSession + */ + private $userSession; + + public function __construct(UserSession $userSession) { $this->authenticators = []; + $this->userSession = $userSession; } public function addAuthenticator(AuthenticatorInterface $sub) @@ -54,7 +60,11 @@ class ProxyAuthenticator extends AbstractAuthenticator $auth = $this->getAuthenticator($request); assert($auth !== null); - return $auth->authenticate($request); + $passport = $auth->authenticate($request); + $provider = $passport->getAttribute('relay_user_session_provider'); + $this->userSession->setProvider($provider); + + return $passport; } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response @@ -67,6 +77,8 @@ class ProxyAuthenticator extends AbstractAuthenticator public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { + $this->userSession->setProvider(null); + $auth = $this->getAuthenticator($request); assert($auth !== null); diff --git a/src/Auth/UserSession.php b/src/Auth/UserSession.php new file mode 100644 index 0000000000000000000000000000000000000000..461d9f3630c136b2b1ce8acb6e5b1d861a19a38d --- /dev/null +++ b/src/Auth/UserSession.php @@ -0,0 +1,98 @@ +<?php + +declare(strict_types=1); + +namespace Dbp\Relay\CoreBundle\Auth; + +use Dbp\Relay\CoreBundle\API\UserSessionInterface; +use Dbp\Relay\CoreBundle\API\UserSessionProviderInterface; +use Symfony\Component\Security\Core\Security; +use Symfony\Component\Uid\Uuid; + +/** + * This service provides user session information, either sourcing information from the active auth provider + * or in case it is used from the CLI or unauthenticated then it returns some reasonable defaults. + */ +class UserSession implements UserSessionInterface +{ + /** + * @var ?UserSessionProviderInterface + */ + private $provider; + + /** + * @var Security + */ + private $security; + + public function __construct(?Security $security = null) + { + $this->security = $security; + } + + public function setProvider(?UserSessionProviderInterface $provider) + { + $this->provider = $provider; + } + + public function getUserIdentifier(): ?string + { + if ($this->provider === null) { + return null; + } + + return $this->provider->getUserIdentifier(); + } + + public function getSessionLoggingId(): string + { + $id = null; + if ($this->provider !== null) { + $id = $this->provider->getSessionLoggingId(); + } + if ($id === null) { + $id = 'unknown'; + } + + return $id; + } + + public function getSessionCacheKey(): string + { + $key = null; + if ($this->provider !== null) { + $key = $this->provider->getSessionCacheKey(); + } + if ($key === null) { + $key = (Uuid::v4())->toRfc4122(); + } + + return $key; + } + + public function getSessionTTL(): int + { + $ttl = -1; + if ($this->provider !== null) { + $ttl = $this->provider->getSessionTTL(); + } + if ($ttl === -1) { + $ttl = 60; + } + + return $ttl; + } + + public function getUserRoles(): array + { + if ($this->provider === null) { + return []; + } + $user = $this->security->getUser(); + if ($user === null) { + return []; + } + + return $user->getRoles(); + } +} diff --git a/src/Logging/LoggingProcessor.php b/src/Logging/LoggingProcessor.php index e014fabb08c0eac79ce40d9505dc1c347dde4233..0b6c309f90a30e5a71d495669a4278fd14943d29 100644 --- a/src/Logging/LoggingProcessor.php +++ b/src/Logging/LoggingProcessor.php @@ -43,10 +43,7 @@ final class LoggingProcessor $this->maskUserId($record); // Add a session ID (the same during multiple requests for the same user session) - $loggingId = $this->userDataProvider->getSessionLoggingId(); - if ($loggingId !== null) { - $record['context']['relay-session-id'] = $loggingId; - } + $record['context']['relay-session-id'] = $this->userDataProvider->getSessionLoggingId(); // Add a request ID (the same during the same client request) $request = $this->requestStack->getMainRequest(); diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml index 9a563541a14e17824d38792add600cc1fbfb4474..fef566f3e835fd5ff3bccce6ffb91d70a59ff8af 100644 --- a/src/Resources/config/services.yaml +++ b/src/Resources/config/services.yaml @@ -58,6 +58,13 @@ services: autowire: true autoconfigure: true + Dbp\Relay\CoreBundle\Auth\UserSession: + autowire: true + autoconfigure: true + + Dbp\Relay\CoreBundle\API\UserSessionInterface: + '@Dbp\Relay\CoreBundle\Auth\UserSession' + Dbp\Relay\CoreBundle\LocalData\LocalDataAwareEventDispatcher: autowire: true autoconfigure: true diff --git a/src/TestUtils/TestUserSession.php b/src/TestUtils/TestUserSession.php index 1abe5873c26ab63012b5b49ec61c9a71f1e49305..672648370e8d50131bf7665b9dfd1beadbcb199b 100644 --- a/src/TestUtils/TestUserSession.php +++ b/src/TestUtils/TestUserSession.php @@ -41,12 +41,12 @@ class TestUserSession implements UserSessionInterface return $this->roles; } - public function getSessionLoggingId(): ?string + public function getSessionLoggingId(): string { return 'logging-id'; } - public function getSessionCacheKey(): ?string + public function getSessionCacheKey(): string { return 'cache'; } diff --git a/tests/Auth/AuthenticatorTest.php b/tests/Auth/AuthenticatorTest.php index 26d9879b226455b678f50a1476312c9711976705..99dc9f9f491c58ed7953da392b08a20ac0d63992 100644 --- a/tests/Auth/AuthenticatorTest.php +++ b/tests/Auth/AuthenticatorTest.php @@ -6,6 +6,7 @@ namespace Dbp\Relay\CoreBundle\Tests\Auth; use Dbp\Relay\CoreBundle\Auth\AuthenticatorCompilerPass; use Dbp\Relay\CoreBundle\Auth\ProxyAuthenticator; +use Dbp\Relay\CoreBundle\Auth\UserSession; use Dbp\Relay\CoreBundle\TestUtils\TestAuthenticator; use Dbp\Relay\CoreBundle\TestUtils\TestUser; use PHPUnit\Framework\TestCase; @@ -17,13 +18,13 @@ class AuthenticatorTest extends TestCase { public function testSupports() { - $auth = new ProxyAuthenticator(); + $auth = new ProxyAuthenticator(new UserSession()); $this->assertFalse($auth->supports(new Request())); } public function testSingle() { - $auth = new ProxyAuthenticator(); + $auth = new ProxyAuthenticator(new UserSession()); $user = new TestUser(); $sub = new TestAuthenticator($user, 'bla'); $auth->addAuthenticator($sub);