Last active
August 29, 2015 14:00
-
-
Save dadamssg/11357758 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ProgrammingAreHard\ResourceBundle\Security; | |
use Symfony\Component\Security\Acl\Domain\ObjectIdentity; | |
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; | |
use Symfony\Component\Security\Acl\Exception\AclNotFoundException; | |
use Symfony\Component\Security\Acl\Exception\NoAceFoundException; | |
use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface; | |
use Symfony\Component\Security\Acl\Permission\BasicPermissionMap; | |
use Symfony\Component\Security\Acl\Permission\MaskBuilder; | |
use Symfony\Component\Security\Acl\Permission\PermissionMapInterface; | |
use Symfony\Component\Security\Core\User\UserInterface; | |
class AclPermissionsArbiter implements PermissionsArbiter | |
{ | |
const MASK_ADD = 1; | |
const MASK_REMOVE = 0; | |
/** | |
* @var object | |
*/ | |
protected $resource; | |
/** | |
* @var string[] | |
*/ | |
protected $permissions = []; | |
/** | |
* @var MutableAclProviderInterface | |
*/ | |
private $aclProvider; | |
/** | |
* @var MaskBuilder | |
*/ | |
private $maskBuilder; | |
/** | |
* @var PermissionMapInterface | |
*/ | |
private $permissionMap; | |
/** | |
* Constructor. | |
* | |
* @param MutableAclProviderInterface $aclProvider | |
* @param MaskBuilder $maskBuilder | |
* @param PermissionMapInterface $permissionMap | |
*/ | |
public function __construct( | |
MutableAclProviderInterface $aclProvider, | |
MaskBuilder $maskBuilder = null, | |
PermissionMapInterface $permissionMap = null | |
) { | |
$this->aclProvider = $aclProvider; | |
$this->maskBuilder = $maskBuilder ? $maskBuilder : new MaskBuilder; | |
$this->permissionMap = $permissionMap ? $permissionMap : new BasicPermissionMap; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setResource($resource) | |
{ | |
$this->resource = $resource; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setPermissions($permissions) | |
{ | |
$this->permissions = is_array($permissions) ? $permissions : (array)$permissions; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function grant(UserInterface $user) | |
{ | |
$acl = $this->getAcl($this->resource); | |
$sid = $this->getSecurityIdentity($user); | |
foreach ($acl->getObjectAces() as $i => $ace) { | |
if ($ace->getSecurityIdentity()->equals($sid)) { | |
$mask = $this->buildMask($ace->getMask()); | |
$acl->updateObjectAce($i, $mask); | |
$this->aclProvider->updateAcl($acl); | |
return; | |
} | |
} | |
$acl->insertObjectAce($sid, $this->buildMask()); | |
$this->aclProvider->updateAcl($acl); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function revoke(UserInterface $user) | |
{ | |
$acl = $this->getAcl($this->resource); | |
$sid = $this->getSecurityIdentity($user); | |
foreach ($acl->getObjectAces() as $i => $ace) { | |
if ($ace->getSecurityIdentity()->equals($sid)) { | |
$mask = $this->buildMask($ace->getMask(), self::MASK_REMOVE); | |
$acl->updateObjectAce($i, $mask); | |
$this->aclProvider->updateAcl($acl); | |
return; | |
} | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function isGranted(UserInterface $user) | |
{ | |
$acl = $this->getAcl($this->resource); | |
if ($masks = $this->getMasksArray()) { | |
try { | |
return $acl->isGranted($masks, [$this->getSecurityIdentity($user)]); | |
} catch (NoAceFoundException $e) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Get mask with the current permissions modifications. | |
* | |
* @param int $mask - mask to begin with | |
* @param int $mode - MASK_ADD or MASK_REMOVE | |
* @return int | |
*/ | |
private function buildMask($mask = 0, $mode = self::MASK_ADD) | |
{ | |
$this->maskBuilder->reset(); | |
$this->maskBuilder->add($mask); | |
foreach ($this->permissions as $permission) { | |
if (self::MASK_ADD === $mode) { | |
$this->maskBuilder->add($permission); | |
} else { | |
$this->maskBuilder->remove($permission); | |
} | |
} | |
return $this->maskBuilder->get(); | |
} | |
/** | |
* Get permission masks. | |
* | |
* @return int[] | |
*/ | |
private function getMasksArray() | |
{ | |
$allMasks = []; | |
foreach ($this->permissions as $permission) { | |
$masks = $this->permissionMap->getMasks($permission, $this->resource); | |
if (is_array($masks)) { | |
$allMasks = array_merge($allMasks, $masks); | |
} | |
} | |
return array_unique($allMasks); | |
} | |
/** | |
* Get resource identity. | |
* | |
* @param object $resource | |
* @return ObjectIdentity | |
*/ | |
private function getObjectIdentity($resource) | |
{ | |
return ObjectIdentity::fromDomainObject($resource); | |
} | |
/** | |
* Get user security identity. | |
* | |
* @param UserInterface $user | |
* @return UserSecurityIdentity | |
*/ | |
private function getSecurityIdentity(UserInterface $user) | |
{ | |
return UserSecurityIdentity::fromAccount($user); | |
} | |
/** | |
* Get resource's ACL. | |
* | |
* @param object $resource | |
* @return \Symfony\Component\Security\Acl\Model\AclInterface|\Symfony\Component\Security\Acl\Model\MutableAclInterface | |
*/ | |
private function getAcl($resource) | |
{ | |
$objectIdentity = $this->getObjectIdentity($resource); | |
try { | |
$acl = $this->aclProvider->findAcl($objectIdentity); | |
} catch (AclNotFoundException $e) { | |
$acl = $this->aclProvider->createAcl($objectIdentity); | |
} | |
return $acl; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$document = new Document('some secure text'); | |
$user = $this->get('security.context')->getToken()->getUser(); | |
$arbiter = $this->get('permissions.arbiter'); | |
$arbiter | |
->setResource($document) | |
->setPermissions(MaskBuilder::MASK_OWNER) | |
->grant($user); | |
// or use an array of permissions | |
$arbiter | |
->setResource($document) | |
->setPermissions([MaskBuilder::MASK_VIEW, MaskBuilder::MASK_EDIT]) | |
->grant($user); | |
$otherUser = // get another user; | |
$arbiter | |
->setPermssions(MaskBuilder::MASK_VIEW) | |
->grant($otherUser); | |
// other user doesn't need VIEW access now. Remove the permissions currently in the arbiter from the user | |
$arbiter->revoke($otherUser); | |
$arbiter->setPermissions(MaskBuilder::MASK_EDIT) | |
// check if user has EDIT permission on the document | |
if ($arbiter->isGranted($user)) { | |
// user has access | |
} | |
if ($arbiter->isGranted($otherUser)) { | |
// execution won't get here because $otherUser isn't granted EDIT permission | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ProgrammingAreHard\ResourceBundle\Security; | |
use Symfony\Component\Security\Core\User\UserInterface; | |
interface PermissionsArbiter | |
{ | |
/** | |
* Set resource. | |
* | |
* @param object $resource | |
* @return $this | |
*/ | |
public function setResource($resource); | |
/** | |
* Set permissions. | |
* | |
* @param int|int[] $permission | |
* @return $this | |
*/ | |
public function setPermissions($permission); | |
/** | |
* Grant user permissions. | |
* | |
* @param UserInterface $user | |
*/ | |
public function grant(UserInterface $user); | |
/** | |
* Revoke user permissions. | |
* | |
* @param UserInterface $user | |
*/ | |
public function revoke(UserInterface $user); | |
/** | |
* Check if user has permissions. | |
* | |
* @param UserInterface $user | |
* @return bool | |
*/ | |
public function isGranted(UserInterface $user); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
permissions.arbiter: | |
class: ProgrammingAreHard\ResourceBundle\Security\AclPermissionsArbiter | |
arguments: | |
- @security.acl.provider |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment