From 9542fbc59b07f79d39c3a5048d6c9176b5c25a31 Mon Sep 17 00:00:00 2001 From: "Eric Richer eric.richer@vistoconsulting.com" Date: Fri, 12 Jul 2024 09:32:47 -0400 Subject: [PATCH 1/3] Added matchIdentityRoles to RoleService Signed-off-by: Eric Richer eric.richer@vistoconsulting.com --- src/Service/RoleService.php | 45 +++++++ src/Service/RoleServiceInterface.php | 8 ++ test/Service/RoleServiceTest.php | 187 ++++++++++++++++++++++++++- 3 files changed, 239 insertions(+), 1 deletion(-) diff --git a/src/Service/RoleService.php b/src/Service/RoleService.php index 098f6ee..77d322d 100644 --- a/src/Service/RoleService.php +++ b/src/Service/RoleService.php @@ -81,6 +81,36 @@ public function getIdentityRoles(IdentityInterface $identity = null, mixed $cont return $this->convertRoles($identity->getRoles()); } + /** + * Check if the given roles match one of the identity's roles + * @param string[]|RoleInterface[] $roles + * @param IdentityInterface|null $identity + * @return bool + */ + public function matchIdentityRoles(array $roles, IdentityInterface $identity = null): bool + { + // Get the roles for the identity + $identityRoles = $this->getIdentityRoles($identity); + + // No roles + if (empty($identityRoles)) { + return false; + } + + $roleNames = []; + + foreach ($roles as $role) { + $roleNames[] = $role instanceof RoleInterface ? $role->getName() : $role; + } + + foreach ($this->flattenRoles($identityRoles) as $role) { + $a[] = $role->getName(); + } + $identityRoles = $a; + + return count(array_intersect($roleNames, $identityRoles)) > 0; + } + /** * Convert the roles (potentially strings) to concrete RoleInterface objects using role provider * @@ -110,4 +140,19 @@ private function convertRoles(iterable $roles): iterable return array_merge($collectedRoles, $this->roleProvider->getRoles($toCollect)); } + + /** + * @param RoleInterface[] $roles + * @return \Generator + */ + private function flattenRoles(array $roles): \Generator + { + foreach ($roles as $role) { + yield $role; + + if ($role->hasChildren()) { + yield from $this->flattenRoles($role->getChildren()); + } + } + } } diff --git a/src/Service/RoleServiceInterface.php b/src/Service/RoleServiceInterface.php index d3773e4..3568173 100644 --- a/src/Service/RoleServiceInterface.php +++ b/src/Service/RoleServiceInterface.php @@ -48,4 +48,12 @@ public function getIdentityRoles(IdentityInterface $identity = null, mixed $cont */ public function getIdentity(): ?IdentityInterface; + /** + * Check if the given roles match one of the identity's roles + * @param RoleInterface[] $roles + * @param IdentityInterface|null $identity + * @return bool + */ + public function matchIdentityRoles(array $roles, IdentityInterface $identity = null): bool; + } diff --git a/test/Service/RoleServiceTest.php b/test/Service/RoleServiceTest.php index fc3cdf0..d43968b 100644 --- a/test/Service/RoleServiceTest.php +++ b/test/Service/RoleServiceTest.php @@ -40,6 +40,156 @@ class RoleServiceTest extends TestCase { use ProphecyTrait; + public static function roleProvider(): array + { + return [ + // No identity role + [ + 'rolesConfig' => [], + 'identityRoles' => [], + 'rolesToCheck' => [ + 'member', + ], + 'doesMatch' => false, + ], + + // Simple + [ + 'rolesConfig' => [ + 'member' => [ + 'children' => ['guest'], + ], + 'guest', + ], + 'identityRoles' => [ + 'guest', + ], + 'rolesToCheck' => [ + 'member', + ], + 'doesMatch' => false, + ], + [ + 'rolesConfig' => [ + 'member' => [ + 'children' => ['guest'], + ], + 'guest', + ], + 'identityRoles' => [ + 'member', + ], + 'rolesToCheck' => [ + 'member', + ], + 'doesMatch' => true, + ], + + // Complex role inheritance + [ + 'rolesConfig' => [ + 'admin' => [ + 'children' => ['moderator'], + ], + 'moderator' => [ + 'children' => ['member'], + ], + 'member' => [ + 'children' => ['guest'], + ], + 'guest', + ], + 'identityRoles' => [ + 'member', + 'moderator', + ], + 'rolesToCheck' => [ + 'admin', + ], + 'doesMatch' => false, + ], + [ + 'rolesConfig' => [ + 'admin' => [ + 'children' => ['moderator'], + ], + 'moderator' => [ + 'children' => ['member'], + ], + 'member' => [ + 'children' => ['guest'], + ], + 'guest', + ], + 'identityRoles' => [ + 'member', + 'admin', + ], + 'rolesToCheck' => [ + 'moderator', + ], + 'doesMatch' => true, + ], + + // Complex role inheritance and multiple check + [ + 'rolesConfig' => [ + 'sysadmin' => [ + 'children' => ['siteadmin', 'admin'], + ], + 'siteadmin', + 'admin' => [ + 'children' => ['moderator'], + ], + 'moderator' => [ + 'children' => ['member'], + ], + 'member' => [ + 'children' => ['guest'], + ], + 'guest', + ], + 'identityRoles' => [ + 'member', + 'moderator', + ], + 'rolesToCheck' => [ + 'admin', + 'sysadmin', + ], + 'doesMatch' => false, + ], + [ + 'rolesConfig' => [ + 'sysadmin' => [ + 'children' => ['siteadmin', 'admin'], + ], + 'siteadmin', + 'admin' => [ + 'children' => ['moderator'], + ], + 'moderator' => [ + 'children' => ['member'], + ], + 'member' => [ + 'children' => ['guest'], + ], + 'guest', + ], + 'identityRoles' => [ + 'moderator', + 'admin', + ], + 'rolesToCheck' => [ + 'sysadmin', + 'siteadmin', + 'member', + ], + 'doesMatch' => true, + ], + ]; + } + public function testReturnGuestRoleIfNoIdentityIsGiven(): void { $identityProvider = $this->createMock(IdentityProviderInterface::class); @@ -59,7 +209,6 @@ public function testReturnGuestRoleIfGuestIdentityIsGiven(): void $identityProvider = $this->createMock(IdentityProviderInterface::class); $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); - $result = $roleService->getIdentityRoles($identity); $this->assertCount(1, $result); @@ -128,4 +277,40 @@ public function testGetIdentity() $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); $this->assertEquals($identity, $roleService->getIdentity()); } + + /** + * @dataProvider roleProvider + */ + public function testMatchIdentityRoles( + array $rolesConfig, + array $identityRoles, + array $rolesToCheck, + $doesMatch + ): void { + $identity = $this->createMock(IdentityInterface::class); + $identity->expects($this->once())->method('getRoles')->willReturn($identityRoles); + + $identityProvider = $this->createMock(IdentityProviderInterface::class); + + $roleService = new RoleService($identityProvider, new InMemoryRoleProvider($rolesConfig), 'guest'); + $this->assertEquals($doesMatch, $roleService->matchIdentityRoles($rolesToCheck, $identity)); + } + + /** + * @dataProvider roleProvider + */ + public function testMatchIdentityRolesWithNullIdentity( + array $rolesConfig, + array $identityRoles, + array $rolesToCheck, + $doesMatch + ): void { + $identity = $this->createMock(IdentityInterface::class); + $identity->expects($this->once())->method('getRoles')->willReturn($identityRoles); + + $identityProvider = $this->createMock(IdentityProviderInterface::class); + $identityProvider->expects($this->once())->method('getIdentity')->willReturn($identity); + $roleService = new RoleService($identityProvider, new InMemoryRoleProvider($rolesConfig), 'guest'); + $this->assertEquals($doesMatch, $roleService->matchIdentityRoles($rolesToCheck, null)); + } } From 65d538b3ec7d8ab0d2eff0f67b6fb97d932e41ba Mon Sep 17 00:00:00 2001 From: "Eric Richer eric.richer@vistoconsulting.com" Date: Fri, 12 Jul 2024 13:57:33 -0400 Subject: [PATCH 2/3] Added AuthorizationServiceAware trait and delegator Signed-off-by: Eric Richer eric.richer@vistoconsulting.com --- .../AuthorizationServiceAwareInterface.php | 35 ++++++++++ .../AuthorizationServiceAwareTrait.php | 51 ++++++++++++++ .../AuthorizationServiceDelegatorFactory.php | 48 +++++++++++++ .../AuthorizationServiceAwareTraitTest.php | 37 ++++++++++ ...thorizationServiceDelegatorFactoryTest.php | 68 +++++++++++++++++++ .../DummyAuthorizationServiceClass.php | 31 +++++++++ 6 files changed, 270 insertions(+) create mode 100644 src/Service/AuthorizationServiceAwareInterface.php create mode 100644 src/Service/AuthorizationServiceAwareTrait.php create mode 100644 src/Service/AuthorizationServiceDelegatorFactory.php create mode 100644 test/Service/AuthorizationServiceAwareTraitTest.php create mode 100644 test/Service/AuthorizationServiceDelegatorFactoryTest.php create mode 100644 test/Service/DummyAuthorizationServiceClass.php diff --git a/src/Service/AuthorizationServiceAwareInterface.php b/src/Service/AuthorizationServiceAwareInterface.php new file mode 100644 index 0000000..269e3dc --- /dev/null +++ b/src/Service/AuthorizationServiceAwareInterface.php @@ -0,0 +1,35 @@ + + * + */ + +interface AuthorizationServiceAwareInterface +{ + /** + * Set the AuthorizationService + * + * @param AuthorizationServiceInterface $authorizationService + * @return void + */ + public function setAuthorizationService(AuthorizationServiceInterface $authorizationService): void; +} diff --git a/src/Service/AuthorizationServiceAwareTrait.php b/src/Service/AuthorizationServiceAwareTrait.php new file mode 100644 index 0000000..9edead1 --- /dev/null +++ b/src/Service/AuthorizationServiceAwareTrait.php @@ -0,0 +1,51 @@ + + * + */ + +trait AuthorizationServiceAwareTrait +{ + /** + * @var AuthorizationServiceInterface|null + */ + protected ?AuthorizationServiceInterface $authorizationService = null; + + /** + * Set the AuthorizationService + * @param AuthorizationServiceInterface $authorizationService + * @return void + */ + public function setAuthorizationService(AuthorizationServiceInterface $authorizationService):void + { + $this->authorizationService = $authorizationService; + } + + /** + * Get the AuthorizationService + * @return AuthorizationServiceInterface|null + */ + public function getAuthorizationService(): ?AuthorizationServiceInterface + { + return $this->authorizationService; + } +} diff --git a/src/Service/AuthorizationServiceDelegatorFactory.php b/src/Service/AuthorizationServiceDelegatorFactory.php new file mode 100644 index 0000000..243959c --- /dev/null +++ b/src/Service/AuthorizationServiceDelegatorFactory.php @@ -0,0 +1,48 @@ + + * + */ + +use Laminas\ServiceManager\Exception\ServiceNotCreatedException; +use Laminas\ServiceManager\Factory\DelegatorFactoryInterface; +use Psr\Container\ContainerInterface; + +class AuthorizationServiceDelegatorFactory implements DelegatorFactoryInterface +{ + + /** + * @inheritDoc + */ + public function __invoke(ContainerInterface $container, $name, callable $callback, ?array $options = null): AuthorizationServiceAwareInterface + { + $instance = call_user_func($callback); + + if (! $instance instanceof AuthorizationServiceAwareInterface) { + throw new ServiceNotCreatedException("The service $name must implement Laminas\Authorization\Service\AuthorizationServiceAwareInterface"); + } + + $authorizationService = $container->get(AuthorizationServiceInterface::class); + $instance->setAuthorizationService($authorizationService); + return $instance; + } +} diff --git a/test/Service/AuthorizationServiceAwareTraitTest.php b/test/Service/AuthorizationServiceAwareTraitTest.php new file mode 100644 index 0000000..e080d04 --- /dev/null +++ b/test/Service/AuthorizationServiceAwareTraitTest.php @@ -0,0 +1,37 @@ + + */ + +use LmcRbac\Service\AuthorizationServiceInterface; +use PHPUnit\Framework\TestCase; + +class AuthorizationServiceAwareTraitTest extends TestCase +{ + public function testAuthorizationServiceAwareTrait() + { + $class = new DummyAuthorizationServiceClass(); + $authorizationService = $this->createMock(AuthorizationServiceInterface::class); + $class->setAuthorizationService($authorizationService); + $this->assertSame($authorizationService, $class->getAuthorizationService()); + } +} diff --git a/test/Service/AuthorizationServiceDelegatorFactoryTest.php b/test/Service/AuthorizationServiceDelegatorFactoryTest.php new file mode 100644 index 0000000..e5a3fa7 --- /dev/null +++ b/test/Service/AuthorizationServiceDelegatorFactoryTest.php @@ -0,0 +1,68 @@ + + */ + +use Laminas\ServiceManager\Exception\ServiceNotCreatedException; +use Laminas\ServiceManager\ServiceManager; +use LmcRbac\Service\AuthorizationServiceDelegatorFactory; +use LmcRbac\Service\AuthorizationServiceInterface; +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use Psr\Container\ContainerInterface; + +class AuthorizationServiceDelegatorFactoryTest extends TestCase +{ + use ProphecyTrait; + + public function testDelegatorFactory(): void + { + $authorizationService = $this->createMock(AuthorizationServiceInterface::class); + $container = $this->prophesize(ContainerInterface::class); + + $callback = function () { + return new DummyAuthorizationServiceClass(); + }; + + $container->get(AuthorizationServiceInterface::class)->willReturn($authorizationService)->shouldBeCalled(); + + $delegatorFactory = new AuthorizationServiceDelegatorFactory(); + $instance = $delegatorFactory($container->reveal(), DummyAuthorizationServiceClass::class, $callback ); + $this->assertInstanceOf(DummyAuthorizationServiceClass::class, $instance); + } + + public function testDelegatorFactoryException(): void + { + $authorizationService = $this->createMock(AuthorizationServiceInterface::class); + $container = $this->prophesize(ContainerInterface::class); + + $callback = function () { + return new \StdClass(); + }; + $delegatorFactory = new AuthorizationServiceDelegatorFactory(); + + $container->get(AuthorizationServiceInterface::class)->willReturn($authorizationService)->shouldNotBeCalled(); + $this->expectException(ServiceNotCreatedException::class); + + $instance = $delegatorFactory($container->reveal(), DummyAuthorizationServiceClass::class, $callback ); + } +} diff --git a/test/Service/DummyAuthorizationServiceClass.php b/test/Service/DummyAuthorizationServiceClass.php new file mode 100644 index 0000000..4cb589a --- /dev/null +++ b/test/Service/DummyAuthorizationServiceClass.php @@ -0,0 +1,31 @@ + + */ + +use LmcRbac\Service\AuthorizationServiceAwareInterface; +use LmcRbac\Service\AuthorizationServiceAwareTrait; + +class DummyAuthorizationServiceClass implements AuthorizationServiceAwareInterface +{ + use AuthorizationServiceAwareTrait; +} From 74ee45caa3d391dfe297854e1c1491e887a303c0 Mon Sep 17 00:00:00 2001 From: "Eric Richer eric.richer@vistoconsulting.com" Date: Mon, 5 Aug 2024 12:05:03 -0400 Subject: [PATCH 3/3] Removed AuthenticationIdentityProvider to make LmcRbac unopiniated on how identities are provided. Roll back isGranted methods to use identities Signed-off-by: Eric Richer eric.richer@vistoconsulting.com --- composer.json | 1 - src/ConfigProvider.php | 1 - .../AuthenticationIdentityProvider.php | 23 -- .../AuthenticationIdentityProviderFactory.php | 26 --- src/Service/AuthorizationService.php | 7 +- src/Service/AuthorizationServiceInterface.php | 4 +- src/Service/RoleService.php | 66 +----- src/Service/RoleServiceFactory.php | 6 +- src/Service/RoleServiceInterface.php | 15 -- test/ConfigProviderTest.php | 1 - test/Container/RoleServiceFactoryTest.php | 2 +- ...henticationIdentityProviderFactoryTest.php | 44 ---- .../AuthenticationIdentityProviderTest.php | 53 ----- test/Service/AuthorizationServiceTest.php | 35 ++- test/Service/RoleServiceTest.php | 217 +----------------- 15 files changed, 34 insertions(+), 467 deletions(-) delete mode 100644 src/Identity/AuthenticationIdentityProvider.php delete mode 100644 src/Identity/AuthenticationIdentityProviderFactory.php delete mode 100644 test/Identity/AuthenticationIdentityProviderFactoryTest.php delete mode 100644 test/Identity/AuthenticationIdentityProviderTest.php diff --git a/composer.json b/composer.json index 1e40b1b..009a3e9 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,6 @@ ], "require": { "php": "^8.1 || ^8.2 || ^8.3", - "laminas/laminas-authentication": "^2.0", "laminas/laminas-servicemanager": "^3.3", "laminas/laminas-stdlib": "^3.1", "doctrine/persistence": "^2.0 || ^3.0" diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 5dc3f1b..82303d2 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -47,7 +47,6 @@ public function getDependencyConfig(): array \LmcRbac\Options\ModuleOptions::class => \LmcRbac\Options\ModuleOptionsFactory::class, \LmcRbac\Role\InMemoryRoleProvider::class => \LmcRbac\Role\InMemoryRoleProviderFactory::class, \LmcRbac\Role\ObjectRepositoryRoleProvider::class => \LmcRbac\Role\ObjectRepositoryRoleProviderFactory::class, - \LmcRbac\Identity\AuthenticationIdentityProvider::class => \LmcRbac\Identity\AuthenticationIdentityProviderFactory::class, \LmcRbac\Service\AuthorizationServiceInterface::class => \LmcRbac\Service\AuthorizationServiceFactory::class, \LmcRbac\Service\RoleServiceInterface::class => \LmcRbac\Service\RoleServiceFactory::class, \LmcRbac\Rbac::class => \Laminas\ServiceManager\Factory\InvokableFactory::class, diff --git a/src/Identity/AuthenticationIdentityProvider.php b/src/Identity/AuthenticationIdentityProvider.php deleted file mode 100644 index 62c4e1d..0000000 --- a/src/Identity/AuthenticationIdentityProvider.php +++ /dev/null @@ -1,23 +0,0 @@ -authenticationService->getIdentity(); - } -} diff --git a/src/Identity/AuthenticationIdentityProviderFactory.php b/src/Identity/AuthenticationIdentityProviderFactory.php deleted file mode 100644 index 8096845..0000000 --- a/src/Identity/AuthenticationIdentityProviderFactory.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class AuthenticationIdentityProviderFactory implements FactoryInterface -{ - /** - * @inheritDoc - */ - public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null): AuthenticationIdentityProvider - { - // Get the Authentication provider - return new AuthenticationIdentityProvider($container->get(AuthenticationService::class)); - - } -} diff --git a/src/Service/AuthorizationService.php b/src/Service/AuthorizationService.php index f946293..2ddcbaa 100644 --- a/src/Service/AuthorizationService.php +++ b/src/Service/AuthorizationService.php @@ -23,6 +23,7 @@ use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Assertion\AssertionSet; +use LmcRbac\Identity\IdentityInterface; use LmcRbac\Permission\PermissionInterface; use LmcRbac\RbacInterface; @@ -58,9 +59,9 @@ public function __construct( $this->assertions = $assertions; } - public function isGranted(string|PermissionInterface $permission, mixed $context = null): bool + public function isGranted(IdentityInterface|null $identity, string|PermissionInterface $permission, mixed $context = null): bool { - $roles = $this->roleService->getIdentityRoles(null, $context); + $roles = $this->roleService->getIdentityRoles($identity, $context); if (empty($roles)) { return false; @@ -82,6 +83,6 @@ public function isGranted(string|PermissionInterface $permission, mixed $context $assertionSet = new AssertionSet($this->assertionPluginManager, $permissionAssertions); - return $assertionSet->assert($permission, $this->roleService->getIdentity(), $context); + return $assertionSet->assert($permission, $identity, $context); } } diff --git a/src/Service/AuthorizationServiceInterface.php b/src/Service/AuthorizationServiceInterface.php index 383295f..bd1cd34 100644 --- a/src/Service/AuthorizationServiceInterface.php +++ b/src/Service/AuthorizationServiceInterface.php @@ -21,6 +21,7 @@ namespace LmcRbac\Service; +use LmcRbac\Identity\IdentityInterface; use LmcRbac\Permission\PermissionInterface; /** @@ -34,9 +35,10 @@ interface AuthorizationServiceInterface /** * Check if the permission is granted to the current identity * + * @param IdentityInterface|null $identity * @param PermissionInterface|string $permission * @param mixed|null $context * @return bool */ - public function isGranted(PermissionInterface|string $permission, mixed $context = null): bool; + public function isGranted(?IdentityInterface $identity, PermissionInterface|string $permission, mixed $context = null): bool; } diff --git a/src/Service/RoleService.php b/src/Service/RoleService.php index 77d322d..39d78c4 100644 --- a/src/Service/RoleService.php +++ b/src/Service/RoleService.php @@ -35,32 +35,18 @@ */ class RoleService implements RoleServiceInterface { - protected IdentityProviderInterface $identityProvider; - protected RoleProviderInterface $roleProvider; protected string $guestRole = ''; public function __construct( - IdentityProviderInterface $identityProvider, RoleProviderInterface $roleProvider, string $guestRole ) { - $this->identityProvider = $identityProvider; $this->roleProvider = $roleProvider; $this->guestRole = $guestRole; } - /** - * Get the current identity from the identity provider - * - * @return IdentityInterface|null - */ - public function getIdentity(): ?IdentityInterface - { - return $this->identityProvider->getIdentity(); - } - /** * Get the identity roles from the current identity, applying some more logic * @@ -70,47 +56,14 @@ public function getIdentity(): ?IdentityInterface */ public function getIdentityRoles(IdentityInterface $identity = null, mixed $context = null): iterable { - // If no identity is provided, get it from the identity provider + // If no identity is provided, get the guest role if (null === $identity) { - $identity = $this->identityProvider->getIdentity(); - if (null === $identity) { - return $this->convertRoles([$this->guestRole]); - } + return $this->convertRoles([$this->guestRole]); } return $this->convertRoles($identity->getRoles()); } - /** - * Check if the given roles match one of the identity's roles - * @param string[]|RoleInterface[] $roles - * @param IdentityInterface|null $identity - * @return bool - */ - public function matchIdentityRoles(array $roles, IdentityInterface $identity = null): bool - { - // Get the roles for the identity - $identityRoles = $this->getIdentityRoles($identity); - - // No roles - if (empty($identityRoles)) { - return false; - } - - $roleNames = []; - - foreach ($roles as $role) { - $roleNames[] = $role instanceof RoleInterface ? $role->getName() : $role; - } - - foreach ($this->flattenRoles($identityRoles) as $role) { - $a[] = $role->getName(); - } - $identityRoles = $a; - - return count(array_intersect($roleNames, $identityRoles)) > 0; - } - /** * Convert the roles (potentially strings) to concrete RoleInterface objects using role provider * @@ -140,19 +93,4 @@ private function convertRoles(iterable $roles): iterable return array_merge($collectedRoles, $this->roleProvider->getRoles($toCollect)); } - - /** - * @param RoleInterface[] $roles - * @return \Generator - */ - private function flattenRoles(array $roles): \Generator - { - foreach ($roles as $role) { - yield $role; - - if ($role->hasChildren()) { - yield from $this->flattenRoles($role->getChildren()); - } - } - } } diff --git a/src/Service/RoleServiceFactory.php b/src/Service/RoleServiceFactory.php index 7b29a75..b19004f 100644 --- a/src/Service/RoleServiceFactory.php +++ b/src/Service/RoleServiceFactory.php @@ -22,9 +22,7 @@ namespace LmcRbac\Service; use Laminas\ServiceManager\Exception\ServiceNotCreatedException; -use LmcRbac\Identity\IdentityProviderInterface; use LmcRbac\Options\ModuleOptions; -use LmcRbac\Service\RoleService; use Psr\Container\ContainerInterface; /** @@ -48,8 +46,8 @@ public function __invoke(ContainerInterface $container): RoleService $roleProviderName = key($roleProvider); return new RoleService( - $container->get($moduleOptions->getIdentityProvider()), $container->get($roleProviderName), - $moduleOptions->getGuestRole()); + $moduleOptions->getGuestRole() + ); } } diff --git a/src/Service/RoleServiceInterface.php b/src/Service/RoleServiceInterface.php index 3568173..7d8585e 100644 --- a/src/Service/RoleServiceInterface.php +++ b/src/Service/RoleServiceInterface.php @@ -41,19 +41,4 @@ interface RoleServiceInterface */ public function getIdentityRoles(IdentityInterface $identity = null, mixed $context = null): iterable; - /** - * Get the current identity from the identity provider - * - * @return IdentityInterface|null - */ - public function getIdentity(): ?IdentityInterface; - - /** - * Check if the given roles match one of the identity's roles - * @param RoleInterface[] $roles - * @param IdentityInterface|null $identity - * @return bool - */ - public function matchIdentityRoles(array $roles, IdentityInterface $identity = null): bool; - } diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php index c0a84d4..25dfd3d 100644 --- a/test/ConfigProviderTest.php +++ b/test/ConfigProviderTest.php @@ -38,7 +38,6 @@ public function testProvidesExpectedConfiguration() \LmcRbac\Options\ModuleOptions::class => \LmcRbac\Options\ModuleOptionsFactory::class, \LmcRbac\Role\InMemoryRoleProvider::class => \LmcRbac\Role\InMemoryRoleProviderFactory::class, \LmcRbac\Role\ObjectRepositoryRoleProvider::class => \LmcRbac\Role\ObjectRepositoryRoleProviderFactory::class, - \LmcRbac\Identity\AuthenticationIdentityProvider::class => \LmcRbac\Identity\AuthenticationIdentityProviderFactory::class, \LmcRbac\Service\AuthorizationServiceInterface::class => \LmcRbac\Service\AuthorizationServiceFactory::class, \LmcRbac\Service\RoleServiceInterface::class => \LmcRbac\Service\RoleServiceFactory::class, \LmcRbac\Rbac::class => \Laminas\ServiceManager\Factory\InvokableFactory::class, diff --git a/test/Container/RoleServiceFactoryTest.php b/test/Container/RoleServiceFactoryTest.php index ffef337..5f023f6 100644 --- a/test/Container/RoleServiceFactoryTest.php +++ b/test/Container/RoleServiceFactoryTest.php @@ -50,7 +50,7 @@ public function testCanCreateRoleService(): void 'services' => [ ModuleOptions::class => $options, InMemoryRoleProvider::class => new InMemoryRoleProvider([]), - \LmcRbac\Identity\AuthenticationIdentityProvider::class => $this->createMock(IdentityProviderInterface::class), +// \LmcRbac\Identity\AuthenticationIdentityProvider::class => $this->createMock(IdentityProviderInterface::class), IdentityProviderInterface::class => $this->createMock(IdentityProviderInterface::class), ], ]); diff --git a/test/Identity/AuthenticationIdentityProviderFactoryTest.php b/test/Identity/AuthenticationIdentityProviderFactoryTest.php deleted file mode 100644 index a00d47c..0000000 --- a/test/Identity/AuthenticationIdentityProviderFactoryTest.php +++ /dev/null @@ -1,44 +0,0 @@ -setService( - 'Laminas\Authentication\AuthenticationService', - $this->createMock('Laminas\Authentication\AuthenticationService') - ); - - $factory = new AuthenticationIdentityProviderFactory(); - $authenticationProvider = $factory($serviceManager, 'LmcRbac\Identity\AuthenticationIdentityProvider'); - - $this->assertInstanceOf('LmcRbac\Identity\AuthenticationIdentityProvider', $authenticationProvider); - } -} diff --git a/test/Identity/AuthenticationIdentityProviderTest.php b/test/Identity/AuthenticationIdentityProviderTest.php deleted file mode 100644 index 949b3c7..0000000 --- a/test/Identity/AuthenticationIdentityProviderTest.php +++ /dev/null @@ -1,53 +0,0 @@ -authenticationService = $this->createMock('Laminas\Authentication\AuthenticationService'); - $this->identityProvider = new AuthenticationIdentityProvider($this->authenticationService); - } - - public function testCanReturnIdentity() - { - $identity = $this->createMock('LmcRbac\Identity\IdentityInterface'); - - $this->authenticationService->expects($this->once()) - ->method('getIdentity') - ->willReturn($identity); - - $this->assertSame($identity, $this->identityProvider->getIdentity()); - } -} diff --git a/test/Service/AuthorizationServiceTest.php b/test/Service/AuthorizationServiceTest.php index 3b1efd3..d2a89c0 100644 --- a/test/Service/AuthorizationServiceTest.php +++ b/test/Service/AuthorizationServiceTest.php @@ -22,8 +22,6 @@ namespace LmcRbacTest\Service; use Laminas\ServiceManager\ServiceManager; -use LmcRbac\Assertion\AssertionContainer; -use LmcRbac\Assertion\AssertionContainerInterface; use LmcRbac\Assertion\AssertionPluginManager; use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Assertion\AssertionSet; @@ -186,15 +184,18 @@ public function testGranted($role, $permission, $context, bool $isGranted, array ]; $identity = new Identity((array) $role); + /* $identityProvider = $this->createMock(IdentityProviderInterface::class); + $identityProvider->expects($this->any()) ->method('getIdentity') ->willReturn($identity); - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider($roleConfig), 'guest'); + */ + $roleService = new RoleService(new InMemoryRoleProvider($roleConfig), 'guest'); $assertionPluginManager = new AssertionPluginManager(new ServiceManager(), $assertionPluginConfig); $authorizationService = new AuthorizationService(new Rbac(), $roleService, $assertionPluginManager, $assertions); - $this->assertEquals($isGranted, $authorizationService->isGranted($permission, $context)); + $this->assertEquals($isGranted, $authorizationService->isGranted($identity, $permission, $context)); } public function testDoNotCallAssertionIfThePermissionIsNotGranted(): void @@ -210,7 +211,7 @@ public function testDoNotCallAssertionIfThePermissionIsNotGranted(): void $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $this->assertFalse($authorizationService->isGranted('foo', 'foo')); + $this->assertFalse($authorizationService->isGranted(null, 'foo', 'foo')); } public function testReturnsFalseForIdentityWithoutRoles(): void @@ -228,7 +229,7 @@ public function testReturnsFalseForIdentityWithoutRoles(): void $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $this->assertFalse($authorizationService->isGranted('foo', 'foo')); + $this->assertFalse($authorizationService->isGranted($identity, 'foo', 'foo')); } public function testReturnsTrueForIdentityWhenHasPermissionButNoAssertionsExists(): void @@ -247,7 +248,7 @@ public function testReturnsTrueForIdentityWhenHasPermissionButNoAssertionsExists $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $this->assertTrue($authorizationService->isGranted('foo', 'foo')); + $this->assertTrue($authorizationService->isGranted($identity, 'foo', 'foo')); } public function testUsesAssertionsAsInstances(): void @@ -258,7 +259,6 @@ public function testUsesAssertionsAsInstances(): void $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); $roleService->expects($this->once())->method('getIdentityRoles')->willreturn($identity->getRoles()); - $roleService->expects($this->once())->method('getIdentity')->willreturn($identity); $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); $rbac->expects($this->once())->method('isGranted')->willReturn(true); @@ -268,7 +268,7 @@ public function testUsesAssertionsAsInstances(): void $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, ['foo' => $assertion]); - $authorizationService->isGranted('foo', 'foo'); + $authorizationService->isGranted($identity, 'foo', 'foo'); $this->assertTrue($assertion->gotCalled()); } @@ -281,7 +281,6 @@ public function testUsesAssertionsAsStrings(): void $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); $roleService->expects($this->once())->method('getIdentityRoles')->willReturn($identity->getRoles()); - $roleService->expects($this->once())->method('getIdentity')->willreturn($identity); $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); $rbac->expects($this->once())->method('isGranted')->willReturn(true); @@ -291,7 +290,7 @@ public function testUsesAssertionsAsStrings(): void $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, ['foo' => 'fooFactory']); - $authorizationService->isGranted('foo', 'foo'); + $authorizationService->isGranted($identity, 'foo', 'foo'); $this->assertTrue($assertion->gotCalled()); } @@ -303,7 +302,6 @@ public function testUsesAssertionsAsCallable(): void $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); $roleService->expects($this->once())->method('getIdentityRoles')->willreturn($identity->getRoles()); - $roleService->expects($this->once())->method('getIdentity')->willreturn($identity); $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); $rbac->expects($this->once())->method('isGranted')->willReturn(true); @@ -326,7 +324,7 @@ public function testUsesAssertionsAsCallable(): void ] ); - $authorizationService->isGranted('foo', 'foo'); + $authorizationService->isGranted($identity, 'foo', 'foo'); $this->assertTrue($called); } @@ -338,7 +336,6 @@ public function testUsesAssertionsAsArrays(): void $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); $roleService->expects($this->once())->method('getIdentityRoles')->willreturn($identity->getRoles()); - $roleService->expects($this->once())->method('getIdentity')->willreturn($identity); $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); $rbac->expects($this->once())->method('isGranted')->willReturn(true); @@ -364,7 +361,7 @@ function ($permission, IdentityInterface $identity = null, $context = null) use ], ]); - $this->assertFalse($authorizationService->isGranted('foo', 'foo')); + $this->assertFalse($authorizationService->isGranted($identity, 'foo', 'foo')); $this->assertTrue($called1); $this->assertTrue($called2); @@ -379,14 +376,13 @@ public function testThrowExceptionForInvalidAssertion(): void $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); $roleService->expects($this->once())->method('getIdentityRoles')->willreturn([$role]); - $roleService->expects($this->once())->method('getIdentity')->willreturn(new Identity()); $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->disableOriginalConstructor()->getMock(); $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, ['foo' => new \stdClass()]); $this->expectException(InvalidArgumentException::class); - $authorizationService->isGranted('foo', 'foo'); + $authorizationService->isGranted(new Identity(), 'foo', 'foo'); } public function testContextIsPassedToRoleService(): void @@ -399,8 +395,7 @@ public function testContextIsPassedToRoleService(): void $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $roleService->expects($this->once())->method('getIdentityRoles')->with(null, $context)->willReturn([]); - $roleService->expects($this->never())->method('getIdentity')->willreturn($identity); - $authorizationService->isGranted('foo', $context); + $roleService->expects($this->once())->method('getIdentityRoles')->with($identity, $context)->willReturn([]); + $authorizationService->isGranted($identity, 'foo', $context); } } diff --git a/test/Service/RoleServiceTest.php b/test/Service/RoleServiceTest.php index d43968b..d26a614 100644 --- a/test/Service/RoleServiceTest.php +++ b/test/Service/RoleServiceTest.php @@ -22,7 +22,6 @@ namespace LmcRbacTest\Service; use LmcRbac\Identity\IdentityInterface; -use LmcRbac\Identity\IdentityProviderInterface; use LmcRbac\Role\InMemoryRoleProvider; use LmcRbac\Role\Role; use LmcRbac\Role\RoleInterface; @@ -40,161 +39,9 @@ class RoleServiceTest extends TestCase { use ProphecyTrait; - public static function roleProvider(): array - { - return [ - // No identity role - [ - 'rolesConfig' => [], - 'identityRoles' => [], - 'rolesToCheck' => [ - 'member', - ], - 'doesMatch' => false, - ], - - // Simple - [ - 'rolesConfig' => [ - 'member' => [ - 'children' => ['guest'], - ], - 'guest', - ], - 'identityRoles' => [ - 'guest', - ], - 'rolesToCheck' => [ - 'member', - ], - 'doesMatch' => false, - ], - [ - 'rolesConfig' => [ - 'member' => [ - 'children' => ['guest'], - ], - 'guest', - ], - 'identityRoles' => [ - 'member', - ], - 'rolesToCheck' => [ - 'member', - ], - 'doesMatch' => true, - ], - - // Complex role inheritance - [ - 'rolesConfig' => [ - 'admin' => [ - 'children' => ['moderator'], - ], - 'moderator' => [ - 'children' => ['member'], - ], - 'member' => [ - 'children' => ['guest'], - ], - 'guest', - ], - 'identityRoles' => [ - 'member', - 'moderator', - ], - 'rolesToCheck' => [ - 'admin', - ], - 'doesMatch' => false, - ], - [ - 'rolesConfig' => [ - 'admin' => [ - 'children' => ['moderator'], - ], - 'moderator' => [ - 'children' => ['member'], - ], - 'member' => [ - 'children' => ['guest'], - ], - 'guest', - ], - 'identityRoles' => [ - 'member', - 'admin', - ], - 'rolesToCheck' => [ - 'moderator', - ], - 'doesMatch' => true, - ], - - // Complex role inheritance and multiple check - [ - 'rolesConfig' => [ - 'sysadmin' => [ - 'children' => ['siteadmin', 'admin'], - ], - 'siteadmin', - 'admin' => [ - 'children' => ['moderator'], - ], - 'moderator' => [ - 'children' => ['member'], - ], - 'member' => [ - 'children' => ['guest'], - ], - 'guest', - ], - 'identityRoles' => [ - 'member', - 'moderator', - ], - 'rolesToCheck' => [ - 'admin', - 'sysadmin', - ], - 'doesMatch' => false, - ], - [ - 'rolesConfig' => [ - 'sysadmin' => [ - 'children' => ['siteadmin', 'admin'], - ], - 'siteadmin', - 'admin' => [ - 'children' => ['moderator'], - ], - 'moderator' => [ - 'children' => ['member'], - ], - 'member' => [ - 'children' => ['guest'], - ], - 'guest', - ], - 'identityRoles' => [ - 'moderator', - 'admin', - ], - 'rolesToCheck' => [ - 'sysadmin', - 'siteadmin', - 'member', - ], - 'doesMatch' => true, - ], - ]; - } - public function testReturnGuestRoleIfNoIdentityIsGiven(): void { - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $identityProvider->expects($this->once())->method('getIdentity')->willReturn(null); - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); + $roleService = new RoleService(new InMemoryRoleProvider([]), 'guest'); $result = $roleService->getIdentityRoles(null); @@ -203,13 +50,11 @@ public function testReturnGuestRoleIfNoIdentityIsGiven(): void $this->assertEquals('guest', $result[0]->getName()); } - public function testReturnGuestRoleIfGuestIdentityIsGiven(): void + public function testReturnGuestRoleIfNullIdentityIsGiven(): void { - $identity = new Identity(['guest']); - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); + $roleService = new RoleService(new InMemoryRoleProvider([]), 'guest'); - $result = $roleService->getIdentityRoles($identity); + $result = $roleService->getIdentityRoles(null); $this->assertCount(1, $result); $this->assertInstanceOf(RoleInterface::class, $result[0]); @@ -218,8 +63,7 @@ public function testReturnGuestRoleIfGuestIdentityIsGiven(): void public function testReturnTraversableRolesFromIdentityGiven(): void { - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); + $roleService = new RoleService(new InMemoryRoleProvider([]), 'guest'); $identity = $this->prophesize(IdentityInterface::class); $identity->getRoles()->willReturn($roles = new \ArrayIterator(['first', 'second', 'third'])); @@ -236,9 +80,8 @@ public function testWillNotInvokeRoleProviderIfAllRolesCollected(): void { $roleProvider = $this->prophesize(RoleProviderInterface::class); $roleProvider->getRoles(Argument::any())->shouldNotBeCalled(); - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $roleService = new RoleService($identityProvider, $roleProvider->reveal(), 'guest'); + $roleService = new RoleService($roleProvider->reveal(), 'guest'); $roles = [new Role('first'), new Role('second'), new Role('third')]; $identity = new Identity($roles); @@ -254,9 +97,8 @@ public function testWillCollectRolesOnlyIfRequired(): void $roleProvider = $this->prophesize(RoleProviderInterface::class); $roles = [new Role('first'), new Role('second'), 'third']; $roleProvider->getRoles(['third'])->shouldBeCalled()->willReturn([new Role('third')]); - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $roleService = new RoleService($identityProvider, $roleProvider->reveal(), 'guest'); + $roleService = new RoleService($roleProvider->reveal(), 'guest'); $identity = new Identity($roles); $result = $roleService->getIdentityRoles($identity); @@ -268,49 +110,4 @@ public function testWillCollectRolesOnlyIfRequired(): void $this->assertEquals($roles[1]->getName(), $result[1]->getName()); $this->assertEquals($roles[2], $result[2]->getName()); } - - public function testGetIdentity() - { - $identity = new Identity([]); - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $identityProvider->expects($this->once())->method('getIdentity')->willReturn($identity); - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); - $this->assertEquals($identity, $roleService->getIdentity()); - } - - /** - * @dataProvider roleProvider - */ - public function testMatchIdentityRoles( - array $rolesConfig, - array $identityRoles, - array $rolesToCheck, - $doesMatch - ): void { - $identity = $this->createMock(IdentityInterface::class); - $identity->expects($this->once())->method('getRoles')->willReturn($identityRoles); - - $identityProvider = $this->createMock(IdentityProviderInterface::class); - - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider($rolesConfig), 'guest'); - $this->assertEquals($doesMatch, $roleService->matchIdentityRoles($rolesToCheck, $identity)); - } - - /** - * @dataProvider roleProvider - */ - public function testMatchIdentityRolesWithNullIdentity( - array $rolesConfig, - array $identityRoles, - array $rolesToCheck, - $doesMatch - ): void { - $identity = $this->createMock(IdentityInterface::class); - $identity->expects($this->once())->method('getRoles')->willReturn($identityRoles); - - $identityProvider = $this->createMock(IdentityProviderInterface::class); - $identityProvider->expects($this->once())->method('getIdentity')->willReturn($identity); - $roleService = new RoleService($identityProvider, new InMemoryRoleProvider($rolesConfig), 'guest'); - $this->assertEquals($doesMatch, $roleService->matchIdentityRoles($rolesToCheck, null)); - } }