Skip to content

Instantly share code, notes, and snippets.

@dadamssg
Last active August 29, 2015 14:00
Show Gist options
  • Save dadamssg/11357758 to your computer and use it in GitHub Desktop.
Save dadamssg/11357758 to your computer and use it in GitHub Desktop.
<?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;
}
}
<?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
}
<?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);
}
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