From 7d27687720055dabce6497d4a24ad23e4e6af235 Mon Sep 17 00:00:00 2001 From: Robin Chalas Date: Mon, 1 Nov 2021 17:51:31 +0100 Subject: [PATCH] Add support for Symfony 5.4 & 6 --- .github/workflows/static-analysis.yml | 3 + .github/workflows/unit-tests.yml | 104 +++++++++--------- composer.json | 14 +-- psalm.xml | 1 - .../LeagueOAuth2ServerExtension.php | 2 +- .../Security/OAuth2Factory.php | 61 +++++----- .../Security/OAuth2FactoryTrait.php | 54 +++++++++ src/Entity/Scope.php | 3 + src/LeagueOAuth2ServerBundle.php | 14 ++- .../ForwardCompatAuthenticatorTrait.php | 41 +++++++ .../Authenticator/OAuth2Authenticator.php | 30 ++++- .../EventListener/CheckScopeListener.php | 8 +- .../Acceptance/DoctrineClientManagerTest.php | 8 +- .../DoctrineCredentialsRevokerTest.php | 4 +- .../Acceptance/InMemoryClientManagerTest.php | 2 +- .../AuthorizationServerCustomGrantTest.php | 2 +- tests/TestKernel.php | 6 +- tests/Unit/CheckScopeListenerTest.php | 4 +- tests/Unit/OAuth2AuthenticatorTest.php | 37 +++++++ tests/Unit/OAuth2TokenTest.php | 5 +- 20 files changed, 284 insertions(+), 119 deletions(-) create mode 100644 src/DependencyInjection/Security/OAuth2FactoryTrait.php create mode 100644 src/Security/Authenticator/ForwardCompatAuthenticatorTrait.php diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 0376a7a4..fd3b5420 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -18,6 +18,9 @@ jobs: tools: composer:v2, cs2pr extensions: intl, bcmath, curl, openssl, mbstring, pdo, pdo_sqlite + - name: "installing psalm" + run: "composer require vimeo/psalm:^4.6 psalm/plugin-symfony:^2.2 --dev --no-update && composer require --no-update" + - name: "installing dependencies" run: "composer update --no-interaction --no-progress" diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ec73db57..4226cb26 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -3,63 +3,65 @@ name: "unit tests" on: [ "pull_request", "push" ] jobs: - unit-tests: - name: "unit tests" - - runs-on: "ubuntu-latest" - + tests: + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.can-fail }} strategy: + fail-fast: false matrix: - symfony-version: - - "5.3.*" - php-version: - - "7.2" - - "7.3" - - "7.4" - - "8.0" - dependencies: - - "lowest" - - "highest" + include: + # Lowest supported stable Symfony branches + - php: 7.2 + symfony: 5.3.* + composer-flags: '--prefer-stable' + can-fail: false + # Development Symfony branches + - php: 7.3 + symfony: 5.4.*@dev + composer-flags: '--prefer-stable' # Needed to force `lcobucci/jwt` to install a usable version + can-fail: false + - php: 7.4 + symfony: 5.4.*@dev + composer-flags: '' + can-fail: false + - php: 8.0 + symfony: 5.4.*@dev + composer-flags: '' + can-fail: false + - php: 8.1 + symfony: 6.0.*@dev + composer-flags: '' + can-fail: false - steps: - - name: "checkout" - uses: "actions/checkout@v2" + name: "PHP ${{ matrix.php }} - Symfony ${{ matrix.symfony }}${{ matrix.composer-flags != '' && format(' - Composer {0}', matrix.composer-flags) || '' }}" - - name: "installing PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "${{ matrix.php-version }}" - tools: composer:v2, cs2pr - extensions: intl, bcmath, curl, openssl, mbstring, pdo, pdo_sqlite - coverage: pcov - ini-values: memory_limit=-1 - - - name: "caching dependencies" - uses: "actions/cache@v2" - with: - path: | - ~/.composer/cache - vendor - key: "php-${{ matrix.php-version }}" - restore-keys: "php-${{ matrix.php-version }}" + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + with: + fetch-depth: 2 - - name: "configuring composer platform" - if: (startsWith(matrix.php, '8.0')) - run: composer config platform.php 7.4.99 + - name: "Cache Composer packages" + uses: "actions/cache@v2" + with: + path: "~/.composer/cache" + key: "php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles('composer.json') }}-flags-${{ matrix.composer-flags }}" + restore-keys: "php-" - - name: "install lowest dependencies" - if: ${{ matrix.dependencies == 'lowest' }} - run: composer update --prefer-lowest --no-interaction --no-progress --prefer-dist + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php }}" + tools: "composer:v2,flex" - - name: "install highest dependencies" - if: ${{ matrix.dependencies == 'highest' }} - run: | - composer require --no-update symfony/config=${{ matrix.symfony-version }} symfony/http-kernel=${{ matrix.symfony-version }} symfony/dependency-injection=${{ matrix.symfony-version }} symfony/options-resolver=${{ matrix.symfony-version }} - composer require --no-update --dev symfony/framework-bundle=${{ matrix.symfony-version }} symfony/yaml=${{ matrix.symfony-version }} - composer update --no-interaction --no-progress --prefer-dist + - name: "Set Composer stability" + if: "matrix.symfony == '5.4.*@dev' || matrix.symfony == '6.0.*@dev'" + run: "composer config minimum-stability dev" - - name: "installing phpunit" - run: vendor/bin/simple-phpunit install + - name: "Install dependencies" + run: "composer update ${{ matrix.composer-flags }} --prefer-dist" + env: + SYMFONY_REQUIRE: "${{ matrix.symfony }}" - - name: "running unit tests" - run: vendor/bin/simple-phpunit + - name: "Run PHPUnit Tests" + run: "vendor/bin/simple-phpunit" diff --git a/composer.json b/composer.json index a44930b7..db01f73e 100644 --- a/composer.json +++ b/composer.json @@ -22,17 +22,16 @@ "league/oauth2-server": "^8.0", "nyholm/psr7": "^1.4", "psr/http-factory": "^1.0", - "symfony/framework-bundle": "^5.3", + "symfony/framework-bundle": "^5.3|^6.0", + "symfony/polyfill-php81": "^1.22", "symfony/psr-http-message-bridge": "^2.0", - "symfony/security-bundle": "^5.3" + "symfony/security-bundle": "^5.3|^6.0" }, "require-dev": { "ext-pdo": "*", "ext-pdo_sqlite": "*", - "psalm/plugin-symfony": "^2.2", - "symfony/browser-kit": "^5.3", - "symfony/phpunit-bridge": "^5.3", - "vimeo/psalm": "^4.6" + "symfony/browser-kit": "^5.3|^6.0", + "symfony/phpunit-bridge": "^5.3|^6.0" }, "autoload": { "psr-4": { "League\\Bundle\\OAuth2ServerBundle\\": "src/" } @@ -47,5 +46,6 @@ "branch-alias": { "dev-master": "0.1-dev" } - } + }, + "minimum-stability": "dev" } diff --git a/psalm.xml b/psalm.xml index 1bb55b3a..4e32f7b7 100644 --- a/psalm.xml +++ b/psalm.xml @@ -4,7 +4,6 @@ forbidEcho="true" strictBinaryOperands="true" phpVersion="7.1" - allowPhpStormGenerics="true" allowStringToStandInForClass="true" rememberPropertyAssignmentsAfterCall="false" checkForThrowsInGlobalScope="true" diff --git a/src/DependencyInjection/LeagueOAuth2ServerExtension.php b/src/DependencyInjection/LeagueOAuth2ServerExtension.php index 5e9ad519..c5a0c16e 100644 --- a/src/DependencyInjection/LeagueOAuth2ServerExtension.php +++ b/src/DependencyInjection/LeagueOAuth2ServerExtension.php @@ -74,7 +74,7 @@ public function load(array $configs, ContainerBuilder $container) /** * {@inheritdoc} */ - public function getAlias() + public function getAlias(): string { return 'league_oauth2_server'; } diff --git a/src/DependencyInjection/Security/OAuth2Factory.php b/src/DependencyInjection/Security/OAuth2Factory.php index a16add28..2c06e7c4 100644 --- a/src/DependencyInjection/Security/OAuth2Factory.php +++ b/src/DependencyInjection/Security/OAuth2Factory.php @@ -4,47 +4,38 @@ namespace League\Bundle\OAuth2ServerBundle\DependencyInjection\Security; -use League\Bundle\OAuth2ServerBundle\Security\Authenticator\OAuth2Authenticator; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AuthenticatorFactoryInterface; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; -use Symfony\Component\Config\Definition\Builder\NodeDefinition; -use Symfony\Component\DependencyInjection\ChildDefinition; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * @author Mathias Arlaud - */ -final class OAuth2Factory implements SecurityFactoryInterface, AuthenticatorFactoryInterface -{ - public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint): array - { - throw new \LogicException('OAuth2 is not supported when "security.enable_authenticator_manager" is not set to true.'); - } - - public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string - { - $authenticator = sprintf('security.authenticator.oauth2.%s', $firewallName); - - $definition = new ChildDefinition(OAuth2Authenticator::class); - $definition->replaceArgument(2, new Reference($userProviderId)); - - $container->setDefinition($authenticator, $definition); - - return $authenticator; - } - - public function getPosition(): string +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; + +if (interface_exists(SecurityFactoryInterface::class) && !interface_exists(AuthenticatorFactoryInterface::class)) { + /** + * Wires the "oauth" authenticator from user configuration. + * + * @author Mathias Arlaud + */ + class OAuth2Factory implements SecurityFactoryInterface { - return 'pre_auth'; + use OAuth2FactoryTrait; } - - public function getKey(): string +} elseif (!method_exists(SecurityExtension::class, 'addAuthenticatorFactory')) { + /** + * Wires the "oauth" authenticator from user configuration. + * + * @author Mathias Arlaud + */ + class OAuth2Factory implements AuthenticatorFactoryInterface, SecurityFactoryInterface { - return 'oauth2'; + use OAuth2FactoryTrait; } - - public function addConfiguration(NodeDefinition $builder): void +} else { + /** + * Wires the "oauth" authenticator from user configuration. + * + * @author Mathias Arlaud + */ + class OAuth2Factory implements AuthenticatorFactoryInterface { + use OAuth2FactoryTrait; } } diff --git a/src/DependencyInjection/Security/OAuth2FactoryTrait.php b/src/DependencyInjection/Security/OAuth2FactoryTrait.php new file mode 100644 index 00000000..95d339ea --- /dev/null +++ b/src/DependencyInjection/Security/OAuth2FactoryTrait.php @@ -0,0 +1,54 @@ + + * @author Robin Chalas + */ +trait OAuth2FactoryTrait +{ + public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint): array + { + throw new \LogicException('OAuth2 is not supported when "security.enable_authenticator_manager" is not set to true.'); + } + + public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string + { + $authenticator = sprintf('security.authenticator.oauth2.%s', $firewallName); + + $definition = new ChildDefinition(OAuth2Authenticator::class); + $definition->replaceArgument(2, new Reference($userProviderId)); + + $container->setDefinition($authenticator, $definition); + + return $authenticator; + } + + public function getPosition(): string + { + return 'pre_auth'; + } + + public function getPriority(): int + { + return -10; + } + + public function getKey(): string + { + return 'oauth2'; + } + + public function addConfiguration(NodeDefinition $builder): void + { + } +} diff --git a/src/Entity/Scope.php b/src/Entity/Scope.php index 0cce52e7..89395c0b 100644 --- a/src/Entity/Scope.php +++ b/src/Entity/Scope.php @@ -13,7 +13,10 @@ final class Scope implements ScopeEntityInterface /** * {@inheritdoc} + * + * @return mixed */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->getIdentifier(); diff --git a/src/LeagueOAuth2ServerBundle.php b/src/LeagueOAuth2ServerBundle.php index d1f6a364..1a3d2e49 100644 --- a/src/LeagueOAuth2ServerBundle.php +++ b/src/LeagueOAuth2ServerBundle.php @@ -10,6 +10,7 @@ use League\Bundle\OAuth2ServerBundle\DependencyInjection\Security\OAuth2Factory; use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\HttpKernel\Bundle\Bundle; final class LeagueOAuth2ServerBundle extends Bundle @@ -30,7 +31,7 @@ public function build(ContainerBuilder $container) /** * {@inheritdoc} */ - public function getContainerExtension() + public function getContainerExtension(): ExtensionInterface { return new LeagueOAuth2ServerExtension(); } @@ -39,6 +40,17 @@ private function configureSecurityExtension(ContainerBuilder $container): void { /** @var SecurityExtension $extension */ $extension = $container->getExtension('security'); + + if (method_exists($extension, 'addAuthenticatorFactory')) { + $extension->addAuthenticatorFactory(new OAuth2Factory()); + + return; + } + + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress InvalidArgument + */ $extension->addSecurityListenerFactory(new OAuth2Factory()); } diff --git a/src/Security/Authenticator/ForwardCompatAuthenticatorTrait.php b/src/Security/Authenticator/ForwardCompatAuthenticatorTrait.php new file mode 100644 index 00000000..5d7cb024 --- /dev/null +++ b/src/Security/Authenticator/ForwardCompatAuthenticatorTrait.php @@ -0,0 +1,41 @@ +getReturnType(); + +if ($r && Passport::class === $r->getName()) { + /** + * @internal + * + * @psalm-suppress UnrecognizedStatement + */ + trait ForwardCompatAuthenticatorTrait + { + public function authenticate(Request $request): Passport + { + return $this->doAuthenticate($request); + } + } +} else { + /** + * @internal + * + * @psalm-suppress UnrecognizedStatement + */ + trait ForwardCompatAuthenticatorTrait + { + public function authenticate(Request $request): PassportInterface + { + return $this->doAuthenticate($request); + } + } +} diff --git a/src/Security/Authenticator/OAuth2Authenticator.php b/src/Security/Authenticator/OAuth2Authenticator.php index ca8eba5c..28e54772 100644 --- a/src/Security/Authenticator/OAuth2Authenticator.php +++ b/src/Security/Authenticator/OAuth2Authenticator.php @@ -30,6 +30,12 @@ */ final class OAuth2Authenticator implements AuthenticatorInterface, AuthenticationEntryPointInterface { + /** + * @psalm-suppress UndefinedTrait + * @psalm-suppress MethodSignatureMismatch + */ + use ForwardCompatAuthenticatorTrait; + /** * @var HttpMessageFactoryInterface */ @@ -73,9 +79,11 @@ public function start(Request $request, AuthenticationException $authException = } /** - * @psalm-suppress DeprecatedMethod + * {@inheritdoc} + * + * @return Passport */ - public function authenticate(Request $request): PassportInterface + public function doAuthenticate(Request $request) /*: Passport */ { try { $psr7Request = $this->resourceServer->validateAuthenticatedRequest($this->httpMessageFactory->createRequest($request)); @@ -100,6 +108,7 @@ public function authenticate(Request $request): PassportInterface return new NullUser(); } if (!method_exists($this->userProvider, 'loadUserByIdentifier')) { + /** @psalm-suppress DeprecatedMethod */ return $this->userProvider->loadUserByUsername($userIdentifier); } @@ -111,7 +120,6 @@ public function authenticate(Request $request): PassportInterface ]); $passport->setAttribute('accessTokenId', $accessTokenId); - $passport->setAttribute('oauthClientId', $oauthClientId); return $passport; @@ -119,6 +127,8 @@ public function authenticate(Request $request): PassportInterface /** * @return OAuth2Token + * + * @psalm-suppress DeprecatedClass */ public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface { @@ -126,6 +136,17 @@ public function createAuthenticatedToken(PassportInterface $passport, string $fi throw new \RuntimeException(sprintf('Cannot create a OAuth2 authenticated token. $passport should be a %s', Passport::class)); } + $token = $this->createToken($passport, $firewallName); + $token->setAuthenticated(true); + + return $token; + } + + /** + * @return OAuth2Token + */ + public function createToken(Passport $passport, string $firewallName): TokenInterface + { /** @var string $accessTokenId */ $accessTokenId = $passport->getAttribute('accessTokenId'); @@ -133,10 +154,9 @@ public function createAuthenticatedToken(PassportInterface $passport, string $fi $scopeBadge = $passport->getBadge(ScopeBadge::class); /** @var string $oauthClientId */ - $oauthClientId = $passport->getAttribute('oauthClientId'); + $oauthClientId = $passport->getAttribute('oauthClientId', ''); $token = new OAuth2Token($passport->getUser(), $accessTokenId, $oauthClientId, $scopeBadge->getScopes(), $this->rolePrefix); - $token->setAuthenticated(true); return $token; } diff --git a/src/Security/EventListener/CheckScopeListener.php b/src/Security/EventListener/CheckScopeListener.php index 60b5a106..83c97a81 100644 --- a/src/Security/EventListener/CheckScopeListener.php +++ b/src/Security/EventListener/CheckScopeListener.php @@ -19,9 +19,6 @@ */ final class CheckScopeListener implements EventSubscriberInterface { - /** - * @var RequestStack - */ private $requestStack; public function __construct(RequestStack $requestStack) @@ -31,7 +28,10 @@ public function __construct(RequestStack $requestStack) public function checkPassport(CheckPassportEvent $event): void { - /** @var Passport $passport */ + /** + * @var Passport $passport + * @psalm-suppress DeprecatedClass + */ $passport = $event->getPassport(); if (!$passport->hasBadge(ScopeBadge::class)) { return; diff --git a/tests/Acceptance/DoctrineClientManagerTest.php b/tests/Acceptance/DoctrineClientManagerTest.php index 4848813a..4e0cd573 100644 --- a/tests/Acceptance/DoctrineClientManagerTest.php +++ b/tests/Acceptance/DoctrineClientManagerTest.php @@ -23,7 +23,7 @@ public function testSimpleDelete(): void { /** @var $em EntityManagerInterface */ $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); - $doctrineClientManager = new DoctrineClientManager($em, self::$container->get(EventDispatcherInterface::class), Client::class); + $doctrineClientManager = new DoctrineClientManager($em, self::getContainer()->get(EventDispatcherInterface::class), Client::class); $client = new Client('client', 'client', 'secret'); $em->persist($client); @@ -42,7 +42,7 @@ public function testClientDeleteCascadesToAccessTokens(): void { /** @var $em EntityManagerInterface */ $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); - $doctrineClientManager = new DoctrineClientManager($em, self::$container->get(EventDispatcherInterface::class), Client::class); + $doctrineClientManager = new DoctrineClientManager($em, self::getContainer()->get(EventDispatcherInterface::class), Client::class); $client = new Client('client', 'client', 'secret'); $em->persist($client); @@ -75,7 +75,7 @@ public function testSaveClientWithoutScopeAddDefaultScopes(): void { /** @var $em EntityManagerInterface */ $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); - $doctrineClientManager = new DoctrineClientManager($em, self::$container->get(EventDispatcherInterface::class), Client::class); + $doctrineClientManager = new DoctrineClientManager($em, self::getContainer()->get(EventDispatcherInterface::class), Client::class); $doctrineClientManager->save($client = new Client('client', 'client', 'secret')); @@ -89,7 +89,7 @@ public function testClientDeleteCascadesToAccessTokensAndRefreshTokens(): void { /** @var $em EntityManagerInterface */ $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); - $doctrineClientManager = new DoctrineClientManager($em, self::$container->get(EventDispatcherInterface::class), Client::class); + $doctrineClientManager = new DoctrineClientManager($em, self::getContainer()->get(EventDispatcherInterface::class), Client::class); $client = new Client('client', 'client', 'secret'); $em->persist($client); diff --git a/tests/Acceptance/DoctrineCredentialsRevokerTest.php b/tests/Acceptance/DoctrineCredentialsRevokerTest.php index b628dd63..673f9857 100644 --- a/tests/Acceptance/DoctrineCredentialsRevokerTest.php +++ b/tests/Acceptance/DoctrineCredentialsRevokerTest.php @@ -38,7 +38,7 @@ public function testRevokesAllCredentialsForUser(): void $em->persist($refreshToken); $em->flush(); - $revoker = new DoctrineCredentialsRevoker($em, new ClientManager($em, self::$container->get(EventDispatcherInterface::class), Client::class)); + $revoker = new DoctrineCredentialsRevoker($em, new ClientManager($em, self::getContainer()->get(EventDispatcherInterface::class), Client::class)); $revoker->revokeCredentialsForUser(FixtureFactory::createUser()); @@ -67,7 +67,7 @@ public function testRevokesAllCredentialsForClient(): void $em->persist($refreshToken); $em->flush(); - $revoker = new DoctrineCredentialsRevoker($em, new ClientManager($em, self::$container->get(EventDispatcherInterface::class), Client::class)); + $revoker = new DoctrineCredentialsRevoker($em, new ClientManager($em, self::getContainer()->get(EventDispatcherInterface::class), Client::class)); $revoker->revokeCredentialsForClient($client); diff --git a/tests/Acceptance/InMemoryClientManagerTest.php b/tests/Acceptance/InMemoryClientManagerTest.php index 74506ebb..106a40ee 100644 --- a/tests/Acceptance/InMemoryClientManagerTest.php +++ b/tests/Acceptance/InMemoryClientManagerTest.php @@ -18,7 +18,7 @@ final class InMemoryClientManagerTest extends AbstractAcceptanceTest { public function testSaveClientWithoutScopeAddDefaultScopes(): void { - $inMemoryClientManager = new InMemoryClientManager(self::$container->get(EventDispatcherInterface::class)); + $inMemoryClientManager = new InMemoryClientManager(self::getContainer()->get(EventDispatcherInterface::class)); $inMemoryClientManager->save($client = new Client('client', 'client', 'secret')); diff --git a/tests/Integration/AuthorizationServerCustomGrantTest.php b/tests/Integration/AuthorizationServerCustomGrantTest.php index 06e4b9f1..acafbd2a 100644 --- a/tests/Integration/AuthorizationServerCustomGrantTest.php +++ b/tests/Integration/AuthorizationServerCustomGrantTest.php @@ -15,7 +15,7 @@ public function testAuthorizationServerHasOurCustomGrantEnabled(): void static::bootKernel(); /** @var AuthorizationServer $authorizationServer */ - $authorizationServer = self::$container->get(AuthorizationServer::class); + $authorizationServer = self::getContainer()->get(AuthorizationServer::class); $reflectionClass = new \ReflectionClass(AuthorizationServer::class); $reflectionProperty = $reflectionClass->getProperty('enabledGrantTypes'); diff --git a/tests/TestKernel.php b/tests/TestKernel.php index ab7ebbb1..e275f1d7 100644 --- a/tests/TestKernel.php +++ b/tests/TestKernel.php @@ -33,7 +33,7 @@ public function boot() /** * {@inheritdoc} */ - public function registerBundles() + public function registerBundles(): iterable { return [ new \Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), @@ -46,7 +46,7 @@ public function registerBundles() /** * {@inheritdoc} */ - public function getCacheDir() + public function getCacheDir(): string { return sprintf('%s/tests/.kernel/cache', $this->getProjectDir()); } @@ -54,7 +54,7 @@ public function getCacheDir() /** * {@inheritdoc} */ - public function getLogDir() + public function getLogDir(): string { return sprintf('%s/tests/.kernel/logs', $this->getProjectDir()); } diff --git a/tests/Unit/CheckScopeListenerTest.php b/tests/Unit/CheckScopeListenerTest.php index 43a20d4a..0e5c69d9 100644 --- a/tests/Unit/CheckScopeListenerTest.php +++ b/tests/Unit/CheckScopeListenerTest.php @@ -12,7 +12,7 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; -use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; +use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; use Symfony\Component\Security\Http\Event\CheckPassportEvent; @@ -22,7 +22,7 @@ public function testNoScopeRequested(): void { $event = new CheckPassportEvent( $this->createMock(AuthenticatorInterface::class), - $this->createMock(PassportInterface::class) + $this->createMock(Passport::class) ); $requestStack = new RequestStack(); diff --git a/tests/Unit/OAuth2AuthenticatorTest.php b/tests/Unit/OAuth2AuthenticatorTest.php index 6249dfda..f956070d 100644 --- a/tests/Unit/OAuth2AuthenticatorTest.php +++ b/tests/Unit/OAuth2AuthenticatorTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; +use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; final class OAuth2AuthenticatorTest extends TestCase @@ -129,8 +130,15 @@ public function testAuthenticateCreatePassportWithNullUser(): void $this->assertInstanceOf(NullUser::class, $passport->getUser()); } + /** + * @group legacy + */ public function testCreateAuthenticatedToken(): void { + if (!interface_exists(PassportInterface::class)) { + $this->markTestSkipped('Irrelevant on Symfony 6+'); + } + if (!class_exists(UserBadge::class)) { $userBadge = new NullUser(); } else { @@ -159,6 +167,35 @@ public function testCreateAuthenticatedToken(): void $this->assertInstanceOf(NullUser::class, $token->getUser()); $this->assertTrue($token->isAuthenticated()); } + + public function testCreateToken(): void + { + if (interface_exists(PassportInterface::class)) { + $this->markTestSkipped('Irrelevant on Symfony <5.4'); + } + + $userBadge = new UserBadge('userIdentifier', static function (): UserInterface { + return new NullUser(); + }); + + $passport = new SelfValidatingPassport($userBadge, [ + new ScopeBadge(['scope_one', 'scope_two']), + ]); + $passport->setAttribute('accessTokenId', 'accessTokenId'); + + $authenticator = new OAuth2Authenticator( + $this->createMock(HttpMessageFactoryInterface::class), + $this->createMock(ResourceServer::class), + $this->createMock(TestUserProvider::class), + 'PREFIX_' + ); + + $token = $authenticator->createToken($passport, 'firewallName'); + + $this->assertSame(['scope_one', 'scope_two'], $token->getScopes()); + $this->assertSame('accessTokenId', $token->getCredentials()); + $this->assertInstanceOf(NullUser::class, $token->getUser()); + } } abstract class TestUserProvider implements UserProviderInterface diff --git a/tests/Unit/OAuth2TokenTest.php b/tests/Unit/OAuth2TokenTest.php index 4fe5ac0a..b0bcc4e4 100644 --- a/tests/Unit/OAuth2TokenTest.php +++ b/tests/Unit/OAuth2TokenTest.php @@ -8,6 +8,7 @@ use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FixtureFactory; use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\User; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Authorization\Voter\CacheableVoterInterface; final class OAuth2TokenTest extends TestCase { @@ -30,6 +31,8 @@ public function testTokenSerialization(): void $this->assertSame($scopes, $token->getScopes()); $this->assertSame([sprintf('%s%s', $rolePrefix, strtoupper($scopes[0]))], $token->getRoleNames()); - $this->assertFalse($unserializedToken->isAuthenticated()); + if (!interface_exists(CacheableVoterInterface::class)) { + $this->assertFalse($unserializedToken->isAuthenticated()); + } } }