Skip to content

Commit

Permalink
Merge pull request #14 from myclabs/RebuildResouceAuthorizations
Browse files Browse the repository at this point in the history
Rebuild resouce authorizations
  • Loading branch information
valentin-claras committed Aug 20, 2014
2 parents 5da2027 + 15e5374 commit e72ccbb
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 1 deletion.
25 changes: 25 additions & 0 deletions src/ACL.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use MyCLabs\ACL\Model\Role;
use MyCLabs\ACL\Model\SecurityIdentityInterface;
use MyCLabs\ACL\Repository\AuthorizationRepository;
use MyCLabs\ACL\Repository\RoleRepository;

/**
* Manages ACL.
Expand Down Expand Up @@ -171,6 +172,30 @@ public function processDeletedResource(EntityResource $resource)
$repository->removeAuthorizationsForResource($resource);
}

/**
* Clears and rebuilds all the authorization for a given resource.
*
* @param EntityResource $resource
*/
public function rebuildAuthorizationsForResource(EntityResource $resource)
{
/** @var RoleRepository $roleRepository */
$roleRepository = $this->entityManager->getRepository('MyCLabs\ACL\Model\Role');
// Get all Role applied directly on the Resource.
$rolesDirectlyLinkedToResource = $roleRepository->findRolesDirectlyLinkedToResource($resource);

// Deletion of all the old authorizations.
$this->processDeletedResource($resource);

// Creation of all the parent authorizations.
$this->processNewResource($resource);

// Create Authorizations from Roles attached directly to the resource.
foreach ($rolesDirectlyLinkedToResource as $role) {
$role->createAuthorizations($this);
}
}

/**
* Clears and rebuilds all the authorizations from the roles.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Model/Role.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* Role.
*
* @ORM\Entity
* @ORM\Entity(repositoryClass="MyCLabs\ACL\Repository\RoleRepository")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\Table(name="ACL_Role")
Expand Down
49 changes: 49 additions & 0 deletions src/Repository/RoleRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace MyCLabs\ACL\Repository;

use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityRepository;
use MyCLabs\ACL\Model\Role;
use MyCLabs\ACL\Model\ClassResource;
use MyCLabs\ACL\Model\EntityResource;
use MyCLabs\ACL\Model\ResourceInterface;

/**
* Authorizations repository.
*
* @author Valentin Claras <[email protected]>
*/
class RoleRepository extends EntityRepository
{
/**
* Returns Roles that are directly linked to the given resource.
*
* @param ResourceInterface $resource
* @return Role[]
*/
public function findRolesDirectlyLinkedToResource(ResourceInterface $resource)
{
$qb = $this->createQueryBuilder('role');

// Join
$qb->join('role.authorizations', 'a');

// Root authorizations means they are attached to the given resource
$qb->andWhere('a.parentAuthorization IS NULL');

if ($resource instanceof EntityResource) {
$qb->andWhere('a.entityClass = :entityClass');
$qb->andWhere('a.entityId = :entityId');
$qb->setParameter('entityClass', ClassUtils::getClass($resource));
$qb->setParameter('entityId', $resource->getId());
}
if ($resource instanceof ClassResource) {
$qb->andWhere('a.entityClass = :entityClass');
$qb->andWhere('a.entityId IS NULL');
$qb->setParameter('entityClass', $resource->getClass());
}

return $qb->getQuery()->getResult();
}
}
41 changes: 41 additions & 0 deletions tests/Integration/RebuildAuthorizationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,47 @@
*/
class RebuildAuthorizationTest extends AbstractIntegrationTest
{
public function testRebuildAuthorizationsForResource()
{
$article1 = new Article();
$this->em->persist($article1);
$article2 = new Article();
$this->em->persist($article2);

$user = new User();
$this->em->persist($user);
$this->em->flush();

$this->acl->grant($user, new ArticleEditorRole($user, $article2));

$this->em->clear();

$qb = $this->em->createQueryBuilder();
$qb->select('count(authorization.id)');
$qb->from('MyCLabs\ACL\Model\Authorization', 'authorization');
$query = $qb->getQuery();

$initialCount = $query->getSingleScalarResult();

$this->acl->rebuildAuthorizationsForResource($article1);

$this->assertFalse($this->acl->isAllowed($user, Actions::VIEW, $article1));
$this->assertFalse($this->acl->isAllowed($user, Actions::EDIT, $article1));
$this->assertTrue($this->acl->isAllowed($user, Actions::VIEW, $article2));
$this->assertTrue($this->acl->isAllowed($user, Actions::EDIT, $article2));

$this->assertEquals($initialCount, $query->getSingleScalarResult());

$this->acl->rebuildAuthorizationsForResource($article2);

$this->assertFalse($this->acl->isAllowed($user, Actions::VIEW, $article1));
$this->assertFalse($this->acl->isAllowed($user, Actions::EDIT, $article1));
$this->assertTrue($this->acl->isAllowed($user, Actions::VIEW, $article2));
$this->assertTrue($this->acl->isAllowed($user, Actions::EDIT, $article2));

$this->assertEquals($initialCount, $query->getSingleScalarResult());
}

public function testRebuildAuthorizations()
{
$article1 = new Article();
Expand Down
117 changes: 117 additions & 0 deletions tests/Unit/Repository/RoleRepositoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace Tests\MyCLabs\ACL\Unit\Repository;

use Doctrine\Common\Cache\ArrayCache;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\Tools\Setup;
use MyCLabs\ACL\ACL;
use MyCLabs\ACL\Doctrine\ACLSetup;
use MyCLabs\ACL\Model\Actions;
use MyCLabs\ACL\Model\Authorization;
use MyCLabs\ACL\Model\ClassResource;
use MyCLabs\ACL\Repository\AuthorizationRepository;
use MyCLabs\ACL\Repository\RoleRepository;
use Tests\MyCLabs\ACL\Unit\Repository\Model\File;
use Tests\MyCLabs\ACL\Unit\Repository\Model\FileOwnerRole;
use Tests\MyCLabs\ACL\Unit\Repository\Model\User;

/**
* @covers \MyCLabs\ACL\Repository\RoleRepository
*/
class RoleRepositoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @var EntityManager
*/
private $em;

/**
* @var ACL
*/
private $acl;

public function setUp()
{
$paths = [
__DIR__ . '/../../../src/Model',
__DIR__ . '/Model',
];
$dbParams = [
'driver' => 'pdo_sqlite',
'memory' => true,
];

$setup = new ACLSetup();
$setup->setSecurityIdentityClass('Tests\MyCLabs\ACL\Unit\Repository\Model\User');
$setup->registerRoleClass('Tests\MyCLabs\ACL\Unit\Repository\Model\FileOwnerRole', 'fileOwner');

// Create the entity manager
$config = Setup::createAnnotationMetadataConfiguration($paths, true, null, new ArrayCache(), false);
$this->em = EntityManager::create($dbParams, $config);

$this->acl = new ACL($this->em);

$setup->setUpEntityManager($this->em, function () {
return $this->acl;
});

// Create the DB
$tool = new SchemaTool($this->em);
$tool->createSchema($this->em->getMetadataFactory()->getAllMetadata());
}

public function testFindRolesDirectlyLinkedToResource()
{
$user = new User();
$this->em->persist($user);
$resource = new File();
$this->em->persist($resource);
$directRole = new FileOwnerRole($user, $resource);
$this->em->persist($directRole);
$parentRole = new FileOwnerRole($user, $resource);
$this->em->persist($parentRole);
$this->em->flush();

$classResource = new ClassResource('\Tests\MyCLabs\ACL\Unit\Repository\Model\File');


$parentView = Authorization::create($parentRole, new Actions([ Actions::VIEW ]), $classResource, true);

$authorizations = [
Authorization::create($directRole, new Actions([ Actions::EDIT ]), $resource, true),
Authorization::create($directRole, new Actions([ Actions::DELETE ]), $resource, true),
$parentView,
$parentView->createChildAuthorization($resource)
];

/** @var AuthorizationRepository $authorizationRepository */
$authorizationRepository = $this->em->getRepository('MyCLabs\ACL\Model\Authorization');

$authorizationRepository->insertBulk($authorizations);

// Check user can VIEW and EDIT the Resource
$this->assertTrue($authorizationRepository->isAllowedOnEntity($user, Actions::VIEW, $resource));
$this->assertTrue($authorizationRepository->isAllowedOnEntity($user, Actions::EDIT, $resource));
$this->assertTrue($authorizationRepository->isAllowedOnEntity($user, Actions::DELETE, $resource));

// Check user can only VIEW the ClassResource
$this->assertTrue($authorizationRepository->isAllowedOnEntityClass($user, Actions::VIEW, $classResource->getClass()));
$this->assertFalse($authorizationRepository->isAllowedOnEntityClass($user, Actions::EDIT, $classResource->getClass()));
$this->assertFalse($authorizationRepository->isAllowedOnEntityClass($user, Actions::DELETE, $classResource->getClass()));

/** @var RoleRepository $roleRepository */
$roleRepository = $this->em->getRepository('MyCLabs\ACL\Model\Role');

// Test for entity resource
$result = $roleRepository->findRolesDirectlyLinkedToResource($resource);
$this->assertCount(1, $result);
$this->assertSame($directRole, $result[0]);

// Test for class resource
$result = $roleRepository->findRolesDirectlyLinkedToResource($classResource);
$this->assertCount(1, $result);
$this->assertSame($parentRole, $result[0]);
}
}

0 comments on commit e72ccbb

Please sign in to comment.