diff --git a/LICENSE b/LICENSE index 2004d9c6..7e89958e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,4 @@ -Copyright (c) 2013, ZF-Commons and LM-Commons Contributors -All rights reserved. +Copyright (c) 2020-2024, LM-Commons Organization and its contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -11,7 +10,7 @@ are permitted provided that the following conditions are met: list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the ZF-Commons, nor the name of LM-Commons nor the names of its + Neither the name of the name of LM-Commons nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/README.md b/README.md index 813fe46e..de642716 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,11 @@ ![Dynamic JSON Badge](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fapi.github.com%2Frepos%2Flm-commons%2Flmcrbac%2Fproperties%2Fvalues&query=%24%5B%3A1%5D.value&label=Maintenance%20Status) -Role-based access control module to provide additional features on top of Zend\Permissions\Rbac - -Based on [ZF-Commons/zfc-rbac](https://github.com/ZF-Commons/zfc-rbac) v3.x. If you are looking for the Laminas version -of zfc-rbac v2, please use [LM-Commons/LmcRbacMvc](https://github.com/LM-Commons/LmcRbacMvc). +Role-based access control module to provide additional features on top of Laminas\Permissions\Rbac ## Requirements -- PHP 7.3 or higher - -**Note:** PHP 7.3 and 8.0 are no longer actively tested +- PHP 8.1 or higher ## Optional diff --git a/UPGRADE.md b/UPGRADE.md index 3739d9a5..8d24da91 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -10,7 +10,7 @@ The ZF-Commons orgnisation has been moved to Laminas-Commons and ZfcRbac has bee To upgrade - Uninstall `zf-commons/zfc-rbac:3.0.0-alpha.1`. -- Install `laminas-commons/lmc-rbac:^1.0` +- Install `lm-commons/lmc-rbac:^1.0` - Change `zfc-rbac.global.php` to `lmc-rbac.global.php` and update the key `zfc_rbac` to `lmc_rbac`. - Review your code for usages of the `ZfcRbac/*` namespace to `LmcRbac/*` namespace. diff --git a/composer.json b/composer.json index 31f28bca..1e40b1b5 100644 --- a/composer.json +++ b/composer.json @@ -32,10 +32,15 @@ { "name": "Bas Kamer", "email": "baskamer@gmail.com" + }, + { + "name": "Eric Richer", + "email": "eric.richer@vistoconsulting.com" } ], "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" @@ -47,7 +52,8 @@ "phpspec/prophecy-phpunit": "^2.0", "friendsofphp/php-cs-fixer": "^3.43", "php-coveralls/php-coveralls": "^2.0", - "doctrine/collections": "^2.0" + "doctrine/orm": "^2.13 | ^3.0", + "symfony/cache": "^4.0| ^5.0 |^6.0" }, "autoload": { "psr-4": { @@ -78,6 +84,7 @@ "cs-fix": "php-cs-fixer fix -v --diff", "test": "phpunit --colors=always", "header": "docheader check src test", - "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" + "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", + "test-coverage-html": "phpunit --colors=always --coverage-html ./build/html" } } diff --git a/config/config.global.php b/config/lmcrbac.global.php.dist similarity index 85% rename from config/config.global.php rename to config/lmcrbac.global.php.dist index ac27686e..c9fd2077 100644 --- a/config/config.global.php +++ b/config/lmcrbac.global.php.dist @@ -25,6 +25,14 @@ return [ 'lmc_rbac' => [ + /** + * Key that is used to fetch the identity provider + * + * Please note that when an identity is found, it MUST implement the LmcRbac\Identity\IdentityProviderInterface + * interface, otherwise it will throw an exception. + */ + // 'identity_provider' => 'LmcRbac\Identity\AuthenticationIdentityProvider', + /** * Set the guest role * @@ -55,7 +63,7 @@ * Defining the assertion map * * The assertion map can automatically map permissions to assertions. This means that every times you check for - * a permission methioned in the assertion map, you'll include the assertion in your check. + * a permission mentioned in the assertion map, you will include the assertion in your check. * * example: * 'assertion_map' => [ diff --git a/data/FlatRole.php.dist b/data/FlatRole.php.dist index 109d6bba..2f8fa131 100644 --- a/data/FlatRole.php.dist +++ b/data/FlatRole.php.dist @@ -16,15 +16,20 @@ * and is licensed under the MIT license. */ +declare(strict_types=1); + use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Rbac\Role\RoleInterface; use LmcRbac\Permission\PermissionInterface; +use LmcRbac\Role\RoleInterface; /** * @ORM\Entity * @ORM\Table(name="roles") */ +#[ORM\Entity] +#[ORM\Table(name: 'roles')] class FlatRole implements RoleInterface { /** @@ -34,21 +39,25 @@ class FlatRole implements RoleInterface * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ - protected $id; + #[ORM\Id] + #[ORM\Column(name: 'id', type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + protected ?int $id; /** * @var string|null * - * @ORM\Column(type="string", length=48, unique=true) + * @ORM\Column(type="string", length=255, unique=true) */ - protected $name; + #[ORM\Column(name: 'name', type: 'string', length: 255, unique: true)] + protected ?string $name; /** - * @var PermissionInterface[]|\Doctrine\Common\Collections\Collection - * + * @var Permission[]|Collection * @ORM\ManyToMany(targetEntity="Permission", indexBy="name", fetch="EAGER", cascade={"persist"}) */ - protected $permissions; + #[ORM\ManyToMany(targetEntity: "Persmission", cascade: ["persist"], fetch: "EAGER", indexBy: "name")] + protected array|Collection|ArrayCollection $permissions; /** * Init the Doctrine collection @@ -61,9 +70,9 @@ class FlatRole implements RoleInterface /** * Get the role identifier * - * @return int + * @return int|null */ - public function getId() + public function getId(): ?int { return $this->id; } @@ -71,12 +80,12 @@ class FlatRole implements RoleInterface /** * Set the role name * - * @param string $name + * @param string $name * @return void */ - public function setName($name) + public function setName(string $name): void { - $this->name = (string) $name; + $this->name = $name; } /** @@ -84,15 +93,15 @@ class FlatRole implements RoleInterface * * @return string */ - public function getName() + public function getName(): string { return $this->name; } /** - * {@inheritDoc} + * @inheritDoc */ - public function addPermission($permission) + public function addPermission(PermissionInterface|string $permission): void { if (is_string($permission)) { $permission = new Permission($permission); @@ -102,13 +111,30 @@ class FlatRole implements RoleInterface } /** - * {@inheritDoc} + * @inheritDoc */ - public function hasPermission($permission) + public function hasPermission(PermissionInterface|string $permission): bool { // This can be a performance problem if your role has a lot of permissions. Please refer // to the cookbook to an elegant way to solve this issue return isset($this->permissions[(string) $permission]); } + + + + public function getChildren(): iterable + { + return []; + } + + public function hasChildren(): bool + { + return false; + } + + public function addChild(RoleInterface $role): void + { + // Do nothing + } } diff --git a/data/HierarchicalRole.php.dist b/data/HierarchicalRole.php.dist index 9066025b..6b21bdd1 100644 --- a/data/HierarchicalRole.php.dist +++ b/data/HierarchicalRole.php.dist @@ -16,16 +16,20 @@ * and is licensed under the MIT license. */ +declare(strict_types=1); + use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use Rbac\Role\HierarchicalRoleInterface; -use LmcRbac\Permission\PermissionInterface; +use LmcRbac\Role\RoleInterface; /** * @ORM\Entity * @ORM\Table(name="roles") */ -class HierarchicalRole implements HierarchicalRoleInterface +#[ORM\Entity] +#[ORM\Table(name: 'roles')] +class HierarchicalRole implements RoleInterface { /** * @var int|null @@ -34,44 +38,50 @@ class HierarchicalRole implements HierarchicalRoleInterface * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ - protected $id; + #[ORM\Id] + #[ORM\Column(name: 'id', type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + protected ?int $id = null; /** * @var string|null * * @ORM\Column(type="string", length=48, unique=true) */ - protected $name; + #[ORM\Column(name: 'name', type: 'string', length: 255, unique: true)] + protected ?string $name; /** - * @var HierarchicalRoleInterface[]|\Doctrine\Common\Collections\Collection + * @var RoleInterface[]|Collection * * @ORM\ManyToMany(targetEntity="HierarchicalRole") */ - protected $children = []; + #[ORM\ManyToMany(targetEntity: RoleInterface::class)] + protected array|Collection|ArrayCollection $children = []; /** - * @var PermissionInterface[]|\Doctrine\Common\Collections\Collection + * @var Permission[]|Collection * * @ORM\ManyToMany(targetEntity="Permission", indexBy="name", fetch="EAGER") */ - protected $permissions; + #[ORM\ManyToMany(targetEntity: Permission::class, fetch: 'EAGER', indexBy: 'name')] + protected array|Collection|ArrayCollection $permissions; /** * Init the Doctrine collection */ public function __construct() { - $this->children = new ArrayCollection(); + $this->children = new ArrayCollection(); $this->permissions = new ArrayCollection(); } /** * Get the role identifier * - * @return int + * @return int|null */ - public function getId() + public function getId(): ?int { return $this->id; } @@ -79,12 +89,12 @@ class HierarchicalRole implements HierarchicalRoleInterface /** * Set the role name * - * @param string $name + * @param string $name * @return void */ - public function setName($name) + public function setName(string $name): void { - $this->name = (string) $name; + $this->name = $name; } /** @@ -92,23 +102,25 @@ class HierarchicalRole implements HierarchicalRoleInterface * * @return string */ - public function getName() + public function getName(): string { return $this->name; } /** - * {@inheritDoc} + * @param RoleInterface $child + * @return void */ - public function addChild(HierarchicalRoleInterface $child) + public function addChild(RoleInterface $role): void { - $this->children[] = $child; + $this->children[] = $role; } /** - * {@inheritDoc} + * @param string|Permission $permission + * @return void */ - public function addPermission($permission) + public function addPermission(string|Permission|\LmcRbac\Permission\PermissionInterface $permission): void { if (is_string($permission)) { $permission = new Permission($permission); @@ -118,9 +130,10 @@ class HierarchicalRole implements HierarchicalRoleInterface } /** - * {@inheritDoc} + * @param $permission + * @return bool */ - public function hasPermission($permission) + public function hasPermission(string|\LmcRbac\Permission\PermissionInterface $permission): bool { // This can be a performance problem if your role has a lot of permissions. Please refer // to the cookbook to an elegant way to solve this issue @@ -129,18 +142,18 @@ class HierarchicalRole implements HierarchicalRoleInterface } /** - * {@inheritDoc} + * @inheritDoc */ - public function getChildren() + public function getChildren(): iterable { return $this->children; } /** - * {@inheritDoc} + * @inheritDoc */ - public function hasChildren() + public function hasChildren(): bool { - return !$this->children->isEmpty(); + return ! $this->children->isEmpty(); } } diff --git a/data/Permission.php.dist b/data/Permission.php.dist index 945a65f2..f6d84fef 100644 --- a/data/Permission.php.dist +++ b/data/Permission.php.dist @@ -1,4 +1,6 @@ name = (string) $name; + $this->name = $name; } /** * Get the permission identifier * - * @return int + * @return int|null */ - public function getId() + public function getId(): ?int { return $this->id; } /** - * {@inheritDoc} + * @inheritDoc */ - public function __toString() + public function __toString(): string { return $this->name; } diff --git a/test/Asset/HierarchicalRole.php b/data/Role.php.dist similarity index 53% rename from test/Asset/HierarchicalRole.php rename to data/Role.php.dist index 8529c6f6..7df58dbc 100644 --- a/test/Asset/HierarchicalRole.php +++ b/data/Role.php.dist @@ -1,5 +1,6 @@ name = $name; + $this->children = new ArrayCollection(); $this->permissions = new ArrayCollection(); } /** * Get the role identifier * - * @return int + * @return int|null */ - public function getId() + public function getId(): ?int { return $this->id; } - public function hasChildren(): bool + /** + * Add a permission + * + * @param string|PermissionInterface $name + * @return void + */ + public function addPermission(string|PermissionInterface $permission): void { - return ! empty($this->children); - } + $permission = new \Permission($permission); - public function getChildren(): iterable - { - return $this->children; + $this->permissions[(string) $permission] = $permission; } public function getName(): string @@ -98,12 +113,23 @@ public function getName(): string return $this->name; } - public function hasPermission(string $permission): bool + public function hasPermission(string|PermissionInterface $permission): bool { + return isset($this->permissions[(string) $permission]); } public function addChild(RoleInterface $child): void { - $this->children[$child->getName()] = $child; + $this->children[] = $child; + } + + public function getChildren(): iterable + { + return $this->children; + } + + public function hasChildren(): bool + { + return ! $this->children->isEmpty(); } } diff --git a/src/Assertion/AssertionContainer.php b/src/Assertion/AssertionContainer.php index e156385b..f32ed128 100644 --- a/src/Assertion/AssertionContainer.php +++ b/src/Assertion/AssertionContainer.php @@ -28,6 +28,8 @@ * * @author Aeneas Rekkas * @licence MIT + * @deprecated Use AssertionPluginManager + * @codeCoverageIgnore */ final class AssertionContainer extends AbstractPluginManager implements AssertionContainerInterface { diff --git a/src/Assertion/AssertionContainerFactory.php b/src/Assertion/AssertionContainerFactory.php index a4e7cd68..25aba37f 100644 --- a/src/Assertion/AssertionContainerFactory.php +++ b/src/Assertion/AssertionContainerFactory.php @@ -21,7 +21,6 @@ namespace LmcRbac\Assertion; -use LmcRbac\Assertion\AssertionContainer; use Psr\Container\ContainerInterface; /** @@ -29,8 +28,10 @@ * * @author Aeneas Rekkas * @licence MIT + * @deprecated Use AssertionPluginManagerFactory + * @codeCoverageIgnore */ -final class AssertionContainerFactory +class AssertionContainerFactory { public function __invoke(ContainerInterface $container): AssertionContainer { diff --git a/src/Assertion/AssertionContainerInterface.php b/src/Assertion/AssertionContainerInterface.php index d35770ac..6cfc6c13 100644 --- a/src/Assertion/AssertionContainerInterface.php +++ b/src/Assertion/AssertionContainerInterface.php @@ -23,6 +23,9 @@ use Psr\Container\ContainerInterface; +/** + * @deprecated + */ interface AssertionContainerInterface extends ContainerInterface { public function get($name): AssertionInterface; diff --git a/src/Assertion/AssertionInterface.php b/src/Assertion/AssertionInterface.php index 1b822315..33dbbe81 100644 --- a/src/Assertion/AssertionInterface.php +++ b/src/Assertion/AssertionInterface.php @@ -22,6 +22,7 @@ namespace LmcRbac\Assertion; use LmcRbac\Identity\IdentityInterface; +use LmcRbac\Permission\PermissionInterface; /** * Interface that you can implement for dynamic assertions @@ -34,8 +35,8 @@ interface AssertionInterface { public function assert( - string $permission, + PermissionInterface|string $permission, IdentityInterface $identity = null, - $context = null + mixed $context = null ): bool; } diff --git a/src/Assertion/AssertionPluginManager.php b/src/Assertion/AssertionPluginManager.php new file mode 100644 index 00000000..2d340234 --- /dev/null +++ b/src/Assertion/AssertionPluginManager.php @@ -0,0 +1,38 @@ +get('config')['lmc_rbac']['assertion_manager']; + + return new AssertionPluginManager($container, $config); + } +} diff --git a/test/Asset/MockRoleWithPermissionProperty.php b/src/Assertion/AssertionPluginManagerInterface.php similarity index 75% rename from test/Asset/MockRoleWithPermissionProperty.php rename to src/Assertion/AssertionPluginManagerInterface.php index 1a2b6b19..4c86b59d 100644 --- a/test/Asset/MockRoleWithPermissionProperty.php +++ b/src/Assertion/AssertionPluginManagerInterface.php @@ -19,19 +19,11 @@ declare(strict_types=1); -namespace LmcRbacTest\Asset; +namespace LmcRbac\Assertion; -use LmcRbac\Role\RoleInterface; +use Psr\Container\ContainerInterface; -class MockRoleWithPermissionProperty implements RoleInterface +interface AssertionPluginManagerInterface extends ContainerInterface { - public function getName(): string - { - return 'role-with-permission-property'; - } - - public function hasPermission(string $permission): bool - { - return false; - } + public function get($name): AssertionInterface; } diff --git a/src/Assertion/AssertionSet.php b/src/Assertion/AssertionSet.php index 12f5131f..82eccb17 100644 --- a/src/Assertion/AssertionSet.php +++ b/src/Assertion/AssertionSet.php @@ -23,8 +23,9 @@ use LmcRbac\Exception; use LmcRbac\Identity\IdentityInterface; +use LmcRbac\Permission\PermissionInterface; -final class AssertionSet implements AssertionInterface +class AssertionSet implements AssertionInterface { /** * Condition constants @@ -32,20 +33,21 @@ final class AssertionSet implements AssertionInterface public const CONDITION_OR = 'condition_or'; public const CONDITION_AND = 'condition_and'; - /** - * @var AssertionContainerInterface - */ - private $assertionContainer; - /** * @var array */ - private $assertions; + private array $assertions; private $condition = self::CONDITION_AND; - public function __construct(AssertionContainerInterface $assertionContainer, array $assertions) - { + /** + * @param AssertionPluginManagerInterface $assertionPluginManager + * @param array $assertions + */ + public function __construct( + private readonly AssertionPluginManagerInterface $assertionPluginManager, + array $assertions + ) { if (isset($assertions['condition'])) { if ($assertions['condition'] !== AssertionSet::CONDITION_AND && $assertions['condition'] !== AssertionSet::CONDITION_OR) { @@ -58,10 +60,9 @@ public function __construct(AssertionContainerInterface $assertionContainer, arr } $this->assertions = $assertions; - $this->assertionContainer = $assertionContainer; } - public function assert(string $permission, IdentityInterface $identity = null, $context = null): bool + public function assert(PermissionInterface|string $permission, IdentityInterface $identity = null, $context = null): bool { if (empty($this->assertions)) { return false; @@ -78,12 +79,12 @@ public function assert(string $permission, IdentityInterface $identity = null, $ $asserted = $assertion->assert($permission, $identity, $context); break; case is_string($assertion): - $this->assertions[$index] = $assertion = $this->assertionContainer->get($assertion); + $this->assertions[$index] = $assertion = $this->assertionPluginManager->get($assertion); $asserted = $assertion->assert($permission, $identity, $context); break; case is_array($assertion): - $this->assertions[$index] = $assertion = new AssertionSet($this->assertionContainer, $assertion); + $this->assertions[$index] = $assertion = new AssertionSet($this->assertionPluginManager, $assertion); $asserted = $assertion->assert($permission, $identity, $context); break; default: diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 5ac52289..5dc3f1b6 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -21,6 +21,9 @@ namespace LmcRbac; +use LmcRbac\Assertion\AssertionPluginManager; +use LmcRbac\Identity\AuthenticationIdentityProvider; + /** * The configuration provider for the LmcRbac module * @@ -40,10 +43,11 @@ public function getDependencyConfig(): array { return [ 'factories' => [ - \LmcRbac\Assertion\AssertionContainerInterface::class => \LmcRbac\Assertion\AssertionContainerFactory::class, + \LmcRbac\Assertion\AssertionPluginManager::class => \LmcRbac\Assertion\AssertionPluginManagerFactory::class, \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/Container/AssertionContainerFactory.php b/src/Container/AssertionContainerFactory.php index aff0db2f..9c65931e 100644 --- a/src/Container/AssertionContainerFactory.php +++ b/src/Container/AssertionContainerFactory.php @@ -31,12 +31,6 @@ * @licence MIT * @deprecated Replaced by LmcRbac\Assertion\AssertionContainerFactory */ -final class AssertionContainerFactory +final class AssertionContainerFactory extends \LmcRbac\Assertion\AssertionContainerFactory { - public function __invoke(ContainerInterface $container): AssertionContainer - { - $config = $container->get('config')['lmc_rbac']['assertion_manager']; - - return new AssertionContainer($container, $config); - } } diff --git a/src/Container/AuthorizationServiceFactory.php b/src/Container/AuthorizationServiceFactory.php index 9428ee74..99ada46a 100644 --- a/src/Container/AuthorizationServiceFactory.php +++ b/src/Container/AuthorizationServiceFactory.php @@ -35,17 +35,6 @@ * @licence MIT * @deprecated Replaced by LmcRbac\Service\AuthorizationServiceFactory */ -final class AuthorizationServiceFactory +final class AuthorizationServiceFactory extends \LmcRbac\Service\AuthorizationServiceFactory { - public function __invoke(ContainerInterface $container): AuthorizationService - { - $moduleOptions = $container->get(ModuleOptions::class); - - return new AuthorizationService( - $container->get(Rbac::class), - $container->get(RoleServiceInterface::class), - $container->get(AssertionContainerInterface::class), - $moduleOptions->getAssertionMap() - ); - } } diff --git a/src/Container/InMemoryRoleProviderFactory.php b/src/Container/InMemoryRoleProviderFactory.php index 420c8fb3..697fede6 100644 --- a/src/Container/InMemoryRoleProviderFactory.php +++ b/src/Container/InMemoryRoleProviderFactory.php @@ -32,14 +32,6 @@ * @licence MIT * @deprecated Replaced by LmcRbac\Role\InMemoryRoleProviderFactory */ -final class InMemoryRoleProviderFactory +final class InMemoryRoleProviderFactory extends \LmcRbac\Role\InMemoryRoleProviderFactory { - public function __invoke(ContainerInterface $container): InMemoryRoleProvider - { - $moduleOptions = $container->get(ModuleOptions::class); - - return new InMemoryRoleProvider( - $moduleOptions->getRoleProvider()[InMemoryRoleProvider::class] ?? [] - ); - } } diff --git a/src/Container/ModuleOptionsFactory.php b/src/Container/ModuleOptionsFactory.php index c2fbff48..43adcea7 100644 --- a/src/Container/ModuleOptionsFactory.php +++ b/src/Container/ModuleOptionsFactory.php @@ -31,10 +31,6 @@ * @licence MIT * @deprecated Replaced by LmcRbac\Options\ModuleOptionsFactory */ -final class ModuleOptionsFactory +final class ModuleOptionsFactory extends \LmcRbac\Options\ModuleOptionsFactory { - public function __invoke(ContainerInterface $container): ModuleOptions - { - return new ModuleOptions($container->get('config')['lmc_rbac']); - } } diff --git a/src/Container/ObjectRepositoryRoleProviderFactory.php b/src/Container/ObjectRepositoryRoleProviderFactory.php index 65072cb1..e8ca2cc5 100644 --- a/src/Container/ObjectRepositoryRoleProviderFactory.php +++ b/src/Container/ObjectRepositoryRoleProviderFactory.php @@ -33,33 +33,6 @@ * @licence MIT * @deprecated Replaced by LmcRbac\Role\ObjectRepositoryRoleProviderFactory */ -final class ObjectRepositoryRoleProviderFactory +final class ObjectRepositoryRoleProviderFactory extends \LmcRbac\Role\ObjectRepositoryRoleProviderFactory { - public function __invoke(ContainerInterface $container): ObjectRepositoryRoleProvider - { - $moduleOptions = $container->get(ModuleOptions::class); - $options = $moduleOptions->getRoleProvider()[ObjectRepositoryRoleProvider::class] ?? []; - - if (! isset($options['role_name_property'])) { - throw new Exception\RuntimeException('The "role_name_property" option is missing'); - } - - if (isset($options['object_repository'])) { - $objectRepository = $container->get($options['object_repository']); - - return new ObjectRepositoryRoleProvider($objectRepository, $options['role_name_property']); - } - - if (isset($options['object_manager'], $options['class_name'])) { - $objectManager = $container->get($options['object_manager']); - $objectRepository = $objectManager->getRepository($options['class_name']); - - return new ObjectRepositoryRoleProvider($objectRepository, $options['role_name_property']); - } - - throw new Exception\RuntimeException( - 'No object repository was found while creating the LmcRbac object repository role provider. Are - you sure you specified either the "object_repository" option or "object_manager"/"class_name" options?' - ); - } } diff --git a/src/Container/RoleServiceFactory.php b/src/Container/RoleServiceFactory.php index 8add13e3..fae8eebc 100644 --- a/src/Container/RoleServiceFactory.php +++ b/src/Container/RoleServiceFactory.php @@ -33,20 +33,6 @@ * @licence MIT * @deprecated Replaced by LmcRbac\Service\RoleServiceFactory */ -final class RoleServiceFactory +final class RoleServiceFactory extends \LmcRbac\Service\RoleServiceFactory { - public function __invoke(ContainerInterface $container): RoleService - { - $moduleOptions = $container->get(ModuleOptions::class); - - // Get the role provider from the options - $roleProvider = $moduleOptions->getRoleProvider(); - if (empty($roleProvider)) { - throw new ServiceNotCreatedException('No role provider defined in LmcRbac configuration.'); - } - - $roleProviderName = key($roleProvider); - - return new RoleService($container->get($roleProviderName), $moduleOptions->getGuestRole()); - } } diff --git a/src/Identity/AuthenticationIdentityProvider.php b/src/Identity/AuthenticationIdentityProvider.php new file mode 100644 index 00000000..62c4e1d5 --- /dev/null +++ b/src/Identity/AuthenticationIdentityProvider.php @@ -0,0 +1,23 @@ +authenticationService->getIdentity(); + } +} diff --git a/src/Identity/AuthenticationIdentityProviderFactory.php b/src/Identity/AuthenticationIdentityProviderFactory.php new file mode 100644 index 00000000..8096845e --- /dev/null +++ b/src/Identity/AuthenticationIdentityProviderFactory.php @@ -0,0 +1,26 @@ + + */ +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/Identity/IdentityProviderInterface.php b/src/Identity/IdentityProviderInterface.php new file mode 100644 index 00000000..1486d9dc --- /dev/null +++ b/src/Identity/IdentityProviderInterface.php @@ -0,0 +1,12 @@ +identityProvider = $identityProvider; + } + + /** + * Get the key of the identity provider used to retrieve the identity + * + * @return string + */ + public function getIdentityProvider(): string + { + return $this->identityProvider; + } + /** * Set the assertions options * diff --git a/src/Options/ModuleOptionsFactory.php b/src/Options/ModuleOptionsFactory.php index dd0f56f5..9c7fda21 100644 --- a/src/Options/ModuleOptionsFactory.php +++ b/src/Options/ModuleOptionsFactory.php @@ -30,7 +30,7 @@ * @author Michaël Gallego * @licence MIT */ -final class ModuleOptionsFactory +class ModuleOptionsFactory { public function __invoke(ContainerInterface $container): ModuleOptions { diff --git a/src/Permission/PermissionInterface.php b/src/Permission/PermissionInterface.php new file mode 100644 index 00000000..59b6e4bf --- /dev/null +++ b/src/Permission/PermissionInterface.php @@ -0,0 +1,17 @@ + + */ + +interface PermissionInterface +{ + /** + * Get the permission name + * @return string + */ + public function __toString(): string; +} diff --git a/src/Rbac.php b/src/Rbac.php index 45ab6eca..2534c02a 100644 --- a/src/Rbac.php +++ b/src/Rbac.php @@ -22,23 +22,23 @@ namespace LmcRbac; use Generator; -use LmcRbac\Role\HierarchicalRoleInterface; +use LmcRbac\Permission\PermissionInterface; use LmcRbac\Role\RoleInterface; use Traversable; /** * Rbac object. It is used to check a permission against roles */ -class Rbac +class Rbac implements RbacInterface { /** * Determines if access is granted by checking the roles for permission. * - * @param RoleInterface|RoleInterface[]|Traversable $roles - * @param string $permission + * @param RoleInterface|iterable $roles + * @param string|PermissionInterface $permission * @return bool */ - public function isGranted($roles, string $permission): bool + public function isGranted(RoleInterface|iterable $roles, string|PermissionInterface $permission): bool { if ($roles instanceof Traversable) { $roles = iterator_to_array($roles); @@ -49,7 +49,7 @@ public function isGranted($roles, string $permission): bool } foreach ($this->flattenRoles($roles) as $role) { - /* @var \LmcRbac\Role\RoleInterface $role */ + /* @var RoleInterface $role */ if ($role->hasPermission($permission)) { return true; } @@ -63,7 +63,7 @@ protected function flattenRoles(array $roles): Generator foreach ($roles as $role) { yield $role; - if ($role instanceof HierarchicalRoleInterface) { + if ($role->hasChildren()) { yield from $this->flattenRoles($role->getChildren()); } } diff --git a/src/RbacInterface.php b/src/RbacInterface.php new file mode 100644 index 00000000..6957c9a5 --- /dev/null +++ b/src/RbacInterface.php @@ -0,0 +1,20 @@ +name = $name; - } - - public function getName(): string - { - return $this->name; - } - - public function getPermissions(): array - { - return $this->permissions; - } - - public function addPermission(string $permission): void - { - $this->permissions[$permission] = $permission; - } - - public function hasPermission(string $permission): bool - { - return isset($this->permissions[$permission]); - } - - public function hasChildren(): bool - { - return ! empty($this->children); - } - - public function getChildren(): iterable - { - return $this->children; - } - - public function addChild(RoleInterface $child): void - { - $this->children[$child->getName()] = $child; - } } diff --git a/src/Role/HierarchicalRoleInterface.php b/src/Role/HierarchicalRoleInterface.php index 6c09fc1e..17d23f77 100644 --- a/src/Role/HierarchicalRoleInterface.php +++ b/src/Role/HierarchicalRoleInterface.php @@ -25,13 +25,7 @@ * Interface for a hierarchical role * * A hierarchical role is a role that can have children. + * @deprecated */ interface HierarchicalRoleInterface extends RoleInterface -{ - public function hasChildren(): bool; - - /** - * @return iterable - */ - public function getChildren(): iterable; -} +{} diff --git a/src/Role/InMemoryRoleProvider.php b/src/Role/InMemoryRoleProvider.php index 6af557fa..bf8caba4 100644 --- a/src/Role/InMemoryRoleProvider.php +++ b/src/Role/InMemoryRoleProvider.php @@ -71,7 +71,7 @@ public function getRoles(iterable $roleNames): iterable $roleConfig = $this->rolesConfig[$roleName]; if (isset($roleConfig['children'])) { - $role = new HierarchicalRole($roleName); + $role = new Role($roleName); $childRoles = (array) $roleConfig['children']; foreach ($this->getRoles($childRoles) as $childRole) { diff --git a/src/Role/InMemoryRoleProviderFactory.php b/src/Role/InMemoryRoleProviderFactory.php index 89c8c502..4b966d5b 100644 --- a/src/Role/InMemoryRoleProviderFactory.php +++ b/src/Role/InMemoryRoleProviderFactory.php @@ -31,7 +31,7 @@ * @author Vytautas Stankus * @licence MIT */ -final class InMemoryRoleProviderFactory +class InMemoryRoleProviderFactory { public function __invoke(ContainerInterface $container): InMemoryRoleProvider { diff --git a/src/Role/ObjectRepositoryRoleProviderFactory.php b/src/Role/ObjectRepositoryRoleProviderFactory.php index 94e37e7c..3ceeec9a 100644 --- a/src/Role/ObjectRepositoryRoleProviderFactory.php +++ b/src/Role/ObjectRepositoryRoleProviderFactory.php @@ -32,7 +32,7 @@ * @author Michaël Gallego * @licence MIT */ -final class ObjectRepositoryRoleProviderFactory +class ObjectRepositoryRoleProviderFactory { public function __invoke(ContainerInterface $container): ObjectRepositoryRoleProvider { diff --git a/src/Role/Role.php b/src/Role/Role.php index 137eb007..58bafc4c 100644 --- a/src/Role/Role.php +++ b/src/Role/Role.php @@ -21,21 +21,28 @@ namespace LmcRbac\Role; +use LmcRbac\Permission\PermissionInterface; + /** * Simple implementation for a role without hierarchy * and using strings as permissions */ -final class Role implements RoleInterface +class Role implements RoleInterface { /** * @var string */ - private $name; + private string $name; /** * @var string[] */ - private $permissions = []; + private array $permissions = []; + + /** + * @var array|RoleInterface[] + */ + private array $children = []; public function __construct(string $name) { @@ -52,13 +59,38 @@ public function getPermissions(): array return $this->permissions; } - public function addPermission(string $permission): void + public function addPermission(PermissionInterface|string $permission): void + { + $this->permissions[$permission] = (string) $permission; + } + + public function hasPermission(PermissionInterface|string $permission): bool + { + if (isset($this->permissions[(string) $permission])) { + return true; + } + + foreach ($this->children as $child) { + if ($child->hasPermission($permission)) { + return true; + } + } + + return false; + } + + public function hasChildren(): bool + { + return ! empty($this->children); + } + + public function getChildren(): iterable { - $this->permissions[$permission] = $permission; + return $this->children; } - public function hasPermission(string $permission): bool + public function addChild(RoleInterface $role): void { - return isset($this->permissions[$permission]); + $this->children[$role->getName()] = $role; } } diff --git a/src/Role/RoleInterface.php b/src/Role/RoleInterface.php index def76f13..258c1c46 100644 --- a/src/Role/RoleInterface.php +++ b/src/Role/RoleInterface.php @@ -21,8 +21,10 @@ namespace LmcRbac\Role; +use LmcRbac\Permission\PermissionInterface; + /** - * Interface for a flat role + * Interface for a role * * The role embeds all the information needed to evaluate if a given role has a given permission */ @@ -30,5 +32,14 @@ interface RoleInterface { public function getName(): string; - public function hasPermission(string $permission): bool; + public function hasPermission(PermissionInterface|string $permission): bool; + + public function addChild(RoleInterface $role): void; + + public function addPermission(PermissionInterface $permission): void; + + public function hasChildren(): bool; + + /** @return iterable */ + public function getChildren(): iterable; } diff --git a/src/Service/AuthorizationService.php b/src/Service/AuthorizationService.php index 64806280..f9462936 100644 --- a/src/Service/AuthorizationService.php +++ b/src/Service/AuthorizationService.php @@ -21,10 +21,10 @@ namespace LmcRbac\Service; -use LmcRbac\Assertion\AssertionContainerInterface; +use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Assertion\AssertionSet; -use LmcRbac\Identity\IdentityInterface; -use LmcRbac\Rbac; +use LmcRbac\Permission\PermissionInterface; +use LmcRbac\RbacInterface; /** * Authorization service is a simple service that internally uses Rbac to check if identity is @@ -33,43 +33,34 @@ * @author Michaël Gallego * @licence MIT */ -final class AuthorizationService implements AuthorizationServiceInterface +class AuthorizationService implements AuthorizationServiceInterface { - /** - * @var Rbac - */ - private $rbac; + protected RbacInterface $rbac; - /** - * @var RoleServiceInterface - */ - private $roleService; + protected RoleServiceInterface $roleService; - /** - * @var AssertionContainerInterface - */ - private $assertionContainer; + private AssertionPluginManagerInterface $assertionPluginManager; /** * @var array */ - private $assertions; + private array $assertions; public function __construct( - Rbac $rbac, + RbacInterface $rbac, RoleServiceInterface $roleService, - AssertionContainerInterface $assertionContainer, + AssertionPluginManagerInterface $assertionPluginManager, array $assertions = [] ) { $this->rbac = $rbac; $this->roleService = $roleService; - $this->assertionContainer = $assertionContainer; + $this->assertionPluginManager = $assertionPluginManager; $this->assertions = $assertions; } - public function isGranted(?IdentityInterface $identity, string $permission, $context = null): bool + public function isGranted(string|PermissionInterface $permission, mixed $context = null): bool { - $roles = $this->roleService->getIdentityRoles($identity, $context); + $roles = $this->roleService->getIdentityRoles(null, $context); if (empty($roles)) { return false; @@ -79,18 +70,18 @@ public function isGranted(?IdentityInterface $identity, string $permission, $con return false; } - if (empty($this->assertions[$permission])) { + if (empty($this->assertions[(string) $permission])) { return true; } - if (\is_array($this->assertions[$permission])) { - $permissionAssertions = $this->assertions[$permission]; + if (\is_array($this->assertions[(string) $permission])) { + $permissionAssertions = $this->assertions[(string) $permission]; } else { - $permissionAssertions = [$this->assertions[$permission]]; + $permissionAssertions = [$this->assertions[(string) $permission]]; } - $assertionSet = new AssertionSet($this->assertionContainer, $permissionAssertions); + $assertionSet = new AssertionSet($this->assertionPluginManager, $permissionAssertions); - return $assertionSet->assert($permission, $identity, $context); + return $assertionSet->assert($permission, $this->roleService->getIdentity(), $context); } } diff --git a/src/Service/AuthorizationServiceFactory.php b/src/Service/AuthorizationServiceFactory.php index e500b008..f3b1ff59 100644 --- a/src/Service/AuthorizationServiceFactory.php +++ b/src/Service/AuthorizationServiceFactory.php @@ -22,6 +22,7 @@ namespace LmcRbac\Service; use LmcRbac\Assertion\AssertionContainerInterface; +use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Options\ModuleOptions; use LmcRbac\Rbac; use LmcRbac\Service\AuthorizationService; @@ -34,7 +35,7 @@ * @author Michaël Gallego * @licence MIT */ -final class AuthorizationServiceFactory +class AuthorizationServiceFactory { public function __invoke(ContainerInterface $container): AuthorizationService { @@ -43,7 +44,7 @@ public function __invoke(ContainerInterface $container): AuthorizationService return new AuthorizationService( $container->get(Rbac::class), $container->get(RoleServiceInterface::class), - $container->get(AssertionContainerInterface::class), + $container->get(AssertionPluginManagerInterface::class), $moduleOptions->getAssertionMap() ); } diff --git a/src/Service/AuthorizationServiceInterface.php b/src/Service/AuthorizationServiceInterface.php index 3cdb1560..383295fa 100644 --- a/src/Service/AuthorizationServiceInterface.php +++ b/src/Service/AuthorizationServiceInterface.php @@ -21,7 +21,7 @@ namespace LmcRbac\Service; -use LmcRbac\Identity\IdentityInterface; +use LmcRbac\Permission\PermissionInterface; /** * Minimal interface for an authorization service @@ -34,10 +34,9 @@ interface AuthorizationServiceInterface /** * Check if the permission is granted to the current identity * - * @param IdentityInterface|null $identity - * @param string $permission - * @param mixed $context + * @param PermissionInterface|string $permission + * @param mixed|null $context * @return bool */ - public function isGranted(?IdentityInterface $identity, string $permission, $context = null): bool; + public function isGranted(PermissionInterface|string $permission, mixed $context = null): bool; } diff --git a/src/Service/RoleService.php b/src/Service/RoleService.php index 64ac27b8..098f6eec 100644 --- a/src/Service/RoleService.php +++ b/src/Service/RoleService.php @@ -22,6 +22,7 @@ namespace LmcRbac\Service; use LmcRbac\Identity\IdentityInterface; +use LmcRbac\Identity\IdentityProviderInterface; use LmcRbac\Role\RoleInterface; use LmcRbac\Role\RoleProviderInterface; use Traversable; @@ -32,35 +33,49 @@ * @author Michaël Gallego * @licence MIT */ -final class RoleService implements RoleServiceInterface +class RoleService implements RoleServiceInterface { - /** - * @var RoleProviderInterface - */ - private $roleProvider; + protected IdentityProviderInterface $identityProvider; - /** - * @var string - */ - private $guestRole; + protected RoleProviderInterface $roleProvider; - public function __construct(RoleProviderInterface $roleProvider, string $guestRole) - { + 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 * - * @param IdentityInterface $identity - * @param null $context + * @param IdentityInterface|null $identity + * @param mixed|null $context * @return RoleInterface[] */ - public function getIdentityRoles(IdentityInterface $identity = null, $context = null): iterable + public function getIdentityRoles(IdentityInterface $identity = null, mixed $context = null): iterable { + // If no identity is provided, get it from the identity provider if (null === $identity) { - return $this->convertRoles([$this->guestRole]); + $identity = $this->identityProvider->getIdentity(); + if (null === $identity) { + return $this->convertRoles([$this->guestRole]); + } } return $this->convertRoles($identity->getRoles()); diff --git a/src/Service/RoleServiceFactory.php b/src/Service/RoleServiceFactory.php index 2b2e93af..7b29a75f 100644 --- a/src/Service/RoleServiceFactory.php +++ b/src/Service/RoleServiceFactory.php @@ -22,6 +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; @@ -32,7 +33,7 @@ * @author Michaël Gallego * @licence MIT */ -final class RoleServiceFactory +class RoleServiceFactory { public function __invoke(ContainerInterface $container): RoleService { @@ -46,6 +47,9 @@ public function __invoke(ContainerInterface $container): RoleService $roleProviderName = key($roleProvider); - return new RoleService($container->get($roleProviderName), $moduleOptions->getGuestRole()); + return new RoleService( + $container->get($moduleOptions->getIdentityProvider()), + $container->get($roleProviderName), + $moduleOptions->getGuestRole()); } } diff --git a/src/Service/RoleServiceInterface.php b/src/Service/RoleServiceInterface.php index 595b4980..d3773e48 100644 --- a/src/Service/RoleServiceInterface.php +++ b/src/Service/RoleServiceInterface.php @@ -36,8 +36,16 @@ interface RoleServiceInterface * Get the identity roles from the current identity, applying some more logic * * @param null|IdentityInterface $identity - * @param mixed $context + * @param mixed|null $context * @return RoleInterface[] */ - public function getIdentityRoles(IdentityInterface $identity = null, $context = null): iterable; + 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; + } diff --git a/test/Assertion/AssertionContainerTest.php b/test/Assertion/AssertionPluginManagerTest.php similarity index 80% rename from test/Assertion/AssertionContainerTest.php rename to test/Assertion/AssertionPluginManagerTest.php index d542c1d1..6a2a0fc1 100644 --- a/test/Assertion/AssertionContainerTest.php +++ b/test/Assertion/AssertionPluginManagerTest.php @@ -22,22 +22,22 @@ namespace LmcRbacTest\Assertion; use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Exception\InvalidServiceException; use Laminas\ServiceManager\Factory\InvokableFactory; -use LmcRbac\Assertion\AssertionContainer; use LmcRbac\Assertion\AssertionInterface; +use LmcRbac\Assertion\AssertionPluginManager; use LmcRbacTest\Asset\SimpleAssertion; use PHPUnit\Framework\TestCase; -use Psr\Container\ContainerExceptionInterface; /** - * @covers \LmcRbac\Assertion\AssertionContainer + * @covers \LmcRbac\Assertion\AssertionPluginManager */ -class AssertionContainerTest extends TestCase +class AssertionPluginManagerTest extends TestCase { public function testValidationOfPluginSucceedsIfAssertionInterfaceIsImplemented() { $containerMock = $this->createMock(ContainerInterface::class); - $container = new AssertionContainer($containerMock, [ + $container = new AssertionPluginManager($containerMock, [ 'factories' => [ SimpleAssertion::class => InvokableFactory::class, ], @@ -49,13 +49,13 @@ public function testValidationOfPluginSucceedsIfAssertionInterfaceIsImplemented( public function testValidationOfPluginFailsIfAssertionInterfaceIsNotImplemented() { $containerMock = $this->createMock(ContainerInterface::class); - $container = new AssertionContainer($containerMock, [ + $container = new AssertionPluginManager($containerMock, [ 'factories' => [ \stdClass::class => InvokableFactory::class, ], ]); - $this->expectException(ContainerExceptionInterface::class); - $this->assertInstanceOf(AssertionInterface::class, $container->get(\stdClass::class)); + $this->expectException(InvalidServiceException::class); + $instance = $container->get(\stdClass::class); } } diff --git a/test/Assertion/AssertionSetTest.php b/test/Assertion/AssertionSetTest.php index a783ac07..452f6044 100644 --- a/test/Assertion/AssertionSetTest.php +++ b/test/Assertion/AssertionSetTest.php @@ -22,6 +22,8 @@ use LmcRbac\Assertion\AssertionContainerInterface; use LmcRbac\Assertion\AssertionInterface; +use LmcRbac\Assertion\AssertionPluginManager; +use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Assertion\AssertionSet; use LmcRbac\Exception\InvalidArgumentException; use LmcRbac\Identity\IdentityInterface; @@ -35,7 +37,7 @@ class AssertionSetTest extends TestCase { public function testImplementsAssertionInterface() { - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, []); $this->assertInstanceOf(AssertionInterface::class, $assertionSet); @@ -43,7 +45,7 @@ public function testImplementsAssertionInterface() public function testWhenNoAssertionsArePresentTheAssertionWillFail() { - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, []); $this->assertFalse($assertionSet->assert('foo')); @@ -51,7 +53,7 @@ public function testWhenNoAssertionsArePresentTheAssertionWillFail() public function testAcceptsAnAndCondition() { - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['condition' => AssertionSet::CONDITION_AND]); $this->assertFalse($assertionSet->assert('foo')); @@ -59,7 +61,7 @@ public function testAcceptsAnAndCondition() public function testAcceptsAnOrCondition() { - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['condition' => AssertionSet::CONDITION_OR]); $this->assertFalse($assertionSet->assert('foo')); @@ -67,7 +69,7 @@ public function testAcceptsAnOrCondition() public function testThrowsExceptionForAnUnknownCondition() { - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $this->expectException(InvalidArgumentException::class); new AssertionSet($assertionContainer, ['condition' => 'unknown']); @@ -78,7 +80,7 @@ public function testWhenNoConditionIsGivenAndIsUsed() $fooAssertion = new SimpleAssertion(true); $barAssertion = new SimpleAssertion(false); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['fooFactory', 'barFactory']); $matcher = $this->exactly(2); @@ -102,7 +104,7 @@ public function testAndConditionWillBreakEarlyWithFailure() $fooAssertion = new SimpleAssertion(false); $barAssertion = new SimpleAssertion(true); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['fooFactory', 'barFactory', 'condition' => AssertionSet::CONDITION_AND]); $assertionContainer->expects($this->once())->method('get')->with('fooFactory')->willReturn($fooAssertion); @@ -118,7 +120,7 @@ public function testOrConditionWillBreakEarlyWithSuccess() $fooAssertion = new SimpleAssertion(true); $barAssertion = new SimpleAssertion(false); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['fooFactory', 'barFactory', 'condition' => AssertionSet::CONDITION_OR]); $assertionContainer->expects($this->once())->method('get')->with('fooFactory')->willReturn($fooAssertion); @@ -133,7 +135,7 @@ public function testAssertionsAsStringsAreCached() { $fooAssertion = new SimpleAssertion(true); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['fooFactory']); $assertionContainer->expects($this->once())->method('get')->with('fooFactory')->willReturn($fooAssertion); @@ -149,7 +151,7 @@ public function testUsesAssertionsAsStrings() { $fooAssertion = new SimpleAssertion(true); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['fooFactory']); $assertionContainer->expects($this->once())->method('get')->with('fooFactory')->willReturn($fooAssertion); @@ -163,7 +165,7 @@ public function testUsesAssertionsAsInstances() { $fooAssertion = new SimpleAssertion(true); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, [$fooAssertion]); $this->assertTrue($assertionSet->assert('permission')); @@ -180,7 +182,7 @@ public function testUsesAssertionsAsCallables() return true; }; - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, [$fooAssertion]); $this->assertTrue($assertionSet->assert('permission')); @@ -193,7 +195,7 @@ public function testUsesAssertionsAsArrays() $fooAssertion = new SimpleAssertion(true); $barAssertion = new SimpleAssertion(true); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, ['fooFactory', ['barFactory']]); $assertionContainer->expects($this->exactly(2)) @@ -213,7 +215,7 @@ public function testThrowExceptionForInvalidAssertion() { $fooAssertion = new \stdClass(); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, [$fooAssertion]); $this->expectException(InvalidArgumentException::class); @@ -225,7 +227,7 @@ public function testThrowExceptionForInvalidAssertion() */ public function testMatrix(array $assertions, bool $expectedResult, array $assertionCalledCount) { - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); + $assertionContainer = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); $assertionSet = new AssertionSet($assertionContainer, $assertions); $this->assertSame($expectedResult, $assertionSet->assert('permission')); diff --git a/test/Asset/Identity.php b/test/Asset/Identity.php index 5b4813e1..a91da376 100644 --- a/test/Asset/Identity.php +++ b/test/Asset/Identity.php @@ -28,7 +28,7 @@ class Identity implements IdentityInterface /** * @var array */ - private $roles; + private array $roles; public function __construct(array $roles = []) { diff --git a/test/Asset/FlatRole.php b/test/Asset/Permission.php similarity index 66% rename from test/Asset/FlatRole.php rename to test/Asset/Permission.php index 1fb729e3..ca2c5eb9 100644 --- a/test/Asset/FlatRole.php +++ b/test/Asset/Permission.php @@ -1,5 +1,6 @@ name = $name; - $this->permissions = new ArrayCollection(); } /** - * Get the role identifier + * Get the permission identifier * - * @return int + * @return int|null */ - public function getId() + public function getId(): ?int { return $this->id; } - public function getName(): string + public function __toString(): string { return $this->name; } - - public function hasPermission(string $permission): bool - { - return isset($this->permissions[$permission]); - } } diff --git a/test/Asset/Role.php b/test/Asset/Role.php new file mode 100644 index 00000000..e047b073 --- /dev/null +++ b/test/Asset/Role.php @@ -0,0 +1,136 @@ +name = $name; + $this->children = new ArrayCollection(); + $this->permissions = new ArrayCollection(); + } + + /** + * Get the role identifier + * + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * Add a permission + * + * @param string $name + * @return void + */ + public function addPermission(string|PermissionInterface $permission): void + { + $permission = new Permission($permission); + + $this->permissions[(string) $permission] = $permission; + } + + public function getName(): string + { + return $this->name; + } + + public function hasPermission(PermissionInterface|string $permission): bool + { + return isset($this->permissions[(string) $permission]); + } + + public function addChild(RoleInterface $role): void + { + $this->children[] = $role; + } + + public function getChildren(): iterable + { + return $this->children; + } + + public function hasChildren(): bool + { + return ! $this->children->isEmpty(); + } +} diff --git a/test/Asset/SimpleAssertion.php b/test/Asset/SimpleAssertion.php index 93022ce9..063946c6 100644 --- a/test/Asset/SimpleAssertion.php +++ b/test/Asset/SimpleAssertion.php @@ -23,6 +23,7 @@ use LmcRbac\Assertion\AssertionInterface; use LmcRbac\Identity\IdentityInterface; +use LmcRbac\Permission\PermissionInterface; class SimpleAssertion implements AssertionInterface { @@ -41,7 +42,7 @@ public function __construct(bool $willAssert = true) $this->willAssert = $willAssert; } - public function assert(string $permission, IdentityInterface $identity = null, $context = null): bool + public function assert(string|PermissionInterface $permission, IdentityInterface $identity = null, $context = null): bool { $this->called++; diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php index 68029849..c0a84d46 100644 --- a/test/ConfigProviderTest.php +++ b/test/ConfigProviderTest.php @@ -34,10 +34,11 @@ public function testProvidesExpectedConfiguration() $provider = new ConfigProvider(); $expected = [ 'factories' => [ - \LmcRbac\Assertion\AssertionContainerInterface::class => \LmcRbac\Assertion\AssertionContainerFactory::class, + \LmcRbac\Assertion\AssertionPluginManager::class => \LmcRbac\Assertion\AssertionPluginManagerFactory::class, \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/AssertionContainerFactoryTest.php b/test/Container/AssertionPluginManagerFactoryTest.php similarity index 76% rename from test/Container/AssertionContainerFactoryTest.php rename to test/Container/AssertionPluginManagerFactoryTest.php index 1418aadc..44d299cd 100644 --- a/test/Container/AssertionContainerFactoryTest.php +++ b/test/Container/AssertionPluginManagerFactoryTest.php @@ -24,12 +24,14 @@ use Laminas\ServiceManager\ServiceManager; use LmcRbac\Assertion\AssertionContainer; use LmcRbac\Assertion\AssertionContainerFactory; +use LmcRbac\Assertion\AssertionPluginManager; +use LmcRbac\Assertion\AssertionPluginManagerFactory; use PHPUnit\Framework\TestCase; /** - * @covers \LmcRbac\Assertion\AssertionContainerFactory + * @covers \LmcRbac\Assertion\AssertionPluginManagerFactory */ -class AssertionContainerFactoryTest extends TestCase +class AssertionPluginManagerFactoryTest extends TestCase { public function testFactory(): void { @@ -40,9 +42,9 @@ public function testFactory(): void ], ]); - $factory = new AssertionContainerFactory(); - $pluginManager = $factory($serviceManager); + $factory = new AssertionPluginManagerFactory(); + $pluginManager = $factory($serviceManager, AssertionPluginManager::class); - $this->assertInstanceOf(AssertionContainer::class, $pluginManager); + $this->assertInstanceOf(AssertionPluginManager::class, $pluginManager); } } diff --git a/test/Container/AuthorizationServiceFactoryTest.php b/test/Container/AuthorizationServiceFactoryTest.php index e8f3eeea..9ab0dac6 100644 --- a/test/Container/AuthorizationServiceFactoryTest.php +++ b/test/Container/AuthorizationServiceFactoryTest.php @@ -22,6 +22,8 @@ namespace LmcRbacTest\Container; use LmcRbac\Assertion\AssertionContainerInterface; +use LmcRbac\Assertion\AssertionPluginManager; +use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Service\AuthorizationServiceFactory; use LmcRbac\Options\ModuleOptions; use LmcRbac\Rbac; @@ -43,7 +45,7 @@ public function testCanCreateAuthorizationService(): void $container = $this->prophesize(ContainerInterface::class); $container->get(ModuleOptions::class)->willReturn(new ModuleOptions([])); $container->get(RoleServiceInterface::class)->willReturn($this->createMock(RoleServiceInterface::class)); - $container->get(AssertionContainerInterface::class)->willReturn($this->createMock(AssertionContainerInterface::class)); + $container->get(AssertionPluginManagerInterface::class)->willReturn($this->createMock(AssertionPluginManager::class)); $container->get(Rbac::class)->willReturn(new Rbac()); $factory = new AuthorizationServiceFactory(); diff --git a/test/Container/RoleServiceFactoryTest.php b/test/Container/RoleServiceFactoryTest.php index 12947f9f..ffef337b 100644 --- a/test/Container/RoleServiceFactoryTest.php +++ b/test/Container/RoleServiceFactoryTest.php @@ -22,8 +22,11 @@ namespace LmcRbacTest\Container; use Laminas\ServiceManager\ServiceManager; +use LmcRbac\Identity\AuthenticationIdentityProviderFactory; +use LmcRbac\Identity\IdentityProviderInterface; use LmcRbac\Options\ModuleOptions; use LmcRbac\Role\InMemoryRoleProvider; +use LmcRbac\Service\RoleService; use LmcRbac\Service\RoleServiceFactory; use PHPUnit\Framework\TestCase; @@ -43,13 +46,17 @@ public function testCanCreateRoleService(): void ], ]); - $container = new ServiceManager(['services' => [ - ModuleOptions::class => $options, - InMemoryRoleProvider::class => new InMemoryRoleProvider([]), - ]]); + $container = new ServiceManager([ + 'services' => [ + ModuleOptions::class => $options, + InMemoryRoleProvider::class => new InMemoryRoleProvider([]), + \LmcRbac\Identity\AuthenticationIdentityProvider::class => $this->createMock(IdentityProviderInterface::class), + IdentityProviderInterface::class => $this->createMock(IdentityProviderInterface::class), + ], + ]); $factory = new RoleServiceFactory(); - $roleService = $factory($container); + $roleService = $factory($container, RoleService::class); $this->assertInstanceOf(\LmcRbac\Service\RoleService::class, $roleService); } diff --git a/test/Asset/MockRoleWithPermissionMethod.php b/test/Identity/AuthenticationIdentityProviderFactoryTest.php similarity index 53% rename from test/Asset/MockRoleWithPermissionMethod.php rename to test/Identity/AuthenticationIdentityProviderFactoryTest.php index 6777d392..a00d47c3 100644 --- a/test/Asset/MockRoleWithPermissionMethod.php +++ b/test/Identity/AuthenticationIdentityProviderFactoryTest.php @@ -1,5 +1,6 @@ setService( + 'Laminas\Authentication\AuthenticationService', + $this->createMock('Laminas\Authentication\AuthenticationService') + ); - public function getName(): string - { - return 'role-with-permission-method'; - } + $factory = new AuthenticationIdentityProviderFactory(); + $authenticationProvider = $factory($serviceManager, 'LmcRbac\Identity\AuthenticationIdentityProvider'); - public function hasPermission(string $permission): bool - { - return in_array($permission, $this->getPermissions(), true); + $this->assertInstanceOf('LmcRbac\Identity\AuthenticationIdentityProvider', $authenticationProvider); } } diff --git a/test/Identity/AuthenticationIdentityProviderTest.php b/test/Identity/AuthenticationIdentityProviderTest.php new file mode 100644 index 00000000..949b3c70 --- /dev/null +++ b/test/Identity/AuthenticationIdentityProviderTest.php @@ -0,0 +1,53 @@ +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/Options/ModuleOptionsTest.php b/test/Options/ModuleOptionsTest.php index a4367458..da1b2eb8 100644 --- a/test/Options/ModuleOptionsTest.php +++ b/test/Options/ModuleOptionsTest.php @@ -37,6 +37,7 @@ public function testAssertModuleDefaultOptions(): void $this->assertIsArray($moduleOptions->getRoleProvider()); $this->assertIsArray($moduleOptions->getAssertionMap()); $this->assertEquals('LmcRbac\Role\InMemoryRoleProvider', key($moduleOptions->getRoleProvider())); + $this->assertEquals('LmcRbac\Identity\AuthenticationIdentityProvider', $moduleOptions->getIdentityProvider()); } public function testSettersAndGetters(): void @@ -47,10 +48,12 @@ public function testSettersAndGetters(): void 'assertion_map' => [ 'foo' => 'bar', ], + 'identity_provider' => 'foo', ]); $this->assertEquals('unknown', $moduleOptions->getGuestRole()); $this->assertEquals([], $moduleOptions->getRoleProvider()); $this->assertEquals(['foo' => 'bar'], $moduleOptions->getAssertionMap()); + $this->assertEquals('foo', $moduleOptions->getIdentityProvider()); } } diff --git a/test/RbacTest.php b/test/Rbac/RbacTest.php similarity index 95% rename from test/RbacTest.php rename to test/Rbac/RbacTest.php index 50a78571..bf6afee9 100644 --- a/test/RbacTest.php +++ b/test/Rbac/RbacTest.php @@ -19,10 +19,9 @@ declare(strict_types=1); -namespace LmcRbacTest; +namespace LmcRbacTest\Rbac; use LmcRbac\Rbac; -use LmcRbac\Role\HierarchicalRole; use LmcRbac\Role\Role; use PHPUnit\Framework\TestCase; @@ -92,7 +91,7 @@ public function testCanCheckHierarchicalRole(): void $childRole = new Role('Bar'); $childRole->addPermission('permission'); - $parentRole = new HierarchicalRole('Foo'); + $parentRole = new Role('Foo'); $parentRole->addChild($childRole); $rbac = new \LmcRbac\Rbac(); @@ -107,7 +106,7 @@ public function testReturnFalseIfNoHierarchicalRoleHasPermission(): void { $childRole = new Role('Bar'); - $parentRole = new HierarchicalRole('Foo'); + $parentRole = new Role('Foo'); $parentRole->addChild($childRole); $rbac = new Rbac(); diff --git a/test/Role/HierarchicalRoleTest.php b/test/Role/HierarchicalRoleTest.php deleted file mode 100644 index 54bc7087..00000000 --- a/test/Role/HierarchicalRoleTest.php +++ /dev/null @@ -1,105 +0,0 @@ -addChild($child); - - $this->assertCount(1, $role->getChildren()); - } - - /** - * @covers \LmcRbac\Role\HierarchicalRole::hasChildren - */ - public function testHasChildren(): void - { - $role = new HierarchicalRole('role'); - - $this->assertFalse($role->hasChildren()); - - $role->addChild(new HierarchicalRole('child')); - - $this->assertTrue($role->hasChildren()); - } - - /** - * @covers \LmcRbac\Role\HierarchicalRole::getChildren - */ - public function testCanGetChildren(): void - { - $role = new HierarchicalRole('role'); - $child1 = new HierarchicalRole('child 1'); - $child2 = new HierarchicalRole('child 2'); - - $role->addChild($child1); - $role->addChild($child2); - - $children = $role->getChildren(); - - $this->assertCount(2, $children); - $this->assertContainsOnlyInstancesOf(HierarchicalRoleInterface::class, $children); - } - - /** - * @covers \LmcRbac\Role\HierarchicalRole::addPermission - */ - public function testRoleCanAddPermission(): void - { - $role = new HierarchicalRole('php'); - $role->addPermission('debug'); - $this->assertTrue($role->hasPermission('debug')); - $role->addPermission('delete'); - $this->assertTrue($role->hasPermission('delete')); - } - - /** - * @covers \LmcRbac\Role\HierarchicalRole::getPermissions - */ - public function testRoleCanGetPermissions(): void - { - $role = new HierarchicalRole('php'); - $role->addPermission('foo'); - $role->addPermission('bar'); - $expectedPermissions = [ - 'foo' => 'foo', - 'bar' => 'bar', - ]; - $this->assertEquals($expectedPermissions, $role->getPermissions()); - } -} diff --git a/test/Role/InMemoryRoleProviderTest.php b/test/Role/InMemoryRoleProviderTest.php index eec80f38..7d92fce6 100644 --- a/test/Role/InMemoryRoleProviderTest.php +++ b/test/Role/InMemoryRoleProviderTest.php @@ -21,7 +21,6 @@ namespace LmcRbacTest\Role; -use LmcRbac\Role\HierarchicalRoleInterface; use LmcRbac\Role\InMemoryRoleProvider; use LmcRbac\Role\RoleInterface; use PHPUnit\Framework\TestCase; @@ -54,13 +53,13 @@ public function testInMemoryProvider(): void // Test admin role $adminRole = $roles[0]; - $this->assertInstanceOf(HierarchicalRoleInterface::class, $adminRole); + $this->assertInstanceOf(RoleInterface::class, $adminRole); $this->assertEquals('admin', $adminRole->getName()); $this->assertTrue($adminRole->hasPermission('delete')); // Test member role $memberRole = $roles[1]; - $this->assertInstanceOf(HierarchicalRoleInterface::class, $memberRole); + $this->assertInstanceOf(RoleInterface::class, $memberRole); $this->assertEquals('member', $memberRole->getName()); $this->assertTrue($memberRole->hasPermission('write')); $this->assertFalse($memberRole->hasPermission('delete')); @@ -68,7 +67,7 @@ public function testInMemoryProvider(): void // Test guest role $guestRole = $roles[2]; $this->assertInstanceOf(RoleInterface::class, $guestRole); - $this->assertNotInstanceOf(HierarchicalRoleInterface::class, $guestRole); +// $this->assertNotInstanceOf(HierarchicalRoleInterface::class, $guestRole); $this->assertEquals('guest', $guestRole->getName()); $this->assertFalse($guestRole->hasPermission('write')); $this->assertFalse($guestRole->hasPermission('delete')); @@ -76,7 +75,7 @@ public function testInMemoryProvider(): void // Test mrx role $guestRole = $roles[3]; $this->assertInstanceOf(RoleInterface::class, $guestRole); - $this->assertNotInstanceOf(HierarchicalRoleInterface::class, $guestRole); +// $this->assertNotInstanceOf(HierarchicalRoleInterface::class, $guestRole); $this->assertEquals('mrx', $guestRole->getName()); $this->assertTrue($guestRole->hasPermission('write')); $this->assertTrue($guestRole->hasPermission('delete')); diff --git a/test/Role/ObjectRepositoryRoleProviderTest.php b/test/Role/ObjectRepositoryRoleProviderTest.php index c130065a..1e13e447 100644 --- a/test/Role/ObjectRepositoryRoleProviderTest.php +++ b/test/Role/ObjectRepositoryRoleProviderTest.php @@ -21,9 +21,15 @@ namespace LmcRbacTest\Role; +use Doctrine\DBAL\DriverManager; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\ORMSetup; +use Doctrine\ORM\Tools\SchemaTool; +use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; use LmcRbac\Role\ObjectRepositoryRoleProvider; -use LmcRbacTest\Asset\FlatRole; +use LmcRbac\Role\Role; +use LmcRbac\Role\RoleInterface; use PHPUnit\Framework\TestCase; /** @@ -31,27 +37,47 @@ */ class ObjectRepositoryRoleProviderTest extends TestCase { + + public static function roleProvider(): array + { + return [ + 'one-role-flat' => [ + 'rolesConfig' => [ + 'admin', + ], + 'rolesToCheck' => ['admin'], + ], + '2-roles-flat' => [ + 'rolesConfig' => [ + 'admin', + 'member', + ], + 'rolesToCheck' => ['admin', 'member'], + ], + ]; + } + public function testObjectRepositoryProviderGetRoles(): void { - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $memberRole = new FlatRole('member'); + $objectRepository = $this->createMock(ObjectRepository::class); + $memberRole = new Role('member'); $provider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); $result = [$memberRole]; - $objectRepository->expects($this->once())->method('findBy')->will($this->returnValue($result)); + $objectRepository->expects($this->once())->method('findBy')->willReturn($result); $this->assertEquals($result, $provider->getRoles(['member'])); } public function testRoleCacheOnConsecutiveCalls(): void { - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $memberRole = new FlatRole('member'); + $objectRepository = $this->createMock(ObjectRepository::class); + $memberRole = new Role('member'); $provider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); $result = [$memberRole]; // note exactly once, consecutive call come from cache - $objectRepository->expects($this->exactly(1))->method('findBy')->will($this->returnValue($result)); + $objectRepository->expects($this->exactly(1))->method('findBy')->willReturn($result); $provider->getRoles(['member']); $provider->getRoles(['member']); @@ -59,13 +85,13 @@ public function testRoleCacheOnConsecutiveCalls(): void public function testClearRoleCache(): void { - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $memberRole = new FlatRole('member'); + $objectRepository = $this->createMock(ObjectRepository::class); + $memberRole = new Role('member'); $provider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); $result = [$memberRole]; // note exactly twice, as cache is cleared - $objectRepository->expects($this->exactly(2))->method('findBy')->will($this->returnValue($result)); + $objectRepository->expects($this->exactly(2))->method('findBy')->willReturn($result); $provider->getRoles(['member']); $provider->clearRoleCache(); @@ -74,16 +100,156 @@ public function testClearRoleCache(): void public function testThrowExceptionIfAskedRoleIsNotFound(): void { - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $memberRole = new FlatRole('member'); + $objectRepository = $this->createMock(ObjectRepository::class); + $memberRole = new Role('member'); $provider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); $result = [$memberRole]; - $objectRepository->expects($this->once())->method('findBy')->will($this->returnValue($result)); + $objectRepository->expects($this->once())->method('findBy')->willReturn($result); $this->expectException('LmcRbac\Exception\RoleNotFoundException'); $this->expectExceptionMessage('Some roles were asked but could not be loaded from database: guest, admin'); $provider->getRoles(['guest', 'admin', 'member']); } + + /** + * @dataProvider roleProvider + */ + public function testObjectRepositoryProviderForFlatRole(array $rolesConfig, array $rolesToCheck) + { + $objectManager = $this->getObjectManager(); + foreach ($rolesConfig as $name => $roleConfig) { + if (is_array($roleConfig)) { + $role = new \LmcRbacTest\Asset\Role($name); + if (isset($roleConfig['permissions'])) { + foreach ($roleConfig['permissions'] as $permission) { + $role->addPermission($permission); + } + } + } else { + $role = new \LmcRbacTest\Asset\Role($roleConfig); + } + $objectManager->persist($role); + } + $objectManager->flush(); + + $objectRepository = $objectManager->getRepository('LmcRbacTest\Asset\Role'); + $objectRepositoryRoleProvider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); + + $roles = $objectRepositoryRoleProvider->getRoles($rolesToCheck); + + $this->assertIsArray($roles); + $this->assertCount(count($rolesToCheck), $roles); + + $i = 0; + foreach ($roles as $role) { + $this->assertInstanceOf(RoleInterface::class, $role); + $this->assertEquals($rolesToCheck[$i], $role->getName()); + $i++; + } + } + + public function testObjectRepositoryProviderForFlatRoleWithPermissions() + { + $objectManager = $this->getObjectManager(); + + // Let's create a role + $adminRole = new \LmcRbacTest\Asset\Role('admin'); + $adminRole->addPermission('manage'); + $adminRole->addPermission('write'); + $adminRole->addPermission('read'); + $objectManager->persist($adminRole); + $objectManager->flush(); + + $objectRepository = $objectManager->getRepository('LmcRbacTest\Asset\Role'); + + $objectRepositoryRoleProvider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); + + // Get only the role + $roles = $objectRepositoryRoleProvider->getRoles(['admin']); + + $this->assertCount(1, $roles); + $this->assertIsArray($roles); + + $this->assertInstanceOf('LmcRbac\Role\RoleInterface', $roles[0]); + $this->assertEquals('admin', $roles[0]->getName()); + $this->assertTrue($roles[0]->hasPermission('manage')); + $this->assertTrue($roles[0]->hasPermission('read')); + $this->assertTrue($roles[0]->hasPermission('write')); + $this->assertFalse($roles[0]->hasPermission('foo')); + } + + public function testObjectRepositoryProviderForHierarchicalRole() + { + $objectManager = $this->getObjectManager(); + + // Let's add some roles + $guestRole = new \LmcRbacTest\Asset\Role('guest'); + $objectManager->persist($guestRole); + + $memberRole = new \LmcRbacTest\Asset\Role('member'); + $memberRole->addChild($guestRole); + $objectManager->persist($memberRole); + + $adminRole = new \LmcRbacTest\Asset\Role('admin'); + $adminRole->addChild($memberRole); + $objectManager->persist($adminRole); + + $objectManager->flush(); + + $objectRepository = $objectManager->getRepository('LmcRbacTest\Asset\Role'); + + $objectRepositoryRoleProvider = new ObjectRepositoryRoleProvider($objectRepository, 'name'); + + // Get only the admin role + $roles = $objectRepositoryRoleProvider->getRoles(['admin']); + + $this->assertCount(1, $roles); + $this->assertIsArray($roles); + + $this->assertInstanceOf('LmcRbac\Role\RoleInterface', $roles[0]); + $this->assertEquals('admin', $roles[0]->getName()); + + $childRolesString = ''; + + foreach ($this->flattenRoles($roles[0]->getChildren()) as $childRole) { + $this->assertInstanceOf('LmcRbac\Role\RoleInterface', $childRole); + $childRolesString .= $childRole->getName(); + } + + $this->assertEquals('memberguest', $childRolesString); + } + + private function getObjectManager(): ObjectManager|EntityManager + { + $config = ORMSetup::createAttributeMetadataConfiguration( + paths: [__DIR__ . '/../Asset'], + isDevMode: true + ); + $connection = DriverManager::getConnection([ + 'driverClass' => 'Doctrine\DBAL\Driver\PDO\SQLite\Driver', + 'path' => null, + 'memory' => true, + 'dbname' => 'test', + ], $config); + $entityManager = new EntityManager($connection, $config); + + $schemaTool = new SchemaTool($objectManager = $entityManager); + $schemaTool->dropDatabase(); + $schemaTool->createSchema($entityManager->getMetadataFactory()->getAllMetadata()); + + return $objectManager; + } + + private function flattenRoles(iterable $roles): \Generator + { + foreach ($roles as $role) { + yield $role; + + if ($role->hasChildren()) { + yield from $this->flattenRoles($role->getChildren()); + } + } + } } diff --git a/test/Role/RoleTest.php b/test/Role/RoleTest.php index 59d4eeb9..f9d26ae5 100644 --- a/test/Role/RoleTest.php +++ b/test/Role/RoleTest.php @@ -22,6 +22,7 @@ namespace LmcRbacTest\Role; use LmcRbac\Role\Role; +use LmcRbac\Role\RoleInterface; use PHPUnit\Framework\TestCase; /** @@ -67,4 +68,67 @@ public function testRoleCanGetPermissions(): void ]; $this->assertEquals($expectedPermissions, $role->getPermissions()); } + + /** + * @covers \LmcRbac\Role\Role::hasPermission + */ + public function testHasPermission(): void + { + $role = new Role('php'); + $role->addPermission('foo'); + $this->assertTrue($role->hasPermission('foo')); + $this->assertFalse($role->hasPermission('bar')); + + $childRole = new Role('child'); + $childRole->addPermission('bar'); + $role->addChild($childRole); + $this->assertTrue($role->hasPermission('bar')); + $this->assertFalse($role->hasPermission('baz')); + + } + + /** + * @covers \LmcRbac\Role\Role::addChild + */ + public function testCanAddChild(): void + { + $role = new Role('role'); + $child = new Role('child'); + + $role->addChild($child); + + $this->assertCount(1, $role->getChildren()); + } + + /** + * @covers \LmcRbac\Role\Role::hasChildren + */ + public function testHasChildren(): void + { + $role = new Role('role'); + + $this->assertFalse($role->hasChildren()); + + $role->addChild(new Role('child')); + + $this->assertTrue($role->hasChildren()); + } + + /** + * @covers \LmcRbac\Role\Role::getChildren + */ + public function testCanGetChildren(): void + { + $role = new Role('role'); + $child1 = new Role('child 1'); + $child2 = new Role('child 2'); + + $role->addChild($child1); + $role->addChild($child2); + + $children = $role->getChildren(); + + $this->assertCount(2, $children); + $this->assertContainsOnlyInstancesOf(RoleInterface::class, $children); + } } diff --git a/test/Service/AuthorizationServiceTest.php b/test/Service/AuthorizationServiceTest.php index 5a11039e..3b1efd32 100644 --- a/test/Service/AuthorizationServiceTest.php +++ b/test/Service/AuthorizationServiceTest.php @@ -24,16 +24,20 @@ use Laminas\ServiceManager\ServiceManager; use LmcRbac\Assertion\AssertionContainer; use LmcRbac\Assertion\AssertionContainerInterface; +use LmcRbac\Assertion\AssertionPluginManager; +use LmcRbac\Assertion\AssertionPluginManagerInterface; use LmcRbac\Assertion\AssertionSet; use LmcRbac\Exception\InvalidArgumentException; use LmcRbac\Identity\IdentityInterface; +use LmcRbac\Identity\IdentityProviderInterface; use LmcRbac\Rbac; +use LmcRbac\RbacInterface; use LmcRbac\Role\InMemoryRoleProvider; +use LmcRbac\Role\Role; use LmcRbac\Role\RoleInterface; use LmcRbac\Service\AuthorizationService; use LmcRbac\Service\RoleService; use LmcRbac\Service\RoleServiceInterface; -use LmcRbacTest\Asset\FlatRole; use LmcRbacTest\Asset\Identity; use LmcRbacTest\Asset\SimpleAssertion; use PHPUnit\Framework\TestCase; @@ -181,12 +185,16 @@ public function testGranted($role, $permission, $context, bool $isGranted, array ], ]; - $roleService = new RoleService(new InMemoryRoleProvider($roleConfig), 'guest'); - $assertionContainer = new AssertionContainer(new ServiceManager(), $assertionPluginConfig); $identity = new Identity((array) $role); - $authorizationService = new AuthorizationService(new Rbac(), $roleService, $assertionContainer, $assertions); - - $this->assertEquals($isGranted, $authorizationService->isGranted($identity, $permission, $context)); + $identityProvider = $this->createMock(IdentityProviderInterface::class); + $identityProvider->expects($this->any()) + ->method('getIdentity') + ->willReturn($identity); + $roleService = new RoleService($identityProvider, 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)); } public function testDoNotCallAssertionIfThePermissionIsNotGranted(): void @@ -195,14 +203,14 @@ public function testDoNotCallAssertionIfThePermissionIsNotGranted(): void $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue([$role])); + $roleService->expects($this->once())->method('getIdentityRoles')->willReturn([$role]); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->never())->method('get'); + $assertionPluginManager = $this->createMock(AssertionPluginManager::class); + $assertionPluginManager->expects($this->never())->method('get'); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $this->assertFalse($authorizationService->isGranted(null, 'foo')); + $this->assertFalse($authorizationService->isGranted('foo', 'foo')); } public function testReturnsFalseForIdentityWithoutRoles(): void @@ -213,99 +221,102 @@ public function testReturnsFalseForIdentityWithoutRoles(): void $rbac->expects($this->never())->method('isGranted'); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue($identity->getRoles())); + $roleService->expects($this->once())->method('getIdentityRoles')->willReturn($identity->getRoles()); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->never())->method('get'); + $assertionPluginManager = $this->createMock(AssertionPluginManager::class); + $assertionPluginManager->expects($this->never())->method('get'); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $this->assertFalse($authorizationService->isGranted($identity, 'foo')); + $this->assertFalse($authorizationService->isGranted('foo', 'foo')); } public function testReturnsTrueForIdentityWhenHasPermissionButNoAssertionsExists(): void { - $role = new FlatRole('admin'); + $role = new Role('admin'); $identity = new Identity([$role]); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue($identity->getRoles())); + $roleService->expects($this->once())->method('getIdentityRoles')->willReturn($identity->getRoles()); $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); $rbac->expects($this->once())->method('isGranted')->willReturn(true); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->never())->method('get'); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); + $assertionPluginManager->expects($this->never())->method('get'); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $this->assertTrue($authorizationService->isGranted($identity, 'foo')); + $this->assertTrue($authorizationService->isGranted('foo', 'foo')); } public function testUsesAssertionsAsInstances(): void { - $role = new FlatRole('admin'); + $role = new Role('admin'); $identity = new Identity([$role]); $assertion = new SimpleAssertion(); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue($identity->getRoles())); + $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); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->never())->method('get'); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); + $assertionPluginManager->expects($this->never())->method('get'); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer, ['foo' => $assertion]); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, ['foo' => $assertion]); - $authorizationService->isGranted($identity, 'foo'); + $authorizationService->isGranted('foo', 'foo'); $this->assertTrue($assertion->gotCalled()); } public function testUsesAssertionsAsStrings(): void { - $role = new FlatRole('admin'); + $role = new Role('admin'); $identity = new Identity([$role]); $assertion = new SimpleAssertion(); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue($identity->getRoles())); + $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); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->once())->method('get')->with('fooFactory')->willReturn($assertion); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); + $assertionPluginManager->expects($this->once())->method('get')->with('fooFactory')->willReturn($assertion); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer, ['foo' => 'fooFactory']); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, ['foo' => 'fooFactory']); - $authorizationService->isGranted($identity, 'foo'); + $authorizationService->isGranted('foo', 'foo'); $this->assertTrue($assertion->gotCalled()); } public function testUsesAssertionsAsCallable(): void { - $role = new FlatRole('admin'); + $role = new Role('admin'); $identity = new Identity([$role]); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue($identity->getRoles())); + $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); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->never())->method('get'); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); + $assertionPluginManager->expects($this->never())->method('get'); $called = false; $authorizationService = new AuthorizationService( $rbac, $roleService, - $assertionContainer, + $assertionPluginManager, [ 'foo' => function ($permission, IdentityInterface $identity = null, $context = null) use (&$called) { $called = true; @@ -315,29 +326,30 @@ public function testUsesAssertionsAsCallable(): void ] ); - $authorizationService->isGranted($identity, 'foo'); + $authorizationService->isGranted('foo', 'foo'); $this->assertTrue($called); } public function testUsesAssertionsAsArrays(): void { - $role = new FlatRole('admin'); + $role = new Role('admin'); $identity = new Identity([$role]); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue($identity->getRoles())); + $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); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $assertionContainer->expects($this->never())->method('get'); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); + $assertionPluginManager->expects($this->never())->method('get'); $called1 = false; $called2 = false; - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer, [ + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, [ 'foo' => [ function ($permission, IdentityInterface $identity = null, $context = null) use (&$called1) { $called1 = true; @@ -352,7 +364,7 @@ function ($permission, IdentityInterface $identity = null, $context = null) use ], ]); - $this->assertFalse($authorizationService->isGranted($identity, 'foo')); + $this->assertFalse($authorizationService->isGranted('foo', 'foo')); $this->assertTrue($called1); $this->assertTrue($called2); @@ -363,17 +375,18 @@ public function testThrowExceptionForInvalidAssertion(): void $role = $this->getMockBuilder(RoleInterface::class)->getMock(); $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); - $rbac->expects($this->once())->method('isGranted')->will($this->returnValue(true)); + $rbac->expects($this->once())->method('isGranted')->willReturn(true); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $roleService->expects($this->once())->method('getIdentityRoles')->will($this->returnValue([$role])); + $roleService->expects($this->once())->method('getIdentityRoles')->willreturn([$role]); + $roleService->expects($this->once())->method('getIdentity')->willreturn(new Identity()); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->disableOriginalConstructor()->getMock(); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer, ['foo' => new \stdClass()]); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->disableOriginalConstructor()->getMock(); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager, ['foo' => new \stdClass()]); $this->expectException(InvalidArgumentException::class); - $authorizationService->isGranted(null, 'foo'); + $authorizationService->isGranted('foo', 'foo'); } public function testContextIsPassedToRoleService(): void @@ -381,12 +394,13 @@ public function testContextIsPassedToRoleService(): void $identity = new Identity([]); $context = 'context'; - $rbac = $this->getMockBuilder(Rbac::class)->disableOriginalConstructor()->getMock(); + $rbac = $this->getMockBuilder(RbacInterface::class)->disableOriginalConstructor()->getMock(); $roleService = $this->getMockBuilder(RoleServiceInterface::class)->getMock(); - $assertionContainer = $this->getMockBuilder(AssertionContainerInterface::class)->getMock(); - $authorizationService = new AuthorizationService($rbac, $roleService, $assertionContainer); + $assertionPluginManager = $this->getMockBuilder(AssertionPluginManagerInterface::class)->getMock(); + $authorizationService = new AuthorizationService($rbac, $roleService, $assertionPluginManager); - $roleService->expects($this->once())->method('getIdentityRoles')->with($identity, $context)->willReturn([]); - $authorizationService->isGranted($identity, 'foo', $context); + $roleService->expects($this->once())->method('getIdentityRoles')->with(null, $context)->willReturn([]); + $roleService->expects($this->never())->method('getIdentity')->willreturn($identity); + $authorizationService->isGranted('foo', $context); } } diff --git a/test/Service/RoleServiceTest.php b/test/Service/RoleServiceTest.php index 1ebb29aa..fc3cdf0f 100644 --- a/test/Service/RoleServiceTest.php +++ b/test/Service/RoleServiceTest.php @@ -22,7 +22,7 @@ namespace LmcRbacTest\Service; use LmcRbac\Identity\IdentityInterface; -use LmcRbac\Role\HierarchicalRole; +use LmcRbac\Identity\IdentityProviderInterface; use LmcRbac\Role\InMemoryRoleProvider; use LmcRbac\Role\Role; use LmcRbac\Role\RoleInterface; @@ -42,7 +42,9 @@ class RoleServiceTest extends TestCase public function testReturnGuestRoleIfNoIdentityIsGiven(): void { - $roleService = new RoleService(new InMemoryRoleProvider([]), 'guest'); + $identityProvider = $this->createMock(IdentityProviderInterface::class); + $identityProvider->expects($this->once())->method('getIdentity')->willReturn(null); + $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); $result = $roleService->getIdentityRoles(null); @@ -53,9 +55,10 @@ public function testReturnGuestRoleIfNoIdentityIsGiven(): void public function testReturnGuestRoleIfGuestIdentityIsGiven(): void { - $roleService = new RoleService(new InMemoryRoleProvider([]), 'guest'); - $identity = new Identity(['guest']); + $identityProvider = $this->createMock(IdentityProviderInterface::class); + $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); + $result = $roleService->getIdentityRoles($identity); @@ -66,7 +69,8 @@ public function testReturnGuestRoleIfGuestIdentityIsGiven(): void public function testReturnTraversableRolesFromIdentityGiven(): void { - $roleService = new RoleService(new InMemoryRoleProvider([]), 'guest'); + $identityProvider = $this->createMock(IdentityProviderInterface::class); + $roleService = new RoleService($identityProvider, new InMemoryRoleProvider([]), 'guest'); $identity = $this->prophesize(IdentityInterface::class); $identity->getRoles()->willReturn($roles = new \ArrayIterator(['first', 'second', 'third'])); @@ -83,9 +87,10 @@ public function testWillNotInvokeRoleProviderIfAllRolesCollected(): void { $roleProvider = $this->prophesize(RoleProviderInterface::class); $roleProvider->getRoles(Argument::any())->shouldNotBeCalled(); + $identityProvider = $this->createMock(IdentityProviderInterface::class); - $roleService = new RoleService($roleProvider->reveal(), 'guest'); - $roles = [new Role('first'), new HierarchicalRole('second'), new Role('third')]; + $roleService = new RoleService($identityProvider, $roleProvider->reveal(), 'guest'); + $roles = [new Role('first'), new Role('second'), new Role('third')]; $identity = new Identity($roles); $result = $roleService->getIdentityRoles($identity); @@ -98,10 +103,11 @@ public function testWillNotInvokeRoleProviderIfAllRolesCollected(): void public function testWillCollectRolesOnlyIfRequired(): void { $roleProvider = $this->prophesize(RoleProviderInterface::class); - $roles = [new Role('first'), new HierarchicalRole('second'), 'third']; + $roles = [new Role('first'), new Role('second'), 'third']; $roleProvider->getRoles(['third'])->shouldBeCalled()->willReturn([new Role('third')]); + $identityProvider = $this->createMock(IdentityProviderInterface::class); - $roleService = new RoleService($roleProvider->reveal(), 'guest'); + $roleService = new RoleService($identityProvider, $roleProvider->reveal(), 'guest'); $identity = new Identity($roles); $result = $roleService->getIdentityRoles($identity); @@ -113,4 +119,13 @@ 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()); + } }