Skip to content

Commit

Permalink
Merge pull request #16 from coopTilleuls/roles_support
Browse files Browse the repository at this point in the history
Roles support & Various fixes
  • Loading branch information
dunglas committed Dec 11, 2014
2 parents 150c636 + 6073204 commit ba691f2
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 23 deletions.
100 changes: 80 additions & 20 deletions Admin/AclAdminExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Doctrine\DBAL\Connection;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchy;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;

Expand All @@ -33,14 +36,24 @@ class AclAdminExtension extends AdminExtension
*/
protected $databaseConnection;

/**
* @var RoleHierarchy
*/
protected $roleHierarchy;

/**
* @param SecurityContextInterface $securityContext
* @param Connection $databaseConnection
* @param array $roleHierarchy
*/
public function __construct(SecurityContextInterface $securityContext, Connection $databaseConnection)
{
public function __construct(
SecurityContextInterface $securityContext,
Connection $databaseConnection,
array $roleHierarchy = array()
) {
$this->securityContext = $securityContext;
$this->databaseConnection = $databaseConnection;
$this->roleHierarchy = new RoleHierarchy($roleHierarchy);
}

/**
Expand All @@ -54,22 +67,50 @@ public function __construct(SecurityContextInterface $securityContext, Connectio
public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query, $context = 'list')
{
// Don't filter for admins and for not ACL enabled classes and for command cli
if (!$admin->isAclEnabled() || !$this->securityContext->getToken() || $admin->isGranted(sprintf($admin->getSecurityHandler()->getBaseRole($admin), 'ADMIN'))) {
if (
!$admin->isAclEnabled()
|| !$this->securityContext->getToken()
|| $admin->isGranted(sprintf($admin->getSecurityHandler()->getBaseRole($admin), 'ADMIN'))
) {
return;
}

// Retrieve current logged user SecurityIdentity
$user = $this->securityContext->getToken()->getUser();
$securityIdentity = UserSecurityIdentity::fromAccount($user);
$userSecurityIdentity = UserSecurityIdentity::fromAccount($user);

// Retrieve current logged user roles
$userRoles = $user->getRoles();

// Find child roles
$roles = array();
foreach ($userRoles as $userRole) {
$roles[] = ($userRole instanceof RoleInterface) ? $userRole : new Role($userRole);
}

$reachableRoles = $this->roleHierarchy->getReachableRoles($roles);

// Get identity ACL user identifier
$identifiers[] = sprintf('%s-%s', $userSecurityIdentity->getClass(), $userSecurityIdentity->getUsername());

// Get identity ACL identifier
$identifier = sprintf('%s-%s', $securityIdentity->getClass(), $securityIdentity->getUsername());
// Get identities ACL roles identifiers
foreach ($reachableRoles as $reachableRole) {
$role = $reachableRole->getRole();
if (!in_array($role, $identifiers)) {
$identifiers[] = $role;
}
}

$identityStmt = $this->databaseConnection->prepare('SELECT id FROM acl_security_identities WHERE identifier = :identifier');
$identityStmt->bindValue('identifier', $identifier);
$identityStmt->execute();
$identityStmt = $this->databaseConnection->executeQuery(
'SELECT id FROM acl_security_identities WHERE identifier IN (?)',
array($identifiers),
array(Connection::PARAM_STR_ARRAY)
);

$identityId = $identityStmt->fetchColumn();
$identityIds = array();
foreach ($identityStmt->fetchAll() as $row) {
$identityIds[] = $row['id'];
}

// Get class ACL identifier
$classType = $admin->getClass();
Expand All @@ -79,15 +120,34 @@ public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query

$classId = $classStmt->fetchColumn();

if ($identityId && $classId) {
$entriesStmt = $this->databaseConnection->prepare('SELECT object_identifier FROM acl_entries AS ae JOIN acl_object_identities AS aoi ON ae.object_identity_id = aoi.id WHERE ae.class_id = :classId AND ae.security_identity_id = :identityId AND (:view = ae.mask & :view OR :operator = ae.mask & :operator OR :master = ae.mask & :master OR :owner = ae.mask & :owner)');
$entriesStmt->bindValue('classId', $classId);
$entriesStmt->bindValue('identityId', $identityId);
$entriesStmt->bindValue('view', MaskBuilder::MASK_VIEW);
$entriesStmt->bindValue('operator', MaskBuilder::MASK_OPERATOR);
$entriesStmt->bindValue('master', MaskBuilder::MASK_MASTER);
$entriesStmt->bindValue('owner', MaskBuilder::MASK_OWNER);
$entriesStmt->execute();
if (!empty($identityIds) && $classId) {
$entriesStmt = $this->databaseConnection->executeQuery(
'SELECT DISTINCT object_identifier FROM acl_entries AS ae JOIN acl_object_identities AS aoi ON ae.object_identity_id = aoi.id WHERE ae.class_id = ? AND ae.security_identity_id IN (?) AND (? = ae.mask & ? OR ? = ae.mask & ? OR ? = ae.mask & ? OR ? = ae.mask & ?)',
array(
$classId,
$identityIds,
MaskBuilder::MASK_VIEW,
MaskBuilder::MASK_VIEW,
MaskBuilder::MASK_OPERATOR,
MaskBuilder::MASK_OPERATOR,
MaskBuilder::MASK_MASTER,
MaskBuilder::MASK_MASTER,
MaskBuilder::MASK_OWNER,
MaskBuilder::MASK_OWNER
),
array(
\PDO::PARAM_INT,
Connection::PARAM_INT_ARRAY,
\PDO::PARAM_INT,
\PDO::PARAM_INT,
\PDO::PARAM_INT,
\PDO::PARAM_INT,
\PDO::PARAM_INT,
\PDO::PARAM_INT,
\PDO::PARAM_INT,
\PDO::PARAM_INT
)
);

$ids = array();
foreach ($entriesStmt->fetchAll() as $row) {
Expand All @@ -96,7 +156,7 @@ public function configureQuery(AdminInterface $admin, ProxyQueryInterface $query

if (count($ids)) {
$query
->andWhere('o.id IN (:ids)')
->andWhere('o IN (:ids)')
->setParameter('ids', $ids)
;

Expand Down
5 changes: 3 additions & 2 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">


<parameters>
Expand All @@ -13,6 +13,7 @@
<service id="coop_tilleuls_acl_sonata_admin_extension.acl.extension" class="%coop_tilleuls_acl_sonata_admin_extension.acl.extension.class%">
<argument type="service" id="security.context" />
<argument type="service" id="security.acl.dbal.connection" />
<argument>%security.role_hierarchy.roles%</argument>
<tag name="sonata.admin.extension" global="true" />
</service>
</services>
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
}
],
"require": {
"sonata-project/admin-bundle": "~2.2,>=2.2.2"
"sonata-project/admin-bundle": "~2.2,>=2.2.2",
"doctrine/dbal": "~2.2"
},
"autoload": {
"psr-0": { "CoopTilleuls\\Bundle\\AclSonataAdminExtensionBundle": "" }
Expand Down

0 comments on commit ba691f2

Please sign in to comment.