This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,667 @@
<?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);
}
}
}

View File

@@ -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\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);
}
}

View File

@@ -0,0 +1,51 @@
<?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);
}

View File

@@ -0,0 +1,229 @@
<?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;
}
}

View File

@@ -0,0 +1,208 @@
<?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);
}
}

View File

@@ -0,0 +1,74 @@
<?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);
}
}

View File

@@ -0,0 +1,114 @@
<?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\DomainObjectInterface;
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
use Symfony\Component\Security\Acl\Util\ClassUtils;
/**
* 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
*
* @throws InvalidDomainObjectException
*
* @return ObjectIdentity
*/
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);
}
}

View File

@@ -0,0 +1,35 @@
<?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;
}
}
}

View File

@@ -0,0 +1,211 @@
<?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));
}
}

View File

@@ -0,0 +1,73 @@
<?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);
}
}

View File

@@ -0,0 +1,98 @@
<?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\AuthenticationTrustResolverInterface;
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\Role\Role;
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 AuthenticationTrustResolverInterface $authenticationTrustResolver
*/
public function __construct(RoleHierarchyInterface $roleHierarchy, AuthenticationTrustResolverInterface $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
if (method_exists($this->roleHierarchy, 'getReachableRoleNames')) {
foreach ($this->roleHierarchy->getReachableRoleNames($this->getRoleNames($token)) as $role) {
$sids[] = new RoleSecurityIdentity($role);
}
} else {
// Symfony < 4.3 BC layer
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;
}
private function getRoleNames(TokenInterface $token)
{
if (method_exists($token, 'getRoleNames')) {
return $token->getRoleNames();
}
// Symfony < 4.3 BC layer
return array_map(function (Role $role) {
return $role->getRole();
}, $token->getRoles());
}
}

View File

@@ -0,0 +1,124 @@
<?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\Acl\Util\ClassUtils;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* 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);
}
}