From b68c9dc947cb900fa0586026401bb96caa33d32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Fri, 13 Oct 2023 18:11:38 +0200 Subject: [PATCH 1/6] Set missing reference for applying-policy-scopes The reference was not converted into a link in https://book.cakephp.org/authorization/2/en/policies.html#applying-policies --- docs/en/component.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/component.rst b/docs/en/component.rst index cfa8ab81..21db93ef 100644 --- a/docs/en/component.rst +++ b/docs/en/component.rst @@ -74,6 +74,8 @@ authorization for anonymous users. Both the ``can()`` and ``authorize()`` suppor anonymous users. Your policies can expect to get ``null`` for the 'user' parameter when the user is not logged in. +.. _applying-policy-scopes: + Applying Policy Scopes ====================== From 38373630d19bb3ffbcda7d5d76a94619a8cd18fa Mon Sep 17 00:00:00 2001 From: Kevin Pfeifer Date: Fri, 10 Nov 2023 14:04:15 +0100 Subject: [PATCH 2/6] fix stan --- src/Identity.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Identity.php b/src/Identity.php index 86ca8d98..d1e42bf6 100644 --- a/src/Identity.php +++ b/src/Identity.php @@ -48,7 +48,7 @@ public function __construct(AuthorizationServiceInterface $service, AuthenIdenti /** * Get the primary key/id field for the identity. * - * @return string|int|null + * @return array|string|int|null */ public function getIdentifier() { From 2e832e9aed705b22657dac3949bee2a352ea3546 Mon Sep 17 00:00:00 2001 From: Jamison Bryant Date: Wed, 8 Nov 2023 13:12:16 -0500 Subject: [PATCH 3/6] Import the files from the 3.x branch --- src/AuthorizationService.php | 10 ++++ src/Policy/BeforeScopeInterface.php | 39 +++++++++++++ tests/TestCase/AuthorizationServiceTest.php | 65 +++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 src/Policy/BeforeScopeInterface.php diff --git a/src/AuthorizationService.php b/src/AuthorizationService.php index db7b331d..4f8988c8 100644 --- a/src/AuthorizationService.php +++ b/src/AuthorizationService.php @@ -18,6 +18,7 @@ use Authorization\Exception\Exception; use Authorization\Policy\BeforePolicyInterface; +use Authorization\Policy\BeforeScopeInterface; use Authorization\Policy\Exception\MissingMethodException; use Authorization\Policy\ResolverInterface; use Authorization\Policy\Result; @@ -120,6 +121,15 @@ public function applyScope(?IdentityInterface $user, string $action, $resource) { $this->authorizationChecked = true; $policy = $this->resolver->getPolicy($resource); + + if ($policy instanceof BeforeScopeInterface) { + $result = $policy->beforeScope($user, $resource, $action); + + if ($result !== null) { + return $result; + } + } + $handler = $this->getScopeHandler($policy, $action); return $handler($user, $resource); diff --git a/src/Policy/BeforeScopeInterface.php b/src/Policy/BeforeScopeInterface.php new file mode 100644 index 00000000..8f46c4de --- /dev/null +++ b/src/Policy/BeforeScopeInterface.php @@ -0,0 +1,39 @@ +can($user, 'add', $entity); } + public function testBeforeScopeNonNull() + { + $entity = new Article(); + + $policy = $this->getMockBuilder(BeforeScopeInterface::class) + ->onlyMethods(['beforeScope']) + ->addMethods(['scopeIndex']) + ->getMock(); + + $policy->expects($this->once()) + ->method('beforeScope') + ->with($this->isInstanceOf(IdentityDecorator::class), $entity, 'index') + ->willReturn('foo'); + + $policy->expects($this->never()) + ->method('scopeIndex'); + + $resolver = new MapResolver([ + Article::class => $policy, + ]); + + $service = new AuthorizationService($resolver); + + $user = new IdentityDecorator($service, [ + 'role' => 'admin', + ]); + + $result = $service->applyScope($user, 'index', $entity); + $this->assertEquals('foo', $result); + } + + public function testBeforeScopeNull() + { + $entity = new Article(); + + $policy = $this->getMockBuilder(BeforeScopeInterface::class) + ->onlyMethods(['beforeScope']) + ->addMethods(['scopeIndex']) + ->getMock(); + + $policy->expects($this->once()) + ->method('beforeScope') + ->with($this->isInstanceOf(IdentityDecorator::class), $entity, 'index') + ->willReturn(null); + + $policy->expects($this->once()) + ->method('scopeIndex') + ->with($this->isInstanceOf(IdentityDecorator::class), $entity) + ->willReturn('bar'); + + $resolver = new MapResolver([ + Article::class => $policy, + ]); + + $service = new AuthorizationService($resolver); + + $user = new IdentityDecorator($service, [ + 'role' => 'admin', + ]); + + $result = $service->applyScope($user, 'index', $entity); + $this->assertEquals('bar', $result); + } + public function testMissingMethod() { $entity = new Article(); From 419c8c120c79d7d2a831c27db930924f1a9098a5 Mon Sep 17 00:00:00 2001 From: Jamison Bryant Date: Thu, 9 Nov 2023 15:45:44 -0500 Subject: [PATCH 4/6] Add docs too --- docs/en/policies.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/en/policies.rst b/docs/en/policies.rst index 60445a7d..4155d9e7 100644 --- a/docs/en/policies.rst +++ b/docs/en/policies.rst @@ -131,7 +131,29 @@ Before hooks are expected to return one of three values: - ``false`` The user is not allowed to proceed with the action. - ``null`` The before hook did not make a decision, and the authorization method will be invoked. - + +Scope Pre-conditions +==================== + +Like policies, scopes can also define pre-conditions. These are useful when you +want to apply common conditions to all scopes in a policy. To use pre-conditions +on scopes you need to implement the ``BeforeScopeInterface`` in your scope policy:: + + namespace App\Policy; + + use Authorization\Policy\BeforeScopeInterface; + + class ArticlesTablePolicy implements BeforeScopeInterface + { + public function beforeScope($user, $query, $action) + { + if ($user->getOriginalData()->is_trial_user) { + return $query->where(['Articles.is_paid_only' => false]); + } + // fall through + } + } + Applying Policies ----------------- See :ref:`applying-policy-scopes` for how to apply policies in your controller actions. From c1bf2440c45a64a1a55dc1d820d989bb06e8734d Mon Sep 17 00:00:00 2001 From: Jamison Bryant Date: Thu, 9 Nov 2023 15:48:45 -0500 Subject: [PATCH 5/6] Add final paragraph --- docs/en/policies.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/en/policies.rst b/docs/en/policies.rst index 4155d9e7..3a97316d 100644 --- a/docs/en/policies.rst +++ b/docs/en/policies.rst @@ -154,6 +154,9 @@ on scopes you need to implement the ``BeforeScopeInterface`` in your scope polic } } +Before scope hooks are expected to return the modified resource object, or if +``null`` is returned then the scope method will be invoked as normal. + Applying Policies ----------------- See :ref:`applying-policy-scopes` for how to apply policies in your controller actions. From cfe53acf459874997d3f594ca09b7a8a56a40c50 Mon Sep 17 00:00:00 2001 From: Jamison Bryant Date: Fri, 10 Nov 2023 11:53:06 -0500 Subject: [PATCH 6/6] Fix version number Co-authored-by: ADmad --- src/Policy/BeforeScopeInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Policy/BeforeScopeInterface.php b/src/Policy/BeforeScopeInterface.php index 8f46c4de..acf61a2b 100644 --- a/src/Policy/BeforeScopeInterface.php +++ b/src/Policy/BeforeScopeInterface.php @@ -11,7 +11,7 @@ * * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) * @link https://cakephp.org CakePHP(tm) Project - * @since 3.0.0 + * @since 2.4.0 * @license https://opensource.org/licenses/mit-license.php MIT License */ namespace Authorization\Policy;