diff options
166 files changed, 3109 insertions, 9670 deletions
diff --git a/Acl/Dbal/AclProvider.php b/Acl/Dbal/AclProvider.php deleted file mode 100644 index 6709023..0000000 --- a/Acl/Dbal/AclProvider.php +++ /dev/null @@ -1,695 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Dbal; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\Statement; -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Domain\Acl; -use Symfony\Component\Security\Acl\Domain\Entry; -use Symfony\Component\Security\Acl\Domain\FieldEntry; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Exception\AclNotFoundException; -use Symfony\Component\Security\Acl\Exception\NotAllAclsFoundException; -use Symfony\Component\Security\Acl\Model\AclCacheInterface; -use Symfony\Component\Security\Acl\Model\AclProviderInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; -use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface; - -/** - * An ACL provider implementation. - * - * This provider assumes that all ACLs share the same PermissionGrantingStrategy. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class AclProvider implements AclProviderInterface -{ - const MAX_BATCH_SIZE = 30; - - /** - * @var AclCacheInterface|null - */ - protected $cache; - - /** - * @var Connection - */ - protected $connection; - protected $loadedAces = array(); - protected $loadedAcls = array(); - protected $options; - - /** - * @var PermissionGrantingStrategyInterface - */ - private $permissionGrantingStrategy; - - /** - * Constructor. - * - * @param Connection $connection - * @param PermissionGrantingStrategyInterface $permissionGrantingStrategy - * @param array $options - * @param AclCacheInterface $cache - */ - public function __construct(Connection $connection, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $options, AclCacheInterface $cache = null) - { - $this->cache = $cache; - $this->connection = $connection; - $this->options = $options; - $this->permissionGrantingStrategy = $permissionGrantingStrategy; - } - - /** - * {@inheritdoc} - */ - public function findChildren(ObjectIdentityInterface $parentOid, $directChildrenOnly = false) - { - $sql = $this->getFindChildrenSql($parentOid, $directChildrenOnly); - - $children = array(); - foreach ($this->connection->executeQuery($sql)->fetchAll() as $data) { - $children[] = new ObjectIdentity($data['object_identifier'], $data['class_type']); - } - - return $children; - } - - /** - * {@inheritdoc} - */ - public function findAcl(ObjectIdentityInterface $oid, array $sids = array()) - { - return $this->findAcls(array($oid), $sids)->offsetGet($oid); - } - - /** - * {@inheritdoc} - */ - public function findAcls(array $oids, array $sids = array()) - { - $result = new \SplObjectStorage(); - $currentBatch = array(); - $oidLookup = array(); - - for ($i = 0, $c = count($oids); $i < $c; ++$i) { - $oid = $oids[$i]; - $oidLookupKey = $oid->getIdentifier().$oid->getType(); - $oidLookup[$oidLookupKey] = $oid; - $aclFound = false; - - // check if result already contains an ACL - if ($result->contains($oid)) { - $aclFound = true; - } - - // check if this ACL has already been hydrated - if (!$aclFound && isset($this->loadedAcls[$oid->getType()][$oid->getIdentifier()])) { - $acl = $this->loadedAcls[$oid->getType()][$oid->getIdentifier()]; - - if (!$acl->isSidLoaded($sids)) { - // FIXME: we need to load ACEs for the missing SIDs. This is never - // reached by the default implementation, since we do not - // filter by SID - throw new \RuntimeException('This is not supported by the default implementation.'); - } else { - $result->attach($oid, $acl); - $aclFound = true; - } - } - - // check if we can locate the ACL in the cache - if (!$aclFound && null !== $this->cache) { - $acl = $this->cache->getFromCacheByIdentity($oid); - - if (null !== $acl) { - if ($acl->isSidLoaded($sids)) { - // check if any of the parents has been loaded since we need to - // ensure that there is only ever one ACL per object identity - $parentAcl = $acl->getParentAcl(); - while (null !== $parentAcl) { - $parentOid = $parentAcl->getObjectIdentity(); - - if (isset($this->loadedAcls[$parentOid->getType()][$parentOid->getIdentifier()])) { - $acl->setParentAcl($this->loadedAcls[$parentOid->getType()][$parentOid->getIdentifier()]); - break; - } else { - $this->loadedAcls[$parentOid->getType()][$parentOid->getIdentifier()] = $parentAcl; - $this->updateAceIdentityMap($parentAcl); - } - - $parentAcl = $parentAcl->getParentAcl(); - } - - $this->loadedAcls[$oid->getType()][$oid->getIdentifier()] = $acl; - $this->updateAceIdentityMap($acl); - $result->attach($oid, $acl); - $aclFound = true; - } else { - $this->cache->evictFromCacheByIdentity($oid); - - foreach ($this->findChildren($oid) as $childOid) { - $this->cache->evictFromCacheByIdentity($childOid); - } - } - } - } - - // looks like we have to load the ACL from the database - if (!$aclFound) { - $currentBatch[] = $oid; - } - - // Is it time to load the current batch? - $currentBatchesCount = count($currentBatch); - if ($currentBatchesCount > 0 && (self::MAX_BATCH_SIZE === $currentBatchesCount || ($i + 1) === $c)) { - try { - $loadedBatch = $this->lookupObjectIdentities($currentBatch, $sids, $oidLookup); - } catch (AclNotFoundException $e) { - if ($result->count()) { - $partialResultException = new NotAllAclsFoundException('The provider could not find ACLs for all object identities.'); - $partialResultException->setPartialResult($result); - throw $partialResultException; - } else { - throw $e; - } - } - foreach ($loadedBatch as $loadedOid) { - $loadedAcl = $loadedBatch->offsetGet($loadedOid); - - if (null !== $this->cache) { - $this->cache->putInCache($loadedAcl); - } - - if (isset($oidLookup[$loadedOid->getIdentifier().$loadedOid->getType()])) { - $result->attach($loadedOid, $loadedAcl); - } - } - - $currentBatch = array(); - } - } - - // check that we got ACLs for all the identities - foreach ($oids as $oid) { - if (!$result->contains($oid)) { - if (1 === count($oids)) { - $objectName = method_exists($oid, '__toString') ? $oid : get_class($oid); - throw new AclNotFoundException(sprintf('No ACL found for %s.', $objectName)); - } - - $partialResultException = new NotAllAclsFoundException('The provider could not find ACLs for all object identities.'); - $partialResultException->setPartialResult($result); - - throw $partialResultException; - } - } - - return $result; - } - - /** - * Constructs the query used for looking up object identities and associated - * ACEs, and security identities. - * - * @param array $ancestorIds - * - * @return string - */ - protected function getLookupSql(array $ancestorIds) - { - // FIXME: add support for filtering by sids (right now we select all sids) - - $sql = <<<SELECTCLAUSE - SELECT - o.id as acl_id, - o.object_identifier, - o.parent_object_identity_id, - o.entries_inheriting, - c.class_type, - e.id as ace_id, - e.object_identity_id, - e.field_name, - e.ace_order, - e.mask, - e.granting, - e.granting_strategy, - e.audit_success, - e.audit_failure, - s.username, - s.identifier as security_identifier - FROM - {$this->options['oid_table_name']} o - INNER JOIN {$this->options['class_table_name']} c ON c.id = o.class_id - LEFT JOIN {$this->options['entry_table_name']} e ON ( - e.class_id = o.class_id AND (e.object_identity_id = o.id OR {$this->connection->getDatabasePlatform()->getIsNullExpression('e.object_identity_id')}) - ) - LEFT JOIN {$this->options['sid_table_name']} s ON ( - s.id = e.security_identity_id - ) - - WHERE (o.id = -SELECTCLAUSE; - - $sql .= implode(' OR o.id = ', $ancestorIds).')'; - - return $sql; - } - - protected function getAncestorLookupSql(array $batch) - { - $sql = <<<SELECTCLAUSE - SELECT a.ancestor_id - FROM - {$this->options['oid_table_name']} o - INNER JOIN {$this->options['class_table_name']} c ON c.id = o.class_id - INNER JOIN {$this->options['oid_ancestors_table_name']} a ON a.object_identity_id = o.id - WHERE ( -SELECTCLAUSE; - - $types = array(); - $count = count($batch); - for ($i = 0; $i < $count; ++$i) { - if (!isset($types[$batch[$i]->getType()])) { - $types[$batch[$i]->getType()] = true; - - // if there is more than one type we can safely break out of the - // loop, because it is the differentiator factor on whether to - // query for only one or more class types - if (count($types) > 1) { - break; - } - } - } - - if (1 === count($types)) { - $ids = array(); - for ($i = 0; $i < $count; ++$i) { - $identifier = (string) $batch[$i]->getIdentifier(); - $ids[] = $this->connection->quote($identifier); - } - - $sql .= sprintf( - '(o.object_identifier IN (%s) AND c.class_type = %s)', - implode(',', $ids), - $this->connection->quote($batch[0]->getType()) - ); - } else { - $where = '(o.object_identifier = %s AND c.class_type = %s)'; - for ($i = 0; $i < $count; ++$i) { - $sql .= sprintf( - $where, - $this->connection->quote($batch[$i]->getIdentifier()), - $this->connection->quote($batch[$i]->getType()) - ); - - if ($i + 1 < $count) { - $sql .= ' OR '; - } - } - } - - $sql .= ')'; - - return $sql; - } - - /** - * Constructs the SQL for retrieving child object identities for the given - * object identities. - * - * @param ObjectIdentityInterface $oid - * @param bool $directChildrenOnly - * - * @return string - */ - protected function getFindChildrenSql(ObjectIdentityInterface $oid, $directChildrenOnly) - { - if (false === $directChildrenOnly) { - $query = <<<FINDCHILDREN - SELECT o.object_identifier, c.class_type - FROM - {$this->options['oid_table_name']} o - INNER JOIN {$this->options['class_table_name']} c ON c.id = o.class_id - INNER JOIN {$this->options['oid_ancestors_table_name']} a ON a.object_identity_id = o.id - WHERE - a.ancestor_id = %d AND a.object_identity_id != a.ancestor_id -FINDCHILDREN; - } else { - $query = <<<FINDCHILDREN - SELECT o.object_identifier, c.class_type - FROM {$this->options['oid_table_name']} o - INNER JOIN {$this->options['class_table_name']} c ON c.id = o.class_id - WHERE o.parent_object_identity_id = %d -FINDCHILDREN; - } - - return sprintf($query, $this->retrieveObjectIdentityPrimaryKey($oid)); - } - - /** - * Constructs the SQL for retrieving the primary key of the given object - * identity. - * - * @param ObjectIdentityInterface $oid - * - * @return string - */ - protected function getSelectObjectIdentityIdSql(ObjectIdentityInterface $oid) - { - $query = <<<'QUERY' - SELECT o.id - FROM %s o - INNER JOIN %s c ON c.id = o.class_id - WHERE o.object_identifier = %s AND c.class_type = %s -QUERY; - - return sprintf( - $query, - $this->options['oid_table_name'], - $this->options['class_table_name'], - $this->connection->quote((string) $oid->getIdentifier()), - $this->connection->quote((string) $oid->getType()) - ); - } - - /** - * Returns the primary key of the passed object identity. - * - * @param ObjectIdentityInterface $oid - * - * @return int - */ - final protected function retrieveObjectIdentityPrimaryKey(ObjectIdentityInterface $oid) - { - return $this->connection->executeQuery($this->getSelectObjectIdentityIdSql($oid))->fetchColumn(); - } - - /** - * This method is called when an ACL instance is retrieved from the cache. - * - * @param AclInterface $acl - */ - private function updateAceIdentityMap(AclInterface $acl) - { - foreach (array('classAces', 'classFieldAces', 'objectAces', 'objectFieldAces') as $property) { - $reflection = new \ReflectionProperty($acl, $property); - $reflection->setAccessible(true); - $value = $reflection->getValue($acl); - - if ('classAces' === $property || 'objectAces' === $property) { - $this->doUpdateAceIdentityMap($value); - } else { - foreach ($value as $field => $aces) { - $this->doUpdateAceIdentityMap($value[$field]); - } - } - - $reflection->setValue($acl, $value); - $reflection->setAccessible(false); - } - } - - /** - * Retrieves all the ids which need to be queried from the database - * including the ids of parent ACLs. - * - * @param array $batch - * - * @return array - */ - private function getAncestorIds(array $batch) - { - $sql = $this->getAncestorLookupSql($batch); - - $ancestorIds = array(); - foreach ($this->connection->executeQuery($sql)->fetchAll() as $data) { - // FIXME: skip ancestors which are cached - // Fix: Oracle returns keys in uppercase - $ancestorIds[] = reset($data); - } - - return $ancestorIds; - } - - /** - * Does either overwrite the passed ACE, or saves it in the global identity - * map to ensure every ACE only gets instantiated once. - * - * @param array &$aces - */ - private function doUpdateAceIdentityMap(array &$aces) - { - foreach ($aces as $index => $ace) { - if (isset($this->loadedAces[$ace->getId()])) { - $aces[$index] = $this->loadedAces[$ace->getId()]; - } else { - $this->loadedAces[$ace->getId()] = $ace; - } - } - } - - /** - * This method is called for object identities which could not be retrieved - * from the cache, and for which thus a database query is required. - * - * @param array $batch - * @param array $sids - * @param array $oidLookup - * - * @return \SplObjectStorage mapping object identities to ACL instances - * - * @throws AclNotFoundException - */ - private function lookupObjectIdentities(array $batch, array $sids, array $oidLookup) - { - $ancestorIds = $this->getAncestorIds($batch); - if (!$ancestorIds) { - throw new AclNotFoundException('There is no ACL for the given object identity.'); - } - - $sql = $this->getLookupSql($ancestorIds); - $stmt = $this->connection->executeQuery($sql); - - return $this->hydrateObjectIdentities($stmt, $oidLookup, $sids); - } - - /** - * This method is called to hydrate ACLs and ACEs. - * - * This method was designed for performance; thus, a lot of code has been - * inlined at the cost of readability, and maintainability. - * - * Keep in mind that changes to this method might severely reduce the - * performance of the entire ACL system. - * - * @param Statement $stmt - * @param array $oidLookup - * @param array $sids - * - * @return \SplObjectStorage - * - * @throws \RuntimeException - */ - private function hydrateObjectIdentities(Statement $stmt, array $oidLookup, array $sids) - { - $parentIdToFill = new \SplObjectStorage(); - $acls = $aces = $emptyArray = array(); - $oidCache = $oidLookup; - $result = new \SplObjectStorage(); - $loadedAces = &$this->loadedAces; - $loadedAcls = &$this->loadedAcls; - $permissionGrantingStrategy = $this->permissionGrantingStrategy; - - // we need these to set protected properties on hydrated objects - $aclReflection = new \ReflectionClass('Symfony\Component\Security\Acl\Domain\Acl'); - $aclClassAcesProperty = $aclReflection->getProperty('classAces'); - $aclClassAcesProperty->setAccessible(true); - $aclClassFieldAcesProperty = $aclReflection->getProperty('classFieldAces'); - $aclClassFieldAcesProperty->setAccessible(true); - $aclObjectAcesProperty = $aclReflection->getProperty('objectAces'); - $aclObjectAcesProperty->setAccessible(true); - $aclObjectFieldAcesProperty = $aclReflection->getProperty('objectFieldAces'); - $aclObjectFieldAcesProperty->setAccessible(true); - $aclParentAclProperty = $aclReflection->getProperty('parentAcl'); - $aclParentAclProperty->setAccessible(true); - - // fetchAll() consumes more memory than consecutive calls to fetch(), - // but it is faster - foreach ($stmt->fetchAll(\PDO::FETCH_NUM) as $data) { - list($aclId, - $objectIdentifier, - $parentObjectIdentityId, - $entriesInheriting, - $classType, - $aceId, - $objectIdentityId, - $fieldName, - $aceOrder, - $mask, - $granting, - $grantingStrategy, - $auditSuccess, - $auditFailure, - $username, - $securityIdentifier) = array_values($data); - - // has the ACL been hydrated during this hydration cycle? - if (isset($acls[$aclId])) { - $acl = $acls[$aclId]; - // has the ACL been hydrated during any previous cycle, or was possibly loaded - // from cache? - } elseif (isset($loadedAcls[$classType][$objectIdentifier])) { - $acl = $loadedAcls[$classType][$objectIdentifier]; - - // keep reference in local array (saves us some hash calculations) - $acls[$aclId] = $acl; - - // attach ACL to the result set; even though we do not enforce that every - // object identity has only one instance, we must make sure to maintain - // referential equality with the oids passed to findAcls() - $oidCacheKey = $objectIdentifier.$classType; - if (!isset($oidCache[$oidCacheKey])) { - $oidCache[$oidCacheKey] = $acl->getObjectIdentity(); - } - $result->attach($oidCache[$oidCacheKey], $acl); - // so, this hasn't been hydrated yet - } else { - // create object identity if we haven't done so yet - $oidLookupKey = $objectIdentifier.$classType; - if (!isset($oidCache[$oidLookupKey])) { - $oidCache[$oidLookupKey] = new ObjectIdentity($objectIdentifier, $classType); - } - - $acl = new Acl((int) $aclId, $oidCache[$oidLookupKey], $permissionGrantingStrategy, $emptyArray, (bool) $entriesInheriting); - - // keep a local, and global reference to this ACL - $loadedAcls[$classType][$objectIdentifier] = $acl; - $acls[$aclId] = $acl; - - // try to fill in parent ACL, or defer until all ACLs have been hydrated - if (null !== $parentObjectIdentityId) { - if (isset($acls[$parentObjectIdentityId])) { - $aclParentAclProperty->setValue($acl, $acls[$parentObjectIdentityId]); - } else { - $parentIdToFill->attach($acl, $parentObjectIdentityId); - } - } - - $result->attach($oidCache[$oidLookupKey], $acl); - } - - // check if this row contains an ACE record - if (null !== $aceId) { - // have we already hydrated ACEs for this ACL? - if (!isset($aces[$aclId])) { - $aces[$aclId] = array($emptyArray, $emptyArray, $emptyArray, $emptyArray); - } - - // has this ACE already been hydrated during a previous cycle, or - // possible been loaded from cache? - // It is important to only ever have one ACE instance per actual row since - // some ACEs are shared between ACL instances - if (!isset($loadedAces[$aceId])) { - if (!isset($sids[$key = ($username ? '1' : '0').$securityIdentifier])) { - if ($username) { - $sids[$key] = new UserSecurityIdentity( - substr($securityIdentifier, 1 + $pos = strpos($securityIdentifier, '-')), - substr($securityIdentifier, 0, $pos) - ); - } else { - $sids[$key] = new RoleSecurityIdentity($securityIdentifier); - } - } - - if (null === $fieldName) { - $loadedAces[$aceId] = new Entry((int) $aceId, $acl, $sids[$key], $grantingStrategy, (int) $mask, (bool) $granting, (bool) $auditFailure, (bool) $auditSuccess); - } else { - $loadedAces[$aceId] = new FieldEntry((int) $aceId, $acl, $fieldName, $sids[$key], $grantingStrategy, (int) $mask, (bool) $granting, (bool) $auditFailure, (bool) $auditSuccess); - } - } - $ace = $loadedAces[$aceId]; - - // assign ACE to the correct property - if (null === $objectIdentityId) { - if (null === $fieldName) { - $aces[$aclId][0][$aceOrder] = $ace; - } else { - $aces[$aclId][1][$fieldName][$aceOrder] = $ace; - } - } else { - if (null === $fieldName) { - $aces[$aclId][2][$aceOrder] = $ace; - } else { - $aces[$aclId][3][$fieldName][$aceOrder] = $ace; - } - } - } - } - - // We do not sort on database level since we only want certain subsets to be sorted, - // and we are going to read the entire result set anyway. - // Sorting on DB level increases query time by an order of magnitude while it is - // almost negligible when we use PHPs array sort functions. - foreach ($aces as $aclId => $aceData) { - $acl = $acls[$aclId]; - - ksort($aceData[0]); - $aclClassAcesProperty->setValue($acl, $aceData[0]); - - foreach (array_keys($aceData[1]) as $fieldName) { - ksort($aceData[1][$fieldName]); - } - $aclClassFieldAcesProperty->setValue($acl, $aceData[1]); - - ksort($aceData[2]); - $aclObjectAcesProperty->setValue($acl, $aceData[2]); - - foreach (array_keys($aceData[3]) as $fieldName) { - ksort($aceData[3][$fieldName]); - } - $aclObjectFieldAcesProperty->setValue($acl, $aceData[3]); - } - - // fill-in parent ACLs where this hasn't been done yet cause the parent ACL was not - // yet available - $processed = 0; - foreach ($parentIdToFill as $acl) { - $parentId = $parentIdToFill->offsetGet($acl); - - // let's see if we have already hydrated this - if (isset($acls[$parentId])) { - $aclParentAclProperty->setValue($acl, $acls[$parentId]); - ++$processed; - - continue; - } - } - - // reset reflection changes - $aclClassAcesProperty->setAccessible(false); - $aclClassFieldAcesProperty->setAccessible(false); - $aclObjectAcesProperty->setAccessible(false); - $aclObjectFieldAcesProperty->setAccessible(false); - $aclParentAclProperty->setAccessible(false); - - // this should never be true if the database integrity hasn't been compromised - if ($processed < count($parentIdToFill)) { - throw new \RuntimeException('Not all parent ids were populated. This implies an integrity problem.'); - } - - return $result; - } -} diff --git a/Acl/Dbal/MutableAclProvider.php b/Acl/Dbal/MutableAclProvider.php deleted file mode 100644 index bd1976f..0000000 --- a/Acl/Dbal/MutableAclProvider.php +++ /dev/null @@ -1,1034 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Dbal; - -use Doctrine\Common\PropertyChangedListener; -use Doctrine\DBAL\Connection; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException; -use Symfony\Component\Security\Acl\Exception\ConcurrentModificationException; -use Symfony\Component\Security\Acl\Model\AclCacheInterface; -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Model\EntryInterface; -use Symfony\Component\Security\Acl\Model\MutableAclInterface; -use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; -use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; - -/** - * An implementation of the MutableAclProviderInterface using Doctrine DBAL. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class MutableAclProvider extends AclProvider implements MutableAclProviderInterface, PropertyChangedListener -{ - private $propertyChanges; - - /** - * {@inheritdoc} - */ - public function __construct(Connection $connection, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $options, AclCacheInterface $cache = null) - { - parent::__construct($connection, $permissionGrantingStrategy, $options, $cache); - - $this->propertyChanges = new \SplObjectStorage(); - } - - /** - * {@inheritdoc} - */ - public function createAcl(ObjectIdentityInterface $oid) - { - if (false !== $this->retrieveObjectIdentityPrimaryKey($oid)) { - $objectName = method_exists($oid, '__toString') ? $oid : get_class($oid); - throw new AclAlreadyExistsException(sprintf('%s is already associated with an ACL.', $objectName)); - } - - $this->connection->beginTransaction(); - try { - $this->createObjectIdentity($oid); - - $pk = $this->retrieveObjectIdentityPrimaryKey($oid); - $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $pk)); - - $this->connection->commit(); - } catch (\Exception $e) { - $this->connection->rollBack(); - - throw $e; - } - - // re-read the ACL from the database to ensure proper caching, etc. - return $this->findAcl($oid); - } - - /** - * {@inheritdoc} - */ - public function deleteAcl(ObjectIdentityInterface $oid) - { - $this->connection->beginTransaction(); - try { - foreach ($this->findChildren($oid, true) as $childOid) { - $this->deleteAcl($childOid); - } - - $oidPK = $this->retrieveObjectIdentityPrimaryKey($oid); - - $this->deleteAccessControlEntries($oidPK); - $this->deleteObjectIdentityRelations($oidPK); - $this->deleteObjectIdentity($oidPK); - - $this->connection->commit(); - } catch (\Exception $e) { - $this->connection->rollBack(); - - throw $e; - } - - // evict the ACL from the in-memory identity map - if (isset($this->loadedAcls[$oid->getType()][$oid->getIdentifier()])) { - $this->propertyChanges->offsetUnset($this->loadedAcls[$oid->getType()][$oid->getIdentifier()]); - unset($this->loadedAcls[$oid->getType()][$oid->getIdentifier()]); - } - - // evict the ACL from any caches - if (null !== $this->cache) { - $this->cache->evictFromCacheByIdentity($oid); - } - } - - /** - * Deletes the security identity from the database. - * ACL entries have the CASCADE option on their foreign key so they will also get deleted. - * - * @param SecurityIdentityInterface $sid - * - * @throws \InvalidArgumentException - */ - public function deleteSecurityIdentity(SecurityIdentityInterface $sid) - { - $this->connection->executeQuery($this->getDeleteSecurityIdentityIdSql($sid)); - } - - /** - * {@inheritdoc} - */ - public function findAcls(array $oids, array $sids = array()) - { - $result = parent::findAcls($oids, $sids); - - foreach ($result as $oid) { - $acl = $result->offsetGet($oid); - - if (false === $this->propertyChanges->contains($acl) && $acl instanceof MutableAclInterface) { - $acl->addPropertyChangedListener($this); - $this->propertyChanges->attach($acl, array()); - } - - $parentAcl = $acl->getParentAcl(); - while (null !== $parentAcl) { - if (false === $this->propertyChanges->contains($parentAcl) && $acl instanceof MutableAclInterface) { - $parentAcl->addPropertyChangedListener($this); - $this->propertyChanges->attach($parentAcl, array()); - } - - $parentAcl = $parentAcl->getParentAcl(); - } - } - - return $result; - } - - /** - * Implementation of PropertyChangedListener. - * - * This allows us to keep track of which values have been changed, so we don't - * have to do a full introspection when ->updateAcl() is called. - * - * @param mixed $sender - * @param string $propertyName - * @param mixed $oldValue - * @param mixed $newValue - * - * @throws \InvalidArgumentException - */ - public function propertyChanged($sender, $propertyName, $oldValue, $newValue) - { - if (!$sender instanceof MutableAclInterface && !$sender instanceof EntryInterface) { - throw new \InvalidArgumentException('$sender must be an instance of MutableAclInterface, or EntryInterface.'); - } - - if ($sender instanceof EntryInterface) { - if (null === $sender->getId()) { - return; - } - - $ace = $sender; - $sender = $ace->getAcl(); - } else { - $ace = null; - } - - if (false === $this->propertyChanges->contains($sender)) { - throw new \InvalidArgumentException('$sender is not being tracked by this provider.'); - } - - $propertyChanges = $this->propertyChanges->offsetGet($sender); - if (null === $ace) { - if (isset($propertyChanges[$propertyName])) { - $oldValue = $propertyChanges[$propertyName][0]; - if ($oldValue === $newValue) { - unset($propertyChanges[$propertyName]); - } else { - $propertyChanges[$propertyName] = array($oldValue, $newValue); - } - } else { - $propertyChanges[$propertyName] = array($oldValue, $newValue); - } - } else { - if (!isset($propertyChanges['aces'])) { - $propertyChanges['aces'] = new \SplObjectStorage(); - } - - $acePropertyChanges = $propertyChanges['aces']->contains($ace) ? $propertyChanges['aces']->offsetGet($ace) : array(); - - if (isset($acePropertyChanges[$propertyName])) { - $oldValue = $acePropertyChanges[$propertyName][0]; - if ($oldValue === $newValue) { - unset($acePropertyChanges[$propertyName]); - } else { - $acePropertyChanges[$propertyName] = array($oldValue, $newValue); - } - } else { - $acePropertyChanges[$propertyName] = array($oldValue, $newValue); - } - - if (count($acePropertyChanges) > 0) { - $propertyChanges['aces']->offsetSet($ace, $acePropertyChanges); - } else { - $propertyChanges['aces']->offsetUnset($ace); - - if (0 === count($propertyChanges['aces'])) { - unset($propertyChanges['aces']); - } - } - } - - $this->propertyChanges->offsetSet($sender, $propertyChanges); - } - - /** - * {@inheritdoc} - */ - public function updateAcl(MutableAclInterface $acl) - { - if (!$this->propertyChanges->contains($acl)) { - throw new \InvalidArgumentException('$acl is not tracked by this provider.'); - } - - $propertyChanges = $this->propertyChanges->offsetGet($acl); - // check if any changes were made to this ACL - if (0 === count($propertyChanges)) { - return; - } - - $sets = $sharedPropertyChanges = array(); - - $this->connection->beginTransaction(); - try { - if (isset($propertyChanges['entriesInheriting'])) { - $sets[] = 'entries_inheriting = '.$this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['entriesInheriting'][1]); - } - - if (isset($propertyChanges['parentAcl'])) { - if (null === $propertyChanges['parentAcl'][1]) { - $sets[] = 'parent_object_identity_id = NULL'; - } else { - $sets[] = 'parent_object_identity_id = '.(int) $propertyChanges['parentAcl'][1]->getId(); - } - - $this->regenerateAncestorRelations($acl); - $childAcls = $this->findAcls($this->findChildren($acl->getObjectIdentity(), false)); - foreach ($childAcls as $childOid) { - $this->regenerateAncestorRelations($childAcls[$childOid]); - } - } - - // check properties for deleted, and created ACEs, and perform deletions - // we need to perform deletions before updating existing ACEs, in order to - // preserve uniqueness of the order field - if (isset($propertyChanges['classAces'])) { - $this->updateOldAceProperty('classAces', $propertyChanges['classAces']); - } - if (isset($propertyChanges['classFieldAces'])) { - $this->updateOldFieldAceProperty('classFieldAces', $propertyChanges['classFieldAces']); - } - if (isset($propertyChanges['objectAces'])) { - $this->updateOldAceProperty('objectAces', $propertyChanges['objectAces']); - } - if (isset($propertyChanges['objectFieldAces'])) { - $this->updateOldFieldAceProperty('objectFieldAces', $propertyChanges['objectFieldAces']); - } - - // this includes only updates of existing ACEs, but neither the creation, nor - // the deletion of ACEs; these are tracked by changes to the ACL's respective - // properties (classAces, classFieldAces, objectAces, objectFieldAces) - if (isset($propertyChanges['aces'])) { - $this->updateAces($propertyChanges['aces']); - } - - // check properties for deleted, and created ACEs, and perform creations - if (isset($propertyChanges['classAces'])) { - $this->updateNewAceProperty('classAces', $propertyChanges['classAces']); - $sharedPropertyChanges['classAces'] = $propertyChanges['classAces']; - } - if (isset($propertyChanges['classFieldAces'])) { - $this->updateNewFieldAceProperty('classFieldAces', $propertyChanges['classFieldAces']); - $sharedPropertyChanges['classFieldAces'] = $propertyChanges['classFieldAces']; - } - if (isset($propertyChanges['objectAces'])) { - $this->updateNewAceProperty('objectAces', $propertyChanges['objectAces']); - } - if (isset($propertyChanges['objectFieldAces'])) { - $this->updateNewFieldAceProperty('objectFieldAces', $propertyChanges['objectFieldAces']); - } - - // if there have been changes to shared properties, we need to synchronize other - // ACL instances for object identities of the same type that are already in-memory - if (count($sharedPropertyChanges) > 0) { - $classAcesProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Acl', 'classAces'); - $classAcesProperty->setAccessible(true); - $classFieldAcesProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Acl', 'classFieldAces'); - $classFieldAcesProperty->setAccessible(true); - - foreach ($this->loadedAcls[$acl->getObjectIdentity()->getType()] as $sameTypeAcl) { - if (isset($sharedPropertyChanges['classAces'])) { - if ($acl !== $sameTypeAcl && $classAcesProperty->getValue($sameTypeAcl) !== $sharedPropertyChanges['classAces'][0]) { - throw new ConcurrentModificationException('The "classAces" property has been modified concurrently.'); - } - - $classAcesProperty->setValue($sameTypeAcl, $sharedPropertyChanges['classAces'][1]); - } - - if (isset($sharedPropertyChanges['classFieldAces'])) { - if ($acl !== $sameTypeAcl && $classFieldAcesProperty->getValue($sameTypeAcl) !== $sharedPropertyChanges['classFieldAces'][0]) { - throw new ConcurrentModificationException('The "classFieldAces" property has been modified concurrently.'); - } - - $classFieldAcesProperty->setValue($sameTypeAcl, $sharedPropertyChanges['classFieldAces'][1]); - } - } - } - - // persist any changes to the acl_object_identities table - if (count($sets) > 0) { - $this->connection->executeQuery($this->getUpdateObjectIdentitySql($acl->getId(), $sets)); - } - - $this->connection->commit(); - } catch (\Exception $e) { - $this->connection->rollBack(); - - throw $e; - } - - $this->propertyChanges->offsetSet($acl, array()); - - if (null !== $this->cache) { - if (count($sharedPropertyChanges) > 0) { - // FIXME: Currently, there is no easy way to clear the cache for ACLs - // of a certain type. The problem here is that we need to make - // sure to clear the cache of all child ACLs as well, and these - // child ACLs might be of a different class type. - $this->cache->clearCache(); - } else { - // if there are no shared property changes, it's sufficient to just delete - // the cache for this ACL - $this->cache->evictFromCacheByIdentity($acl->getObjectIdentity()); - - foreach ($this->findChildren($acl->getObjectIdentity()) as $childOid) { - $this->cache->evictFromCacheByIdentity($childOid); - } - } - } - } - - /** - * Updates a user security identity when the user's username changes. - * - * @param UserSecurityIdentity $usid - * @param string $oldUsername - */ - public function updateUserSecurityIdentity(UserSecurityIdentity $usid, $oldUsername) - { - $this->connection->executeQuery($this->getUpdateUserSecurityIdentitySql($usid, $oldUsername)); - } - - /** - * Constructs the SQL for deleting access control entries. - * - * @param int $oidPK - * - * @return string - */ - protected function getDeleteAccessControlEntriesSql($oidPK) - { - return sprintf( - 'DELETE FROM %s WHERE object_identity_id = %d', - $this->options['entry_table_name'], - $oidPK - ); - } - - /** - * Constructs the SQL for deleting a specific ACE. - * - * @param int $acePK - * - * @return string - */ - protected function getDeleteAccessControlEntrySql($acePK) - { - return sprintf( - 'DELETE FROM %s WHERE id = %d', - $this->options['entry_table_name'], - $acePK - ); - } - - /** - * Constructs the SQL for deleting an object identity. - * - * @param int $pk - * - * @return string - */ - protected function getDeleteObjectIdentitySql($pk) - { - return sprintf( - 'DELETE FROM %s WHERE id = %d', - $this->options['oid_table_name'], - $pk - ); - } - - /** - * Constructs the SQL for deleting relation entries. - * - * @param int $pk - * - * @return string - */ - protected function getDeleteObjectIdentityRelationsSql($pk) - { - return sprintf( - 'DELETE FROM %s WHERE object_identity_id = %d', - $this->options['oid_ancestors_table_name'], - $pk - ); - } - - /** - * Constructs the SQL for inserting an ACE. - * - * @param int $classId - * @param int|null $objectIdentityId - * @param string|null $field - * @param int $aceOrder - * @param int $securityIdentityId - * @param string $strategy - * @param int $mask - * @param bool $granting - * @param bool $auditSuccess - * @param bool $auditFailure - * - * @return string - */ - protected function getInsertAccessControlEntrySql($classId, $objectIdentityId, $field, $aceOrder, $securityIdentityId, $strategy, $mask, $granting, $auditSuccess, $auditFailure) - { - $query = <<<'QUERY' - INSERT INTO %s ( - class_id, - object_identity_id, - field_name, - ace_order, - security_identity_id, - mask, - granting, - granting_strategy, - audit_success, - audit_failure - ) - VALUES (%d, %s, %s, %d, %d, %d, %s, %s, %s, %s) -QUERY; - - return sprintf( - $query, - $this->options['entry_table_name'], - $classId, - null === $objectIdentityId ? 'NULL' : (int) $objectIdentityId, - null === $field ? 'NULL' : $this->connection->quote($field), - $aceOrder, - $securityIdentityId, - $mask, - $this->connection->getDatabasePlatform()->convertBooleans($granting), - $this->connection->quote($strategy), - $this->connection->getDatabasePlatform()->convertBooleans($auditSuccess), - $this->connection->getDatabasePlatform()->convertBooleans($auditFailure) - ); - } - - /** - * Constructs the SQL for inserting a new class type. - * - * @param string $classType - * - * @return string - */ - protected function getInsertClassSql($classType) - { - return sprintf( - 'INSERT INTO %s (class_type) VALUES (%s)', - $this->options['class_table_name'], - $this->connection->quote($classType) - ); - } - - /** - * Constructs the SQL for inserting a relation entry. - * - * @param int $objectIdentityId - * @param int $ancestorId - * - * @return string - */ - protected function getInsertObjectIdentityRelationSql($objectIdentityId, $ancestorId) - { - return sprintf( - 'INSERT INTO %s (object_identity_id, ancestor_id) VALUES (%d, %d)', - $this->options['oid_ancestors_table_name'], - $objectIdentityId, - $ancestorId - ); - } - - /** - * Constructs the SQL for inserting an object identity. - * - * @param string $identifier - * @param int $classId - * @param bool $entriesInheriting - * - * @return string - */ - protected function getInsertObjectIdentitySql($identifier, $classId, $entriesInheriting) - { - $query = <<<'QUERY' - INSERT INTO %s (class_id, object_identifier, entries_inheriting) - VALUES (%d, %s, %s) -QUERY; - - return sprintf( - $query, - $this->options['oid_table_name'], - $classId, - $this->connection->quote($identifier), - $this->connection->getDatabasePlatform()->convertBooleans($entriesInheriting) - ); - } - - /** - * Constructs the SQL for inserting a security identity. - * - * @param SecurityIdentityInterface $sid - * - * @return string - * - * @throws \InvalidArgumentException - */ - protected function getInsertSecurityIdentitySql(SecurityIdentityInterface $sid) - { - if ($sid instanceof UserSecurityIdentity) { - $identifier = $sid->getClass().'-'.$sid->getUsername(); - $username = true; - } elseif ($sid instanceof RoleSecurityIdentity) { - $identifier = $sid->getRole(); - $username = false; - } else { - throw new \InvalidArgumentException('$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.'); - } - - return sprintf( - 'INSERT INTO %s (identifier, username) VALUES (%s, %s)', - $this->options['sid_table_name'], - $this->connection->quote($identifier), - $this->connection->getDatabasePlatform()->convertBooleans($username) - ); - } - - /** - * Constructs the SQL for selecting an ACE. - * - * @param int $classId - * @param int $oid - * @param string $field - * @param int $order - * - * @return string - */ - protected function getSelectAccessControlEntryIdSql($classId, $oid, $field, $order) - { - return sprintf( - 'SELECT id FROM %s WHERE class_id = %d AND %s AND %s AND ace_order = %d', - $this->options['entry_table_name'], - $classId, - null === $oid ? - $this->connection->getDatabasePlatform()->getIsNullExpression('object_identity_id') - : 'object_identity_id = '.(int) $oid, - null === $field ? - $this->connection->getDatabasePlatform()->getIsNullExpression('field_name') - : 'field_name = '.$this->connection->quote($field), - $order - ); - } - - /** - * Constructs the SQL for selecting the primary key associated with - * the passed class type. - * - * @param string $classType - * - * @return string - */ - protected function getSelectClassIdSql($classType) - { - return sprintf( - 'SELECT id FROM %s WHERE class_type = %s', - $this->options['class_table_name'], - $this->connection->quote($classType) - ); - } - - /** - * Constructs the SQL for selecting the primary key of a security identity. - * - * @param SecurityIdentityInterface $sid - * - * @return string - * - * @throws \InvalidArgumentException - */ - protected function getSelectSecurityIdentityIdSql(SecurityIdentityInterface $sid) - { - if ($sid instanceof UserSecurityIdentity) { - $identifier = $sid->getClass().'-'.$sid->getUsername(); - $username = true; - } elseif ($sid instanceof RoleSecurityIdentity) { - $identifier = $sid->getRole(); - $username = false; - } else { - throw new \InvalidArgumentException('$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.'); - } - - return sprintf( - 'SELECT id FROM %s WHERE identifier = %s AND username = %s', - $this->options['sid_table_name'], - $this->connection->quote($identifier), - $this->connection->getDatabasePlatform()->convertBooleans($username) - ); - } - - /** - * Constructs the SQL to delete a security identity. - * - * @param SecurityIdentityInterface $sid - * - * @return string - * - * @throws \InvalidArgumentException - */ - protected function getDeleteSecurityIdentityIdSql(SecurityIdentityInterface $sid) - { - $select = $this->getSelectSecurityIdentityIdSql($sid); - $delete = preg_replace('/^SELECT id FROM/', 'DELETE FROM', $select); - - return $delete; - } - - /** - * Constructs the SQL for updating an object identity. - * - * @param int $pk - * @param array $changes - * - * @return string - * - * @throws \InvalidArgumentException - */ - protected function getUpdateObjectIdentitySql($pk, array $changes) - { - if (0 === count($changes)) { - throw new \InvalidArgumentException('There are no changes.'); - } - - return sprintf( - 'UPDATE %s SET %s WHERE id = %d', - $this->options['oid_table_name'], - implode(', ', $changes), - $pk - ); - } - - /** - * Constructs the SQL for updating a user security identity. - * - * @param UserSecurityIdentity $usid - * @param string $oldUsername - * - * @return string - */ - protected function getUpdateUserSecurityIdentitySql(UserSecurityIdentity $usid, $oldUsername) - { - if ($usid->getUsername() == $oldUsername) { - throw new \InvalidArgumentException('There are no changes.'); - } - - $oldIdentifier = $usid->getClass().'-'.$oldUsername; - $newIdentifier = $usid->getClass().'-'.$usid->getUsername(); - - return sprintf( - 'UPDATE %s SET identifier = %s WHERE identifier = %s AND username = %s', - $this->options['sid_table_name'], - $this->connection->quote($newIdentifier), - $this->connection->quote($oldIdentifier), - $this->connection->getDatabasePlatform()->convertBooleans(true) - ); - } - - /** - * Constructs the SQL for updating an ACE. - * - * @param int $pk - * @param array $sets - * - * @return string - * - * @throws \InvalidArgumentException - */ - protected function getUpdateAccessControlEntrySql($pk, array $sets) - { - if (0 === count($sets)) { - throw new \InvalidArgumentException('There are no changes.'); - } - - return sprintf( - 'UPDATE %s SET %s WHERE id = %d', - $this->options['entry_table_name'], - implode(', ', $sets), - $pk - ); - } - - /** - * Creates the ACL for the passed object identity. - * - * @param ObjectIdentityInterface $oid - */ - private function createObjectIdentity(ObjectIdentityInterface $oid) - { - $classId = $this->createOrRetrieveClassId($oid->getType()); - - $this->connection->executeQuery($this->getInsertObjectIdentitySql($oid->getIdentifier(), $classId, true)); - } - - /** - * Returns the primary key for the passed class type. - * - * If the type does not yet exist in the database, it will be created. - * - * @param string $classType - * - * @return int - */ - private function createOrRetrieveClassId($classType) - { - if (false !== $id = $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn()) { - return $id; - } - - $this->connection->executeQuery($this->getInsertClassSql($classType)); - - return $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn(); - } - - /** - * Returns the primary key for the passed security identity. - * - * If the security identity does not yet exist in the database, it will be - * created. - * - * @param SecurityIdentityInterface $sid - * - * @return int - */ - private function createOrRetrieveSecurityIdentityId(SecurityIdentityInterface $sid) - { - if (false !== $id = $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn()) { - return $id; - } - - $this->connection->executeQuery($this->getInsertSecurityIdentitySql($sid)); - - return $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn(); - } - - /** - * Deletes all ACEs for the given object identity primary key. - * - * @param int $oidPK - */ - private function deleteAccessControlEntries($oidPK) - { - $this->connection->executeQuery($this->getDeleteAccessControlEntriesSql($oidPK)); - } - - /** - * Deletes the object identity from the database. - * - * @param int $pk - */ - private function deleteObjectIdentity($pk) - { - $this->connection->executeQuery($this->getDeleteObjectIdentitySql($pk)); - } - - /** - * Deletes all entries from the relations table from the database. - * - * @param int $pk - */ - private function deleteObjectIdentityRelations($pk) - { - $this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk)); - } - - /** - * This regenerates the ancestor table which is used for fast read access. - * - * @param AclInterface $acl - */ - private function regenerateAncestorRelations(AclInterface $acl) - { - $pk = $acl->getId(); - $this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk)); - $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $pk)); - - $parentAcl = $acl->getParentAcl(); - while (null !== $parentAcl) { - $this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $parentAcl->getId())); - - $parentAcl = $parentAcl->getParentAcl(); - } - } - - /** - * This processes new entries changes on an ACE related property (classFieldAces, or objectFieldAces). - * - * @param string $name - * @param array $changes - */ - private function updateNewFieldAceProperty($name, array $changes) - { - $sids = new \SplObjectStorage(); - $classIds = new \SplObjectStorage(); - foreach ($changes[1] as $field => $new) { - for ($i = 0, $c = count($new); $i < $c; ++$i) { - $ace = $new[$i]; - - if (null === $ace->getId()) { - if ($sids->contains($ace->getSecurityIdentity())) { - $sid = $sids->offsetGet($ace->getSecurityIdentity()); - } else { - $sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity()); - } - - $oid = $ace->getAcl()->getObjectIdentity(); - if ($classIds->contains($oid)) { - $classId = $classIds->offsetGet($oid); - } else { - $classId = $this->createOrRetrieveClassId($oid->getType()); - } - - $objectIdentityId = $name === 'classFieldAces' ? null : $ace->getAcl()->getId(); - - $this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, $field, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure())); - $aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, $field, $i))->fetchColumn(); - $this->loadedAces[$aceId] = $ace; - - $aceIdProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Entry', 'id'); - $aceIdProperty->setAccessible(true); - $aceIdProperty->setValue($ace, (int) $aceId); - } - } - } - } - - /** - * This processes old entries changes on an ACE related property (classFieldAces, or objectFieldAces). - * - * @param string $name - * @param array $changes - */ - private function updateOldFieldAceProperty($name, array $changes) - { - $currentIds = array(); - foreach ($changes[1] as $field => $new) { - for ($i = 0, $c = count($new); $i < $c; ++$i) { - $ace = $new[$i]; - - if (null !== $ace->getId()) { - $currentIds[$ace->getId()] = true; - } - } - } - - foreach ($changes[0] as $old) { - for ($i = 0, $c = count($old); $i < $c; ++$i) { - $ace = $old[$i]; - - if (!isset($currentIds[$ace->getId()])) { - $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId())); - unset($this->loadedAces[$ace->getId()]); - } - } - } - } - - /** - * This processes new entries changes on an ACE related property (classAces, or objectAces). - * - * @param string $name - * @param array $changes - */ - private function updateNewAceProperty($name, array $changes) - { - list(, $new) = $changes; - - $sids = new \SplObjectStorage(); - $classIds = new \SplObjectStorage(); - for ($i = 0, $c = count($new); $i < $c; ++$i) { - $ace = $new[$i]; - - if (null === $ace->getId()) { - if ($sids->contains($ace->getSecurityIdentity())) { - $sid = $sids->offsetGet($ace->getSecurityIdentity()); - } else { - $sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity()); - } - - $oid = $ace->getAcl()->getObjectIdentity(); - if ($classIds->contains($oid)) { - $classId = $classIds->offsetGet($oid); - } else { - $classId = $this->createOrRetrieveClassId($oid->getType()); - } - - $objectIdentityId = $name === 'classAces' ? null : $ace->getAcl()->getId(); - - $this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, null, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure())); - $aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, null, $i))->fetchColumn(); - $this->loadedAces[$aceId] = $ace; - - $aceIdProperty = new \ReflectionProperty($ace, 'id'); - $aceIdProperty->setAccessible(true); - $aceIdProperty->setValue($ace, (int) $aceId); - } - } - } - - /** - * This processes old entries changes on an ACE related property (classAces, or objectAces). - * - * @param string $name - * @param array $changes - */ - private function updateOldAceProperty($name, array $changes) - { - list($old, $new) = $changes; - $currentIds = array(); - - for ($i = 0, $c = count($new); $i < $c; ++$i) { - $ace = $new[$i]; - - if (null !== $ace->getId()) { - $currentIds[$ace->getId()] = true; - } - } - - for ($i = 0, $c = count($old); $i < $c; ++$i) { - $ace = $old[$i]; - - if (!isset($currentIds[$ace->getId()])) { - $this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId())); - unset($this->loadedAces[$ace->getId()]); - } - } - } - - /** - * Persists the changes which were made to ACEs to the database. - * - * @param \SplObjectStorage $aces - */ - private function updateAces(\SplObjectStorage $aces) - { - foreach ($aces as $ace) { - $this->updateAce($aces, $ace); - } - } - - private function updateAce(\SplObjectStorage $aces, $ace) - { - $propertyChanges = $aces->offsetGet($ace); - $sets = array(); - - if (isset($propertyChanges['aceOrder']) - && $propertyChanges['aceOrder'][1] > $propertyChanges['aceOrder'][0] - && $propertyChanges == $aces->offsetGet($ace)) { - $aces->next(); - if ($aces->valid()) { - $this->updateAce($aces, $aces->current()); - } - } - - if (isset($propertyChanges['mask'])) { - $sets[] = sprintf('mask = %d', $propertyChanges['mask'][1]); - } - if (isset($propertyChanges['strategy'])) { - $sets[] = sprintf('granting_strategy = %s', $this->connection->quote($propertyChanges['strategy'])); - } - if (isset($propertyChanges['aceOrder'])) { - $sets[] = sprintf('ace_order = %d', $propertyChanges['aceOrder'][1]); - } - if (isset($propertyChanges['auditSuccess'])) { - $sets[] = sprintf('audit_success = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditSuccess'][1])); - } - if (isset($propertyChanges['auditFailure'])) { - $sets[] = sprintf('audit_failure = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditFailure'][1])); - } - - $this->connection->executeQuery($this->getUpdateAccessControlEntrySql($ace->getId(), $sets)); - } -} diff --git a/Acl/Dbal/Schema.php b/Acl/Dbal/Schema.php deleted file mode 100644 index ed9327c..0000000 --- a/Acl/Dbal/Schema.php +++ /dev/null @@ -1,154 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Dbal; - -use Doctrine\DBAL\Schema\Schema as BaseSchema; -use Doctrine\DBAL\Connection; - -/** - * The schema used for the ACL system. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -final class Schema extends BaseSchema -{ - protected $options; - - /** - * Constructor. - * - * @param array $options the names for tables - * @param Connection $connection - */ - public function __construct(array $options, Connection $connection = null) - { - $schemaConfig = null === $connection ? null : $connection->getSchemaManager()->createSchemaConfig(); - - parent::__construct(array(), array(), $schemaConfig); - - $this->options = $options; - - $this->addClassTable(); - $this->addSecurityIdentitiesTable(); - $this->addObjectIdentitiesTable(); - $this->addObjectIdentityAncestorsTable(); - $this->addEntryTable(); - } - - /** - * Merges ACL schema with the given schema. - * - * @param BaseSchema $schema - */ - public function addToSchema(BaseSchema $schema) - { - foreach ($this->getTables() as $table) { - $schema->_addTable($table); - } - - foreach ($this->getSequences() as $sequence) { - $schema->_addSequence($sequence); - } - } - - /** - * Adds the class table to the schema. - */ - protected function addClassTable() - { - $table = $this->createTable($this->options['class_table_name']); - $table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => 'auto')); - $table->addColumn('class_type', 'string', array('length' => 200)); - $table->setPrimaryKey(array('id')); - $table->addUniqueIndex(array('class_type')); - } - - /** - * Adds the entry table to the schema. - */ - protected function addEntryTable() - { - $table = $this->createTable($this->options['entry_table_name']); - - $table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => 'auto')); - $table->addColumn('class_id', 'integer', array('unsigned' => true)); - $table->addColumn('object_identity_id', 'integer', array('unsigned' => true, 'notnull' => false)); - $table->addColumn('field_name', 'string', array('length' => 50, 'notnull' => false)); - $table->addColumn('ace_order', 'smallint', array('unsigned' => true)); - $table->addColumn('security_identity_id', 'integer', array('unsigned' => true)); - $table->addColumn('mask', 'integer'); - $table->addColumn('granting', 'boolean'); - $table->addColumn('granting_strategy', 'string', array('length' => 30)); - $table->addColumn('audit_success', 'boolean'); - $table->addColumn('audit_failure', 'boolean'); - - $table->setPrimaryKey(array('id')); - $table->addUniqueIndex(array('class_id', 'object_identity_id', 'field_name', 'ace_order')); - $table->addIndex(array('class_id', 'object_identity_id', 'security_identity_id')); - - $table->addForeignKeyConstraint($this->getTable($this->options['class_table_name']), array('class_id'), array('id'), array('onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE')); - $table->addForeignKeyConstraint($this->getTable($this->options['oid_table_name']), array('object_identity_id'), array('id'), array('onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE')); - $table->addForeignKeyConstraint($this->getTable($this->options['sid_table_name']), array('security_identity_id'), array('id'), array('onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE')); - } - - /** - * Adds the object identity table to the schema. - */ - protected function addObjectIdentitiesTable() - { - $table = $this->createTable($this->options['oid_table_name']); - - $table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => 'auto')); - $table->addColumn('class_id', 'integer', array('unsigned' => true)); - $table->addColumn('object_identifier', 'string', array('length' => 100)); - $table->addColumn('parent_object_identity_id', 'integer', array('unsigned' => true, 'notnull' => false)); - $table->addColumn('entries_inheriting', 'boolean'); - - $table->setPrimaryKey(array('id')); - $table->addUniqueIndex(array('object_identifier', 'class_id')); - $table->addIndex(array('parent_object_identity_id')); - - $table->addForeignKeyConstraint($table, array('parent_object_identity_id'), array('id')); - } - - /** - * Adds the object identity relation table to the schema. - */ - protected function addObjectIdentityAncestorsTable() - { - $table = $this->createTable($this->options['oid_ancestors_table_name']); - - $table->addColumn('object_identity_id', 'integer', array('unsigned' => true)); - $table->addColumn('ancestor_id', 'integer', array('unsigned' => true)); - - $table->setPrimaryKey(array('object_identity_id', 'ancestor_id')); - - $oidTable = $this->getTable($this->options['oid_table_name']); - $table->addForeignKeyConstraint($oidTable, array('object_identity_id'), array('id'), array('onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE')); - $table->addForeignKeyConstraint($oidTable, array('ancestor_id'), array('id'), array('onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE')); - } - - /** - * Adds the security identity table to the schema. - */ - protected function addSecurityIdentitiesTable() - { - $table = $this->createTable($this->options['sid_table_name']); - - $table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => 'auto')); - $table->addColumn('identifier', 'string', array('length' => 200)); - $table->addColumn('username', 'boolean'); - - $table->setPrimaryKey(array('id')); - $table->addUniqueIndex(array('identifier', 'username')); - } -} diff --git a/Acl/Domain/Acl.php b/Acl/Domain/Acl.php deleted file mode 100644 index fb70738..0000000 --- a/Acl/Domain/Acl.php +++ /dev/null @@ -1,667 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Doctrine\Common\NotifyPropertyChanged; -use Doctrine\Common\PropertyChangedListener; -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Model\AuditableAclInterface; -use Symfony\Component\Security\Acl\Model\EntryInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; -use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; - -/** - * An ACL implementation. - * - * Each object identity has exactly one associated ACL. Each ACL can have four - * different types of ACEs (class ACEs, object ACEs, class field ACEs, object field - * ACEs). - * - * You should not iterate over the ACEs yourself, but instead use isGranted(), - * or isFieldGranted(). These will utilize an implementation of PermissionGrantingStrategy - * internally. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class Acl implements AuditableAclInterface, NotifyPropertyChanged -{ - private $parentAcl; - private $permissionGrantingStrategy; - private $objectIdentity; - private $classAces = array(); - private $classFieldAces = array(); - private $objectAces = array(); - private $objectFieldAces = array(); - private $id; - private $loadedSids; - private $entriesInheriting; - private $listeners = array(); - - /** - * Constructor. - * - * @param int $id - * @param ObjectIdentityInterface $objectIdentity - * @param PermissionGrantingStrategyInterface $permissionGrantingStrategy - * @param array $loadedSids - * @param bool $entriesInheriting - */ - public function __construct($id, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSids, $entriesInheriting) - { - $this->id = $id; - $this->objectIdentity = $objectIdentity; - $this->permissionGrantingStrategy = $permissionGrantingStrategy; - $this->loadedSids = $loadedSids; - $this->entriesInheriting = $entriesInheriting; - } - - /** - * Adds a property changed listener. - * - * @param PropertyChangedListener $listener - */ - public function addPropertyChangedListener(PropertyChangedListener $listener) - { - $this->listeners[] = $listener; - } - - /** - * {@inheritdoc} - */ - public function deleteClassAce($index) - { - $this->deleteAce('classAces', $index); - } - - /** - * {@inheritdoc} - */ - public function deleteClassFieldAce($index, $field) - { - $this->deleteFieldAce('classFieldAces', $index, $field); - } - - /** - * {@inheritdoc} - */ - public function deleteObjectAce($index) - { - $this->deleteAce('objectAces', $index); - } - - /** - * {@inheritdoc} - */ - public function deleteObjectFieldAce($index, $field) - { - $this->deleteFieldAce('objectFieldAces', $index, $field); - } - - /** - * {@inheritdoc} - */ - public function getClassAces() - { - return $this->classAces; - } - - /** - * {@inheritdoc} - */ - public function getClassFieldAces($field) - { - return isset($this->classFieldAces[$field]) ? $this->classFieldAces[$field] : array(); - } - - /** - * {@inheritdoc} - */ - public function getObjectAces() - { - return $this->objectAces; - } - - /** - * {@inheritdoc} - */ - public function getObjectFieldAces($field) - { - return isset($this->objectFieldAces[$field]) ? $this->objectFieldAces[$field] : array(); - } - - /** - * {@inheritdoc} - */ - public function getId() - { - return $this->id; - } - - /** - * {@inheritdoc} - */ - public function getObjectIdentity() - { - return $this->objectIdentity; - } - - /** - * {@inheritdoc} - */ - public function getParentAcl() - { - return $this->parentAcl; - } - - /** - * {@inheritdoc} - */ - public function insertClassAce(SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null) - { - $this->insertAce('classAces', $index, $mask, $sid, $granting, $strategy); - } - - /** - * {@inheritdoc} - */ - public function insertClassFieldAce($field, SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null) - { - $this->insertFieldAce('classFieldAces', $index, $field, $mask, $sid, $granting, $strategy); - } - - /** - * {@inheritdoc} - */ - public function insertObjectAce(SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null) - { - $this->insertAce('objectAces', $index, $mask, $sid, $granting, $strategy); - } - - /** - * {@inheritdoc} - */ - public function insertObjectFieldAce($field, SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null) - { - $this->insertFieldAce('objectFieldAces', $index, $field, $mask, $sid, $granting, $strategy); - } - - /** - * {@inheritdoc} - */ - public function isEntriesInheriting() - { - return $this->entriesInheriting; - } - - /** - * {@inheritdoc} - */ - public function isFieldGranted($field, array $masks, array $securityIdentities, $administrativeMode = false) - { - return $this->permissionGrantingStrategy->isFieldGranted($this, $field, $masks, $securityIdentities, $administrativeMode); - } - - /** - * {@inheritdoc} - */ - public function isGranted(array $masks, array $securityIdentities, $administrativeMode = false) - { - return $this->permissionGrantingStrategy->isGranted($this, $masks, $securityIdentities, $administrativeMode); - } - - /** - * {@inheritdoc} - */ - public function isSidLoaded($sids) - { - if (!$this->loadedSids) { - return true; - } - - if (!is_array($sids)) { - $sids = array($sids); - } - - foreach ($sids as $sid) { - if (!$sid instanceof SecurityIdentityInterface) { - throw new \InvalidArgumentException( - '$sid must be an instance of SecurityIdentityInterface.'); - } - - foreach ($this->loadedSids as $loadedSid) { - if ($loadedSid->equals($sid)) { - continue 2; - } - } - - return false; - } - - return true; - } - - /** - * Implementation for the \Serializable interface. - * - * @return string - */ - public function serialize() - { - return serialize(array( - null === $this->parentAcl ? null : $this->parentAcl->getId(), - $this->objectIdentity, - $this->classAces, - $this->classFieldAces, - $this->objectAces, - $this->objectFieldAces, - $this->id, - $this->loadedSids, - $this->entriesInheriting, - )); - } - - /** - * Implementation for the \Serializable interface. - * - * @param string $serialized - */ - public function unserialize($serialized) - { - list($this->parentAcl, - $this->objectIdentity, - $this->classAces, - $this->classFieldAces, - $this->objectAces, - $this->objectFieldAces, - $this->id, - $this->loadedSids, - $this->entriesInheriting - ) = unserialize($serialized); - - $this->listeners = array(); - } - - /** - * {@inheritdoc} - */ - public function setEntriesInheriting($boolean) - { - if ($this->entriesInheriting !== $boolean) { - $this->onPropertyChanged('entriesInheriting', $this->entriesInheriting, $boolean); - $this->entriesInheriting = $boolean; - } - } - - /** - * {@inheritdoc} - */ - public function setParentAcl(AclInterface $acl = null) - { - if (null !== $acl && null === $acl->getId()) { - throw new \InvalidArgumentException('$acl must have an ID.'); - } - - if ($this->parentAcl !== $acl) { - $this->onPropertyChanged('parentAcl', $this->parentAcl, $acl); - $this->parentAcl = $acl; - } - } - - /** - * {@inheritdoc} - */ - public function updateClassAce($index, $mask, $strategy = null) - { - $this->updateAce('classAces', $index, $mask, $strategy); - } - - /** - * {@inheritdoc} - */ - public function updateClassFieldAce($index, $field, $mask, $strategy = null) - { - $this->updateFieldAce('classFieldAces', $index, $field, $mask, $strategy); - } - - /** - * {@inheritdoc} - */ - public function updateObjectAce($index, $mask, $strategy = null) - { - $this->updateAce('objectAces', $index, $mask, $strategy); - } - - /** - * {@inheritdoc} - */ - public function updateObjectFieldAce($index, $field, $mask, $strategy = null) - { - $this->updateFieldAce('objectFieldAces', $index, $field, $mask, $strategy); - } - - /** - * {@inheritdoc} - */ - public function updateClassAuditing($index, $auditSuccess, $auditFailure) - { - $this->updateAuditing($this->classAces, $index, $auditSuccess, $auditFailure); - } - - /** - * {@inheritdoc} - */ - public function updateClassFieldAuditing($index, $field, $auditSuccess, $auditFailure) - { - if (!isset($this->classFieldAces[$field])) { - throw new \InvalidArgumentException(sprintf('There are no ACEs for field "%s".', $field)); - } - - $this->updateAuditing($this->classFieldAces[$field], $index, $auditSuccess, $auditFailure); - } - - /** - * {@inheritdoc} - */ - public function updateObjectAuditing($index, $auditSuccess, $auditFailure) - { - $this->updateAuditing($this->objectAces, $index, $auditSuccess, $auditFailure); - } - - /** - * {@inheritdoc} - */ - public function updateObjectFieldAuditing($index, $field, $auditSuccess, $auditFailure) - { - if (!isset($this->objectFieldAces[$field])) { - throw new \InvalidArgumentException(sprintf('There are no ACEs for field "%s".', $field)); - } - - $this->updateAuditing($this->objectFieldAces[$field], $index, $auditSuccess, $auditFailure); - } - - /** - * Deletes an ACE. - * - * @param string $property - * @param int $index - * - * @throws \OutOfBoundsException - */ - private function deleteAce($property, $index) - { - $aces = &$this->$property; - if (!isset($aces[$index])) { - throw new \OutOfBoundsException(sprintf('The index "%d" does not exist.', $index)); - } - - $oldValue = $this->$property; - unset($aces[$index]); - $this->$property = array_values($this->$property); - $this->onPropertyChanged($property, $oldValue, $this->$property); - - for ($i = $index, $c = count($this->$property); $i < $c; ++$i) { - $this->onEntryPropertyChanged($aces[$i], 'aceOrder', $i + 1, $i); - } - } - - /** - * Deletes a field-based ACE. - * - * @param string $property - * @param int $index - * @param string $field - * - * @throws \OutOfBoundsException - */ - private function deleteFieldAce($property, $index, $field) - { - $aces = &$this->$property; - if (!isset($aces[$field][$index])) { - throw new \OutOfBoundsException(sprintf('The index "%d" does not exist.', $index)); - } - - $oldValue = $this->$property; - unset($aces[$field][$index]); - $aces[$field] = array_values($aces[$field]); - $this->onPropertyChanged($property, $oldValue, $this->$property); - - for ($i = $index, $c = count($aces[$field]); $i < $c; ++$i) { - $this->onEntryPropertyChanged($aces[$field][$i], 'aceOrder', $i + 1, $i); - } - } - - /** - * Inserts an ACE. - * - * @param string $property - * @param int $index - * @param int $mask - * @param SecurityIdentityInterface $sid - * @param bool $granting - * @param string $strategy - * - * @throws \OutOfBoundsException - * @throws \InvalidArgumentException - */ - private function insertAce($property, $index, $mask, SecurityIdentityInterface $sid, $granting, $strategy = null) - { - if ($index < 0 || $index > count($this->$property)) { - throw new \OutOfBoundsException(sprintf('The index must be in the interval [0, %d].', count($this->$property))); - } - - if (!is_int($mask)) { - throw new \InvalidArgumentException('$mask must be an integer.'); - } - - if (null === $strategy) { - if (true === $granting) { - $strategy = PermissionGrantingStrategy::ALL; - } else { - $strategy = PermissionGrantingStrategy::ANY; - } - } - - $aces = &$this->$property; - $oldValue = $this->$property; - if (isset($aces[$index])) { - $this->$property = array_merge( - array_slice($this->$property, 0, $index), - array(true), - array_slice($this->$property, $index) - ); - - for ($i = $index, $c = count($this->$property) - 1; $i < $c; ++$i) { - $this->onEntryPropertyChanged($aces[$i + 1], 'aceOrder', $i, $i + 1); - } - } - - $aces[$index] = new Entry(null, $this, $sid, $strategy, $mask, $granting, false, false); - $this->onPropertyChanged($property, $oldValue, $this->$property); - } - - /** - * Inserts a field-based ACE. - * - * @param string $property - * @param int $index - * @param string $field - * @param int $mask - * @param SecurityIdentityInterface $sid - * @param bool $granting - * @param string $strategy - * - * @throws \InvalidArgumentException - * @throws \OutOfBoundsException - */ - private function insertFieldAce($property, $index, $field, $mask, SecurityIdentityInterface $sid, $granting, $strategy = null) - { - if (0 === strlen($field)) { - throw new \InvalidArgumentException('$field cannot be empty.'); - } - - if (!is_int($mask)) { - throw new \InvalidArgumentException('$mask must be an integer.'); - } - - if (null === $strategy) { - if (true === $granting) { - $strategy = PermissionGrantingStrategy::ALL; - } else { - $strategy = PermissionGrantingStrategy::ANY; - } - } - - $aces = &$this->$property; - if (!isset($aces[$field])) { - $aces[$field] = array(); - } - - if ($index < 0 || $index > count($aces[$field])) { - throw new \OutOfBoundsException(sprintf('The index must be in the interval [0, %d].', count($this->$property))); - } - - $oldValue = $aces; - if (isset($aces[$field][$index])) { - $aces[$field] = array_merge( - array_slice($aces[$field], 0, $index), - array(true), - array_slice($aces[$field], $index) - ); - - for ($i = $index, $c = count($aces[$field]) - 1; $i < $c; ++$i) { - $this->onEntryPropertyChanged($aces[$field][$i + 1], 'aceOrder', $i, $i + 1); - } - } - - $aces[$field][$index] = new FieldEntry(null, $this, $field, $sid, $strategy, $mask, $granting, false, false); - $this->onPropertyChanged($property, $oldValue, $this->$property); - } - - /** - * Updates an ACE. - * - * @param string $property - * @param int $index - * @param int $mask - * @param string $strategy - * - * @throws \OutOfBoundsException - */ - private function updateAce($property, $index, $mask, $strategy = null) - { - $aces = &$this->$property; - if (!isset($aces[$index])) { - throw new \OutOfBoundsException(sprintf('The index "%d" does not exist.', $index)); - } - - $ace = $aces[$index]; - if ($mask !== $oldMask = $ace->getMask()) { - $this->onEntryPropertyChanged($ace, 'mask', $oldMask, $mask); - $ace->setMask($mask); - } - if (null !== $strategy && $strategy !== $oldStrategy = $ace->getStrategy()) { - $this->onEntryPropertyChanged($ace, 'strategy', $oldStrategy, $strategy); - $ace->setStrategy($strategy); - } - } - - /** - * Updates auditing for an ACE. - * - * @param array &$aces - * @param int $index - * @param bool $auditSuccess - * @param bool $auditFailure - * - * @throws \OutOfBoundsException - */ - private function updateAuditing(array &$aces, $index, $auditSuccess, $auditFailure) - { - if (!isset($aces[$index])) { - throw new \OutOfBoundsException(sprintf('The index "%d" does not exist.', $index)); - } - - if ($auditSuccess !== $aces[$index]->isAuditSuccess()) { - $this->onEntryPropertyChanged($aces[$index], 'auditSuccess', !$auditSuccess, $auditSuccess); - $aces[$index]->setAuditSuccess($auditSuccess); - } - - if ($auditFailure !== $aces[$index]->isAuditFailure()) { - $this->onEntryPropertyChanged($aces[$index], 'auditFailure', !$auditFailure, $auditFailure); - $aces[$index]->setAuditFailure($auditFailure); - } - } - - /** - * Updates a field-based ACE. - * - * @param string $property - * @param int $index - * @param string $field - * @param int $mask - * @param string $strategy - * - * @throws \InvalidArgumentException - * @throws \OutOfBoundsException - */ - private function updateFieldAce($property, $index, $field, $mask, $strategy = null) - { - if (0 === strlen($field)) { - throw new \InvalidArgumentException('$field cannot be empty.'); - } - - $aces = &$this->$property; - if (!isset($aces[$field][$index])) { - throw new \OutOfBoundsException(sprintf('The index "%d" does not exist.', $index)); - } - - $ace = $aces[$field][$index]; - if ($mask !== $oldMask = $ace->getMask()) { - $this->onEntryPropertyChanged($ace, 'mask', $oldMask, $mask); - $ace->setMask($mask); - } - if (null !== $strategy && $strategy !== $oldStrategy = $ace->getStrategy()) { - $this->onEntryPropertyChanged($ace, 'strategy', $oldStrategy, $strategy); - $ace->setStrategy($strategy); - } - } - - /** - * Called when a property of the ACL changes. - * - * @param string $name - * @param mixed $oldValue - * @param mixed $newValue - */ - private function onPropertyChanged($name, $oldValue, $newValue) - { - foreach ($this->listeners as $listener) { - $listener->propertyChanged($this, $name, $oldValue, $newValue); - } - } - - /** - * Called when a property of an ACE associated with this ACL changes. - * - * @param EntryInterface $entry - * @param string $name - * @param mixed $oldValue - * @param mixed $newValue - */ - private function onEntryPropertyChanged(EntryInterface $entry, $name, $oldValue, $newValue) - { - foreach ($this->listeners as $listener) { - $listener->propertyChanged($entry, $name, $oldValue, $newValue); - } - } -} diff --git a/Acl/Domain/AclCollectionCache.php b/Acl/Domain/AclCollectionCache.php deleted file mode 100644 index 5dfef08..0000000 --- a/Acl/Domain/AclCollectionCache.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Model\AclProviderInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - -/** - * This service caches ACLs for an entire collection of objects. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class AclCollectionCache -{ - private $aclProvider; - private $objectIdentityRetrievalStrategy; - private $securityIdentityRetrievalStrategy; - - /** - * Constructor. - * - * @param AclProviderInterface $aclProvider - * @param ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy - * @param SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy - */ - public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy) - { - $this->aclProvider = $aclProvider; - $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy; - $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy; - } - - /** - * Batch loads ACLs for an entire collection; thus, it reduces the number - * of required queries considerably. - * - * @param mixed $collection anything that can be passed to foreach() - * @param TokenInterface[] $tokens an array of TokenInterface implementations - */ - public function cache($collection, array $tokens = array()) - { - $sids = array(); - foreach ($tokens as $token) { - $sids = array_merge($sids, $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token)); - } - - $oids = array(); - foreach ($collection as $domainObject) { - $oids[] = $this->objectIdentityRetrievalStrategy->getObjectIdentity($domainObject); - } - - $this->aclProvider->findAcls($oids, $sids); - } -} diff --git a/Acl/Domain/AuditLogger.php b/Acl/Domain/AuditLogger.php deleted file mode 100644 index e3f3bdd..0000000 --- a/Acl/Domain/AuditLogger.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Model\AuditableEntryInterface; -use Symfony\Component\Security\Acl\Model\EntryInterface; -use Symfony\Component\Security\Acl\Model\AuditLoggerInterface; - -/** - * Base audit logger implementation. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -abstract class AuditLogger implements AuditLoggerInterface -{ - /** - * Performs some checks if logging was requested. - * - * @param bool $granted - * @param EntryInterface $ace - */ - public function logIfNeeded($granted, EntryInterface $ace) - { - if (!$ace instanceof AuditableEntryInterface) { - return; - } - - if ($granted && $ace->isAuditSuccess()) { - $this->doLog($granted, $ace); - } elseif (!$granted && $ace->isAuditFailure()) { - $this->doLog($granted, $ace); - } - } - - /** - * This method is only called when logging is needed. - * - * @param bool $granted - * @param EntryInterface $ace - */ - abstract protected function doLog($granted, EntryInterface $ace); -} diff --git a/Acl/Domain/DoctrineAclCache.php b/Acl/Domain/DoctrineAclCache.php deleted file mode 100644 index 667a19e..0000000 --- a/Acl/Domain/DoctrineAclCache.php +++ /dev/null @@ -1,229 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Cache\CacheProvider; -use Symfony\Component\Security\Acl\Model\AclCacheInterface; -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; -use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface; - -/** - * This class is a wrapper around the actual cache implementation. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class DoctrineAclCache implements AclCacheInterface -{ - const PREFIX = 'sf2_acl_'; - - private $cache; - private $prefix; - private $permissionGrantingStrategy; - - /** - * Constructor. - * - * @param Cache $cache - * @param PermissionGrantingStrategyInterface $permissionGrantingStrategy - * @param string $prefix - * - * @throws \InvalidArgumentException - */ - public function __construct(Cache $cache, PermissionGrantingStrategyInterface $permissionGrantingStrategy, $prefix = self::PREFIX) - { - if (0 === strlen($prefix)) { - throw new \InvalidArgumentException('$prefix cannot be empty.'); - } - - $this->cache = $cache; - $this->permissionGrantingStrategy = $permissionGrantingStrategy; - $this->prefix = $prefix; - } - - /** - * {@inheritdoc} - */ - public function clearCache() - { - if ($this->cache instanceof CacheProvider) { - $this->cache->deleteAll(); - } - } - - /** - * {@inheritdoc} - */ - public function evictFromCacheById($aclId) - { - $lookupKey = $this->getAliasKeyForIdentity($aclId); - if (!$this->cache->contains($lookupKey)) { - return; - } - - $key = $this->cache->fetch($lookupKey); - if ($this->cache->contains($key)) { - $this->cache->delete($key); - } - - $this->cache->delete($lookupKey); - } - - /** - * {@inheritdoc} - */ - public function evictFromCacheByIdentity(ObjectIdentityInterface $oid) - { - $key = $this->getDataKeyByIdentity($oid); - if (!$this->cache->contains($key)) { - return; - } - - $this->cache->delete($key); - } - - /** - * {@inheritdoc} - */ - public function getFromCacheById($aclId) - { - $lookupKey = $this->getAliasKeyForIdentity($aclId); - if (!$this->cache->contains($lookupKey)) { - return; - } - - $key = $this->cache->fetch($lookupKey); - if (!$this->cache->contains($key)) { - $this->cache->delete($lookupKey); - - return; - } - - return $this->unserializeAcl($this->cache->fetch($key)); - } - - /** - * {@inheritdoc} - */ - public function getFromCacheByIdentity(ObjectIdentityInterface $oid) - { - $key = $this->getDataKeyByIdentity($oid); - if (!$this->cache->contains($key)) { - return; - } - - return $this->unserializeAcl($this->cache->fetch($key)); - } - - /** - * {@inheritdoc} - */ - public function putInCache(AclInterface $acl) - { - if (null === $acl->getId()) { - throw new \InvalidArgumentException('Transient ACLs cannot be cached.'); - } - - if (null !== $parentAcl = $acl->getParentAcl()) { - $this->putInCache($parentAcl); - } - - $key = $this->getDataKeyByIdentity($acl->getObjectIdentity()); - $this->cache->save($key, serialize($acl)); - $this->cache->save($this->getAliasKeyForIdentity($acl->getId()), $key); - } - - /** - * Unserializes the ACL. - * - * @param string $serialized - * - * @return AclInterface - */ - private function unserializeAcl($serialized) - { - $acl = unserialize($serialized); - - if (null !== $parentId = $acl->getParentAcl()) { - $parentAcl = $this->getFromCacheById($parentId); - - if (null === $parentAcl) { - return; - } - - $acl->setParentAcl($parentAcl); - } - - $reflectionProperty = new \ReflectionProperty($acl, 'permissionGrantingStrategy'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($acl, $this->permissionGrantingStrategy); - $reflectionProperty->setAccessible(false); - - $aceAclProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Entry', 'acl'); - $aceAclProperty->setAccessible(true); - - foreach ($acl->getObjectAces() as $ace) { - $aceAclProperty->setValue($ace, $acl); - } - foreach ($acl->getClassAces() as $ace) { - $aceAclProperty->setValue($ace, $acl); - } - - $aceClassFieldProperty = new \ReflectionProperty($acl, 'classFieldAces'); - $aceClassFieldProperty->setAccessible(true); - foreach ($aceClassFieldProperty->getValue($acl) as $aces) { - foreach ($aces as $ace) { - $aceAclProperty->setValue($ace, $acl); - } - } - $aceClassFieldProperty->setAccessible(false); - - $aceObjectFieldProperty = new \ReflectionProperty($acl, 'objectFieldAces'); - $aceObjectFieldProperty->setAccessible(true); - foreach ($aceObjectFieldProperty->getValue($acl) as $aces) { - foreach ($aces as $ace) { - $aceAclProperty->setValue($ace, $acl); - } - } - $aceObjectFieldProperty->setAccessible(false); - - $aceAclProperty->setAccessible(false); - - return $acl; - } - - /** - * Returns the key for the object identity. - * - * @param ObjectIdentityInterface $oid - * - * @return string - */ - private function getDataKeyByIdentity(ObjectIdentityInterface $oid) - { - return $this->prefix.md5($oid->getType()).sha1($oid->getType()) - .'_'.md5($oid->getIdentifier()).sha1($oid->getIdentifier()); - } - - /** - * Returns the alias key for the object identity key. - * - * @param string $aclId - * - * @return string - */ - private function getAliasKeyForIdentity($aclId) - { - return $this->prefix.$aclId; - } -} diff --git a/Acl/Domain/Entry.php b/Acl/Domain/Entry.php deleted file mode 100644 index 55c4b37..0000000 --- a/Acl/Domain/Entry.php +++ /dev/null @@ -1,208 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Model\AuditableEntryInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; - -/** - * Auditable ACE implementation. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class Entry implements AuditableEntryInterface -{ - private $acl; - private $mask; - private $id; - private $securityIdentity; - private $strategy; - private $auditFailure; - private $auditSuccess; - private $granting; - - /** - * Constructor. - * - * @param int $id - * @param AclInterface $acl - * @param SecurityIdentityInterface $sid - * @param string $strategy - * @param int $mask - * @param bool $granting - * @param bool $auditFailure - * @param bool $auditSuccess - */ - public function __construct($id, AclInterface $acl, SecurityIdentityInterface $sid, $strategy, $mask, $granting, $auditFailure, $auditSuccess) - { - $this->id = $id; - $this->acl = $acl; - $this->securityIdentity = $sid; - $this->strategy = $strategy; - $this->mask = $mask; - $this->granting = $granting; - $this->auditFailure = $auditFailure; - $this->auditSuccess = $auditSuccess; - } - - /** - * {@inheritdoc} - */ - public function getAcl() - { - return $this->acl; - } - - /** - * {@inheritdoc} - */ - public function getMask() - { - return $this->mask; - } - - /** - * {@inheritdoc} - */ - public function getId() - { - return $this->id; - } - - /** - * {@inheritdoc} - */ - public function getSecurityIdentity() - { - return $this->securityIdentity; - } - - /** - * {@inheritdoc} - */ - public function getStrategy() - { - return $this->strategy; - } - - /** - * {@inheritdoc} - */ - public function isAuditFailure() - { - return $this->auditFailure; - } - - /** - * {@inheritdoc} - */ - public function isAuditSuccess() - { - return $this->auditSuccess; - } - - /** - * {@inheritdoc} - */ - public function isGranting() - { - return $this->granting; - } - - /** - * Turns on/off auditing on permissions denials. - * - * Do never call this method directly. Use the respective methods on the - * AclInterface instead. - * - * @param bool $boolean - */ - public function setAuditFailure($boolean) - { - $this->auditFailure = $boolean; - } - - /** - * Turns on/off auditing on permission grants. - * - * Do never call this method directly. Use the respective methods on the - * AclInterface instead. - * - * @param bool $boolean - */ - public function setAuditSuccess($boolean) - { - $this->auditSuccess = $boolean; - } - - /** - * Sets the permission mask. - * - * Do never call this method directly. Use the respective methods on the - * AclInterface instead. - * - * @param int $mask - */ - public function setMask($mask) - { - $this->mask = $mask; - } - - /** - * Sets the mask comparison strategy. - * - * Do never call this method directly. Use the respective methods on the - * AclInterface instead. - * - * @param string $strategy - */ - public function setStrategy($strategy) - { - $this->strategy = $strategy; - } - - /** - * Implementation of \Serializable. - * - * @return string - */ - public function serialize() - { - return serialize(array( - $this->mask, - $this->id, - $this->securityIdentity, - $this->strategy, - $this->auditFailure, - $this->auditSuccess, - $this->granting, - )); - } - - /** - * Implementation of \Serializable. - * - * @param string $serialized - */ - public function unserialize($serialized) - { - list($this->mask, - $this->id, - $this->securityIdentity, - $this->strategy, - $this->auditFailure, - $this->auditSuccess, - $this->granting - ) = unserialize($serialized); - } -} diff --git a/Acl/Domain/FieldEntry.php b/Acl/Domain/FieldEntry.php deleted file mode 100644 index 86b1e5b..0000000 --- a/Acl/Domain/FieldEntry.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Model\FieldEntryInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; - -/** - * Field-aware ACE implementation which is auditable. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class FieldEntry extends Entry implements FieldEntryInterface -{ - private $field; - - /** - * Constructor. - * - * @param int $id - * @param AclInterface $acl - * @param string $field - * @param SecurityIdentityInterface $sid - * @param string $strategy - * @param int $mask - * @param bool $granting - * @param bool $auditFailure - * @param bool $auditSuccess - */ - public function __construct($id, AclInterface $acl, $field, SecurityIdentityInterface $sid, $strategy, $mask, $granting, $auditFailure, $auditSuccess) - { - parent::__construct($id, $acl, $sid, $strategy, $mask, $granting, $auditFailure, $auditSuccess); - - $this->field = $field; - } - - /** - * {@inheritdoc} - */ - public function getField() - { - return $this->field; - } - - /** - * {@inheritdoc} - */ - public function serialize() - { - return serialize(array( - $this->field, - parent::serialize(), - )); - } - - /** - * {@inheritdoc} - */ - public function unserialize($serialized) - { - list($this->field, $parentStr) = unserialize($serialized); - parent::unserialize($parentStr); - } -} diff --git a/Acl/Domain/ObjectIdentity.php b/Acl/Domain/ObjectIdentity.php deleted file mode 100644 index ec817e2..0000000 --- a/Acl/Domain/ObjectIdentity.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Core\Util\ClassUtils; -use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException; -use Symfony\Component\Security\Acl\Model\DomainObjectInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; - -/** - * ObjectIdentity implementation. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -final class ObjectIdentity implements ObjectIdentityInterface -{ - private $identifier; - private $type; - - /** - * Constructor. - * - * @param string $identifier - * @param string $type - * - * @throws \InvalidArgumentException - */ - public function __construct($identifier, $type) - { - if ('' === $identifier) { - throw new \InvalidArgumentException('$identifier cannot be empty.'); - } - if (empty($type)) { - throw new \InvalidArgumentException('$type cannot be empty.'); - } - - $this->identifier = $identifier; - $this->type = $type; - } - - /** - * Constructs an ObjectIdentity for the given domain object. - * - * @param object $domainObject - * - * @return ObjectIdentity - * - * @throws InvalidDomainObjectException - */ - public static function fromDomainObject($domainObject) - { - if (!is_object($domainObject)) { - throw new InvalidDomainObjectException('$domainObject must be an object.'); - } - - try { - if ($domainObject instanceof DomainObjectInterface) { - return new self($domainObject->getObjectIdentifier(), ClassUtils::getRealClass($domainObject)); - } elseif (method_exists($domainObject, 'getId')) { - return new self((string) $domainObject->getId(), ClassUtils::getRealClass($domainObject)); - } - } catch (\InvalidArgumentException $e) { - throw new InvalidDomainObjectException($e->getMessage(), 0, $e); - } - - throw new InvalidDomainObjectException('$domainObject must either implement the DomainObjectInterface, or have a method named "getId".'); - } - - /** - * {@inheritdoc} - */ - public function getIdentifier() - { - return $this->identifier; - } - - /** - * {@inheritdoc} - */ - public function getType() - { - return $this->type; - } - - /** - * {@inheritdoc} - */ - public function equals(ObjectIdentityInterface $identity) - { - // comparing the identifier with === might lead to problems, so we - // waive this restriction - return $this->identifier == $identity->getIdentifier() - && $this->type === $identity->getType(); - } - - /** - * Returns a textual representation of this object identity. - * - * @return string - */ - public function __toString() - { - return sprintf('ObjectIdentity(%s, %s)', $this->identifier, $this->type); - } -} diff --git a/Acl/Domain/ObjectIdentityRetrievalStrategy.php b/Acl/Domain/ObjectIdentityRetrievalStrategy.php deleted file mode 100644 index 80de6e0..0000000 --- a/Acl/Domain/ObjectIdentityRetrievalStrategy.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException; -use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; - -/** - * Strategy to be used for retrieving object identities from domain objects. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class ObjectIdentityRetrievalStrategy implements ObjectIdentityRetrievalStrategyInterface -{ - /** - * {@inheritdoc} - */ - public function getObjectIdentity($domainObject) - { - try { - return ObjectIdentity::fromDomainObject($domainObject); - } catch (InvalidDomainObjectException $e) { - return; - } - } -} diff --git a/Acl/Domain/PermissionGrantingStrategy.php b/Acl/Domain/PermissionGrantingStrategy.php deleted file mode 100644 index f8a09a6..0000000 --- a/Acl/Domain/PermissionGrantingStrategy.php +++ /dev/null @@ -1,211 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Exception\NoAceFoundException; -use Symfony\Component\Security\Acl\Model\AclInterface; -use Symfony\Component\Security\Acl\Model\AuditLoggerInterface; -use Symfony\Component\Security\Acl\Model\EntryInterface; -use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; - -/** - * The permission granting strategy to apply to the access control list. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class PermissionGrantingStrategy implements PermissionGrantingStrategyInterface -{ - const EQUAL = 'equal'; - const ALL = 'all'; - const ANY = 'any'; - - private $auditLogger; - - /** - * Sets the audit logger. - * - * @param AuditLoggerInterface $auditLogger - */ - public function setAuditLogger(AuditLoggerInterface $auditLogger) - { - $this->auditLogger = $auditLogger; - } - - /** - * {@inheritdoc} - */ - public function isGranted(AclInterface $acl, array $masks, array $sids, $administrativeMode = false) - { - try { - try { - $aces = $acl->getObjectAces(); - - if (!$aces) { - throw new NoAceFoundException(); - } - - return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode); - } catch (NoAceFoundException $e) { - $aces = $acl->getClassAces(); - - if (!$aces) { - throw $e; - } - - return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode); - } - } catch (NoAceFoundException $e) { - if ($acl->isEntriesInheriting() && null !== $parentAcl = $acl->getParentAcl()) { - return $parentAcl->isGranted($masks, $sids, $administrativeMode); - } - - throw $e; - } - } - - /** - * {@inheritdoc} - */ - public function isFieldGranted(AclInterface $acl, $field, array $masks, array $sids, $administrativeMode = false) - { - try { - try { - $aces = $acl->getObjectFieldAces($field); - if (!$aces) { - throw new NoAceFoundException(); - } - - return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode); - } catch (NoAceFoundException $e) { - $aces = $acl->getClassFieldAces($field); - if (!$aces) { - throw $e; - } - - return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode); - } - } catch (NoAceFoundException $e) { - if ($acl->isEntriesInheriting() && null !== $parentAcl = $acl->getParentAcl()) { - return $parentAcl->isFieldGranted($field, $masks, $sids, $administrativeMode); - } - - throw $e; - } - } - - /** - * Makes an authorization decision. - * - * The order of ACEs, and SIDs is significant; the order of permission masks - * not so much. It is important to note that the more specific security - * identities should be at the beginning of the SIDs array in order for this - * strategy to produce intuitive authorization decisions. - * - * First, we will iterate over permissions, then over security identities. - * For each combination of permission, and identity we will test the - * available ACEs until we find one which is applicable. - * - * The first applicable ACE will make the ultimate decision for the - * permission/identity combination. If it is granting, this method will return - * true, if it is denying, the method will continue to check the next - * permission/identity combination. - * - * This process is repeated until either a granting ACE is found, or no - * permission/identity combinations are left. Finally, we will either throw - * an NoAceFoundException, or deny access. - * - * @param AclInterface $acl - * @param EntryInterface[] $aces An array of ACE to check against - * @param array $masks An array of permission masks - * @param SecurityIdentityInterface[] $sids An array of SecurityIdentityInterface implementations - * @param bool $administrativeMode True turns off audit logging - * - * @return bool true, or false; either granting, or denying access respectively - * - * @throws NoAceFoundException - */ - private function hasSufficientPermissions(AclInterface $acl, array $aces, array $masks, array $sids, $administrativeMode) - { - $firstRejectedAce = null; - - foreach ($masks as $requiredMask) { - foreach ($sids as $sid) { - foreach ($aces as $ace) { - if ($sid->equals($ace->getSecurityIdentity()) && $this->isAceApplicable($requiredMask, $ace)) { - if ($ace->isGranting()) { - if (!$administrativeMode && null !== $this->auditLogger) { - $this->auditLogger->logIfNeeded(true, $ace); - } - - return true; - } - - if (null === $firstRejectedAce) { - $firstRejectedAce = $ace; - } - - break 2; - } - } - } - } - - if (null !== $firstRejectedAce) { - if (!$administrativeMode && null !== $this->auditLogger) { - $this->auditLogger->logIfNeeded(false, $firstRejectedAce); - } - - return false; - } - - throw new NoAceFoundException(); - } - - /** - * Determines whether the ACE is applicable to the given permission/security - * identity combination. - * - * Per default, we support three different comparison strategies. - * - * Strategy ALL: - * The ACE will be considered applicable when all the turned-on bits in the - * required mask are also turned-on in the ACE mask. - * - * Strategy ANY: - * The ACE will be considered applicable when any of the turned-on bits in - * the required mask is also turned-on the in the ACE mask. - * - * Strategy EQUAL: - * The ACE will be considered applicable when the bitmasks are equal. - * - * @param int $requiredMask - * @param EntryInterface $ace - * - * @return bool - * - * @throws \RuntimeException if the ACE strategy is not supported - */ - private function isAceApplicable($requiredMask, EntryInterface $ace) - { - $strategy = $ace->getStrategy(); - if (self::ALL === $strategy) { - return $requiredMask === ($ace->getMask() & $requiredMask); - } elseif (self::ANY === $strategy) { - return 0 !== ($ace->getMask() & $requiredMask); - } elseif (self::EQUAL === $strategy) { - return $requiredMask === $ace->getMask(); - } - - throw new \RuntimeException(sprintf('The strategy "%s" is not supported.', $strategy)); - } -} diff --git a/Acl/Domain/RoleSecurityIdentity.php b/Acl/Domain/RoleSecurityIdentity.php deleted file mode 100644 index c28a1c5..0000000 --- a/Acl/Domain/RoleSecurityIdentity.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; -use Symfony\Component\Security\Core\Role\Role; - -/** - * A SecurityIdentity implementation for roles. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -final class RoleSecurityIdentity implements SecurityIdentityInterface -{ - private $role; - - /** - * Constructor. - * - * @param mixed $role a Role instance, or its string representation - */ - public function __construct($role) - { - if ($role instanceof Role) { - $role = $role->getRole(); - } - - $this->role = $role; - } - - /** - * Returns the role name. - * - * @return string - */ - public function getRole() - { - return $this->role; - } - - /** - * {@inheritdoc} - */ - public function equals(SecurityIdentityInterface $sid) - { - if (!$sid instanceof self) { - return false; - } - - return $this->role === $sid->getRole(); - } - - /** - * Returns a textual representation of this security identity. - * - * This is solely used for debugging purposes, not to make an equality decision. - * - * @return string - */ - public function __toString() - { - return sprintf('RoleSecurityIdentity(%s)', $this->role); - } -} diff --git a/Acl/Domain/SecurityIdentityRetrievalStrategy.php b/Acl/Domain/SecurityIdentityRetrievalStrategy.php deleted file mode 100644 index a08f67e..0000000 --- a/Acl/Domain/SecurityIdentityRetrievalStrategy.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface; -use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; -use Symfony\Component\Security\Core\Role\RoleHierarchyInterface; -use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; - -/** - * Strategy for retrieving security identities. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class SecurityIdentityRetrievalStrategy implements SecurityIdentityRetrievalStrategyInterface -{ - private $roleHierarchy; - private $authenticationTrustResolver; - - /** - * Constructor. - * - * @param RoleHierarchyInterface $roleHierarchy - * @param AuthenticationTrustResolver $authenticationTrustResolver - */ - public function __construct(RoleHierarchyInterface $roleHierarchy, AuthenticationTrustResolver $authenticationTrustResolver) - { - $this->roleHierarchy = $roleHierarchy; - $this->authenticationTrustResolver = $authenticationTrustResolver; - } - - /** - * {@inheritdoc} - */ - public function getSecurityIdentities(TokenInterface $token) - { - $sids = array(); - - // add user security identity - if (!$token instanceof AnonymousToken) { - try { - $sids[] = UserSecurityIdentity::fromToken($token); - } catch (\InvalidArgumentException $e) { - // ignore, user has no user security identity - } - } - - // add all reachable roles - foreach ($this->roleHierarchy->getReachableRoles($token->getRoles()) as $role) { - $sids[] = new RoleSecurityIdentity($role); - } - - // 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; - } -} diff --git a/Acl/Domain/UserSecurityIdentity.php b/Acl/Domain/UserSecurityIdentity.php deleted file mode 100644 index ea17c63..0000000 --- a/Acl/Domain/UserSecurityIdentity.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Domain; - -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\Util\ClassUtils; -use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface; - -/** - * A SecurityIdentity implementation used for actual users. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -final class UserSecurityIdentity implements SecurityIdentityInterface -{ - private $username; - private $class; - - /** - * Constructor. - * - * @param string $username the username representation - * @param string $class the user's fully qualified class name - * - * @throws \InvalidArgumentException - */ - public function __construct($username, $class) - { - if ('' === $username || null === $username) { - throw new \InvalidArgumentException('$username must not be empty.'); - } - if (empty($class)) { - throw new \InvalidArgumentException('$class must not be empty.'); - } - - $this->username = (string) $username; - $this->class = $class; - } - - /** - * Creates a user security identity from a UserInterface. - * - * @param UserInterface $user - * - * @return UserSecurityIdentity - */ - public static function fromAccount(UserInterface $user) - { - return new self($user->getUsername(), ClassUtils::getRealClass($user)); - } - - /** - * Creates a user security identity from a TokenInterface. - * - * @param TokenInterface $token - * - * @return UserSecurityIdentity - */ - public static function fromToken(TokenInterface $token) - { - $user = $token->getUser(); - - if ($user instanceof UserInterface) { - return self::fromAccount($user); - } - - return new self((string) $user, is_object($user) ? ClassUtils::getRealClass($user) : ClassUtils::getRealClass($token)); - } - - /** - * Returns the username. - * - * @return string - */ - public function getUsername() - { - return $this->username; - } - - /** - * Returns the user's class name. - * - * @return string - */ - public function getClass() - { - return $this->class; - } - - /** - * {@inheritdoc} - */ - public function equals(SecurityIdentityInterface $sid) - { - if (!$sid instanceof self) { - return false; - } - - return $this->username === $sid->getUsername() - && $this->class === $sid->getClass(); - } - - /** - * A textual representation of this security identity. - * - * This is not used for equality comparison, but only for debugging. - * - * @return string - */ - public function __toString() - { - return sprintf('UserSecurityIdentity(%s, %s)', $this->username, $this->class); - } -} diff --git a/Acl/Exception/AclAlreadyExistsException.php b/Acl/Exception/AclAlreadyExistsException.php deleted file mode 100644 index 512da7f..0000000 --- a/Acl/Exception/AclAlreadyExistsException.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown when someone tries to create an ACL for an object - * identity that already has one. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class AclAlreadyExistsException extends Exception -{ -} diff --git a/Acl/Exception/AclNotFoundException.php b/Acl/Exception/AclNotFoundException.php deleted file mode 100644 index bd66c00..0000000 --- a/Acl/Exception/AclNotFoundException.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown when we cannot locate an ACL for a passed - * ObjectIdentity implementation. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class AclNotFoundException extends Exception -{ -} diff --git a/Acl/Exception/ConcurrentModificationException.php b/Acl/Exception/ConcurrentModificationException.php deleted file mode 100644 index a527d9c..0000000 --- a/Acl/Exception/ConcurrentModificationException.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown whenever you change shared properties of more than - * one ACL of the same class type concurrently. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class ConcurrentModificationException extends Exception -{ -} diff --git a/Acl/Exception/Exception.php b/Acl/Exception/Exception.php deleted file mode 100644 index f1e1001..0000000 --- a/Acl/Exception/Exception.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * Base ACL exception. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class Exception extends \RuntimeException -{ -} diff --git a/Acl/Exception/InvalidDomainObjectException.php b/Acl/Exception/InvalidDomainObjectException.php deleted file mode 100644 index fc1a646..0000000 --- a/Acl/Exception/InvalidDomainObjectException.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown when ObjectIdentity fails to construct an object - * identity from the passed domain object. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class InvalidDomainObjectException extends Exception -{ -} diff --git a/Acl/Exception/NoAceFoundException.php b/Acl/Exception/NoAceFoundException.php deleted file mode 100644 index 4d194d9..0000000 --- a/Acl/Exception/NoAceFoundException.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown when we cannot locate an ACE that matches the - * combination of permission masks and security identities. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class NoAceFoundException extends Exception -{ - public function __construct() - { - parent::__construct('No applicable ACE was found.'); - } -} diff --git a/Acl/Exception/NotAllAclsFoundException.php b/Acl/Exception/NotAllAclsFoundException.php deleted file mode 100644 index a634382..0000000 --- a/Acl/Exception/NotAllAclsFoundException.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown when you have requested ACLs for multiple object - * identities, but the AclProvider implementation failed to find ACLs for all - * identities. - * - * This exception contains the partial result. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class NotAllAclsFoundException extends AclNotFoundException -{ - private $partialResult; - - /** - * Sets the partial result. - * - * @param \SplObjectStorage $result - */ - public function setPartialResult(\SplObjectStorage $result) - { - $this->partialResult = $result; - } - - /** - * Returns the partial result. - * - * @return \SplObjectStorage - */ - public function getPartialResult() - { - return $this->partialResult; - } -} diff --git a/Acl/Exception/SidNotLoadedException.php b/Acl/Exception/SidNotLoadedException.php deleted file mode 100644 index cb8c4cc..0000000 --- a/Acl/Exception/SidNotLoadedException.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Exception; - -/** - * This exception is thrown when ACEs for an SID are requested which has not - * been loaded from the database. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class SidNotLoadedException extends Exception -{ -} diff --git a/Acl/Model/AclCacheInterface.php b/Acl/Model/AclCacheInterface.php deleted file mode 100644 index 1e74585..0000000 --- a/Acl/Model/AclCacheInterface.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * AclCache Interface. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface AclCacheInterface -{ - /** - * Removes an ACL from the cache. - * - * @param string $primaryKey a serialized primary key - */ - public function evictFromCacheById($primaryKey); - - /** - * Removes an ACL from the cache. - * - * The ACL which is returned, must reference the passed object identity. - * - * @param ObjectIdentityInterface $oid - */ - public function evictFromCacheByIdentity(ObjectIdentityInterface $oid); - - /** - * Retrieves an ACL for the given object identity primary key from the cache. - * - * @param int $primaryKey - * - * @return AclInterface - */ - public function getFromCacheById($primaryKey); - - /** - * Retrieves an ACL for the given object identity from the cache. - * - * @param ObjectIdentityInterface $oid - * - * @return AclInterface - */ - public function getFromCacheByIdentity(ObjectIdentityInterface $oid); - - /** - * Stores a new ACL in the cache. - * - * @param AclInterface $acl - */ - public function putInCache(AclInterface $acl); - - /** - * Removes all ACLs from the cache. - */ - public function clearCache(); -} diff --git a/Acl/Model/AclInterface.php b/Acl/Model/AclInterface.php deleted file mode 100644 index 13a6cf8..0000000 --- a/Acl/Model/AclInterface.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -use Symfony\Component\Security\Acl\Exception\NoAceFoundException; - -/** - * This interface represents an access control list (ACL) for a domain object. - * Each domain object can have exactly one associated ACL. - * - * An ACL contains all access control entries (ACE) for a given domain object. - * In order to avoid needing references to the domain object itself, implementations - * use ObjectIdentity implementations as an additional level of indirection. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface AclInterface extends \Serializable -{ - /** - * Returns all class-based ACEs associated with this ACL. - * - * @return array - */ - public function getClassAces(); - - /** - * Returns all class-field-based ACEs associated with this ACL. - * - * @param string $field - * - * @return array - */ - public function getClassFieldAces($field); - - /** - * Returns all object-based ACEs associated with this ACL. - * - * @return array - */ - public function getObjectAces(); - - /** - * Returns all object-field-based ACEs associated with this ACL. - * - * @param string $field - * - * @return array - */ - public function getObjectFieldAces($field); - - /** - * Returns the object identity associated with this ACL. - * - * @return ObjectIdentityInterface - */ - public function getObjectIdentity(); - - /** - * Returns the parent ACL, or null if there is none. - * - * @return AclInterface|null - */ - public function getParentAcl(); - - /** - * Whether this ACL is inheriting ACEs from a parent ACL. - * - * @return bool - */ - public function isEntriesInheriting(); - - /** - * Determines whether field access is granted. - * - * @param string $field - * @param array $masks - * @param array $securityIdentities - * @param bool $administrativeMode - * - * @return bool - */ - public function isFieldGranted($field, array $masks, array $securityIdentities, $administrativeMode = false); - - /** - * Determines whether access is granted. - * - * @param array $masks - * @param array $securityIdentities - * @param bool $administrativeMode - * - * @return bool - * - * @throws NoAceFoundException when no ACE was applicable for this request - */ - public function isGranted(array $masks, array $securityIdentities, $administrativeMode = false); - - /** - * Whether the ACL has loaded ACEs for all of the passed security identities. - * - * @param mixed $securityIdentities an implementation of SecurityIdentityInterface, or an array thereof - * - * @return bool - */ - public function isSidLoaded($securityIdentities); -} diff --git a/Acl/Model/AclProviderInterface.php b/Acl/Model/AclProviderInterface.php deleted file mode 100644 index f9b41cb..0000000 --- a/Acl/Model/AclProviderInterface.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -use Symfony\Component\Security\Acl\Exception\AclNotFoundException; - -/** - * Provides a common interface for retrieving ACLs. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface AclProviderInterface -{ - /** - * Retrieves all child object identities from the database. - * - * @param ObjectIdentityInterface $parentOid - * @param bool $directChildrenOnly - * - * @return array returns an array of child 'ObjectIdentity's - */ - public function findChildren(ObjectIdentityInterface $parentOid, $directChildrenOnly = false); - - /** - * Returns the ACL that belongs to the given object identity. - * - * @param ObjectIdentityInterface $oid - * @param SecurityIdentityInterface[] $sids - * - * @return AclInterface - * - * @throws AclNotFoundException when there is no ACL - */ - public function findAcl(ObjectIdentityInterface $oid, array $sids = array()); - - /** - * Returns the ACLs that belong to the given object identities. - * - * @param ObjectIdentityInterface[] $oids an array of ObjectIdentityInterface implementations - * @param SecurityIdentityInterface[] $sids an array of SecurityIdentityInterface implementations - * - * @return \SplObjectStorage mapping the passed object identities to ACLs - * - * @throws AclNotFoundException when we cannot find an ACL for all identities - */ - public function findAcls(array $oids, array $sids = array()); -} diff --git a/Acl/Model/AuditLoggerInterface.php b/Acl/Model/AuditLoggerInterface.php deleted file mode 100644 index fde4de6..0000000 --- a/Acl/Model/AuditLoggerInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * Interface for audit loggers. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface AuditLoggerInterface -{ - /** - * This method is called whenever access is granted, or denied, and - * administrative mode is turned off. - * - * @param bool $granted - * @param EntryInterface $ace - */ - public function logIfNeeded($granted, EntryInterface $ace); -} diff --git a/Acl/Model/AuditableAclInterface.php b/Acl/Model/AuditableAclInterface.php deleted file mode 100644 index e7a60e5..0000000 --- a/Acl/Model/AuditableAclInterface.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * This interface adds auditing capabilities to the ACL. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface AuditableAclInterface extends MutableAclInterface -{ - /** - * Updates auditing for class-based ACE. - * - * @param int $index - * @param bool $auditSuccess - * @param bool $auditFailure - */ - public function updateClassAuditing($index, $auditSuccess, $auditFailure); - - /** - * Updates auditing for class-field-based ACE. - * - * @param int $index - * @param string $field - * @param bool $auditSuccess - * @param bool $auditFailure - */ - public function updateClassFieldAuditing($index, $field, $auditSuccess, $auditFailure); - - /** - * Updates auditing for object-based ACE. - * - * @param int $index - * @param bool $auditSuccess - * @param bool $auditFailure - */ - public function updateObjectAuditing($index, $auditSuccess, $auditFailure); - - /** - * Updates auditing for object-field-based ACE. - * - * @param int $index - * @param string $field - * @param bool $auditSuccess - * @param bool $auditFailure - */ - public function updateObjectFieldAuditing($index, $field, $auditSuccess, $auditFailure); -} diff --git a/Acl/Model/AuditableEntryInterface.php b/Acl/Model/AuditableEntryInterface.php deleted file mode 100644 index 9561577..0000000 --- a/Acl/Model/AuditableEntryInterface.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * ACEs can implement this interface if they support auditing capabilities. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface AuditableEntryInterface extends EntryInterface -{ - /** - * Whether auditing for successful grants is turned on. - * - * @return bool - */ - public function isAuditFailure(); - - /** - * Whether auditing for successful denies is turned on. - * - * @return bool - */ - public function isAuditSuccess(); -} diff --git a/Acl/Model/DomainObjectInterface.php b/Acl/Model/DomainObjectInterface.php deleted file mode 100644 index 195cb4e..0000000 --- a/Acl/Model/DomainObjectInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * This method can be implemented by domain objects which you want to store - * ACLs for if they do not have a getId() method, or getId() does not return - * a unique identifier. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface DomainObjectInterface -{ - /** - * Returns a unique identifier for this domain object. - * - * @return string - */ - public function getObjectIdentifier(); -} diff --git a/Acl/Model/EntryInterface.php b/Acl/Model/EntryInterface.php deleted file mode 100644 index 0b244b7..0000000 --- a/Acl/Model/EntryInterface.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * This class represents an individual entry in the ACL list. - * - * Instances MUST be immutable, as they are returned by the ACL and should not - * allow client modification. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface EntryInterface extends \Serializable -{ - /** - * The ACL this ACE is associated with. - * - * @return AclInterface - */ - public function getAcl(); - - /** - * The primary key of this ACE. - * - * @return int - */ - public function getId(); - - /** - * The permission mask of this ACE. - * - * @return int - */ - public function getMask(); - - /** - * The security identity associated with this ACE. - * - * @return SecurityIdentityInterface - */ - public function getSecurityIdentity(); - - /** - * The strategy for comparing masks. - * - * @return string - */ - public function getStrategy(); - - /** - * Returns whether this ACE is granting, or denying. - * - * @return bool - */ - public function isGranting(); -} diff --git a/Acl/Model/FieldEntryInterface.php b/Acl/Model/FieldEntryInterface.php deleted file mode 100644 index ae2f808..0000000 --- a/Acl/Model/FieldEntryInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * Interface for entries which are restricted to specific fields. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface FieldEntryInterface extends EntryInterface -{ - /** - * Returns the field used for this entry. - * - * @return string - */ - public function getField(); -} diff --git a/Acl/Model/MutableAclInterface.php b/Acl/Model/MutableAclInterface.php deleted file mode 100644 index 2ba7bd5..0000000 --- a/Acl/Model/MutableAclInterface.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * This interface adds mutators for the AclInterface. - * - * All changes to Access Control Entries must go through this interface. Access - * Control Entries must never be modified directly. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface MutableAclInterface extends AclInterface -{ - /** - * Deletes a class-based ACE. - * - * @param int $index - */ - public function deleteClassAce($index); - - /** - * Deletes a class-field-based ACE. - * - * @param int $index - * @param string $field - */ - public function deleteClassFieldAce($index, $field); - - /** - * Deletes an object-based ACE. - * - * @param int $index - */ - public function deleteObjectAce($index); - - /** - * Deletes an object-field-based ACE. - * - * @param int $index - * @param string $field - */ - public function deleteObjectFieldAce($index, $field); - - /** - * Returns the primary key of this ACL. - * - * @return int - */ - public function getId(); - - /** - * Inserts a class-based ACE. - * - * @param SecurityIdentityInterface $sid - * @param int $mask - * @param int $index - * @param bool $granting - * @param string $strategy - */ - public function insertClassAce(SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null); - - /** - * Inserts a class-field-based ACE. - * - * @param string $field - * @param SecurityIdentityInterface $sid - * @param int $mask - * @param int $index - * @param bool $granting - * @param string $strategy - */ - public function insertClassFieldAce($field, SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null); - - /** - * Inserts an object-based ACE. - * - * @param SecurityIdentityInterface $sid - * @param int $mask - * @param int $index - * @param bool $granting - * @param string $strategy - */ - public function insertObjectAce(SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null); - - /** - * Inserts an object-field-based ACE. - * - * @param string $field - * @param SecurityIdentityInterface $sid - * @param int $mask - * @param int $index - * @param bool $granting - * @param string $strategy - */ - public function insertObjectFieldAce($field, SecurityIdentityInterface $sid, $mask, $index = 0, $granting = true, $strategy = null); - - /** - * Sets whether entries are inherited. - * - * @param bool $boolean - */ - public function setEntriesInheriting($boolean); - - /** - * Sets the parent ACL. - * - * @param AclInterface|null $acl - */ - public function setParentAcl(AclInterface $acl = null); - - /** - * Updates a class-based ACE. - * - * @param int $index - * @param int $mask - * @param string $strategy if null the strategy should not be changed - */ - public function updateClassAce($index, $mask, $strategy = null); - - /** - * Updates a class-field-based ACE. - * - * @param int $index - * @param string $field - * @param int $mask - * @param string $strategy if null the strategy should not be changed - */ - public function updateClassFieldAce($index, $field, $mask, $strategy = null); - - /** - * Updates an object-based ACE. - * - * @param int $index - * @param int $mask - * @param string $strategy if null the strategy should not be changed - */ - public function updateObjectAce($index, $mask, $strategy = null); - - /** - * Updates an object-field-based ACE. - * - * @param int $index - * @param string $field - * @param int $mask - * @param string $strategy if null the strategy should not be changed - */ - public function updateObjectFieldAce($index, $field, $mask, $strategy = null); -} diff --git a/Acl/Model/MutableAclProviderInterface.php b/Acl/Model/MutableAclProviderInterface.php deleted file mode 100644 index ee6d7c4..0000000 --- a/Acl/Model/MutableAclProviderInterface.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException; - -/** - * Provides support for creating and storing ACL instances. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface MutableAclProviderInterface extends AclProviderInterface -{ - /** - * Creates a new ACL for the given object identity. - * - * @param ObjectIdentityInterface $oid - * - * @return MutableAclInterface - * - * @throws AclAlreadyExistsException when there already is an ACL for the given - * object identity - */ - public function createAcl(ObjectIdentityInterface $oid); - - /** - * Deletes the ACL for a given object identity. - * - * This will automatically trigger a delete for any child ACLs. If you don't - * want child ACLs to be deleted, you will have to set their parent ACL to null. - * - * @param ObjectIdentityInterface $oid - */ - public function deleteAcl(ObjectIdentityInterface $oid); - - /** - * Persists any changes which were made to the ACL, or any associated - * access control entries. - * - * Changes to parent ACLs are not persisted. - * - * @param MutableAclInterface $acl - */ - public function updateAcl(MutableAclInterface $acl); -} diff --git a/Acl/Model/ObjectIdentityInterface.php b/Acl/Model/ObjectIdentityInterface.php deleted file mode 100644 index 6574b49..0000000 --- a/Acl/Model/ObjectIdentityInterface.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * Represents the identity of an individual domain object instance. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface ObjectIdentityInterface -{ - /** - * We specifically require this method so we can check for object equality - * explicitly, and do not have to rely on referencial equality instead. - * - * Though in most cases, both checks should result in the same outcome. - * - * Referential Equality: $object1 === $object2 - * Example for Object Equality: $object1->getId() === $object2->getId() - * - * @param ObjectIdentityInterface $identity - * - * @return bool - */ - public function equals(ObjectIdentityInterface $identity); - - /** - * Obtains a unique identifier for this object. The identifier must not be - * re-used for other objects with the same type. - * - * @return string cannot return null - */ - public function getIdentifier(); - - /** - * Returns a type for the domain object. Typically, this is the PHP class name. - * - * @return string cannot return null - */ - public function getType(); -} diff --git a/Acl/Model/ObjectIdentityRetrievalStrategyInterface.php b/Acl/Model/ObjectIdentityRetrievalStrategyInterface.php deleted file mode 100644 index 542066a..0000000 --- a/Acl/Model/ObjectIdentityRetrievalStrategyInterface.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * Retrieves the object identity for a given domain object. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface ObjectIdentityRetrievalStrategyInterface -{ - /** - * Retrieves the object identity from a domain object. - * - * @param object $domainObject - * - * @return ObjectIdentityInterface - */ - public function getObjectIdentity($domainObject); -} diff --git a/Acl/Model/PermissionGrantingStrategyInterface.php b/Acl/Model/PermissionGrantingStrategyInterface.php deleted file mode 100644 index fa3430d..0000000 --- a/Acl/Model/PermissionGrantingStrategyInterface.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * Interface used by permission granting implementations. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface PermissionGrantingStrategyInterface -{ - /** - * Determines whether access to a domain object is to be granted. - * - * @param AclInterface $acl - * @param array $masks - * @param array $sids - * @param bool $administrativeMode - * - * @return bool - */ - public function isGranted(AclInterface $acl, array $masks, array $sids, $administrativeMode = false); - - /** - * Determines whether access to a domain object's field is to be granted. - * - * @param AclInterface $acl - * @param string $field - * @param array $masks - * @param array $sids - * @param bool $administrativeMode - * - * @return bool - */ - public function isFieldGranted(AclInterface $acl, $field, array $masks, array $sids, $administrativeMode = false); -} diff --git a/Acl/Model/SecurityIdentityInterface.php b/Acl/Model/SecurityIdentityInterface.php deleted file mode 100644 index 0a24a54..0000000 --- a/Acl/Model/SecurityIdentityInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -/** - * This interface provides an additional level of indirection, so that - * we can work with abstracted versions of security objects and do - * not have to save the entire objects. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface SecurityIdentityInterface -{ - /** - * This method is used to compare two security identities in order to - * not rely on referential equality. - * - * @param SecurityIdentityInterface $identity - */ - public function equals(SecurityIdentityInterface $identity); -} diff --git a/Acl/Model/SecurityIdentityRetrievalStrategyInterface.php b/Acl/Model/SecurityIdentityRetrievalStrategyInterface.php deleted file mode 100644 index b5fcb75..0000000 --- a/Acl/Model/SecurityIdentityRetrievalStrategyInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Model; - -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - -/** - * Interface for retrieving security identities from tokens. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface SecurityIdentityRetrievalStrategyInterface -{ - /** - * Retrieves the available security identities for the given token. - * - * The order in which the security identities are returned is significant. - * Typically, security identities should be ordered from most specific to - * least specific. - * - * @param TokenInterface $token - * - * @return SecurityIdentityInterface[] An array of SecurityIdentityInterface implementations - */ - public function getSecurityIdentities(TokenInterface $token); -} diff --git a/Acl/Permission/AbstractMaskBuilder.php b/Acl/Permission/AbstractMaskBuilder.php deleted file mode 100644 index 93f1755..0000000 --- a/Acl/Permission/AbstractMaskBuilder.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Permission; - -/** - * This abstract class implements nearly all the MaskBuilderInterface methods. - */ -abstract class AbstractMaskBuilder implements MaskBuilderInterface -{ - /** - * @var int - */ - protected $mask; - - /** - * Constructor. - * - * @param int $mask optional; defaults to 0 - */ - public function __construct($mask = 0) - { - $this->set($mask); - } - - /** - * {@inheritdoc} - */ - public function set($mask) - { - if (!is_int($mask)) { - throw new \InvalidArgumentException('$mask must be an integer.'); - } - - $this->mask = $mask; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function get() - { - return $this->mask; - } - - /** - * {@inheritdoc} - */ - public function add($mask) - { - $this->mask |= $this->resolveMask($mask); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function remove($mask) - { - $this->mask &= ~$this->resolveMask($mask); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function reset() - { - $this->mask = 0; - - return $this; - } -} diff --git a/Acl/Permission/BasicPermissionMap.php b/Acl/Permission/BasicPermissionMap.php deleted file mode 100644 index fa5437d..0000000 --- a/Acl/Permission/BasicPermissionMap.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Permission; - -/** - * This is basic permission map complements the masks which have been defined - * on the standard implementation of the MaskBuilder. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class BasicPermissionMap implements PermissionMapInterface, MaskBuilderRetrievalInterface -{ - const PERMISSION_VIEW = 'VIEW'; - const PERMISSION_EDIT = 'EDIT'; - const PERMISSION_CREATE = 'CREATE'; - const PERMISSION_DELETE = 'DELETE'; - const PERMISSION_UNDELETE = 'UNDELETE'; - const PERMISSION_OPERATOR = 'OPERATOR'; - const PERMISSION_MASTER = 'MASTER'; - const PERMISSION_OWNER = 'OWNER'; - - protected $map; - - public function __construct() - { - $this->map = array( - self::PERMISSION_VIEW => array( - MaskBuilder::MASK_VIEW, - MaskBuilder::MASK_EDIT, - MaskBuilder::MASK_OPERATOR, - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_EDIT => array( - MaskBuilder::MASK_EDIT, - MaskBuilder::MASK_OPERATOR, - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_CREATE => array( - MaskBuilder::MASK_CREATE, - MaskBuilder::MASK_OPERATOR, - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_DELETE => array( - MaskBuilder::MASK_DELETE, - MaskBuilder::MASK_OPERATOR, - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_UNDELETE => array( - MaskBuilder::MASK_UNDELETE, - MaskBuilder::MASK_OPERATOR, - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_OPERATOR => array( - MaskBuilder::MASK_OPERATOR, - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_MASTER => array( - MaskBuilder::MASK_MASTER, - MaskBuilder::MASK_OWNER, - ), - - self::PERMISSION_OWNER => array( - MaskBuilder::MASK_OWNER, - ), - ); - } - - /** - * {@inheritdoc} - */ - public function getMasks($permission, $object) - { - if (!isset($this->map[$permission])) { - return; - } - - return $this->map[$permission]; - } - - /** - * {@inheritdoc} - */ - public function contains($permission) - { - return isset($this->map[$permission]); - } - - /** - * {@inheritdoc} - */ - public function getMaskBuilder() - { - return new MaskBuilder(); - } -} diff --git a/Acl/Permission/MaskBuilder.php b/Acl/Permission/MaskBuilder.php deleted file mode 100644 index ed13ecb..0000000 --- a/Acl/Permission/MaskBuilder.php +++ /dev/null @@ -1,151 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Permission; - -/** - * This class allows you to build cumulative permissions easily, or convert - * masks to a human-readable format. - * - * <code> - * $builder = new MaskBuilder(); - * $builder - * ->add('view') - * ->add('create') - * ->add('edit') - * ; - * var_dump($builder->get()); // int(7) - * var_dump($builder->getPattern()); // string(32) ".............................ECV" - * </code> - * - * We have defined some commonly used base permissions which you can use: - * - VIEW: the SID is allowed to view the domain object / field - * - CREATE: the SID is allowed to create new instances of the domain object / fields - * - EDIT: the SID is allowed to edit existing instances of the domain object / field - * - DELETE: the SID is allowed to delete domain objects - * - UNDELETE: the SID is allowed to recover domain objects from trash - * - OPERATOR: the SID is allowed to perform any action on the domain object - * except for granting others permissions - * - MASTER: the SID is allowed to perform any action on the domain object, - * and is allowed to grant other SIDs any permission except for - * MASTER and OWNER permissions - * - OWNER: the SID is owning the domain object in question and can perform any - * action on the domain object as well as grant any permission - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class MaskBuilder extends AbstractMaskBuilder -{ - const MASK_VIEW = 1; // 1 << 0 - const MASK_CREATE = 2; // 1 << 1 - const MASK_EDIT = 4; // 1 << 2 - const MASK_DELETE = 8; // 1 << 3 - const MASK_UNDELETE = 16; // 1 << 4 - const MASK_OPERATOR = 32; // 1 << 5 - const MASK_MASTER = 64; // 1 << 6 - const MASK_OWNER = 128; // 1 << 7 - const MASK_IDDQD = 1073741823; // 1 << 0 | 1 << 1 | ... | 1 << 30 - - const CODE_VIEW = 'V'; - const CODE_CREATE = 'C'; - const CODE_EDIT = 'E'; - const CODE_DELETE = 'D'; - const CODE_UNDELETE = 'U'; - const CODE_OPERATOR = 'O'; - const CODE_MASTER = 'M'; - const CODE_OWNER = 'N'; - - const ALL_OFF = '................................'; - const OFF = '.'; - const ON = '*'; - - /** - * Returns a human-readable representation of the permission. - * - * @return string - */ - public function getPattern() - { - $pattern = self::ALL_OFF; - $length = strlen($pattern); - $bitmask = str_pad(decbin($this->mask), $length, '0', STR_PAD_LEFT); - - for ($i = $length - 1; $i >= 0; --$i) { - if ('1' === $bitmask[$i]) { - try { - $pattern[$i] = self::getCode(1 << ($length - $i - 1)); - } catch (\Exception $e) { - $pattern[$i] = self::ON; - } - } - } - - return $pattern; - } - - /** - * Returns the code for the passed mask. - * - * @param int $mask - * - * @return string - * - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public static function getCode($mask) - { - if (!is_int($mask)) { - throw new \InvalidArgumentException('$mask must be an integer.'); - } - - $reflection = new \ReflectionClass(get_called_class()); - foreach ($reflection->getConstants() as $name => $cMask) { - if (0 !== strpos($name, 'MASK_') || $mask !== $cMask) { - continue; - } - - if (!defined($cName = 'static::CODE_'.substr($name, 5))) { - throw new \RuntimeException('There was no code defined for this mask.'); - } - - return constant($cName); - } - - throw new \InvalidArgumentException(sprintf('The mask "%d" is not supported.', $mask)); - } - - /** - * Returns the mask for the passed code. - * - * @param mixed $code - * - * @return int - * - * @throws \InvalidArgumentException - */ - public function resolveMask($code) - { - if (is_string($code)) { - if (!defined($name = sprintf('static::MASK_%s', strtoupper($code)))) { - throw new \InvalidArgumentException(sprintf('The code "%s" is not supported', $code)); - } - - return constant($name); - } - - if (!is_int($code)) { - throw new \InvalidArgumentException('$code must be an integer.'); - } - - return $code; - } -} diff --git a/Acl/Permission/MaskBuilderInterface.php b/Acl/Permission/MaskBuilderInterface.php deleted file mode 100644 index ef183de..0000000 --- a/Acl/Permission/MaskBuilderInterface.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Permission; - -/** - * This is the interface that must be implemented by mask builders. - */ -interface MaskBuilderInterface -{ - /** - * Set the mask of this permission. - * - * @param int $mask - * - * @return MaskBuilderInterface - * - * @throws \InvalidArgumentException if $mask is not an integer - */ - public function set($mask); - - /** - * Returns the mask of this permission. - * - * @return int - */ - public function get(); - - /** - * Adds a mask to the permission. - * - * @param mixed $mask - * - * @return MaskBuilderInterface - * - * @throws \InvalidArgumentException - */ - public function add($mask); - - /** - * Removes a mask from the permission. - * - * @param mixed $mask - * - * @return MaskBuilderInterface - * - * @throws \InvalidArgumentException - */ - public function remove($mask); - - /** - * Resets the PermissionBuilder. - * - * @return MaskBuilderInterface - */ - public function reset(); - - /** - * Returns the mask for the passed code. - * - * @param mixed $code - * - * @return int - * - * @throws \InvalidArgumentException - */ - public function resolveMask($code); -} diff --git a/Acl/Permission/MaskBuilderRetrievalInterface.php b/Acl/Permission/MaskBuilderRetrievalInterface.php deleted file mode 100644 index 2cde262..0000000 --- a/Acl/Permission/MaskBuilderRetrievalInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Permission; - -/** - * Retrieves the MaskBuilder. - */ -interface MaskBuilderRetrievalInterface -{ - /** - * Returns a new instance of the MaskBuilder used in the permissionMap. - * - * @return MaskBuilderInterface - */ - public function getMaskBuilder(); -} diff --git a/Acl/Permission/PermissionMapInterface.php b/Acl/Permission/PermissionMapInterface.php deleted file mode 100644 index 0b2f1ce..0000000 --- a/Acl/Permission/PermissionMapInterface.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Permission; - -/** - * This is the interface that must be implemented by permission maps. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -interface PermissionMapInterface -{ - /** - * Returns an array of bitmasks. - * - * The security identity must have been granted access to at least one of - * these bitmasks. - * - * @param string $permission - * @param object $object - * - * @return array may return null if permission/object combination is not supported - */ - public function getMasks($permission, $object); - - /** - * Whether this map contains the given permission. - * - * @param string $permission - * - * @return bool - */ - public function contains($permission); -} diff --git a/Acl/Resources/bin/generateSql.php b/Acl/Resources/bin/generateSql.php deleted file mode 100644 index 4b1b38d..0000000 --- a/Acl/Resources/bin/generateSql.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -require_once __DIR__.'/../../../../ClassLoader/ClassLoader.php'; - -use Symfony\Component\ClassLoader\ClassLoader; -use Symfony\Component\Finder\Finder; -use Symfony\Component\Security\Acl\Dbal\Schema; - -$loader = new ClassLoader(); -$loader->addPrefixes(array( - 'Symfony' => __DIR__.'/../../../../../..', - 'Doctrine\\Common' => __DIR__.'/../../../../../../../vendor/doctrine-common/lib', - 'Doctrine\\DBAL\\Migrations' => __DIR__.'/../../../../../../../vendor/doctrine-migrations/lib', - 'Doctrine\\DBAL' => __DIR__.'/../../../../../../../vendor/doctrine/dbal/lib', - 'Doctrine' => __DIR__.'/../../../../../../../vendor/doctrine/lib', -)); -$loader->register(); - -$schema = new Schema(array( - 'class_table_name' => 'acl_classes', - 'entry_table_name' => 'acl_entries', - 'oid_table_name' => 'acl_object_identities', - 'oid_ancestors_table_name' => 'acl_object_identity_ancestors', - 'sid_table_name' => 'acl_security_identities', -)); - -$reflection = new ReflectionClass('Doctrine\\DBAL\\Platforms\\AbstractPlatform'); -$finder = new Finder(); -$finder->name('*Platform.php')->in(dirname($reflection->getFileName())); -foreach ($finder as $file) { - require_once $file->getPathName(); - $className = 'Doctrine\\DBAL\\Platforms\\'.$file->getBasename('.php'); - - $reflection = new ReflectionClass($className); - if ($reflection->isAbstract()) { - continue; - } - - $platform = $reflection->newInstance(); - $targetFile = sprintf(__DIR__.'/../schema/%s.sql', $platform->getName()); - file_put_contents($targetFile, implode("\n\n", $schema->toSql($platform))); -} diff --git a/Acl/Resources/schema/db2.sql b/Acl/Resources/schema/db2.sql deleted file mode 100644 index 2d10c14..0000000 --- a/Acl/Resources/schema/db2.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE acl_classes (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) - -CREATE TABLE acl_security_identities (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, identifier VARCHAR(200) NOT NULL, username SMALLINT NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) - -CREATE TABLE acl_object_identities (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, parent_object_identity_id INTEGER DEFAULT NULL, class_id INTEGER NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting SMALLINT NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) - -CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id) - -CREATE TABLE acl_object_identity_ancestors (object_identity_id INTEGER NOT NULL, ancestor_id INTEGER NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id)) - -CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id) - -CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id) - -CREATE TABLE acl_entries (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, class_id INTEGER NOT NULL, object_identity_id INTEGER DEFAULT NULL, security_identity_id INTEGER NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order SMALLINT NOT NULL, mask INTEGER NOT NULL, granting SMALLINT NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success SMALLINT NOT NULL, audit_failure SMALLINT NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) - -CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id) - -CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id) - -CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id) - -CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id) - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
\ No newline at end of file diff --git a/Acl/Resources/schema/drizzle.sql b/Acl/Resources/schema/drizzle.sql deleted file mode 100644 index 9398c29..0000000 --- a/Acl/Resources/schema/drizzle.sql +++ /dev/null @@ -1,21 +0,0 @@ -CREATE TABLE acl_classes (id INT AUTO_INCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL, UNIQUE INDEX UNIQ_69DD750638A36066 (class_type), PRIMARY KEY(id)) COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_security_identities (id INT AUTO_INCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL, UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 (identifier, username), PRIMARY KEY(id)) COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_object_identities (id INT AUTO_INCREMENT NOT NULL, parent_object_identity_id INT DEFAULT NULL, class_id INT NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BOOLEAN NOT NULL, UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 (object_identifier, class_id), INDEX IDX_9407E54977FA751A (parent_object_identity_id), PRIMARY KEY(id)) COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, INDEX IDX_825DE2993D9AB4A6 (object_identity_id), INDEX IDX_825DE299C671CEA1 (ancestor_id), PRIMARY KEY(object_identity_id, ancestor_id)) COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_entries (id INT AUTO_INCREMENT NOT NULL, class_id INT NOT NULL, object_identity_id INT DEFAULT NULL, security_identity_id INT NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order INT NOT NULL, mask INT NOT NULL, granting BOOLEAN NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BOOLEAN NOT NULL, audit_failure BOOLEAN NOT NULL, UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 (class_id, object_identity_id, field_name, ace_order), INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 (class_id, object_identity_id, security_identity_id), INDEX IDX_46C8B806EA000B10 (class_id), INDEX IDX_46C8B8063D9AB4A6 (object_identity_id), INDEX IDX_46C8B806DF9183C9 (security_identity_id), PRIMARY KEY(id)) COLLATE utf8_unicode_ci ENGINE = InnoDB - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
\ No newline at end of file diff --git a/Acl/Resources/schema/mssql.sql b/Acl/Resources/schema/mssql.sql deleted file mode 100644 index 8126f78..0000000 --- a/Acl/Resources/schema/mssql.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE acl_classes (id INT IDENTITY NOT NULL, class_type NVARCHAR(200) NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) WHERE class_type IS NOT NULL - -CREATE TABLE acl_security_identities (id INT IDENTITY NOT NULL, identifier NVARCHAR(200) NOT NULL, username BIT NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) WHERE identifier IS NOT NULL AND username IS NOT NULL - -CREATE TABLE acl_object_identities (id INT IDENTITY NOT NULL, parent_object_identity_id INT, class_id INT NOT NULL, object_identifier NVARCHAR(100) NOT NULL, entries_inheriting BIT NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) WHERE object_identifier IS NOT NULL AND class_id IS NOT NULL - -CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id) - -CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, PRIMARY KEY (object_identity_id, ancestor_id)) - -CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id) - -CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id) - -CREATE TABLE acl_entries (id INT IDENTITY NOT NULL, class_id INT NOT NULL, object_identity_id INT, security_identity_id INT NOT NULL, field_name NVARCHAR(50), ace_order SMALLINT NOT NULL, mask INT NOT NULL, granting BIT NOT NULL, granting_strategy NVARCHAR(30) NOT NULL, audit_success BIT NOT NULL, audit_failure BIT NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) WHERE class_id IS NOT NULL AND object_identity_id IS NOT NULL AND field_name IS NOT NULL AND ace_order IS NOT NULL - -CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id) - -CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id) - -CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id) - -CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id) - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
\ No newline at end of file diff --git a/Acl/Resources/schema/mysql.sql b/Acl/Resources/schema/mysql.sql deleted file mode 100644 index 1c63f4d..0000000 --- a/Acl/Resources/schema/mysql.sql +++ /dev/null @@ -1,21 +0,0 @@ -CREATE TABLE acl_classes (id INT UNSIGNED AUTO_INCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL, UNIQUE INDEX UNIQ_69DD750638A36066 (class_type), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_security_identities (id INT UNSIGNED AUTO_INCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 (identifier, username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_object_identities (id INT UNSIGNED AUTO_INCREMENT NOT NULL, parent_object_identity_id INT UNSIGNED DEFAULT NULL, class_id INT UNSIGNED NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 (object_identifier, class_id), INDEX IDX_9407E54977FA751A (parent_object_identity_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_object_identity_ancestors (object_identity_id INT UNSIGNED NOT NULL, ancestor_id INT UNSIGNED NOT NULL, INDEX IDX_825DE2993D9AB4A6 (object_identity_id), INDEX IDX_825DE299C671CEA1 (ancestor_id), PRIMARY KEY(object_identity_id, ancestor_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB - -CREATE TABLE acl_entries (id INT UNSIGNED AUTO_INCREMENT NOT NULL, class_id INT UNSIGNED NOT NULL, object_identity_id INT UNSIGNED DEFAULT NULL, security_identity_id INT UNSIGNED NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order SMALLINT UNSIGNED NOT NULL, mask INT NOT NULL, granting TINYINT(1) NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success TINYINT(1) NOT NULL, audit_failure TINYINT(1) NOT NULL, UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 (class_id, object_identity_id, field_name, ace_order), INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 (class_id, object_identity_id, security_identity_id), INDEX IDX_46C8B806EA000B10 (class_id), INDEX IDX_46C8B8063D9AB4A6 (object_identity_id), INDEX IDX_46C8B806DF9183C9 (security_identity_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
\ No newline at end of file diff --git a/Acl/Resources/schema/oracle.sql b/Acl/Resources/schema/oracle.sql deleted file mode 100644 index 94821dc..0000000 --- a/Acl/Resources/schema/oracle.sql +++ /dev/null @@ -1,175 +0,0 @@ -CREATE TABLE acl_classes (id NUMBER(10) NOT NULL, class_type VARCHAR2(200) NOT NULL, PRIMARY KEY(id)) - -DECLARE - constraints_Count NUMBER; -BEGIN - SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_CLASSES' AND CONSTRAINT_TYPE = 'P'; - IF constraints_Count = 0 OR constraints_Count = '' THEN - EXECUTE IMMEDIATE 'ALTER TABLE ACL_CLASSES ADD CONSTRAINT ACL_CLASSES_AI_PK PRIMARY KEY (ID)'; - END IF; -END; - -CREATE SEQUENCE ACL_CLASSES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 - -CREATE TRIGGER ACL_CLASSES_AI_PK - BEFORE INSERT - ON ACL_CLASSES - FOR EACH ROW -DECLARE - last_Sequence NUMBER; - last_InsertID NUMBER; -BEGIN - SELECT ACL_CLASSES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN - SELECT ACL_CLASSES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - ELSE - SELECT NVL(Last_Number, 0) INTO last_Sequence - FROM User_Sequences - WHERE Sequence_Name = 'ACL_CLASSES_SEQ'; - SELECT :NEW.ID INTO last_InsertID FROM DUAL; - WHILE (last_InsertID > last_Sequence) LOOP - SELECT ACL_CLASSES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL; - END LOOP; - END IF; -END; - -CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) - -CREATE TABLE acl_security_identities (id NUMBER(10) NOT NULL, identifier VARCHAR2(200) NOT NULL, username NUMBER(1) NOT NULL, PRIMARY KEY(id)) - -DECLARE - constraints_Count NUMBER; -BEGIN - SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_SECURITY_IDENTITIES' AND CONSTRAINT_TYPE = 'P'; - IF constraints_Count = 0 OR constraints_Count = '' THEN - EXECUTE IMMEDIATE 'ALTER TABLE ACL_SECURITY_IDENTITIES ADD CONSTRAINT ACL_SECURITY_IDENTITIES_AI_PK PRIMARY KEY (ID)'; - END IF; -END; - -CREATE SEQUENCE ACL_SECURITY_IDENTITIES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 - -CREATE TRIGGER ACL_SECURITY_IDENTITIES_AI_PK - BEFORE INSERT - ON ACL_SECURITY_IDENTITIES - FOR EACH ROW -DECLARE - last_Sequence NUMBER; - last_InsertID NUMBER; -BEGIN - SELECT ACL_SECURITY_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN - SELECT ACL_SECURITY_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - ELSE - SELECT NVL(Last_Number, 0) INTO last_Sequence - FROM User_Sequences - WHERE Sequence_Name = 'ACL_SECURITY_IDENTITIES_SEQ'; - SELECT :NEW.ID INTO last_InsertID FROM DUAL; - WHILE (last_InsertID > last_Sequence) LOOP - SELECT ACL_SECURITY_IDENTITIES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL; - END LOOP; - END IF; -END; - -CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) - -CREATE TABLE acl_object_identities (id NUMBER(10) NOT NULL, parent_object_identity_id NUMBER(10) DEFAULT NULL NULL, class_id NUMBER(10) NOT NULL, object_identifier VARCHAR2(100) NOT NULL, entries_inheriting NUMBER(1) NOT NULL, PRIMARY KEY(id)) - -DECLARE - constraints_Count NUMBER; -BEGIN - SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_OBJECT_IDENTITIES' AND CONSTRAINT_TYPE = 'P'; - IF constraints_Count = 0 OR constraints_Count = '' THEN - EXECUTE IMMEDIATE 'ALTER TABLE ACL_OBJECT_IDENTITIES ADD CONSTRAINT ACL_OBJECT_IDENTITIES_AI_PK PRIMARY KEY (ID)'; - END IF; -END; - -CREATE SEQUENCE ACL_OBJECT_IDENTITIES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 - -CREATE TRIGGER ACL_OBJECT_IDENTITIES_AI_PK - BEFORE INSERT - ON ACL_OBJECT_IDENTITIES - FOR EACH ROW -DECLARE - last_Sequence NUMBER; - last_InsertID NUMBER; -BEGIN - SELECT ACL_OBJECT_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN - SELECT ACL_OBJECT_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - ELSE - SELECT NVL(Last_Number, 0) INTO last_Sequence - FROM User_Sequences - WHERE Sequence_Name = 'ACL_OBJECT_IDENTITIES_SEQ'; - SELECT :NEW.ID INTO last_InsertID FROM DUAL; - WHILE (last_InsertID > last_Sequence) LOOP - SELECT ACL_OBJECT_IDENTITIES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL; - END LOOP; - END IF; -END; - -CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) - -CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id) - -CREATE TABLE acl_object_identity_ancestors (object_identity_id NUMBER(10) NOT NULL, ancestor_id NUMBER(10) NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id)) - -CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id) - -CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id) - -CREATE TABLE acl_entries (id NUMBER(10) NOT NULL, class_id NUMBER(10) NOT NULL, object_identity_id NUMBER(10) DEFAULT NULL NULL, security_identity_id NUMBER(10) NOT NULL, field_name VARCHAR2(50) DEFAULT NULL NULL, ace_order NUMBER(5) NOT NULL, mask NUMBER(10) NOT NULL, granting NUMBER(1) NOT NULL, granting_strategy VARCHAR2(30) NOT NULL, audit_success NUMBER(1) NOT NULL, audit_failure NUMBER(1) NOT NULL, PRIMARY KEY(id)) - -DECLARE - constraints_Count NUMBER; -BEGIN - SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_ENTRIES' AND CONSTRAINT_TYPE = 'P'; - IF constraints_Count = 0 OR constraints_Count = '' THEN - EXECUTE IMMEDIATE 'ALTER TABLE ACL_ENTRIES ADD CONSTRAINT ACL_ENTRIES_AI_PK PRIMARY KEY (ID)'; - END IF; -END; - -CREATE SEQUENCE ACL_ENTRIES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1 - -CREATE TRIGGER ACL_ENTRIES_AI_PK - BEFORE INSERT - ON ACL_ENTRIES - FOR EACH ROW -DECLARE - last_Sequence NUMBER; - last_InsertID NUMBER; -BEGIN - SELECT ACL_ENTRIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN - SELECT ACL_ENTRIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL; - ELSE - SELECT NVL(Last_Number, 0) INTO last_Sequence - FROM User_Sequences - WHERE Sequence_Name = 'ACL_ENTRIES_SEQ'; - SELECT :NEW.ID INTO last_InsertID FROM DUAL; - WHILE (last_InsertID > last_Sequence) LOOP - SELECT ACL_ENTRIES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL; - END LOOP; - END IF; -END; - -CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) - -CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id) - -CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id) - -CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id) - -CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id) - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON DELETE CASCADE
\ No newline at end of file diff --git a/Acl/Resources/schema/postgresql.sql b/Acl/Resources/schema/postgresql.sql deleted file mode 100644 index 05ca439..0000000 --- a/Acl/Resources/schema/postgresql.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE acl_classes (id SERIAL NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) - -CREATE TABLE acl_security_identities (id SERIAL NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) - -CREATE TABLE acl_object_identities (id SERIAL NOT NULL, parent_object_identity_id INT DEFAULT NULL, class_id INT NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BOOLEAN NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) - -CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id) - -CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id)) - -CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id) - -CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id) - -CREATE TABLE acl_entries (id SERIAL NOT NULL, class_id INT NOT NULL, object_identity_id INT DEFAULT NULL, security_identity_id INT NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order SMALLINT NOT NULL, mask INT NOT NULL, granting BOOLEAN NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BOOLEAN NOT NULL, audit_failure BOOLEAN NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) - -CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id) - -CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id) - -CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id) - -CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id) - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) NOT DEFERRABLE INITIALLY IMMEDIATE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
\ No newline at end of file diff --git a/Acl/Resources/schema/sqlanywhere.sql b/Acl/Resources/schema/sqlanywhere.sql deleted file mode 100644 index d73b102..0000000 --- a/Acl/Resources/schema/sqlanywhere.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE acl_classes (id UNSIGNED INT IDENTITY NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) - -CREATE TABLE acl_security_identities (id UNSIGNED INT IDENTITY NOT NULL, identifier VARCHAR(200) NOT NULL, username BIT NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) - -CREATE TABLE acl_object_identities (id UNSIGNED INT IDENTITY NOT NULL, parent_object_identity_id UNSIGNED INT DEFAULT NULL, class_id UNSIGNED INT NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BIT NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) - -CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id) - -CREATE TABLE acl_object_identity_ancestors (object_identity_id UNSIGNED INT NOT NULL, ancestor_id UNSIGNED INT NOT NULL, PRIMARY KEY (object_identity_id, ancestor_id)) - -CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id) - -CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id) - -CREATE TABLE acl_entries (id UNSIGNED INT IDENTITY NOT NULL, class_id UNSIGNED INT NOT NULL, object_identity_id UNSIGNED INT DEFAULT NULL, security_identity_id UNSIGNED INT NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order UNSIGNED SMALLINT NOT NULL, mask INT NOT NULL, granting BIT NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BIT NOT NULL, audit_failure BIT NOT NULL, PRIMARY KEY (id)) - -CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) - -CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id) - -CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id) - -CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id) - -CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id) - -ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE - -ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
\ No newline at end of file diff --git a/Acl/Resources/schema/sqlite.sql b/Acl/Resources/schema/sqlite.sql deleted file mode 100644 index 4429bfa..0000000 --- a/Acl/Resources/schema/sqlite.sql +++ /dev/null @@ -1,31 +0,0 @@ -CREATE TABLE acl_classes (id INTEGER NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) - -CREATE TABLE acl_security_identities (id INTEGER NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL, PRIMARY KEY(id)) - -CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) - -CREATE TABLE acl_object_identities (id INTEGER NOT NULL, parent_object_identity_id INTEGER UNSIGNED DEFAULT NULL, class_id INTEGER UNSIGNED NOT NULL, object_identifier VARCHAR(100) NOT NULL, entries_inheriting BOOLEAN NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) NOT DEFERRABLE INITIALLY IMMEDIATE) - -CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id) - -CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id) - -CREATE TABLE acl_object_identity_ancestors (object_identity_id INTEGER UNSIGNED NOT NULL, ancestor_id INTEGER UNSIGNED NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id), CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE) - -CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id) - -CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id) - -CREATE TABLE acl_entries (id INTEGER NOT NULL, class_id INTEGER UNSIGNED NOT NULL, object_identity_id INTEGER UNSIGNED DEFAULT NULL, security_identity_id INTEGER UNSIGNED NOT NULL, field_name VARCHAR(50) DEFAULT NULL, ace_order SMALLINT UNSIGNED NOT NULL, mask INTEGER NOT NULL, granting BOOLEAN NOT NULL, granting_strategy VARCHAR(30) NOT NULL, audit_success BOOLEAN NOT NULL, audit_failure BOOLEAN NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE) - -CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order) - -CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id) - -CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id) - -CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id) - -CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
\ No newline at end of file diff --git a/Acl/Tests/Dbal/AclProviderBenchmarkTest.php b/Acl/Tests/Dbal/AclProviderBenchmarkTest.php deleted file mode 100644 index c95b474..0000000 --- a/Acl/Tests/Dbal/AclProviderBenchmarkTest.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Dbal; - -use Symfony\Component\Security\Acl\Dbal\AclProvider; -use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Dbal\Schema; -use Doctrine\DBAL\DriverManager; - -/** - * @group benchmark - */ -class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase -{ - /** @var \Doctrine\DBAL\Connection */ - protected $con; - protected $insertClassStmt; - protected $insertSidStmt; - protected $insertOidAncestorStmt; - protected $insertOidStmt; - protected $insertEntryStmt; - - protected function setUp() - { - try { - $this->con = DriverManager::getConnection(array( - 'driver' => 'pdo_mysql', - 'host' => 'localhost', - 'user' => 'root', - 'dbname' => 'testdb', - )); - $this->con->connect(); - } catch (\Exception $e) { - $this->markTestSkipped('Unable to connect to the database: '.$e->getMessage()); - } - } - - protected function tearDown() - { - $this->con = null; - } - - public function testFindAcls() - { - // $this->generateTestData(); - - // get some random test object identities from the database - $oids = array(); - $stmt = $this->con->executeQuery('SELECT object_identifier, class_type FROM acl_object_identities o INNER JOIN acl_classes c ON c.id = o.class_id ORDER BY RAND() LIMIT 25'); - foreach ($stmt->fetchAll() as $oid) { - $oids[] = new ObjectIdentity($oid['object_identifier'], $oid['class_type']); - } - - $provider = $this->getProvider(); - - $start = microtime(true); - $provider->findAcls($oids); - $time = microtime(true) - $start; - echo 'Total Time: '.$time."s\n"; - } - - /** - * This generates a huge amount of test data to be used mainly for benchmarking - * purposes, not so much for testing. That's why it's not called by default. - */ - protected function generateTestData() - { - $sm = $this->con->getSchemaManager(); - $sm->dropAndCreateDatabase('testdb'); - $this->con->exec('USE testdb'); - - // import the schema - $schema = new Schema($options = $this->getOptions()); - foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) { - $this->con->exec($sql); - } - - // setup prepared statements - $this->insertClassStmt = $this->con->prepare('INSERT INTO acl_classes (id, class_type) VALUES (?, ?)'); - $this->insertSidStmt = $this->con->prepare('INSERT INTO acl_security_identities (id, identifier, username) VALUES (?, ?, ?)'); - $this->insertOidStmt = $this->con->prepare('INSERT INTO acl_object_identities (id, class_id, object_identifier, parent_object_identity_id, entries_inheriting) VALUES (?, ?, ?, ?, ?)'); - $this->insertEntryStmt = $this->con->prepare('INSERT INTO acl_entries (id, class_id, object_identity_id, field_name, ace_order, security_identity_id, mask, granting, granting_strategy, audit_success, audit_failure) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); - $this->insertOidAncestorStmt = $this->con->prepare('INSERT INTO acl_object_identity_ancestors (object_identity_id, ancestor_id) VALUES (?, ?)'); - - for ($i = 0; $i < 40000; ++$i) { - $this->generateAclHierarchy(); - } - } - - protected function generateAclHierarchy() - { - $rootId = $this->generateAcl($this->chooseClassId(), null, array()); - - $this->generateAclLevel(rand(1, 15), $rootId, array($rootId)); - } - - protected function generateAclLevel($depth, $parentId, $ancestors) - { - $level = count($ancestors); - for ($i = 0, $t = rand(1, 10); $i < $t; ++$i) { - $id = $this->generateAcl($this->chooseClassId(), $parentId, $ancestors); - - if ($level < $depth) { - $this->generateAclLevel($depth, $id, array_merge($ancestors, array($id))); - } - } - } - - protected function chooseClassId() - { - static $id = 1000; - - if ($id === 1000 || ($id < 1500 && rand(0, 1))) { - $this->insertClassStmt->execute(array($id, $this->getRandomString(rand(20, 100), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\_'))); - ++$id; - - return $id - 1; - } else { - return rand(1000, $id - 1); - } - } - - protected function generateAcl($classId, $parentId, $ancestors) - { - static $id = 1000; - - $this->insertOidStmt->execute(array( - $id, - $classId, - $this->getRandomString(rand(20, 50)), - $parentId, - rand(0, 1), - )); - - $this->insertOidAncestorStmt->execute(array($id, $id)); - foreach ($ancestors as $ancestor) { - $this->insertOidAncestorStmt->execute(array($id, $ancestor)); - } - - $this->generateAces($classId, $id); - ++$id; - - return $id - 1; - } - - protected function chooseSid() - { - static $id = 1000; - - if ($id === 1000 || ($id < 11000 && rand(0, 1))) { - $this->insertSidStmt->execute(array( - $id, - $this->getRandomString(rand(5, 30)), - rand(0, 1), - )); - ++$id; - - return $id - 1; - } else { - return rand(1000, $id - 1); - } - } - - protected function generateAces($classId, $objectId) - { - static $id = 1000; - - $sids = array(); - $fieldOrder = array(); - - for ($i = 0; $i <= 30; ++$i) { - $fieldName = rand(0, 1) ? null : $this->getRandomString(rand(10, 20)); - - do { - $sid = $this->chooseSid(); - } while (array_key_exists($sid, $sids) && in_array($fieldName, $sids[$sid], true)); - - $fieldOrder[$fieldName] = array_key_exists($fieldName, $fieldOrder) ? $fieldOrder[$fieldName] + 1 : 0; - if (!isset($sids[$sid])) { - $sids[$sid] = array(); - } - $sids[$sid][] = $fieldName; - - $strategy = rand(0, 2); - if ($strategy === 0) { - $strategy = PermissionGrantingStrategy::ALL; - } elseif ($strategy === 1) { - $strategy = PermissionGrantingStrategy::ANY; - } else { - $strategy = PermissionGrantingStrategy::EQUAL; - } - - // id, cid, oid, field, order, sid, mask, granting, strategy, a success, a failure - $this->insertEntryStmt->execute(array( - $id, - $classId, - rand(0, 5) ? $objectId : null, - $fieldName, - $fieldOrder[$fieldName], - $sid, - $this->generateMask(), - rand(0, 1), - $strategy, - rand(0, 1), - rand(0, 1), - )); - - ++$id; - } - } - - protected function generateMask() - { - $i = rand(1, 30); - $mask = 0; - - while ($i <= 30) { - $mask |= 1 << rand(0, 30); - ++$i; - } - - return $mask; - } - - protected function getRandomString($length, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') - { - $s = ''; - $cLength = strlen($chars); - - while (strlen($s) < $length) { - $s .= $chars[mt_rand(0, $cLength - 1)]; - } - - return $s; - } - - protected function getOptions() - { - return array( - 'oid_table_name' => 'acl_object_identities', - 'oid_ancestors_table_name' => 'acl_object_identity_ancestors', - 'class_table_name' => 'acl_classes', - 'sid_table_name' => 'acl_security_identities', - 'entry_table_name' => 'acl_entries', - ); - } - - protected function getStrategy() - { - return new PermissionGrantingStrategy(); - } - - protected function getProvider() - { - return new AclProvider($this->con, $this->getStrategy(), $this->getOptions()); - } -} diff --git a/Acl/Tests/Dbal/AclProviderTest.php b/Acl/Tests/Dbal/AclProviderTest.php deleted file mode 100644 index 680c6c3..0000000 --- a/Acl/Tests/Dbal/AclProviderTest.php +++ /dev/null @@ -1,280 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Dbal; - -use Symfony\Component\Security\Acl\Dbal\AclProvider; -use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Dbal\Schema; -use Doctrine\DBAL\DriverManager; - -/** - * @requires extension pdo_sqlite - */ -class AclProviderTest extends \PHPUnit_Framework_TestCase -{ - protected $con; - protected $insertClassStmt; - protected $insertEntryStmt; - protected $insertOidStmt; - protected $insertOidAncestorStmt; - protected $insertSidStmt; - - /** - * @expectedException \Symfony\Component\Security\Acl\Exception\AclNotFoundException - * @expectedMessage There is no ACL for the given object identity. - */ - public function testFindAclThrowsExceptionWhenNoAclExists() - { - $this->getProvider()->findAcl(new ObjectIdentity('foo', 'foo')); - } - - public function testFindAclsThrowsExceptionUnlessAnACLIsFoundForEveryOID() - { - $oids = array(); - $oids[] = new ObjectIdentity('1', 'foo'); - $oids[] = new ObjectIdentity('foo', 'foo'); - - try { - $this->getProvider()->findAcls($oids); - - $this->fail('Provider did not throw an expected exception.'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Security\Acl\Exception\AclNotFoundException', $e); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Exception\NotAllAclsFoundException', $e); - - $partialResult = $e->getPartialResult(); - $this->assertTrue($partialResult->contains($oids[0])); - $this->assertFalse($partialResult->contains($oids[1])); - } - } - - public function testFindAcls() - { - $oids = array(); - $oids[] = new ObjectIdentity('1', 'foo'); - $oids[] = new ObjectIdentity('2', 'foo'); - - $provider = $this->getProvider(); - - $acls = $provider->findAcls($oids); - $this->assertInstanceOf('SplObjectStorage', $acls); - $this->assertCount(2, $acls); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl0 = $acls->offsetGet($oids[0])); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl1 = $acls->offsetGet($oids[1])); - $this->assertTrue($oids[0]->equals($acl0->getObjectIdentity())); - $this->assertTrue($oids[1]->equals($acl1->getObjectIdentity())); - } - - public function testFindAclsWithDifferentTypes() - { - $oids = array(); - $oids[] = new ObjectIdentity('123', 'Bundle\SomeVendor\MyBundle\Entity\SomeEntity'); - $oids[] = new ObjectIdentity('123', 'Bundle\MyBundle\Entity\AnotherEntity'); - - $provider = $this->getProvider(); - - $acls = $provider->findAcls($oids); - $this->assertInstanceOf('SplObjectStorage', $acls); - $this->assertCount(2, $acls); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl0 = $acls->offsetGet($oids[0])); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl1 = $acls->offsetGet($oids[1])); - $this->assertTrue($oids[0]->equals($acl0->getObjectIdentity())); - $this->assertTrue($oids[1]->equals($acl1->getObjectIdentity())); - } - - public function testFindAclCachesAclInMemory() - { - $oid = new ObjectIdentity('1', 'foo'); - $provider = $this->getProvider(); - - $acl = $provider->findAcl($oid); - $this->assertSame($acl, $cAcl = $provider->findAcl($oid)); - - $cAces = $cAcl->getObjectAces(); - foreach ($acl->getObjectAces() as $index => $ace) { - $this->assertSame($ace, $cAces[$index]); - } - } - - public function testFindAcl() - { - $oid = new ObjectIdentity('1', 'foo'); - $provider = $this->getProvider(); - - $acl = $provider->findAcl($oid); - - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl); - $this->assertTrue($oid->equals($acl->getObjectIdentity())); - $this->assertEquals(4, $acl->getId()); - $this->assertCount(0, $acl->getClassAces()); - $this->assertCount(0, $this->getField($acl, 'classFieldAces')); - $this->assertCount(3, $acl->getObjectAces()); - $this->assertCount(0, $this->getField($acl, 'objectFieldAces')); - - $aces = $acl->getObjectAces(); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Entry', $aces[0]); - $this->assertTrue($aces[0]->isGranting()); - $this->assertTrue($aces[0]->isAuditSuccess()); - $this->assertTrue($aces[0]->isAuditFailure()); - $this->assertEquals('all', $aces[0]->getStrategy()); - $this->assertSame(2, $aces[0]->getMask()); - - // check ACE are in correct order - $i = 0; - foreach ($aces as $index => $ace) { - $this->assertEquals($i, $index); - ++$i; - } - - $sid = $aces[0]->getSecurityIdentity(); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\UserSecurityIdentity', $sid); - $this->assertEquals('john.doe', $sid->getUsername()); - $this->assertEquals('SomeClass', $sid->getClass()); - } - - protected function setUp() - { - $this->con = DriverManager::getConnection(array( - 'driver' => 'pdo_sqlite', - 'memory' => true, - )); - - // import the schema - $schema = new Schema($options = $this->getOptions()); - foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) { - $this->con->exec($sql); - } - - // populate the schema with some test data - $this->insertClassStmt = $this->con->prepare('INSERT INTO acl_classes (id, class_type) VALUES (?, ?)'); - foreach ($this->getClassData() as $data) { - $this->insertClassStmt->execute($data); - } - - $this->insertSidStmt = $this->con->prepare('INSERT INTO acl_security_identities (id, identifier, username) VALUES (?, ?, ?)'); - foreach ($this->getSidData() as $data) { - $this->insertSidStmt->execute($data); - } - - $this->insertOidStmt = $this->con->prepare('INSERT INTO acl_object_identities (id, class_id, object_identifier, parent_object_identity_id, entries_inheriting) VALUES (?, ?, ?, ?, ?)'); - foreach ($this->getOidData() as $data) { - $this->insertOidStmt->execute($data); - } - - $this->insertEntryStmt = $this->con->prepare('INSERT INTO acl_entries (id, class_id, object_identity_id, field_name, ace_order, security_identity_id, mask, granting, granting_strategy, audit_success, audit_failure) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); - foreach ($this->getEntryData() as $data) { - $this->insertEntryStmt->execute($data); - } - - $this->insertOidAncestorStmt = $this->con->prepare('INSERT INTO acl_object_identity_ancestors (object_identity_id, ancestor_id) VALUES (?, ?)'); - foreach ($this->getOidAncestorData() as $data) { - $this->insertOidAncestorStmt->execute($data); - } - } - - protected function tearDown() - { - $this->con = null; - } - - protected function getField($object, $field) - { - $reflection = new \ReflectionProperty($object, $field); - $reflection->setAccessible(true); - - return $reflection->getValue($object); - } - - protected function getEntryData() - { - // id, cid, oid, field, order, sid, mask, granting, strategy, a success, a failure - return array( - array(1, 1, 1, null, 0, 1, 1, 1, 'all', 1, 1), - array(2, 1, 1, null, 1, 2, 1 << 2 | 1 << 1, 0, 'any', 0, 0), - array(3, 3, 4, null, 0, 1, 2, 1, 'all', 1, 1), - array(4, 3, 4, null, 2, 2, 1, 1, 'all', 1, 1), - array(5, 3, 4, null, 1, 3, 1, 1, 'all', 1, 1), - ); - } - - protected function getOidData() - { - // id, cid, oid, parent_oid, entries_inheriting - return array( - array(1, 1, '123', null, 1), - array(2, 2, '123', 1, 1), - array(3, 2, 'i:3:123', 1, 1), - array(4, 3, '1', 2, 1), - array(5, 3, '2', 2, 1), - ); - } - - protected function getOidAncestorData() - { - return array( - array(1, 1), - array(2, 1), - array(2, 2), - array(3, 1), - array(3, 3), - array(4, 2), - array(4, 1), - array(4, 4), - array(5, 2), - array(5, 1), - array(5, 5), - ); - } - - protected function getSidData() - { - return array( - array(1, 'SomeClass-john.doe', 1), - array(2, 'MyClass-john.doe@foo.com', 1), - array(3, 'FooClass-123', 1), - array(4, 'MooClass-ROLE_USER', 1), - array(5, 'ROLE_USER', 0), - array(6, 'IS_AUTHENTICATED_FULLY', 0), - ); - } - - protected function getClassData() - { - return array( - array(1, 'Bundle\SomeVendor\MyBundle\Entity\SomeEntity'), - array(2, 'Bundle\MyBundle\Entity\AnotherEntity'), - array(3, 'foo'), - ); - } - - protected function getOptions() - { - return array( - 'oid_table_name' => 'acl_object_identities', - 'oid_ancestors_table_name' => 'acl_object_identity_ancestors', - 'class_table_name' => 'acl_classes', - 'sid_table_name' => 'acl_security_identities', - 'entry_table_name' => 'acl_entries', - ); - } - - protected function getStrategy() - { - return new PermissionGrantingStrategy(); - } - - protected function getProvider() - { - return new AclProvider($this->con, $this->getStrategy(), $this->getOptions()); - } -} diff --git a/Acl/Tests/Dbal/MutableAclProviderTest.php b/Acl/Tests/Dbal/MutableAclProviderTest.php deleted file mode 100644 index c2169e4..0000000 --- a/Acl/Tests/Dbal/MutableAclProviderTest.php +++ /dev/null @@ -1,572 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Dbal; - -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Model\FieldEntryInterface; -use Symfony\Component\Security\Acl\Model\AuditableEntryInterface; -use Symfony\Component\Security\Acl\Model\EntryInterface; -use Symfony\Component\Security\Acl\Domain\Entry; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\Acl; -use Symfony\Component\Security\Acl\Exception\AclNotFoundException; -use Symfony\Component\Security\Acl\Exception\ConcurrentModificationException; -use Symfony\Component\Security\Acl\Dbal\AclProvider; -use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy; -use Symfony\Component\Security\Acl\Dbal\MutableAclProvider; -use Symfony\Component\Security\Acl\Dbal\Schema; -use Doctrine\DBAL\DriverManager; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; - -/** - * @requires extension pdo_sqlite - */ -class MutableAclProviderTest extends \PHPUnit_Framework_TestCase -{ - protected $con; - - public static function assertAceEquals(EntryInterface $a, EntryInterface $b) - { - self::assertInstanceOf(get_class($a), $b); - - foreach (array('getId', 'getMask', 'getStrategy', 'isGranting') as $getter) { - self::assertSame($a->$getter(), $b->$getter()); - } - - self::assertTrue($a->getSecurityIdentity()->equals($b->getSecurityIdentity())); - self::assertSame($a->getAcl()->getId(), $b->getAcl()->getId()); - - if ($a instanceof AuditableEntryInterface) { - self::assertSame($a->isAuditSuccess(), $b->isAuditSuccess()); - self::assertSame($a->isAuditFailure(), $b->isAuditFailure()); - } - - if ($a instanceof FieldEntryInterface) { - self::assertSame($a->getField(), $b->getField()); - } - } - - /** - * @expectedException \Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException - */ - public function testCreateAclThrowsExceptionWhenAclAlreadyExists() - { - $provider = $this->getProvider(); - $oid = new ObjectIdentity('123456', 'FOO'); - $provider->createAcl($oid); - $provider->createAcl($oid); - } - - public function testCreateAcl() - { - $provider = $this->getProvider(); - $oid = new ObjectIdentity('123456', 'FOO'); - $acl = $provider->createAcl($oid); - $cachedAcl = $provider->findAcl($oid); - - $this->assertInstanceOf('Symfony\Component\Security\Acl\Domain\Acl', $acl); - $this->assertSame($acl, $cachedAcl); - $this->assertTrue($acl->getObjectIdentity()->equals($oid)); - } - - public function testDeleteAcl() - { - $provider = $this->getProvider(); - $oid = new ObjectIdentity(1, 'Foo'); - $acl = $provider->createAcl($oid); - - $provider->deleteAcl($oid); - $loadedAcls = $this->getField($provider, 'loadedAcls'); - $this->assertCount(0, $loadedAcls['Foo']); - - try { - $provider->findAcl($oid); - $this->fail('ACL has not been properly deleted.'); - } catch (AclNotFoundException $e) { - } - } - - public function testDeleteAclDeletesChildren() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $parentAcl = $provider->createAcl(new ObjectIdentity(2, 'Foo')); - $acl->setParentAcl($parentAcl); - $provider->updateAcl($acl); - $provider->deleteAcl($parentAcl->getObjectIdentity()); - - try { - $provider->findAcl(new ObjectIdentity(1, 'Foo')); - $this->fail('Child-ACLs have not been deleted.'); - } catch (AclNotFoundException $e) { - } - } - - public function testFindAclsAddsPropertyListener() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - - $propertyChanges = $this->getField($provider, 'propertyChanges'); - $this->assertCount(1, $propertyChanges); - $this->assertTrue($propertyChanges->contains($acl)); - $this->assertEquals(array(), $propertyChanges->offsetGet($acl)); - - $listeners = $this->getField($acl, 'listeners'); - $this->assertSame($provider, $listeners[0]); - } - - public function testFindAclsAddsPropertyListenerOnlyOnce() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $acl = $provider->findAcl(new ObjectIdentity(1, 'Foo')); - - $propertyChanges = $this->getField($provider, 'propertyChanges'); - $this->assertCount(1, $propertyChanges); - $this->assertTrue($propertyChanges->contains($acl)); - $this->assertEquals(array(), $propertyChanges->offsetGet($acl)); - - $listeners = $this->getField($acl, 'listeners'); - $this->assertCount(1, $listeners); - $this->assertSame($provider, $listeners[0]); - } - - public function testFindAclsAddsPropertyListenerToParentAcls() - { - $provider = $this->getProvider(); - $this->importAcls($provider, array( - 'main' => array( - 'object_identifier' => '1', - 'class_type' => 'foo', - 'parent_acl' => 'parent', - ), - 'parent' => array( - 'object_identifier' => '1', - 'class_type' => 'anotherFoo', - ), - )); - - $propertyChanges = $this->getField($provider, 'propertyChanges'); - $this->assertCount(0, $propertyChanges); - - $acl = $provider->findAcl(new ObjectIdentity('1', 'foo')); - $this->assertCount(2, $propertyChanges); - $this->assertTrue($propertyChanges->contains($acl)); - $this->assertTrue($propertyChanges->contains($acl->getParentAcl())); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testPropertyChangedDoesNotTrackUnmanagedAcls() - { - $provider = $this->getProvider(); - $acl = new Acl(1, new ObjectIdentity(1, 'foo'), new PermissionGrantingStrategy(), array(), false); - - $provider->propertyChanged($acl, 'classAces', array(), array('foo')); - } - - public function testPropertyChangedTracksChangesToAclProperties() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $propertyChanges = $this->getField($provider, 'propertyChanges'); - - $provider->propertyChanged($acl, 'entriesInheriting', false, true); - $changes = $propertyChanges->offsetGet($acl); - $this->assertTrue(isset($changes['entriesInheriting'])); - $this->assertFalse($changes['entriesInheriting'][0]); - $this->assertTrue($changes['entriesInheriting'][1]); - - $provider->propertyChanged($acl, 'entriesInheriting', true, false); - $provider->propertyChanged($acl, 'entriesInheriting', false, true); - $provider->propertyChanged($acl, 'entriesInheriting', true, false); - $changes = $propertyChanges->offsetGet($acl); - $this->assertFalse(isset($changes['entriesInheriting'])); - } - - public function testPropertyChangedTracksChangesToAceProperties() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $ace = new Entry(1, $acl, new UserSecurityIdentity('foo', 'FooClass'), 'all', 1, true, true, true); - $ace2 = new Entry(2, $acl, new UserSecurityIdentity('foo', 'FooClass'), 'all', 1, true, true, true); - $propertyChanges = $this->getField($provider, 'propertyChanges'); - - $provider->propertyChanged($ace, 'mask', 1, 3); - $changes = $propertyChanges->offsetGet($acl); - $this->assertTrue(isset($changes['aces'])); - $this->assertInstanceOf('\SplObjectStorage', $changes['aces']); - $this->assertTrue($changes['aces']->contains($ace)); - $aceChanges = $changes['aces']->offsetGet($ace); - $this->assertTrue(isset($aceChanges['mask'])); - $this->assertEquals(1, $aceChanges['mask'][0]); - $this->assertEquals(3, $aceChanges['mask'][1]); - - $provider->propertyChanged($ace, 'strategy', 'all', 'any'); - $changes = $propertyChanges->offsetGet($acl); - $this->assertTrue(isset($changes['aces'])); - $this->assertInstanceOf('\SplObjectStorage', $changes['aces']); - $this->assertTrue($changes['aces']->contains($ace)); - $aceChanges = $changes['aces']->offsetGet($ace); - $this->assertTrue(isset($aceChanges['mask'])); - $this->assertTrue(isset($aceChanges['strategy'])); - $this->assertEquals('all', $aceChanges['strategy'][0]); - $this->assertEquals('any', $aceChanges['strategy'][1]); - - $provider->propertyChanged($ace, 'mask', 3, 1); - $changes = $propertyChanges->offsetGet($acl); - $aceChanges = $changes['aces']->offsetGet($ace); - $this->assertFalse(isset($aceChanges['mask'])); - $this->assertTrue(isset($aceChanges['strategy'])); - - $provider->propertyChanged($ace2, 'mask', 1, 3); - $provider->propertyChanged($ace, 'strategy', 'any', 'all'); - $changes = $propertyChanges->offsetGet($acl); - $this->assertTrue(isset($changes['aces'])); - $this->assertFalse($changes['aces']->contains($ace)); - $this->assertTrue($changes['aces']->contains($ace2)); - - $provider->propertyChanged($ace2, 'mask', 3, 4); - $provider->propertyChanged($ace2, 'mask', 4, 1); - $changes = $propertyChanges->offsetGet($acl); - $this->assertFalse(isset($changes['aces'])); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testUpdateAclDoesNotAcceptUntrackedAcls() - { - $provider = $this->getProvider(); - $acl = new Acl(1, new ObjectIdentity(1, 'Foo'), new PermissionGrantingStrategy(), array(), true); - $provider->updateAcl($acl); - } - - public function testUpdateDoesNothingWhenThereAreNoChanges() - { - $con = $this->getMock('Doctrine\DBAL\Connection', array(), array(), '', false); - $con - ->expects($this->never()) - ->method('beginTransaction') - ; - $con - ->expects($this->never()) - ->method('executeQuery') - ; - - $provider = new MutableAclProvider($con, new PermissionGrantingStrategy(), array()); - $acl = new Acl(1, new ObjectIdentity(1, 'Foo'), new PermissionGrantingStrategy(), array(), true); - $propertyChanges = $this->getField($provider, 'propertyChanges'); - $propertyChanges->offsetSet($acl, array()); - $provider->updateAcl($acl); - } - - public function testUpdateAclThrowsExceptionOnConcurrentModificationOfSharedProperties() - { - $provider = $this->getProvider(); - $acl1 = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $acl2 = $provider->createAcl(new ObjectIdentity(2, 'Foo')); - $acl3 = $provider->createAcl(new ObjectIdentity(1, 'AnotherFoo')); - $sid = new RoleSecurityIdentity('ROLE_FOO'); - - $acl1->insertClassAce($sid, 1); - $acl3->insertClassAce($sid, 1); - $provider->updateAcl($acl1); - $provider->updateAcl($acl3); - - $acl2->insertClassAce($sid, 16); - $provider->updateAcl($acl2); - - $acl1->insertClassAce($sid, 3); - $acl2->insertClassAce($sid, 5); - try { - $provider->updateAcl($acl1); - $this->fail('Provider failed to detect a concurrent modification.'); - } catch (ConcurrentModificationException $e) { - } - } - - public function testUpdateAcl() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $sid = new UserSecurityIdentity('johannes', 'FooClass'); - $acl->setEntriesInheriting(!$acl->isEntriesInheriting()); - - $acl->insertObjectAce($sid, 1); - $acl->insertClassAce($sid, 5, 0, false); - $acl->insertObjectAce($sid, 2, 1, true); - $acl->insertClassFieldAce('field', $sid, 2, 0, true); - $provider->updateAcl($acl); - - $acl->updateObjectAce(0, 3); - $acl->deleteObjectAce(1); - $acl->updateObjectAuditing(0, true, false); - $acl->updateClassFieldAce(0, 'field', 15); - $provider->updateAcl($acl); - - $reloadProvider = $this->getProvider(); - $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo')); - $this->assertNotSame($acl, $reloadedAcl); - $this->assertSame($acl->isEntriesInheriting(), $reloadedAcl->isEntriesInheriting()); - - $aces = $acl->getObjectAces(); - $reloadedAces = $reloadedAcl->getObjectAces(); - $this->assertEquals(count($aces), count($reloadedAces)); - foreach ($aces as $index => $ace) { - $this->assertAceEquals($ace, $reloadedAces[$index]); - } - } - - public function testUpdateAclWorksForChangingTheParentAcl() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $parentAcl = $provider->createAcl(new ObjectIdentity(1, 'AnotherFoo')); - $acl->setParentAcl($parentAcl); - $provider->updateAcl($acl); - - $reloadProvider = $this->getProvider(); - $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo')); - $this->assertNotSame($acl, $reloadedAcl); - $this->assertSame($parentAcl->getId(), $reloadedAcl->getParentAcl()->getId()); - } - - public function testUpdateAclUpdatesChildAclsCorrectly() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - - $parentAcl = $provider->createAcl(new ObjectIdentity(1, 'Bar')); - $acl->setParentAcl($parentAcl); - $provider->updateAcl($acl); - - $parentParentAcl = $provider->createAcl(new ObjectIdentity(1, 'Baz')); - $parentAcl->setParentAcl($parentParentAcl); - $provider->updateAcl($parentAcl); - - $newParentParentAcl = $provider->createAcl(new ObjectIdentity(2, 'Baz')); - $parentAcl->setParentAcl($newParentParentAcl); - $provider->updateAcl($parentAcl); - - $reloadProvider = $this->getProvider(); - $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo')); - $this->assertEquals($newParentParentAcl->getId(), $reloadedAcl->getParentAcl()->getParentAcl()->getId()); - } - - public function testUpdateAclInsertingMultipleObjectFieldAcesThrowsDBConstraintViolations() - { - $provider = $this->getProvider(); - $oid = new ObjectIdentity(1, 'Foo'); - $sid1 = new UserSecurityIdentity('johannes', 'FooClass'); - $sid2 = new UserSecurityIdentity('guilro', 'FooClass'); - $sid3 = new UserSecurityIdentity('bmaz', 'FooClass'); - $fieldName = 'fieldName'; - - $acl = $provider->createAcl($oid); - $acl->insertObjectFieldAce($fieldName, $sid1, 4); - $provider->updateAcl($acl); - - $acl = $provider->findAcl($oid); - $acl->insertObjectFieldAce($fieldName, $sid2, 4); - $provider->updateAcl($acl); - - $acl = $provider->findAcl($oid); - $acl->insertObjectFieldAce($fieldName, $sid3, 4); - $provider->updateAcl($acl); - } - - public function testUpdateAclDeletingObjectFieldAcesThrowsDBConstraintViolations() - { - $provider = $this->getProvider(); - $oid = new ObjectIdentity(1, 'Foo'); - $sid1 = new UserSecurityIdentity('johannes', 'FooClass'); - $sid2 = new UserSecurityIdentity('guilro', 'FooClass'); - $sid3 = new UserSecurityIdentity('bmaz', 'FooClass'); - $fieldName = 'fieldName'; - - $acl = $provider->createAcl($oid); - $acl->insertObjectFieldAce($fieldName, $sid1, 4); - $provider->updateAcl($acl); - - $acl = $provider->findAcl($oid); - $acl->insertObjectFieldAce($fieldName, $sid2, 4); - $provider->updateAcl($acl); - - $index = 0; - $acl->deleteObjectFieldAce($index, $fieldName); - $provider->updateAcl($acl); - - $acl = $provider->findAcl($oid); - $acl->insertObjectFieldAce($fieldName, $sid3, 4); - $provider->updateAcl($acl); - } - - public function testUpdateUserSecurityIdentity() - { - $provider = $this->getProvider(); - $acl = $provider->createAcl(new ObjectIdentity(1, 'Foo')); - $sid = new UserSecurityIdentity('johannes', 'FooClass'); - $acl->setEntriesInheriting(!$acl->isEntriesInheriting()); - - $acl->insertObjectAce($sid, 1); - $acl->insertClassAce($sid, 5, 0, false); - $acl->insertObjectAce($sid, 2, 1, true); - $acl->insertClassFieldAce('field', $sid, 2, 0, true); - $provider->updateAcl($acl); - - $newSid = new UserSecurityIdentity('mathieu', 'FooClass'); - $provider->updateUserSecurityIdentity($newSid, 'johannes'); - - $reloadProvider = $this->getProvider(); - $reloadedAcl = $reloadProvider->findAcl(new ObjectIdentity(1, 'Foo')); - - $this->assertNotSame($acl, $reloadedAcl); - $this->assertSame($acl->isEntriesInheriting(), $reloadedAcl->isEntriesInheriting()); - - $aces = $acl->getObjectAces(); - $reloadedAces = $reloadedAcl->getObjectAces(); - $this->assertEquals(count($aces), count($reloadedAces)); - foreach ($reloadedAces as $ace) { - $this->assertTrue($ace->getSecurityIdentity()->equals($newSid)); - } - } - - /** - * Imports acls. - * - * Data must have the following format: - * array( - * *name* => array( - * 'object_identifier' => *required* - * 'class_type' => *required*, - * 'parent_acl' => *name (optional)* - * ), - * ) - * - * @param AclProvider $provider - * @param array $data - * - * @throws \InvalidArgumentException - * @throws \Exception - */ - protected function importAcls(AclProvider $provider, array $data) - { - $aclIds = $parentAcls = array(); - $con = $this->getField($provider, 'connection'); - $con->beginTransaction(); - try { - foreach ($data as $name => $aclData) { - if (!isset($aclData['object_identifier'], $aclData['class_type'])) { - throw new \InvalidArgumentException('"object_identifier", and "class_type" must be present.'); - } - - $this->callMethod($provider, 'createObjectIdentity', array(new ObjectIdentity($aclData['object_identifier'], $aclData['class_type']))); - $aclId = $con->lastInsertId(); - $aclIds[$name] = $aclId; - - $sql = $this->callMethod($provider, 'getInsertObjectIdentityRelationSql', array($aclId, $aclId)); - $con->executeQuery($sql); - - if (isset($aclData['parent_acl'])) { - if (isset($aclIds[$aclData['parent_acl']])) { - $con->executeQuery('UPDATE acl_object_identities SET parent_object_identity_id = '.$aclIds[$aclData['parent_acl']].' WHERE id = '.$aclId); - $con->executeQuery($this->callMethod($provider, 'getInsertObjectIdentityRelationSql', array($aclId, $aclIds[$aclData['parent_acl']]))); - } else { - $parentAcls[$aclId] = $aclData['parent_acl']; - } - } - } - - foreach ($parentAcls as $aclId => $name) { - if (!isset($aclIds[$name])) { - throw new \InvalidArgumentException(sprintf('"%s" does not exist.', $name)); - } - - $con->executeQuery(sprintf('UPDATE acl_object_identities SET parent_object_identity_id = %d WHERE id = %d', $aclIds[$name], $aclId)); - $con->executeQuery($this->callMethod($provider, 'getInsertObjectIdentityRelationSql', array($aclId, $aclIds[$name]))); - } - - $con->commit(); - } catch (\Exception $e) { - $con->rollBack(); - - throw $e; - } - } - - protected function callMethod($object, $method, array $args) - { - $method = new \ReflectionMethod($object, $method); - $method->setAccessible(true); - - return $method->invokeArgs($object, $args); - } - - protected function setUp() - { - $this->con = DriverManager::getConnection(array( - 'driver' => 'pdo_sqlite', - 'memory' => true, - )); - - // import the schema - $schema = new Schema($this->getOptions()); - foreach ($schema->toSql($this->con->getDatabasePlatform()) as $sql) { - $this->con->exec($sql); - } - } - - protected function tearDown() - { - $this->con = null; - } - - protected function getField($object, $field) - { - $reflection = new \ReflectionProperty($object, $field); - $reflection->setAccessible(true); - - return $reflection->getValue($object); - } - - public function setField($object, $field, $value) - { - $reflection = new \ReflectionProperty($object, $field); - $reflection->setAccessible(true); - $reflection->setValue($object, $value); - $reflection->setAccessible(false); - } - - protected function getOptions() - { - return array( - 'oid_table_name' => 'acl_object_identities', - 'oid_ancestors_table_name' => 'acl_object_identity_ancestors', - 'class_table_name' => 'acl_classes', - 'sid_table_name' => 'acl_security_identities', - 'entry_table_name' => 'acl_entries', - ); - } - - protected function getStrategy() - { - return new PermissionGrantingStrategy(); - } - - protected function getProvider($cache = null) - { - return new MutableAclProvider($this->con, $this->getStrategy(), $this->getOptions(), $cache); - } -} diff --git a/Acl/Tests/Domain/AclTest.php b/Acl/Tests/Domain/AclTest.php deleted file mode 100644 index 84b9ba9..0000000 --- a/Acl/Tests/Domain/AclTest.php +++ /dev/null @@ -1,513 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Domain\Acl; - -class AclTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $acl = new Acl(1, $oid = new ObjectIdentity('foo', 'foo'), $permissionStrategy = new PermissionGrantingStrategy(), array(), true); - - $this->assertSame(1, $acl->getId()); - $this->assertSame($oid, $acl->getObjectIdentity()); - $this->assertNull($acl->getParentAcl()); - $this->assertTrue($acl->isEntriesInheriting()); - } - - /** - * @expectedException \OutOfBoundsException - * @dataProvider getDeleteAceTests - */ - public function testDeleteAceThrowsExceptionOnInvalidIndex($type) - { - $acl = $this->getAcl(); - $acl->{'delete'.$type.'Ace'}(0); - } - - /** - * @dataProvider getDeleteAceTests - */ - public function testDeleteAce($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 1); - $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 2, 1); - $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 3, 2); - - $listener = $this->getListener(array( - $type.'Aces', 'aceOrder', 'aceOrder', $type.'Aces', - )); - $acl->addPropertyChangedListener($listener); - - $this->assertCount(3, $acl->{'get'.$type.'Aces'}()); - - $acl->{'delete'.$type.'Ace'}(0); - $this->assertCount(2, $aces = $acl->{'get'.$type.'Aces'}()); - $this->assertEquals(2, $aces[0]->getMask()); - $this->assertEquals(3, $aces[1]->getMask()); - - $acl->{'delete'.$type.'Ace'}(1); - $this->assertCount(1, $aces = $acl->{'get'.$type.'Aces'}()); - $this->assertEquals(2, $aces[0]->getMask()); - } - - public function getDeleteAceTests() - { - return array( - array('class'), - array('object'), - ); - } - - /** - * @expectedException \OutOfBoundsException - * @dataProvider getDeleteFieldAceTests - */ - public function testDeleteFieldAceThrowsExceptionOnInvalidIndex($type) - { - $acl = $this->getAcl(); - $acl->{'delete'.$type.'Ace'}('foo', 0); - } - - /** - * @dataProvider getDeleteFieldAceTests - */ - public function testDeleteFieldAce($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 1, 0); - $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 2, 1); - $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 3, 2); - - $listener = $this->getListener(array( - $type.'Aces', 'aceOrder', 'aceOrder', $type.'Aces', - )); - $acl->addPropertyChangedListener($listener); - - $this->assertCount(3, $acl->{'get'.$type.'Aces'}('foo')); - - $acl->{'delete'.$type.'Ace'}(0, 'foo'); - $this->assertCount(2, $aces = $acl->{'get'.$type.'Aces'}('foo')); - $this->assertEquals(2, $aces[0]->getMask()); - $this->assertEquals(3, $aces[1]->getMask()); - - $acl->{'delete'.$type.'Ace'}(1, 'foo'); - $this->assertCount(1, $aces = $acl->{'get'.$type.'Aces'}('foo')); - $this->assertEquals(2, $aces[0]->getMask()); - } - - public function getDeleteFieldAceTests() - { - return array( - array('classField'), - array('objectField'), - ); - } - - /** - * @dataProvider getInsertAceTests - */ - public function testInsertAce($property, $method) - { - $acl = $this->getAcl(); - - $listener = $this->getListener(array( - $property, 'aceOrder', $property, 'aceOrder', $property, - )); - $acl->addPropertyChangedListener($listener); - - $sid = new RoleSecurityIdentity('foo'); - $acl->$method($sid, 1); - $acl->$method($sid, 2); - $acl->$method($sid, 3, 1, false); - - $this->assertCount(3, $aces = $acl->{'get'.$property}()); - $this->assertEquals(2, $aces[0]->getMask()); - $this->assertEquals(3, $aces[1]->getMask()); - $this->assertEquals(1, $aces[2]->getMask()); - } - - /** - * @expectedException \OutOfBoundsException - * @dataProvider getInsertAceTests - */ - public function testInsertClassAceThrowsExceptionOnInvalidIndex($property, $method) - { - $acl = $this->getAcl(); - $acl->$method(new RoleSecurityIdentity('foo'), 1, 1); - } - - public function getInsertAceTests() - { - return array( - array('classAces', 'insertClassAce'), - array('objectAces', 'insertObjectAce'), - ); - } - - /** - * @dataProvider getInsertFieldAceTests - */ - public function testInsertClassFieldAce($property, $method) - { - $acl = $this->getAcl(); - - $listener = $this->getListener(array( - $property, $property, 'aceOrder', $property, - 'aceOrder', 'aceOrder', $property, - )); - $acl->addPropertyChangedListener($listener); - - $sid = new RoleSecurityIdentity('foo'); - $acl->$method('foo', $sid, 1); - $acl->$method('foo2', $sid, 1); - $acl->$method('foo', $sid, 3); - $acl->$method('foo', $sid, 2); - - $this->assertCount(3, $aces = $acl->{'get'.$property}('foo')); - $this->assertCount(1, $acl->{'get'.$property}('foo2')); - $this->assertEquals(2, $aces[0]->getMask()); - $this->assertEquals(3, $aces[1]->getMask()); - $this->assertEquals(1, $aces[2]->getMask()); - } - - /** - * @expectedException \OutOfBoundsException - * @dataProvider getInsertFieldAceTests - */ - public function testInsertClassFieldAceThrowsExceptionOnInvalidIndex($property, $method) - { - $acl = $this->getAcl(); - $acl->$method('foo', new RoleSecurityIdentity('foo'), 1, 1); - } - - public function getInsertFieldAceTests() - { - return array( - array('classFieldAces', 'insertClassFieldAce'), - array('objectFieldAces', 'insertObjectFieldAce'), - ); - } - - public function testIsFieldGranted() - { - $sids = array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_IDDQD')); - $masks = array(1, 2, 4); - $strategy = $this->getMock('Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface'); - $acl = new Acl(1, new ObjectIdentity(1, 'foo'), $strategy, array(), true); - - $strategy - ->expects($this->once()) - ->method('isFieldGranted') - ->with($this->equalTo($acl), $this->equalTo('foo'), $this->equalTo($masks), $this->equalTo($sids), $this->isTrue()) - ->will($this->returnValue(true)) - ; - - $this->assertTrue($acl->isFieldGranted('foo', $masks, $sids, true)); - } - - public function testIsGranted() - { - $sids = array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_IDDQD')); - $masks = array(1, 2, 4); - $strategy = $this->getMock('Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface'); - $acl = new Acl(1, new ObjectIdentity(1, 'foo'), $strategy, array(), true); - - $strategy - ->expects($this->once()) - ->method('isGranted') - ->with($this->equalTo($acl), $this->equalTo($masks), $this->equalTo($sids), $this->isTrue()) - ->will($this->returnValue(true)) - ; - - $this->assertTrue($acl->isGranted($masks, $sids, true)); - } - - public function testSetGetParentAcl() - { - $acl = $this->getAcl(); - $parentAcl = $this->getAcl(); - - $listener = $this->getListener(array('parentAcl')); - $acl->addPropertyChangedListener($listener); - - $this->assertNull($acl->getParentAcl()); - $acl->setParentAcl($parentAcl); - $this->assertSame($parentAcl, $acl->getParentAcl()); - - $acl->setParentAcl(null); - $this->assertNull($acl->getParentAcl()); - } - - public function testSetIsEntriesInheriting() - { - $acl = $this->getAcl(); - - $listener = $this->getListener(array('entriesInheriting')); - $acl->addPropertyChangedListener($listener); - - $this->assertTrue($acl->isEntriesInheriting()); - $acl->setEntriesInheriting(false); - $this->assertFalse($acl->isEntriesInheriting()); - } - - public function testIsSidLoadedWhenAllSidsAreLoaded() - { - $acl = $this->getAcl(); - - $this->assertTrue($acl->isSidLoaded(new UserSecurityIdentity('foo', 'Foo'))); - $this->assertTrue($acl->isSidLoaded(new RoleSecurityIdentity('ROLE_FOO', 'Foo'))); - } - - public function testIsSidLoaded() - { - $acl = new Acl(1, new ObjectIdentity('1', 'foo'), new PermissionGrantingStrategy(), array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('johannes', 'Bar')), true); - - $this->assertTrue($acl->isSidLoaded(new UserSecurityIdentity('foo', 'Foo'))); - $this->assertTrue($acl->isSidLoaded(new UserSecurityIdentity('johannes', 'Bar'))); - $this->assertTrue($acl->isSidLoaded(array( - new UserSecurityIdentity('foo', 'Foo'), - new UserSecurityIdentity('johannes', 'Bar'), - ))); - $this->assertFalse($acl->isSidLoaded(new RoleSecurityIdentity('ROLE_FOO'))); - $this->assertFalse($acl->isSidLoaded(new UserSecurityIdentity('schmittjoh@gmail.com', 'Moo'))); - $this->assertFalse($acl->isSidLoaded(array( - new UserSecurityIdentity('foo', 'Foo'), - new UserSecurityIdentity('johannes', 'Bar'), - new RoleSecurityIdentity('ROLE_FOO'), - ))); - } - - /** - * @dataProvider getUpdateAceTests - * @expectedException \OutOfBoundsException - */ - public function testUpdateAceThrowsOutOfBoundsExceptionOnInvalidIndex($type) - { - $acl = $this->getAcl(); - $acl->{'update'.$type}(0, 1); - } - - /** - * @dataProvider getUpdateAceTests - */ - public function testUpdateAce($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type}(new RoleSecurityIdentity('foo'), 1); - - $listener = $this->getListener(array( - 'mask', 'mask', 'strategy', - )); - $acl->addPropertyChangedListener($listener); - - $aces = $acl->{'get'.$type.'s'}(); - $ace = reset($aces); - $this->assertEquals(1, $ace->getMask()); - $this->assertEquals('all', $ace->getStrategy()); - - $acl->{'update'.$type}(0, 3); - $this->assertEquals(3, $ace->getMask()); - $this->assertEquals('all', $ace->getStrategy()); - - $acl->{'update'.$type}(0, 1, 'foo'); - $this->assertEquals(1, $ace->getMask()); - $this->assertEquals('foo', $ace->getStrategy()); - } - - public function getUpdateAceTests() - { - return array( - array('classAce'), - array('objectAce'), - ); - } - - /** - * @dataProvider getUpdateFieldAceTests - * @expectedException \OutOfBoundsException - */ - public function testUpdateFieldAceThrowsExceptionOnInvalidIndex($type) - { - $acl = $this->getAcl(); - $acl->{'update'.$type}(0, 'foo', 1); - } - - /** - * @dataProvider getUpdateFieldAceTests - */ - public function testUpdateFieldAce($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type}('foo', new UserSecurityIdentity('foo', 'Foo'), 1); - - $listener = $this->getListener(array( - 'mask', 'mask', 'strategy', - )); - $acl->addPropertyChangedListener($listener); - - $aces = $acl->{'get'.$type.'s'}('foo'); - $ace = reset($aces); - $this->assertEquals(1, $ace->getMask()); - $this->assertEquals('all', $ace->getStrategy()); - - $acl->{'update'.$type}(0, 'foo', 3); - $this->assertEquals(3, $ace->getMask()); - $this->assertEquals('all', $ace->getStrategy()); - - $acl->{'update'.$type}(0, 'foo', 1, 'foo'); - $this->assertEquals(1, $ace->getMask()); - $this->assertEquals('foo', $ace->getStrategy()); - } - - public function getUpdateFieldAceTests() - { - return array( - array('classFieldAce'), - array('objectFieldAce'), - ); - } - - /** - * @dataProvider getUpdateAuditingTests - * @expectedException \OutOfBoundsException - */ - public function testUpdateAuditingThrowsExceptionOnInvalidIndex($type) - { - $acl = $this->getAcl(); - $acl->{'update'.$type.'Auditing'}(0, true, false); - } - - /** - * @dataProvider getUpdateAuditingTests - */ - public function testUpdateAuditing($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type.'Ace'}(new RoleSecurityIdentity('foo'), 1); - - $listener = $this->getListener(array( - 'auditFailure', 'auditSuccess', 'auditFailure', - )); - $acl->addPropertyChangedListener($listener); - - $aces = $acl->{'get'.$type.'Aces'}(); - $ace = reset($aces); - $this->assertFalse($ace->isAuditSuccess()); - $this->assertFalse($ace->isAuditFailure()); - - $acl->{'update'.$type.'Auditing'}(0, false, true); - $this->assertFalse($ace->isAuditSuccess()); - $this->assertTrue($ace->isAuditFailure()); - - $acl->{'update'.$type.'Auditing'}(0, true, false); - $this->assertTrue($ace->isAuditSuccess()); - $this->assertFalse($ace->isAuditFailure()); - } - - public function getUpdateAuditingTests() - { - return array( - array('class'), - array('object'), - ); - } - - /** - * @expectedException \InvalidArgumentException - * @dataProvider getUpdateFieldAuditingTests - */ - public function testUpdateFieldAuditingThrowsExceptionOnInvalidField($type) - { - $acl = $this->getAcl(); - $acl->{'update'.$type.'Auditing'}(0, 'foo', true, true); - } - - /** - * @expectedException \OutOfBoundsException - * @dataProvider getUpdateFieldAuditingTests - */ - public function testUpdateFieldAuditingThrowsExceptionOnInvalidIndex($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 1); - $acl->{'update'.$type.'Auditing'}(1, 'foo', true, false); - } - - /** - * @dataProvider getUpdateFieldAuditingTests - */ - public function testUpdateFieldAuditing($type) - { - $acl = $this->getAcl(); - $acl->{'insert'.$type.'Ace'}('foo', new RoleSecurityIdentity('foo'), 1); - - $listener = $this->getListener(array( - 'auditSuccess', 'auditSuccess', 'auditFailure', - )); - $acl->addPropertyChangedListener($listener); - - $aces = $acl->{'get'.$type.'Aces'}('foo'); - $ace = reset($aces); - $this->assertFalse($ace->isAuditSuccess()); - $this->assertFalse($ace->isAuditFailure()); - - $acl->{'update'.$type.'Auditing'}(0, 'foo', true, false); - $this->assertTrue($ace->isAuditSuccess()); - $this->assertFalse($ace->isAuditFailure()); - - $acl->{'update'.$type.'Auditing'}(0, 'foo', false, true); - $this->assertFalse($ace->isAuditSuccess()); - $this->assertTrue($ace->isAuditFailure()); - } - - public function getUpdateFieldAuditingTests() - { - return array( - array('classField'), - array('objectField'), - ); - } - - protected function getListener($expectedChanges) - { - $aceProperties = array('aceOrder', 'mask', 'strategy', 'auditSuccess', 'auditFailure'); - - $listener = $this->getMock('Doctrine\Common\PropertyChangedListener'); - foreach ($expectedChanges as $index => $property) { - if (in_array($property, $aceProperties)) { - $class = 'Symfony\Component\Security\Acl\Domain\Entry'; - } else { - $class = 'Symfony\Component\Security\Acl\Domain\Acl'; - } - - $listener - ->expects($this->at($index)) - ->method('propertyChanged') - ->with($this->isInstanceOf($class), $this->equalTo($property)) - ; - } - - return $listener; - } - - protected function getAcl() - { - return new Acl(1, new ObjectIdentity(1, 'foo'), new PermissionGrantingStrategy(), array(), true); - } -} diff --git a/Acl/Tests/Domain/AuditLoggerTest.php b/Acl/Tests/Domain/AuditLoggerTest.php deleted file mode 100644 index 15538d3..0000000 --- a/Acl/Tests/Domain/AuditLoggerTest.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -class AuditLoggerTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getTestLogData - */ - public function testLogIfNeeded($granting, $audit) - { - $logger = $this->getLogger(); - $ace = $this->getEntry(); - - if (true === $granting) { - $ace - ->expects($this->once()) - ->method('isAuditSuccess') - ->will($this->returnValue($audit)) - ; - - $ace - ->expects($this->never()) - ->method('isAuditFailure') - ; - } else { - $ace - ->expects($this->never()) - ->method('isAuditSuccess') - ; - - $ace - ->expects($this->once()) - ->method('isAuditFailure') - ->will($this->returnValue($audit)) - ; - } - - if (true === $audit) { - $logger - ->expects($this->once()) - ->method('doLog') - ->with($this->equalTo($granting), $this->equalTo($ace)) - ; - } else { - $logger - ->expects($this->never()) - ->method('doLog') - ; - } - - $logger->logIfNeeded($granting, $ace); - } - - public function getTestLogData() - { - return array( - array(true, false), - array(true, true), - array(false, false), - array(false, true), - ); - } - - protected function getEntry() - { - return $this->getMock('Symfony\Component\Security\Acl\Model\AuditableEntryInterface'); - } - - protected function getLogger() - { - return $this->getMockForAbstractClass('Symfony\Component\Security\Acl\Domain\AuditLogger'); - } -} diff --git a/Acl/Tests/Domain/DoctrineAclCacheTest.php b/Acl/Tests/Domain/DoctrineAclCacheTest.php deleted file mode 100644 index 255f7f4..0000000 --- a/Acl/Tests/Domain/DoctrineAclCacheTest.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy; -use Symfony\Component\Security\Acl\Domain\Acl; -use Symfony\Component\Security\Acl\Domain\DoctrineAclCache; -use Doctrine\Common\Cache\ArrayCache; - -class DoctrineAclCacheTest extends \PHPUnit_Framework_TestCase -{ - protected $permissionGrantingStrategy; - - /** - * @expectedException \InvalidArgumentException - * @dataProvider getEmptyValue - */ - public function testConstructorDoesNotAcceptEmptyPrefix($empty) - { - new DoctrineAclCache(new ArrayCache(), $this->getPermissionGrantingStrategy(), $empty); - } - - public function getEmptyValue() - { - return array( - array(null), - array(false), - array(''), - ); - } - - public function test() - { - $cache = $this->getCache(); - - $aclWithParent = $this->getAcl(1); - $acl = $this->getAcl(); - - $cache->putInCache($aclWithParent); - $cache->putInCache($acl); - - $cachedAcl = $cache->getFromCacheByIdentity($acl->getObjectIdentity()); - $this->assertEquals($acl->getId(), $cachedAcl->getId()); - $this->assertNull($acl->getParentAcl()); - - $cachedAclWithParent = $cache->getFromCacheByIdentity($aclWithParent->getObjectIdentity()); - $this->assertEquals($aclWithParent->getId(), $cachedAclWithParent->getId()); - $this->assertNotNull($cachedParentAcl = $cachedAclWithParent->getParentAcl()); - $this->assertEquals($aclWithParent->getParentAcl()->getId(), $cachedParentAcl->getId()); - } - - protected function getAcl($depth = 0) - { - static $id = 1; - - $acl = new Acl($id, new ObjectIdentity($id, 'foo'), $this->getPermissionGrantingStrategy(), array(), $depth > 0); - - // insert some ACEs - $sid = new UserSecurityIdentity('johannes', 'Foo'); - $acl->insertClassAce($sid, 1); - $acl->insertClassFieldAce('foo', $sid, 1); - $acl->insertObjectAce($sid, 1); - $acl->insertObjectFieldAce('foo', $sid, 1); - ++$id; - - if ($depth > 0) { - $acl->setParentAcl($this->getAcl($depth - 1)); - } - - return $acl; - } - - protected function getPermissionGrantingStrategy() - { - if (null === $this->permissionGrantingStrategy) { - $this->permissionGrantingStrategy = new PermissionGrantingStrategy(); - } - - return $this->permissionGrantingStrategy; - } - - protected function getCache($cacheDriver = null, $prefix = DoctrineAclCache::PREFIX) - { - if (null === $cacheDriver) { - $cacheDriver = new ArrayCache(); - } - - return new DoctrineAclCache($cacheDriver, $this->getPermissionGrantingStrategy(), $prefix); - } -} diff --git a/Acl/Tests/Domain/EntryTest.php b/Acl/Tests/Domain/EntryTest.php deleted file mode 100644 index ab8e481..0000000 --- a/Acl/Tests/Domain/EntryTest.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\Entry; - -class EntryTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $ace = $this->getAce($acl = $this->getAcl(), $sid = $this->getSid()); - - $this->assertEquals(123, $ace->getId()); - $this->assertSame($acl, $ace->getAcl()); - $this->assertSame($sid, $ace->getSecurityIdentity()); - $this->assertEquals('foostrat', $ace->getStrategy()); - $this->assertEquals(123456, $ace->getMask()); - $this->assertTrue($ace->isGranting()); - $this->assertTrue($ace->isAuditSuccess()); - $this->assertFalse($ace->isAuditFailure()); - } - - public function testSetAuditSuccess() - { - $ace = $this->getAce(); - - $this->assertTrue($ace->isAuditSuccess()); - $ace->setAuditSuccess(false); - $this->assertFalse($ace->isAuditSuccess()); - $ace->setAuditSuccess(true); - $this->assertTrue($ace->isAuditSuccess()); - } - - public function testSetAuditFailure() - { - $ace = $this->getAce(); - - $this->assertFalse($ace->isAuditFailure()); - $ace->setAuditFailure(true); - $this->assertTrue($ace->isAuditFailure()); - $ace->setAuditFailure(false); - $this->assertFalse($ace->isAuditFailure()); - } - - public function testSetMask() - { - $ace = $this->getAce(); - - $this->assertEquals(123456, $ace->getMask()); - $ace->setMask(4321); - $this->assertEquals(4321, $ace->getMask()); - } - - public function testSetStrategy() - { - $ace = $this->getAce(); - - $this->assertEquals('foostrat', $ace->getStrategy()); - $ace->setStrategy('foo'); - $this->assertEquals('foo', $ace->getStrategy()); - } - - public function testSerializeUnserialize() - { - $ace = $this->getAce(); - - $serialized = serialize($ace); - $uAce = unserialize($serialized); - - $this->assertNull($uAce->getAcl()); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface', $uAce->getSecurityIdentity()); - $this->assertEquals($ace->getId(), $uAce->getId()); - $this->assertEquals($ace->getMask(), $uAce->getMask()); - $this->assertEquals($ace->getStrategy(), $uAce->getStrategy()); - $this->assertEquals($ace->isGranting(), $uAce->isGranting()); - $this->assertEquals($ace->isAuditSuccess(), $uAce->isAuditSuccess()); - $this->assertEquals($ace->isAuditFailure(), $uAce->isAuditFailure()); - } - - protected function getAce($acl = null, $sid = null) - { - if (null === $acl) { - $acl = $this->getAcl(); - } - if (null === $sid) { - $sid = $this->getSid(); - } - - return new Entry( - 123, - $acl, - $sid, - 'foostrat', - 123456, - true, - false, - true - ); - } - - protected function getAcl() - { - return $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'); - } - - protected function getSid() - { - return $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface'); - } -} diff --git a/Acl/Tests/Domain/FieldEntryTest.php b/Acl/Tests/Domain/FieldEntryTest.php deleted file mode 100644 index 735e2e8..0000000 --- a/Acl/Tests/Domain/FieldEntryTest.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\FieldEntry; - -class FieldEntryTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $ace = $this->getAce(); - - $this->assertEquals('foo', $ace->getField()); - } - - public function testSerializeUnserialize() - { - $ace = $this->getAce(); - - $serialized = serialize($ace); - $uAce = unserialize($serialized); - - $this->assertNull($uAce->getAcl()); - $this->assertInstanceOf('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface', $uAce->getSecurityIdentity()); - $this->assertEquals($ace->getId(), $uAce->getId()); - $this->assertEquals($ace->getField(), $uAce->getField()); - $this->assertEquals($ace->getMask(), $uAce->getMask()); - $this->assertEquals($ace->getStrategy(), $uAce->getStrategy()); - $this->assertEquals($ace->isGranting(), $uAce->isGranting()); - $this->assertEquals($ace->isAuditSuccess(), $uAce->isAuditSuccess()); - $this->assertEquals($ace->isAuditFailure(), $uAce->isAuditFailure()); - } - - protected function getAce($acl = null, $sid = null) - { - if (null === $acl) { - $acl = $this->getAcl(); - } - if (null === $sid) { - $sid = $this->getSid(); - } - - return new FieldEntry( - 123, - $acl, - 'foo', - $sid, - 'foostrat', - 123456, - true, - false, - true - ); - } - - protected function getAcl() - { - return $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'); - } - - protected function getSid() - { - return $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityInterface'); - } -} diff --git a/Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php b/Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php deleted file mode 100644 index 59fc3bd..0000000 --- a/Acl/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\ObjectIdentityRetrievalStrategy; - -class ObjectIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase -{ - public function testGetObjectIdentityReturnsNullForInvalidDomainObject() - { - $strategy = new ObjectIdentityRetrievalStrategy(); - $this->assertNull($strategy->getObjectIdentity('foo')); - } - - public function testGetObjectIdentity() - { - $strategy = new ObjectIdentityRetrievalStrategy(); - $domainObject = new DomainObject(); - $objectIdentity = $strategy->getObjectIdentity($domainObject); - - $this->assertEquals($domainObject->getId(), $objectIdentity->getIdentifier()); - $this->assertEquals(get_class($domainObject), $objectIdentity->getType()); - } -} - -class DomainObject -{ - public function getId() - { - return 'foo'; - } -} diff --git a/Acl/Tests/Domain/ObjectIdentityTest.php b/Acl/Tests/Domain/ObjectIdentityTest.php deleted file mode 100644 index 770ada7..0000000 --- a/Acl/Tests/Domain/ObjectIdentityTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain -{ - use Symfony\Component\Security\Acl\Domain\ObjectIdentity; - use Symfony\Component\Security\Acl\Model\DomainObjectInterface; - - class ObjectIdentityTest extends \PHPUnit_Framework_TestCase - { - public function testConstructor() - { - $id = new ObjectIdentity('fooid', 'footype'); - - $this->assertEquals('fooid', $id->getIdentifier()); - $this->assertEquals('footype', $id->getType()); - } - - // Test that constructor never changes passed type, even with proxies - public function testConstructorWithProxy() - { - $id = new ObjectIdentity('fooid', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject'); - - $this->assertEquals('fooid', $id->getIdentifier()); - $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType()); - } - - public function testFromDomainObjectPrefersInterfaceOverGetId() - { - $domainObject = new DomainObjectImplementation(); - - $id = ObjectIdentity::fromDomainObject($domainObject); - $this->assertEquals('getObjectIdentifier()', $id->getIdentifier()); - } - - public function testFromDomainObjectWithoutInterface() - { - $id = ObjectIdentity::fromDomainObject(new TestDomainObject()); - $this->assertEquals('getId()', $id->getIdentifier()); - $this->assertEquals('Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType()); - } - - public function testFromDomainObjectWithProxy() - { - $id = ObjectIdentity::fromDomainObject(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject()); - $this->assertEquals('getId()', $id->getIdentifier()); - $this->assertEquals('Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType()); - } - - public function testFromDomainObjectWithoutInterfaceEnforcesStringIdentifier() - { - $domainObject = new TestDomainObject(); - $domainObject->id = 1; - $id = ObjectIdentity::fromDomainObject($domainObject); - - $this->assertSame('1', $id->getIdentifier()); - $this->assertEquals('Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType()); - } - - public function testFromDomainObjectWithoutInterfaceAllowsZeroAsIdentifier() - { - $domainObject = new TestDomainObject(); - $domainObject->id = '0'; - $id = ObjectIdentity::fromDomainObject($domainObject); - - $this->assertSame('0', $id->getIdentifier()); - $this->assertEquals('Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType()); - } - - /** - * @dataProvider getCompareData - */ - public function testEquals($oid1, $oid2, $equal) - { - if ($equal) { - $this->assertTrue($oid1->equals($oid2)); - } else { - $this->assertFalse($oid1->equals($oid2)); - } - } - - public function getCompareData() - { - return array( - array(new ObjectIdentity('123', 'foo'), new ObjectIdentity('123', 'foo'), true), - array(new ObjectIdentity('123', 'foo'), new ObjectIdentity(123, 'foo'), true), - array(new ObjectIdentity('1', 'foo'), new ObjectIdentity('2', 'foo'), false), - array(new ObjectIdentity('1', 'bla'), new ObjectIdentity('1', 'blub'), false), - ); - } - } - - class TestDomainObject - { - public $id = 'getId()'; - - public function getObjectIdentifier() - { - return 'getObjectIdentifier()'; - } - - public function getId() - { - return $this->id; - } - } - - class DomainObjectImplementation implements DomainObjectInterface - { - public function getObjectIdentifier() - { - return 'getObjectIdentifier()'; - } - - public function getId() - { - return 'getId()'; - } - } -} - -namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain -{ - class TestDomainObject extends \Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject - { - } -} diff --git a/Acl/Tests/Domain/PermissionGrantingStrategyTest.php b/Acl/Tests/Domain/PermissionGrantingStrategyTest.php deleted file mode 100644 index 34ef690..0000000 --- a/Acl/Tests/Domain/PermissionGrantingStrategyTest.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\Acl; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy; -use Symfony\Component\Security\Acl\Exception\NoAceFoundException; - -class PermissionGrantingStrategyTest extends \PHPUnit_Framework_TestCase -{ - public function testIsGrantedObjectAcesHavePriority() - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $acl->insertClassAce($sid, 1); - $acl->insertObjectAce($sid, 1, 0, false); - $this->assertFalse($strategy->isGranted($acl, array(1), array($sid))); - } - - public function testIsGrantedFallsBackToClassAcesIfNoApplicableObjectAceWasFound() - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $acl->insertClassAce($sid, 1); - $this->assertTrue($strategy->isGranted($acl, array(1), array($sid))); - } - - public function testIsGrantedFavorsLocalAcesOverParentAclAces() - { - $strategy = new PermissionGrantingStrategy(); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $acl = $this->getAcl($strategy); - $acl->insertClassAce($sid, 1); - - $parentAcl = $this->getAcl($strategy); - $acl->setParentAcl($parentAcl); - $parentAcl->insertClassAce($sid, 1, 0, false); - - $this->assertTrue($strategy->isGranted($acl, array(1), array($sid))); - } - - public function testIsGrantedFallsBackToParentAcesIfNoLocalAcesAreApplicable() - { - $strategy = new PermissionGrantingStrategy(); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - $anotherSid = new UserSecurityIdentity('ROLE_USER', 'Foo'); - - $acl = $this->getAcl($strategy); - $acl->insertClassAce($anotherSid, 1, 0, false); - - $parentAcl = $this->getAcl($strategy); - $acl->setParentAcl($parentAcl); - $parentAcl->insertClassAce($sid, 1); - - $this->assertTrue($strategy->isGranted($acl, array(1), array($sid))); - } - - /** - * @expectedException \Symfony\Component\Security\Acl\Exception\NoAceFoundException - */ - public function testIsGrantedReturnsExceptionIfNoAceIsFound() - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $strategy->isGranted($acl, array(1), array($sid)); - } - - public function testIsGrantedFirstApplicableEntryMakesUltimateDecisionForPermissionIdentityCombination() - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - $aSid = new RoleSecurityIdentity('ROLE_USER'); - - $acl->insertClassAce($aSid, 1); - $acl->insertClassAce($sid, 1, 1, false); - $acl->insertClassAce($sid, 1, 2); - $this->assertFalse($strategy->isGranted($acl, array(1), array($sid, $aSid))); - - $acl->insertObjectAce($sid, 1, 0, false); - $acl->insertObjectAce($aSid, 1, 1); - $this->assertFalse($strategy->isGranted($acl, array(1), array($sid, $aSid))); - } - - public function testIsGrantedCallsAuditLoggerOnGrant() - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $logger = $this->getMock('Symfony\Component\Security\Acl\Model\AuditLoggerInterface'); - $logger - ->expects($this->once()) - ->method('logIfNeeded') - ; - $strategy->setAuditLogger($logger); - - $acl->insertObjectAce($sid, 1); - $acl->updateObjectAuditing(0, true, false); - - $this->assertTrue($strategy->isGranted($acl, array(1), array($sid))); - } - - public function testIsGrantedCallsAuditLoggerOnDeny() - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $logger = $this->getMock('Symfony\Component\Security\Acl\Model\AuditLoggerInterface'); - $logger - ->expects($this->once()) - ->method('logIfNeeded') - ; - $strategy->setAuditLogger($logger); - - $acl->insertObjectAce($sid, 1, 0, false); - $acl->updateObjectAuditing(0, false, true); - - $this->assertFalse($strategy->isGranted($acl, array(1), array($sid))); - } - - /** - * @dataProvider getAllStrategyTests - */ - public function testIsGrantedStrategies($maskStrategy, $aceMask, $requiredMask, $result) - { - $strategy = new PermissionGrantingStrategy(); - $acl = $this->getAcl($strategy); - $sid = new UserSecurityIdentity('johannes', 'Foo'); - - $acl->insertObjectAce($sid, $aceMask, 0, true, $maskStrategy); - - if (false === $result) { - try { - $strategy->isGranted($acl, array($requiredMask), array($sid)); - $this->fail('The ACE is not supposed to match.'); - } catch (NoAceFoundException $e) { - } - } else { - $this->assertTrue($strategy->isGranted($acl, array($requiredMask), array($sid))); - } - } - - public function getAllStrategyTests() - { - return array( - array('all', 1 << 0 | 1 << 1, 1 << 0, true), - array('all', 1 << 0 | 1 << 1, 1 << 2, false), - array('all', 1 << 0 | 1 << 10, 1 << 0 | 1 << 10, true), - array('all', 1 << 0 | 1 << 1, 1 << 0 | 1 << 1 || 1 << 2, false), - array('any', 1 << 0 | 1 << 1, 1 << 0, true), - array('any', 1 << 0 | 1 << 1, 1 << 0 | 1 << 2, true), - array('any', 1 << 0 | 1 << 1, 1 << 2, false), - array('equal', 1 << 0 | 1 << 1, 1 << 0, false), - array('equal', 1 << 0 | 1 << 1, 1 << 1, false), - array('equal', 1 << 0 | 1 << 1, 1 << 0 | 1 << 1, true), - ); - } - - protected function getAcl($strategy) - { - static $id = 1; - - return new Acl($id++, new ObjectIdentity(1, 'Foo'), $strategy, array(), true); - } -} diff --git a/Acl/Tests/Domain/RoleSecurityIdentityTest.php b/Acl/Tests/Domain/RoleSecurityIdentityTest.php deleted file mode 100644 index ad5f236..0000000 --- a/Acl/Tests/Domain/RoleSecurityIdentityTest.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Core\Role\Role; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; - -class RoleSecurityIdentityTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $id = new RoleSecurityIdentity('ROLE_FOO'); - - $this->assertEquals('ROLE_FOO', $id->getRole()); - } - - public function testConstructorWithRoleInstance() - { - $id = new RoleSecurityIdentity(new Role('ROLE_FOO')); - - $this->assertEquals('ROLE_FOO', $id->getRole()); - } - - /** - * @dataProvider getCompareData - */ - public function testEquals($id1, $id2, $equal) - { - if ($equal) { - $this->assertTrue($id1->equals($id2)); - } else { - $this->assertFalse($id1->equals($id2)); - } - } - - public function getCompareData() - { - return array( - array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_FOO'), true), - array(new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity(new Role('ROLE_FOO')), true), - array(new RoleSecurityIdentity('ROLE_USER'), new RoleSecurityIdentity('ROLE_FOO'), false), - array(new RoleSecurityIdentity('ROLE_FOO'), new UserSecurityIdentity('ROLE_FOO', 'Foo'), false), - ); - } -} diff --git a/Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php b/Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php deleted file mode 100644 index 160c27c..0000000 --- a/Acl/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\SecurityIdentityRetrievalStrategy; - -class SecurityIdentityRetrievalStrategyTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getSecurityIdentityRetrievalTests - */ - public function testGetSecurityIdentities($user, array $roles, $authenticationStatus, array $sids) - { - $strategy = $this->getStrategy($roles, $authenticationStatus); - - if ('anonymous' === $authenticationStatus) { - $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken') - ->disableOriginalConstructor() - ->getMock(); - } else { - $class = ''; - if (is_string($user)) { - $class = 'MyCustomTokenImpl'; - } - - $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface') - ->setMockClassName($class) - ->getMock(); - } - $token - ->expects($this->once()) - ->method('getRoles') - ->will($this->returnValue(array('foo'))) - ; - if ('anonymous' === $authenticationStatus) { - $token - ->expects($this->never()) - ->method('getUser') - ; - } else { - $token - ->expects($this->once()) - ->method('getUser') - ->will($this->returnValue($user)) - ; - } - - $extractedSids = $strategy->getSecurityIdentities($token); - - foreach ($extractedSids as $index => $extractedSid) { - if (!isset($sids[$index])) { - $this->fail(sprintf('Expected SID at index %d, but there was none.', true)); - } - - if (false === $sids[$index]->equals($extractedSid)) { - $this->fail(sprintf('Index: %d, expected SID "%s", but got "%s".', $index, $sids[$index], $extractedSid)); - } - } - } - - public function getSecurityIdentityRetrievalTests() - { - return array( - array($this->getAccount('johannes', 'FooUser'), array('ROLE_USER', 'ROLE_SUPERADMIN'), 'fullFledged', array( - new UserSecurityIdentity('johannes', 'FooUser'), - new RoleSecurityIdentity('ROLE_USER'), - new RoleSecurityIdentity('ROLE_SUPERADMIN'), - new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'), - new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'), - new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'), - )), - array('johannes', array('ROLE_FOO'), 'fullFledged', array( - new UserSecurityIdentity('johannes', 'MyCustomTokenImpl'), - new RoleSecurityIdentity('ROLE_FOO'), - new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'), - new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'), - new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'), - )), - array(new CustomUserImpl('johannes'), array('ROLE_FOO'), 'fullFledged', array( - new UserSecurityIdentity('johannes', 'Symfony\Component\Security\Acl\Tests\Domain\CustomUserImpl'), - new RoleSecurityIdentity('ROLE_FOO'), - new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'), - new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'), - new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'), - )), - array($this->getAccount('foo', 'FooBarUser'), array('ROLE_FOO'), 'rememberMe', array( - new UserSecurityIdentity('foo', 'FooBarUser'), - new RoleSecurityIdentity('ROLE_FOO'), - new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'), - new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'), - )), - array('guest', array('ROLE_FOO'), 'anonymous', array( - new RoleSecurityIdentity('ROLE_FOO'), - new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY'), - )), - ); - } - - protected function getAccount($username, $class) - { - $account = $this->getMock('Symfony\Component\Security\Core\User\UserInterface', array(), array(), $class); - $account - ->expects($this->any()) - ->method('getUsername') - ->will($this->returnValue($username)) - ; - - return $account; - } - - protected function getStrategy(array $roles = array(), $authenticationStatus = 'fullFledged') - { - $roleHierarchy = $this->getMock('Symfony\Component\Security\Core\Role\RoleHierarchyInterface'); - $roleHierarchy - ->expects($this->once()) - ->method('getReachableRoles') - ->with($this->equalTo(array('foo'))) - ->will($this->returnValue($roles)) - ; - - $trustResolver = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver', array(), array('', '')); - - $trustResolver - ->expects($this->at(0)) - ->method('isAnonymous') - ->will($this->returnValue('anonymous' === $authenticationStatus)) - ; - - if ('fullFledged' === $authenticationStatus) { - $trustResolver - ->expects($this->once()) - ->method('isFullFledged') - ->will($this->returnValue(true)) - ; - $trustResolver - ->expects($this->never()) - ->method('isRememberMe') - ; - } elseif ('rememberMe' === $authenticationStatus) { - $trustResolver - ->expects($this->once()) - ->method('isFullFledged') - ->will($this->returnValue(false)) - ; - $trustResolver - ->expects($this->once()) - ->method('isRememberMe') - ->will($this->returnValue(true)) - ; - } else { - $trustResolver - ->expects($this->at(1)) - ->method('isAnonymous') - ->will($this->returnValue(true)) - ; - $trustResolver - ->expects($this->once()) - ->method('isFullFledged') - ->will($this->returnValue(false)) - ; - $trustResolver - ->expects($this->once()) - ->method('isRememberMe') - ->will($this->returnValue(false)) - ; - } - - return new SecurityIdentityRetrievalStrategy($roleHierarchy, $trustResolver); - } -} - -class CustomUserImpl -{ - protected $name; - - public function __construct($name) - { - $this->name = $name; - } - - public function __toString() - { - return $this->name; - } -} diff --git a/Acl/Tests/Domain/UserSecurityIdentityTest.php b/Acl/Tests/Domain/UserSecurityIdentityTest.php deleted file mode 100644 index 09d3f0d..0000000 --- a/Acl/Tests/Domain/UserSecurityIdentityTest.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Domain; - -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; - -class UserSecurityIdentityTest extends \PHPUnit_Framework_TestCase -{ - public function testConstructor() - { - $id = new UserSecurityIdentity('foo', 'Foo'); - - $this->assertEquals('foo', $id->getUsername()); - $this->assertEquals('Foo', $id->getClass()); - } - - // Test that constructor never changes the type, even for proxies - public function testConstructorWithProxy() - { - $id = new UserSecurityIdentity('foo', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\Foo'); - - $this->assertEquals('foo', $id->getUsername()); - $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\Foo', $id->getClass()); - } - - /** - * @dataProvider getCompareData - */ - public function testEquals($id1, $id2, $equal) - { - $this->assertSame($equal, $id1->equals($id2)); - } - - public function getCompareData() - { - $account = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface') - ->setMockClassName('USI_AccountImpl') - ->getMock(); - $account - ->expects($this->any()) - ->method('getUsername') - ->will($this->returnValue('foo')) - ; - - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); - $token - ->expects($this->any()) - ->method('getUser') - ->will($this->returnValue($account)) - ; - - return array( - array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('foo', 'Foo'), true), - array(new UserSecurityIdentity('foo', 'Bar'), new UserSecurityIdentity('foo', 'Foo'), false), - array(new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('bar', 'Foo'), false), - array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromAccount($account), false), - array(new UserSecurityIdentity('bla', 'Foo'), new UserSecurityIdentity('blub', 'Foo'), false), - array(new UserSecurityIdentity('foo', 'Foo'), new RoleSecurityIdentity('foo'), false), - array(new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromToken($token), false), - array(new UserSecurityIdentity('foo', 'USI_AccountImpl'), UserSecurityIdentity::fromToken($token), true), - ); - } -} diff --git a/Acl/Tests/Permission/BasicPermissionMapTest.php b/Acl/Tests/Permission/BasicPermissionMapTest.php deleted file mode 100644 index 2afe588..0000000 --- a/Acl/Tests/Permission/BasicPermissionMapTest.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Permission; - -use Symfony\Component\Security\Acl\Permission\BasicPermissionMap; - -class BasicPermissionMapTest extends \PHPUnit_Framework_TestCase -{ - public function testGetMasksReturnsNullWhenNotSupportedMask() - { - $map = new BasicPermissionMap(); - $this->assertNull($map->getMasks('IS_AUTHENTICATED_REMEMBERED', null)); - } -} diff --git a/Acl/Tests/Permission/MaskBuilderTest.php b/Acl/Tests/Permission/MaskBuilderTest.php deleted file mode 100644 index 8245669..0000000 --- a/Acl/Tests/Permission/MaskBuilderTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Permission; - -use Symfony\Component\Security\Acl\Permission\MaskBuilder; - -class MaskBuilderTest extends \PHPUnit_Framework_TestCase -{ - /** - * @expectedException \InvalidArgumentException - * @dataProvider getInvalidConstructorData - */ - public function testConstructorWithNonInteger($invalidMask) - { - new MaskBuilder($invalidMask); - } - - public function getInvalidConstructorData() - { - return array( - array(234.463), - array('asdgasdf'), - array(array()), - array(new \stdClass()), - ); - } - - public function testConstructorWithoutArguments() - { - $builder = new MaskBuilder(); - - $this->assertEquals(0, $builder->get()); - } - - public function testConstructor() - { - $builder = new MaskBuilder(123456); - - $this->assertEquals(123456, $builder->get()); - } - - public function testAddAndRemove() - { - $builder = new MaskBuilder(); - - $builder - ->add('view') - ->add('eDiT') - ->add('ownEr') - ; - $mask = $builder->get(); - - $this->assertEquals(MaskBuilder::MASK_VIEW, $mask & MaskBuilder::MASK_VIEW); - $this->assertEquals(MaskBuilder::MASK_EDIT, $mask & MaskBuilder::MASK_EDIT); - $this->assertEquals(MaskBuilder::MASK_OWNER, $mask & MaskBuilder::MASK_OWNER); - $this->assertEquals(0, $mask & MaskBuilder::MASK_MASTER); - $this->assertEquals(0, $mask & MaskBuilder::MASK_CREATE); - $this->assertEquals(0, $mask & MaskBuilder::MASK_DELETE); - $this->assertEquals(0, $mask & MaskBuilder::MASK_UNDELETE); - - $builder->remove('edit')->remove('OWner'); - $mask = $builder->get(); - $this->assertEquals(0, $mask & MaskBuilder::MASK_EDIT); - $this->assertEquals(0, $mask & MaskBuilder::MASK_OWNER); - $this->assertEquals(MaskBuilder::MASK_VIEW, $mask & MaskBuilder::MASK_VIEW); - } - - public function testGetPattern() - { - $builder = new MaskBuilder(); - $this->assertEquals(MaskBuilder::ALL_OFF, $builder->getPattern()); - - $builder->add('view'); - $this->assertEquals(str_repeat('.', 31).'V', $builder->getPattern()); - - $builder->add('owner'); - $this->assertEquals(str_repeat('.', 24).'N......V', $builder->getPattern()); - - $builder->add(1 << 10); - $this->assertEquals(str_repeat('.', 21).MaskBuilder::ON.'..N......V', $builder->getPattern()); - } - - public function testReset() - { - $builder = new MaskBuilder(); - $this->assertEquals(0, $builder->get()); - - $builder->add('view'); - $this->assertTrue($builder->get() > 0); - - $builder->reset(); - $this->assertEquals(0, $builder->get()); - } -} diff --git a/Acl/Tests/Voter/AclVoterTest.php b/Acl/Tests/Voter/AclVoterTest.php deleted file mode 100644 index 2148135..0000000 --- a/Acl/Tests/Voter/AclVoterTest.php +++ /dev/null @@ -1,432 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Tests\Voter; - -use Symfony\Component\Security\Acl\Exception\NoAceFoundException; -use Symfony\Component\Security\Acl\Voter\FieldVote; -use Symfony\Component\Security\Acl\Exception\AclNotFoundException; -use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity; -use Symfony\Component\Security\Acl\Domain\ObjectIdentity; -use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; -use Symfony\Component\Security\Acl\Voter\AclVoter; - -class AclVoterTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider getSupportsAttributeTests - */ - public function testSupportsAttribute($attribute, $supported) - { - list($voter, , $permissionMap) = $this->getVoter(true, false); - - $permissionMap - ->expects($this->once()) - ->method('contains') - ->with($this->identicalTo($attribute)) - ->will($this->returnValue($supported)) - ; - - $this->assertSame($supported, $voter->supportsAttribute($attribute)); - } - - /** - * @dataProvider getSupportsAttributeNonStringTests - */ - public function testSupportsAttributeNonString($attribute) - { - list($voter) = $this->getVoter(true, false); - - $this->assertFalse($voter->supportsAttribute($attribute)); - } - - public function getSupportsAttributeTests() - { - return array( - array('foo', true), - array('foo', false), - ); - } - - public function getSupportsAttributeNonStringTests() - { - return array( - array(new \stdClass()), - array(1), - array(true), - array(array()), - ); - } - - /** - * @dataProvider getSupportsClassTests - */ - public function testSupportsClass($class) - { - list($voter) = $this->getVoter(); - - $this->assertTrue($voter->supportsClass($class)); - } - - public function getSupportsClassTests() - { - return array( - array('foo'), - array('bar'), - array('moo'), - ); - } - - public function testVote() - { - list($voter, , $permissionMap) = $this->getVoter(); - $permissionMap - ->expects($this->atLeastOnce()) - ->method('getMasks') - ->will($this->returnValue(null)) - ; - - $this->assertSame(VoterInterface::ACCESS_ABSTAIN, $voter->vote($this->getToken(), null, array('VIEW', 'EDIT', 'DELETE'))); - } - - /** - * @dataProvider getTrueFalseTests - */ - public function testVoteWhenNoObjectIsPassed($allowIfObjectIdentityUnavailable) - { - list($voter, , $permissionMap) = $this->getVoter($allowIfObjectIdentityUnavailable); - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->will($this->returnValue(array())) - ; - - if ($allowIfObjectIdentityUnavailable) { - $vote = VoterInterface::ACCESS_GRANTED; - } else { - $vote = VoterInterface::ACCESS_ABSTAIN; - } - - $this->assertSame($vote, $voter->vote($this->getToken(), null, array('VIEW'))); - } - - /** - * @dataProvider getTrueFalseTests - */ - public function testVoteWhenOidStrategyReturnsNull($allowIfUnavailable) - { - list($voter, , $permissionMap, $oidStrategy) = $this->getVoter($allowIfUnavailable); - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->will($this->returnValue(array())) - ; - - $oidStrategy - ->expects($this->once()) - ->method('getObjectIdentity') - ->will($this->returnValue(null)) - ; - - if ($allowIfUnavailable) { - $vote = VoterInterface::ACCESS_GRANTED; - } else { - $vote = VoterInterface::ACCESS_ABSTAIN; - } - - $this->assertSame($vote, $voter->vote($this->getToken(), new \stdClass(), array('VIEW'))); - } - - public function getTrueFalseTests() - { - return array(array(true), array(false)); - } - - public function testVoteNoAclFound() - { - list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter(); - - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->will($this->returnValue(array())) - ; - - $oidStrategy - ->expects($this->once()) - ->method('getObjectIdentity') - ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo'))) - ; - - $sidStrategy - ->expects($this->once()) - ->method('getSecurityIdentities') - ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO')))) - ; - - $provider - ->expects($this->once()) - ->method('findAcl') - ->with($this->equalTo($oid), $this->equalTo($sids)) - ->will($this->throwException(new AclNotFoundException('Not found.'))) - ; - - $this->assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->getToken(), new \stdClass(), array('VIEW'))); - } - - /** - * @dataProvider getTrueFalseTests - */ - public function testVoteGrantsAccess($grant) - { - list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter(); - - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->with($this->equalTo('VIEW')) - ->will($this->returnValue($masks = array(1, 2, 3))) - ; - - $oidStrategy - ->expects($this->once()) - ->method('getObjectIdentity') - ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo'))) - ; - - $sidStrategy - ->expects($this->once()) - ->method('getSecurityIdentities') - ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO')))) - ; - - $provider - ->expects($this->once()) - ->method('findAcl') - ->with($this->equalTo($oid), $this->equalTo($sids)) - ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'))) - ; - - $acl - ->expects($this->once()) - ->method('isGranted') - ->with($this->identicalTo($masks), $this->equalTo($sids), $this->isFalse()) - ->will($this->returnValue($grant)) - ; - - if ($grant) { - $vote = VoterInterface::ACCESS_GRANTED; - } else { - $vote = VoterInterface::ACCESS_DENIED; - } - - $this->assertSame($vote, $voter->vote($this->getToken(), new \stdClass(), array('VIEW'))); - } - - public function testVoteNoAceFound() - { - list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter(); - - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->with($this->equalTo('VIEW')) - ->will($this->returnValue($masks = array(1, 2, 3))) - ; - - $oidStrategy - ->expects($this->once()) - ->method('getObjectIdentity') - ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo'))) - ; - - $sidStrategy - ->expects($this->once()) - ->method('getSecurityIdentities') - ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO')))) - ; - - $provider - ->expects($this->once()) - ->method('findAcl') - ->with($this->equalTo($oid), $this->equalTo($sids)) - ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'))) - ; - - $acl - ->expects($this->once()) - ->method('isGranted') - ->with($this->identicalTo($masks), $this->equalTo($sids), $this->isFalse()) - ->will($this->throwException(new NoAceFoundException('No ACE'))) - ; - - $this->assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->getToken(), new \stdClass(), array('VIEW'))); - } - - /** - * @dataProvider getTrueFalseTests - */ - public function testVoteGrantsFieldAccess($grant) - { - list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter(); - - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->with($this->equalTo('VIEW')) - ->will($this->returnValue($masks = array(1, 2, 3))) - ; - - $oidStrategy - ->expects($this->once()) - ->method('getObjectIdentity') - ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo'))) - ; - - $sidStrategy - ->expects($this->once()) - ->method('getSecurityIdentities') - ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO')))) - ; - - $provider - ->expects($this->once()) - ->method('findAcl') - ->with($this->equalTo($oid), $this->equalTo($sids)) - ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'))) - ; - - $acl - ->expects($this->once()) - ->method('isFieldGranted') - ->with($this->identicalTo('foo'), $this->identicalTo($masks), $this->equalTo($sids), $this->isFalse()) - ->will($this->returnValue($grant)) - ; - - if ($grant) { - $vote = VoterInterface::ACCESS_GRANTED; - } else { - $vote = VoterInterface::ACCESS_DENIED; - } - - $this->assertSame($vote, $voter->vote($this->getToken(), new FieldVote(new \stdClass(), 'foo'), array('VIEW'))); - } - - public function testVoteNoFieldAceFound() - { - list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter(); - - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->with($this->equalTo('VIEW')) - ->will($this->returnValue($masks = array(1, 2, 3))) - ; - - $oidStrategy - ->expects($this->once()) - ->method('getObjectIdentity') - ->will($this->returnValue($oid = new ObjectIdentity('1', 'Foo'))) - ; - - $sidStrategy - ->expects($this->once()) - ->method('getSecurityIdentities') - ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO')))) - ; - - $provider - ->expects($this->once()) - ->method('findAcl') - ->with($this->equalTo($oid), $this->equalTo($sids)) - ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'))) - ; - - $acl - ->expects($this->once()) - ->method('isFieldGranted') - ->with($this->identicalTo('foo'), $this->identicalTo($masks), $this->equalTo($sids), $this->isFalse()) - ->will($this->throwException(new NoAceFoundException('No ACE'))) - ; - - $this->assertSame(VoterInterface::ACCESS_DENIED, $voter->vote($this->getToken(), new FieldVote(new \stdClass(), 'foo'), array('VIEW'))); - } - - public function testWhenReceivingAnObjectIdentityInterfaceWeDontRetrieveANewObjectIdentity() - { - list($voter, $provider, $permissionMap, $oidStrategy, $sidStrategy) = $this->getVoter(); - - $oid = new ObjectIdentity('someID', 'someType'); - - $permissionMap - ->expects($this->once()) - ->method('getMasks') - ->with($this->equalTo('VIEW')) - ->will($this->returnValue($masks = array(1, 2, 3))) - ; - - $oidStrategy - ->expects($this->never()) - ->method('getObjectIdentity') - ; - - $sidStrategy - ->expects($this->once()) - ->method('getSecurityIdentities') - ->will($this->returnValue($sids = array(new UserSecurityIdentity('johannes', 'Foo'), new RoleSecurityIdentity('ROLE_FOO')))) - ; - - $provider - ->expects($this->once()) - ->method('findAcl') - ->with($this->equalTo($oid), $this->equalTo($sids)) - ->will($this->returnValue($acl = $this->getMock('Symfony\Component\Security\Acl\Model\AclInterface'))) - ; - - $acl - ->expects($this->once()) - ->method('isGranted') - ->with($this->identicalTo($masks), $this->equalTo($sids), $this->isFalse()) - ->will($this->throwException(new NoAceFoundException('No ACE'))) - ; - - $voter->vote($this->getToken(), $oid, array('VIEW')); - } - - protected function getToken() - { - return $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); - } - - protected function getVoter($allowIfObjectIdentityUnavailable = true, $alwaysContains = true) - { - $provider = $this->getMock('Symfony\Component\Security\Acl\Model\AclProviderInterface'); - $permissionMap = $this->getMock('Symfony\Component\Security\Acl\Permission\PermissionMapInterface'); - $oidStrategy = $this->getMock('Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface'); - $sidStrategy = $this->getMock('Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface'); - - if ($alwaysContains) { - $permissionMap - ->expects($this->any()) - ->method('contains') - ->will($this->returnValue(true)); - } - - return array( - new AclVoter($provider, $oidStrategy, $sidStrategy, $permissionMap, null, $allowIfObjectIdentityUnavailable), - $provider, - $permissionMap, - $oidStrategy, - $sidStrategy, - ); - } -} diff --git a/Acl/Voter/AclVoter.php b/Acl/Voter/AclVoter.php deleted file mode 100644 index ec6024a..0000000 --- a/Acl/Voter/AclVoter.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Voter; - -use Psr\Log\LoggerInterface; -use Symfony\Component\Security\Acl\Exception\NoAceFoundException; -use Symfony\Component\Security\Acl\Exception\AclNotFoundException; -use Symfony\Component\Security\Acl\Model\AclProviderInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface; -use Symfony\Component\Security\Acl\Permission\PermissionMapInterface; -use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface; -use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; - -/** - * This voter can be used as a base class for implementing your own permissions. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class AclVoter implements VoterInterface -{ - private $aclProvider; - private $permissionMap; - private $objectIdentityRetrievalStrategy; - private $securityIdentityRetrievalStrategy; - private $allowIfObjectIdentityUnavailable; - private $logger; - - public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, LoggerInterface $logger = null, $allowIfObjectIdentityUnavailable = true) - { - $this->aclProvider = $aclProvider; - $this->permissionMap = $permissionMap; - $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy; - $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy; - $this->logger = $logger; - $this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable; - } - - public function supportsAttribute($attribute) - { - return is_string($attribute) && $this->permissionMap->contains($attribute); - } - - public function vote(TokenInterface $token, $object, array $attributes) - { - foreach ($attributes as $attribute) { - if (!$this->supportsAttribute($attribute)) { - continue; - } - - if (null === $masks = $this->permissionMap->getMasks($attribute, $object)) { - continue; - } - - if (null === $object) { - if (null !== $this->logger) { - $this->logger->debug(sprintf('Object identity unavailable. Voting to %s.', $this->allowIfObjectIdentityUnavailable ? 'grant access' : 'abstain')); - } - - return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN; - } elseif ($object instanceof FieldVote) { - $field = $object->getField(); - $object = $object->getDomainObject(); - } else { - $field = null; - } - - if ($object instanceof ObjectIdentityInterface) { - $oid = $object; - } elseif (null === $oid = $this->objectIdentityRetrievalStrategy->getObjectIdentity($object)) { - if (null !== $this->logger) { - $this->logger->debug(sprintf('Object identity unavailable. Voting to %s.', $this->allowIfObjectIdentityUnavailable ? 'grant access' : 'abstain')); - } - - return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN; - } - - if (!$this->supportsClass($oid->getType())) { - return self::ACCESS_ABSTAIN; - } - - $sids = $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token); - - try { - $acl = $this->aclProvider->findAcl($oid, $sids); - - if (null === $field && $acl->isGranted($masks, $sids, false)) { - if (null !== $this->logger) { - $this->logger->debug('ACL found, permission granted. Voting to grant access.'); - } - - return self::ACCESS_GRANTED; - } elseif (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) { - if (null !== $this->logger) { - $this->logger->debug('ACL found, permission granted. Voting to grant access.'); - } - - return self::ACCESS_GRANTED; - } - - if (null !== $this->logger) { - $this->logger->debug('ACL found, insufficient permissions. Voting to deny access.'); - } - - return self::ACCESS_DENIED; - } catch (AclNotFoundException $e) { - if (null !== $this->logger) { - $this->logger->debug('No ACL found for the object identity. Voting to deny access.'); - } - - return self::ACCESS_DENIED; - } catch (NoAceFoundException $e) { - if (null !== $this->logger) { - $this->logger->debug('ACL found, no ACE applicable. Voting to deny access.'); - } - - return self::ACCESS_DENIED; - } - } - - // no attribute was supported - return self::ACCESS_ABSTAIN; - } - - /** - * You can override this method when writing a voter for a specific domain - * class. - * - * @param string $class The class name - * - * @return bool - */ - public function supportsClass($class) - { - return true; - } -} diff --git a/Acl/Voter/FieldVote.php b/Acl/Voter/FieldVote.php deleted file mode 100644 index 8782f76..0000000 --- a/Acl/Voter/FieldVote.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Acl\Voter; - -/** - * This class is a lightweight wrapper around field vote requests which does - * not violate any interface contracts. - * - * @author Johannes M. Schmitt <schmittjoh@gmail.com> - */ -class FieldVote -{ - private $domainObject; - private $field; - - public function __construct($domainObject, $field) - { - $this->domainObject = $domainObject; - $this->field = $field; - } - - public function getDomainObject() - { - return $this->domainObject; - } - - public function getField() - { - return $this->field; - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 22eb9cd..b33f053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,26 @@ CHANGELOG ========= +2.8.0 +----- + + * deprecated `getKey()` of the `AnonymousToken`, `RememberMeToken`, + `AbstractRememberMeServices` and `DigestAuthenticationEntryPoint` classes in favor of `getSecret()`. + * deprecated `Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface`, use + `Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface` instead + * deprecated `Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface`, use + `Symfony\Component\Security\Http\Authentication\SimpleFormAuthenticatorInterface` instead + * deprecated `Symfony\Component\Security\Core\Util\ClassUtils`, use + `Symfony\Component\Security\Acl\Util\ClassUtils` instead + * deprecated the `Symfony\Component\Security\Core\Util\SecureRandom` class in favor of the `random_bytes()` function + * deprecated `supportsAttribute()` and `supportsClass()` methods of + `Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface` and + `Symfony\Component\Security\Core\Authorization\Voter\VoterInterface`. + * deprecated `getSupportedAttributes()` and `getSupportedClasses()` methods of + `Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter`, use `supports()` instead. + * deprecated the `intention` option for all the authentication listeners, + use the `csrf_token_id` option instead. + 2.7.0 ----- diff --git a/Core/Authentication/Provider/AnonymousAuthenticationProvider.php b/Core/Authentication/Provider/AnonymousAuthenticationProvider.php index 7fbbf85..ff3d15f 100644 --- a/Core/Authentication/Provider/AnonymousAuthenticationProvider.php +++ b/Core/Authentication/Provider/AnonymousAuthenticationProvider.php @@ -22,16 +22,22 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; */ class AnonymousAuthenticationProvider implements AuthenticationProviderInterface { - private $key; + /** + * Used to determine if the token is created by the application + * instead of a malicious client. + * + * @var string + */ + private $secret; /** * Constructor. * - * @param string $key The key shared with the authentication token + * @param string $secret The secret shared with the AnonymousToken */ - public function __construct($key) + public function __construct($secret) { - $this->key = $key; + $this->secret = $secret; } /** @@ -43,7 +49,7 @@ class AnonymousAuthenticationProvider implements AuthenticationProviderInterface return; } - if ($this->key !== $token->getKey()) { + if ($this->secret !== $token->getSecret()) { throw new BadCredentialsException('The Token does not contain the expected key.'); } diff --git a/Core/Authentication/Provider/LdapBindAuthenticationProvider.php b/Core/Authentication/Provider/LdapBindAuthenticationProvider.php new file mode 100644 index 0000000..e887f99 --- /dev/null +++ b/Core/Authentication/Provider/LdapBindAuthenticationProvider.php @@ -0,0 +1,89 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authentication\Provider; + +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Ldap\LdapClientInterface; +use Symfony\Component\Ldap\Exception\ConnectionException; + +/** + * LdapBindAuthenticationProvider authenticates a user against an LDAP server. + * + * The only way to check user credentials is to try to connect the user with its + * credentials to the ldap. + * + * @author Charles Sarrazin <charles@sarraz.in> + */ +class LdapBindAuthenticationProvider extends UserAuthenticationProvider +{ + private $userProvider; + private $ldap; + private $dnString; + + /** + * Constructor. + * + * @param UserProviderInterface $userProvider A UserProvider + * @param UserCheckerInterface $userChecker A UserChecker + * @param string $providerKey The provider key + * @param LdapClientInterface $ldap An Ldap client + * @param string $dnString A string used to create the bind DN + * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not + */ + public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, LdapClientInterface $ldap, $dnString = '{username}', $hideUserNotFoundExceptions = true) + { + parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions); + + $this->userProvider = $userProvider; + $this->ldap = $ldap; + $this->dnString = $dnString; + } + + /** + * {@inheritdoc} + */ + protected function retrieveUser($username, UsernamePasswordToken $token) + { + if ('NONE_PROVIDED' === $username) { + throw new UsernameNotFoundException('Username can not be null'); + } + + return $this->userProvider->loadUserByUsername($username); + } + + /** + * {@inheritdoc} + */ + protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) + { + $username = $token->getUsername(); + $password = $token->getCredentials(); + + if ('' === $password) { + throw new BadCredentialsException('The presented password must not be empty.'); + } + + try { + $username = $this->ldap->escape($username, '', LDAP_ESCAPE_DN); + $dn = str_replace('{username}', $username, $this->dnString); + + $this->ldap->bind($dn, $password); + } catch (ConnectionException $e) { + throw new BadCredentialsException('The presented password is invalid.'); + } + } +} diff --git a/Core/Authentication/Provider/RememberMeAuthenticationProvider.php b/Core/Authentication/Provider/RememberMeAuthenticationProvider.php index 82be1d1..f0a74eb 100644 --- a/Core/Authentication/Provider/RememberMeAuthenticationProvider.php +++ b/Core/Authentication/Provider/RememberMeAuthenticationProvider.php @@ -19,20 +19,20 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException; class RememberMeAuthenticationProvider implements AuthenticationProviderInterface { private $userChecker; - private $key; + private $secret; private $providerKey; /** * Constructor. * * @param UserCheckerInterface $userChecker An UserCheckerInterface interface - * @param string $key A key - * @param string $providerKey A provider key + * @param string $secret A secret + * @param string $providerKey A provider secret */ - public function __construct(UserCheckerInterface $userChecker, $key, $providerKey) + public function __construct(UserCheckerInterface $userChecker, $secret, $providerKey) { $this->userChecker = $userChecker; - $this->key = $key; + $this->secret = $secret; $this->providerKey = $providerKey; } @@ -45,14 +45,14 @@ class RememberMeAuthenticationProvider implements AuthenticationProviderInterfac return; } - if ($this->key !== $token->getKey()) { - throw new BadCredentialsException('The presented key does not match.'); + if ($this->secret !== $token->getSecret()) { + throw new BadCredentialsException('The presented secret does not match.'); } $user = $token->getUser(); $this->userChecker->checkPreAuth($user); - $authenticatedToken = new RememberMeToken($user, $this->providerKey, $this->key); + $authenticatedToken = new RememberMeToken($user, $this->providerKey, $this->secret); $authenticatedToken->setAttributes($token->getAttributes()); return $authenticatedToken; diff --git a/Core/Authentication/SimpleFormAuthenticatorInterface.php b/Core/Authentication/SimpleFormAuthenticatorInterface.php index 95ee881..ae2b58b 100644 --- a/Core/Authentication/SimpleFormAuthenticatorInterface.php +++ b/Core/Authentication/SimpleFormAuthenticatorInterface.php @@ -14,6 +14,8 @@ namespace Symfony\Component\Security\Core\Authentication; use Symfony\Component\HttpFoundation\Request; /** + * @deprecated Deprecated since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead. + * * @author Jordi Boggiano <j.boggiano@seld.be> */ interface SimpleFormAuthenticatorInterface extends SimpleAuthenticatorInterface diff --git a/Core/Authentication/SimplePreAuthenticatorInterface.php b/Core/Authentication/SimplePreAuthenticatorInterface.php index 6164e7d..c01f064 100644 --- a/Core/Authentication/SimplePreAuthenticatorInterface.php +++ b/Core/Authentication/SimplePreAuthenticatorInterface.php @@ -14,6 +14,8 @@ namespace Symfony\Component\Security\Core\Authentication; use Symfony\Component\HttpFoundation\Request; /** + * @deprecated Since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead. + * * @author Jordi Boggiano <j.boggiano@seld.be> */ interface SimplePreAuthenticatorInterface extends SimpleAuthenticatorInterface diff --git a/Core/Authentication/Token/AnonymousToken.php b/Core/Authentication/Token/AnonymousToken.php index 0d7dea0..bbbfe64 100644 --- a/Core/Authentication/Token/AnonymousToken.php +++ b/Core/Authentication/Token/AnonymousToken.php @@ -20,20 +20,20 @@ use Symfony\Component\Security\Core\Role\RoleInterface; */ class AnonymousToken extends AbstractToken { - private $key; + private $secret; /** * Constructor. * - * @param string $key The key shared with the authentication provider - * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string - * @param RoleInterface[] $roles An array of roles + * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client + * @param string|object $user The user can be a UserInterface instance, or an object implementing a __toString method or the username as a regular string + * @param RoleInterface[] $roles An array of roles */ - public function __construct($key, $user, array $roles = array()) + public function __construct($secret, $user, array $roles = array()) { parent::__construct($roles); - $this->key = $key; + $this->secret = $secret; $this->setUser($user); $this->setAuthenticated(true); } @@ -47,13 +47,23 @@ class AnonymousToken extends AbstractToken } /** - * Returns the key. - * - * @return string The Key + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * Returns the secret. + * + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -61,7 +71,7 @@ class AnonymousToken extends AbstractToken */ public function serialize() { - return serialize(array($this->key, parent::serialize())); + return serialize(array($this->secret, parent::serialize())); } /** @@ -69,7 +79,7 @@ class AnonymousToken extends AbstractToken */ public function unserialize($serialized) { - list($this->key, $parentStr) = unserialize($serialized); + list($this->secret, $parentStr) = unserialize($serialized); parent::unserialize($parentStr); } } diff --git a/Core/Authentication/Token/RememberMeToken.php b/Core/Authentication/Token/RememberMeToken.php index 609fdad..60e36f2 100644 --- a/Core/Authentication/Token/RememberMeToken.php +++ b/Core/Authentication/Token/RememberMeToken.php @@ -20,7 +20,7 @@ use Symfony\Component\Security\Core\User\UserInterface; */ class RememberMeToken extends AbstractToken { - private $key; + private $secret; private $providerKey; /** @@ -28,16 +28,16 @@ class RememberMeToken extends AbstractToken * * @param UserInterface $user * @param string $providerKey - * @param string $key + * @param string $secret A secret used to make sure the token is created by the app and not by a malicious client * * @throws \InvalidArgumentException */ - public function __construct(UserInterface $user, $providerKey, $key) + public function __construct(UserInterface $user, $providerKey, $secret) { parent::__construct($user->getRoles()); - if (empty($key)) { - throw new \InvalidArgumentException('$key must not be empty.'); + if (empty($secret)) { + throw new \InvalidArgumentException('$secret must not be empty.'); } if (empty($providerKey)) { @@ -45,7 +45,7 @@ class RememberMeToken extends AbstractToken } $this->providerKey = $providerKey; - $this->key = $key; + $this->secret = $secret; $this->setUser($user); parent::setAuthenticated(true); @@ -64,9 +64,9 @@ class RememberMeToken extends AbstractToken } /** - * Returns the provider key. + * Returns the provider secret. * - * @return string The provider key + * @return string The provider secret */ public function getProviderKey() { @@ -74,13 +74,23 @@ class RememberMeToken extends AbstractToken } /** - * Returns the key. - * - * @return string The Key + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * Returns the secret. + * + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -97,7 +107,7 @@ class RememberMeToken extends AbstractToken public function serialize() { return serialize(array( - $this->key, + $this->secret, $this->providerKey, parent::serialize(), )); @@ -108,7 +118,7 @@ class RememberMeToken extends AbstractToken */ public function unserialize($serialized) { - list($this->key, $this->providerKey, $parentStr) = unserialize($serialized); + list($this->secret, $this->providerKey, $parentStr) = unserialize($serialized); parent::unserialize($parentStr); } } diff --git a/Core/Authorization/AccessDecisionManager.php b/Core/Authorization/AccessDecisionManager.php index b8b6a77..7cefef1 100644 --- a/Core/Authorization/AccessDecisionManager.php +++ b/Core/Authorization/AccessDecisionManager.php @@ -41,12 +41,8 @@ class AccessDecisionManager implements AccessDecisionManagerInterface * * @throws \InvalidArgumentException */ - public function __construct(array $voters, $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) + public function __construct(array $voters = array(), $strategy = self::STRATEGY_AFFIRMATIVE, $allowIfAllAbstainDecisions = false, $allowIfEqualGrantedDeniedDecisions = true) { - if (!$voters) { - throw new \InvalidArgumentException('You must at least add one voter.'); - } - $strategyMethod = 'decide'.ucfirst($strategy); if (!is_callable(array($this, $strategyMethod))) { throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy)); @@ -59,6 +55,16 @@ class AccessDecisionManager implements AccessDecisionManagerInterface } /** + * Configures the voters. + * + * @param VoterInterface[] $voters An array of VoterInterface instances + */ + public function setVoters(array $voters) + { + $this->voters = $voters; + } + + /** * {@inheritdoc} */ public function decide(TokenInterface $token, array $attributes, $object = null) @@ -71,6 +77,8 @@ class AccessDecisionManager implements AccessDecisionManagerInterface */ public function supportsAttribute($attribute) { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED); + foreach ($this->voters as $voter) { if ($voter->supportsAttribute($attribute)) { return true; @@ -85,6 +93,8 @@ class AccessDecisionManager implements AccessDecisionManagerInterface */ public function supportsClass($class) { + @trigger_error('The '.__METHOD__.' is deprecated since version 2.8 and will be removed in version 3.0.', E_USER_DEPRECATED); + foreach ($this->voters as $voter) { if ($voter->supportsClass($class)) { return true; @@ -144,7 +154,6 @@ class AccessDecisionManager implements AccessDecisionManagerInterface { $grant = 0; $deny = 0; - $abstain = 0; foreach ($this->voters as $voter) { $result = $voter->vote($token, $object, $attributes); @@ -158,11 +167,6 @@ class AccessDecisionManager implements AccessDecisionManagerInterface ++$deny; break; - - default: - ++$abstain; - - break; } } @@ -174,7 +178,7 @@ class AccessDecisionManager implements AccessDecisionManagerInterface return false; } - if ($grant == $deny && $grant != 0) { + if ($grant > 0) { return $this->allowIfEqualGrantedDeniedDecisions; } diff --git a/Core/Authorization/AccessDecisionManagerInterface.php b/Core/Authorization/AccessDecisionManagerInterface.php index 16209ba..d18b5e3 100644 --- a/Core/Authorization/AccessDecisionManagerInterface.php +++ b/Core/Authorization/AccessDecisionManagerInterface.php @@ -37,6 +37,8 @@ interface AccessDecisionManagerInterface * @param string $attribute An attribute * * @return bool true if this decision manager supports the attribute, false otherwise + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsAttribute($attribute); @@ -46,6 +48,8 @@ interface AccessDecisionManagerInterface * @param string $class A class name * * @return true if this decision manager can process the class + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsClass($class); } diff --git a/Core/Authorization/Voter/AbstractVoter.php b/Core/Authorization/Voter/AbstractVoter.php index efa1562..5dcf787 100644 --- a/Core/Authorization/Voter/AbstractVoter.php +++ b/Core/Authorization/Voter/AbstractVoter.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Security\Core\Authorization\Voter; +@trigger_error('The '.__NAMESPACE__.'\AbstractVoter class is deprecated since version 2.8, to be removed in 3.0. Upgrade to Symfony\Component\Security\Core\Authorization\Voter\Voter instead.', E_USER_DEPRECATED); + use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -18,6 +20,8 @@ use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; * Abstract Voter implementation that reduces boilerplate code required to create a custom Voter. * * @author Roman Marintšenko <inoryy@gmail.com> + * + * @deprecated since version 2.8, to be removed in 3.0. Upgrade to Symfony\Component\Security\Core\Authorization\Voter\Voter instead. */ abstract class AbstractVoter implements VoterInterface { diff --git a/Core/Authorization/Voter/ExpressionVoter.php b/Core/Authorization/Voter/ExpressionVoter.php index 98b8f50..96a7ece 100644 --- a/Core/Authorization/Voter/ExpressionVoter.php +++ b/Core/Authorization/Voter/ExpressionVoter.php @@ -102,6 +102,7 @@ class ExpressionVoter implements VoterInterface 'token' => $token, 'user' => $token->getUser(), 'object' => $object, + 'subject' => $object, 'roles' => array_map(function ($role) { return $role->getRole(); }, $roles), 'trust_resolver' => $this->trustResolver, ); diff --git a/Core/Authorization/Voter/Voter.php b/Core/Authorization/Voter/Voter.php new file mode 100644 index 0000000..8d36fd8 --- /dev/null +++ b/Core/Authorization/Voter/Voter.php @@ -0,0 +1,85 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * Voter is an abstract default implementation of a voter. + * + * @author Roman Marintšenko <inoryy@gmail.com> + * @author Grégoire Pineau <lyrixx@lyrixx.info> + */ +abstract class Voter implements VoterInterface +{ + /** + * {@inheritdoc} + */ + public function supportsAttribute($attribute) + { + throw new \BadMethodCallException('supportsAttribute method is deprecated since version 2.8, to be removed in 3.0'); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + throw new \BadMethodCallException('supportsClass method is deprecated since version 2.8, to be removed in 3.0'); + } + + /** + * {@inheritdoc} + */ + public function vote(TokenInterface $token, $object, array $attributes) + { + // abstain vote by default in case none of the attributes are supported + $vote = self::ACCESS_ABSTAIN; + + foreach ($attributes as $attribute) { + if (!$this->supports($attribute, $object)) { + continue; + } + + // as soon as at least one attribute is supported, default is to deny access + $vote = self::ACCESS_DENIED; + + if ($this->voteOnAttribute($attribute, $object, $token)) { + // grant access as soon as at least one attribute returns a positive response + return self::ACCESS_GRANTED; + } + } + + return $vote; + } + + /** + * Determines if the attribute and subject are supported by this voter. + * + * @param string $attribute An attribute + * @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type + * + * @return bool True if the attribute and subject are supported, false otherwise + */ + abstract protected function supports($attribute, $subject); + + /** + * Perform a single access check operation on a given attribute, subject and token. + * + * @param string $attribute + * @param mixed $subject + * @param TokenInterface $token + * + * @return bool + */ + abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token); +} diff --git a/Core/Authorization/Voter/VoterInterface.php b/Core/Authorization/Voter/VoterInterface.php index 1032cb2..91ddc1f 100644 --- a/Core/Authorization/Voter/VoterInterface.php +++ b/Core/Authorization/Voter/VoterInterface.php @@ -30,6 +30,8 @@ interface VoterInterface * @param mixed $attribute An attribute (usually the attribute name string) * * @return bool true if this Voter supports the attribute, false otherwise + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsAttribute($attribute); @@ -39,6 +41,8 @@ interface VoterInterface * @param string $class A class name * * @return bool true if this Voter can process the class + * + * @deprecated since version 2.8, to be removed in 3.0. */ public function supportsClass($class); diff --git a/Core/Encoder/BCryptPasswordEncoder.php b/Core/Encoder/BCryptPasswordEncoder.php index 83ae334..b992765 100644 --- a/Core/Encoder/BCryptPasswordEncoder.php +++ b/Core/Encoder/BCryptPasswordEncoder.php @@ -36,10 +36,6 @@ class BCryptPasswordEncoder extends BasePasswordEncoder */ public function __construct($cost) { - if (!function_exists('password_hash')) { - throw new \RuntimeException('To use the BCrypt encoder, you need to upgrade to PHP 5.5 or install the "ircmaxell/password-compat" via Composer.'); - } - $cost = (int) $cost; if ($cost < 4 || $cost > 31) { throw new \InvalidArgumentException('Cost must be in the range of 4-31.'); @@ -77,6 +73,8 @@ class BCryptPasswordEncoder extends BasePasswordEncoder $options = array('cost' => $this->cost); if ($salt) { + @trigger_error('Passing a $salt to '.__METHOD__.'() is deprecated since version 2.8 and will be ignored in 3.0.', E_USER_DEPRECATED); + $options['salt'] = $salt; } diff --git a/Core/Encoder/BasePasswordEncoder.php b/Core/Encoder/BasePasswordEncoder.php index fcf2e47..d86f260 100644 --- a/Core/Encoder/BasePasswordEncoder.php +++ b/Core/Encoder/BasePasswordEncoder.php @@ -11,8 +11,6 @@ namespace Symfony\Component\Security\Core\Encoder; -use Symfony\Component\Security\Core\Util\StringUtils; - /** * BasePasswordEncoder is the base class for all password encoders. * @@ -83,7 +81,7 @@ abstract class BasePasswordEncoder implements PasswordEncoderInterface */ protected function comparePasswords($password1, $password2) { - return StringUtils::equals($password1, $password2); + return hash_equals($password1, $password2); } /** diff --git a/Core/Encoder/Pbkdf2PasswordEncoder.php b/Core/Encoder/Pbkdf2PasswordEncoder.php index 6f24c4f..8422a4b 100644 --- a/Core/Encoder/Pbkdf2PasswordEncoder.php +++ b/Core/Encoder/Pbkdf2PasswordEncoder.php @@ -64,11 +64,7 @@ class Pbkdf2PasswordEncoder extends BasePasswordEncoder throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm)); } - if (function_exists('hash_pbkdf2')) { - $digest = hash_pbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length, true); - } else { - $digest = $this->hashPbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length); - } + $digest = hash_pbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length, true); return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest); } @@ -80,24 +76,4 @@ class Pbkdf2PasswordEncoder extends BasePasswordEncoder { return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt)); } - - private function hashPbkdf2($algorithm, $password, $salt, $iterations, $length = 0) - { - // Number of blocks needed to create the derived key - $blocks = ceil($length / strlen(hash($algorithm, null, true))); - $digest = ''; - - for ($i = 1; $i <= $blocks; ++$i) { - $ib = $block = hash_hmac($algorithm, $salt.pack('N', $i), $password, true); - - // Iterations - for ($j = 1; $j < $iterations; ++$j) { - $ib ^= ($block = hash_hmac($algorithm, $block, $password, true)); - } - - $digest .= $ib; - } - - return substr($digest, 0, $this->length); - } } diff --git a/Core/Exception/AuthenticationExpiredException.php b/Core/Exception/AuthenticationExpiredException.php new file mode 100644 index 0000000..caf2e6c --- /dev/null +++ b/Core/Exception/AuthenticationExpiredException.php @@ -0,0 +1,31 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Exception; + +/** + * AuthenticationServiceException is thrown when an authenticated token becomes un-authentcated between requests. + * + * In practice, this is due to the User changing between requests (e.g. password changes), + * causes the token to become un-authenticated. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class AuthenticationExpiredException extends AccountStatusException +{ + /** + * {@inheritdoc} + */ + public function getMessageKey() + { + return 'Authentication expired because your account information has changed.'; + } +} diff --git a/Core/Exception/CustomUserMessageAuthenticationException.php b/Core/Exception/CustomUserMessageAuthenticationException.php new file mode 100644 index 0000000..9f5071f --- /dev/null +++ b/Core/Exception/CustomUserMessageAuthenticationException.php @@ -0,0 +1,79 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Exception; + +/** + * An authentication exception where you can control the message shown to the user. + * + * Be sure that the message passed to this exception is something that + * can be shown safely to your user. In other words, avoid catching + * other exceptions and passing their message directly to this class. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class CustomUserMessageAuthenticationException extends AuthenticationException +{ + private $messageKey; + + private $messageData = array(); + + public function __construct($message = '', array $messageData = array(), $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->setSafeMessage($message, $messageData); + } + + /** + * Set a message that will be shown to the user. + * + * @param string $messageKey The message or message key + * @param array $messageData Data to be passed into the translator + */ + public function setSafeMessage($messageKey, array $messageData = array()) + { + $this->messageKey = $messageKey; + $this->messageData = $messageData; + } + + public function getMessageKey() + { + return $this->messageKey; + } + + public function getMessageData() + { + return $this->messageData; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array( + parent::serialize(), + $this->messageKey, + $this->messageData, + )); + } + + /** + * {@inheritdoc} + */ + public function unserialize($str) + { + list($parentData, $this->messageKey, $this->messageData) = unserialize($str); + + parent::unserialize($parentData); + } +} diff --git a/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php b/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php index 5a189b0..8f4b392 100644 --- a/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php +++ b/Core/Tests/Authentication/Provider/AnonymousAuthenticationProviderTest.php @@ -33,11 +33,11 @@ class AnonymousAuthenticationProviderTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException */ - public function testAuthenticateWhenKeyIsNotValid() + public function testAuthenticateWhenSecretIsNotValid() { $provider = $this->getProvider('foo'); - $this->assertNull($provider->authenticate($this->getSupportedToken('bar'))); + $provider->authenticate($this->getSupportedToken('bar')); } public function testAuthenticate() @@ -48,19 +48,19 @@ class AnonymousAuthenticationProviderTest extends \PHPUnit_Framework_TestCase $this->assertSame($token, $provider->authenticate($token)); } - protected function getSupportedToken($key) + protected function getSupportedToken($secret) { - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', array('getKey'), array(), '', false); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\AnonymousToken', array('getSecret'), array(), '', false); $token->expects($this->any()) - ->method('getKey') - ->will($this->returnValue($key)) + ->method('getSecret') + ->will($this->returnValue($secret)) ; return $token; } - protected function getProvider($key) + protected function getProvider($secret) { - return new AnonymousAuthenticationProvider($key); + return new AnonymousAuthenticationProvider($secret); } } diff --git a/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php b/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php new file mode 100644 index 0000000..fbb4d73 --- /dev/null +++ b/Core/Tests/Authentication/Provider/LdapBindAuthenticationProviderTest.php @@ -0,0 +1,81 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\Authentication\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\LdapBindAuthenticationProvider; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\User\User; +use Symfony\Component\Ldap\Exception\ConnectionException; + +/** + * @requires extension ldap + */ +class LdapBindAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException + * @expectedExceptionMessage The presented password must not be empty. + */ + public function testEmptyPasswordShouldThrowAnException() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + + $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); + $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); + $reflection->setAccessible(true); + + $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', '', 'key')); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException + * @expectedExceptionMessage The presented password is invalid. + */ + public function testBindFailureShouldThrowAnException() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + $ldap + ->expects($this->once()) + ->method('bind') + ->will($this->throwException(new ConnectionException())) + ; + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + + $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); + $reflection = new \ReflectionMethod($provider, 'checkAuthentication'); + $reflection->setAccessible(true); + + $reflection->invoke($provider, new User('foo', null), new UsernamePasswordToken('foo', 'bar', 'key')); + } + + public function testRetrieveUser() + { + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider + ->expects($this->once()) + ->method('loadUserByUsername') + ->with('foo') + ; + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + + $userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + + $provider = new LdapBindAuthenticationProvider($userProvider, $userChecker, 'key', $ldap); + $reflection = new \ReflectionMethod($provider, 'retrieveUser'); + $reflection->setAccessible(true); + + $reflection->invoke($provider, 'foo', new UsernamePasswordToken('foo', 'bar', 'key')); + } +} diff --git a/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php b/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php index a6fff4b..735d195 100644 --- a/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php +++ b/Core/Tests/Authentication/Provider/RememberMeAuthenticationProviderTest.php @@ -36,10 +36,10 @@ class RememberMeAuthenticationProviderTest extends \PHPUnit_Framework_TestCase /** * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException */ - public function testAuthenticateWhenKeysDoNotMatch() + public function testAuthenticateWhenSecretsDoNotMatch() { - $provider = $this->getProvider(null, 'key1'); - $token = $this->getSupportedToken(null, 'key2'); + $provider = $this->getProvider(null, 'secret1'); + $token = $this->getSupportedToken(null, 'secret2'); $provider->authenticate($token); } @@ -77,7 +77,7 @@ class RememberMeAuthenticationProviderTest extends \PHPUnit_Framework_TestCase $this->assertEquals('', $authToken->getCredentials()); } - protected function getSupportedToken($user = null, $key = 'test') + protected function getSupportedToken($user = null, $secret = 'test') { if (null === $user) { $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); @@ -87,7 +87,7 @@ class RememberMeAuthenticationProviderTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(array())); } - $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('getProviderKey'), array($user, 'foo', $key)); + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', array('getProviderKey'), array($user, 'foo', $secret)); $token ->expects($this->once()) ->method('getProviderKey') diff --git a/Core/Tests/Authentication/Token/AnonymousTokenTest.php b/Core/Tests/Authentication/Token/AnonymousTokenTest.php index b5cf006..cac2039 100644 --- a/Core/Tests/Authentication/Token/AnonymousTokenTest.php +++ b/Core/Tests/Authentication/Token/AnonymousTokenTest.php @@ -28,7 +28,7 @@ class AnonymousTokenTest extends \PHPUnit_Framework_TestCase public function testGetKey() { $token = new AnonymousToken('foo', 'bar'); - $this->assertEquals('foo', $token->getKey()); + $this->assertEquals('foo', $token->getSecret()); } public function testGetCredentials() diff --git a/Core/Tests/Authentication/Token/RememberMeTokenTest.php b/Core/Tests/Authentication/Token/RememberMeTokenTest.php index 7449204..b83de4a 100644 --- a/Core/Tests/Authentication/Token/RememberMeTokenTest.php +++ b/Core/Tests/Authentication/Token/RememberMeTokenTest.php @@ -22,7 +22,7 @@ class RememberMeTokenTest extends \PHPUnit_Framework_TestCase $token = new RememberMeToken($user, 'fookey', 'foo'); $this->assertEquals('fookey', $token->getProviderKey()); - $this->assertEquals('foo', $token->getKey()); + $this->assertEquals('foo', $token->getSecret()); $this->assertEquals(array(new Role('ROLE_FOO')), $token->getRoles()); $this->assertSame($user, $token->getUser()); $this->assertTrue($token->isAuthenticated()); @@ -31,7 +31,7 @@ class RememberMeTokenTest extends \PHPUnit_Framework_TestCase /** * @expectedException \InvalidArgumentException */ - public function testConstructorKeyCannotBeNull() + public function testConstructorSecretCannotBeNull() { new RememberMeToken( $this->getUser(), @@ -43,7 +43,7 @@ class RememberMeTokenTest extends \PHPUnit_Framework_TestCase /** * @expectedException \InvalidArgumentException */ - public function testConstructorKeyCannotBeEmptyString() + public function testConstructorSecretCannotBeEmptyString() { new RememberMeToken( $this->getUser(), diff --git a/Core/Tests/Authorization/AccessDecisionManagerTest.php b/Core/Tests/Authorization/AccessDecisionManagerTest.php index 7a9ab08..412af91 100644 --- a/Core/Tests/Authorization/AccessDecisionManagerTest.php +++ b/Core/Tests/Authorization/AccessDecisionManagerTest.php @@ -16,6 +16,9 @@ use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase { + /** + * @group legacy + */ public function testSupportsClass() { $manager = new AccessDecisionManager(array( @@ -31,6 +34,9 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase $this->assertFalse($manager->supportsClass('FooClass')); } + /** + * @group legacy + */ public function testSupportsAttribute() { $manager = new AccessDecisionManager(array( @@ -49,14 +55,6 @@ class AccessDecisionManagerTest extends \PHPUnit_Framework_TestCase /** * @expectedException \InvalidArgumentException */ - public function testSetVotersEmpty() - { - $manager = new AccessDecisionManager(array()); - } - - /** - * @expectedException \InvalidArgumentException - */ public function testSetUnsupportedStrategy() { new AccessDecisionManager(array($this->getVoter(VoterInterface::ACCESS_GRANTED)), 'fooBar'); diff --git a/Core/Tests/Authorization/Voter/AbstractVoterTest.php b/Core/Tests/Authorization/Voter/AbstractVoterTest.php index 2ab943b..b537c1b 100644 --- a/Core/Tests/Authorization/Voter/AbstractVoterTest.php +++ b/Core/Tests/Authorization/Voter/AbstractVoterTest.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; -use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +/** + * @group legacy + */ class AbstractVoterTest extends \PHPUnit_Framework_TestCase { protected $token; @@ -49,26 +51,8 @@ class AbstractVoterTest extends \PHPUnit_Framework_TestCase */ public function testVote(array $attributes, $expectedVote, $object, $message) { - $voter = new AbstractVoterTest_Voter(); + $voter = new Fixtures\MyVoter(); $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); } } - -class AbstractVoterTest_Voter extends AbstractVoter -{ - protected function getSupportedClasses() - { - return array('stdClass'); - } - - protected function getSupportedAttributes() - { - return array('EDIT', 'CREATE'); - } - - protected function isGranted($attribute, $object, $user = null) - { - return 'EDIT' === $attribute; - } -} diff --git a/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php b/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php new file mode 100644 index 0000000..b75f798 --- /dev/null +++ b/Core/Tests/Authorization/Voter/Fixtures/MyVoter.php @@ -0,0 +1,27 @@ +<?php + +namespace Symfony\Component\Security\Core\Tests\Authorization\Voter\Fixtures; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; + +/** + * @group legacy + */ +class MyVoter extends AbstractVoter +{ + protected function getSupportedClasses() + { + return array('stdClass'); + } + + protected function getSupportedAttributes() + { + return array('EDIT', 'CREATE'); + } + + protected function isGranted($attribute, $object, $user = null) + { + return 'EDIT' === $attribute; + } +} diff --git a/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php b/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php index c50ecf3..4b03bac 100644 --- a/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php +++ b/Core/Tests/Authorization/Voter/RoleHierarchyVoterTest.php @@ -33,4 +33,19 @@ class RoleHierarchyVoterTest extends RoleVoterTest array(array('ROLE_FOO'), array('ROLE_FOOBAR'), VoterInterface::ACCESS_GRANTED), )); } + + /** + * @dataProvider getVoteWithEmptyHierarchyTests + */ + public function testVoteWithEmptyHierarchy($roles, $attributes, $expected) + { + $voter = new RoleHierarchyVoter(new RoleHierarchy(array())); + + $this->assertSame($expected, $voter->vote($this->getToken($roles), null, $attributes)); + } + + public function getVoteWithEmptyHierarchyTests() + { + return parent::getVoteTests(); + } } diff --git a/Core/Tests/Authorization/Voter/VoterTest.php b/Core/Tests/Authorization/Voter/VoterTest.php new file mode 100644 index 0000000..4bac44d --- /dev/null +++ b/Core/Tests/Authorization/Voter/VoterTest.php @@ -0,0 +1,70 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\Authorization\Voter; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Authorization\Voter\Voter; +use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; + +class VoterTest extends \PHPUnit_Framework_TestCase +{ + protected $token; + + protected function setUp() + { + $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + } + + public function getTests() + { + return array( + array(array('EDIT'), VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if attribute and class are supported and attribute grants access'), + array(array('CREATE'), VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if attribute and class are supported and attribute does not grant access'), + + array(array('DELETE', 'EDIT'), VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute is supported and grants access'), + array(array('DELETE', 'CREATE'), VoterInterface::ACCESS_DENIED, new \stdClass(), 'ACCESS_DENIED if one attribute is supported and denies access'), + + array(array('CREATE', 'EDIT'), VoterInterface::ACCESS_GRANTED, new \stdClass(), 'ACCESS_GRANTED if one attribute grants access'), + + array(array('DELETE'), VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attribute is supported'), + + array(array('EDIT'), VoterInterface::ACCESS_ABSTAIN, $this, 'ACCESS_ABSTAIN if class is not supported'), + + array(array('EDIT'), VoterInterface::ACCESS_ABSTAIN, null, 'ACCESS_ABSTAIN if object is null'), + + array(array(), VoterInterface::ACCESS_ABSTAIN, new \stdClass(), 'ACCESS_ABSTAIN if no attributes were provided'), + ); + } + + /** + * @dataProvider getTests + */ + public function testVote(array $attributes, $expectedVote, $object, $message) + { + $voter = new VoterTest_Voter(); + + $this->assertEquals($expectedVote, $voter->vote($this->token, $object, $attributes), $message); + } +} + +class VoterTest_Voter extends Voter +{ + protected function voteOnAttribute($attribute, $object, TokenInterface $token) + { + return 'EDIT' === $attribute; + } + + protected function supports($attribute, $object) + { + return $object instanceof \stdClass && in_array($attribute, array('EDIT', 'CREATE')); + } +} diff --git a/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php b/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php new file mode 100644 index 0000000..408dd2a --- /dev/null +++ b/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php @@ -0,0 +1,26 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\Exception; + +use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; + +class CustomUserMessageAuthenticationExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testConstructWithSAfeMessage() + { + $e = new CustomUserMessageAuthenticationException('SAFE MESSAGE', array('foo' => true)); + + $this->assertEquals('SAFE MESSAGE', $e->getMessageKey()); + $this->assertEquals(array('foo' => true), $e->getMessageData()); + $this->assertEquals('SAFE MESSAGE', $e->getMessage()); + } +} diff --git a/Core/Tests/LegacySecurityContextInterfaceTest.php b/Core/Tests/LegacySecurityContextInterfaceTest.php deleted file mode 100644 index a45ecf9..0000000 --- a/Core/Tests/LegacySecurityContextInterfaceTest.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/* - * This file is part of the Symfony package. - * - * (c) Fabien Potencier <fabien@symfony.com> - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Security\Core\Tests; - -use Symfony\Component\Security\Core\SecurityContextInterface; -use Symfony\Component\Security\Core\Security; - -/** - * @group legacy - */ -class LegacySecurityContextInterfaceTest extends \PHPUnit_Framework_TestCase -{ - /** - * Test if the BC Layer is working as intended. - */ - public function testConstantSync() - { - $this->assertSame(Security::ACCESS_DENIED_ERROR, SecurityContextInterface::ACCESS_DENIED_ERROR); - $this->assertSame(Security::AUTHENTICATION_ERROR, SecurityContextInterface::AUTHENTICATION_ERROR); - $this->assertSame(Security::LAST_USERNAME, SecurityContextInterface::LAST_USERNAME); - } -} diff --git a/Core/Tests/LegacySecurityContextTest.php b/Core/Tests/LegacySecurityContextTest.php index 92d7c16..4502261 100644 --- a/Core/Tests/LegacySecurityContextTest.php +++ b/Core/Tests/LegacySecurityContextTest.php @@ -11,9 +11,9 @@ namespace Symfony\Component\Security\Core\Tests; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; -use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; +use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\SecurityContext; +use Symfony\Component\Security\Core\SecurityContextInterface; /** * @group legacy @@ -119,4 +119,14 @@ class LegacySecurityContextTest extends \PHPUnit_Framework_TestCase array(true, null), ); } + + /** + * Test if the BC Layer is working as intended. + */ + public function testConstantSync() + { + $this->assertSame(Security::ACCESS_DENIED_ERROR, SecurityContextInterface::ACCESS_DENIED_ERROR); + $this->assertSame(Security::AUTHENTICATION_ERROR, SecurityContextInterface::AUTHENTICATION_ERROR); + $this->assertSame(Security::LAST_USERNAME, SecurityContextInterface::LAST_USERNAME); + } } diff --git a/Core/Tests/User/LdapUserProviderTest.php b/Core/Tests/User/LdapUserProviderTest.php new file mode 100644 index 0000000..9b126e9 --- /dev/null +++ b/Core/Tests/User/LdapUserProviderTest.php @@ -0,0 +1,105 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\Tests\User; + +use Symfony\Component\Security\Core\User\LdapUserProvider; +use Symfony\Component\Ldap\Exception\ConnectionException; + +/** + * @requires extension ldap + */ +class LdapUserProviderTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testLoadUserByUsernameFailsIfCantConnectToLdap() + { + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + $ldap + ->expects($this->once()) + ->method('bind') + ->will($this->throwException(new ConnectionException())) + ; + + $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); + $provider->loadUserByUsername('foo'); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testLoadUserByUsernameFailsIfNoLdapEntries() + { + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + $ldap + ->expects($this->once()) + ->method('escape') + ->will($this->returnValue('foo')) + ; + + $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); + $provider->loadUserByUsername('foo'); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\UsernameNotFoundException + */ + public function testLoadUserByUsernameFailsIfMoreThanOneLdapEntry() + { + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + $ldap + ->expects($this->once()) + ->method('escape') + ->will($this->returnValue('foo')) + ; + $ldap + ->expects($this->once()) + ->method('find') + ->will($this->returnValue(array( + array(), + array(), + 'count' => 2, + ))) + ; + + $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); + $provider->loadUserByUsername('foo'); + } + + public function testSuccessfulLoadUserByUsername() + { + $ldap = $this->getMock('Symfony\Component\Ldap\LdapClientInterface'); + $ldap + ->expects($this->once()) + ->method('escape') + ->will($this->returnValue('foo')) + ; + $ldap + ->expects($this->once()) + ->method('find') + ->will($this->returnValue(array( + array( + 'sAMAccountName' => 'foo', + 'userpassword' => 'bar', + ), + 'count' => 1, + ))) + ; + + $provider = new LdapUserProvider($ldap, 'ou=MyBusiness,dc=symfony,dc=com'); + $this->assertInstanceOf( + 'Symfony\Component\Security\Core\User\User', + $provider->loadUserByUsername('foo') + ); + } +} diff --git a/Core/Tests/Util/ClassUtilsTest.php b/Core/Tests/Util/ClassUtilsTest.php index e8f0143..b048206 100644 --- a/Core/Tests/Util/ClassUtilsTest.php +++ b/Core/Tests/Util/ClassUtilsTest.php @@ -13,6 +13,9 @@ namespace Symfony\Component\Security\Core\Tests\Util { use Symfony\Component\Security\Core\Util\ClassUtils; + /** + * @group legacy + */ class ClassUtilsTest extends \PHPUnit_Framework_TestCase { public static function dataGetClass() diff --git a/Core/Tests/Util/StringUtilsTest.php b/Core/Tests/Util/StringUtilsTest.php index faeaf25..78d9b05 100644 --- a/Core/Tests/Util/StringUtilsTest.php +++ b/Core/Tests/Util/StringUtilsTest.php @@ -15,6 +15,8 @@ use Symfony\Component\Security\Core\Util\StringUtils; /** * Data from PHP.net's hash_equals tests. + * + * @group legacy */ class StringUtilsTest extends \PHPUnit_Framework_TestCase { diff --git a/Core/User/LdapUserProvider.php b/Core/User/LdapUserProvider.php new file mode 100644 index 0000000..1593564 --- /dev/null +++ b/Core/User/LdapUserProvider.php @@ -0,0 +1,108 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Core\User; + +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Ldap\Exception\ConnectionException; +use Symfony\Component\Ldap\LdapClientInterface; + +/** + * LdapUserProvider is a simple user provider on top of ldap. + * + * @author Grégoire Pineau <lyrixx@lyrixx.info> + * @author Charles Sarrazin <charles@sarraz.in> + */ +class LdapUserProvider implements UserProviderInterface +{ + private $ldap; + private $baseDn; + private $searchDn; + private $searchPassword; + private $defaultRoles; + private $defaultSearch; + + /** + * @param LdapClientInterface $ldap + * @param string $baseDn + * @param string $searchDn + * @param string $searchPassword + * @param array $defaultRoles + * @param string $uidKey + * @param string $filter + */ + public function __construct(LdapClientInterface $ldap, $baseDn, $searchDn = null, $searchPassword = null, array $defaultRoles = array(), $uidKey = 'sAMAccountName', $filter = '({uid_key}={username})') + { + $this->ldap = $ldap; + $this->baseDn = $baseDn; + $this->searchDn = $searchDn; + $this->searchPassword = $searchPassword; + $this->defaultRoles = $defaultRoles; + $this->defaultSearch = str_replace('{uid_key}', $uidKey, $filter); + } + + /** + * {@inheritdoc} + */ + public function loadUserByUsername($username) + { + try { + $this->ldap->bind($this->searchDn, $this->searchPassword); + $username = $this->ldap->escape($username, '', LDAP_ESCAPE_FILTER); + $query = str_replace('{username}', $username, $this->defaultSearch); + $search = $this->ldap->find($this->baseDn, $query); + } catch (ConnectionException $e) { + throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username), 0, $e); + } + + if (!$search) { + throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username)); + } + + if ($search['count'] > 1) { + throw new UsernameNotFoundException('More than one user found'); + } + + $user = $search[0]; + + return $this->loadUser($username, $user); + } + + public function loadUser($username, $user) + { + $password = isset($user['userpassword']) ? $user['userpassword'] : null; + + $roles = $this->defaultRoles; + + return new User($username, $password, $roles); + } + + /** + * {@inheritdoc} + */ + public function refreshUser(UserInterface $user) + { + if (!$user instanceof User) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); + } + + return new User($user->getUsername(), null, $user->getRoles()); + } + + /** + * {@inheritdoc} + */ + public function supportsClass($class) + { + return $class === 'Symfony\Component\Security\Core\User\User'; + } +} diff --git a/Core/User/UserCheckerInterface.php b/Core/User/UserCheckerInterface.php index 3dd8d51..62ea9f0 100644 --- a/Core/User/UserCheckerInterface.php +++ b/Core/User/UserCheckerInterface.php @@ -11,10 +11,13 @@ namespace Symfony\Component\Security\Core\User; +use Symfony\Component\Security\Core\Exception\AccountStatusException; + /** - * UserCheckerInterface checks user account when authentication occurs. + * Implement to throw AccountStatusException during the authentication process. * - * This should not be used to make authentication decisions. + * Can be used when you want to check the account status, e.g when the account is + * disabled or blocked. This should not be used to make authentication decisions. * * @author Fabien Potencier <fabien@symfony.com> */ @@ -24,6 +27,8 @@ interface UserCheckerInterface * Checks the user account before authentication. * * @param UserInterface $user a UserInterface instance + * + * @throws AccountStatusException */ public function checkPreAuth(UserInterface $user); @@ -31,6 +36,8 @@ interface UserCheckerInterface * Checks the user account after authentication. * * @param UserInterface $user a UserInterface instance + * + * @throws AccountStatusException */ public function checkPostAuth(UserInterface $user); } diff --git a/Core/User/UserProviderInterface.php b/Core/User/UserProviderInterface.php index d17e3b7..146ed65 100644 --- a/Core/User/UserProviderInterface.php +++ b/Core/User/UserProviderInterface.php @@ -43,8 +43,6 @@ interface UserProviderInterface * * @return UserInterface * - * @see UsernameNotFoundException - * * @throws UsernameNotFoundException if the user is not found */ public function loadUserByUsername($username); diff --git a/Core/Util/ClassUtils.php b/Core/Util/ClassUtils.php index 6c87096..06186ef 100644 --- a/Core/Util/ClassUtils.php +++ b/Core/Util/ClassUtils.php @@ -11,13 +11,15 @@ namespace Symfony\Component\Security\Core\Util; -use Doctrine\Common\Util\ClassUtils as DoctrineClassUtils; +use Symfony\Component\Security\Acl\Util\ClassUtils as AclClassUtils; + +@trigger_error('The '.__NAMESPACE__.'\ClassUtils class is deprecated since version 2.8, to be removed in 3.0. Use Symfony\Component\Security\Acl\Util\ClassUtils instead.', E_USER_DEPRECATED); /** * Class related functionality for objects that * might or might not be proxy objects at the moment. * - * @see DoctrineClassUtils + * @deprecated ClassUtils is deprecated since version 2.8, to be removed in 3.0. Use Acl ClassUtils instead. * * @author Benjamin Eberlei <kontakt@beberlei.de> * @author Johannes Schmitt <schmittjoh@gmail.com> @@ -54,6 +56,11 @@ class ClassUtils */ public static function getRealClass($object) { + if (class_exists('Symfony\Component\Security\Acl\Util\ClassUtils')) { + return AclClassUtils::getRealClass($object); + } + + // fallback in case security-acl is not installed $class = is_object($object) ? get_class($object) : $object; if (false === $pos = strrpos($class, '\\'.self::MARKER.'\\')) { diff --git a/Core/Util/SecureRandom.php b/Core/Util/SecureRandom.php index 478f556..06ed893 100644 --- a/Core/Util/SecureRandom.php +++ b/Core/Util/SecureRandom.php @@ -11,11 +11,15 @@ namespace Symfony\Component\Security\Core\Util; +@trigger_error('The '.__NAMESPACE__.'\SecureRandom class is deprecated since version 2.8 and will be removed in 3.0. Use the random_bytes() function instead.', E_USER_DEPRECATED); + /** * A secure random number generator implementation. * * @author Fabien Potencier <fabien@symfony.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com> + * + * @deprecated since version 2.8, to be removed in 3.0. Use the random_bytes function instead */ final class SecureRandom implements SecureRandomInterface { diff --git a/Core/Util/SecureRandomInterface.php b/Core/Util/SecureRandomInterface.php index 87d3ace..df5509b 100644 --- a/Core/Util/SecureRandomInterface.php +++ b/Core/Util/SecureRandomInterface.php @@ -15,6 +15,8 @@ namespace Symfony\Component\Security\Core\Util; * Interface that needs to be implemented by all secure random number generators. * * @author Fabien Potencier <fabien@symfony.com> + * + * @deprecated since version 2.8, to be removed in 3.0. Use the random_bytes function instead */ interface SecureRandomInterface { diff --git a/Core/Util/StringUtils.php b/Core/Util/StringUtils.php index 343585c..bb0c8b2 100644 --- a/Core/Util/StringUtils.php +++ b/Core/Util/StringUtils.php @@ -11,10 +11,16 @@ namespace Symfony\Component\Security\Core\Util; +@trigger_error('The '.__NAMESPACE__.'\\StringUtils class is deprecated since version 2.8 and will be removed in 3.0. Use hash_equals() instead.', E_USER_DEPRECATED); + +use Symfony\Polyfill\Util\Binary; + /** * String utility functions. * * @author Fabien Potencier <fabien@symfony.com> + * + * @deprecated since 2.8, to be removed in 3.0. */ class StringUtils { @@ -47,25 +53,7 @@ class StringUtils $userInput = (string) $userInput; } - if (function_exists('hash_equals')) { - return hash_equals($knownString, $userInput); - } - - $knownLen = self::safeStrlen($knownString); - $userLen = self::safeStrlen($userInput); - - if ($userLen !== $knownLen) { - return false; - } - - $result = 0; - - for ($i = 0; $i < $knownLen; ++$i) { - $result |= (ord($knownString[$i]) ^ ord($userInput[$i])); - } - - // They are only identical strings if $result is exactly 0... - return 0 === $result; + return hash_equals($knownString, $userInput); } /** @@ -77,17 +65,6 @@ class StringUtils */ public static function safeStrlen($string) { - // Premature optimization - // Since this cannot be changed at runtime, we can cache it - static $funcExists = null; - if (null === $funcExists) { - $funcExists = function_exists('mb_strlen'); - } - - if ($funcExists) { - return mb_strlen($string, '8bit'); - } - - return strlen($string); + return Binary::strlen($string); } } diff --git a/Core/composer.json b/Core/composer.json index 354c55e..3362971 100644 --- a/Core/composer.json +++ b/Core/composer.json @@ -17,22 +17,25 @@ ], "require": { "php": ">=5.3.9", - "paragonie/random_compat": "~1.0" + "symfony/polyfill-php55": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0" }, "require-dev": { - "symfony/event-dispatcher": "~2.1", - "symfony/expression-language": "~2.6", - "symfony/http-foundation": "~2.4", - "symfony/validator": "~2.5,>=2.5.9", - "psr/log": "~1.0", - "ircmaxell/password-compat": "1.0.*" + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/http-foundation": "~2.4|~3.0.0", + "symfony/ldap": "~2.8|~3.0.0", + "symfony/validator": "~2.5,>=2.5.9|~3.0.0", + "psr/log": "~1.0" }, "suggest": { "symfony/event-dispatcher": "", "symfony/http-foundation": "", "symfony/validator": "For using the user password constraint", "symfony/expression-language": "For using the expression voter", - "ircmaxell/password-compat": "For using the BCrypt password encoder in PHP <5.5" + "symfony/ldap": "For using LDAP integration" }, "autoload": { "psr-4": { "Symfony\\Component\\Security\\Core\\": "" }, @@ -43,7 +46,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/Csrf/CsrfTokenManager.php b/Csrf/CsrfTokenManager.php index e129502..cdda543 100644 --- a/Csrf/CsrfTokenManager.php +++ b/Csrf/CsrfTokenManager.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Security\Csrf; -use Symfony\Component\Security\Core\Util\StringUtils; use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator; use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface; use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage; @@ -92,6 +91,6 @@ class CsrfTokenManager implements CsrfTokenManagerInterface return false; } - return StringUtils::equals($this->storage->getToken($token->getId()), $token->getValue()); + return hash_equals($this->storage->getToken($token->getId()), $token->getValue()); } } diff --git a/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php b/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php index 1b325e5..e4ea80c 100644 --- a/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php +++ b/Csrf/Tests/TokenGenerator/UriSafeTokenGeneratorTest.php @@ -44,8 +44,7 @@ class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->random = $this->getMock('Symfony\Component\Security\Core\Util\SecureRandomInterface'); - $this->generator = new UriSafeTokenGenerator($this->random, self::ENTROPY); + $this->generator = new UriSafeTokenGenerator(self::ENTROPY); } protected function tearDown() @@ -56,11 +55,6 @@ class UriSafeTokenGeneratorTest extends \PHPUnit_Framework_TestCase public function testGenerateToken() { - $this->random->expects($this->once()) - ->method('nextBytes') - ->with(self::ENTROPY / 8) - ->will($this->returnValue(self::$bytes)); - $token = $this->generator->generateToken(); $this->assertTrue(ctype_print($token), 'is printable'); diff --git a/Csrf/TokenGenerator/UriSafeTokenGenerator.php b/Csrf/TokenGenerator/UriSafeTokenGenerator.php index 31e82ee..432adf2 100644 --- a/Csrf/TokenGenerator/UriSafeTokenGenerator.php +++ b/Csrf/TokenGenerator/UriSafeTokenGenerator.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Csrf\TokenGenerator; use Symfony\Component\Security\Core\Util\SecureRandomInterface; -use Symfony\Component\Security\Core\Util\SecureRandom; /** * Generates CSRF tokens. @@ -22,13 +21,6 @@ use Symfony\Component\Security\Core\Util\SecureRandom; class UriSafeTokenGenerator implements TokenGeneratorInterface { /** - * The generator for random values. - * - * @var SecureRandomInterface - */ - private $random; - - /** * The amount of entropy collected for each token (in bits). * * @var int @@ -38,15 +30,17 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface /** * Generates URI-safe CSRF tokens. * - * @param SecureRandomInterface|null $random The random value generator used for - * generating entropy - * @param int $entropy The amount of entropy collected for - * each token (in bits) + * @param int $entropy The amount of entropy collected for each token (in bits) */ - public function __construct(SecureRandomInterface $random = null, $entropy = 256) + public function __construct($entropy = 256) { - $this->random = $random ?: new SecureRandom(); - $this->entropy = $entropy; + if ($entropy instanceof SecureRandomInterface || func_num_args() === 2) { + @trigger_error('The '.__METHOD__.' method now requires the entropy to be given as the first argument. The SecureRandomInterface will be removed in 3.0.', E_USER_DEPRECATED); + + $this->entropy = func_num_args() === 2 ? func_get_arg(1) : 256; + } else { + $this->entropy = $entropy; + } } /** @@ -57,7 +51,7 @@ class UriSafeTokenGenerator implements TokenGeneratorInterface // Generate an URI safe base64 encoded string that does not contain "+", // "/" or "=" which need to be URL encoded and make URLs unnecessarily // longer. - $bytes = $this->random->nextBytes($this->entropy / 8); + $bytes = random_bytes($this->entropy / 8); return rtrim(strtr(base64_encode($bytes), '+/', '-_'), '='); } diff --git a/Csrf/composer.json b/Csrf/composer.json index 2930e32..4afc7ca 100644 --- a/Csrf/composer.json +++ b/Csrf/composer.json @@ -17,10 +17,12 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.4" + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/security-core": "~2.4|~3.0.0" }, "require-dev": { - "symfony/http-foundation": "~2.1" + "symfony/http-foundation": "~2.1|~3.0.0" }, "suggest": { "symfony/http-foundation": "For using the class SessionTokenStorage." @@ -34,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/Acl/.gitignore b/Guard/.gitignore index c49a5d8..c49a5d8 100644 --- a/Acl/.gitignore +++ b/Guard/.gitignore diff --git a/Guard/AbstractGuardAuthenticator.php b/Guard/AbstractGuardAuthenticator.php new file mode 100644 index 0000000..609d772 --- /dev/null +++ b/Guard/AbstractGuardAuthenticator.php @@ -0,0 +1,41 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard; + +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; + +/** + * An optional base class that creates a PostAuthenticationGuardToken for you. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface +{ + /** + * Shortcut to create a PostAuthenticationGuardToken for you, if you don't really + * care about which authenticated token you're using. + * + * @param UserInterface $user + * @param string $providerKey + * + * @return PostAuthenticationGuardToken + */ + public function createAuthenticatedToken(UserInterface $user, $providerKey) + { + return new PostAuthenticationGuardToken( + $user, + $providerKey, + $user->getRoles() + ); + } +} diff --git a/Guard/Authenticator/AbstractFormLoginAuthenticator.php b/Guard/Authenticator/AbstractFormLoginAuthenticator.php new file mode 100644 index 0000000..6d6d14e --- /dev/null +++ b/Guard/Authenticator/AbstractFormLoginAuthenticator.php @@ -0,0 +1,111 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Authenticator; + +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Security; + +/** + * A base class to make form login authentication easier! + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +abstract class AbstractFormLoginAuthenticator extends AbstractGuardAuthenticator +{ + /** + * Return the URL to the login page. + * + * @return string + */ + abstract protected function getLoginUrl(); + + /** + * The user will be redirected to the secure page they originally tried + * to access. But if no such page exists (i.e. the user went to the + * login page directly), this returns the URL the user should be redirected + * to after logging in successfully (e.g. your homepage). + * + * @return string + */ + abstract protected function getDefaultSuccessRedirectUrl(); + + /** + * Override to change what happens after a bad username/password is submitted. + * + * @param Request $request + * @param AuthenticationException $exception + * + * @return RedirectResponse + */ + public function onAuthenticationFailure(Request $request, AuthenticationException $exception) + { + if ($request->getSession() instanceof SessionInterface) { + $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception); + } + + $url = $this->getLoginUrl(); + + return new RedirectResponse($url); + } + + /** + * Override to change what happens after successful authentication. + * + * @param Request $request + * @param TokenInterface $token + * @param string $providerKey + * + * @return RedirectResponse + */ + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) + { + $targetPath = null; + + // if the user hit a secure page and start() was called, this was + // the URL they were on, and probably where you want to redirect to + if ($request->getSession() instanceof SessionInterface) { + $targetPath = $request->getSession()->get('_security.'.$providerKey.'.target_path'); + } + + if (!$targetPath) { + $targetPath = $this->getDefaultSuccessRedirectUrl(); + } + + return new RedirectResponse($targetPath); + } + + public function supportsRememberMe() + { + return true; + } + + /** + * Override to control what happens when the user hits a secure page + * but isn't logged in yet. + * + * @param Request $request + * @param AuthenticationException|null $authException + * + * @return RedirectResponse + */ + public function start(Request $request, AuthenticationException $authException = null) + { + $url = $this->getLoginUrl(); + + return new RedirectResponse($url); + } +} diff --git a/Guard/Firewall/GuardAuthenticationListener.php b/Guard/Firewall/GuardAuthenticationListener.php new file mode 100644 index 0000000..ed0a36e --- /dev/null +++ b/Guard/Firewall/GuardAuthenticationListener.php @@ -0,0 +1,201 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; +use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Firewall\ListenerInterface; +use Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface; + +/** + * Authentication listener for the "guard" system. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class GuardAuthenticationListener implements ListenerInterface +{ + private $guardHandler; + private $authenticationManager; + private $providerKey; + private $guardAuthenticators; + private $logger; + private $rememberMeServices; + + /** + * @param GuardAuthenticatorHandler $guardHandler The Guard handler + * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance + * @param string $providerKey The provider (i.e. firewall) key + * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider + * @param LoggerInterface $logger A LoggerInterface instance + */ + public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, array $guardAuthenticators, LoggerInterface $logger = null) + { + if (empty($providerKey)) { + throw new \InvalidArgumentException('$providerKey must not be empty.'); + } + + $this->guardHandler = $guardHandler; + $this->authenticationManager = $authenticationManager; + $this->providerKey = $providerKey; + $this->guardAuthenticators = $guardAuthenticators; + $this->logger = $logger; + } + + /** + * Iterates over each authenticator to see if each wants to authenticate the request. + * + * @param GetResponseEvent $event + */ + public function handle(GetResponseEvent $event) + { + if (null !== $this->logger) { + $this->logger->debug('Checking for guard authentication credentials.', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators))); + } + + foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { + // get a key that's unique to *this* guard authenticator + // this MUST be the same as GuardAuthenticationProvider + $uniqueGuardKey = $this->providerKey.'_'.$key; + + $this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event); + + if ($event->hasResponse()) { + if (null !== $this->logger) { + $this->logger->debug(sprintf('The "%s" authenticator set the response. Any later authenticator will not be called', get_class($guardAuthenticator))); + } + + break; + } + } + } + + private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorInterface $guardAuthenticator, GetResponseEvent $event) + { + $request = $event->getRequest(); + try { + if (null !== $this->logger) { + $this->logger->debug('Calling getCredentials() on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator))); + } + + // allow the authenticator to fetch authentication info from the request + $credentials = $guardAuthenticator->getCredentials($request); + + // allow null to be returned to skip authentication + if (null === $credentials) { + return; + } + + // create a token with the unique key, so that the provider knows which authenticator to use + $token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey); + + if (null !== $this->logger) { + $this->logger->debug('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator))); + } + // pass the token into the AuthenticationManager system + // this indirectly calls GuardAuthenticationProvider::authenticate() + $token = $this->authenticationManager->authenticate($token); + + if (null !== $this->logger) { + $this->logger->info('Guard authentication successful!', array('token' => $token, 'authenticator' => get_class($guardAuthenticator))); + } + + // sets the token on the token storage, etc + $this->guardHandler->authenticateWithToken($token, $request); + } catch (AuthenticationException $e) { + // oh no! Authentication failed! + + if (null !== $this->logger) { + $this->logger->info('Guard authentication failed.', array('exception' => $e, 'authenticator' => get_class($guardAuthenticator))); + } + + $response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey); + + if ($response instanceof Response) { + $event->setResponse($response); + } + + return; + } + + // success! + $response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey); + if ($response instanceof Response) { + if (null !== $this->logger) { + $this->logger->debug('Guard authenticator set success response.', array('response' => $response, 'authenticator' => get_class($guardAuthenticator))); + } + + $event->setResponse($response); + } else { + if (null !== $this->logger) { + $this->logger->debug('Guard authenticator set no success response: request continues.', array('authenticator' => get_class($guardAuthenticator))); + } + } + + // attempt to trigger the remember me functionality + $this->triggerRememberMe($guardAuthenticator, $request, $token, $response); + } + + /** + * Should be called if this listener will support remember me. + * + * @param RememberMeServicesInterface $rememberMeServices + */ + public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices) + { + $this->rememberMeServices = $rememberMeServices; + } + + /** + * Checks to see if remember me is supported in the authenticator and + * on the firewall. If it is, the RememberMeServicesInterface is notified. + * + * @param GuardAuthenticatorInterface $guardAuthenticator + * @param Request $request + * @param TokenInterface $token + * @param Response $response + */ + private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null) + { + if (null === $this->rememberMeServices) { + if (null !== $this->logger) { + $this->logger->debug('Remember me skipped: it is not configured for the firewall.', array('authenticator' => get_class($guardAuthenticator))); + } + + return; + } + + if (!$guardAuthenticator->supportsRememberMe()) { + if (null !== $this->logger) { + $this->logger->debug('Remember me skipped: your authenticator does not support it.', array('authenticator' => get_class($guardAuthenticator))); + } + + return; + } + + if (!$response instanceof Response) { + throw new \LogicException(sprintf( + '%s::onAuthenticationSuccess *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.', + get_class($guardAuthenticator) + )); + } + + $this->rememberMeServices->loginSuccess($request, $response, $token); + } +} diff --git a/Guard/GuardAuthenticatorHandler.php b/Guard/GuardAuthenticatorHandler.php new file mode 100644 index 0000000..5e1351d --- /dev/null +++ b/Guard/GuardAuthenticatorHandler.php @@ -0,0 +1,139 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\SecurityEvents; + +/** + * A utility class that does much of the *work* during the guard authentication process. + * + * By having the logic here instead of the listener, more of the process + * can be called directly (e.g. for manual authentication) or overridden. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class GuardAuthenticatorHandler +{ + private $tokenStorage; + + private $dispatcher; + + public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null) + { + $this->tokenStorage = $tokenStorage; + $this->dispatcher = $eventDispatcher; + } + + /** + * Authenticates the given token in the system. + * + * @param TokenInterface $token + * @param Request $request + */ + public function authenticateWithToken(TokenInterface $token, Request $request) + { + $this->tokenStorage->setToken($token); + + if (null !== $this->dispatcher) { + $loginEvent = new InteractiveLoginEvent($request, $token); + $this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent); + } + } + + /** + * Returns the "on success" response for the given GuardAuthenticator. + * + * @param TokenInterface $token + * @param Request $request + * @param GuardAuthenticatorInterface $guardAuthenticator + * @param string $providerKey The provider (i.e. firewall) key + * + * @return null|Response + */ + public function handleAuthenticationSuccess(TokenInterface $token, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey) + { + $response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey); + + // check that it's a Response or null + if ($response instanceof Response || null === $response) { + return $response; + } + + throw new \UnexpectedValueException(sprintf( + 'The %s::onAuthenticationSuccess method must return null or a Response object. You returned %s.', + get_class($guardAuthenticator), + is_object($response) ? get_class($response) : gettype($response) + )); + } + + /** + * Convenience method for authenticating the user and returning the + * Response *if any* for success. + * + * @param UserInterface $user + * @param Request $request + * @param GuardAuthenticatorInterface $authenticator + * @param string $providerKey The provider (i.e. firewall) key + * + * @return Response|null + */ + public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, GuardAuthenticatorInterface $authenticator, $providerKey) + { + // create an authenticated token for the User + $token = $authenticator->createAuthenticatedToken($user, $providerKey); + // authenticate this in the system + $this->authenticateWithToken($token, $request); + + // return the success metric + return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey); + } + + /** + * Handles an authentication failure and returns the Response for the + * GuardAuthenticator. + * + * @param AuthenticationException $authenticationException + * @param Request $request + * @param GuardAuthenticatorInterface $guardAuthenticator + * @param string $providerKey The key of the firewall + * + * @return null|Response + */ + public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey) + { + $token = $this->tokenStorage->getToken(); + if ($token instanceof PostAuthenticationGuardToken && $providerKey === $token->getProviderKey()) { + $this->tokenStorage->setToken(null); + } + + $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException); + if ($response instanceof Response || null === $response) { + // returning null is ok, it means they want the request to continue + return $response; + } + + throw new \UnexpectedValueException(sprintf( + 'The %s::onAuthenticationFailure method must return null or a Response object. You returned %s.', + get_class($guardAuthenticator), + is_object($response) ? get_class($response) : gettype($response) + )); + } +} diff --git a/Guard/GuardAuthenticatorInterface.php b/Guard/GuardAuthenticatorInterface.php new file mode 100644 index 0000000..b28f06d --- /dev/null +++ b/Guard/GuardAuthenticatorInterface.php @@ -0,0 +1,160 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Guard\Token\GuardTokenInterface; +use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; + +/** + * The interface for all "guard" authenticators. + * + * The methods on this interface are called throughout the guard authentication + * process to give you the power to control most parts of the process from + * one location. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface +{ + /** + * Get the authentication credentials from the request and return them + * as any type (e.g. an associate array). If you return null, authentication + * will be skipped. + * + * Whatever value you return here will be passed to getUser() and checkCredentials() + * + * For example, for a form login, you might: + * + * if ($request->request->has('_username')) { + * return array( + * 'username' => $request->request->get('_username'), + * 'password' => $request->request->get('_password'), + * ); + * } else { + * return; + * } + * + * Or for an API token that's on a header, you might use: + * + * return array('api_key' => $request->headers->get('X-API-TOKEN')); + * + * @param Request $request + * + * @return mixed|null + */ + public function getCredentials(Request $request); + + /** + * Return a UserInterface object based on the credentials. + * + * The *credentials* are the return value from getCredentials() + * + * You may throw an AuthenticationException if you wish. If you return + * null, then a UsernameNotFoundException is thrown for you. + * + * @param mixed $credentials + * @param UserProviderInterface $userProvider + * + * @throws AuthenticationException + * + * @return UserInterface|null + */ + public function getUser($credentials, UserProviderInterface $userProvider); + + /** + * Returns true if the credentials are valid. + * + * If any value other than true is returned, authentication will + * fail. You may also throw an AuthenticationException if you wish + * to cause authentication to fail. + * + * The *credentials* are the return value from getCredentials() + * + * @param mixed $credentials + * @param UserInterface $user + * + * @return bool + * + * @throws AuthenticationException + */ + public function checkCredentials($credentials, UserInterface $user); + + /** + * Create an authenticated token for the given user. + * + * If you don't care about which token class is used or don't really + * understand what a "token" is, you can skip this method by extending + * the AbstractGuardAuthenticator class from your authenticator. + * + * @see AbstractGuardAuthenticator + * + * @param UserInterface $user + * @param string $providerKey The provider (i.e. firewall) key + * + * @return GuardTokenInterface + */ + public function createAuthenticatedToken(UserInterface $user, $providerKey); + + /** + * Called when authentication executed, but failed (e.g. wrong username password). + * + * This should return the Response sent back to the user, like a + * RedirectResponse to the login page or a 403 response. + * + * If you return null, the request will continue, but the user will + * not be authenticated. This is probably not what you want to do. + * + * @param Request $request + * @param AuthenticationException $exception + * + * @return Response|null + */ + public function onAuthenticationFailure(Request $request, AuthenticationException $exception); + + /** + * Called when authentication executed and was successful! + * + * This should return the Response sent back to the user, like a + * RedirectResponse to the last page they visited. + * + * If you return null, the current request will continue, and the user + * will be authenticated. This makes sense, for example, with an API. + * + * @param Request $request + * @param TokenInterface $token + * @param string $providerKey The provider (i.e. firewall) key + * + * @return Response|null + */ + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey); + + /** + * Does this method support remember me cookies? + * + * Remember me cookie will be set if *all* of the following are met: + * A) This method returns true + * B) The remember_me key under your firewall is configured + * C) The "remember me" functionality is activated. This is usually + * done by having a _remember_me checkbox in your form, but + * can be configured by the "always_remember_me" and "remember_me_parameter" + * parameters under the "remember_me" firewall key + * + * @return bool + */ + public function supportsRememberMe(); +} diff --git a/Acl/LICENSE b/Guard/LICENSE index 12a7453..12a7453 100644 --- a/Acl/LICENSE +++ b/Guard/LICENSE diff --git a/Guard/Provider/GuardAuthenticationProvider.php b/Guard/Provider/GuardAuthenticationProvider.php new file mode 100644 index 0000000..4347e02 --- /dev/null +++ b/Guard/Provider/GuardAuthenticationProvider.php @@ -0,0 +1,148 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Provider; + +use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; +use Symfony\Component\Security\Guard\GuardAuthenticatorInterface; +use Symfony\Component\Security\Guard\Token\GuardTokenInterface; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Core\Exception\AuthenticationExpiredException; + +/** + * Responsible for accepting the PreAuthenticationGuardToken and calling + * the correct authenticator to retrieve the authenticated token. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class GuardAuthenticationProvider implements AuthenticationProviderInterface +{ + /** + * @var GuardAuthenticatorInterface[] + */ + private $guardAuthenticators; + private $userProvider; + private $providerKey; + private $userChecker; + + /** + * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener + * @param UserProviderInterface $userProvider The user provider + * @param string $providerKey The provider (i.e. firewall) key + * @param UserCheckerInterface $userChecker + */ + public function __construct(array $guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker) + { + $this->guardAuthenticators = $guardAuthenticators; + $this->userProvider = $userProvider; + $this->providerKey = $providerKey; + $this->userChecker = $userChecker; + } + + /** + * Finds the correct authenticator for the token and calls it. + * + * @param GuardTokenInterface $token + * + * @return TokenInterface + */ + public function authenticate(TokenInterface $token) + { + if (!$this->supports($token)) { + throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.'); + } + + if (!$token instanceof PreAuthenticationGuardToken) { + /* + * The listener *only* passes PreAuthenticationGuardToken instances. + * This means that an authenticated token (e.g. PostAuthenticationGuardToken) + * is being passed here, which happens if that token becomes + * "not authenticated" (e.g. happens if the user changes between + * requests). In this case, the user should be logged out, so + * we will return an AnonymousToken to accomplish that. + */ + + // this should never happen - but technically, the token is + // authenticated... so it could just be returned + if ($token->isAuthenticated()) { + return $token; + } + + // this AccountStatusException causes the user to be logged out + throw new AuthenticationExpiredException(); + } + + // find the *one* GuardAuthenticator that this token originated from + foreach ($this->guardAuthenticators as $key => $guardAuthenticator) { + // get a key that's unique to *this* guard authenticator + // this MUST be the same as GuardAuthenticationListener + $uniqueGuardKey = $this->providerKey.'_'.$key; + + if ($uniqueGuardKey == $token->getGuardProviderKey()) { + return $this->authenticateViaGuard($guardAuthenticator, $token); + } + } + + // no matching authenticator found - but there will be multiple GuardAuthenticationProvider + // instances that will be checked if you have multiple firewalls. + } + + private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token) + { + // get the user from the GuardAuthenticator + $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider); + + if (null === $user) { + throw new UsernameNotFoundException(sprintf( + 'Null returned from %s::getUser()', + get_class($guardAuthenticator) + )); + } + + if (!$user instanceof UserInterface) { + throw new \UnexpectedValueException(sprintf( + 'The %s::getUser() method must return a UserInterface. You returned %s.', + get_class($guardAuthenticator), + is_object($user) ? get_class($user) : gettype($user) + )); + } + + $this->userChecker->checkPreAuth($user); + if (true !== $guardAuthenticator->checkCredentials($token->getCredentials(), $user)) { + throw new BadCredentialsException(sprintf('Authentication failed because %s::checkCredentials() did not return true.', get_class($guardAuthenticator))); + } + $this->userChecker->checkPostAuth($user); + + // turn the UserInterface into a TokenInterface + $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey); + if (!$authenticatedToken instanceof TokenInterface) { + throw new \UnexpectedValueException(sprintf( + 'The %s::createAuthenticatedToken() method must return a TokenInterface. You returned %s.', + get_class($guardAuthenticator), + is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken) + )); + } + + return $authenticatedToken; + } + + public function supports(TokenInterface $token) + { + return $token instanceof GuardTokenInterface; + } +} diff --git a/Acl/README.md b/Guard/README.md index a6048fd..ce70622 100644 --- a/Acl/README.md +++ b/Guard/README.md @@ -1,10 +1,9 @@ -Security Component - ACL (Access Control List) -============================================== +Security Component - Guard +========================== -Security provides an infrastructure for sophisticated authorization systems, -which makes it possible to easily separate the actual authorization logic from -so called user providers that hold the users credentials. It is inspired by -the Java Spring framework. +The Guard component brings many layers of authentication together, making +it much easier to create complex authentication systems where you have +total control. Resources --------- diff --git a/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php b/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php new file mode 100644 index 0000000..3dbbf84 --- /dev/null +++ b/Guard/Tests/Authenticator/FormLoginAuthenticatorTest.php @@ -0,0 +1,212 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Tests\Authenticator; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; + +/** + * @author Jean Pasdeloup <jpasdeloup@sedona.fr> + */ +class FormLoginAuthenticatorTest extends \PHPUnit_Framework_TestCase +{ + private $requestWithoutSession; + private $requestWithSession; + private $authenticator; + + const LOGIN_URL = 'http://login'; + const DEFAULT_SUCCESS_URL = 'http://defaultsuccess'; + const CUSTOM_SUCCESS_URL = 'http://customsuccess'; + + public function testAuthenticationFailureWithoutSession() + { + $failureResponse = $this->authenticator->onAuthenticationFailure($this->requestWithoutSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + public function testAuthenticationFailureWithSession() + { + $this->requestWithSession->getSession() + ->expects($this->once()) + ->method('set'); + + $failureResponse = $this->authenticator->onAuthenticationFailure($this->requestWithSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + public function testAuthenticationSuccessWithoutSession() + { + $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') + ->disableOriginalConstructor() + ->getMock(); + + $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithoutSession, $token, 'providerkey'); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); + $this->assertEquals(self::DEFAULT_SUCCESS_URL, $redirectResponse->getTargetUrl()); + } + + public function testAuthenticationSuccessWithSessionButEmpty() + { + $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->requestWithSession->getSession() + ->expects($this->once()) + ->method('get') + ->will($this->returnValue(null)); + + $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey'); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); + $this->assertEquals(self::DEFAULT_SUCCESS_URL, $redirectResponse->getTargetUrl()); + } + + public function testAuthenticationSuccessWithSessionAndTarget() + { + $token = $this->getMockBuilder('Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->requestWithSession->getSession() + ->expects($this->once()) + ->method('get') + ->will($this->returnValue(self::CUSTOM_SUCCESS_URL)); + + $redirectResponse = $this->authenticator->onAuthenticationSuccess($this->requestWithSession, $token, 'providerkey'); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $redirectResponse); + $this->assertEquals(self::CUSTOM_SUCCESS_URL, $redirectResponse->getTargetUrl()); + } + + public function testRememberMe() + { + $doSupport = $this->authenticator->supportsRememberMe(); + + $this->assertTrue($doSupport); + } + + public function testStartWithoutSession() + { + $failureResponse = $this->authenticator->start($this->requestWithoutSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + public function testStartWithSession() + { + $failureResponse = $this->authenticator->start($this->requestWithSession, new AuthenticationException()); + + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\RedirectResponse', $failureResponse); + $this->assertEquals(self::LOGIN_URL, $failureResponse->getTargetUrl()); + } + + protected function setUp() + { + $this->requestWithoutSession = new Request(array(), array(), array(), array(), array(), array()); + $this->requestWithSession = new Request(array(), array(), array(), array(), array(), array()); + + $session = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\Session\\SessionInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->requestWithSession->setSession($session); + + $this->authenticator = new TestFormLoginAuthenticator(); + $this->authenticator + ->setLoginUrl(self::LOGIN_URL) + ->setDefaultSuccessRedirectUrl(self::DEFAULT_SUCCESS_URL) + ; + } + + protected function tearDown() + { + $this->request = null; + $this->requestWithSession = null; + } +} + +class TestFormLoginAuthenticator extends AbstractFormLoginAuthenticator +{ + private $loginUrl; + private $defaultSuccessRedirectUrl; + + /** + * @param mixed $defaultSuccessRedirectUrl + * + * @return TestFormLoginAuthenticator + */ + public function setDefaultSuccessRedirectUrl($defaultSuccessRedirectUrl) + { + $this->defaultSuccessRedirectUrl = $defaultSuccessRedirectUrl; + + return $this; + } + + /** + * @param mixed $loginUrl + * + * @return TestFormLoginAuthenticator + */ + public function setLoginUrl($loginUrl) + { + $this->loginUrl = $loginUrl; + + return $this; + } + + /** + * {@inheritdoc} + */ + protected function getLoginUrl() + { + return $this->loginUrl; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultSuccessRedirectUrl() + { + return $this->defaultSuccessRedirectUrl; + } + + /** + * {@inheritdoc} + */ + public function getCredentials(Request $request) + { + return 'credentials'; + } + + /** + * {@inheritdoc} + */ + public function getUser($credentials, UserProviderInterface $userProvider) + { + return $userProvider->loadUserByUsername($credentials); + } + + /** + * {@inheritdoc} + */ + public function checkCredentials($credentials, UserInterface $user) + { + return true; + } +} diff --git a/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php b/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php new file mode 100644 index 0000000..3224fee --- /dev/null +++ b/Guard/Tests/Firewall/GuardAuthenticationListenerTest.php @@ -0,0 +1,256 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Tests\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; + +/** + * @author Ryan Weaver <weaverryan@gmail.com> + */ +class GuardAuthenticationListenerTest extends \PHPUnit_Framework_TestCase +{ + private $authenticationManager; + private $guardAuthenticatorHandler; + private $event; + private $logger; + private $request; + private $rememberMeServices; + + public function testHandleSuccess() + { + $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $providerKey = 'my_firewall'; + + $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base'); + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->with($this->equalTo($this->request)) + ->will($this->returnValue($credentials)); + + // a clone of the token that should be created internally + $uniqueGuardKey = 'my_firewall_0'; + $nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey); + + $this->authenticationManager + ->expects($this->once()) + ->method('authenticate') + ->with($this->equalTo($nonAuthedToken)) + ->will($this->returnValue($authenticateToken)); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('authenticateWithToken') + ->with($authenticateToken, $this->request); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('handleAuthenticationSuccess') + ->with($authenticateToken, $this->request, $authenticator, $providerKey); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->setRememberMeServices($this->rememberMeServices); + // should never be called - our handleAuthenticationSuccess() does not return a Response + $this->rememberMeServices + ->expects($this->never()) + ->method('loginSuccess'); + + $listener->handle($this->event); + } + + public function testHandleSuccessStopsAfterResponseIsSet() + { + $authenticator1 = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticator2 = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + + // mock the first authenticator to fail, and set a Response + $authenticator1 + ->expects($this->once()) + ->method('getCredentials') + ->willThrowException(new AuthenticationException()); + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('handleAuthenticationFailure') + ->willReturn(new Response()); + // the second authenticator should *never* be called + $authenticator2 + ->expects($this->never()) + ->method('getCredentials'); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + 'my_firewall', + array($authenticator1, $authenticator2), + $this->logger + ); + + $listener->handle($this->event); + } + + public function testHandleSuccessWithRememberMe() + { + $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $providerKey = 'my_firewall_with_rememberme'; + + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->with($this->equalTo($this->request)) + ->will($this->returnValue(array('username' => 'anything_not_empty'))); + + $this->authenticationManager + ->expects($this->once()) + ->method('authenticate') + ->will($this->returnValue($authenticateToken)); + + $successResponse = new Response('Success!'); + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('handleAuthenticationSuccess') + ->will($this->returnValue($successResponse)); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->setRememberMeServices($this->rememberMeServices); + $authenticator->expects($this->once()) + ->method('supportsRememberMe') + ->will($this->returnValue(true)); + // should be called - we do have a success Response + $this->rememberMeServices + ->expects($this->once()) + ->method('loginSuccess'); + + $listener->handle($this->event); + } + + public function testHandleCatchesAuthenticationException() + { + $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $providerKey = 'my_firewall2'; + + $authException = new AuthenticationException('Get outta here crazy user with a bad password!'); + $authenticator + ->expects($this->once()) + ->method('getCredentials') + ->will($this->throwException($authException)); + + // this is not called + $this->authenticationManager + ->expects($this->never()) + ->method('authenticate'); + + $this->guardAuthenticatorHandler + ->expects($this->once()) + ->method('handleAuthenticationFailure') + ->with($authException, $this->request, $authenticator, $providerKey); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticator), + $this->logger + ); + + $listener->handle($this->event); + } + + public function testReturnNullToSkipAuth() + { + $authenticatorA = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticatorB = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $providerKey = 'my_firewall3'; + + $authenticatorA + ->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue(null)); + $authenticatorB + ->expects($this->once()) + ->method('getCredentials') + ->will($this->returnValue(null)); + + // this is not called + $this->authenticationManager + ->expects($this->never()) + ->method('authenticate'); + + $this->guardAuthenticatorHandler + ->expects($this->never()) + ->method('handleAuthenticationSuccess'); + + $listener = new GuardAuthenticationListener( + $this->guardAuthenticatorHandler, + $this->authenticationManager, + $providerKey, + array($authenticatorA, $authenticatorB), + $this->logger + ); + + $listener->handle($this->event); + } + + protected function setUp() + { + $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->guardAuthenticatorHandler = $this->getMockBuilder('Symfony\Component\Security\Guard\GuardAuthenticatorHandler') + ->disableOriginalConstructor() + ->getMock(); + + $this->request = new Request(array(), array(), array(), array(), array(), array()); + + $this->event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + ->disableOriginalConstructor() + ->setMethods(array('getRequest')) + ->getMock(); + $this->event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($this->request)); + + $this->logger = $this->getMock('Psr\Log\LoggerInterface'); + $this->rememberMeServices = $this->getMock('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface'); + } + + protected function tearDown() + { + $this->authenticationManager = null; + $this->guardAuthenticatorHandler = null; + $this->event = null; + $this->logger = null; + $this->request = null; + $this->rememberMeServices = null; + } +} diff --git a/Guard/Tests/GuardAuthenticatorHandlerTest.php b/Guard/Tests/GuardAuthenticatorHandlerTest.php new file mode 100644 index 0000000..6f36702 --- /dev/null +++ b/Guard/Tests/GuardAuthenticatorHandlerTest.php @@ -0,0 +1,141 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Tests; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; +use Symfony\Component\Security\Http\SecurityEvents; + +class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase +{ + private $tokenStorage; + private $dispatcher; + private $token; + private $request; + private $guardAuthenticator; + + public function testAuthenticateWithToken() + { + $this->tokenStorage->expects($this->once()) + ->method('setToken') + ->with($this->token); + + $loginEvent = new InteractiveLoginEvent($this->request, $this->token); + + $this->dispatcher + ->expects($this->once()) + ->method('dispatch') + ->with($this->equalTo(SecurityEvents::INTERACTIVE_LOGIN), $this->equalTo($loginEvent)) + ; + + $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher); + $handler->authenticateWithToken($this->token, $this->request); + } + + public function testHandleAuthenticationSuccess() + { + $providerKey = 'my_handleable_firewall'; + $response = new Response('Guard all the things!'); + $this->guardAuthenticator->expects($this->once()) + ->method('onAuthenticationSuccess') + ->with($this->request, $this->token, $providerKey) + ->will($this->returnValue($response)); + + $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher); + $actualResponse = $handler->handleAuthenticationSuccess($this->token, $this->request, $this->guardAuthenticator, $providerKey); + $this->assertSame($response, $actualResponse); + } + + public function testHandleAuthenticationFailure() + { + // setToken() not called - getToken() will return null, so there's nothing to clear + $this->tokenStorage->expects($this->never()) + ->method('setToken') + ->with(null); + $authException = new AuthenticationException('Bad password!'); + + $response = new Response('Try again, but with the right password!'); + $this->guardAuthenticator->expects($this->once()) + ->method('onAuthenticationFailure') + ->with($this->request, $authException) + ->will($this->returnValue($response)); + + $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher); + $actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, 'firewall_provider_key'); + $this->assertSame($response, $actualResponse); + } + + /** + * @dataProvider getTokenClearingTests + */ + public function testHandleAuthenticationClearsToken($tokenClass, $tokenProviderKey, $actualProviderKey, $shouldTokenBeCleared) + { + $token = $this->getMockBuilder($tokenClass) + ->disableOriginalConstructor() + ->getMock(); + $token->expects($this->any()) + ->method('getProviderKey') + ->will($this->returnValue($tokenProviderKey)); + + // make the $token be the current token + $this->tokenStorage->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token)); + + $this->tokenStorage->expects($shouldTokenBeCleared ? $this->once() : $this->never()) + ->method('setToken') + ->with(null); + $authException = new AuthenticationException('Bad password!'); + + $response = new Response('Try again, but with the right password!'); + $this->guardAuthenticator->expects($this->once()) + ->method('onAuthenticationFailure') + ->with($this->request, $authException) + ->will($this->returnValue($response)); + + $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher); + $actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator, $actualProviderKey); + $this->assertSame($response, $actualResponse); + } + + public function getTokenClearingTests() + { + $tests = array(); + // correct token class and matching firewall => clear the token + $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'the_firewall_key', true); + $tests[] = array('Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken', 'the_firewall_key', 'different_key', false); + $tests[] = array('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', 'the_firewall_key', 'the_firewall_key', false); + + return $tests; + } + + protected function setUp() + { + $this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $this->request = new Request(array(), array(), array(), array(), array(), array()); + $this->guardAuthenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + } + + protected function tearDown() + { + $this->tokenStorage = null; + $this->dispatcher = null; + $this->token = null; + $this->request = null; + $this->guardAuthenticator = null; + } +} diff --git a/Guard/Tests/Provider/GuardAuthenticationProviderTest.php b/Guard/Tests/Provider/GuardAuthenticationProviderTest.php new file mode 100644 index 0000000..bfcf24b --- /dev/null +++ b/Guard/Tests/Provider/GuardAuthenticationProviderTest.php @@ -0,0 +1,150 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Tests\Provider; + +use Symfony\Component\Security\Guard\Provider\GuardAuthenticationProvider; +use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; + +/** + * @author Ryan Weaver <weaverryan@gmail.com> + */ +class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase +{ + private $userProvider; + private $userChecker; + private $preAuthenticationToken; + + public function testAuthenticate() + { + $providerKey = 'my_cool_firewall'; + + $authenticatorA = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticatorB = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticatorC = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + $authenticators = array($authenticatorA, $authenticatorB, $authenticatorC); + + // called 2 times - for authenticator A and B (stops on B because of match) + $this->preAuthenticationToken->expects($this->exactly(2)) + ->method('getGuardProviderKey') + // it will return the "1" index, which will match authenticatorB + ->will($this->returnValue('my_cool_firewall_1')); + + $enteredCredentials = array( + 'username' => '_weaverryan_test_user', + 'password' => 'guard_auth_ftw', + ); + $this->preAuthenticationToken->expects($this->atLeastOnce()) + ->method('getCredentials') + ->will($this->returnValue($enteredCredentials)); + + // authenticators A and C are never called + $authenticatorA->expects($this->never()) + ->method('getUser'); + $authenticatorC->expects($this->never()) + ->method('getUser'); + + $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $authenticatorB->expects($this->once()) + ->method('getUser') + ->with($enteredCredentials, $this->userProvider) + ->will($this->returnValue($mockedUser)); + // checkCredentials is called + $authenticatorB->expects($this->once()) + ->method('checkCredentials') + ->with($enteredCredentials, $mockedUser) + // authentication works! + ->will($this->returnValue(true)); + $authedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $authenticatorB->expects($this->once()) + ->method('createAuthenticatedToken') + ->with($mockedUser, $providerKey) + ->will($this->returnValue($authedToken)); + + // user checker should be called + $this->userChecker->expects($this->once()) + ->method('checkPreAuth') + ->with($mockedUser); + $this->userChecker->expects($this->once()) + ->method('checkPostAuth') + ->with($mockedUser); + + $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker); + $actualAuthedToken = $provider->authenticate($this->preAuthenticationToken); + $this->assertSame($authedToken, $actualAuthedToken); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\BadCredentialsException + */ + public function testCheckCredentialsReturningNonTrueFailsAuthentication() + { + $providerKey = 'my_uncool_firewall'; + + $authenticator = $this->getMock('Symfony\Component\Security\Guard\GuardAuthenticatorInterface'); + + // make sure the authenticator is used + $this->preAuthenticationToken->expects($this->any()) + ->method('getGuardProviderKey') + // the 0 index, to match the only authenticator + ->will($this->returnValue('my_uncool_firewall_0')); + + $this->preAuthenticationToken->expects($this->atLeastOnce()) + ->method('getCredentials') + ->will($this->returnValue('non-null-value')); + + $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $authenticator->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($mockedUser)); + // checkCredentials is called + $authenticator->expects($this->once()) + ->method('checkCredentials') + // authentication fails :( + ->will($this->returnValue(null)); + + $provider = new GuardAuthenticationProvider(array($authenticator), $this->userProvider, $providerKey, $this->userChecker); + $provider->authenticate($this->preAuthenticationToken); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AuthenticationExpiredException + */ + public function testGuardWithNoLongerAuthenticatedTriggersLogout() + { + $providerKey = 'my_firewall_abc'; + + // create a token and mark it as NOT authenticated anymore + // this mimics what would happen if a user "changed" between request + $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $token = new PostAuthenticationGuardToken($mockedUser, $providerKey, array('ROLE_USER')); + $token->setAuthenticated(false); + + $provider = new GuardAuthenticationProvider(array(), $this->userProvider, $providerKey, $this->userChecker); + $actualToken = $provider->authenticate($token); + } + + protected function setUp() + { + $this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface'); + $this->preAuthenticationToken = $this->getMockBuilder('Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken') + ->disableOriginalConstructor() + ->getMock(); + } + + protected function tearDown() + { + $this->userProvider = null; + $this->userChecker = null; + $this->preAuthenticationToken = null; + } +} diff --git a/Guard/Token/GuardTokenInterface.php b/Guard/Token/GuardTokenInterface.php new file mode 100644 index 0000000..063ffd3 --- /dev/null +++ b/Guard/Token/GuardTokenInterface.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Token; + +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * A marker interface that both guard tokens implement. + * + * Any tokens passed to GuardAuthenticationProvider (i.e. any tokens that + * are handled by the guard auth system) must implement this + * interface. + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +interface GuardTokenInterface extends TokenInterface +{ +} diff --git a/Guard/Token/PostAuthenticationGuardToken.php b/Guard/Token/PostAuthenticationGuardToken.php new file mode 100644 index 0000000..36c40ca --- /dev/null +++ b/Guard/Token/PostAuthenticationGuardToken.php @@ -0,0 +1,90 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Token; + +use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; +use Symfony\Component\Security\Core\Role\RoleInterface; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Used as an "authenticated" token, though it could be set to not-authenticated later. + * + * If you're using Guard authentication, you *must* use a class that implements + * GuardTokenInterface as your authenticated token (like this class). + * + * @author Ryan Weaver <ryan@knpuniversity.com>n@gmail.com> + */ +class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface +{ + private $providerKey; + + /** + * @param UserInterface $user The user! + * @param string $providerKey The provider (firewall) key + * @param RoleInterface[]|string[] $roles An array of roles + * + * @throws \InvalidArgumentException + */ + public function __construct(UserInterface $user, $providerKey, array $roles) + { + parent::__construct($roles); + + if (empty($providerKey)) { + throw new \InvalidArgumentException('$providerKey (i.e. firewall key) must not be empty.'); + } + + $this->setUser($user); + $this->providerKey = $providerKey; + + // this token is meant to be used after authentication success, so it is always authenticated + // you could set it as non authenticated later if you need to + parent::setAuthenticated(true); + } + + /** + * This is meant to be only an authenticated token, where credentials + * have already been used and are thus cleared. + * + * {@inheritdoc} + */ + public function getCredentials() + { + return array(); + } + + /** + * Returns the provider (firewall) key. + * + * @return string + */ + public function getProviderKey() + { + return $this->providerKey; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->providerKey, parent::serialize())); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->providerKey, $parentStr) = unserialize($serialized); + parent::unserialize($parentStr); + } +} diff --git a/Guard/Token/PreAuthenticationGuardToken.php b/Guard/Token/PreAuthenticationGuardToken.php new file mode 100644 index 0000000..abbe985 --- /dev/null +++ b/Guard/Token/PreAuthenticationGuardToken.php @@ -0,0 +1,65 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Guard\Token; + +use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; + +/** + * The token used by the guard auth system before authentication. + * + * The GuardAuthenticationListener creates this, which is then consumed + * immediately by the GuardAuthenticationProvider. If authentication is + * successful, a different authenticated token is returned + * + * @author Ryan Weaver <ryan@knpuniversity.com> + */ +class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface +{ + private $credentials; + private $guardProviderKey; + + /** + * @param mixed $credentials + * @param string $guardProviderKey Unique key that bind this token to a specific GuardAuthenticatorInterface + */ + public function __construct($credentials, $guardProviderKey) + { + $this->credentials = $credentials; + $this->guardProviderKey = $guardProviderKey; + + parent::__construct(array()); + + // never authenticated + parent::setAuthenticated(false); + } + + public function getGuardProviderKey() + { + return $this->guardProviderKey; + } + + /** + * Returns the user credentials, which might be an array of anything you + * wanted to put in there (e.g. username, password, favoriteColor). + * + * @return mixed The user credentials + */ + public function getCredentials() + { + return $this->credentials; + } + + public function setAuthenticated($authenticated) + { + throw new \LogicException('The PreAuthenticationGuardToken is *never* authenticated.'); + } +} diff --git a/Acl/composer.json b/Guard/composer.json index b292742..3208920 100644 --- a/Acl/composer.json +++ b/Guard/composer.json @@ -1,7 +1,7 @@ { - "name": "symfony/security-acl", + "name": "symfony/security-guard", "type": "library", - "description": "Symfony Security Component - ACL (Access Control List)", + "description": "Symfony Security Component - Guard", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", @@ -17,20 +17,14 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.4" + "symfony/security-core": "~2.8|~3.0.0", + "symfony/security-http": "~2.7|~3.0.0" }, "require-dev": { - "doctrine/common": "~2.2", - "doctrine/dbal": "~2.2", "psr/log": "~1.0" }, - "suggest": { - "symfony/class-loader": "For using the ACL generateSql script", - "symfony/finder": "For using the ACL generateSql script", - "doctrine/dbal": "For using the built-in ACL implementation" - }, "autoload": { - "psr-4": { "Symfony\\Component\\Security\\Acl\\": "" }, + "psr-4": { "Symfony\\Component\\Security\\Guard\\": "" }, "exclude-from-classmap": [ "/Tests/" ] @@ -38,7 +32,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/Acl/phpunit.xml.dist b/Guard/phpunit.xml.dist index b1ea047..da5a382 100644 --- a/Acl/phpunit.xml.dist +++ b/Guard/phpunit.xml.dist @@ -16,7 +16,7 @@ </php> <testsuites> - <testsuite name="Symfony Security Component ACL Test Suite"> + <testsuite name="Symfony Security Component Guard Suite"> <directory>./Tests/</directory> </testsuite> </testsuites> diff --git a/Http/Authentication/DefaultAuthenticationFailureHandler.php b/Http/Authentication/DefaultAuthenticationFailureHandler.php index 830c00a..ea5c356 100644 --- a/Http/Authentication/DefaultAuthenticationFailureHandler.php +++ b/Http/Authentication/DefaultAuthenticationFailureHandler.php @@ -17,6 +17,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * Class with the default authentication failure handling logic. @@ -82,7 +83,7 @@ class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandle */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { - if ($failureUrl = $request->get($this->options['failure_path_parameter'], null, true)) { + if ($failureUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['failure_path_parameter'])) { $this->options['failure_path'] = $failureUrl; } diff --git a/Http/Authentication/DefaultAuthenticationSuccessHandler.php b/Http/Authentication/DefaultAuthenticationSuccessHandler.php index b6a7df5..bfc0c8b 100644 --- a/Http/Authentication/DefaultAuthenticationSuccessHandler.php +++ b/Http/Authentication/DefaultAuthenticationSuccessHandler.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Security\Http\Authentication; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * Class with the default authentication success handling logic. @@ -108,7 +109,7 @@ class DefaultAuthenticationSuccessHandler implements AuthenticationSuccessHandle return $this->options['default_target_path']; } - if ($targetUrl = $request->get($this->options['target_path_parameter'], null, true)) { + if ($targetUrl = ParameterBagUtils::getRequestParameterValue($request, $this->options['target_path_parameter'])) { return $targetUrl; } diff --git a/Http/Authentication/SimpleFormAuthenticatorInterface.php b/Http/Authentication/SimpleFormAuthenticatorInterface.php new file mode 100644 index 0000000..112688c --- /dev/null +++ b/Http/Authentication/SimpleFormAuthenticatorInterface.php @@ -0,0 +1,21 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Authentication; + +use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface as BaseSimpleFormAuthenticatorInterface; + +/** + * @author Jordi Boggiano <j.boggiano@seld.be> + */ +interface SimpleFormAuthenticatorInterface extends BaseSimpleFormAuthenticatorInterface +{ +} diff --git a/Http/Authentication/SimplePreAuthenticatorInterface.php b/Http/Authentication/SimplePreAuthenticatorInterface.php new file mode 100644 index 0000000..afa8049 --- /dev/null +++ b/Http/Authentication/SimplePreAuthenticatorInterface.php @@ -0,0 +1,21 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http\Authentication; + +use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface as BaseSimplePreAuthenticatorInterface; + +/** + * @author Jordi Boggiano <j.boggiano@seld.be> + */ +interface SimplePreAuthenticatorInterface extends BaseSimplePreAuthenticatorInterface +{ +} diff --git a/Http/EntryPoint/AuthenticationEntryPointInterface.php b/Http/EntryPoint/AuthenticationEntryPointInterface.php index c8e43e5..9bade0c 100644 --- a/Http/EntryPoint/AuthenticationEntryPointInterface.php +++ b/Http/EntryPoint/AuthenticationEntryPointInterface.php @@ -16,8 +16,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; /** - * AuthenticationEntryPointInterface is the interface used to start the - * authentication scheme. + * Implement this interface for any classes that will be called to "start" + * the authentication process (see method for more details). * * @author Fabien Potencier <fabien@symfony.com> */ diff --git a/Http/EntryPoint/DigestAuthenticationEntryPoint.php b/Http/EntryPoint/DigestAuthenticationEntryPoint.php index 89f80ad..cdb98eb 100644 --- a/Http/EntryPoint/DigestAuthenticationEntryPoint.php +++ b/Http/EntryPoint/DigestAuthenticationEntryPoint.php @@ -24,15 +24,15 @@ use Psr\Log\LoggerInterface; */ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterface { - private $key; + private $secret; private $realmName; private $nonceValiditySeconds; private $logger; - public function __construct($realmName, $key, $nonceValiditySeconds = 300, LoggerInterface $logger = null) + public function __construct($realmName, $secret, $nonceValiditySeconds = 300, LoggerInterface $logger = null) { $this->realmName = $realmName; - $this->key = $key; + $this->secret = $secret; $this->nonceValiditySeconds = $nonceValiditySeconds; $this->logger = $logger; } @@ -43,7 +43,7 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac public function start(Request $request, AuthenticationException $authException = null) { $expiryTime = microtime(true) + $this->nonceValiditySeconds * 1000; - $signatureValue = md5($expiryTime.':'.$this->key); + $signatureValue = md5($expiryTime.':'.$this->secret); $nonceValue = $expiryTime.':'.$signatureValue; $nonceValueBase64 = base64_encode($nonceValue); @@ -65,11 +65,21 @@ class DigestAuthenticationEntryPoint implements AuthenticationEntryPointInterfac } /** - * @return string + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * @return string + */ + public function getSecret() + { + return $this->secret; } /** diff --git a/Http/Firewall/AnonymousAuthenticationListener.php b/Http/Firewall/AnonymousAuthenticationListener.php index f7feee8..0d60673 100644 --- a/Http/Firewall/AnonymousAuthenticationListener.php +++ b/Http/Firewall/AnonymousAuthenticationListener.php @@ -27,14 +27,14 @@ use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; class AnonymousAuthenticationListener implements ListenerInterface { private $tokenStorage; - private $key; + private $secret; private $authenticationManager; private $logger; - public function __construct(TokenStorageInterface $tokenStorage, $key, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) + public function __construct(TokenStorageInterface $tokenStorage, $secret, LoggerInterface $logger = null, AuthenticationManagerInterface $authenticationManager = null) { $this->tokenStorage = $tokenStorage; - $this->key = $key; + $this->secret = $secret; $this->authenticationManager = $authenticationManager; $this->logger = $logger; } @@ -51,7 +51,7 @@ class AnonymousAuthenticationListener implements ListenerInterface } try { - $token = new AnonymousToken($this->key, 'anon.', array()); + $token = new AnonymousToken($this->secret, 'anon.', array()); if (null !== $this->authenticationManager) { $token = $this->authenticationManager->authenticate($token); } diff --git a/Http/Firewall/DigestAuthenticationListener.php b/Http/Firewall/DigestAuthenticationListener.php index 702cf33..71bdf6c 100644 --- a/Http/Firewall/DigestAuthenticationListener.php +++ b/Http/Firewall/DigestAuthenticationListener.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Core\Util\StringUtils; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -79,7 +78,7 @@ class DigestAuthenticationListener implements ListenerInterface } try { - $digestAuth->validateAndDecode($this->authenticationEntryPoint->getKey(), $this->authenticationEntryPoint->getRealmName()); + $digestAuth->validateAndDecode($this->authenticationEntryPoint->getSecret(), $this->authenticationEntryPoint->getRealmName()); } catch (BadCredentialsException $e) { $this->fail($event, $request, $e); @@ -100,7 +99,7 @@ class DigestAuthenticationListener implements ListenerInterface return; } - if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) { + if (!hash_equals($serverDigestMd5, $digestAuth->getResponse())) { if (null !== $this->logger) { $this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse())); } diff --git a/Http/Firewall/LogoutListener.php b/Http/Firewall/LogoutListener.php index 96f5685..e19d39c 100644 --- a/Http/Firewall/LogoutListener.php +++ b/Http/Firewall/LogoutListener.php @@ -24,6 +24,7 @@ use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface; use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * LogoutListener logout users. @@ -56,11 +57,21 @@ class LogoutListener implements ListenerInterface throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + $this->tokenStorage = $tokenStorage; $this->httpUtils = $httpUtils; $this->options = array_merge(array( 'csrf_parameter' => '_csrf_token', - 'intention' => 'logout', + 'csrf_token_id' => 'logout', 'logout_path' => '/logout', ), $options); $this->successHandler = $successHandler; @@ -98,9 +109,9 @@ class LogoutListener implements ListenerInterface } if (null !== $this->csrfTokenManager) { - $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new LogoutException('Invalid CSRF token.'); } } diff --git a/Http/Firewall/SimpleFormAuthenticationListener.php b/Http/Firewall/SimpleFormAuthenticationListener.php index 8123e0e..331d018 100644 --- a/Http/Firewall/SimpleFormAuthenticationListener.php +++ b/Http/Firewall/SimpleFormAuthenticationListener.php @@ -27,6 +27,7 @@ use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInt use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Http\HttpUtils; +use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Psr\Log\LoggerInterface; @@ -70,6 +71,16 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + $this->simpleAuthenticator = $simpleAuthenticator; $this->csrfTokenManager = $csrfTokenManager; @@ -77,7 +88,7 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', 'post_only' => true, ), $options); @@ -102,19 +113,19 @@ class SimpleFormAuthenticationListener extends AbstractAuthenticationListener protected function attemptAuthentication(Request $request) { if (null !== $this->csrfTokenManager) { - $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } } if ($this->options['post_only']) { - $username = trim($request->request->get($this->options['username_parameter'], null, true)); - $password = $request->request->get($this->options['password_parameter'], null, true); + $username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']); } else { - $username = trim($request->get($this->options['username_parameter'], null, true)); - $password = $request->get($this->options['password_parameter'], null, true); + $username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } if (strlen($username) > Security::MAX_USERNAME_LENGTH) { diff --git a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php index ba4329b..866d0c3 100644 --- a/Http/Firewall/UsernamePasswordFormAuthenticationListener.php +++ b/Http/Firewall/UsernamePasswordFormAuthenticationListener.php @@ -19,6 +19,7 @@ use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; +use Symfony\Component\Security\Http\ParameterBagUtils; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; @@ -48,11 +49,21 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL throw new InvalidArgumentException('The CSRF token manager should be an instance of CsrfProviderInterface or CsrfTokenManagerInterface.'); } + if (isset($options['intention'])) { + if (isset($options['csrf_token_id'])) { + throw new \InvalidArgumentException(sprintf('You should only define an option for one of "intention" or "csrf_token_id" for the "%s". Use the "csrf_token_id" as it replaces "intention".', __CLASS__)); + } + + @trigger_error('The "intention" option for the '.__CLASS__.' is deprecated since version 2.8 and will be removed in 3.0. Use the "csrf_token_id" option instead.', E_USER_DEPRECATED); + + $options['csrf_token_id'] = $options['intention']; + } + parent::__construct($tokenStorage, $authenticationManager, $sessionStrategy, $httpUtils, $providerKey, $successHandler, $failureHandler, array_merge(array( 'username_parameter' => '_username', 'password_parameter' => '_password', 'csrf_parameter' => '_csrf_token', - 'intention' => 'authenticate', + 'csrf_token_id' => 'authenticate', 'post_only' => true, ), $options), $logger, $dispatcher); @@ -77,19 +88,19 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL protected function attemptAuthentication(Request $request) { if (null !== $this->csrfTokenManager) { - $csrfToken = $request->get($this->options['csrf_parameter'], null, true); + $csrfToken = ParameterBagUtils::getRequestParameterValue($request, $this->options['csrf_parameter']); - if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['intention'], $csrfToken))) { + if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken($this->options['csrf_token_id'], $csrfToken))) { throw new InvalidCsrfTokenException('Invalid CSRF token.'); } } if ($this->options['post_only']) { - $username = trim($request->request->get($this->options['username_parameter'], null, true)); - $password = $request->request->get($this->options['password_parameter'], null, true); + $username = trim(ParameterBagUtils::getParameterBagValue($request->request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getParameterBagValue($request->request, $this->options['password_parameter']); } else { - $username = trim($request->get($this->options['username_parameter'], null, true)); - $password = $request->get($this->options['password_parameter'], null, true); + $username = trim(ParameterBagUtils::getRequestParameterValue($request, $this->options['username_parameter'])); + $password = ParameterBagUtils::getRequestParameterValue($request, $this->options['password_parameter']); } if (strlen($username) > Security::MAX_USERNAME_LENGTH) { diff --git a/Http/Logout/LogoutUrlGenerator.php b/Http/Logout/LogoutUrlGenerator.php index 4ad63cc..761e56a 100644 --- a/Http/Logout/LogoutUrlGenerator.php +++ b/Http/Logout/LogoutUrlGenerator.php @@ -86,7 +86,7 @@ class LogoutUrlGenerator * Generates the logout URL for the firewall. * * @param string|null $key The firewall key or null to use the current firewall key - * @param bool|string $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) * * @return string The logout URL * diff --git a/Http/ParameterBagUtils.php b/Http/ParameterBagUtils.php new file mode 100644 index 0000000..eed5421 --- /dev/null +++ b/Http/ParameterBagUtils.php @@ -0,0 +1,96 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Security\Http; + +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\PropertyAccess\Exception\AccessException; +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\PropertyAccess; + +/** + * @internal + */ +final class ParameterBagUtils +{ + private static $propertyAccessor; + + /** + * Returns a "parameter" value. + * + * Paths like foo[bar] will be evaluated to find deeper items in nested data structures. + * + * @param ParameterBag $parameters The parameter bag + * @param string $path The key + * + * @return mixed + * + * @throws InvalidArgumentException when the given path is malformed + */ + public static function getParameterBagValue(ParameterBag $parameters, $path) + { + if (false === $pos = strpos($path, '[')) { + return $parameters->get($path); + } + + $root = substr($path, 0, $pos); + + if (null === $value = $parameters->get($root)) { + return; + } + + if (null === self::$propertyAccessor) { + self::$propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + try { + return self::$propertyAccessor->getValue($value, substr($path, $pos)); + } catch (AccessException $e) { + return; + } + } + + /** + * Returns a request "parameter" value. + * + * Paths like foo[bar] will be evaluated to find deeper items in nested data structures. + * + * @param Request $request The request + * @param string $path The key + * + * @return mixed + * + * @throws InvalidArgumentException when the given path is malformed + */ + public static function getRequestParameterValue(Request $request, $path) + { + if (false === $pos = strpos($path, '[')) { + return $request->get($path); + } + + $root = substr($path, 0, $pos); + + if (null === $value = $request->get($root)) { + return; + } + + if (null === self::$propertyAccessor) { + self::$propertyAccessor = PropertyAccess::createPropertyAccessor(); + } + + try { + return self::$propertyAccessor->getValue($value, substr($path, $pos)); + } catch (AccessException $e) { + return; + } + } +} diff --git a/Http/RememberMe/AbstractRememberMeServices.php b/Http/RememberMe/AbstractRememberMeServices.php index cd8640d..8627bc8 100644 --- a/Http/RememberMe/AbstractRememberMeServices.php +++ b/Http/RememberMe/AbstractRememberMeServices.php @@ -23,6 +23,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Cookie; use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Http\ParameterBagUtils; /** * Base class implementing the RememberMeServicesInterface. @@ -39,24 +40,24 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface 'httponly' => true, ); private $providerKey; - private $key; + private $secret; private $userProviders; /** * Constructor. * * @param array $userProviders - * @param string $key + * @param string $secret * @param string $providerKey * @param array $options * @param LoggerInterface $logger * * @throws \InvalidArgumentException */ - public function __construct(array $userProviders, $key, $providerKey, array $options = array(), LoggerInterface $logger = null) + public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null) { - if (empty($key)) { - throw new \InvalidArgumentException('$key must not be empty.'); + if (empty($secret)) { + throw new \InvalidArgumentException('$secret must not be empty.'); } if (empty($providerKey)) { throw new \InvalidArgumentException('$providerKey must not be empty.'); @@ -66,7 +67,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface } $this->userProviders = $userProviders; - $this->key = $key; + $this->secret = $secret; $this->providerKey = $providerKey; $this->options = array_merge($this->options, $options); $this->logger = $logger; @@ -84,11 +85,21 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface } /** - * @return string + * @deprecated Since version 2.8, to be removed in 3.0. Use getSecret() instead. */ public function getKey() { - return $this->key; + @trigger_error(__method__.'() is deprecated since version 2.8 and will be removed in 3.0. Use getSecret() instead.', E_USER_DEPRECATED); + + return $this->getSecret(); + } + + /** + * @return string + */ + public function getSecret() + { + return $this->secret; } /** @@ -125,7 +136,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface $this->logger->info('Remember-me cookie accepted.'); } - return new RememberMeToken($user, $this->providerKey, $this->key); + return new RememberMeToken($user, $this->providerKey, $this->secret); } catch (CookieTheftException $e) { $this->cancelCookie($request); @@ -312,7 +323,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface return true; } - $parameter = $request->get($this->options['remember_me_parameter'], null, true); + $parameter = ParameterBagUtils::getRequestParameterValue($request, $this->options['remember_me_parameter']); if (null === $parameter && null !== $this->logger) { $this->logger->debug('Did not send remember-me cookie.', array('parameter' => $this->options['remember_me_parameter'])); diff --git a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index cbbbb23..807a4a7 100644 --- a/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -21,7 +21,6 @@ use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Util\SecureRandomInterface; use Psr\Log\LoggerInterface; -use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -33,23 +32,26 @@ use Symfony\Component\Security\Core\Util\StringUtils; class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices { private $tokenProvider; - private $secureRandom; /** * Constructor. * + * Note: The $secureRandom parameter is deprecated since version 2.8 and will be removed in 3.0. + * * @param array $userProviders - * @param string $key + * @param string $secret * @param string $providerKey * @param array $options * @param LoggerInterface $logger * @param SecureRandomInterface $secureRandom */ - public function __construct(array $userProviders, $key, $providerKey, array $options, LoggerInterface $logger = null, SecureRandomInterface $secureRandom) + public function __construct(array $userProviders, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null, SecureRandomInterface $secureRandom = null) { - parent::__construct($userProviders, $key, $providerKey, $options, $logger); + if (null !== $secureRandom) { + @trigger_error('The $secureRandom parameter in '.__METHOD__.' is deprecated since version 2.8 and will be removed in 3.0.', E_USER_DEPRECATED); + } - $this->secureRandom = $secureRandom; + parent::__construct($userProviders, $secret, $providerKey, $options, $logger); } /** @@ -91,7 +93,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices list($series, $tokenValue) = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); - if (!StringUtils::equals($persistentToken->getTokenValue(), $tokenValue)) { + if (!hash_equals($persistentToken->getTokenValue(), $tokenValue)) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } @@ -99,7 +101,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices throw new AuthenticationException('The cookie has expired.'); } - $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); + $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->updateToken($series, $tokenValue, new \DateTime()); $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie( @@ -121,8 +123,8 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices */ protected function onLoginSuccess(Request $request, Response $response, TokenInterface $token) { - $series = base64_encode($this->secureRandom->nextBytes(64)); - $tokenValue = base64_encode($this->secureRandom->nextBytes(64)); + $series = base64_encode(random_bytes(64)); + $tokenValue = base64_encode(random_bytes(64)); $this->tokenProvider->createNewToken( new PersistentToken( diff --git a/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php index d68ada5..a443702 100644 --- a/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/Http/RememberMe/TokenBasedRememberMeServices.php @@ -17,7 +17,6 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface providing @@ -54,7 +53,7 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user))); } - if (true !== StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { + if (true !== hash_equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { throw new AuthenticationException('The cookie\'s hash is invalid.'); } @@ -121,6 +120,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices */ protected function generateCookieHash($class, $username, $expires, $password) { - return hash_hmac('sha256', $class.$username.$expires.$password, $this->getKey()); + return hash_hmac('sha256', $class.$username.$expires.$password, $this->getSecret()); } } diff --git a/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php b/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php index 82b5533..8f854c8 100644 --- a/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php +++ b/Http/Tests/Authentication/DefaultAuthenticationFailureHandlerTest.php @@ -17,17 +17,12 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCase { - private $httpKernel = null; - - private $httpUtils = null; - - private $logger = null; - - private $request = null; - - private $session = null; - - private $exception = null; + private $httpKernel; + private $httpUtils; + private $logger; + private $request; + private $session; + private $exception; protected function setUp() { @@ -145,7 +140,7 @@ class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCas public function testFailurePathCanBeOverwrittenWithRequest() { $this->request->expects($this->once()) - ->method('get')->with('_failure_path', null, true) + ->method('get')->with('_failure_path') ->will($this->returnValue('/auth/login')); $this->httpUtils->expects($this->once()) @@ -155,12 +150,25 @@ class DefaultAuthenticationFailureHandlerTest extends \PHPUnit_Framework_TestCas $handler->onAuthenticationFailure($this->request, $this->exception); } + public function testFailurePathCanBeOverwrittenWithNestedAttributeInRequest() + { + $this->request->expects($this->once()) + ->method('get')->with('_failure_path') + ->will($this->returnValue(array('value' => '/auth/login'))); + + $this->httpUtils->expects($this->once()) + ->method('createRedirectResponse')->with($this->request, '/auth/login'); + + $handler = new DefaultAuthenticationFailureHandler($this->httpKernel, $this->httpUtils, array('failure_path_parameter' => '_failure_path[value]'), $this->logger); + $handler->onAuthenticationFailure($this->request, $this->exception); + } + public function testFailurePathParameterCanBeOverwritten() { $options = array('failure_path_parameter' => '_my_failure_path'); $this->request->expects($this->once()) - ->method('get')->with('_my_failure_path', null, true) + ->method('get')->with('_my_failure_path') ->will($this->returnValue('/auth/login')); $this->httpUtils->expects($this->once()) diff --git a/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php b/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php index 4d1847d..2c22da6 100644 --- a/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php +++ b/Http/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php @@ -68,6 +68,20 @@ class DefaultAuthenticationSuccessHandlerTest extends \PHPUnit_Framework_TestCas $this->assertSame($response, $result); } + public function testTargetPathIsPassedAsNestedParameterWithRequest() + { + $this->request->expects($this->once()) + ->method('get')->with('_target_path') + ->will($this->returnValue(array('value' => '/dashboard'))); + + $response = $this->expectRedirectResponse('/dashboard'); + + $handler = new DefaultAuthenticationSuccessHandler($this->httpUtils, array('target_path_parameter' => '_target_path[value]')); + $result = $handler->onAuthenticationSuccess($this->request, $this->token); + + $this->assertSame($response, $result); + } + public function testTargetPathParameterIsCustomised() { $options = array('target_path_parameter' => '_my_target_path'); diff --git a/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php b/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php index 181e340..4082986 100644 --- a/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php +++ b/Http/Tests/EntryPoint/DigestAuthenticationEntryPointTest.php @@ -23,7 +23,7 @@ class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase $authenticationException = new AuthenticationException('TheAuthenticationExceptionMessage'); - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); $response = $entryPoint->start($request, $authenticationException); $this->assertEquals(401, $response->getStatusCode()); @@ -34,7 +34,7 @@ class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase { $request = $this->getMock('Symfony\Component\HttpFoundation\Request'); - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); $response = $entryPoint->start($request); $this->assertEquals(401, $response->getStatusCode()); @@ -47,7 +47,7 @@ class DigestAuthenticationEntryPointTest extends \PHPUnit_Framework_TestCase $nonceExpiredException = new NonceExpiredException('TheNonceExpiredExceptionMessage'); - $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheKey'); + $entryPoint = new DigestAuthenticationEntryPoint('TheRealmName', 'TheSecret'); $response = $entryPoint->start($request, $nonceExpiredException); $this->assertEquals(401, $response->getStatusCode()); diff --git a/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php b/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php index 3450c1e..d99b562 100644 --- a/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php +++ b/Http/Tests/Firewall/AnonymousAuthenticationListenerTest.php @@ -35,7 +35,7 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->method('authenticate') ; - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -48,14 +48,14 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue(null)) ; - $anonymousToken = new AnonymousToken('TheKey', 'anon.', array()); + $anonymousToken = new AnonymousToken('TheSecret', 'anon.', array()); $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); $authenticationManager ->expects($this->once()) ->method('authenticate') ->with($this->callback(function ($token) { - return 'TheKey' === $token->getKey(); + return 'TheSecret' === $token->getSecret(); })) ->will($this->returnValue($anonymousToken)) ; @@ -66,7 +66,7 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->with($anonymousToken) ; - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', null, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', null, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } @@ -81,7 +81,7 @@ class AnonymousAuthenticationListenerTest extends \PHPUnit_Framework_TestCase $authenticationManager = $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface'); - $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheKey', $logger, $authenticationManager); + $listener = new AnonymousAuthenticationListener($tokenStorage, 'TheSecret', $logger, $authenticationManager); $listener->handle($this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false)); } } diff --git a/Http/Tests/Firewall/DigestAuthenticationListenerTest.php b/Http/Tests/Firewall/DigestAuthenticationListenerTest.php new file mode 100644 index 0000000..80b2dc4 --- /dev/null +++ b/Http/Tests/Firewall/DigestAuthenticationListenerTest.php @@ -0,0 +1,79 @@ +<?php + +namespace Symfony\Component\Security\Http\Tests\Firewall; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; +use Symfony\Component\Security\Http\Firewall\DigestAuthenticationListener; + +class DigestAuthenticationListenerTest extends \PHPUnit_Framework_TestCase +{ + public function testHandleWithValidDigest() + { + $time = microtime(true) + 1000; + $secret = 'ThisIsASecret'; + $nonce = base64_encode($time.':'.md5($time.':'.$secret)); + $username = 'user'; + $password = 'password'; + $realm = 'Welcome, robot!'; + $cnonce = 'MDIwODkz'; + $nc = '00000001'; + $qop = 'auth'; + $uri = '/path/info?p1=5&p2=5'; + + $serverDigest = $this->calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, 'GET', $uri); + + $digestData = + 'username="'.$username.'", realm="'.$realm.'", nonce="'.$nonce.'", '. + 'uri="'.$uri.'", cnonce="'.$cnonce.'", nc='.$nc.', qop="'.$qop.'", '. + 'response="'.$serverDigest.'"' + ; + + $request = new Request(array(), array(), array(), array(), array(), array('PHP_AUTH_DIGEST' => $digestData)); + + $entryPoint = new DigestAuthenticationEntryPoint($realm, $secret); + + $user = $this->getMock('Symfony\Component\Security\Core\User\UserInterface'); + $user->method('getPassword')->willReturn($password); + + $providerKey = 'TheProviderKey'; + + $tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface'); + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + $tokenStorage + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo(new UsernamePasswordToken($user, $password, $providerKey))) + ; + + $userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface'); + $userProvider->method('loadUserByUsername')->willReturn($user); + + $listener = new DigestAuthenticationListener($tokenStorage, $userProvider, $providerKey, $entryPoint); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false); + $event + ->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + + private function calculateServerDigest($username, $realm, $password, $nc, $nonce, $cnonce, $qop, $method, $uri) + { + $response = md5( + md5($username.':'.$realm.':'.$password).':'.$nonce.':'.$nc.':'.$cnonce.':'.$qop.':'.md5($method.':'.$uri) + ); + + return sprintf('username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop="%s", response="%s"', + $username, $realm, $nonce, $uri, $cnonce, $nc, $qop, $response + ); + } +} diff --git a/Http/Tests/Firewall/LogoutListenerTest.php b/Http/Tests/Firewall/LogoutListenerTest.php index 15c996e..367c810 100644 --- a/Http/Tests/Firewall/LogoutListenerTest.php +++ b/Http/Tests/Firewall/LogoutListenerTest.php @@ -213,7 +213,7 @@ class LogoutListenerTest extends \PHPUnit_Framework_TestCase $successHandler ?: $this->getSuccessHandler(), $options = array( 'csrf_parameter' => '_csrf_token', - 'intention' => 'logout', + 'csrf_token_id' => 'logout', 'logout_path' => '/logout', 'target_url' => '/', ), diff --git a/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php b/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php index 0a1286c..adf91b1 100644 --- a/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php +++ b/Http/Tests/Firewall/SimplePreAuthenticationListenerTest.php @@ -42,7 +42,7 @@ class SimplePreAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->will($this->returnValue($this->token)) ; - $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface'); + $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface'); $simpleAuthenticator ->expects($this->once()) ->method('createToken') @@ -79,7 +79,7 @@ class SimplePreAuthenticationListenerTest extends \PHPUnit_Framework_TestCase ->with($this->equalTo(null)) ; - $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface'); + $simpleAuthenticator = $this->getMock('Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface'); $simpleAuthenticator ->expects($this->once()) ->method('createToken') diff --git a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php index ddfaaeb..7495398 100644 --- a/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php +++ b/Http/Tests/RememberMe/AbstractRememberMeServicesTest.php @@ -25,10 +25,10 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', $service->getRememberMeParameter()); } - public function testGetKey() + public function testGetSecret() { $service = $this->getService(); - $this->assertEquals('fookey', $service->getKey()); + $this->assertEquals('foosecret', $service->getSecret()); } public function testAutoLoginReturnsNullWhenNoCookie() @@ -78,7 +78,7 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase $returnedToken = $service->autoLogin($request); $this->assertSame($user, $returnedToken->getUser()); - $this->assertSame('fookey', $returnedToken->getKey()); + $this->assertSame('foosecret', $returnedToken->getSecret()); $this->assertSame('fookey', $returnedToken->getProviderKey()); } @@ -284,7 +284,7 @@ class AbstractRememberMeServicesTest extends \PHPUnit_Framework_TestCase } return $this->getMockForAbstractClass('Symfony\Component\Security\Http\RememberMe\AbstractRememberMeServices', array( - array($userProvider), 'fookey', 'fookey', $options, $logger, + array($userProvider), 'foosecret', 'fookey', $options, $logger, )); } diff --git a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php index f43963e..30cf4a2 100644 --- a/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php +++ b/Http/Tests/RememberMe/PersistentTokenBasedRememberMeServicesTest.php @@ -20,7 +20,6 @@ use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices; use Symfony\Component\Security\Core\Exception\TokenNotFoundException; use Symfony\Component\Security\Core\Exception\CookieTheftException; -use Symfony\Component\Security\Core\Util\SecureRandom; class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase { @@ -183,7 +182,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); $this->assertSame($user, $returnedToken->getUser()); - $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertEquals('foosecret', $returnedToken->getSecret()); $this->assertTrue($request->attributes->has(RememberMeServicesInterface::COOKIE_ATTR_NAME)); } @@ -322,7 +321,7 @@ class PersistentTokenBasedRememberMeServicesTest extends \PHPUnit_Framework_Test $userProvider = $this->getProvider(); } - return new PersistentTokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger, new SecureRandom(sys_get_temp_dir().'/_sf2.seed')); + return new PersistentTokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger); } protected function getProvider() diff --git a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php index e3b58e9..ee8a99e 100644 --- a/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php +++ b/Http/Tests/RememberMe/TokenBasedRememberMeServicesTest.php @@ -140,7 +140,7 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\RememberMeToken', $returnedToken); $this->assertSame($user, $returnedToken->getUser()); - $this->assertEquals('fookey', $returnedToken->getKey()); + $this->assertEquals('foosecret', $returnedToken->getSecret()); } public function provideUsernamesForAutoLogin() @@ -265,7 +265,7 @@ class TokenBasedRememberMeServicesTest extends \PHPUnit_Framework_TestCase $userProvider = $this->getProvider(); } - $service = new TokenBasedRememberMeServices(array($userProvider), 'fookey', 'fookey', $options, $logger); + $service = new TokenBasedRememberMeServices(array($userProvider), 'foosecret', 'fookey', $options, $logger); return $service; } diff --git a/Http/composer.json b/Http/composer.json index 1b36428..24708ac 100644 --- a/Http/composer.json +++ b/Http/composer.json @@ -17,14 +17,17 @@ ], "require": { "php": ">=5.3.9", - "symfony/security-core": "~2.6", - "symfony/event-dispatcher": "~2.1", - "symfony/http-foundation": "~2.4", - "symfony/http-kernel": "~2.4" + "symfony/security-core": "~2.8", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/http-foundation": "~2.4|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/property-access": "~2.3|~3.0.0" }, "require-dev": { - "symfony/routing": "~2.2", - "symfony/security-csrf": "~2.4", + "symfony/routing": "~2.2|~3.0.0", + "symfony/security-csrf": "~2.4|~3.0.0", "psr/log": "~1.0" }, "suggest": { @@ -40,7 +43,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } diff --git a/composer.json b/composer.json index b64e1b8..3eab48b 100644 --- a/composer.json +++ b/composer.json @@ -17,37 +17,37 @@ ], "require": { "php": ">=5.3.9", - "paragonie/random_compat": "~1.0", - "symfony/event-dispatcher": "~2.2", - "symfony/http-foundation": "~2.1", - "symfony/http-kernel": "~2.4" + "symfony/security-acl": "~2.7|~3.0.0", + "symfony/event-dispatcher": "~2.2|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0", + "symfony/http-kernel": "~2.4|~3.0.0", + "symfony/polyfill-php55": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0", + "symfony/property-access": "~2.3|~3.0.0" }, "replace": { - "symfony/security-acl": "self.version", "symfony/security-core": "self.version", "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", "symfony/security-http": "self.version" }, "require-dev": { - "symfony/finder": "~2.3", - "symfony/intl": "~2.3", - "symfony/routing": "~2.2", - "symfony/validator": "~2.5,>=2.5.9", - "doctrine/common": "~2.2", - "doctrine/dbal": "~2.2", + "symfony/finder": "~2.3|~3.0.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/routing": "~2.2|~3.0.0", + "symfony/validator": "~2.5,>=2.5.9|~3.0.0", "psr/log": "~1.0", - "ircmaxell/password-compat": "~1.0", - "symfony/expression-language": "~2.6" + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/ldap": "~2.8|~3.0.0" }, "suggest": { - "symfony/class-loader": "For using the ACL generateSql script", - "symfony/finder": "For using the ACL generateSql script", "symfony/form": "", "symfony/validator": "For using the user password constraint", "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs", - "doctrine/dbal": "For using the built-in ACL implementation", "symfony/expression-language": "For using the expression voter", - "ircmaxell/password-compat": "For using the BCrypt password encoder in PHP <5.5" + "symfony/ldap": "For using the LDAP user and authentication providers" }, "autoload": { "psr-4": { "Symfony\\Component\\Security\\": "" }, @@ -58,7 +58,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } } } |