diff --git a/.gitignore b/.gitignore index 23fcec14..9d86457e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ phpunit.xml .phpunit.result.cache # Tests -tests/.kernel/ +tests/config/reference.php +tests/.var/ diff --git a/composer.json b/composer.json index 9d6bcb74..d172a6ea 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "phpstan/phpstan": "^2.1", "phpstan/phpstan-symfony": "^2.0", "symfony/browser-kit": "^6.4|^7.4|^8.0", - "symfony/phpunit-bridge": "^7.4|^8.0" + "symfony/phpunit-bridge": "^8.1" }, "conflict": { "doctrine/doctrine-bundle": "<2.8.0", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9b5a7741..0bca5df7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -9,10 +9,8 @@ failOnWarning="true" > - - + - diff --git a/tests/Acceptance/CustomPersistenceManagerTest.php b/tests/Acceptance/CustomPersistenceManagerTest.php index aa2da8a8..fe9f72bd 100644 --- a/tests/Acceptance/CustomPersistenceManagerTest.php +++ b/tests/Acceptance/CustomPersistenceManagerTest.php @@ -24,11 +24,9 @@ use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeRefreshTokenManager; use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FixtureFactory; use League\Bundle\OAuth2ServerBundle\Tests\TestHelper; -use League\Bundle\OAuth2ServerBundle\Tests\TestKernel; use League\Bundle\OAuth2ServerBundle\ValueObject\RedirectUri; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\Console\Application; -use Symfony\Component\HttpKernel\KernelInterface; class CustomPersistenceManagerTest extends AbstractAcceptanceTest { @@ -40,7 +38,7 @@ class CustomPersistenceManagerTest extends AbstractAcceptanceTest protected function setUp(): void { - $this->client = self::createClient(); + $this->client = self::createClient(['environment' => 'custom_persistence']); $this->accessTokenManager = $this->createMock(AccessTokenManagerInterface::class); $this->clientManager = $this->createMock(ClientManagerInterface::class); $this->refreshTokenManager = $this->createMock(RefreshTokenManagerInterface::class); @@ -177,23 +175,4 @@ public function testSuccessfullDeviceCodeRequest(): void $this->client->getResponse(); static::assertResponseIsSuccessful(); } - - protected static function createKernel(array $options = []): KernelInterface - { - return new TestKernel( - 'test', - false, - null, - [ - 'custom' => [ - 'access_token_manager' => 'test.access_token_manager', - 'authorization_code_manager' => 'test.authorization_code_manager', - 'client_manager' => 'test.client_manager', - 'refresh_token_manager' => 'test.refresh_token_manager', - 'credentials_revoker' => 'test.credentials_revoker', - 'device_code_manager' => 'test.device_code_manager', - ], - ] - ); - } } diff --git a/tests/Acceptance/JwtLeewayConfigurationTest.php b/tests/Acceptance/JwtLeewayConfigurationTest.php index d0a23982..e1dd8482 100644 --- a/tests/Acceptance/JwtLeewayConfigurationTest.php +++ b/tests/Acceptance/JwtLeewayConfigurationTest.php @@ -5,16 +5,14 @@ namespace League\Bundle\OAuth2ServerBundle\Tests\Acceptance; use League\Bundle\OAuth2ServerBundle\Repository\AccessTokenRepository; -use League\Bundle\OAuth2ServerBundle\Tests\TestKernel; use League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator; use Symfony\Bundle\FrameworkBundle\Console\Application; -use Symfony\Component\HttpKernel\KernelInterface; class JwtLeewayConfigurationTest extends AbstractAcceptanceTest { protected function setUp(): void { - $this->client = self::createClient(); + $this->client = self::createClient(['environment' => 'jwt_leeway']); $this->application = new Application($this->client->getKernel()); } @@ -27,16 +25,4 @@ public function testLeewayConfigurationIsSet(): void $expected = new BearerTokenValidator($tokenRepository, new \DateInterval('PT60S')); $this->assertEquals($expected, $validator); } - - protected static function createKernel(array $options = []): KernelInterface - { - return new TestKernel( - 'test', - false, - [ - 'public_key' => '%env(PUBLIC_KEY_PATH)%', - 'jwt_leeway' => 'PT60S', - ] - ); - } } diff --git a/tests/Integration/AuthorizationServerCustomGrantTest.php b/tests/Integration/AuthorizationServerCustomGrantTest.php index ea0e0efa..5268b28e 100644 --- a/tests/Integration/AuthorizationServerCustomGrantTest.php +++ b/tests/Integration/AuthorizationServerCustomGrantTest.php @@ -12,7 +12,7 @@ final class AuthorizationServerCustomGrantTest extends KernelTestCase { public function testAuthorizationServerHasOurCustomGrantEnabled(): void { - static::bootKernel(); + static::bootKernel(['environment' => 'fake_grant']); /** @var AuthorizationServer $authorizationServer */ $authorizationServer = self::getContainer()->get(AuthorizationServer::class); diff --git a/tests/TestKernel.php b/tests/TestKernel.php index bd72ecd4..c9e565c9 100644 --- a/tests/TestKernel.php +++ b/tests/TestKernel.php @@ -4,273 +4,63 @@ namespace League\Bundle\OAuth2ServerBundle\Tests; -use Doctrine\DBAL\Platforms\SQLitePlatform; +use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; +use League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle; use League\Bundle\OAuth2ServerBundle\Manager\AccessTokenManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\AuthorizationCodeManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\DeviceCodeManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\RefreshTokenManagerInterface; use League\Bundle\OAuth2ServerBundle\Manager\ScopeManagerInterface; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeAccessTokenManager; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeAuthorizationCodeManager; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeClientManager; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeCredentialsRevoker; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeDeviceCodeManager; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeGrant; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeRefreshTokenManager; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FixtureFactory; -use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\SecurityTestController; -use Symfony\Component\Config\Loader\LoaderInterface; +use League\Bundle\OAuth2ServerBundle\Service\CredentialsRevokerInterface; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\Kernel; final class TestKernel extends Kernel implements CompilerPassInterface { - public function __construct( - string $environment, - bool $debug, - private ?array $resourceServiceConfig = null, - private ?array $persistenceConfig = null, - ) { - parent::__construct($environment, $debug); - } + use MicroKernelTrait; - public function boot(): void + public function registerBundles(): iterable { - $this->initializeEnvironmentVariables(); - - parent::boot(); + yield new DoctrineBundle(); + yield new FrameworkBundle(); + yield new SecurityBundle(); + yield new LeagueOAuth2ServerBundle(); } - public function registerBundles(): iterable + public function getProjectDir(): string { - return [ - new \Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), - new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new \Symfony\Bundle\SecurityBundle\SecurityBundle(), - new \League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle(), - ]; + return __DIR__; } public function getCacheDir(): string { - $cacheDirectory = 'cache'; - - // create unique cache directory when custom config is provided - if (null !== $this->resourceServiceConfig || null !== $this->persistenceConfig) { - $cacheDirectory = '/cache/' . hash('sha256', serialize(($this->resourceServiceConfig ?? []) + ($this->persistenceConfig ?? []))); - } - - return \sprintf('%s/tests/.kernel/' . $cacheDirectory, $this->getProjectDir()); + return $this->getProjectDir() . '/.var/cache/' . $this->environment; } public function getLogDir(): string { - return \sprintf('%s/tests/.kernel/logs', $this->getProjectDir()); + return $this->getProjectDir() . '/.var/log/' . $this->environment; } public function process(ContainerBuilder $container): void { - $this->exposeManagerServices($container); - } - - public function registerContainerConfiguration(LoaderInterface $loader): void - { - $loader->load(function (ContainerBuilder $container) { - $doctrine = [ - 'dbal' => [ - 'driver' => 'pdo_sqlite', - 'charset' => 'utf8mb4', - 'url' => 'sqlite:///:memory:', - 'default_table_options' => [ - 'charset' => 'utf8mb4', - 'utf8mb4_unicode_ci' => 'utf8mb4_unicode_ci', - ], - ], - ]; - - $doctrine['orm'] = []; - $container->loadFromExtension('doctrine', $doctrine); - - $framework = [ - 'secret' => 'nope', - 'test' => null, - 'router' => [ - 'resource' => __DIR__ . '/Fixtures/routes.php', - 'type' => 'php', - 'utf8' => true, - ], - 'http_method_override' => true, - 'php_errors' => ['log' => true], - ]; - - if (interface_exists(ValueResolverInterface::class)) { - $framework['handle_all_throwables'] = true; - } - - $container->loadFromExtension('framework', $framework); - - if (!$container->hasDefinition('kernel')) { - $container->register('kernel', static::class) - ->setSynthetic(true) - ->setPublic(true) - ->addTag('routing.route_loader'); - } - - $security = [ - 'firewalls' => [ - 'test' => [ - 'provider' => 'in_memory', - 'pattern' => '^/security-test', - 'stateless' => true, - 'oauth2' => true, - ], - 'authorization' => [ - 'provider' => 'in_memory', - 'pattern' => '^/authorize', - 'http_basic' => true, - 'stateless' => true, - ], - ], - 'providers' => [ - 'in_memory' => [ - 'memory' => [ - 'users' => [ - FixtureFactory::FIXTURE_USER => [ - 'roles' => ['ROLE_USER'], - ], - FixtureFactory::FIXTURE_USER_TWO => [ - 'roles' => ['ROLE_USER'], - ], - ], - ], - ], - 'another_provider' => [ - 'memory' => [ - 'users' => [ - FixtureFactory::FIXTURE_USER => [ - 'roles' => ['ROLE_USER'], - ], - FixtureFactory::FIXTURE_USER_TWO => [ - 'roles' => ['ROLE_USER'], - ], - ], - ], - ], - ], - 'access_control' => [ - [ - 'path' => '^/authorize', - 'roles' => 'IS_AUTHENTICATED', - ], - [ - 'path' => '^/device-code', - 'roles' => 'IS_AUTHENTICATED', - ], - ], - ]; - - $container->loadFromExtension('security', $security); - - $container->loadFromExtension('league_oauth2_server', [ - 'authorization_server' => [ - 'private_key' => '%env(PRIVATE_KEY_PATH)%', - 'encryption_key' => '%env(ENCRYPTION_KEY)%', - 'enable_password_grant' => true, - 'enable_implicit_grant' => true, - ], - 'resource_server' => $this->resourceServiceConfig ?? ['public_key' => '%env(PUBLIC_KEY_PATH)%'], - 'scopes' => [ - 'available' => [ - FixtureFactory::FIXTURE_SCOPE_FIRST, - FixtureFactory::FIXTURE_SCOPE_SECOND, - ], - 'default' => [ - FixtureFactory::FIXTURE_SCOPE_SECOND, - ], - ], - 'persistence' => $this->persistenceConfig ?? ['doctrine' => ['entity_manager' => 'default']], - ]); - - $this->configureControllers($container); - $this->configureDatabaseServices($container); - $this->configureCustomPersistenceServices($container); - $this->registerFakeGrant($container); - }); - } - - private function exposeManagerServices(ContainerBuilder $container): void - { - $container - ->getAlias(ScopeManagerInterface::class) - ->setPublic(true) - ; - - $container - ->getAlias(ClientManagerInterface::class) - ->setPublic(true) - ; - - $container - ->getAlias(AccessTokenManagerInterface::class) - ->setPublic(true) - ; - - $container - ->getAlias(RefreshTokenManagerInterface::class) - ->setPublic(true) - ; - - $container - ->getAlias(AuthorizationCodeManagerInterface::class) - ->setPublic(true) - ; - - $container - ->getAlias(DeviceCodeManagerInterface::class) - ->setPublic(true) - ; - } - - private function configureControllers(ContainerBuilder $container): void - { - $container - ->register(SecurityTestController::class) - ->setAutoconfigured(true) - ->setAutowired(true) - ; - } - - private function configureDatabaseServices(ContainerBuilder $container): void - { - $container - ->register(SQLitePlatform::class) - ->setAutoconfigured(true) - ->setAutowired(true) - ; - } - - private function configureCustomPersistenceServices(ContainerBuilder $container): void - { - $container->register('test.access_token_manager', FakeAccessTokenManager::class)->setPublic(true); - $container->register('test.authorization_code_manager', FakeAuthorizationCodeManager::class)->setPublic(true); - $container->register('test.client_manager', FakeClientManager::class)->setPublic(true); - $container->register('test.refresh_token_manager', FakeRefreshTokenManager::class)->setPublic(true); - $container->register('test.credentials_revoker', FakeCredentialsRevoker::class)->setPublic(true); - $container->register('test.device_code_manager', FakeDeviceCodeManager::class)->setPublic(true); - } - - private function registerFakeGrant(ContainerBuilder $container): void - { - $container->register(FakeGrant::class)->setAutoconfigured(true); - } + $publicServicesAlias = [ + ScopeManagerInterface::class, + ClientManagerInterface::class, + AccessTokenManagerInterface::class, + RefreshTokenManagerInterface::class, + AuthorizationCodeManagerInterface::class, + DeviceCodeManagerInterface::class, + CredentialsRevokerInterface::class, + ]; - private function initializeEnvironmentVariables(): void - { - putenv(\sprintf('PRIVATE_KEY_PATH=%s', TestHelper::PRIVATE_KEY_PATH)); - putenv(\sprintf('PUBLIC_KEY_PATH=%s', TestHelper::PUBLIC_KEY_PATH)); - putenv(\sprintf('ENCRYPTION_KEY=%s', TestHelper::ENCRYPTION_KEY)); + foreach ($publicServicesAlias as $serviceAlias) { + $container->getAlias($serviceAlias)->setPublic(true); + } } } diff --git a/tests/config/packages/custom_persistence/league_oauth2_server.php b/tests/config/packages/custom_persistence/league_oauth2_server.php new file mode 100644 index 00000000..63e7deab --- /dev/null +++ b/tests/config/packages/custom_persistence/league_oauth2_server.php @@ -0,0 +1,20 @@ +extension('league_oauth2_server', [ + 'persistence' => [ + 'custom' => [ + 'access_token_manager' => 'test.access_token_manager', + 'authorization_code_manager' => 'test.authorization_code_manager', + 'client_manager' => 'test.client_manager', + 'refresh_token_manager' => 'test.refresh_token_manager', + 'credentials_revoker' => 'test.credentials_revoker', + 'device_code_manager' => 'test.device_code_manager', + ], + ], + ]); +}; diff --git a/tests/config/packages/doctrine.php b/tests/config/packages/doctrine.php new file mode 100644 index 00000000..16755702 --- /dev/null +++ b/tests/config/packages/doctrine.php @@ -0,0 +1,20 @@ +extension('doctrine', [ + 'dbal' => [ + 'driver' => 'pdo_sqlite', + 'charset' => 'utf8mb4', + 'url' => 'sqlite:///:memory:', + 'default_table_options' => [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_ci', + ], + ], + 'orm' => [], + ]); +}; diff --git a/tests/config/packages/framework.php b/tests/config/packages/framework.php new file mode 100644 index 00000000..ed217af8 --- /dev/null +++ b/tests/config/packages/framework.php @@ -0,0 +1,17 @@ +extension('framework', [ + 'secret' => 'nope', + 'test' => true, + 'http_method_override' => false, + 'handle_all_throwables' => true, + 'php_errors' => [ + 'log' => true, + ], + ]); +}; diff --git a/tests/config/packages/jwt_leeway/league_oauth2_server.php b/tests/config/packages/jwt_leeway/league_oauth2_server.php new file mode 100644 index 00000000..86ac40a4 --- /dev/null +++ b/tests/config/packages/jwt_leeway/league_oauth2_server.php @@ -0,0 +1,13 @@ +extension('league_oauth2_server', [ + 'resource_server' => [ + 'jwt_leeway' => 'PT60S', + ], + ]); +}; diff --git a/tests/config/packages/league_oauth2_server.php b/tests/config/packages/league_oauth2_server.php new file mode 100644 index 00000000..b9329e29 --- /dev/null +++ b/tests/config/packages/league_oauth2_server.php @@ -0,0 +1,34 @@ +extension('league_oauth2_server', [ + 'authorization_server' => [ + 'private_key' => TestHelper::PRIVATE_KEY_PATH, + 'encryption_key' => TestHelper::ENCRYPTION_KEY, + 'enable_password_grant' => true, + 'enable_implicit_grant' => true, + ], + 'resource_server' => ['public_key' => TestHelper::PUBLIC_KEY_PATH], + 'scopes' => [ + 'available' => [ + FixtureFactory::FIXTURE_SCOPE_FIRST, + FixtureFactory::FIXTURE_SCOPE_SECOND, + ], + 'default' => [ + FixtureFactory::FIXTURE_SCOPE_SECOND, + ], + ], + 'persistence' => [ + 'doctrine' => [ + 'entity_manager' => 'default', + ], + ], + ]); +}; diff --git a/tests/config/packages/security.php b/tests/config/packages/security.php new file mode 100644 index 00000000..58f06782 --- /dev/null +++ b/tests/config/packages/security.php @@ -0,0 +1,62 @@ +extension('security', [ + 'firewalls' => [ + 'test' => [ + 'provider' => 'in_memory', + 'pattern' => '^/security-test', + 'stateless' => true, + 'oauth2' => true, + ], + 'authorization' => [ + 'provider' => 'in_memory', + 'pattern' => '^/authorize', + 'http_basic' => true, + 'stateless' => true, + ], + ], + 'providers' => [ + 'in_memory' => [ + 'memory' => [ + 'users' => [ + FixtureFactory::FIXTURE_USER => [ + 'roles' => ['ROLE_USER'], + ], + FixtureFactory::FIXTURE_USER_TWO => [ + 'roles' => ['ROLE_USER'], + ], + ], + ], + ], + 'another_provider' => [ + 'memory' => [ + 'users' => [ + FixtureFactory::FIXTURE_USER => [ + 'roles' => ['ROLE_USER'], + ], + FixtureFactory::FIXTURE_USER_TWO => [ + 'roles' => ['ROLE_USER'], + ], + ], + ], + ], + ], + 'access_control' => [ + [ + 'path' => '^/authorize', + 'roles' => 'IS_AUTHENTICATED', + ], + [ + 'path' => '^/device-code', + 'roles' => 'IS_AUTHENTICATED', + ], + ], + ]); +}; diff --git a/tests/config/routes.php b/tests/config/routes.php new file mode 100644 index 00000000..8c1c6c5d --- /dev/null +++ b/tests/config/routes.php @@ -0,0 +1,29 @@ +add('security_test', '/security-test') + ->controller([SecurityTestController::class, 'helloAction']) + + ->add('security_test_scopes', '/security-test-scopes') + ->controller([SecurityTestController::class, 'scopeAction']) + ->defaults([ + 'oauth2_scopes' => [FixtureFactory::FIXTURE_SCOPE_FIRST], + ]) + + ->add('security_test_roles', '/security-test-roles') + ->controller([SecurityTestController::class, 'rolesAction']) + ->defaults([ + 'oauth2_scopes' => [FixtureFactory::FIXTURE_SCOPE_FIRST], + ]) + + ->add('security_test_authorization', '/security-test-authorization') + ->controller([SecurityTestController::class, 'authorizationAction']) + ; +}; diff --git a/tests/config/routes/league_oauth2_server.php b/tests/config/routes/league_oauth2_server.php new file mode 100644 index 00000000..32fe3cd0 --- /dev/null +++ b/tests/config/routes/league_oauth2_server.php @@ -0,0 +1,9 @@ +import('@LeagueOAuth2ServerBundle/config/routes.php'); +}; diff --git a/tests/config/services.php b/tests/config/services.php new file mode 100644 index 00000000..9f94d444 --- /dev/null +++ b/tests/config/services.php @@ -0,0 +1,17 @@ +services() + ->set(SecurityTestController::class) + ->autoconfigure() + ->autowire() + ->set('logger', NullLogger::class) + ; +}; diff --git a/tests/config/services_custom_persistence.php b/tests/config/services_custom_persistence.php new file mode 100644 index 00000000..4ef69582 --- /dev/null +++ b/tests/config/services_custom_persistence.php @@ -0,0 +1,23 @@ +services() + ->set('test.access_token_manager', FakeAccessTokenManager::class) + ->set('test.authorization_code_manager', FakeAuthorizationCodeManager::class) + ->set('test.client_manager', FakeClientManager::class) + ->set('test.refresh_token_manager', FakeRefreshTokenManager::class) + ->set('test.credentials_revoker', FakeCredentialsRevoker::class) + ->set('test.device_code_manager', FakeDeviceCodeManager::class) + ; +}; diff --git a/tests/config/services_fake_grant.php b/tests/config/services_fake_grant.php new file mode 100644 index 00000000..c8e881c7 --- /dev/null +++ b/tests/config/services_fake_grant.php @@ -0,0 +1,14 @@ +services() + ->set(FakeGrant::class) + ->autoconfigure() + ; +};