Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • dbp/relay/dbp-relay-auth-bundle
1 result
Select Git revision
Show changes
Commits on Source (1)
  • Reiter, Christoph's avatar
    Add an interface that allows customizing the user roles · 89d4166c
    Reiter, Christoph authored
    This adds UserRolesInterface which is used for converting the
    oauth2 scopes to symfony roles.
    
    The default interface implementation converts them to "ROLE_SCOPE_FOO".
    
    The interface also gets passed the user ID and can fetch roles from other
    places as well, like LDAP, or ignore the scopes etc.
    
    Fixes #4
    89d4166c
<?php
declare(strict_types=1);
namespace Dbp\Relay\AuthBundle\API;
interface UserRolesInterface
{
/**
* @param string[] $scopes
*
* @return string[]
*/
public function getRoles(?string $userIdentifier, array $scopes): array;
}
......@@ -23,3 +23,10 @@ services:
Dbp\Relay\CoreBundle\API\UserSessionInterface:
'@Dbp\Relay\AuthBundle\Service\OIDCUserSession'
Dbp\Relay\AuthBundle\Service\DefaultUserRoles:
autowire: true
autoconfigure: true
Dbp\Relay\AuthBundle\API\UserRolesInterface:
'@Dbp\Relay\AuthBundle\Service\DefaultUserRoles'
\ No newline at end of file
<?php
declare(strict_types=1);
namespace Dbp\Relay\AuthBundle\Service;
use Dbp\Relay\AuthBundle\API\UserRolesInterface;
class DefaultUserRoles implements UserRolesInterface
{
/**
* The default implementation converts OAuth2 scopes to Symfony roles
* by prefixing them with "ROLE_SCOPE_" and converting to uppercase.
*/
public function getRoles(?string $userIdentifier, array $scopes): array
{
$roles = [];
foreach ($scopes as $scope) {
$roles[] = 'ROLE_SCOPE_'.mb_strtoupper($scope);
}
return $roles;
}
}
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Dbp\Relay\AuthBundle\Service;
use Dbp\Relay\AuthBundle\API\UserRolesInterface;
use Dbp\Relay\CoreBundle\API\UserSessionInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
......@@ -19,10 +20,16 @@ class OIDCUserSession implements UserSessionInterface
*/
private $parameters;
public function __construct(ParameterBagInterface $parameters)
/**
* @var UserRolesInterface
*/
private $userRoles;
public function __construct(ParameterBagInterface $parameters, UserRolesInterface $userRoles)
{
$this->jwt = null;
$this->parameters = $parameters;
$this->userRoles = $userRoles;
}
public function getUserIdentifier(): ?string
......@@ -36,21 +43,18 @@ class OIDCUserSession implements UserSessionInterface
return $this->jwt['username'] ?? null;
}
private static function getScopes($jwt): array
{
return preg_split('/\s+/', $jwt['scope'] ?? '', -1, PREG_SPLIT_NO_EMPTY);
}
public function getUserRoles(): array
{
assert($this->jwt !== null);
$scopes = self::getScopes($this->jwt);
$userIdentifier = $this->getUserIdentifier();
$scopes = [];
if ($this->jwt['scope'] ?? '' !== '') {
$scopes = explode(' ', $this->jwt['scope']);
}
$roles = [];
foreach ($scopes as $scope) {
$roles[] = 'ROLE_SCOPE_'.mb_strtoupper($scope);
}
return $roles;
return $this->userRoles->getRoles($userIdentifier, $scopes);
}
/**
......@@ -58,13 +62,11 @@ class OIDCUserSession implements UserSessionInterface
*/
public static function isServiceAccountToken(array $jwt): bool
{
if (!array_key_exists('scope', $jwt)) {
throw new \RuntimeException('Token missing scope key');
}
$scope = $jwt['scope'];
$scopes = self::getScopes($jwt);
// XXX: This is the main difference I found compared to other flows, but that's a Keycloak
// implementation detail I guess.
$has_openid_scope = in_array('openid', explode(' ', $scope), true);
$has_openid_scope = in_array('openid', $scopes, true);
return !$has_openid_scope;
}
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Dbp\Relay\AuthBundle\Tests\Authenticator;
use Dbp\Relay\AuthBundle\Service\DefaultUserRoles;
use Dbp\Relay\AuthBundle\Service\OIDCUserSession;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
......@@ -21,7 +22,7 @@ class UserSessionTest extends TestCase
public function testGetLoggingId()
{
$session = new OIDCUserSession(new ParameterBag());
$session = new OIDCUserSession(new ParameterBag(), new DefaultUserRoles());
$session->setSessionToken([]);
$this->assertSame('unknown-unknown', $session->getSessionLoggingId());
......@@ -31,7 +32,7 @@ class UserSessionTest extends TestCase
public function testGetUserRoles()
{
$session = new OIDCUserSession(new ParameterBag());
$session = new OIDCUserSession(new ParameterBag(), new DefaultUserRoles());
$session->setSessionToken([]);
$this->assertSame([], $session->getUserRoles());
$session->setSessionToken(['scope' => 'foo bar quux-buz a_b']);
......@@ -42,7 +43,7 @@ class UserSessionTest extends TestCase
public function testGetSessionCacheKey()
{
$session = new OIDCUserSession(new ParameterBag());
$session = new OIDCUserSession(new ParameterBag(), new DefaultUserRoles());
$session->setSessionToken(['scope' => 'foo']);
$old = $session->getSessionCacheKey();
$session->setSessionToken(['scope' => 'bar']);
......@@ -52,7 +53,7 @@ class UserSessionTest extends TestCase
public function testGetSessionTTL()
{
$session = new OIDCUserSession(new ParameterBag());
$session = new OIDCUserSession(new ParameterBag(), new DefaultUserRoles());
$session->setSessionToken([]);
$this->assertSame(-1, $session->getSessionTTL());
......
<?php
declare(strict_types=1);
namespace Dbp\Relay\AuthBundle\Tests;
use Dbp\Relay\AuthBundle\Service\DefaultUserRoles;
use PHPUnit\Framework\TestCase;
class DefaultUserRolesTest extends TestCase
{
public function testGetRoles()
{
$userRoles = new DefaultUserRoles();
$roles = $userRoles->getRoles(null, ['foo-bar']);
$this->assertSame(['ROLE_SCOPE_FOO-BAR'], $roles);
}
}