├── tests ├── tmp │ └── .gitignore ├── php.ini-unix ├── php.ini-win └── IPubTests │ ├── Permissions │ ├── files │ │ ├── extensions.neon │ │ ├── presenters.neon │ │ └── config.neon │ ├── libs │ │ ├── ResourcesProvider.php │ │ ├── DummyExtensionThree.php │ │ ├── DummyExtensionTwo.php │ │ ├── PermissionsProvider.php │ │ ├── DummyExtensionOne.php │ │ └── RolesProvider.php │ ├── ExtensionTest.phpt │ ├── ResourcesInheritanceTest.phpt │ ├── PermissionsTest.phpt │ ├── RolesInheritanceTest.phpt │ ├── AccessTest.phpt │ └── AnnotationsTest.phpt │ └── bootstrap.php ├── .travis.yml ├── src └── IPub │ └── Permissions │ ├── Exceptions │ ├── IException.php │ ├── InvalidStateException.php │ └── InvalidArgumentException.php │ ├── Access │ ├── IChecker.php │ ├── ICheckRequirements.php │ ├── LinkChecker.php │ ├── LatteChecker.php │ └── AnnotationChecker.php │ ├── DI │ ├── IRolesProvider.php │ ├── IResourcesProvider.php │ ├── IPermissionsProvider.php │ └── PermissionsExtension.php │ ├── Providers │ ├── IRolesProvider.php │ ├── IPermissionsProvider.php │ ├── IResourcesProvider.php │ ├── ResourcesProvider.php │ ├── RolesProvider.php │ └── PermissionsProvider.php │ ├── Entities │ ├── IPermission.php │ ├── IResource.php │ ├── Permission.php │ ├── Resource.php │ ├── IRole.php │ └── Role.php │ ├── Configuration.php │ ├── TPermission.php │ ├── Security │ └── Permission.php │ └── Latte │ └── Macros.php ├── .travis.composer.php ├── .scrutinizer.yml ├── composer.json ├── readme.md ├── license.md └── docs └── en └── index.md /tests/tmp/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.* -------------------------------------------------------------------------------- /tests/php.ini-unix: -------------------------------------------------------------------------------- 1 | extension = xdebug.so 2 | 3 | [xdebug] 4 | xdebug.enable = On 5 | -------------------------------------------------------------------------------- /tests/php.ini-win: -------------------------------------------------------------------------------- 1 | [PHP] 2 | extension_dir = "./ext" 3 | extension = php_xdebug.dll 4 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/files/extensions.neon: -------------------------------------------------------------------------------- 1 | extensions: 2 | - IPubTests\Permissions\Libs\DummyExtensionOne 3 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/files/presenters.neon: -------------------------------------------------------------------------------- 1 | application: 2 | mapping: 3 | *: IPubTests\Permissions\*Presenter 4 | 5 | services: 6 | # Test presenter 7 | presenters.test: 8 | class: IPubTests\Permissions\TestPresenter 9 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/files/config.neon: -------------------------------------------------------------------------------- 1 | permissions: 2 | providers: 3 | roles: FALSE 4 | permissions: \IPubTests\Permissions\Libs\PermissionsProvider 5 | resources: \IPubTests\Permissions\Libs\ResourcesProvider 6 | 7 | services: 8 | # Default roles provider 9 | providers.roles: 10 | class: IPubTests\Permissions\Libs\RolesProvider 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | env: 4 | - NETTE=default 5 | - NETTE=~2.4.0 6 | 7 | php: 8 | - 7.1 9 | - 7.2 10 | 11 | before_script: 12 | - php .travis.composer.php 13 | - composer self-update 14 | - composer install --no-interaction --prefer-source --dev 15 | 16 | script: 17 | - vendor/bin/tester -s -p php -c ./tests/php.ini-unix tests 18 | 19 | after_failure: 20 | - 'for i in $(find ./tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done' 21 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Exceptions/IException.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Exceptions 10 | * @since 1.0.0 11 | * 12 | * @date 14.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Exceptions; 18 | 19 | interface IException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /.travis.composer.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Exceptions 10 | * @since 1.0.0 11 | * 12 | * @date 14.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Exceptions; 18 | 19 | class InvalidStateException extends \InvalidArgumentException implements IException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Exceptions/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Exceptions 10 | * @since 1.0.0 11 | * 12 | * @date 14.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Exceptions; 18 | 19 | class InvalidArgumentException extends \InvalidArgumentException implements IException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /tests/IPubTests/bootstrap.php: -------------------------------------------------------------------------------- 1 | run(); 29 | } 30 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Access/IChecker.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Access 10 | * @since 1.0.0 11 | * 12 | * @date 13.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Access; 18 | 19 | /** 20 | * Checker interface 21 | * 22 | * @package iPublikuj:Permissions! 23 | * @subpackage Access 24 | * 25 | * @author Adam Kadlec 26 | */ 27 | interface IChecker 28 | { 29 | /** 30 | * @param mixed $element 31 | * 32 | * @return bool 33 | */ 34 | function isAllowed($element) : bool; 35 | } 36 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/libs/ResourcesProvider.php: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 2.0.0 12 | * 13 | * @date 01.12.16 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions\Libs; 19 | 20 | use IPub\Permissions\Providers; 21 | 22 | class ResourcesProvider extends Providers\ResourcesProvider 23 | { 24 | public function __construct() 25 | { 26 | $this->addResource('firstResource'); 27 | $this->addResource('secondResource'); 28 | $this->addResource('thirdResource'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/IPub/Permissions/DI/IRolesProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage DI 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\DI; 18 | 19 | /** 20 | * Extension providers interface for roles 21 | * 22 | * @package iPublikuj:Permissions! 23 | * @subpackage DI 24 | * 25 | * @author Adam Kadlec 26 | */ 27 | interface IRolesProvider 28 | { 29 | /** 30 | * Return array of users roles 31 | * 32 | * @return array 33 | */ 34 | function getRoles() : array; 35 | } 36 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | php: true 3 | 4 | build: 5 | environment: 6 | php: 7 | version: 7.1.0 8 | 9 | dependencies: 10 | before: 11 | - 12 | command: 'mkdir -p build/logs' 13 | 14 | tests: 15 | override: 16 | - 17 | command: 'vendor/bin/tester tests -p php -c ./tests/php.ini-unix --coverage build/logs/clover.xml --coverage-src src' 18 | coverage: 19 | file: build/logs/clover.xml 20 | format: php-clover 21 | 22 | filter: 23 | excluded_paths: 24 | - tests/* 25 | 26 | coding_style: 27 | php: 28 | indentation: 29 | general: 30 | use_tabs: true 31 | spaces: 32 | before_parentheses: 33 | closure_definition: true 34 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Access/ICheckRequirements.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Access 10 | * @since 1.0.0 11 | * 12 | * @date 13.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Access; 18 | 19 | /** 20 | * Requirements checker interface 21 | * 22 | * @package iPublikuj:Permissions! 23 | * @subpackage Access 24 | * 25 | * @author Adam Kadlec 26 | */ 27 | interface ICheckRequirements 28 | { 29 | /** 30 | * @param mixed $element 31 | * 32 | * @return bool 33 | */ 34 | function isAllowed($element) : bool; 35 | } 36 | -------------------------------------------------------------------------------- /src/IPub/Permissions/DI/IResourcesProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage DI 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\DI; 18 | 19 | /** 20 | * Extension providers interface for resources 21 | * 22 | * @package iPublikuj:Permissions! 23 | * @subpackage DI 24 | * 25 | * @author Adam Kadlec 26 | */ 27 | interface IResourcesProvider 28 | { 29 | /** 30 | * Return array of access resources 31 | * 32 | * @return array 33 | */ 34 | function getResources() : array; 35 | } 36 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Providers/IRolesProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Providers 10 | * @since 1.0.0 11 | * 12 | * @date 10.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Providers; 18 | 19 | use IPub\Permissions\Entities; 20 | 21 | /** 22 | * Roles provider interface 23 | * 24 | * @package iPublikuj:Permissions! 25 | * @subpackage Providers 26 | * 27 | * @author Adam Kadlec 28 | */ 29 | interface IRolesProvider 30 | { 31 | /** 32 | * @return Entities\IRole[] 33 | */ 34 | public function findAll() : array; 35 | } 36 | -------------------------------------------------------------------------------- /src/IPub/Permissions/DI/IPermissionsProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage DI 10 | * @since 1.0.0 11 | * 12 | * @date 12.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\DI; 18 | 19 | /** 20 | * Extension providers interface for permissions 21 | * 22 | * @package iPublikuj:Permissions! 23 | * @subpackage DI 24 | * 25 | * @author Adam Kadlec 26 | */ 27 | interface IPermissionsProvider 28 | { 29 | /** 30 | * Return array of permissions 31 | * 32 | * @return array 33 | */ 34 | function getPermissions() : array; 35 | } 36 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Providers/IPermissionsProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Providers 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Providers; 18 | 19 | use IPub\Permissions\Entities; 20 | 21 | /** 22 | * Permissions provider interface 23 | * 24 | * @package iPublikuj:Permissions! 25 | * @subpackage Providers 26 | * 27 | * @author Adam Kadlec 28 | */ 29 | interface IPermissionsProvider 30 | { 31 | /** 32 | * @return Entities\IPermission[] 33 | */ 34 | public function findAll() : array; 35 | } 36 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Providers/IResourcesProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Providers 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Providers; 18 | 19 | use IPub\Permissions\Entities; 20 | 21 | /** 22 | * Permission resources provider interface 23 | * 24 | * @package iPublikuj:Permissions! 25 | * @subpackage Providers 26 | * 27 | * @author Adam Kadlec 28 | */ 29 | interface IResourcesProvider 30 | { 31 | /** 32 | * @return Entities\IResource[] 33 | */ 34 | public function findAll() : array; 35 | } 36 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/libs/DummyExtensionThree.php: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 2.0.0 12 | * 13 | * @date 01.12.16 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions\Libs; 19 | 20 | use Nette; 21 | use Nette\DI; 22 | 23 | use IPub\Permissions; 24 | 25 | class DummyExtensionThree extends DI\CompilerExtension implements Permissions\DI\IPermissionsProvider 26 | { 27 | public function getPermissions() : array 28 | { 29 | return [ 30 | [ 31 | 'resource' => 'fromExtensionResource', 32 | 'wrongKey' => 'fromExtensionPrivilege', 33 | ], 34 | ]; 35 | } 36 | 37 | /** 38 | * @param Nette\Configurator $config 39 | * @param string $extensionName 40 | * 41 | * @return void 42 | */ 43 | public static function register(Nette\Configurator $config, $extensionName = 'dummyThree') 44 | { 45 | $config->onCompile[] = function (Nette\Configurator $config, Nette\DI\Compiler $compiler) use ($extensionName) { 46 | $compiler->addExtension($extensionName, new DummyExtensionThree()); 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/libs/DummyExtensionTwo.php: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 2.0.0 12 | * 13 | * @date 01.12.16 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions\Libs; 19 | 20 | use Nette; 21 | use Nette\DI; 22 | 23 | use IPub\Permissions; 24 | 25 | class DummyExtensionTwo extends DI\CompilerExtension implements Permissions\DI\IPermissionsProvider 26 | { 27 | public function getPermissions() : array 28 | { 29 | return [ 30 | 'wrongStringVersion' => [ 31 | 'title' => 'This is example title', 32 | 'description' => 'This is example description' 33 | ], 34 | ]; 35 | } 36 | 37 | /** 38 | * @param Nette\Configurator $config 39 | * @param string $extensionName 40 | * 41 | * @return void 42 | */ 43 | public static function register(Nette\Configurator $config, $extensionName = 'dummyTwo') 44 | { 45 | $config->onCompile[] = function (Nette\Configurator $config, Nette\DI\Compiler $compiler) use ($extensionName) { 46 | $compiler->addExtension($extensionName, new DummyExtensionTwo()); 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/libs/PermissionsProvider.php: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 2.0.0 12 | * 13 | * @date 01.12.16 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions\Libs; 19 | 20 | use IPub\Permissions\Providers; 21 | 22 | class PermissionsProvider extends Providers\PermissionsProvider 23 | { 24 | public function __construct(ResourcesProvider $resourcesProvider) 25 | { 26 | $this->addPermission($resourcesProvider->getResource('firstResource'), 'firstPrivilege', [ 27 | 'title' => 'This is first example title', 28 | 'description' => 'This is first example description', 29 | ]); 30 | 31 | $this->addPermission($resourcesProvider->getResource('secondResource'), 'secondPrivilege', [ 32 | 'title' => 'This is second example title', 33 | 'description' => 'This is second example description', 34 | ]); 35 | 36 | $this->addPermission($resourcesProvider->getResource('thirdResource'), 'thirdPrivilege', [ 37 | 'title' => 'This is third example title', 38 | 'description' => 'This is third example description', 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ipub/permissions", 3 | "type" : "library", 4 | "description" : "Simple permission checker for Nette Framework", 5 | "keywords" : ["nette", "permission", "acl", "tools", "ipub", "ipublikuj"], 6 | "homepage" : "https://github.com/iPublikuj/permissions", 7 | "license" : ["GPL-2.0", "GPL-3.0"], 8 | 9 | "authors": [ 10 | { 11 | "name" : "iPublikuj:cms", 12 | "email" : "info@ipublikuj.eu", 13 | "homepage" : "https://www.ipublikuj.eu/" 14 | } 15 | ], 16 | 17 | "support": { 18 | "email" :"support@ipublikuj.eu", 19 | "issues" :"https://github.com/iPublikuj/permissions/issues" 20 | }, 21 | 22 | "extra": { 23 | "ipub" : { 24 | "configuration" : { 25 | "extensions" : { 26 | "permissions" : "IPub\\Permissions\\DI\\PermissionsExtension" 27 | } 28 | } 29 | } 30 | }, 31 | 32 | "require": { 33 | "php" : ">=7.1.0", 34 | 35 | "nette/application" : "~2.4", 36 | "nette/di" : "~2.4", 37 | "nette/utils" : "~2.4", 38 | "nette/security" : "~2.4", 39 | 40 | "latte/latte" : "~2.4" 41 | }, 42 | 43 | "require-dev": { 44 | "nette/bootstrap" : "~2.4", 45 | "nette/mail" : "~2.4", 46 | "nette/robot-loader" : "~2.4", 47 | "nette/safe-stream" : "~2.3", 48 | "nette/tester" : "~2.0", 49 | 50 | "tracy/tracy" : "~2.4", 51 | 52 | "pds/skeleton" : "~1.0" 53 | }, 54 | 55 | "autoload": { 56 | "psr-0": { 57 | "IPub\\Permissions\\": "src/" 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Entities/IPermission.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Entities 10 | * @since 1.0.0 11 | * 12 | * @date 13.03.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Entities; 18 | 19 | interface IPermission 20 | { 21 | /** 22 | * Permission string delimiter 23 | * 24 | * @var string 25 | */ 26 | public const DELIMITER = ':'; 27 | 28 | /** 29 | * Get permission resource 30 | * 31 | * @return IResource|NULL 32 | */ 33 | function getResource() : ?IResource; 34 | 35 | /** 36 | * Get permission privilege 37 | * 38 | * @return string|NULL 39 | */ 40 | function getPrivilege() : ?string; 41 | 42 | /** 43 | * Get permission assertion callback 44 | * 45 | * @return callable|NULL 46 | */ 47 | public function getAssertion() : ?callable; 48 | 49 | /** 50 | * Set permission details like title, description, etc. 51 | * 52 | * @param array $details 53 | * 54 | * @return void 55 | */ 56 | function setDetails(array $details) : void; 57 | 58 | /** 59 | * Get permission title 60 | * 61 | * @return string|NULL 62 | */ 63 | function getTitle() : ?string; 64 | 65 | /** 66 | * Get permission title 67 | * 68 | * @return string|NULL 69 | */ 70 | function getDescription() : ?string; 71 | } 72 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Entities/IResource.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Entities 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Entities; 18 | 19 | use Nette\Security as NS; 20 | 21 | interface IResource extends NS\IResource 22 | { 23 | /** 24 | * @param IResource|NULL $parent 25 | * 26 | * @return void 27 | */ 28 | function setParent(?IResource $parent = NULL) : void; 29 | 30 | /** 31 | * @return IResource|NULL 32 | */ 33 | function getParent() : ?IResource; 34 | 35 | /** 36 | * @param IResource[] $resources 37 | * 38 | * @return void 39 | */ 40 | function setChildren(array $resources) : void; 41 | 42 | /** 43 | * @param IResource $resource 44 | * 45 | * @return void 46 | */ 47 | function addChild(IResource $resource) : void; 48 | 49 | /** 50 | * @return IResource[] 51 | */ 52 | function getChildren() : array; 53 | 54 | /** 55 | * @param string $name 56 | * 57 | * @return void 58 | */ 59 | function setName(string $name) : void; 60 | 61 | /** 62 | * @return string|NULL 63 | */ 64 | function getName() : ?string; 65 | 66 | /** 67 | * @param string $comment 68 | * 69 | * @return void 70 | */ 71 | function setComment(string $comment) : void; 72 | 73 | /** 74 | * @return string|NULL 75 | */ 76 | function getComment() : ?string; 77 | } 78 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Configuration.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage common 10 | * @since 2.0.0 11 | * 12 | * @date 17.02.15 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions; 18 | 19 | use Nette; 20 | use Nette\Application; 21 | 22 | /** 23 | * Permissions's extension configuration storage. Store basic extension settings 24 | * 25 | * @package iPublikuj:Permissions! 26 | * @subpackage common 27 | * 28 | * @author Adam Kadlec 29 | */ 30 | class Configuration 31 | { 32 | /** 33 | * Implement nette smart magic 34 | */ 35 | use Nette\SmartObject; 36 | 37 | /** 38 | * @var string|NULL 39 | */ 40 | private $redirectUrl; 41 | 42 | /** 43 | * @var Application\LinkGenerator 44 | */ 45 | private $linkGenerator; 46 | 47 | /** 48 | * @param string|NULL $redirectUrl 49 | * @param Application\LinkGenerator $linkGenerator 50 | */ 51 | public function __construct(?string $redirectUrl = NULL, Application\LinkGenerator $linkGenerator) 52 | { 53 | $this->redirectUrl = $redirectUrl; 54 | $this->linkGenerator = $linkGenerator; 55 | } 56 | 57 | /** 58 | * Build the URL for redirection if is set 59 | * 60 | * @param array $params 61 | * 62 | * @return string|NULL 63 | * 64 | * @throws Application\UI\InvalidLinkException 65 | */ 66 | public function getRedirectUrl(array $params = []) : ?string 67 | { 68 | if ($this->redirectUrl) { 69 | return $this->linkGenerator->link($this->redirectUrl, $params); 70 | } 71 | 72 | return NULL; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/libs/DummyExtensionOne.php: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 2.0.0 12 | * 13 | * @date 01.12.16 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions\Libs; 19 | 20 | use Nette; 21 | use Nette\DI; 22 | 23 | use IPub\Permissions; 24 | 25 | class DummyExtensionOne extends DI\CompilerExtension implements Permissions\DI\IPermissionsProvider 26 | { 27 | public function getPermissions() : array 28 | { 29 | return [ 30 | 'fromExtensionResourceOne:fromExtensionPrivilegeOne' => [ 31 | 'title' => 'This is example title', 32 | 'description' => 'This is example description', 33 | ], 34 | new Permissions\Entities\Permission( 35 | new Permissions\Entities\Resource('fromExtensionResourceTwo'), 36 | 'fromExtensionPrivilegeTwo', 37 | [ 38 | 'title' => 'This is second example title', 39 | 'description' => 'This is second example description', 40 | ] 41 | ), 42 | [ 43 | 'resource' => 'fromExtensionResourceThree', 44 | 'privilege' => 'fromExtensionPrivilegeThree', 45 | ], 46 | ]; 47 | } 48 | 49 | /** 50 | * @param Nette\Configurator $config 51 | * @param string $extensionName 52 | * 53 | * @return void 54 | */ 55 | public static function register(Nette\Configurator $config, $extensionName = 'dummyOne') 56 | { 57 | $config->onCompile[] = function (Nette\Configurator $config, Nette\DI\Compiler $compiler) use ($extensionName) { 58 | $compiler->addExtension($extensionName, new DummyExtensionOne()); 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/IPub/Permissions/TPermission.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage common 10 | * @since 1.0.0 11 | * 12 | * @date 13.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions; 18 | 19 | use Nette\Application; 20 | 21 | /** 22 | * Helper trait 23 | * 24 | * @package iPublikuj:Permissions! 25 | * @subpackage common 26 | * 27 | * @author Adam Kadlec 28 | */ 29 | trait TPermission 30 | { 31 | /** 32 | * @var Configuration 33 | */ 34 | protected $permissionConfiguration; 35 | 36 | /** 37 | * @var Access\ICheckRequirements 38 | */ 39 | protected $requirementsChecker; 40 | 41 | /** 42 | * @param Access\ICheckRequirements $requirementsChecker 43 | * @param Configuration $configuration 44 | */ 45 | public function injectPermission( 46 | Access\ICheckRequirements $requirementsChecker, 47 | Configuration $configuration 48 | ) { 49 | $this->requirementsChecker = $requirementsChecker; 50 | $this->permissionConfiguration = $configuration; 51 | } 52 | 53 | /** 54 | * @param mixed $element 55 | * 56 | * @return void 57 | * 58 | * @throws Application\ForbiddenRequestException 59 | * @throws Application\UI\InvalidLinkException 60 | */ 61 | public function checkRequirements($element) : void 62 | { 63 | $redirectUrl = $this->permissionConfiguration->getRedirectUrl([ 64 | 'backlink' => $this->storeRequest(), 65 | ]); 66 | 67 | try { 68 | parent::checkRequirements($element); 69 | 70 | if (!$this->requirementsChecker->isAllowed($element)) { 71 | throw new Application\ForbiddenRequestException; 72 | } 73 | 74 | } catch (Application\ForbiddenRequestException $ex) { 75 | if ($redirectUrl) { 76 | $this->getPresenter()->redirectUrl($redirectUrl); 77 | 78 | } else { 79 | throw $ex; 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Providers/ResourcesProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Providers 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Providers; 18 | 19 | use Nette; 20 | 21 | use IPub\Permissions\Entities; 22 | use IPub\Permissions\Exceptions; 23 | 24 | /** 25 | * Basic resources provider 26 | * 27 | * @package iPublikuj:Permissions! 28 | * @subpackage Providers 29 | * 30 | * @author Adam Kadlec 31 | */ 32 | class ResourcesProvider implements IResourcesProvider 33 | { 34 | /** 35 | * Implement nette smart magic 36 | */ 37 | use Nette\SmartObject; 38 | 39 | /** 40 | * @var Entities\IResource[] 41 | */ 42 | private $resources = []; 43 | 44 | /** 45 | * @param string $id 46 | * @param Entities\IResource|NULL $parent 47 | * 48 | * @return Entities\IResource 49 | */ 50 | public function addResource(string $id, ?Entities\IResource $parent = NULL) : Entities\IResource 51 | { 52 | if (array_key_exists($id, $this->resources)) { 53 | return $this->resources[$id]; 54 | } 55 | 56 | $resource = new Entities\Resource($id); 57 | 58 | if ($parent) { 59 | $resource->setParent($parent); 60 | } 61 | 62 | $this->resources[$id] = $resource; 63 | 64 | return $resource; 65 | } 66 | 67 | /** 68 | * @param string $id 69 | * 70 | * @return Entities\IResource 71 | */ 72 | public function getResource(string $id) : Entities\IResource 73 | { 74 | if (!array_key_exists($id, $this->resources)) { 75 | throw new Exceptions\InvalidStateException(sprintf('Resource "%s" is not registered.', $id)); 76 | } 77 | 78 | return $this->resources[$id]; 79 | } 80 | 81 | /** 82 | * @return Entities\IResource[] 83 | */ 84 | public function findAll() : array 85 | { 86 | return $this->resources; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Permissions 2 | 3 | [![Build Status](https://img.shields.io/travis/iPublikuj/permissions.svg?style=flat-square)](https://travis-ci.org/iPublikuj/permissions) 4 | [![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/iPublikuj/permissions.svg?style=flat-square)](https://scrutinizer-ci.com/g/iPublikuj/permissions/?branch=master) 5 | [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/iPublikuj/permissions.svg?style=flat-square)](https://scrutinizer-ci.com/g/iPublikuj/permissions/?branch=master) 6 | [![Latest Stable Version](https://img.shields.io/packagist/v/ipub/permissions.svg?style=flat-square)](https://packagist.org/packages/ipub/permissions) 7 | [![Composer Downloads](https://img.shields.io/packagist/dt/ipub/permissions.svg?style=flat-square)](https://packagist.org/packages/ipub/permissions) 8 | [![License](https://img.shields.io/packagist/l/ipub/permissions.svg?style=flat-square)](https://packagist.org/packages/ipub/permissions) 9 | 10 | Simple permission checker for [Nette Framework](http://nette.org/) 11 | 12 | ## Installation 13 | 14 | The best way to install ipub/permissions is using [Composer](http://getcomposer.org/): 15 | 16 | ```sh 17 | $ composer require ipub/permissions:@dev 18 | ``` 19 | 20 | After that you have to register extension in config.neon. 21 | 22 | ```neon 23 | extensions: 24 | permission: IPub\Permissions\DI\PermissionsExtension 25 | ``` 26 | 27 | Package contains trait, which you will have to use in presenter to override default **checkRequirements** method. This works only for PHP 5.4+, for older version you can simply copy trait content and paste it into class where you want to use it. 28 | 29 | ```php 30 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 1.0.0 12 | * 13 | * @date 13.01.15 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions; 19 | 20 | use Nette; 21 | 22 | use Tester; 23 | use Tester\Assert; 24 | 25 | use IPub\Permissions; 26 | 27 | require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'bootstrap.php'; 28 | require __DIR__ . DS . 'libs' . DS . 'ResourcesProvider.php'; 29 | require __DIR__ . DS . 'libs' . DS . 'PermissionsProvider.php'; 30 | require __DIR__ . DS . 'libs' . DS . 'RolesProvider.php'; 31 | 32 | class ExtensionTest extends Tester\TestCase 33 | { 34 | public function testFunctional() : void 35 | { 36 | $dic = $this->createContainer(); 37 | 38 | Assert::true($dic->getService('permissions.permissions') instanceof Permissions\Security\Permission); 39 | Assert::true($dic->getService('permissions.config') instanceof Permissions\Configuration); 40 | 41 | Assert::true($dic->getService('permissions.providers.permissions') instanceof Permissions\Providers\IPermissionsProvider); 42 | 43 | Assert::true($dic->getService('permissions.checkers.annotation') instanceof Permissions\Access\AnnotationChecker); 44 | Assert::true($dic->getService('permissions.checkers.latte') instanceof Permissions\Access\LatteChecker); 45 | Assert::true($dic->getService('permissions.checkers.link') instanceof Permissions\Access\LinkChecker); 46 | } 47 | 48 | /** 49 | * @return Nette\DI\Container 50 | */ 51 | private function createContainer() : Nette\DI\Container 52 | { 53 | $config = new Nette\Configurator(); 54 | $config->setTempDirectory(TEMP_DIR); 55 | 56 | Permissions\DI\PermissionsExtension::register($config); 57 | 58 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'config.neon'); 59 | 60 | return $config->createContainer(); 61 | } 62 | } 63 | 64 | \run(new ExtensionTest()); 65 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Providers/RolesProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Providers 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Providers; 18 | 19 | use Nette; 20 | 21 | use IPub\Permissions\Entities; 22 | use IPub\Permissions\Exceptions; 23 | 24 | /** 25 | * Basic roles provider 26 | * 27 | * @package iPublikuj:Permissions! 28 | * @subpackage Providers 29 | * 30 | * @author Adam Kadlec 31 | */ 32 | class RolesProvider implements IRolesProvider 33 | { 34 | /** 35 | * Implement nette smart magic 36 | */ 37 | use Nette\SmartObject; 38 | 39 | /** 40 | * @var Entities\IRole[] 41 | */ 42 | private $roles = []; 43 | 44 | /** 45 | * @param string $id 46 | * @param Entities\IRole|NULL $parent 47 | * @param Entities\IPermission|Entities\IPermission[]|NULL $permissions 48 | * 49 | * @return Entities\IRole 50 | */ 51 | public function addRole(string $id, ?Entities\IRole $parent = NULL, $permissions = NULL) : Entities\IRole 52 | { 53 | if (array_key_exists($id, $this->roles)) { 54 | throw new Exceptions\InvalidStateException(sprintf('Role "%s" has been already added.', $id)); 55 | } 56 | 57 | if ($permissions instanceof Entities\IPermission) { 58 | $permissions = [$permissions]; 59 | } 60 | 61 | $role = new Entities\Role($id); 62 | 63 | if ($parent) { 64 | $role->setParent($parent); 65 | } 66 | 67 | if ($permissions) { 68 | $role->setPermissions($permissions); 69 | } 70 | 71 | $this->roles[$id] = $role; 72 | 73 | return $role; 74 | } 75 | 76 | /** 77 | * @param string $roleName 78 | * 79 | * @return Entities\IRole 80 | */ 81 | public function getRole(string $roleName) : Entities\IRole 82 | { 83 | if (!array_key_exists($roleName, $this->roles)) { 84 | throw new Exceptions\InvalidArgumentException(sprintf('Role "%s" is not registered.', $roleName)); 85 | } 86 | 87 | return $this->roles[$roleName]; 88 | } 89 | 90 | /** 91 | * @return Entities\IRole[] 92 | */ 93 | public function findAll() : array 94 | { 95 | return $this->roles; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Providers/PermissionsProvider.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Providers 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Providers; 18 | 19 | use Nette; 20 | 21 | use IPub\Permissions\Entities; 22 | use IPub\Permissions\Exceptions; 23 | 24 | /** 25 | * Basic permissions provider 26 | * 27 | * @package iPublikuj:Permissions! 28 | * @subpackage Providers 29 | * 30 | * @author Adam Kadlec 31 | */ 32 | class PermissionsProvider implements IPermissionsProvider 33 | { 34 | /** 35 | * Implement nette smart magic 36 | */ 37 | use Nette\SmartObject; 38 | 39 | /** 40 | * @var Entities\IPermission[] 41 | */ 42 | private $permissions = []; 43 | 44 | /** 45 | * @param Entities\IResource|NULL $resource 46 | * @param string|NULL $privilege 47 | * @param array|NULL $details 48 | * @param callable|NULL $assertion 49 | * 50 | * @return Entities\IPermission 51 | */ 52 | public function addPermission(?Entities\IResource $resource = NULL, ?string $privilege = NULL, ?array $details = NULL, ?callable $assertion = NULL) : Entities\IPermission 53 | { 54 | $permission = new Entities\Permission($resource, $privilege, $details, $assertion); 55 | 56 | if (array_key_exists((string) $permission, $this->permissions)) { 57 | throw new Exceptions\InvalidStateException(sprintf('Permission "%s" is already registered.', (string) $permission)); 58 | } 59 | 60 | $this->permissions[(string) $permission] = $permission; 61 | 62 | return $permission; 63 | } 64 | 65 | /** 66 | * @param string $id 67 | * 68 | * @return Entities\IPermission 69 | */ 70 | public function getPermission(string $id) : Entities\IPermission 71 | { 72 | if (!array_key_exists($id, $this->permissions)) { 73 | throw new Exceptions\InvalidStateException(sprintf('Permission "%s" is not registered.', $id)); 74 | } 75 | 76 | return $this->permissions[$id]; 77 | } 78 | 79 | /** 80 | * @return Entities\IPermission[] 81 | */ 82 | public function findAll() : array 83 | { 84 | return $this->permissions; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/libs/RolesProvider.php: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 1.0.0 12 | * 13 | * @date 13.01.15 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions\Libs; 19 | 20 | use IPub\Permissions\Entities; 21 | use IPub\Permissions\Providers; 22 | 23 | class RolesProvider extends Providers\RolesProvider 24 | { 25 | public function __construct(PermissionsProvider $permissionsProvider) 26 | { 27 | // Create guest role 28 | $guest = $this->addRole(Entities\IRole::ROLE_ANONYMOUS, NULL, $permissionsProvider->getPermission('firstResource:firstPrivilege')); 29 | $guest->setName('Guest'); 30 | 31 | // Create authenticated role 32 | $authenticated = $this->addRole(Entities\IRole::ROLE_AUTHENTICATED, $guest, [ 33 | $permissionsProvider->getPermission('firstResource:firstPrivilege'), 34 | $permissionsProvider->getPermission('secondResource:secondPrivilege'), 35 | ]); 36 | $authenticated->setName('Registered user'); 37 | 38 | $administrator = $this->addRole(Entities\IRole::ROLE_ADMINISTRATOR, NULL, [ 39 | $permissionsProvider->getPermission('firstResource:firstPrivilege'), 40 | $permissionsProvider->getPermission('secondResource:secondPrivilege'), 41 | $permissionsProvider->getPermission('thirdResource:thirdPrivilege'), 42 | ]); 43 | $administrator->setName('Administrator'); 44 | 45 | $customChild = $this->addRole('user-defined-child-role'); 46 | $customChild->setName('User Defined Child'); 47 | $customChild->setComment('Registered in custom role as children of another role'); 48 | 49 | $custom = $this->addRole('user-defined-role', NULL, [ 50 | $permissionsProvider->getPermission('firstResource:firstPrivilege'), 51 | $permissionsProvider->getPermission('thirdResource:thirdPrivilege'), 52 | ]); 53 | $custom->addChild($customChild); 54 | $custom->setName('User Defined'); 55 | $custom->setComment('Registered in custom role'); 56 | 57 | $customInherited = $this->addRole('user-defined-inherited-role'); 58 | $customInherited->setParent($custom); 59 | $customInherited->setName('User Defined Inherited'); 60 | $customInherited->setComment('Registered in custom role inheriting another role'); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | Good news everyone! You may use iPublikuj Framework under the terms of either 5 | the New BSD License or the GNU General Public License (GPL) version 2 or 3. 6 | 7 | The BSD License is recommended for most projects. It is easy to understand and it 8 | places almost no restrictions on what you can do with the framework. If the GPL 9 | fits better to your project, you can use the framework under this license. 10 | 11 | You don't have to notify anyone which license you are using. You can freely 12 | use iPublikuj Framework in commercial projects as long as the copyright header 13 | remains intact. 14 | 15 | New BSD License 16 | --------------- 17 | 18 | Copyright (c) 2014 Adam Kadlec 19 | All rights reserved. 20 | 21 | Redistribution and use in source and binary forms, with or without modification, 22 | are permitted provided that the following conditions are met: 23 | 24 | * Redistributions of source code must retain the above copyright notice, 25 | this list of conditions and the following disclaimer. 26 | 27 | * Redistributions in binary form must reproduce the above copyright notice, 28 | this list of conditions and the following disclaimer in the documentation 29 | and/or other materials provided with the distribution. 30 | 31 | * Neither the name of "iPublikuj Framework" nor the names of its contributors 32 | may be used to endorse or promote products derived from this software 33 | without specific prior written permission. 34 | 35 | This software is provided by the copyright holders and contributors "as is" and 36 | any express or implied warranties, including, but not limited to, the implied 37 | warranties of merchantability and fitness for a particular purpose are 38 | disclaimed. In no event shall the copyright owner or contributors be liable for 39 | any direct, indirect, incidental, special, exemplary, or consequential damages 40 | (including, but not limited to, procurement of substitute goods or services; 41 | loss of use, data, or profits; or business interruption) however caused and on 42 | any theory of liability, whether in contract, strict liability, or tort 43 | (including negligence or otherwise) arising in any way out of the use of this 44 | software, even if advised of the possibility of such damage. 45 | 46 | 47 | 48 | GNU General Public License 49 | -------------------------- 50 | 51 | GPL licenses are very very long, so instead of including them here we offer 52 | you URLs with full text: 53 | 54 | - [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) 55 | - [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) -------------------------------------------------------------------------------- /src/IPub/Permissions/Security/Permission.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Security 10 | * @since 1.0.0 11 | * 12 | * @date 10.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Security; 18 | 19 | use Nette\Security as NS; 20 | 21 | use IPub\Permissions\Entities; 22 | use IPub\Permissions\Providers; 23 | 24 | /** 25 | * Nette user permission 26 | * 27 | * @package iPublikuj:Permissions! 28 | * @subpackage Security 29 | * 30 | * @author Adam Kadlec 31 | */ 32 | class Permission extends NS\Permission implements NS\IAuthorizator 33 | { 34 | /** 35 | * @param Providers\IRolesProvider $rolesProvider 36 | * @param Providers\IResourcesProvider $resourcesProvider 37 | */ 38 | public function __construct( 39 | Providers\IRolesProvider $rolesProvider, 40 | Providers\IResourcesProvider $resourcesProvider 41 | ) { 42 | // Get all available resources 43 | $resources = $resourcesProvider->findAll(); 44 | 45 | foreach ($resources as $resource) { 46 | $resourceParent = $resource->getParent(); 47 | 48 | // Assign resource to application permission checker 49 | $this->addResource($resource->getResourceId(), $resourceParent ? $resourceParent->getResourceId() : NULL); 50 | } 51 | 52 | // Get all available roles 53 | $roles = $rolesProvider->findAll(); 54 | 55 | // Register all available roles 56 | foreach ($roles as $role) { 57 | $this->checkAndAddRole($role); 58 | 59 | // Allow all privileges for administrator 60 | if ($role->isAdministrator()) { 61 | $this->allow($role->getRoleId(), self::ALL, self::ALL); 62 | 63 | // For others apply setup privileges 64 | } else { 65 | foreach ($role->getPermissions() as $permission) { 66 | $resource = $permission->getResource(); 67 | $resource = $resource ? $resource->getResourceId() : NS\IAuthorizator::ALL; 68 | 69 | $this->allow($role->getRoleId(), $resource, $permission->getPrivilege(), $permission->getAssertion()); 70 | } 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * @param Entities\IRole $role 77 | * 78 | * @return void 79 | */ 80 | private function checkAndAddRole(Entities\IRole $role) : void 81 | { 82 | $roleParent = $role->getParent(); 83 | 84 | if ($roleParent) { 85 | $this->checkAndAddRole($roleParent); 86 | } 87 | 88 | // Assign role to application permission checker 89 | if (!$this->hasRole($role->getRoleId())) { 90 | $this->addRole($role->getRoleId(), $roleParent ? $roleParent->getRoleId() : NULL); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Entities/Permission.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Entities 10 | * @since 1.0.0 11 | * 12 | * @date 13.03.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Entities; 18 | 19 | use Nette; 20 | use Nette\Utils; 21 | 22 | class Permission implements IPermission 23 | { 24 | /** 25 | * Implement nette smart magic 26 | */ 27 | use Nette\SmartObject; 28 | 29 | /** 30 | * Permission resource 31 | * 32 | * @var string 33 | */ 34 | private $resource; 35 | 36 | /** 37 | * Permission privilege 38 | * 39 | * @var string 40 | */ 41 | private $privilege; 42 | 43 | /** 44 | * @var callable|NULL 45 | */ 46 | private $assertion; 47 | 48 | /** 49 | * Permission details 50 | * 51 | * @var Utils\ArrayHash 52 | */ 53 | private $details; 54 | 55 | /** 56 | * @param IResource|NULL $resource 57 | * @param string|NULL $privilege 58 | * @param array $details 59 | * @param callable|NULL $assertion 60 | */ 61 | public function __construct(?IResource $resource = NULL, ?string $privilege = NULL, $details = [], ?callable $assertion = NULL) 62 | { 63 | $this->resource = $resource; 64 | $this->privilege = $privilege; 65 | $this->assertion = $assertion; 66 | 67 | // Check if permission details are provided too 68 | if (!empty($details)) { 69 | $this->details = Utils\ArrayHash::from($details); 70 | } 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function getResource() : ?IResource 77 | { 78 | return $this->resource; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | */ 84 | public function getPrivilege() : ?string 85 | { 86 | return $this->privilege; 87 | } 88 | 89 | /** 90 | * {@inheritdoc} 91 | */ 92 | public function getAssertion() : ?callable 93 | { 94 | return $this->assertion; 95 | } 96 | 97 | /** 98 | * {@inheritdoc} 99 | */ 100 | public function setDetails(array $details) : void 101 | { 102 | $this->details = Utils\ArrayHash::from($details); 103 | } 104 | 105 | /** 106 | * {@inheritdoc} 107 | */ 108 | public function getTitle() : ?string 109 | { 110 | return $this->details->offsetExists('title') ? $this->details->offsetGet('title') : NULL; 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | public function getDescription() : ?string 117 | { 118 | return $this->details->offsetExists('description') ? $this->details->offsetGet('description') : NULL; 119 | } 120 | 121 | /** 122 | * Convert permission object to string 123 | * 124 | * @return string 125 | */ 126 | public function __toString() 127 | { 128 | return ((string) $this->resource) . self::DELIMITER . $this->privilege; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/ResourcesInheritanceTest.phpt: -------------------------------------------------------------------------------- 1 | createContainer(); 48 | 49 | // Get permissions service 50 | $this->permission = $dic->getService('permissions.permissions'); 51 | } 52 | 53 | 54 | public function testResourcePermissionsInheriting() : void 55 | { 56 | Assert::true($this->permission->isAllowed('authenticated', 'firstResource', 'firstPrivilege'), 57 | 'Role "authenticated" can access "firstResource:firstPrivilege"'); 58 | 59 | Assert::false($this->permission->isAllowed('authenticated', 'firstResource'), 60 | 'Role "authenticated" can not access all privileges from "firstResource:" resource'); 61 | 62 | Assert::false($this->permission->isAllowed('authenticated', 'thirdResource', 'thirdPrivilege'), 63 | 'Role "authenticated" can not access "thirdResource:thirdPrivilege"'); 64 | 65 | Assert::true($this->permission->isAllowed('user-defined-child-role', 'firstResource', 'firstPrivilege'), 66 | 'Role "user-defined-child-role" can access "firstResource:firstPrivilege"'); 67 | 68 | Assert::true($this->permission->isAllowed('user-defined-role', 'firstResource', 'firstPrivilege'), 69 | 'Role "user-defined-role" can access "firstResource:firstPrivilege"'); 70 | 71 | Assert::true($this->permission->isAllowed('user-defined-inherited-role', 'firstResource', 'firstPrivilege'), 72 | 'Role "user-defined-inherited-role" can access "firstResource:firstPrivilege"'); 73 | } 74 | 75 | /** 76 | * @return Nette\DI\Container 77 | */ 78 | private function createContainer() : Nette\DI\Container 79 | { 80 | $config = new Nette\Configurator(); 81 | $config->setTempDirectory(TEMP_DIR); 82 | 83 | Permissions\DI\PermissionsExtension::register($config); 84 | 85 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'config.neon'); 86 | 87 | return $config->createContainer(); 88 | } 89 | } 90 | 91 | \run(new ResourcesInheritanceTest()); 92 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/PermissionsTest.phpt: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 1.0.0 12 | * 13 | * @date 14.01.15 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions; 19 | 20 | use Nette; 21 | 22 | use Tester; 23 | use Tester\Assert; 24 | 25 | use IPub\Permissions; 26 | 27 | require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'bootstrap.php'; 28 | 29 | require __DIR__ . DS . 'libs' . DS . 'ResourcesProvider.php'; 30 | require __DIR__ . DS . 'libs' . DS . 'PermissionsProvider.php'; 31 | require __DIR__ . DS . 'libs' . DS . 'RolesProvider.php'; 32 | 33 | require __DIR__ . DS . 'libs' . DS . 'DummyExtensionOne.php'; 34 | require __DIR__ . DS . 'libs' . DS . 'DummyExtensionTwo.php'; 35 | require __DIR__ . DS . 'libs' . DS . 'DummyExtensionThree.php'; 36 | 37 | class PermissionsTest extends Tester\TestCase 38 | { 39 | /** 40 | * @return array[]|array 41 | */ 42 | public function dataExtensionsWithInvalidPermissions() : array 43 | { 44 | return [ 45 | ['DummyExtensionTwo'], 46 | ['DummyExtensionThree'], 47 | ]; 48 | } 49 | 50 | public function testRegisteringPermissions() : void 51 | { 52 | $config = $this->initializeContainer(); 53 | 54 | \IPubTests\Permissions\Libs\DummyExtensionOne::register($config); 55 | 56 | $dic = $config->createContainer(); 57 | 58 | /** @var Permissions\Providers\IPermissionsProvider $permissions */ 59 | $permissionsProvider = $dic->getByType(Permissions\Providers\IPermissionsProvider::class); 60 | /** @var Permissions\Providers\IResourcesProvider $resourcesProvider */ 61 | $resourcesProvider = $dic->getByType(Permissions\Providers\IResourcesProvider::class); 62 | 63 | Assert::count(6, $permissionsProvider->findAll()); 64 | Assert::count(6, $resourcesProvider->findAll()); 65 | } 66 | 67 | /** 68 | * @dataProvider dataExtensionsWithInvalidPermissions 69 | * 70 | * @param string $extensionName 71 | * 72 | * @throws IPub\Permissions\Exceptions\InvalidArgumentException 73 | */ 74 | public function testRegisteringInvalidPermissions(string $extensionName) : void 75 | { 76 | $config = $this->initializeContainer(); 77 | 78 | eval('\IPubTests\Permissions\Libs\\' . $extensionName . '::register($config);'); 79 | 80 | $config->createContainer(); 81 | } 82 | 83 | /** 84 | * @return Nette\Configurator 85 | */ 86 | private function initializeContainer() : Nette\Configurator 87 | { 88 | $config = new Nette\Configurator(); 89 | $config->setTempDirectory(TEMP_DIR); 90 | 91 | Permissions\DI\PermissionsExtension::register($config); 92 | 93 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'config.neon'); 94 | 95 | return $config; 96 | } 97 | } 98 | 99 | \run(new PermissionsTest()); 100 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Entities/Resource.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Entities 10 | * @since 2.0.0 11 | * 12 | * @date 30.11.16 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Entities; 18 | 19 | use Nette; 20 | 21 | class Resource implements IResource 22 | { 23 | /** 24 | * Implement nette smart magic 25 | */ 26 | use Nette\SmartObject; 27 | 28 | /** 29 | * @var string 30 | */ 31 | protected $id; 32 | 33 | /** 34 | * @var IResource|NULL 35 | */ 36 | protected $parent; 37 | 38 | /** 39 | * @var IResource[] 40 | */ 41 | protected $children; 42 | 43 | /** 44 | * @var string|NULL 45 | */ 46 | protected $name; 47 | 48 | /** 49 | * @var string|NULL 50 | */ 51 | protected $comment; 52 | 53 | public function __construct(string $id) 54 | { 55 | $this->id = $id; 56 | 57 | $this->children = new \SplObjectStorage(); 58 | } 59 | 60 | /** 61 | * {@inheritdoc} 62 | */ 63 | public function setParent(IResource $parent = NULL) : void 64 | { 65 | $this->parent = $parent; 66 | } 67 | 68 | /** 69 | * {@inheritdoc} 70 | */ 71 | public function getParent() : ?IResource 72 | { 73 | return $this->parent; 74 | } 75 | 76 | /** 77 | * {@inheritdoc} 78 | */ 79 | public function setChildren(array $resources) : void 80 | { 81 | $this->children = new \SplObjectStorage(); 82 | 83 | foreach ($resources as $child) { 84 | if ($child instanceof IResource) { 85 | $this->children->attach($child); 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function addChild(IResource $resource) : void 94 | { 95 | if (!$this->children->contains($resource)) { 96 | $this->children->attach($resource); 97 | } 98 | } 99 | 100 | /** 101 | * {@inheritdoc} 102 | */ 103 | public function getChildren() : array 104 | { 105 | $children = []; 106 | 107 | $this->children->rewind(); 108 | 109 | while ($this->children->valid()) 110 | { 111 | $children[] = $this->children->current(); 112 | $this->children->next(); 113 | } 114 | 115 | return $children; 116 | } 117 | 118 | /** 119 | * {@inheritdoc} 120 | */ 121 | public function getResourceId() : string 122 | { 123 | return $this->id; 124 | } 125 | 126 | /** 127 | * {@inheritdoc} 128 | */ 129 | public function setName(string $name) : void 130 | { 131 | $this->name = $name; 132 | } 133 | 134 | /** 135 | * {@inheritdoc} 136 | */ 137 | public function getName() : ?string 138 | { 139 | return $this->name; 140 | } 141 | 142 | /** 143 | * {@inheritdoc} 144 | */ 145 | public function setComment(string $comment) : void 146 | { 147 | $this->comment = $comment; 148 | } 149 | 150 | /** 151 | * {@inheritdoc} 152 | */ 153 | public function getComment() : ?string 154 | { 155 | return $this->comment; 156 | } 157 | 158 | /** 159 | * Convert resource object to string 160 | * 161 | * @return string 162 | */ 163 | public function __toString() 164 | { 165 | return $this->id; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Latte/Macros.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Latte 10 | * @since 1.0.0 11 | * 12 | * @date 10.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Latte; 18 | 19 | use Nette\Utils; 20 | 21 | use Latte; 22 | use Latte\Compiler; 23 | use Latte\MacroNode; 24 | use Latte\Macros\MacroSet; 25 | use Latte\PhpWriter; 26 | 27 | /** 28 | * Permissions latte macros definition 29 | * 30 | * @package iPublikuj:Permissions! 31 | * @subpackage Latte 32 | * 33 | * @author Adam Kadlec 34 | */ 35 | final class Macros extends MacroSet 36 | { 37 | /** 38 | * @param Compiler $compiler 39 | * 40 | * @return void 41 | */ 42 | public static function install(Compiler $compiler) : void 43 | { 44 | $me = new static($compiler); 45 | 46 | /** 47 | * {ifAllowed role => 'some role', resource => 'some resource'}...{/ifAllowed} 48 | */ 49 | $me->addMacro('ifAllowed', [$me, 'macroIfAllowed'], [$me, 'macroIfAllowedEnd']); 50 | 51 | /** 52 | * ... 53 | */ 54 | $me->addMacro('allowedHref', [$me, 'macroIfAllowedLink'], [$me, 'macroIfAllowedLinkEnd']); 55 | } 56 | 57 | /** 58 | * @param MacroNode $node 59 | * @param PhpWriter $writer 60 | * 61 | * @return string 62 | */ 63 | public static function macroIfAllowed(MacroNode $node, PhpWriter $writer) : string 64 | { 65 | return $writer->write('if($presenter->context->getByType("IPub\Permissions\Access\LatteChecker")->isAllowed(%node.array)){'); 66 | } 67 | 68 | /** 69 | * @param MacroNode $node 70 | * @param PhpWriter $writer 71 | * 72 | * @return string 73 | */ 74 | public static function macroIfAllowedEnd(MacroNode $node, PhpWriter $writer) : string 75 | { 76 | return $writer->write('}'); 77 | } 78 | 79 | /** 80 | * @param MacroNode $node 81 | * @param PhpWriter $writer 82 | * 83 | * @return string 84 | * 85 | * @throws Latte\CompileException 86 | */ 87 | public static function macroIfAllowedLink(MacroNode $node, PhpWriter $writer) : string 88 | { 89 | // This macro is allowed only as n:macro in element 90 | if (Utils\Strings::lower($node->htmlNode->name) !== 'a') { 91 | throw new Latte\CompileException("Macro n:allowedHref is allowed only in link element, you used it in {$node->htmlNode->name}."); 92 | } 93 | 94 | return $writer->write('if($presenter->context->getByType("IPub\Permissions\Access\LinkChecker")->isAllowed(%node.word)){'); 95 | } 96 | 97 | /** 98 | * @param MacroNode $node 99 | * @param PhpWriter $writer 100 | * 101 | * @return string 102 | */ 103 | public static function macroIfAllowedLinkEnd(MacroNode $node, PhpWriter $writer) : string 104 | { 105 | return $writer->write('}'); 106 | } 107 | 108 | /** 109 | * @param MacroNode $node 110 | * @param PhpWriter $writer 111 | * 112 | * @return string 113 | */ 114 | public function macroHref(MacroNode $node, PhpWriter $writer) : string 115 | { 116 | return $writer->write(' ?> href="name === 'plink' ? '$_presenter' : '$_control') . '->link(%node.word, %node.array?)) ?>" 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Entities 10 | * @since 1.0.0 11 | * 12 | * @date 12.03.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Entities; 18 | 19 | use Nette\Security as NS; 20 | 21 | interface IRole extends NS\IRole 22 | { 23 | /** 24 | * The identifier of the anonymous role 25 | * 26 | * @var string 27 | */ 28 | public const ROLE_ANONYMOUS = 'guest'; 29 | 30 | /** 31 | * The identifier of the authenticated role 32 | * 33 | * @var string 34 | */ 35 | public const ROLE_AUTHENTICATED = 'authenticated'; 36 | 37 | /** 38 | * The identifier of the administrator role 39 | * 40 | * @var string 41 | */ 42 | public const ROLE_ADMINISTRATOR = 'administrator'; 43 | 44 | /** 45 | * @param IRole|NULL $parent 46 | * 47 | * @return void 48 | */ 49 | function setParent(?IRole $parent = NULL) : void; 50 | 51 | /** 52 | * @return IRole|NULL 53 | */ 54 | function getParent() : ?IRole; 55 | 56 | /** 57 | * @param IRole[] $roles 58 | * 59 | * @return void 60 | */ 61 | function setChildren(array $roles) : void; 62 | 63 | /** 64 | * @param IRole $role 65 | * 66 | * @return void 67 | */ 68 | function addChild(IRole $role) : void; 69 | 70 | /** 71 | * @return IRole[] 72 | */ 73 | function getChildren() : array; 74 | 75 | /** 76 | * @param string $name 77 | * 78 | * @return void 79 | */ 80 | function setName(string $name) : void; 81 | 82 | /** 83 | * @return string|NULL 84 | */ 85 | function getName() : ?string; 86 | 87 | /** 88 | * @param string|NULL $comment 89 | * 90 | * @return void 91 | */ 92 | function setComment(?string $comment) : void; 93 | 94 | /** 95 | * @return string|NULL 96 | */ 97 | function getComment() : ?string; 98 | 99 | /** 100 | * Add one permission to role 101 | * 102 | * @param IPermission[] $permissions 103 | * 104 | * @return void 105 | */ 106 | function setPermissions(array $permissions) : void; 107 | 108 | /** 109 | * Adds a permission 110 | * 111 | * @param IPermission $permission 112 | * 113 | * @return void 114 | */ 115 | function addPermission(IPermission $permission) : void; 116 | 117 | /** 118 | * Returns permissions for the role 119 | * 120 | * @return IPermission[] 121 | */ 122 | function getPermissions() : array; 123 | 124 | /** 125 | * Checks if a permission exists for this role. 126 | * 127 | * @param IPermission $permission 128 | * 129 | * @return bool 130 | */ 131 | function hasPermission(IPermission $permission) : bool; 132 | 133 | /** 134 | * Remove one specific permission from the role 135 | * 136 | * @param IPermission $permission 137 | * 138 | * @return void 139 | */ 140 | function removePermission(IPermission $permission) : void; 141 | 142 | /** 143 | * Clear all role permissions 144 | * 145 | * @return void 146 | */ 147 | function clearPermissions() : void; 148 | 149 | /** 150 | * Check if role is one from system roles 151 | * 152 | * @return bool 153 | */ 154 | function isLocked() : bool; 155 | 156 | /** 157 | * Check if role is guest 158 | * 159 | * @return bool 160 | */ 161 | function isAnonymous() : bool; 162 | 163 | /** 164 | * Check if role is authenticated 165 | * 166 | * @return bool 167 | */ 168 | function isAuthenticated() : bool; 169 | 170 | /** 171 | * Check if role is administrator 172 | * 173 | * @return bool 174 | */ 175 | function isAdministrator() : bool; 176 | } 177 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Access/LinkChecker.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Access 10 | * @since 1.0.0 11 | * 12 | * @date 13.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Access; 18 | 19 | use Nette; 20 | use Nette\Application; 21 | use Nette\Application\UI; 22 | 23 | /** 24 | * Create link access checker 25 | * 26 | * @package iPublikuj:Permissions! 27 | * @subpackage Access 28 | * 29 | * @author Adam Kadlec 30 | */ 31 | final class LinkChecker implements IChecker 32 | { 33 | /** 34 | * Implement nette smart magic 35 | */ 36 | use Nette\SmartObject; 37 | 38 | /** 39 | * @var Application\IPresenterFactory 40 | */ 41 | private $presenterFactory; 42 | 43 | /** 44 | * @var Application\Application 45 | */ 46 | private $application; 47 | 48 | /** 49 | * @var ICheckRequirements 50 | */ 51 | private $requirementsChecker; 52 | 53 | /** 54 | * @param Application\IPresenterFactory $presenterFactory 55 | * @param Application\Application $application 56 | * @param ICheckRequirements $requirementsChecker 57 | */ 58 | function __construct( 59 | Application\IPresenterFactory $presenterFactory, 60 | Application\Application $application, 61 | ICheckRequirements $requirementsChecker 62 | ) { 63 | $this->presenterFactory = $presenterFactory; 64 | $this->application = $application; 65 | 66 | // Permission annotation access checker 67 | $this->requirementsChecker = $requirementsChecker; 68 | } 69 | 70 | /** 71 | * Check whenever current user is allowed to use given link 72 | * 73 | * @param string $element etc "this", ":Admin:Show:default" 74 | * 75 | * @return bool 76 | * 77 | * @throws Application\InvalidPresenterException 78 | * @throws \ReflectionException 79 | */ 80 | public function isAllowed($element) : bool 81 | { 82 | list($presenter, $action) = $this->formatLink($element); 83 | 84 | $presenterReflection = new UI\ComponentReflection($this->presenterFactory->getPresenterClass($presenter)); 85 | 86 | if (!$this->requirementsChecker->isAllowed($presenterReflection)) { 87 | return FALSE; 88 | } 89 | 90 | $actionKey = UI\Presenter::ACTION_KEY . ucfirst($action); 91 | 92 | if ($presenterReflection->hasMethod($actionKey) && !$this->requirementsChecker->isAllowed($presenterReflection->getMethod($actionKey))) { 93 | return FALSE; 94 | } 95 | 96 | return TRUE; 97 | } 98 | 99 | /** 100 | * Format link to format array('module:submodule:presenter', 'action') 101 | * 102 | * @param string $destination 103 | * 104 | * @return array(presenter, action) 105 | */ 106 | public function formatLink(string $destination) : array 107 | { 108 | if ($destination === 'this') { 109 | return [$this->application->getPresenter()->getName(), $this->application->getPresenter()->getAction()]; 110 | } 111 | 112 | $parts = explode(':', $destination); 113 | 114 | if ($destination[0] != ':') { 115 | $current = explode(':', $this->application->getPresenter()->getName()); 116 | 117 | if (strpos($destination, ':') !== FALSE) { 118 | // Remove presenter 119 | array_pop($current); 120 | } 121 | 122 | $parts = array_merge($current, $parts); 123 | 124 | } else { 125 | // Remove empty 126 | array_shift($parts); 127 | } 128 | 129 | if ($destination[strlen($destination) - 1] == ':') { 130 | // Remove empty 131 | array_pop($parts); 132 | 133 | $action = UI\Presenter::DEFAULT_ACTION; 134 | 135 | } else { 136 | $action = array_pop($parts); 137 | } 138 | 139 | return [implode(':', $parts), $action]; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/RolesInheritanceTest.phpt: -------------------------------------------------------------------------------- 1 | createContainer(); 52 | 53 | // Get permissions services 54 | $this->permission = $dic->getService('permissions.permissions'); 55 | $this->rolesProvider = $dic->getByType(Permissions\Providers\IRolesProvider::class); 56 | } 57 | 58 | 59 | public function testRolesProviderHierarchy() : void 60 | { 61 | $administrator = $this->rolesProvider->getRole('administrator'); 62 | 63 | Assert::null($administrator->getParent(), 'Role "administrator" does not have parent'); 64 | Assert::count(0, $administrator->getChildren(), 'Role "administrator" does not have any children'); 65 | 66 | $guest = $this->rolesProvider->getRole('guest'); 67 | 68 | Assert::null($guest->getParent(), 'Role "guest" does not have parents'); 69 | Assert::count(1, $guest->getChildren(), 'Role "guest" does have one children'); 70 | 71 | $authenticated = $this->rolesProvider->getRole('authenticated'); 72 | 73 | Assert::equal($guest, $authenticated->getParent(), 'Parent role of "authenticated" is "guest"'); 74 | Assert::count(0, $authenticated->getChildren(), 'Role "authenticated" does not have any children'); 75 | 76 | $userDefined = $this->rolesProvider->getRole('user-defined-role'); 77 | 78 | Assert::null($userDefined->getParent(), 'Role "user-defined-role" does not have parents'); 79 | Assert::count(2, $userDefined->getChildren(), 'Role "user-defined-role" does have 2 children'); 80 | 81 | $userDefinedChild = $this->rolesProvider->getRole('user-defined-child-role'); 82 | 83 | Assert::equal($userDefined, $userDefinedChild->getParent(), 'Parent role of "user-defined-child-role" is "user-defined-child"'); 84 | Assert::count(0, $userDefinedChild->getChildren(), 'Role "user-defined-child-role" does not have any children'); 85 | 86 | $userDefinedInherited = $this->rolesProvider->getRole('user-defined-inherited-role'); 87 | 88 | Assert::equal($userDefined, $userDefinedInherited->getParent(), 'Parent role of "user-defined-inherited-role" is "user-defined-role"'); 89 | Assert::count(0, $userDefinedInherited->getChildren(), 'Role "user-defined-inherited-role" does not have any children'); 90 | } 91 | 92 | public function testPermissionRolesHierarchy() : void 93 | { 94 | Assert::equal($this->permission->getRoleParents('administrator'), [], 'Role "administrator" does not have parents'); 95 | 96 | Assert::equal($this->permission->getRoleParents('guest'), [], 'Role "guest" does not have parents'); 97 | 98 | Assert::equal($this->permission->getRoleParents('authenticated'), ['guest'], 'Role "guest" is the only parent role of "authenticated"'); 99 | 100 | Assert::equal($this->permission->getRoleParents('user-defined-role'), [], 'Role "user-defined-role" does not have parents'); 101 | 102 | Assert::equal($this->permission->getRoleParents('user-defined-child-role'), ['user-defined-role'], 'Role "user-defined-child-role" is the only parent role of "user-defined-role"'); 103 | 104 | Assert::equal($this->permission->getRoleParents('user-defined-inherited-role'), ['user-defined-role'], 'Role "user-defined-inherited-role" is the only parent role of "user-defined-role"'); 105 | 106 | Assert::true($this->permission->roleInheritsFrom('user-defined-child-role', 'user-defined-role'), 'Role "user-defined-child-role" inherits from "user-defined-role"'); 107 | } 108 | 109 | 110 | public function testPermissionInheritance() : void 111 | { 112 | Assert::true($this->permission->isAllowed('user-defined-role', 'firstResource', 'firstPrivilege'), 113 | 'Role "user-defined-role" is allowed "firstResource:"'); 114 | 115 | Assert::true($this->permission->isAllowed('user-defined-child-role', 'firstResource', 'firstPrivilege'), 116 | 'Role "user-defined-child-role" is also allowed "firstResource:" because it inherits from "user-defined-role"'); 117 | } 118 | 119 | /** 120 | * @return Nette\DI\Container 121 | */ 122 | private function createContainer() : Nette\DI\Container 123 | { 124 | $config = new Nette\Configurator(); 125 | $config->setTempDirectory(TEMP_DIR); 126 | 127 | Permissions\DI\PermissionsExtension::register($config); 128 | 129 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'config.neon'); 130 | 131 | return $config->createContainer(); 132 | } 133 | } 134 | 135 | 136 | \run(new RolesInheritanceTest()); 137 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Access/LatteChecker.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Access 10 | * @since 1.0.0 11 | * 12 | * @date 14.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Access; 18 | 19 | use Nette; 20 | use Nette\Utils; 21 | use Nette\Security as NS; 22 | 23 | use IPub\Permissions\Entities; 24 | use IPub\Permissions\Exceptions; 25 | 26 | /** 27 | * Latte helper for access checking 28 | * 29 | * @package iPublikuj:Permissions! 30 | * @subpackage Access 31 | * 32 | * @author Adam Kadlec 33 | */ 34 | final class LatteChecker implements IChecker 35 | { 36 | /** 37 | * Implement nette smart magic 38 | */ 39 | use Nette\SmartObject; 40 | 41 | /** 42 | * @var NS\User 43 | */ 44 | private $user; 45 | 46 | /** 47 | * @param NS\User $user 48 | */ 49 | public function __construct(NS\User $user) 50 | { 51 | $this->user = $user; 52 | } 53 | 54 | /** 55 | * {@inheritdoc} 56 | */ 57 | public function isAllowed($element) : bool 58 | { 59 | // Check annotations only if element have to be secured 60 | if (is_array($element)) { 61 | $element = Utils\ArrayHash::from($element); 62 | 63 | return $this->checkUser($element) 64 | && $this->checkResources($element) 65 | && $this->checkPrivileges($element) 66 | && $this->checkPermission($element) 67 | && $this->checkRoles($element); 68 | 69 | } else { 70 | return TRUE; 71 | } 72 | } 73 | 74 | /** 75 | * @param Utils\ArrayHash $element 76 | * 77 | * @return bool 78 | * 79 | * @throws Exceptions\InvalidArgumentException 80 | */ 81 | private function checkUser(Utils\ArrayHash $element) : bool 82 | { 83 | // Check if element has user parameter 84 | if ($element->offsetExists('user')) { 85 | // Get user parameter 86 | $user = $element->offsetGet('user'); 87 | 88 | // Parameter is single string 89 | if (is_string($user) && in_array($user, ['loggedIn', 'guest'], TRUE)) { 90 | // User have to be logged in and is not 91 | if ($user === 'loggedIn' && $this->user->isLoggedIn() === FALSE) { 92 | return FALSE; 93 | 94 | // User have to be logged out and is logged in 95 | } elseif ($user === 'guest' && $this->user->isLoggedIn() === TRUE) { 96 | return FALSE; 97 | } 98 | 99 | // Parameter have multiple definitions 100 | } else { 101 | throw new Exceptions\InvalidArgumentException('In parameter \'user\' is allowed only one from two strings: \'loggedIn\' & \'guest\''); 102 | } 103 | 104 | return TRUE; 105 | } 106 | 107 | return TRUE; 108 | } 109 | 110 | /** 111 | * @param Utils\ArrayHash $element 112 | * 113 | * @return bool 114 | * 115 | * @throws Exceptions\InvalidStateException 116 | */ 117 | protected function checkResources(Utils\ArrayHash $element) : bool 118 | { 119 | // Check if element has resource parameter & privilege parameter 120 | if ($element->offsetExists('resource')) { 121 | $resources = (array) $element->offsetGet('resource'); 122 | $privileges = $element->offsetExists('privilege') ? (array) $element->offsetGet('privilege') : []; 123 | 124 | if (count($resources) != 1) { 125 | throw new Exceptions\InvalidStateException('Invalid resources count in \'resource\' parameter!'); 126 | } 127 | 128 | foreach ($resources as $resource) { 129 | if (count($privileges)) { 130 | foreach ($privileges as $privilege) { 131 | if ($this->user->isAllowed($resource, $privilege)) { 132 | return TRUE; 133 | } 134 | } 135 | 136 | } else { 137 | if ($this->user->isAllowed($resource)) { 138 | return TRUE; 139 | } 140 | } 141 | } 142 | 143 | return FALSE; 144 | } 145 | 146 | return TRUE; 147 | } 148 | 149 | /** 150 | * @param Utils\ArrayHash $element 151 | * 152 | * @return bool 153 | * 154 | * @throws Exceptions\InvalidStateException 155 | */ 156 | protected function checkPrivileges(Utils\ArrayHash $element) : bool 157 | { 158 | // Check if element has privilege parameter & hasn't resource parameter 159 | if (!$element->offsetExists('resource') && $element->offsetExists('privilege')) { 160 | $privileges = (array) $element->offsetGet('privilege'); 161 | 162 | if (count($privileges) != 1) { 163 | throw new Exceptions\InvalidStateException('Invalid privileges count in \'privilege\' parameter!'); 164 | } 165 | 166 | foreach ($privileges as $privilege) { 167 | if ($this->user->isAllowed(NS\IAuthorizator::ALL, $privilege)) { 168 | return TRUE; 169 | } 170 | } 171 | 172 | return FALSE; 173 | } 174 | 175 | return TRUE; 176 | } 177 | 178 | /** 179 | * @param Utils\ArrayHash $element 180 | * 181 | * @return bool 182 | */ 183 | protected function checkPermission(Utils\ArrayHash $element) : bool 184 | { 185 | // Check if element has permission parameter 186 | if ($element->offsetExists('permission')) { 187 | $permissions = (array) $element->offsetGet('permission'); 188 | 189 | foreach ($permissions as $permission) { 190 | // Parse resource & privilege from permission 191 | list($resource, $privilege) = explode(Entities\IPermission::DELIMITER, $permission); 192 | 193 | // Remove white spaces 194 | $resource = Utils\Strings::trim($resource); 195 | $privilege = Utils\Strings::trim($privilege); 196 | 197 | if ($this->user->isAllowed($resource, $privilege)) { 198 | return TRUE; 199 | } 200 | } 201 | 202 | return FALSE; 203 | } 204 | 205 | return TRUE; 206 | } 207 | 208 | /** 209 | * @param Utils\ArrayHash $element 210 | * 211 | * @return bool 212 | */ 213 | protected function checkRoles(Utils\ArrayHash $element) : bool 214 | { 215 | // Check if element has role parameter 216 | if ($element->offsetExists('role')) { 217 | $roles = (array) $element->offsetGet('role'); 218 | 219 | foreach ($roles as $role) { 220 | if ($this->user->isInRole($role)) { 221 | return TRUE; 222 | } 223 | } 224 | 225 | return FALSE; 226 | } 227 | 228 | return TRUE; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Entities/Role.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Entities 10 | * @since 1.0.0 11 | * 12 | * @date 12.03.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Entities; 18 | 19 | use Nette; 20 | 21 | use IPub\Permissions\Exceptions; 22 | 23 | class Role implements IRole 24 | { 25 | /** 26 | * Implement nette smart magic 27 | */ 28 | use Nette\SmartObject; 29 | 30 | /** 31 | * @var string 32 | */ 33 | protected $id; 34 | 35 | /** 36 | * @var IRole|NULL 37 | */ 38 | protected $parent; 39 | 40 | /** 41 | * @var \SplObjectStorage 42 | */ 43 | protected $children; 44 | 45 | /** 46 | * @var string|NULL 47 | */ 48 | protected $name; 49 | 50 | /** 51 | * @var string|NULL 52 | */ 53 | protected $comment; 54 | 55 | /** 56 | * @var \SplObjectStorage 57 | */ 58 | protected $permissions; 59 | 60 | /** 61 | * @param string $id 62 | * @param string|NULL $name 63 | * @param string|NULL $comment 64 | */ 65 | public function __construct(string $id, ?string $name = NULL, ?string $comment = NULL) 66 | { 67 | // Role identifier 68 | $this->id = $id; 69 | 70 | $this->name = $name; 71 | $this->comment = $comment; 72 | 73 | // Storage initialization 74 | $this->permissions = new \SplObjectStorage(); 75 | $this->children = new \SplObjectStorage(); 76 | } 77 | 78 | /** 79 | * {@inheritdoc} 80 | */ 81 | public function setParent(IRole $parent = NULL) : void 82 | { 83 | $parent->addChild($this); 84 | 85 | $this->parent = $parent; 86 | } 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | public function getParent() : ?IRole 92 | { 93 | return $this->parent; 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function setChildren(array $roles) : void 100 | { 101 | foreach ($this->getChildren() as $child) { 102 | $child->setParent(NULL); 103 | } 104 | 105 | $this->children = new \SplObjectStorage(); 106 | 107 | foreach ($roles as $child) { 108 | if ($child instanceof IRole) { 109 | $this->children->attach($child); 110 | $child->setParent($this); 111 | } 112 | } 113 | } 114 | 115 | /** 116 | * {@inheritdoc} 117 | */ 118 | public function addChild(IRole $role) : void 119 | { 120 | if (!$this->children->contains($role)) { 121 | $this->children->attach($role); 122 | $role->setParent($this); 123 | } 124 | } 125 | 126 | /** 127 | * {@inheritdoc} 128 | */ 129 | public function getChildren() : array 130 | { 131 | $children = []; 132 | 133 | $this->children->rewind(); 134 | 135 | while ($this->children->valid()) 136 | { 137 | $children[] = $this->children->current(); 138 | $this->children->next(); 139 | } 140 | 141 | return $children; 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | */ 147 | public function getRoleId() : string 148 | { 149 | return $this->id; 150 | } 151 | 152 | /** 153 | * {@inheritdoc} 154 | */ 155 | public function setName(string $name) : void 156 | { 157 | $this->name = $name; 158 | } 159 | 160 | /** 161 | * {@inheritdoc} 162 | */ 163 | public function getName() : ?string 164 | { 165 | return $this->name ?: $this->id; 166 | } 167 | 168 | /** 169 | * {@inheritdoc} 170 | */ 171 | public function setComment(?string $comment) : void 172 | { 173 | $this->comment = $comment; 174 | } 175 | 176 | /** 177 | * {@inheritdoc} 178 | */ 179 | public function getComment() : ?string 180 | { 181 | return $this->comment; 182 | } 183 | 184 | /** 185 | * {@inheritdoc} 186 | */ 187 | public function setPermissions(array $permissions) : void 188 | { 189 | $this->permissions = new \SplObjectStorage(); 190 | 191 | foreach ($permissions as $permission) { 192 | if ($permission instanceof IPermission) { 193 | $this->permissions->attach($permission); 194 | } 195 | } 196 | } 197 | 198 | /** 199 | * {@inheritdoc} 200 | */ 201 | public function addPermission(IPermission $permission) : void 202 | { 203 | if (!$this->permissions->contains($permission)) { 204 | $this->permissions->attach($permission); 205 | } 206 | } 207 | 208 | /** 209 | * {@inheritdoc} 210 | */ 211 | public function getPermissions() : array 212 | { 213 | $permissions = []; 214 | 215 | $this->permissions->rewind(); 216 | 217 | while ($this->permissions->valid()) 218 | { 219 | $permissions[] = $this->permissions->current(); 220 | $this->permissions->next(); 221 | } 222 | 223 | return $permissions; 224 | } 225 | 226 | /** 227 | * {@inheritdoc} 228 | */ 229 | public function hasPermission(IPermission $permission) : bool 230 | { 231 | return $this->permissions->contains($permission); 232 | } 233 | 234 | /** 235 | * {@inheritdoc} 236 | */ 237 | public function removePermission(IPermission $permission) : void 238 | { 239 | if (!$this->permissions->contains($permission)) { 240 | throw new Exceptions\InvalidArgumentException(sprintf('Permission "%s" cannot be removed since it is not associated with the role %s', $permission, $this->getName())); 241 | } 242 | 243 | $this->permissions->detach($permission); 244 | } 245 | 246 | /** 247 | * {@inheritdoc} 248 | */ 249 | public function clearPermissions() : void 250 | { 251 | $this->permissions = new \SplObjectStorage(); 252 | } 253 | 254 | /** 255 | * {@inheritdoc} 256 | */ 257 | public function isLocked() : bool 258 | { 259 | return in_array($this->id, [IRole::ROLE_ANONYMOUS, IRole::ROLE_AUTHENTICATED, IRole::ROLE_ADMINISTRATOR], TRUE); 260 | } 261 | 262 | /** 263 | * {@inheritdoc} 264 | */ 265 | public function isAnonymous() : bool 266 | { 267 | return $this->id === IRole::ROLE_ANONYMOUS; 268 | } 269 | 270 | /** 271 | * {@inheritdoc} 272 | */ 273 | public function isAuthenticated() : bool 274 | { 275 | return $this->id === IRole::ROLE_AUTHENTICATED; 276 | } 277 | 278 | /** 279 | * {@inheritdoc} 280 | */ 281 | public function isAdministrator() : bool 282 | { 283 | return $this->id === IRole::ROLE_ADMINISTRATOR; 284 | } 285 | 286 | /** 287 | * Convert role object to string 288 | * 289 | * @return string 290 | */ 291 | public function __toString() 292 | { 293 | return $this->id; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/AccessTest.phpt: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 1.0.0 12 | * 13 | * @date 14.01.15 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions; 19 | 20 | use Nette; 21 | use Nette\Application; 22 | use Nette\Application\UI; 23 | use Nette\Security as NS; 24 | 25 | use Tester; 26 | use Tester\Assert; 27 | 28 | use IPub\Permissions; 29 | 30 | require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'bootstrap.php'; 31 | require __DIR__ . DS . 'libs' . DS . 'ResourcesProvider.php'; 32 | require __DIR__ . DS . 'libs' . DS . 'PermissionsProvider.php'; 33 | require __DIR__ . DS . 'libs' . DS . 'RolesProvider.php'; 34 | 35 | class AccessTest extends Tester\TestCase 36 | { 37 | /** 38 | * @var Application\IPresenterFactory 39 | */ 40 | private $presenterFactory; 41 | 42 | /** 43 | * @var Nette\DI\Container 44 | */ 45 | private $container; 46 | 47 | /** 48 | * @var NS\User 49 | */ 50 | private $user; 51 | 52 | /** 53 | * @return array[]|array 54 | */ 55 | public function dataRegisteredUsers() : array 56 | { 57 | return [ 58 | ['john', '123456'], 59 | ['jane', '123456'], 60 | ]; 61 | } 62 | 63 | public function dataGuestUsers() : array 64 | { 65 | return [ 66 | ['guest'] 67 | ]; 68 | } 69 | 70 | /** 71 | * Set up 72 | */ 73 | public function setUp() : void 74 | { 75 | parent::setUp(); 76 | 77 | $this->container = $this->createContainer(); 78 | 79 | // Get presenter factory from container 80 | $this->presenterFactory = $this->container->getByType(Nette\Application\IPresenterFactory::class); 81 | 82 | // Get application user 83 | $this->user = $this->container->getService('user'); 84 | 85 | // Create user authenticator 86 | $authenticator = new Nette\Security\SimpleAuthenticator([ 87 | 'john' => '123456', 88 | 'jane' => '123456', 89 | ], [ 90 | 'john' => [ 91 | Permissions\Entities\IRole::ROLE_AUTHENTICATED 92 | ], 93 | 'jane' => [ 94 | Permissions\Entities\IRole::ROLE_AUTHENTICATED, 95 | Permissions\Entities\IRole::ROLE_ADMINISTRATOR 96 | ] 97 | ]); 98 | $this->user->setAuthenticator($authenticator); 99 | } 100 | 101 | /** 102 | * @dataProvider dataRegisteredUsers 103 | * 104 | * @param string $username 105 | * @param string $password 106 | */ 107 | public function testPresenterActionAllowed(string $username, string $password) : void 108 | { 109 | // Create test presenter 110 | $presenter = $this->createPresenter(); 111 | 112 | // Try to login user 113 | $this->user->login($username, $password); 114 | 115 | // Create GET request 116 | $request = new Application\Request('Test', 'GET', ['action' => 'allowed']); 117 | // & fire presenter & catch response 118 | $response = $presenter->run($request); 119 | 120 | // Logout user 121 | $this->user->logout(TRUE); 122 | 123 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 124 | Assert::equal('Passed', $response->getSource()); 125 | } 126 | 127 | /** 128 | * @dataProvider dataRegisteredUsers 129 | * 130 | * @param string $username 131 | * @param string $password 132 | */ 133 | public function testPresenterActionAllowedRole(string $username, string $password) : void 134 | { 135 | // Create test presenter 136 | $presenter = $this->createPresenter(); 137 | 138 | // Try to login user 139 | $this->user->login($username, $password); 140 | 141 | // Create GET request 142 | $request = new Application\Request('Test', 'GET', ['action' => 'allowedRole']); 143 | // & fire presenter & catch response 144 | $response = $presenter->run($request); 145 | 146 | // Logout user 147 | $this->user->logout(TRUE); 148 | 149 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 150 | Assert::equal('Passed', $response->getSource()); 151 | } 152 | 153 | /** 154 | * @dataProvider dataGuestUsers 155 | * 156 | * @param string $username 157 | * 158 | * @throws Nette\Application\ForbiddenRequestException 159 | */ 160 | public function testPresenterActionNotAllowed(string $username) : void 161 | { 162 | // Create test presenter 163 | $presenter = $this->createPresenter(); 164 | 165 | // Create GET request 166 | $request = new Application\Request('Test', 'GET', ['action' => 'allowedRole']); 167 | // & fire presenter 168 | $presenter->run($request); 169 | } 170 | 171 | /** 172 | * @dataProvider dataRegisteredUsers 173 | * 174 | * @param string $username 175 | * @param string $password 176 | * 177 | * @throws Nette\Application\ForbiddenRequestException 178 | */ 179 | public function testNotAllowedLoggedIn(string $username, string $password) : void 180 | { 181 | // Create test presenter 182 | $presenter = $this->createPresenter(); 183 | 184 | // Try to login user 185 | $this->user->login($username, $password); 186 | 187 | // Create GET request 188 | $request = new Application\Request('Test', 'GET', ['action' => 'onlyGuest']); 189 | // & fire presenter & catch 190 | $presenter->run($request); 191 | } 192 | 193 | /** 194 | * @dataProvider dataGuestUsers 195 | * 196 | * @param string $username 197 | */ 198 | public function testAllowedGuest(string $username) : void 199 | { 200 | $this->user->logout(TRUE); 201 | 202 | // Create test presenter 203 | $presenter = $this->createPresenter(); 204 | 205 | // Create GET request 206 | $request = new Application\Request('Test', 'GET', ['action' => 'onlyGuest']); 207 | // & fire presenter & catch response 208 | $response = $presenter->run($request); 209 | 210 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 211 | Assert::equal('Passed', $response->getSource()); 212 | } 213 | 214 | /** 215 | * @return Application\IPresenter 216 | */ 217 | protected function createPresenter() : Application\IPresenter 218 | { 219 | // Create test presenter 220 | $presenter = $this->presenterFactory->createPresenter('Test'); 221 | // Disable auto canonicalize to prevent redirection 222 | $presenter->autoCanonicalize = FALSE; 223 | 224 | return $presenter; 225 | } 226 | 227 | /** 228 | * @return Nette\DI\Container 229 | */ 230 | private function createContainer() : Nette\DI\Container 231 | { 232 | $config = new Nette\Configurator(); 233 | $config->setTempDirectory(TEMP_DIR); 234 | 235 | Permissions\DI\PermissionsExtension::register($config); 236 | 237 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'config.neon'); 238 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'presenters.neon'); 239 | 240 | return $config->createContainer(); 241 | } 242 | } 243 | 244 | /** 245 | * @Secured 246 | * @Secured\Resource(firstResource) 247 | * @Secured\Privilege(firstPrivilege) 248 | */ 249 | class TestPresenter extends UI\Presenter 250 | { 251 | use Permissions\TPermission; 252 | 253 | public function renderAllowed() : void 254 | { 255 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 256 | } 257 | 258 | /** 259 | * @Secured 260 | * @Secured\Role(authenticated, administrator) 261 | */ 262 | public function renderAllowedRole() : void 263 | { 264 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 265 | } 266 | 267 | /** 268 | * @Secured 269 | * @Secured\User(guest) 270 | */ 271 | public function renderOnlyGuest() : void 272 | { 273 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 274 | } 275 | } 276 | 277 | \run(new AccessTest()); 278 | -------------------------------------------------------------------------------- /tests/IPubTests/Permissions/AnnotationsTest.phpt: -------------------------------------------------------------------------------- 1 | 9 | * @package iPublikuj:Permissions! 10 | * @subpackage Tests 11 | * @since 1.0.0 12 | * 13 | * @date 14.01.15 14 | */ 15 | 16 | declare(strict_types = 1); 17 | 18 | namespace IPubTests\Permissions; 19 | 20 | use Nette; 21 | use Nette\Application; 22 | use Nette\Application\UI; 23 | use Nette\Security as NS; 24 | 25 | use Tester; 26 | use Tester\Assert; 27 | 28 | use IPub\Permissions; 29 | 30 | require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'bootstrap.php'; 31 | require __DIR__ . DS . 'libs' . DS . 'ResourcesProvider.php'; 32 | require __DIR__ . DS . 'libs' . DS . 'PermissionsProvider.php'; 33 | require __DIR__ . DS . 'libs' . DS . 'RolesProvider.php'; 34 | 35 | class AnnotationsTest extends Tester\TestCase 36 | { 37 | /** 38 | * @var Application\IPresenterFactory 39 | */ 40 | private $presenterFactory; 41 | 42 | /** 43 | * @var Nette\DI\Container 44 | */ 45 | private $container; 46 | 47 | /** 48 | * @var NS\User 49 | */ 50 | private $user; 51 | 52 | /** 53 | * @return array[]|array 54 | */ 55 | public function dataRegisteredUsers() : array 56 | { 57 | return [ 58 | ['john', '123456'], 59 | ['jane', '123456'], 60 | ]; 61 | } 62 | 63 | public function dataGuestUsers() : array 64 | { 65 | return [ 66 | ['guest'] 67 | ]; 68 | } 69 | 70 | /** 71 | * Set up 72 | */ 73 | public function setUp() : void 74 | { 75 | parent::setUp(); 76 | 77 | $this->container = $this->createContainer(); 78 | 79 | // Get presenter factory from container 80 | $this->presenterFactory = $this->container->getByType('Nette\Application\IPresenterFactory'); 81 | 82 | // Get application user 83 | $this->user = $this->container->getService('user'); 84 | 85 | // Create user authenticator 86 | $authenticator = new Nette\Security\SimpleAuthenticator([ 87 | 'john' => '123456', 88 | 'jane' => '123456', 89 | ], [ 90 | 'john' => [ 91 | Permissions\Entities\IRole::ROLE_AUTHENTICATED 92 | ], 93 | 'jane' => [ 94 | Permissions\Entities\IRole::ROLE_AUTHENTICATED, 95 | Permissions\Entities\IRole::ROLE_ADMINISTRATOR 96 | ] 97 | ]); 98 | $this->user->setAuthenticator($authenticator); 99 | } 100 | 101 | /** 102 | * @dataProvider dataRegisteredUsers 103 | * 104 | * @param string $username 105 | * @param string $password 106 | */ 107 | public function testCheckUser(string $username, string $password) : void 108 | { 109 | // Create test presenter 110 | $presenter = $this->createPresenter(); 111 | 112 | // Try to login user 113 | $this->user->login($username, $password); 114 | 115 | // Create GET request 116 | $request = new Application\Request('Test', 'GET', ['action' => 'user']); 117 | // & fire presenter & catch response 118 | $response = $presenter->run($request); 119 | 120 | // Logout user 121 | $this->user->logout(TRUE); 122 | 123 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 124 | Assert::equal('Passed', $response->getSource()); 125 | } 126 | 127 | /** 128 | * @dataProvider dataRegisteredUsers 129 | * 130 | * @param string $username 131 | * @param string $password 132 | */ 133 | public function testCheckResourcePrivilege(string $username, string $password) : void 134 | { 135 | // Create test presenter 136 | $presenter = $this->createPresenter(); 137 | 138 | // Try to login user 139 | $this->user->login($username, $password); 140 | 141 | // Create GET request 142 | $request = new Application\Request('Test', 'GET', ['action' => 'resourcePrivilege']); 143 | // & fire presenter & catch response 144 | $response = $presenter->run($request); 145 | 146 | // Logout user 147 | $this->user->logout(TRUE); 148 | 149 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 150 | Assert::equal('Passed', $response->getSource()); 151 | } 152 | 153 | /** 154 | * @dataProvider dataRegisteredUsers 155 | * 156 | * @param string $username 157 | * @param string $password 158 | */ 159 | public function testCheckPermission(string $username, string $password) : void 160 | { 161 | // Create test presenter 162 | $presenter = $this->createPresenter(); 163 | 164 | // Try to login user 165 | $this->user->login($username, $password); 166 | 167 | // Create GET request 168 | $request = new Application\Request('Test', 'GET', ['action' => 'permission']); 169 | // & fire presenter & catch response 170 | $response = $presenter->run($request); 171 | 172 | // Logout user 173 | $this->user->logout(TRUE); 174 | 175 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 176 | Assert::equal('Passed', $response->getSource()); 177 | } 178 | 179 | /** 180 | * @dataProvider dataRegisteredUsers 181 | * 182 | * @param string $username 183 | * @param string $password 184 | */ 185 | public function testCheckRole(string $username, string $password) : void 186 | { 187 | // Create test presenter 188 | $presenter = $this->createPresenter(); 189 | 190 | // Try to login user 191 | $this->user->login($username, $password); 192 | 193 | // Create GET request 194 | $request = new Application\Request('Test', 'GET', ['action' => 'role']); 195 | // & fire presenter & catch response 196 | $response = $presenter->run($request); 197 | 198 | // Logout user 199 | $this->user->logout(TRUE); 200 | 201 | Assert::true($response instanceof Nette\Application\Responses\TextResponse); 202 | Assert::equal('Passed', $response->getSource()); 203 | } 204 | 205 | /** 206 | * @return Application\IPresenter 207 | */ 208 | protected function createPresenter() : Application\IPresenter 209 | { 210 | // Create test presenter 211 | $presenter = $this->presenterFactory->createPresenter('Test'); 212 | // Disable auto canonicalize to prevent redirection 213 | $presenter->autoCanonicalize = FALSE; 214 | 215 | return $presenter; 216 | } 217 | 218 | /** 219 | * @return Nette\DI\Container 220 | */ 221 | private function createContainer() : Nette\DI\Container 222 | { 223 | $config = new Nette\Configurator(); 224 | $config->setTempDirectory(TEMP_DIR); 225 | 226 | Permissions\DI\PermissionsExtension::register($config); 227 | 228 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'config.neon'); 229 | $config->addConfig(__DIR__ . DS . 'files' . DS . 'presenters.neon'); 230 | 231 | return $config->createContainer(); 232 | } 233 | } 234 | 235 | class TestPresenter extends UI\Presenter 236 | { 237 | use Permissions\TPermission; 238 | 239 | /** 240 | * @Secured 241 | * @Secured\User(loggedIn) 242 | */ 243 | public function renderUser() : void 244 | { 245 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 246 | } 247 | 248 | /** 249 | * @Secured 250 | * @Secured\Resource(firstResource) 251 | * @Secured\Privilege(firstPrivilege) 252 | */ 253 | public function renderResourcePrivilege() : void 254 | { 255 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 256 | } 257 | 258 | /** 259 | * @Secured 260 | * @Secured\Permission(secondResource:secondPrivilege) 261 | */ 262 | public function renderPermission() : void 263 | { 264 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 265 | } 266 | 267 | /** 268 | * @Secured 269 | * @Secured\Role(authenticated) 270 | */ 271 | public function renderRole() : void 272 | { 273 | $this->sendResponse(new Application\Responses\TextResponse('Passed')); 274 | } 275 | } 276 | 277 | \run(new AnnotationsTest()); 278 | -------------------------------------------------------------------------------- /src/IPub/Permissions/Access/AnnotationChecker.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage Access 10 | * @since 1.0.0 11 | * 12 | * @date 13.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\Access; 18 | 19 | use Nette; 20 | use Nette\Application\UI; 21 | use Nette\Utils; 22 | use Nette\Security as NS; 23 | 24 | use IPub\Permissions\Entities; 25 | use IPub\Permissions\Exceptions; 26 | 27 | /** 28 | * Presenter & component annotation access checker 29 | * 30 | * @package iPublikuj:Permissions! 31 | * @subpackage Access 32 | * 33 | * @author Adam Kadlec 34 | */ 35 | final class AnnotationChecker implements IChecker, ICheckRequirements 36 | { 37 | /** 38 | * Implement nette smart magic 39 | */ 40 | use Nette\SmartObject; 41 | 42 | /** 43 | * @var NS\User 44 | */ 45 | private $user; 46 | 47 | /** 48 | * @param NS\User $user 49 | */ 50 | public function __construct(NS\User $user) 51 | { 52 | $this->user = $user; 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function isAllowed($element) : bool 59 | { 60 | // Check annotations only if element have to be secured 61 | if ( 62 | $element instanceof \Reflector 63 | && $element->hasAnnotation('Secured') 64 | ) { 65 | return $this->checkUser($element) 66 | && $this->checkResources($element) 67 | && $this->checkPrivileges($element) 68 | && $this->checkPermission($element) 69 | && $this->checkRoles($element); 70 | 71 | } else { 72 | return TRUE; 73 | } 74 | } 75 | 76 | /** 77 | * @param UI\ComponentReflection|UI\MethodReflection|Nette\Reflection\ClassType|Nette\Reflection\Method|\Reflector $element 78 | * 79 | * @return bool 80 | * 81 | * @throws Exceptions\InvalidArgumentException 82 | */ 83 | private function checkUser($element) : bool 84 | { 85 | // Check if element has @Secured\User annotation 86 | if ($element->hasAnnotation('Secured\User')) { 87 | // Get user annotation 88 | $user = $element->getAnnotation('Secured\User'); 89 | 90 | // Annotation is single string 91 | if (is_string($user) && in_array($user, ['loggedIn', 'guest'], TRUE)) { 92 | // User have to be logged in and is not 93 | if ($user === 'loggedIn' && $this->user->isLoggedIn() === FALSE) { 94 | return FALSE; 95 | 96 | // User have to be logged out and is logged in 97 | } elseif ($user === 'guest' && $this->user->isLoggedIn() === TRUE) { 98 | return FALSE; 99 | } 100 | 101 | // Annotation have wrong definition 102 | } else { 103 | throw new Exceptions\InvalidArgumentException('In @Security\User annotation is allowed only one from two strings: \'loggedIn\' & \'guest\''); 104 | } 105 | 106 | return TRUE; 107 | } 108 | 109 | return TRUE; 110 | } 111 | 112 | /** 113 | * @param UI\ComponentReflection|UI\MethodReflection|Nette\Reflection\ClassType|Nette\Reflection\Method|\Reflector $element 114 | * 115 | * @return bool 116 | * 117 | * @throws Exceptions\InvalidStateException 118 | */ 119 | private function checkResources($element) : bool 120 | { 121 | // Check if element has @Security\Resource annotation & @Secured\Privilege annotation 122 | if ($element->hasAnnotation('Secured\Resource')) { 123 | $resources = $this->getElementAttribute($element, 'Secured\Resource'); 124 | 125 | if (count($resources) != 1) { 126 | throw new Exceptions\InvalidStateException('Invalid resources count in @Security\Resource annotation!'); 127 | } 128 | 129 | $privileges = $this->getElementAttribute($element, 'Secured\Privilege'); 130 | 131 | foreach ($resources as $resource) { 132 | if ($privileges !== FALSE) { 133 | foreach ($privileges as $privilege) { 134 | if ($this->user->isAllowed($resource, $privilege)) { 135 | return TRUE; 136 | } 137 | } 138 | 139 | } else { 140 | if ($this->user->isAllowed($resource)) { 141 | return TRUE; 142 | } 143 | } 144 | } 145 | 146 | return FALSE; 147 | } 148 | 149 | return TRUE; 150 | } 151 | 152 | /** 153 | * @param UI\ComponentReflection|UI\MethodReflection|Nette\Reflection\ClassType|Nette\Reflection\Method|\Reflector $element 154 | * 155 | * @return bool 156 | * 157 | * @throws Exceptions\InvalidStateException 158 | */ 159 | private function checkPrivileges($element) : bool 160 | { 161 | // Check if element has @Secured\Privilege annotation & hasn't @Secured\Resource annotation 162 | if (!$element->hasAnnotation('Secured\Resource') && $element->hasAnnotation('Secured\Privilege')) { 163 | $privileges = $this->getElementAttribute($element, 'Secured\Privilege'); 164 | 165 | if (count($privileges) != 1) { 166 | throw new Exceptions\InvalidStateException('Invalid privileges count in @Security\Privilege annotation!'); 167 | } 168 | 169 | foreach ($privileges as $privilege) { 170 | // Check if privilege name is defined 171 | if ($privilege === TRUE) { 172 | continue; 173 | } 174 | 175 | if ($this->user->isAllowed(NS\IAuthorizator::ALL, $privilege)) { 176 | return TRUE; 177 | } 178 | } 179 | 180 | return FALSE; 181 | } 182 | 183 | return TRUE; 184 | } 185 | 186 | /** 187 | * @param UI\ComponentReflection|UI\MethodReflection|Nette\Reflection\ClassType|Nette\Reflection\Method|\Reflector $element 188 | * 189 | * @return bool 190 | */ 191 | private function checkPermission($element) : bool 192 | { 193 | // Check if element has @Secured\Permission annotation 194 | if ($element->hasAnnotation('Secured\Permission')) { 195 | $permissions = $this->getElementAttribute($element, 'Secured\Permission'); 196 | 197 | foreach ($permissions as $permission) { 198 | // Check if parameters are defined 199 | if ($permission === TRUE) { 200 | continue; 201 | } 202 | 203 | // Parse resource & privilege from permission 204 | list($resource, $privilege) = explode(Entities\IPermission::DELIMITER, $permission); 205 | 206 | // Remove white spaces 207 | $resource = Utils\Strings::trim($resource); 208 | $privilege = Utils\Strings::trim($privilege); 209 | 210 | if ($this->user->isAllowed($resource, $privilege)) { 211 | return TRUE; 212 | } 213 | } 214 | 215 | return FALSE; 216 | } 217 | 218 | return TRUE; 219 | } 220 | 221 | /** 222 | * @param UI\ComponentReflection|UI\MethodReflection|Nette\Reflection\ClassType|Nette\Reflection\Method|\Reflector $element 223 | * 224 | * @return bool 225 | */ 226 | private function checkRoles($element) : bool 227 | { 228 | // Check if element has @Secured\Role annotation 229 | if ($element->hasAnnotation('Secured\Role')) { 230 | $roles = $this->getElementAttribute($element, 'Secured\Role'); 231 | 232 | foreach ($roles as $role) { 233 | // Check if role name is defined 234 | if ($role === TRUE) { 235 | continue; 236 | } 237 | 238 | if ($this->user->isInRole($role)) { 239 | return TRUE; 240 | } 241 | } 242 | 243 | return FALSE; 244 | } 245 | 246 | return TRUE; 247 | } 248 | 249 | /** 250 | * @param UI\ComponentReflection|UI\MethodReflection|Nette\Reflection\ClassType|Nette\Reflection\Method|\Reflector $element 251 | * @param string $attribute 252 | * 253 | * @return array|FALSE 254 | */ 255 | private function getElementAttribute($element, string $attribute) 256 | { 257 | if (class_exists('Nette\Application\UI\ComponentReflection')) { 258 | return UI\ComponentReflection::parseAnnotation($element, $attribute); 259 | } 260 | 261 | $values = (array) $element->getAnnotation($attribute); 262 | 263 | return is_array($values) ? $values : ($values ? [$values] : FALSE); 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/IPub/Permissions/DI/PermissionsExtension.php: -------------------------------------------------------------------------------- 1 | 8 | * @package iPublikuj:Permissions! 9 | * @subpackage DI 10 | * @since 1.0.0 11 | * 12 | * @date 10.10.14 13 | */ 14 | 15 | declare(strict_types = 1); 16 | 17 | namespace IPub\Permissions\DI; 18 | 19 | use Nette; 20 | use Nette\DI; 21 | use Nette\Security as NS; 22 | use Nette\Utils; 23 | 24 | use IPub\Permissions; 25 | use IPub\Permissions\Access; 26 | use IPub\Permissions\Entities; 27 | use IPub\Permissions\Exceptions; 28 | use IPub\Permissions\Providers; 29 | use IPub\Permissions\Security; 30 | 31 | /** 32 | * Permission extension container 33 | * 34 | * @package iPublikuj:Permissions! 35 | * @subpackage DI 36 | * 37 | * @author Adam Kadlec 38 | */ 39 | final class PermissionsExtension extends DI\CompilerExtension 40 | { 41 | /** 42 | * @var array 43 | */ 44 | private $defaults = [ 45 | 'annotation' => TRUE, 46 | 'redirectUrl' => NULL, 47 | 'providers' => [ 48 | 'roles' => TRUE, 49 | 'resources' => TRUE, 50 | 'permissions' => TRUE, 51 | ], 52 | ]; 53 | 54 | /** 55 | * @return void 56 | */ 57 | public function loadConfiguration() : void 58 | { 59 | // Get container builder 60 | $builder = $this->getContainerBuilder(); 61 | // Get extension configuration 62 | $configuration = $this->getConfig($this->defaults); 63 | 64 | // Application permissions 65 | $builder->addDefinition($this->prefix('permissions')) 66 | ->setType(Security\Permission::class); 67 | 68 | $builder->addDefinition($this->prefix('config')) 69 | ->setType(Permissions\Configuration::class) 70 | ->setArguments([ 71 | $configuration['redirectUrl'], 72 | ]); 73 | 74 | /** 75 | * Data providers 76 | */ 77 | 78 | if ($configuration['providers']['roles'] === TRUE) { 79 | $builder->addDefinition($this->prefix('providers.roles')) 80 | ->setType(Providers\RolesProvider::class); 81 | 82 | } elseif (is_string($configuration['providers']['roles']) && class_exists($configuration['providers']['roles'])) { 83 | $builder->addDefinition($this->prefix('providers.roles')) 84 | ->setType($configuration['providers']['roles']); 85 | } 86 | 87 | if ($configuration['providers']['resources'] === TRUE) { 88 | $builder->addDefinition($this->prefix('providers.resources')) 89 | ->setType(Providers\ResourcesProvider::class); 90 | 91 | } elseif (is_string($configuration['providers']['resources']) && class_exists($configuration['providers']['resources'])) { 92 | $builder->addDefinition($this->prefix('providers.resources')) 93 | ->setType($configuration['providers']['resources']); 94 | } 95 | 96 | if ($configuration['providers']['permissions'] === TRUE) { 97 | $builder->addDefinition($this->prefix('providers.permissions')) 98 | ->setType(Providers\PermissionsProvider::class); 99 | 100 | } elseif (is_string($configuration['providers']['permissions']) && class_exists($configuration['providers']['permissions'])) { 101 | $builder->addDefinition($this->prefix('providers.permissions')) 102 | ->setType($configuration['providers']['permissions']); 103 | } 104 | 105 | /** 106 | * Access checkers 107 | */ 108 | 109 | // Check if annotation checker is enabled 110 | if ($configuration['annotation'] === TRUE) { 111 | // Annotation access checkers 112 | $builder->addDefinition($this->prefix('checkers.annotation')) 113 | ->setType(Access\AnnotationChecker::class); 114 | } 115 | 116 | // Latte access checker 117 | $builder->addDefinition($this->prefix('checkers.latte')) 118 | ->setType(Access\LatteChecker::class); 119 | 120 | // Link access checker 121 | $builder->addDefinition($this->prefix('checkers.link')) 122 | ->setType(Access\LinkChecker::class); 123 | } 124 | 125 | /** 126 | * {@inheritdoc} 127 | */ 128 | public function beforeCompile() : void 129 | { 130 | parent::beforeCompile(); 131 | 132 | // Get container builder 133 | $builder = $this->getContainerBuilder(); 134 | 135 | // Get acl permissions service 136 | $permissionsProvider = $builder->findByType(Providers\IPermissionsProvider::class); 137 | $permissionsProvider = reset($permissionsProvider); 138 | 139 | // Get acl resources service 140 | $resourcesProvider = $builder->findByType(Providers\IResourcesProvider::class); 141 | $resourcesProvider = reset($resourcesProvider); 142 | 143 | // Check all extensions and search for permissions provider 144 | foreach ($this->compiler->getExtensions() as $extension) { 145 | if (!$extension instanceof IPermissionsProvider) { 146 | continue; 147 | } 148 | 149 | // Get permissions & details 150 | $this->registerPermissionsResources($extension->getPermissions(), $resourcesProvider, $permissionsProvider); 151 | } 152 | 153 | // Install extension latte macros 154 | $latteFactory = $builder->getDefinition($builder->getByType(Nette\Bridges\ApplicationLatte\ILatteFactory::class) ?: 'nette.latteFactory'); 155 | 156 | $latteFactory 157 | ->addSetup('IPub\Permissions\Latte\Macros::install(?->getCompiler())', ['@self']); 158 | } 159 | 160 | /** 161 | * @param Nette\Configurator $config 162 | * @param string $extensionName 163 | * 164 | * @return void 165 | */ 166 | public static function register(Nette\Configurator $config, $extensionName = 'permissions') : void 167 | { 168 | $config->onCompile[] = function (Nette\Configurator $config, Nette\DI\Compiler $compiler) use ($extensionName) { 169 | $compiler->addExtension($extensionName, new PermissionsExtension()); 170 | }; 171 | } 172 | 173 | /** 174 | * @param array $permissions 175 | * @param DI\ServiceDefinition|NULL $resourcesProvider 176 | * @param DI\ServiceDefinition|NULL $permissionsProvider 177 | * 178 | * @return void 179 | * 180 | * @throws Exceptions\InvalidArgumentException 181 | */ 182 | private function registerPermissionsResources( 183 | array $permissions, 184 | DI\ServiceDefinition $resourcesProvider = NULL, 185 | DI\ServiceDefinition $permissionsProvider = NULL 186 | ) : void { 187 | foreach ($permissions as $permission => $details) { 188 | if (is_string($permission) && Utils\Strings::contains($permission, Entities\IPermission::DELIMITER)) { 189 | // Parse resource & privilege from permission 190 | list($resource, $privilege) = explode(Entities\IPermission::DELIMITER, $permission); 191 | 192 | // Remove white spaces 193 | $resource = Utils\Strings::trim($resource); 194 | $privilege = Utils\Strings::trim($privilege); 195 | 196 | $resource = new Entities\Resource($resource); 197 | 198 | } elseif (is_array($details)) { 199 | if (!isset($details['resource']) || !isset($details['privilege'])) { 200 | throw new Exceptions\InvalidArgumentException('Permission must include resource & privilege.'); 201 | } 202 | 203 | // Remove white spaces 204 | $resource = Utils\Strings::trim($details['resource']); 205 | $privilege = Utils\Strings::trim($details['privilege']); 206 | 207 | $resource = new Entities\Resource($resource); 208 | 209 | $details = NULL; 210 | 211 | } elseif ($details instanceof Entities\IPermission) { 212 | $resource = $details->getResource(); 213 | $privilege = $details->getPrivilege(); 214 | 215 | $details = NULL; 216 | 217 | // Resource & privilege is in string with delimiter 218 | } else { 219 | throw new Exceptions\InvalidArgumentException(sprintf('Permission must be only string with delimiter, array with resource & privilege or instance of IPub\Permissions\Entities\IPermission, %s given', gettype($permission))); 220 | } 221 | 222 | $privilege = $privilege === '' ? NS\IAuthorizator::ALL : $privilege; 223 | 224 | // Assign permission to service 225 | $permissionsProvider->addSetup('addPermission', [$resource, $privilege, $details]); 226 | $resourcesProvider->addSetup('addResource', [$resource->getResourceId()]); 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /docs/en/index.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | Simple permission checker for [Nette Framework](http://nette.org/) 4 | 5 | ## Installation 6 | 7 | The best way to install ipub/permissions is using [Composer](http://getcomposer.org/): 8 | 9 | ```sh 10 | $ composer require ipub/permissions:@dev 11 | ``` 12 | 13 | After that you have to register extension in config.neon. 14 | 15 | ```neon 16 | extensions: 17 | permission: IPub\Permissions\DI\PermissionsExtension 18 | ``` 19 | 20 | Package contains trait, which you will have to use in presenter to override default **checkRequirements** method. This works only for PHP 5.4+, for older version you can simply copy trait content and paste it into class where you want to use it. 21 | 22 | ```php 23 | addResource('customerArea'); 92 | 93 | $intranet = $this->addResource('intranet', $customerArea); 94 | 95 | $salesModule = $this->addResource('salesModule', $intranet); 96 | $ordersModule = $this->addResource('ordersModule', $intranet); 97 | 98 | $invoices = $this->addResource('invoices', $ordersModule); 99 | $products = $this->addResource('products', $ordersModule); 100 | 101 | // ... more resources definitions 102 | } 103 | } 104 | ``` 105 | 106 | ```php 107 | addPermission($resourcesProvider->getResource('customerArea'), \Nette\Security\IAuhtorizator::ALL); 115 | $this->addPermission($resourcesProvider->getResource('intranet'), \Nette\Security\IAuhtorizator::ALL'); 116 | $this->addPermission($resourcesProvider->getResource('salesModule'), \Nette\Security\IAuhtorizator::ALL'); 117 | $this->addPermission($resourcesProvider->getResource('ordersModule'), \Nette\Security\IAuhtorizator::ALL'); 118 | $this->addPermission($resourcesProvider->getResource('ordersModule'), 'read'); 119 | $this->addPermission($resourcesProvider->getResource('invoices'), \Nette\Security\IAuhtorizator::ALL'); 120 | $this->addPermission($resourcesProvider->getResource('products'), \Nette\Security\IAuhtorizator::ALL', function($acl, $role, $resource, $privilege) { 121 | // ...code of permission assertion 122 | }); 123 | 124 | // ... more permissions definitions 125 | } 126 | 127 | } 128 | ``` 129 | 130 | Now just register both providers like any other services: 131 | 132 | ```neon 133 | permissions: 134 | providers: 135 | resources: FALSE // We have to disable automatic service registration 136 | permissions: FALSE // We have to disable automatic service registration 137 | 138 | services: 139 | - \YourNamespace\MyResourcesProvider 140 | - \YourNamespace\MyPermissionsProvider 141 | ``` 142 | 143 | or you can combine them together: 144 | 145 | ```neon 146 | permissions: 147 | providers: 148 | resources: \YourNamespace\MyResourcesProvider // Override default extension service 149 | permissions: \YourNamespace\MyPermissionsProvider // Override default extension service 150 | ``` 151 | 152 | #### Creating roles 153 | 154 | As second step, you have to feed up permission service with roles. Permission service is waiting for some RolesProvider which implement IPub\Permissions\Providers\IRolesProvider interface. 155 | If you don't want to create your own service, this extension is registering its own basic roles provider, similar to resources and permissions providers above. 156 | 157 | ```php 158 | addRole(\IPub\Permissions\Entities\IRole::ROLE_ADMINISTRATOR); 167 | $this->addRole(\IPub\Permissions\Entities\IRole::ROLE_ANONYMOUS); 168 | $this->addRole(\IPub\Permissions\Entities\IRole::ROLE_AUTHENTICATED, $this->getRole(\IPub\Permissions\Entities\IRole::ROLE_ANONYMOUS), $permissionsProvider->getPermission('customerArea:')); 169 | 170 | $this->addRole('employee', $this->getRole(\IPub\Permissions\Entities\IRole::ROLE_AUTHENTICATED), [ 171 | $permissionsProvider->getPermission('intranet:'), 172 | ]); 173 | $this->addRole('sales', $this->getRole('employee'), [ 174 | $permissionsProvider->getPermission('salesModule:'), 175 | $permissionsProvider->getPermission('ordersModule:read'), 176 | ]); 177 | $this->addRole('manager', $this->getRole('employee'), [ 178 | $permissionsProvider->getPermission('ordersModule:'), 179 | ]); 180 | 181 | // ... more roles definitions 182 | } 183 | 184 | } 185 | ``` 186 | 187 | Don't forget to register your roles provider: 188 | 189 | ```neon 190 | permissions: 191 | providers: 192 | roles: FALSE // We have to disable automatic service registration 193 | 194 | services: 195 | - \YourNamespace\MyRolesProvider 196 | ``` 197 | 198 | or you can combine them together: 199 | 200 | ```neon 201 | permissions: 202 | providers: 203 | roles: \YourNamespace\MyRolesProvider // Override default extension service 204 | ``` 205 | 206 | Now your'e set! 207 | 208 | ### Automatic registration 209 | 210 | If you don't want to create your own providers services and use little bit automation, you can use extension providers with automatic services registration and fill. 211 | 212 | #### Creating resources & privileges 213 | 214 | Once you have created your roles, you can create resources and his privileges. For this operation is available IPermissionProvider. So simply implement this interface into you extension 215 | 216 | ```php 217 | [ 230 | 'title' => 'this part is optional and can be used for editing purposes, etc.', 231 | 'description' => 'this part is optional and can be used for editing purposes, etc.' 232 | ], 233 | 'someOtherResourceName:' => [ 234 | 'title' => 'this part is optional and can be used for editing purposes, etc.', 235 | 'description' => 'this part is optional and can be used for editing purposes, etc.' 236 | ], 237 | ] 238 | } 239 | } 240 | ``` 241 | 242 | So as you can see, there is special method **getPermissions** and this method only return an array of all permission which you want to register. Extension will parse this values and register all resources and permissions automatically. 243 | 244 | #### Creating roles 245 | 246 | Roles have to be registered like in manual registration. Automatic part is not implemented yet. 247 | 248 | ## Usage 249 | 250 | Library provide a PHP trait, which enables pleasant quering Nette ACL system you've just configured. Please note that traits are available from PHP 5.4, for older versions of PHP you must copy/paste trait contents. This trait is effective only in 251 | presenter(s). 252 | 253 | ```php 254 | class BasePresenter extends Nette\Application\UI\Presenter 255 | { 256 | use \IPub\Permissions\TPermission; 257 | } 258 | ``` 259 | 260 | ### Using in annotations 261 | 262 | This extension provide a variable annotation checker. So you can secure each presenter or presenter action. 263 | 264 | ```php 265 | isAllowed('resource', 'privilege'); 315 | ``` 316 | 317 | and if user has access to this combination, you will receive *TRUE* value 318 | 319 | ### Using in Latte 320 | 321 | In latte you can use two special macros. 322 | 323 | ```html 324 |
325 |

326 | This text is for everyone.... 327 |

328 | {ifAllowed resource => 'system', privilege => 'manage user permissions'} 329 |

330 | But this one is only for special persons.... 331 |

332 | {/ifAllowed} 333 |
334 | ``` 335 | 336 | Macro **ifAllowed** is very similar to annotations definition. You can use here one or all of available parameters: user, resource, privilege, permission or role. 337 | 338 | This macro can be also used as **n:** macro: 339 | 340 | ```html 341 |
342 |

343 | This text is for everyone.... 344 |

345 |

'system', privilege => 'manage user permissions'> 346 | But this one is only for special persons.... 347 |

348 |
349 | ``` 350 | 351 | And second special macro is for links: 352 | 353 | ```html 354 | 355 | Link text... 356 | 357 | ``` 358 | 359 | Macro **n:allowedHref** is expecting only valid link and in case user doesn't have access to this link, link is displayed. 360 | 361 | ### Redirect to login page 362 | 363 | If user is not logged-in and tries to access secured resource a default action is throwing the `\Nette\Application\ForbiddenRequestException`. However if you configure so called `redirectUrl`, request will be redirected to this url (eg. login 364 | page) when this situation occurs. 365 | 366 | Also all parameters of the original request will be stored. That way you are able to restore original request and be redirected to secured resource after successful login. To configure `redirectUrl` add this to your configuration: 367 | 368 | ``` 369 | permissions: 370 | redirectUrl: 'Login:default' 371 | ``` 372 | 373 | To restore the original request prepare persistent param `backlink` in the presenter and use it in login procedure (callback) 374 | 375 | ```php 376 | class LoginPresenter extends \Nette\Application\UI\Presenter 377 | { 378 | /** 379 | * @persistent 380 | */ 381 | public $backlink; 382 | 383 | public function processLoginForm($form) 384 | { 385 | $this->getUser()->login($form->getValues()); 386 | $this->restoreRequest($this->backlink); 387 | $this->redirect('Admin:default'); 388 | } 389 | } 390 | ``` 391 | --------------------------------------------------------------------------------