diff -r 000000000000 -r 7f95f8617b0b vendor/symfony/src/Symfony/Component/Security/Acl/Domain/Acl.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/symfony/src/Symfony/Component/Security/Acl/Domain/Acl.php Sat Sep 24 15:40:41 2011 +0200 @@ -0,0 +1,679 @@ + + * + * 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\MutableAclInterface; +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 + */ +class Acl implements AuditableAclInterface, NotifyPropertyChanged +{ + private $parentAcl; + private $permissionGrantingStrategy; + private $objectIdentity; + private $classAces; + private $classFieldAces; + private $objectAces; + private $objectFieldAces; + private $id; + private $loadedSids; + private $entriesInheriting; + private $listeners; + + /** + * Constructor + * + * @param integer $id + * @param ObjectIdentityInterface $objectIdentity + * @param PermissionGrantingStrategyInterface $permissionGrantingStrategy + * @param array $loadedSids + * @param Boolean $entriesInheriting + * @return void + */ + public function __construct($id, ObjectIdentityInterface $objectIdentity, PermissionGrantingStrategyInterface $permissionGrantingStrategy, array $loadedSids = array(), $entriesInheriting) + { + $this->id = $id; + $this->objectIdentity = $objectIdentity; + $this->permissionGrantingStrategy = $permissionGrantingStrategy; + $this->loadedSids = $loadedSids; + $this->entriesInheriting = $entriesInheriting; + $this->parentAcl = null; + $this->classAces = array(); + $this->classFieldAces = array(); + $this->objectAces = array(); + $this->objectFieldAces = array(); + $this->listeners = array(); + } + + /** + * Adds a property changed listener + * + * @param PropertyChangedListener $listener + * @return void + */ + 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 + * @return void + */ + 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) + { + if (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 integer $index + * @throws \OutOfBoundsException + * @return void + */ + 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 integer $index + * @param string $field + * @throws \OutOfBoundsException + * @return void + */ + 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 integer $index + * @param integer $mask + * @param SecurityIdentityInterface $sid + * @param Boolean $granting + * @param string $strategy + * @throws \OutOfBoundsException + * @throws \InvalidArgumentException + * @return void + */ + 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 integer $index + * @param string $field + * @param integer $mask + * @param SecurityIdentityInterface $sid + * @param Boolean $granting + * @param string $strategy + * @throws \InvalidArgumentException + * @throws \OutOfBoundsException + * @return void + */ + 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 integer $index + * @param integer $mask + * @param string $strategy + * @throws \OutOfBoundsException + * @return void + */ + 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 integer $index + * @param Boolean $auditSuccess + * @param Boolean $auditFailure + * @throws \OutOfBoundsException + * @return void + */ + 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 integer $index + * @param string $field + * @param integer $mask + * @param string $strategy + * @throws \InvalidArgumentException + * @throws \OutOfBoundsException + * @return void + */ + 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 + * @return void + */ + 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 + * @return void + */ + private function onEntryPropertyChanged(EntryInterface $entry, $name, $oldValue, $newValue) + { + foreach ($this->listeners as $listener) { + $listener->propertyChanged($entry, $name, $oldValue, $newValue); + } + } +}