Created
August 7, 2012 13:49
-
-
Save PoisonousJohn/3285478 to your computer and use it in GitHub Desktop.
Acl Helper
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 WSL\BaseBundle\Util\Helper; | |
use Doctrine\ORM\Query; | |
use Doctrine\ORM\QueryBuilder; | |
use Symfony\Component\Security\Acl\Permission\MaskBuilder; | |
/* | |
* To change this template, choose Tools | Templates | |
* and open the template in the editor. | |
*/ | |
/** | |
* Description of ACL | |
* | |
* @author Anil | |
*/ | |
class ACLHelper | |
{ | |
function __construct($doctrine, $securityContext) | |
{ | |
$this->em = $doctrine->getEntityManager(); | |
$this->securityContext = $securityContext; | |
$this->aclConnection = $doctrine->getConnection('acl'); | |
} | |
protected function cloneQuery(Query $query) | |
{ | |
$aclAppliedQuery = clone $query; | |
$params = $query->getParameters(); | |
foreach ($params as $key => $param) { | |
$aclAppliedQuery->setParameter($key, $param); | |
} | |
return $aclAppliedQuery; | |
} | |
/** | |
* This will clone the original query and | |
* @param QueryBuilder $queryBuilder | |
* @param array $permissions | |
* @return type | |
*/ | |
public function apply(QueryBuilder $queryBuilder, | |
array $permissions = array("VIEW")) | |
{ | |
$whereQueryParts = $queryBuilder->getDQLPart('where'); | |
if (empty($whereQueryParts)) { | |
$fromQueryParts = $queryBuilder->getDQLPart('from'); | |
$firstFromQueryAlias = $fromQueryParts[0]->getAlias(); | |
$queryBuilder->where($firstFromQueryAlias . '.id > 0'); // this will help in cases where no where query is specified, where query is required to walk in where clause | |
} | |
$query = $this->cloneQuery($queryBuilder->getQuery()); | |
$builder = new MaskBuilder(); | |
foreach ($permissions as $permission) { | |
$mask = constant(get_class($builder) . '::MASK_' . strtoupper($permission)); | |
$builder->add($mask); | |
} | |
$query->setHint('acl.mask', $builder->get()); | |
//Change this to the place where you saved your Aclwalker | |
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, | |
'WSL\BaseBundle\Util\Doctrine\SqlWalker\Aclwalker'); | |
$entities = $queryBuilder->getRootEntities(); | |
$query->setHint('acl.root.entities', $entities); | |
$query->setHint('acl.extra.query', | |
$this->getPermittedIdsACLSQLForUser($query, $queryBuilder)); | |
$class = $this->em->getClassMetadata($entities[0]); | |
$entityRootTableName = $class->getQuotedTableName($this->em->getConnection()->getDatabasePlatform()); | |
$entityRootAlias = $queryBuilder->getRootAlias(); | |
$query->setHint('acl.entityRootTableName', $entityRootTableName); | |
$query->setHint('acl.entityRootTableDqlAlias', $entityRootAlias); | |
return $query; | |
} | |
/** | |
* This query works well with small offset, but if want to use it with large offsets please refer to the link on how to implement | |
* http://www.scribd.com/doc/14683263/Efficient-Pagination-Using-MySQL | |
* This will only check permissions on first enity added in the from clause, it will not check permissions | |
* By default the number of rows returned are 10 starting from 0 | |
* @param Query $query | |
* @param QueryBuilder $queryBuilder | |
* @return String Sql | |
*/ | |
private function getPermittedIdsACLSQLForUser(Query $query, | |
QueryBuilder $queryBuilder) | |
{ | |
$database = $this->aclConnection->getDatabase(); | |
$mask = $query->getHint('acl.mask'); | |
$rootEntities = $query->getHint('acl.root.entities'); | |
foreach ($rootEntities as $rootEntity) { | |
$rE[] = '"' . str_replace('\\', '\\\\', $rootEntity) . '"'; | |
// For now ACL will be checked for first root entity, it will not check for all other entities in join etc.., | |
break; | |
} | |
$rootEntities = implode(',', $rE); | |
$token = $this->securityContext->getToken(); // for now lets imagine we will have token i.e user is logged in | |
$user = $token->getUser(); | |
$INString = "''"; | |
if (is_object($user)) { | |
$userRoles = $user->getRoles(); | |
foreach ($userRoles as $role) { | |
// The reason we ignore this is because by default FOSUserBundle adds ROLE_USER for every user | |
if ($role !== 'ROLE_USER'){ | |
$uR[] = '"' . $role . '"'; | |
} | |
} | |
$INString = implode(' OR s.identifier = ', (array) $uR); | |
$INString .= ' OR s.identifier = "' . str_replace('\\', '\\\\', | |
get_class($user)) . '-' . $user->getUserName() . '"'; | |
} | |
$selectQuery = <<<SELECTQUERY | |
SELECT DISTINCT o.object_identifier as id FROM {$database}.acl_object_identities as o | |
INNER JOIN {$database}.acl_classes c ON c.id = o.class_id | |
LEFT JOIN {$database}.acl_entries e ON ( | |
e.class_id = o.class_id AND (e.object_identity_id = o.id OR {$this->aclConnection->getDatabasePlatform()->getIsNullExpression('e.object_identity_id')}) | |
) | |
LEFT JOIN {$database}.acl_security_identities s ON ( | |
s.id = e.security_identity_id | |
) | |
WHERE c.class_type = {$rootEntities} | |
AND s.identifier = {$INString} | |
AND e.mask >= {$mask} | |
SELECTQUERY; | |
return $selectQuery; | |
} | |
} | |
?> |
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 WSL\BaseBundle\Util\Doctrine\SqlWalker; | |
use Doctrine\ORM\Query\SqlWalker; | |
use Doctrine\ORM\Query; | |
use Doctrine\ORM\Query\TreeWalkerAdapter; | |
use Doctrine\ORM\Query\AST\SelectStatement; | |
use Doctrine\ORM\Query\Exec\SingleSelectExecutor; | |
/* | |
* To change this template, choose Tools | Templates | |
* and open the template in the editor. | |
*/ | |
/** | |
* Description of Aclwalker | |
* | |
* @author Anil | |
*/ | |
class Aclwalker extends SqlWalker | |
{ | |
/** | |
* Walks down a FromClause AST node, thereby generating the appropriate SQL. | |
* | |
* @return string The SQL. | |
*/ | |
public function walkFromClause($fromClause) | |
{ | |
$sql = parent::walkFromClause($fromClause); | |
$tableAlias = $this->getSQLTableAlias($this->getQuery()->getHint('acl.entityRootTableName'), | |
$this->getQuery()->getHint('acl.entityRootTableDqlAlias')); | |
$extraQuery = $this->getQuery()->getHint('acl.extra.query'); | |
$tempAclView = <<<tempAclView | |
JOIN ({$extraQuery}) ta_ ON {$tableAlias}.id = ta_.id | |
tempAclView; | |
return $sql . $tempAclView; | |
} | |
} |
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 | |
$aclHelper = $this->container()->get('acl.helper'); | |
$qb = $this->_em->createQueryBuilder(); | |
//create query | |
$qb = $qb->select('p') | |
->from('Entity\Product', 'p') | |
; | |
$qb->setMaxResults(10); | |
$qb->setFirstResult(100); | |
// The query object returned here is a clone obj so, you can always use $qb->getQuery() to get the original query obj | |
$query = $aclHelper->apply($qb); | |
$result = $query->getArrayResult(); | |
return $result; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment