diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2f6eea8c5..95c4a086b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -61,19 +61,20 @@ jobs: symfony-version: ^4.4 - php: '7.4' symfony-version: ^5.4 + - php: '7.4' + symfony-version: ^5.4 + bc: true - php: '8.0' symfony-version: ^4.4 - php: '8.0' symfony-version: ^5.4 - php: '8.0' - stability: dev symfony-version: ^6.0 - php: '8.1' symfony-version: ^4.4 - php: '8.1' symfony-version: ^5.4 - php: '8.1' - stability: dev symfony-version: ^6.0 fail-fast: false @@ -91,27 +92,25 @@ jobs: extensions: intl, bcmath, curl, openssl, mbstring ini-values: memory_limit=-1 - - name: "Configure Composer minimum stability" - if: matrix.stability - run: "composer config minimum-stability ${{ matrix.stability }}" - - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v1" with: path: ${{ steps.composercache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-composer- - - name: Configure Composer minimum stability - if: matrix.stability - run: composer config minimum-stability ${{ matrix.stability }} + - name: Update Symfony version if: matrix.symfony-version != '' run: composer require --no-update "symfony/framework-bundle:${{ matrix.symfony-version }}" + - name: Update project dependencies run: | composer global require --no-progress --no-scripts --no-plugins symfony/flex composer update --no-interaction --no-progress --ansi + - name: Run PHPUnit tests + env: + USE_NEW_SYMFONY_SECURITY: matrix.bc != 'true' run: | mkdir -p build/logs/phpunit vendor/bin/phpunit --log-junit build/logs/phpunit/junit.xml diff --git a/DependencyInjection/Security/Factory/OAuthAuthenticatorFactory.php b/DependencyInjection/Security/Factory/OAuthAuthenticatorFactory.php index be2698489..487a618f1 100644 --- a/DependencyInjection/Security/Factory/OAuthAuthenticatorFactory.php +++ b/DependencyInjection/Security/Factory/OAuthAuthenticatorFactory.php @@ -67,6 +67,7 @@ public function createAuthenticator( ->addArgument($config['resource_owners']) ->addArgument(new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config))) ->addArgument(new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config))) + ->addArgument(new Reference('http_kernel')) ->addArgument(array_intersect_key($config, $this->options)) ; diff --git a/Security/Http/Authenticator/OAuthAuthenticator.php b/Security/Http/Authenticator/OAuthAuthenticator.php index 346707561..93de00d35 100644 --- a/Security/Http/Authenticator/OAuthAuthenticator.php +++ b/Security/Http/Authenticator/OAuthAuthenticator.php @@ -20,6 +20,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AuthenticationServiceException; @@ -44,6 +45,7 @@ final class OAuthAuthenticator implements AuthenticatorInterface, Authentication private ResourceOwnerMapInterface $resourceOwnerMap; private AuthenticationSuccessHandlerInterface $successHandler; private AuthenticationFailureHandlerInterface $failureHandler; + private HttpKernelInterface $httpKernel; /** * @var string[] @@ -63,6 +65,7 @@ public function __construct( array $checkPaths, AuthenticationSuccessHandlerInterface $successHandler, AuthenticationFailureHandlerInterface $failureHandler, + HttpKernelInterface $kernel, array $options ) { $this->failureHandler = $failureHandler; @@ -71,6 +74,7 @@ public function __construct( $this->resourceOwnerMap = $resourceOwnerMap; $this->userProvider = $userProvider; $this->httpUtils = $httpUtils; + $this->httpKernel = $kernel; $this->options = $options; } @@ -87,6 +91,21 @@ public function supports(Request $request): bool public function start(Request $request, AuthenticationException $authException = null): Response { + if ($this->options['use_forward'] ?? false) { + $subRequest = $this->httpUtils->createRequest($request, $this->options['login_path']); + + /** @var \ArrayIterator $iterator */ + $iterator = $request->query->getIterator(); + $subRequest->query->add($iterator->getArrayCopy()); + + $response = $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + if (200 === $response->getStatusCode()) { + $response->headers->set('X-Status-Code', '401'); + } + + return $response; + } + return new RedirectResponse($this->httpUtils->generateUri($request, $this->options['login_path'])); } diff --git a/Security/Http/EntryPoint/OAuthEntryPoint.php b/Security/Http/EntryPoint/OAuthEntryPoint.php index 1bb5a9957..ee41f9984 100644 --- a/Security/Http/EntryPoint/OAuthEntryPoint.php +++ b/Security/Http/EntryPoint/OAuthEntryPoint.php @@ -12,6 +12,7 @@ namespace HWI\Bundle\OAuthBundle\Security\Http\EntryPoint; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; @@ -43,7 +44,7 @@ public function __construct(HttpKernelInterface $kernel, HttpUtils $httpUtils, s /** * {@inheritdoc} */ - public function start(Request $request, AuthenticationException $authException = null) + public function start(Request $request, AuthenticationException $authException = null): Response { if ($this->useForward) { $subRequest = $this->httpUtils->createRequest($request, $this->loginPath); diff --git a/Security/Http/Firewall/OAuthListener.php b/Security/Http/Firewall/OAuthListener.php index c0d0f9981..e822b91c4 100644 --- a/Security/Http/Firewall/OAuthListener.php +++ b/Security/Http/Firewall/OAuthListener.php @@ -17,6 +17,8 @@ use HWI\Bundle\OAuthBundle\Security\Http\ResourceOwnerMapInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener; @@ -61,7 +63,7 @@ public function requiresAuthentication(Request $request): bool } /** - * {@inheritdoc} + * @return TokenInterface|Response|null */ protected function attemptAuthentication(Request $request) { diff --git a/Tests/App/AppKernel.php b/Tests/App/AppKernel.php index 8efe501ea..536749fa2 100644 --- a/Tests/App/AppKernel.php +++ b/Tests/App/AppKernel.php @@ -43,8 +43,14 @@ public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(__DIR__.'/config/config.yaml'); - if (Kernel::VERSION_ID >= 50400) { - $loader->load(__DIR__.'/config/security_v5.yaml'); + if (Kernel::VERSION_ID >= 60000) { + $loader->load(__DIR__.'/config/security_v6.yaml'); + } elseif (Kernel::VERSION_ID >= 50100 && Kernel::VERSION_ID < 59999) { + if (0 === (int) getenv('USE_NEW_SYMFONY_SECURITY')) { + $loader->load(__DIR__.'/config/security_v5_bc.yaml'); + } else { + $loader->load(__DIR__.'/config/security_v5.yaml'); + } } else { $loader->load(__DIR__.'/config/security_v4.yaml'); } diff --git a/Tests/App/config/security_v5_bc.yaml b/Tests/App/config/security_v5_bc.yaml new file mode 100644 index 000000000..a0aa78d21 --- /dev/null +++ b/Tests/App/config/security_v5_bc.yaml @@ -0,0 +1,30 @@ +security: + password_hashers: + HWI\Bundle\OAuthBundle\Tests\Fixtures\User: sha512 + + providers: + HWI\Bundle\OAuthBundle\Tests\App\UserProvider: + id: HWI\Bundle\OAuthBundle\Tests\App\UserProvider + + enable_authenticator_manager: false + + firewalls: + login_area: + pattern: ^/(login$|connect|login_hwi) + anonymous: true + context: hwi_context + main: + pattern: ^/ + oauth: + resource_owners: + google: "/check-login/google" + login_path: /login + use_forward: false + failure_path: /login + oauth_user_provider: + service: HWI\Bundle\OAuthBundle\Tests\App\UserProvider + provider: HWI\Bundle\OAuthBundle\Tests\App\UserProvider + context: hwi_context + + access_control: + - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } diff --git a/Tests/App/config/security_v6.yaml b/Tests/App/config/security_v6.yaml new file mode 100644 index 000000000..6f78e4e99 --- /dev/null +++ b/Tests/App/config/security_v6.yaml @@ -0,0 +1,27 @@ +security: + password_hashers: + HWI\Bundle\OAuthBundle\Tests\Fixtures\User: sha512 + + providers: + HWI\Bundle\OAuthBundle\Tests\App\UserProvider: + id: HWI\Bundle\OAuthBundle\Tests\App\UserProvider + + enable_authenticator_manager: true + + firewalls: + main: + pattern: ^/ + oauth: + resource_owners: + google: "/check-login/google" + login_path: /login + use_forward: false + failure_path: /login + oauth_user_provider: + service: HWI\Bundle\OAuthBundle\Tests\App\UserProvider + provider: HWI\Bundle\OAuthBundle\Tests\App\UserProvider + context: hwi_context + + access_control: + - { path: '^/(login$|connect|login_hwi)', roles: PUBLIC_ACCESS } + - { path: ^/, roles: ROLE_USER } diff --git a/Tests/Security/Http/Authenticator/OAuthAuthenticatorTest.php b/Tests/Security/Http/Authenticator/OAuthAuthenticatorTest.php index 7ce374b24..3e1321c69 100644 --- a/Tests/Security/Http/Authenticator/OAuthAuthenticatorTest.php +++ b/Tests/Security/Http/Authenticator/OAuthAuthenticatorTest.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Core\User\User; @@ -69,6 +70,7 @@ public function testSupports(): void ['/a', '/b'], $this->getAuthenticationSuccessHandlerMock(), $this->getAuthenticationFailureHandlerMock(), + $this->createMock(HttpKernelInterface::class), [] ); @@ -150,6 +152,7 @@ public function testAuthenticate(): void [], $this->getAuthenticationSuccessHandlerMock(), $this->getAuthenticationFailureHandlerMock(), + $this->createMock(HttpKernelInterface::class), [] ); @@ -190,6 +193,7 @@ public function testOnAuthenticationSuccess(): void [], $successHandlerMock, $this->getAuthenticationFailureHandlerMock(), + $this->createMock(HttpKernelInterface::class), [] ); @@ -216,6 +220,7 @@ public function testOnAuthenticationFailure(): void [], $this->getAuthenticationSuccessHandlerMock(), $failureHandlerMock, + $this->createMock(HttpKernelInterface::class), [] );