Skip to content
Snippets Groups Projects
Commit 7430fbcf authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Move the user session into the core

and remove roles and oidc specifics. Instead we provide the session
in the core and forward requests to a oidc specific backend.

This also means the session can provide useful values even in case
it is used from the CLI and unauthenticated.
parent 27e4adc9
Branches
Tags v0.1.51
No related merge requests found
Pipeline #202509 passed
...@@ -6,58 +6,37 @@ namespace Dbp\Relay\CoreBundle\API; ...@@ -6,58 +6,37 @@ namespace Dbp\Relay\CoreBundle\API;
interface UserSessionInterface 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 * 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. * 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; 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 * 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. * 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. * 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 * 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. * 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 * 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). * 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. * 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. * 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; public function getSessionTTL(): int;
} }
...@@ -19,9 +19,15 @@ class ProxyAuthenticator extends AbstractAuthenticator ...@@ -19,9 +19,15 @@ class ProxyAuthenticator extends AbstractAuthenticator
*/ */
private $authenticators; private $authenticators;
public function __construct() /**
* @var UserSession
*/
private $userSession;
public function __construct(UserSession $userSession)
{ {
$this->authenticators = []; $this->authenticators = [];
$this->userSession = $userSession;
} }
public function addAuthenticator(AuthenticatorInterface $sub) public function addAuthenticator(AuthenticatorInterface $sub)
...@@ -54,7 +60,11 @@ class ProxyAuthenticator extends AbstractAuthenticator ...@@ -54,7 +60,11 @@ class ProxyAuthenticator extends AbstractAuthenticator
$auth = $this->getAuthenticator($request); $auth = $this->getAuthenticator($request);
assert($auth !== null); 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 public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
...@@ -67,6 +77,8 @@ class ProxyAuthenticator extends AbstractAuthenticator ...@@ -67,6 +77,8 @@ class ProxyAuthenticator extends AbstractAuthenticator
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{ {
$this->userSession->setProvider(null);
$auth = $this->getAuthenticator($request); $auth = $this->getAuthenticator($request);
assert($auth !== null); assert($auth !== null);
......
<?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();
}
}
...@@ -43,10 +43,7 @@ final class LoggingProcessor ...@@ -43,10 +43,7 @@ final class LoggingProcessor
$this->maskUserId($record); $this->maskUserId($record);
// Add a session ID (the same during multiple requests for the same user session) // Add a session ID (the same during multiple requests for the same user session)
$loggingId = $this->userDataProvider->getSessionLoggingId(); $record['context']['relay-session-id'] = $this->userDataProvider->getSessionLoggingId();
if ($loggingId !== null) {
$record['context']['relay-session-id'] = $loggingId;
}
// Add a request ID (the same during the same client request) // Add a request ID (the same during the same client request)
$request = $this->requestStack->getMainRequest(); $request = $this->requestStack->getMainRequest();
......
...@@ -58,6 +58,13 @@ services: ...@@ -58,6 +58,13 @@ services:
autowire: true autowire: true
autoconfigure: 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: Dbp\Relay\CoreBundle\LocalData\LocalDataAwareEventDispatcher:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
......
...@@ -41,12 +41,12 @@ class TestUserSession implements UserSessionInterface ...@@ -41,12 +41,12 @@ class TestUserSession implements UserSessionInterface
return $this->roles; return $this->roles;
} }
public function getSessionLoggingId(): ?string public function getSessionLoggingId(): string
{ {
return 'logging-id'; return 'logging-id';
} }
public function getSessionCacheKey(): ?string public function getSessionCacheKey(): string
{ {
return 'cache'; return 'cache';
} }
......
...@@ -6,6 +6,7 @@ namespace Dbp\Relay\CoreBundle\Tests\Auth; ...@@ -6,6 +6,7 @@ namespace Dbp\Relay\CoreBundle\Tests\Auth;
use Dbp\Relay\CoreBundle\Auth\AuthenticatorCompilerPass; use Dbp\Relay\CoreBundle\Auth\AuthenticatorCompilerPass;
use Dbp\Relay\CoreBundle\Auth\ProxyAuthenticator; use Dbp\Relay\CoreBundle\Auth\ProxyAuthenticator;
use Dbp\Relay\CoreBundle\Auth\UserSession;
use Dbp\Relay\CoreBundle\TestUtils\TestAuthenticator; use Dbp\Relay\CoreBundle\TestUtils\TestAuthenticator;
use Dbp\Relay\CoreBundle\TestUtils\TestUser; use Dbp\Relay\CoreBundle\TestUtils\TestUser;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
...@@ -17,13 +18,13 @@ class AuthenticatorTest extends TestCase ...@@ -17,13 +18,13 @@ class AuthenticatorTest extends TestCase
{ {
public function testSupports() public function testSupports()
{ {
$auth = new ProxyAuthenticator(); $auth = new ProxyAuthenticator(new UserSession());
$this->assertFalse($auth->supports(new Request())); $this->assertFalse($auth->supports(new Request()));
} }
public function testSingle() public function testSingle()
{ {
$auth = new ProxyAuthenticator(); $auth = new ProxyAuthenticator(new UserSession());
$user = new TestUser(); $user = new TestUser();
$sub = new TestAuthenticator($user, 'bla'); $sub = new TestAuthenticator($user, 'bla');
$auth->addAuthenticator($sub); $auth->addAuthenticator($sub);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment