diff --git a/src/bundle/Resources/config/security.yml b/src/bundle/Resources/config/security.yml index f92ce3cd..902c66bf 100644 --- a/src/bundle/Resources/config/security.yml +++ b/src/bundle/Resources/config/security.yml @@ -11,6 +11,8 @@ services: arguments: $headerName: '%ibexa.rest.authorization_header_name%' + Ibexa\Rest\Security\JWTTokenCreationRESTRequestMatcher: ~ + Ibexa\Rest\Server\Security\CsrfTokenManager: arguments: - '@?security.csrf.token_generator' diff --git a/src/lib/Security/AuthorizationHeaderRESTRequestMatcher.php b/src/lib/Security/AuthorizationHeaderRESTRequestMatcher.php index 13284042..0d518ce4 100644 --- a/src/lib/Security/AuthorizationHeaderRESTRequestMatcher.php +++ b/src/lib/Security/AuthorizationHeaderRESTRequestMatcher.php @@ -34,6 +34,7 @@ public function __construct( int $port = null ) { parent::__construct($path, $host, $methods, $ips, $attributes, $schemes, $port); + $this->headerName = $headerName; } @@ -43,10 +44,7 @@ public function matches(Request $request): bool return false; } - if ( - $request->attributes->get('_route') === 'ibexa.rest.create_token' - || !empty($request->headers->get($this->headerName ?? 'Authorization')) - ) { + if (!empty($request->headers->get($this->headerName ?? 'Authorization'))) { return parent::matches($request); } diff --git a/src/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriber.php b/src/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriber.php index 36f58cd8..02918392 100644 --- a/src/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriber.php +++ b/src/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriber.php @@ -8,8 +8,6 @@ namespace Ibexa\Rest\Security\EventListener\JWT; -use Ibexa\Contracts\Core\Repository\PermissionResolver; -use Ibexa\Core\MVC\Symfony\Security\UserInterface as IbexaUser; use Ibexa\Rest\Server\Exceptions\BadResponseException; use Lexik\Bundle\JWTAuthenticationBundle\Event\AuthenticationSuccessEvent; use Lexik\Bundle\JWTAuthenticationBundle\Events; @@ -19,7 +17,6 @@ final readonly class AuthenticationSuccessSubscriber implements EventSubscriberInterface { public function __construct( - private PermissionResolver $permissionResolver, private RequestStack $requestStack, ) { } @@ -42,11 +39,6 @@ public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void return; } - $user = $event->getUser(); - if ($user instanceof IbexaUser) { - $this->permissionResolver->setCurrentUserReference($user->getAPIUser()); - } - $this->normalizeResponseToRest($event); } diff --git a/src/lib/Security/JWTTokenCreationRESTRequestMatcher.php b/src/lib/Security/JWTTokenCreationRESTRequestMatcher.php new file mode 100644 index 00000000..b9b52ffc --- /dev/null +++ b/src/lib/Security/JWTTokenCreationRESTRequestMatcher.php @@ -0,0 +1,33 @@ +attributes->get('is_rest_request', false) !== true) { + return false; + } + + if ($request->attributes->get('_route') === 'ibexa.rest.create_token') { + return parent::matches($request); + } + + return false; + } +} diff --git a/tests/contracts/Security/AuthorizationHeaderRESTRequestMatcherTest.php b/tests/contracts/Security/AuthorizationHeaderRESTRequestMatcherTest.php index 72ffc98f..ace53899 100644 --- a/tests/contracts/Security/AuthorizationHeaderRESTRequestMatcherTest.php +++ b/tests/contracts/Security/AuthorizationHeaderRESTRequestMatcherTest.php @@ -58,18 +58,6 @@ public function testMatchesRestRequestsWithCustomHeader(): void self::assertTrue($matcher->matches($request)); } - public function testMatchesRestJwtCreationEndpoint(): void - { - $matcher = new AuthorizationHeaderRESTRequestMatcher(); - - $request = $this->createRequest([ - 'is_rest_request' => true, - '_route' => 'ibexa.rest.create_token', - ]); - - self::assertTrue($matcher->matches($request)); - } - /** * @param array $attributes * @param array|string> $server diff --git a/tests/contracts/Security/JWTTokenCreationRESTRequestMatcherTest.php b/tests/contracts/Security/JWTTokenCreationRESTRequestMatcherTest.php new file mode 100644 index 00000000..71256473 --- /dev/null +++ b/tests/contracts/Security/JWTTokenCreationRESTRequestMatcherTest.php @@ -0,0 +1,55 @@ +matches(new Request())); + } + + public function testDoesNotMatchRestRequestsWithoutHeader(): void + { + $matcher = new JWTTokenCreationRESTRequestMatcher(); + + $request = $this->createRequest([ + 'is_rest_request' => true, + ]); + + self::assertFalse($matcher->matches($request)); + } + + public function testMatchesRestJwtCreationEndpoint(): void + { + $matcher = new JWTTokenCreationRESTRequestMatcher(); + + $request = $this->createRequest([ + 'is_rest_request' => true, + '_route' => 'ibexa.rest.create_token', + ]); + + self::assertTrue($matcher->matches($request)); + } + + /** + * @param array $attributes + * @param array|string> $server + */ + private function createRequest(array $attributes = [], array $server = []): Request + { + return new Request([], [], $attributes, [], [], $server); + } +} diff --git a/tests/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriberTest.php b/tests/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriberTest.php index 16f13c0d..9d0f601d 100644 --- a/tests/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriberTest.php +++ b/tests/lib/Security/EventListener/JWT/AuthenticationSuccessSubscriberTest.php @@ -8,7 +8,6 @@ namespace Ibexa\Tests\Rest\Security\EventListener\JWT; -use Ibexa\Contracts\Core\Repository\PermissionResolver; use Ibexa\Core\MVC\Symfony\Security\User; use Ibexa\Core\Repository\Values\User\User as ApiUser; use Ibexa\Rest\Security\EventListener\JWT\AuthenticationSuccessSubscriber; @@ -27,7 +26,6 @@ final class AuthenticationSuccessSubscriberTest extends TestCase public function testGetSubscribedEvents(): void { $subscriber = new AuthenticationSuccessSubscriber( - $this->createMock(PermissionResolver::class), $this->getRequestStackMock() ); @@ -46,15 +44,9 @@ public function testOnAuthenticationSuccess( UserInterface $user, bool $isPermissionResolverInvoked ): void { - $permissionResolver = $this->createMock(PermissionResolver::class); - $permissionResolver - ->expects($isPermissionResolverInvoked === true ? self::once() : self::never()) - ->method('setCurrentUserReference'); - $event = new AuthenticationSuccessEvent(['token' => 'foo_token'], $user, new Response()); $subscriber = new AuthenticationSuccessSubscriber( - $permissionResolver, $this->getRequestStackMock() ); @@ -91,7 +83,6 @@ public function dataProviderForTestOnAuthenticationSuccess(): iterable public function testResponseIsMissingJwtToken(): void { $subscriber = new AuthenticationSuccessSubscriber( - $this->createMock(PermissionResolver::class), $this->getRequestStackMock() ); @@ -113,7 +104,6 @@ public function testResponseIsMissingJwtToken(): void public function testSkippingResponseNormalizingForNonRestRequest(): void { $subscriber = new AuthenticationSuccessSubscriber( - $this->createMock(PermissionResolver::class), $this->getRequestStackMock(false) );