From 4b0293c63e76c19f5b703a6f97c9cea70c0d3720 Mon Sep 17 00:00:00 2001 From: sidux Date: Tue, 28 Jan 2025 16:25:35 +0100 Subject: [PATCH] feat(security): add roles param to simplfy migration --- src/Attribute/Security.php | 10 ++++++---- src/Interceptor/Impl/SecurityInterceptor.php | 19 +++++++++++++++++-- .../Stub/Security/SecurityAnnotatedClass.php | 15 +++++++++++++++ tests/Interceptor/SecurityInterceptorTest.php | 18 ++++++++++++++++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/Attribute/Security.php b/src/Attribute/Security.php index fb133b2..0645e10 100644 --- a/src/Attribute/Security.php +++ b/src/Attribute/Security.php @@ -8,14 +8,16 @@ final class Security extends Attribute { /** - * @param array|string|null $handlers + * @param array|string|null $handlers + * @param array|null $roles * @param class-string<\RuntimeException> $exception */ public function __construct( - public readonly ?string $expression = null, + public readonly ?string $expression = null, public array|string|null $handlers = null, - public ?string $message = null, - public ?string $exception = null, + public ?string $message = null, + public ?string $exception = null, + public ?array $roles = null, ) { parent::__construct(); $this->setHandlers($handlers); diff --git a/src/Interceptor/Impl/SecurityInterceptor.php b/src/Interceptor/Impl/SecurityInterceptor.php index dd63b90..a7431a5 100644 --- a/src/Interceptor/Impl/SecurityInterceptor.php +++ b/src/Interceptor/Impl/SecurityInterceptor.php @@ -51,10 +51,25 @@ public function prefix(Instance $instance): Response return new Response(); } + $rolesExpressions = null; + if ($attribute->roles !== null) { + if ($attribute->expression !== null) { + throw new \RuntimeException('You cannot use both roles and expression in the Security attribute.'); + } + $rolesExpressions = array_map( + static fn (string $role) => "is_granted('{$role}')", + $attribute->roles + ); + } + $expression = $attribute->expression; if ($expression === null) { - $role = $this->guessRoleName($instance); - $expression = "is_granted('{$role}')"; + if ($rolesExpressions !== null) { + $expression = implode(' or ', $rolesExpressions); + } else { + $role = $this->guessRoleName($instance); + $expression = "is_granted('{$role}')"; + } } $handlers = $this->getHandlers(SecurityHandler::class, $attribute); foreach ($handlers as $handler) { diff --git a/tests/Double/Stub/Security/SecurityAnnotatedClass.php b/tests/Double/Stub/Security/SecurityAnnotatedClass.php index 0e1cdfb..63d2c8b 100644 --- a/tests/Double/Stub/Security/SecurityAnnotatedClass.php +++ b/tests/Double/Stub/Security/SecurityAnnotatedClass.php @@ -61,4 +61,19 @@ public function accessDeniedWithMessage(): void public function accessDeniedWithException(): void { } + + #[Security(roles: ['ROLE_1'])] + public function rolesParamOne(): void + { + } + + #[Security(roles: ['ROLE_3', 'ROLE_1'])] + public function rolesParamMultiple(): void + { + } + + #[Security("is_granted('ROLE_1')", roles: ['ROLE_1', 'ROLE_2'])] + public function conflict(): void + { + } } diff --git a/tests/Interceptor/SecurityInterceptorTest.php b/tests/Interceptor/SecurityInterceptorTest.php index ea43d67..b2af623 100644 --- a/tests/Interceptor/SecurityInterceptorTest.php +++ b/tests/Interceptor/SecurityInterceptorTest.php @@ -111,4 +111,22 @@ public function testAccessDeniedWithException(): void $this->expectExceptionMessage('Invalid argument.'); $this->proxy->accessDeniedWithException(); } + + public function testWithRolesParamOne(): void + { + $this->proxy->rolesParamOne(); + $this->assertSame(['ROLE_1'], $this->handler->attributes); + } + + public function testWithRolesParamMultiple(): void + { + $this->proxy->rolesParamMultiple(); + $this->assertSame(['ROLE_3', 'ROLE_1'], $this->handler->attributes); + } + + public function testWithRolesAndExpressionShouldThrow(): void + { + $this->expectException(\RuntimeException::class); + $this->proxy->conflict(); + } }