Skip to content

Commit e72ccbb

Browse files
Merge pull request #14 from myclabs/RebuildResouceAuthorizations
Rebuild resouce authorizations
2 parents 5da2027 + 15e5374 commit e72ccbb

File tree

5 files changed

+233
-1
lines changed

5 files changed

+233
-1
lines changed

src/ACL.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use MyCLabs\ACL\Model\Role;
1414
use MyCLabs\ACL\Model\SecurityIdentityInterface;
1515
use MyCLabs\ACL\Repository\AuthorizationRepository;
16+
use MyCLabs\ACL\Repository\RoleRepository;
1617

1718
/**
1819
* Manages ACL.
@@ -171,6 +172,30 @@ public function processDeletedResource(EntityResource $resource)
171172
$repository->removeAuthorizationsForResource($resource);
172173
}
173174

175+
/**
176+
* Clears and rebuilds all the authorization for a given resource.
177+
*
178+
* @param EntityResource $resource
179+
*/
180+
public function rebuildAuthorizationsForResource(EntityResource $resource)
181+
{
182+
/** @var RoleRepository $roleRepository */
183+
$roleRepository = $this->entityManager->getRepository('MyCLabs\ACL\Model\Role');
184+
// Get all Role applied directly on the Resource.
185+
$rolesDirectlyLinkedToResource = $roleRepository->findRolesDirectlyLinkedToResource($resource);
186+
187+
// Deletion of all the old authorizations.
188+
$this->processDeletedResource($resource);
189+
190+
// Creation of all the parent authorizations.
191+
$this->processNewResource($resource);
192+
193+
// Create Authorizations from Roles attached directly to the resource.
194+
foreach ($rolesDirectlyLinkedToResource as $role) {
195+
$role->createAuthorizations($this);
196+
}
197+
}
198+
174199
/**
175200
* Clears and rebuilds all the authorizations from the roles.
176201
*/

src/Model/Role.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
/**
1111
* Role.
1212
*
13-
* @ORM\Entity
13+
* @ORM\Entity(repositoryClass="MyCLabs\ACL\Repository\RoleRepository")
1414
* @ORM\InheritanceType("JOINED")
1515
* @ORM\DiscriminatorColumn(name="type", type="string")
1616
* @ORM\Table(name="ACL_Role")

src/Repository/RoleRepository.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace MyCLabs\ACL\Repository;
4+
5+
use Doctrine\Common\Util\ClassUtils;
6+
use Doctrine\ORM\EntityRepository;
7+
use MyCLabs\ACL\Model\Role;
8+
use MyCLabs\ACL\Model\ClassResource;
9+
use MyCLabs\ACL\Model\EntityResource;
10+
use MyCLabs\ACL\Model\ResourceInterface;
11+
12+
/**
13+
* Authorizations repository.
14+
*
15+
* @author Valentin Claras <dev.myclabs.acl@valentin.claras.fr>
16+
*/
17+
class RoleRepository extends EntityRepository
18+
{
19+
/**
20+
* Returns Roles that are directly linked to the given resource.
21+
*
22+
* @param ResourceInterface $resource
23+
* @return Role[]
24+
*/
25+
public function findRolesDirectlyLinkedToResource(ResourceInterface $resource)
26+
{
27+
$qb = $this->createQueryBuilder('role');
28+
29+
// Join
30+
$qb->join('role.authorizations', 'a');
31+
32+
// Root authorizations means they are attached to the given resource
33+
$qb->andWhere('a.parentAuthorization IS NULL');
34+
35+
if ($resource instanceof EntityResource) {
36+
$qb->andWhere('a.entityClass = :entityClass');
37+
$qb->andWhere('a.entityId = :entityId');
38+
$qb->setParameter('entityClass', ClassUtils::getClass($resource));
39+
$qb->setParameter('entityId', $resource->getId());
40+
}
41+
if ($resource instanceof ClassResource) {
42+
$qb->andWhere('a.entityClass = :entityClass');
43+
$qb->andWhere('a.entityId IS NULL');
44+
$qb->setParameter('entityClass', $resource->getClass());
45+
}
46+
47+
return $qb->getQuery()->getResult();
48+
}
49+
}

tests/Integration/RebuildAuthorizationsTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,47 @@
1212
*/
1313
class RebuildAuthorizationTest extends AbstractIntegrationTest
1414
{
15+
public function testRebuildAuthorizationsForResource()
16+
{
17+
$article1 = new Article();
18+
$this->em->persist($article1);
19+
$article2 = new Article();
20+
$this->em->persist($article2);
21+
22+
$user = new User();
23+
$this->em->persist($user);
24+
$this->em->flush();
25+
26+
$this->acl->grant($user, new ArticleEditorRole($user, $article2));
27+
28+
$this->em->clear();
29+
30+
$qb = $this->em->createQueryBuilder();
31+
$qb->select('count(authorization.id)');
32+
$qb->from('MyCLabs\ACL\Model\Authorization', 'authorization');
33+
$query = $qb->getQuery();
34+
35+
$initialCount = $query->getSingleScalarResult();
36+
37+
$this->acl->rebuildAuthorizationsForResource($article1);
38+
39+
$this->assertFalse($this->acl->isAllowed($user, Actions::VIEW, $article1));
40+
$this->assertFalse($this->acl->isAllowed($user, Actions::EDIT, $article1));
41+
$this->assertTrue($this->acl->isAllowed($user, Actions::VIEW, $article2));
42+
$this->assertTrue($this->acl->isAllowed($user, Actions::EDIT, $article2));
43+
44+
$this->assertEquals($initialCount, $query->getSingleScalarResult());
45+
46+
$this->acl->rebuildAuthorizationsForResource($article2);
47+
48+
$this->assertFalse($this->acl->isAllowed($user, Actions::VIEW, $article1));
49+
$this->assertFalse($this->acl->isAllowed($user, Actions::EDIT, $article1));
50+
$this->assertTrue($this->acl->isAllowed($user, Actions::VIEW, $article2));
51+
$this->assertTrue($this->acl->isAllowed($user, Actions::EDIT, $article2));
52+
53+
$this->assertEquals($initialCount, $query->getSingleScalarResult());
54+
}
55+
1556
public function testRebuildAuthorizations()
1657
{
1758
$article1 = new Article();
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
3+
namespace Tests\MyCLabs\ACL\Unit\Repository;
4+
5+
use Doctrine\Common\Cache\ArrayCache;
6+
use Doctrine\ORM\EntityManager;
7+
use Doctrine\ORM\Tools\SchemaTool;
8+
use Doctrine\ORM\Tools\Setup;
9+
use MyCLabs\ACL\ACL;
10+
use MyCLabs\ACL\Doctrine\ACLSetup;
11+
use MyCLabs\ACL\Model\Actions;
12+
use MyCLabs\ACL\Model\Authorization;
13+
use MyCLabs\ACL\Model\ClassResource;
14+
use MyCLabs\ACL\Repository\AuthorizationRepository;
15+
use MyCLabs\ACL\Repository\RoleRepository;
16+
use Tests\MyCLabs\ACL\Unit\Repository\Model\File;
17+
use Tests\MyCLabs\ACL\Unit\Repository\Model\FileOwnerRole;
18+
use Tests\MyCLabs\ACL\Unit\Repository\Model\User;
19+
20+
/**
21+
* @covers \MyCLabs\ACL\Repository\RoleRepository
22+
*/
23+
class RoleRepositoryTest extends \PHPUnit_Framework_TestCase
24+
{
25+
/**
26+
* @var EntityManager
27+
*/
28+
private $em;
29+
30+
/**
31+
* @var ACL
32+
*/
33+
private $acl;
34+
35+
public function setUp()
36+
{
37+
$paths = [
38+
__DIR__ . '/../../../src/Model',
39+
__DIR__ . '/Model',
40+
];
41+
$dbParams = [
42+
'driver' => 'pdo_sqlite',
43+
'memory' => true,
44+
];
45+
46+
$setup = new ACLSetup();
47+
$setup->setSecurityIdentityClass('Tests\MyCLabs\ACL\Unit\Repository\Model\User');
48+
$setup->registerRoleClass('Tests\MyCLabs\ACL\Unit\Repository\Model\FileOwnerRole', 'fileOwner');
49+
50+
// Create the entity manager
51+
$config = Setup::createAnnotationMetadataConfiguration($paths, true, null, new ArrayCache(), false);
52+
$this->em = EntityManager::create($dbParams, $config);
53+
54+
$this->acl = new ACL($this->em);
55+
56+
$setup->setUpEntityManager($this->em, function () {
57+
return $this->acl;
58+
});
59+
60+
// Create the DB
61+
$tool = new SchemaTool($this->em);
62+
$tool->createSchema($this->em->getMetadataFactory()->getAllMetadata());
63+
}
64+
65+
public function testFindRolesDirectlyLinkedToResource()
66+
{
67+
$user = new User();
68+
$this->em->persist($user);
69+
$resource = new File();
70+
$this->em->persist($resource);
71+
$directRole = new FileOwnerRole($user, $resource);
72+
$this->em->persist($directRole);
73+
$parentRole = new FileOwnerRole($user, $resource);
74+
$this->em->persist($parentRole);
75+
$this->em->flush();
76+
77+
$classResource = new ClassResource('\Tests\MyCLabs\ACL\Unit\Repository\Model\File');
78+
79+
80+
$parentView = Authorization::create($parentRole, new Actions([ Actions::VIEW ]), $classResource, true);
81+
82+
$authorizations = [
83+
Authorization::create($directRole, new Actions([ Actions::EDIT ]), $resource, true),
84+
Authorization::create($directRole, new Actions([ Actions::DELETE ]), $resource, true),
85+
$parentView,
86+
$parentView->createChildAuthorization($resource)
87+
];
88+
89+
/** @var AuthorizationRepository $authorizationRepository */
90+
$authorizationRepository = $this->em->getRepository('MyCLabs\ACL\Model\Authorization');
91+
92+
$authorizationRepository->insertBulk($authorizations);
93+
94+
// Check user can VIEW and EDIT the Resource
95+
$this->assertTrue($authorizationRepository->isAllowedOnEntity($user, Actions::VIEW, $resource));
96+
$this->assertTrue($authorizationRepository->isAllowedOnEntity($user, Actions::EDIT, $resource));
97+
$this->assertTrue($authorizationRepository->isAllowedOnEntity($user, Actions::DELETE, $resource));
98+
99+
// Check user can only VIEW the ClassResource
100+
$this->assertTrue($authorizationRepository->isAllowedOnEntityClass($user, Actions::VIEW, $classResource->getClass()));
101+
$this->assertFalse($authorizationRepository->isAllowedOnEntityClass($user, Actions::EDIT, $classResource->getClass()));
102+
$this->assertFalse($authorizationRepository->isAllowedOnEntityClass($user, Actions::DELETE, $classResource->getClass()));
103+
104+
/** @var RoleRepository $roleRepository */
105+
$roleRepository = $this->em->getRepository('MyCLabs\ACL\Model\Role');
106+
107+
// Test for entity resource
108+
$result = $roleRepository->findRolesDirectlyLinkedToResource($resource);
109+
$this->assertCount(1, $result);
110+
$this->assertSame($directRole, $result[0]);
111+
112+
// Test for class resource
113+
$result = $roleRepository->findRolesDirectlyLinkedToResource($classResource);
114+
$this->assertCount(1, $result);
115+
$this->assertSame($parentRole, $result[0]);
116+
}
117+
}

0 commit comments

Comments
 (0)