/** * Apply the ACL constraints to the specified query builder, using the permission definition * * @param QueryBuilder $queryBuilder The query builder * @param PermissionDefinition $permissionDef The permission definition * * @return QueryBuilder */ public function apply(QueryBuilder $queryBuilder, PermissionDefinition $permissionDef) { $aclConnection = $this->em->getConnection(); $databasePrefix = is_file($aclConnection->getDatabase()) ? '' : $aclConnection->getDatabase() . '.'; $rootEntity = $permissionDef->getEntity(); $linkAlias = $permissionDef->getAlias(); // Only tables with a single ID PK are currently supported $linkField = $this->em->getClassMetadata($rootEntity)->getSingleIdentifierColumnName(); $rootEntity = '"' . str_replace('\\', '\\\\', $rootEntity) . '"'; $query = $queryBuilder; $builder = new MaskBuilder(); foreach ($permissionDef->getPermissions() as $permission) { $mask = constant(get_class($builder) . '::MASK_' . strtoupper($permission)); $builder->add($mask); } $mask = $builder->get(); /* @var $token TokenInterface */ $token = $this->tokenStorage->getToken(); $userRoles = array(); if (!is_null($token)) { $user = $token->getUser(); $userRoles = $this->roleHierarchy->getReachableRoles($token->getRoles()); } // Security context does not provide anonymous role automatically. $uR = array('"IS_AUTHENTICATED_ANONYMOUSLY"'); /* @var $role RoleInterface */ foreach ($userRoles as $role) { // The reason we ignore this is because by default FOSUserBundle adds ROLE_USER for every user if ($role->getRole() !== 'ROLE_USER') { $uR[] = '"' . $role->getRole() . '"'; } } $uR = array_unique($uR); $inString = implode(' OR s.identifier = ', (array) $uR); if (is_object($user)) { $inString .= ' OR s.identifier = "' . str_replace('\\', '\\\\', get_class($user)) . '-' . $user->getUserName() . '"'; } $joinTableQuery = <<<SELECTQUERY SELECT DISTINCT o.object_identifier as id FROM {$databasePrefix}acl_object_identities as o INNER JOIN {$databasePrefix}acl_classes c ON c.id = o.class_id LEFT JOIN {$databasePrefix}acl_entries e ON ( e.class_id = o.class_id AND (e.object_identity_id = o.id OR {$aclConnection->getDatabasePlatform()->getIsNullExpression('e.object_identity_id')}) ) LEFT JOIN {$databasePrefix}acl_security_identities s ON ( s.id = e.security_identity_id ) WHERE c.class_type = {$rootEntity} AND (s.identifier = {$inString}) AND e.mask & {$mask} > 0 SELECTQUERY; $query->join($linkAlias, '(' . $joinTableQuery . ')', 'perms_', 'perms_.id = ' . $linkAlias . '.' . $linkField); return $query; }
/** * Returns true if $user is granted $requiredRole * @param $requiredRole * @param UserInterface $user * @return bool */ public function isGranted($requiredRole, UserInterface $user) { $requiredRole = new Role($requiredRole); foreach ($user->getRoles() as $role) { $hierarchy = $this->roleHierarchy->getReachableRoles([new Role($role)]); if (in_array($requiredRole, $hierarchy)) { return true; } } return false; }
/** * @param array $arguments * @return array */ private function getVariables(array $arguments) { $token = $this->tokenStorage->getToken(); if ($this->roleHierarchy !== null) { $roles = $this->roleHierarchy->getReachableRoles($token->getRoles()); } else { $roles = $token->getRoles(); } $variables = array('token' => $token, 'user' => $token->getUser(), 'roles' => array_map(function (Role $role) { return $role->getRole(); }, $roles), 'trust_resolver' => $this->trustResolver, 'auth_checker' => $this->authChecker, 'args' => $arguments); return $variables; }
/** * @param Connection $connection * @param UserInterface $user * * @return int[] */ private function findSidIds(Connection $connection, UserInterface $user = null) { $userSid = $this->aclIdentifier->getUserSecurityIdentity($user); $queryBuilder = $connection->createQueryBuilder(); $queryBuilder->select('acl_s.id')->from($this->aclTables['sid'], 'acl_s')->where('acl_s.username = :username_true AND acl_s.identifier = :identifier')->setParameter('identifier', $userSid->getClass() . '-' . $userSid->getUsername())->setParameter('username_true', true, \PDO::PARAM_BOOL); if (null === $user && null !== $this->tokenStorage->getToken()) { $user = $this->tokenStorage->getToken()->getUser(); } if ($user instanceof UserInterface) { $roles = $this->roleHierarchy->getReachableRoles(array_map(function ($role) { if (is_string($role)) { $role = new Role($role); } return $role; }, $user->getRoles())); $roles = array_map(function (RoleInterface $role) { return $role->getRole(); }, $roles); if (!empty($roles)) { $queryBuilder->orWhere('acl_s.username = :username_false AND acl_s.identifier IN (:roles)')->setParameter('roles', $roles, Connection::PARAM_STR_ARRAY)->setParameter('username_false', false, \PDO::PARAM_BOOL); } } return array_map(function (array $row) { return (int) $row['id']; }, $queryBuilder->execute()->fetchAll()); }
/** * @param TokenInterface $token * @return array */ protected function getUserRoles(TokenInterface $token) { $roles = array(); foreach ($this->roleHierarchy->getReachableRoles($token->getRoles()) as $role) { $roles[] = $role->getRole(); } return array_unique($roles); }
/** * @param $roleName * @param TokenInterface $token * @return bool */ protected function hasRole($roleName, TokenInterface $token) { foreach ($this->roleHierarchy->getReachableRoles($token->getRoles()) as $role) { if ($roleName === $role->getRole()) { return true; } } return false; }
/** * Evaluate the annotation * * @param GroupSecurityInterface $annotation * @param Request $request */ protected function evaluate(GroupSecurityInterface $annotation, Request $request) { $roles = array_map(function ($role) { return $role->getRole(); }, $this->roleHierarchy->getReachableRoles($this->tokenStorage->getToken()->getRoles())); if ($annotation->isStrictExpression() || !in_array($this->adminRole, $roles)) { if (!$this->language->evaluate($annotation->getExpression(), array("group" => $this->getGroup($annotation, $request), "groupSecurityChecker" => $this->groupSecurityChecker))) { throw new AccessDeniedException("GroupSecurity's expression (\"" . $annotation->expression . "\") denied access."); } } }
/** * {@inheritdoc} */ public function getSecurityIdentities(Token\TokenInterface $token) { $sids = array(); // add user security identity if (!$token instanceof Token\AnonymousToken) { try { $sids[] = UserSecurityIdentity::fromToken($token); } catch (\InvalidArgumentException $invalid) { // ignore, user has no user security identity } } // add all reachable roles foreach ($this->roleHierarchy->getReachableRoles($token->getRoles()) as $role) { $sids[] = new RoleSecurityIdentity($role); } // add journal roles $user = $token->getUser(); try { $selectedJournal = $this->journalService->getSelectedJournal(); } catch (\Exception $e) { $selectedJournal = false; } if ($user instanceof User && $selectedJournal instanceof Journal) { foreach ($user->getJournalRoles($selectedJournal) as $journalRoles) { $sids[] = new JournalRoleSecurityIdentity($journalRoles[0], $journalRoles[1]); } } // add built-in special roles if ($this->authenticationTrustResolver->isFullFledged($token)) { $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_FULLY); $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED); $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY); } elseif ($this->authenticationTrustResolver->isRememberMe($token)) { $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED); $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY); } elseif ($this->authenticationTrustResolver->isAnonymous($token)) { $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_ANONYMOUSLY); } return $sids; }
/** * @covers Kunstmaan\AdminBundle\Helper\Security\Acl\AclNativeHelper::apply */ public function testApplyAnonymous() { $queryBuilder = new QueryBuilder($this->conn); $queryBuilder->add('from', array(array('table' => 'myTable', 'alias' => 'n'))); $roles = array(); $this->token->expects($this->once())->method('getRoles')->will($this->returnValue($roles)); $this->rh->expects($this->once())->method('getReachableRoles')->with($roles)->will($this->returnValue($roles)); $this->token->expects($this->any())->method('getUser')->will($this->returnValue('anon.')); $permissionDef = new PermissionDefinition(array('view'), 'Kunstmaan\\NodeBundle\\Entity\\Node', 'n'); /* @var $qb QueryBuilder */ $qb = $this->object->apply($queryBuilder, $permissionDef); $query = $qb->getSQL(); $this->assertContains('"IS_AUTHENTICATED_ANONYMOUSLY"', $query); }
protected function extractRoles(TokenInterface $token) { // FIXME: this is the definition of a hack if (property_exists($token, 'reachableRoles') === true) { return $token->reachableRoles; } if ($token->getUsername() === 'anon.') { $roles = [new Role('ROLE_PUBLIC')]; } else { $roles = $token->getRoles(); } $token->reachableRoles = $this->roleHierarchy->getReachableRoles($roles); return $token->reachableRoles; }
/** * 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 the first entity 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 * * @return string */ private function getPermittedAclIdsSQLForUser(Query $query) { $aclConnection = $this->em->getConnection(); $databasePrefix = is_file($aclConnection->getDatabase()) ? '' : $aclConnection->getDatabase() . '.'; $mask = $query->getHint('acl.mask'); $rootEntity = '"' . str_replace('\\', '\\\\', $query->getHint('acl.root.entity')) . '"'; /* @var $token TokenInterface */ $token = $this->securityContext->getToken(); $userRoles = array(); $user = null; if (!is_null($token)) { $user = $token->getUser(); $userRoles = $this->roleHierarchy->getReachableRoles($token->getRoles()); } // Security context does not provide anonymous role automatically. $uR = array('"IS_AUTHENTICATED_ANONYMOUSLY"'); /* @var $role RoleInterface */ foreach ($userRoles as $role) { // The reason we ignore this is because by default FOSUserBundle adds ROLE_USER for every user if ($role->getRole() !== 'ROLE_USER') { $uR[] = '"' . $role->getRole() . '"'; } } $uR = array_unique($uR); $inString = implode(' OR s.identifier = ', $uR); if (is_object($user)) { $inString .= ' OR s.identifier = "' . str_replace('\\', '\\\\', get_class($user)) . '-' . $user->getUserName() . '"'; } $selectQuery = <<<SELECTQUERY SELECT DISTINCT o.object_identifier as id FROM {$databasePrefix}acl_object_identities as o INNER JOIN {$databasePrefix}acl_classes c ON c.id = o.class_id LEFT JOIN {$databasePrefix}acl_entries e ON ( e.class_id = o.class_id AND (e.object_identity_id = o.id OR {$aclConnection->getDatabasePlatform()->getIsNullExpression('e.object_identity_id')}) ) LEFT JOIN {$databasePrefix}acl_security_identities s ON ( s.id = e.security_identity_id ) WHERE c.class_type = {$rootEntity} AND (s.identifier = {$inString}) AND e.mask & {$mask} > 0 SELECTQUERY; return $selectQuery; }
/** * @covers Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper::getAllowedEntityIds * @covers Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper::getPermittedAclIdsSQLForUser */ public function testGetAllowedEntityIds() { $roles = array(new Role('ROLE_KING')); $allRoles = array($roles[0], new Role('ROLE_SUBJECT')); $this->token->expects($this->once())->method('getRoles')->will($this->returnValue($roles)); $this->rh->expects($this->once())->method('getReachableRoles')->with($roles)->will($this->returnValue($allRoles)); $user = $this->getMockBuilder('FOS\\UserBundle\\Model\\UserInterface')->getMock(); $user->expects($this->any())->method('getUsername')->will($this->returnValue('MyUser')); $this->token->expects($this->any())->method('getUser')->will($this->returnValue($user)); $hydrator = $this->getMockBuilder('Doctrine\\ORM\\Internal\\Hydration\\ScalarHydrator')->disableOriginalConstructor()->getMock(); $rows = array(array('id' => 1), array('id' => 9)); $hydrator->expects($this->once())->method('hydrateAll')->will($this->returnValue($rows)); $this->em->expects($this->any())->method('newHydrator')->will($this->returnValue($hydrator)); /* @var $query NativeQuery */ $query = new NativeQuery($this->em); $this->em->expects($this->once())->method('createNativeQuery')->will($this->returnValue($query)); $permissionDef = new PermissionDefinition(array('view'), 'Kunstmaan\\NodeBundle\\Entity\\Node', 'n'); /* @var $result array */ $result = $this->object->getAllowedEntityIds($permissionDef); $this->assertEquals(array(1, 9), $result); }