damejidlo / permissions
Installs: 6 078
Dependents: 0
Suggesters: 0
Security: 0
Stars: 11
Watchers: 12
Forks: 0
Open Issues: 0
Requires
- php: >=7.1
- nette/security: ^2.4
- nette/utils: ^2.4
Requires (Dev)
- damejidlo/coding-standard: ^0.10.0
- mockery/mockery: ^1.2
- nette/tester: ^2.2
This package is auto-updated.
Last update: 2020-01-16 15:01:26 UTC
README
Motivation
Purpose of this library is to add User specific data to isAllowed
evaluation. Assertion callback got
IUser
directly as first argument.
This solves biggest "problem" of native ACL in Nette such is:
$callback = function (IUser $user, $queriedRole, $queriedResource) { return $user->getEntity()->getId() === $queriedResource->getEntity()->getCreatorId(); }; // god can destroy world, but only the one he created $authorizator->allow('god', 'world', 'destroy', $callback);
Another aspect of this library is separating Authorizator from Nette\Security\User
as
it's definitely not users responsibility to provide this functionality.
Disclaimer
This library is written to be as much as possible similar to Permission
class in Nette. However evaluation of rules
is written from scratch.
And therefore:
- does not implement
Nette\Security\IAuthorizator
(it can't due to differentisAllowed
method API), - can be significantly slower (but is written nicely),
- there is no guarantee that behaves 100% same way.
Installation
composer require damejidlo/permission
Configuration
AccessList
and Neon
Example implementation of your own AccessList
service.
class AccessList extends Authorizator { /** * @param string[][] $roles */ public function addRoles(array $roles) { foreach ($roles as $role => $parentRoles) { $this->addRole($role, $parentRoles); } } /** * @param @param string[] $resources */ public function addResources(array $resources) { foreach ($resources as $resource) { $this->addResource($resource); } } /** * @param string[][][] $directives */ public function addDirectives(array $directives) { foreach ($directives as $resource => $resourceDirectives) { foreach ($resourceDirectives as $privilege => $privilegeDirectives) { foreach ($privilegeDirectives as $roleIdentifier => $directiveType) { $this->createDirective($directiveType, $roleIdentifier, $resource, $privilege); } } } } public function someStuff() { $callback = function (IUser $user, $queriedRole, $queriedResource) { return $user->getEntity()->getId() === $queriedResource->getEntity()->getCreatorId(); }; // god can destroy world, but only the one he created $authorizator->allow('god', 'world', 'destroy', $callback); } }
Then just add to your config.neon
parameters: acl: roles: writer: [] reviewer: [writer] resources: - article directives: article: create: writer: allow publish: reviewer: allow services: acl: class: YourProject\Security\AccessList setup: - addRoles(%acl.roles%) - addResources(%acl.resources%) - addDirectives(%acl.directives%) - someStuff() # here we can do some "cool stuff"
Create your AclUser
class AclUser extends Object implements IUser { // Implement `getRoles` method }
Creating your own Nette\Security\User
You need to create your own User
service
class MyLoggedUser extends \Nette\Security\User { /** * @param IUserStorage $storage * @param IAuthenticator|NULL $authenticator */ public function __construct(IUserStorage $storage, IAuthenticator $authenticator = NULL) { parent::__construct($storage, $authenticator); // No IAuthorizator here !!! } /** * @inheritdoc */ public function isAllowed($resource = IAuthorizator::ALL, $privilege = IAuthorizator::ALL) { throw new LogicException('Use Damejidlo\ACL\Authorizator directly. User shouldn\'t have such a responsibility'); } /** * @inheritdoc */ public function isInRole($role) { throw new LogicException('Use Damejidlo\ACL\Authorizator directly. User shouldn\'t have such a responsibility'); } /** * @return AclUser */ public function getAclUser() { $entity = $this->getEntity(); // depens on your implementation return new AclUser($entity, $this->getRoles()); } }
services: user: Some\Namespace\MyLoggedUser
Load your Authorizator into template
Best way is to create your own TemplateFactory
. And in createTemplate
method just call:
/** * @param Control|NULL $control * @return Template */ public function createTemplate(Control $control = NULL) { $template = parent::createTemplate($control); // Some stuff (helper registration, etc...) $template->setParameters([ 'authorizator' => $this->authorizator, ]); return $template; }
Usage
And now, profit!
// In some Presenter public function handleDestroy($worldId) { $world = $this->worldFinder->findWorld($worldId); $resource = new WorldResource($world); $permission = 'destroy'; if (!$this->authorizator->isAllowed($this->user->getAclUser(), $resource, $permission) { throw new NotAllowedException($resource, $permission); } }