├── .github
├── psalm
│ ├── .gitignore
│ └── psalm.baseline.xml
└── workflows
│ ├── ci.yml
│ └── psalm.yml
├── .gitignore
├── .php-cs-fixer.dist.php
├── CHANGELOG.md
├── Dbal
├── AclProvider.php
├── MutableAclProvider.php
└── Schema.php
├── Domain
├── Acl.php
├── AclCacheTrait.php
├── AclCollectionCache.php
├── AuditLogger.php
├── DoctrineAclCache.php
├── Entry.php
├── FieldEntry.php
├── ObjectIdentity.php
├── ObjectIdentityRetrievalStrategy.php
├── PermissionGrantingStrategy.php
├── PsrAclCache.php
├── RoleSecurityIdentity.php
├── SecurityIdentityRetrievalStrategy.php
└── UserSecurityIdentity.php
├── Exception
├── AclAlreadyExistsException.php
├── AclNotFoundException.php
├── ConcurrentModificationException.php
├── Exception.php
├── InvalidDomainObjectException.php
├── NoAceFoundException.php
├── NotAllAclsFoundException.php
└── SidNotLoadedException.php
├── LICENSE
├── Model
├── AclCacheInterface.php
├── AclInterface.php
├── AclProviderInterface.php
├── AuditLoggerInterface.php
├── AuditableAclInterface.php
├── AuditableEntryInterface.php
├── DomainObjectInterface.php
├── EntryInterface.php
├── FieldEntryInterface.php
├── MutableAclInterface.php
├── MutableAclProviderInterface.php
├── ObjectIdentityInterface.php
├── ObjectIdentityRetrievalStrategyInterface.php
├── PermissionGrantingStrategyInterface.php
├── SecurityIdentityInterface.php
└── SecurityIdentityRetrievalStrategyInterface.php
├── Permission
├── AbstractMaskBuilder.php
├── BasicPermissionMap.php
├── MaskBuilder.php
├── MaskBuilderInterface.php
├── MaskBuilderRetrievalInterface.php
└── PermissionMapInterface.php
├── README.md
├── Resources
├── bin
│ └── generateSql.php
└── schema
│ ├── db2.sql
│ ├── drizzle.sql
│ ├── mssql.sql
│ ├── mysql.sql
│ ├── oracle.sql
│ ├── postgresql.sql
│ ├── sqlanywhere.sql
│ └── sqlite.sql
├── Tests
├── Dbal
│ ├── AclProviderBenchmarkTest.php
│ ├── AclProviderTest.php
│ └── MutableAclProviderTest.php
├── Domain
│ ├── AclTest.php
│ ├── AuditLoggerTest.php
│ ├── DoctrineAclCacheTest.php
│ ├── EntryTest.php
│ ├── FieldEntryTest.php
│ ├── ObjectIdentityRetrievalStrategyTest.php
│ ├── ObjectIdentityTest.php
│ ├── PermissionGrantingStrategyTest.php
│ ├── PsrAclCacheTest.php
│ ├── RoleSecurityIdentityTest.php
│ ├── SecurityIdentityRetrievalStrategyTest.php
│ └── UserSecurityIdentityTest.php
├── Fixtures
│ ├── Account.php
│ ├── SerializableAclInterface.php
│ └── SerializableAuditableEntryInterface.php
├── Permission
│ ├── BasicPermissionMapTest.php
│ └── MaskBuilderTest.php
└── Voter
│ └── AclVoterTest.php
├── Util
└── ClassUtils.php
├── Voter
├── AclVoter.php
└── FieldVote.php
├── composer.json
├── phpunit.xml.dist
└── psalm.xml
/.github/psalm/.gitignore:
--------------------------------------------------------------------------------
1 | /cache
2 |
--------------------------------------------------------------------------------
/.github/psalm/psalm.baseline.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches: [main]
7 |
8 | jobs:
9 | test:
10 | name: 'Test ${{ matrix.deps }} on PHP ${{ matrix.php }}'
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | php: ['7.2.5', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
17 | include:
18 | - php: '7.4'
19 | deps: lowest
20 | deprecations: max[self]=0
21 | - php: '8.4'
22 | deps: highest
23 |
24 | steps:
25 | - name: Checkout code
26 | uses: actions/checkout@v2
27 |
28 | - name: Setup PHP
29 | uses: shivammathur/setup-php@v2
30 | with:
31 | php-version: '${{ matrix.php }}'
32 | coverage: none
33 |
34 | - name: Configure composer
35 | if: "${{ matrix.deps == 'highest' }}"
36 | run: composer config minimum-stability dev
37 |
38 | - name: Composer install
39 | uses: ramsey/composer-install@v1
40 | with:
41 | dependency-versions: '${{ matrix.deps }}'
42 |
43 | - name: Install PHPUnit
44 | run: vendor/bin/simple-phpunit install
45 |
46 | - name: Run tests
47 | run: vendor/bin/simple-phpunit
48 | env:
49 | SYMFONY_DEPRECATIONS_HELPER: '${{ matrix.deprecations }}'
50 |
51 | cs:
52 | name: 'Code Style'
53 | runs-on: ubuntu-latest
54 |
55 | steps:
56 | - name: Checkout code
57 | uses: actions/checkout@v2
58 | - name: PHP-CS-Fixer
59 | uses: docker://oskarstark/php-cs-fixer-ga:3.11.0
60 | with:
61 | args: --diff --dry-run
62 |
--------------------------------------------------------------------------------
/.github/workflows/psalm.yml:
--------------------------------------------------------------------------------
1 | name: Static analysis
2 |
3 | on:
4 | pull_request: ~
5 |
6 | defaults:
7 | run:
8 | shell: bash
9 |
10 | jobs:
11 | psalm:
12 | name: Psalm
13 | runs-on: Ubuntu-22.04
14 |
15 | steps:
16 | - name: Setup PHP
17 | uses: shivammathur/setup-php@v2
18 | with:
19 | php-version: '8.0'
20 | ini-values: "memory_limit=-1"
21 | coverage: none
22 |
23 | - name: Checkout target branch
24 | uses: actions/checkout@v2
25 | with:
26 | ref: ${{ github.base_ref }}
27 |
28 | - name: Checkout PR
29 | uses: actions/checkout@v2
30 |
31 | - name: Install dependencies
32 | run: |
33 | composer remove --dev --no-update --no-interaction symfony/phpunit-bridge
34 | composer require --no-update vimeo/psalm phpunit/phpunit:^9.5 psalm/plugin-phpunit
35 | composer update --no-progress --ansi
36 | git checkout -- composer.json
37 |
38 | ./vendor/bin/psalm --version
39 |
40 | - name: Generate Psalm baseline
41 | run: |
42 | git checkout -m ${{ github.base_ref }}
43 | ./vendor/bin/psalm --set-baseline=.github/psalm/psalm.baseline.xml --no-progress
44 | git checkout -m FETCH_HEAD
45 |
46 | - name: Psalm
47 | run: |
48 | ./vendor/bin/psalm --output-format=github --no-progress
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 | phpunit.xml
4 | .phpunit.result.cache
5 | .php-cs-fixer.cache
6 | .php-cs-fixer.php
7 |
--------------------------------------------------------------------------------
/.php-cs-fixer.dist.php:
--------------------------------------------------------------------------------
1 | setRules([
5 | '@PHP71Migration' => true,
6 | '@PHPUnit75Migration:risky' => true,
7 | '@Symfony' => true,
8 | '@Symfony:risky' => true,
9 | 'protected_to_private' => false,
10 | 'phpdoc_to_comment' => ['ignored_tags' => ['psalm-suppress']],
11 | ])
12 | ->setRiskyAllowed(true)
13 | ->setFinder(
14 | (new PhpCsFixer\Finder())
15 | ->in(__DIR__)
16 | ->append([__FILE__])
17 | )
18 | ;
19 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 | =========
3 |
4 | 3.2.0
5 | -----
6 |
7 | * Change minimum PHP version to 7.2.5
8 | * Add PSR-6 support for ACL caching
9 | * Add support for `doctrine/cache` v2
10 | * Drop support for Symfony 3
11 | * Deprecate not implementing `__serialize()` and `__unserialize()` methods in
12 | `AclInterface` and `EntryInterface` implementations. The methods will be
13 | added to the interfaces in 4.0.
14 |
--------------------------------------------------------------------------------
/Dbal/Schema.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Dbal;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\Platforms\SQLServerPlatform;
16 | use Doctrine\DBAL\Schema\Schema as BaseSchema;
17 | use Doctrine\DBAL\Schema\SchemaConfig;
18 |
19 | /**
20 | * The schema used for the ACL system.
21 | *
22 | * @author Johannes M. Schmitt
23 | */
24 | final class Schema extends BaseSchema
25 | {
26 | protected $options;
27 | protected $platform;
28 |
29 | /**
30 | * @param array $options the names for tables
31 | */
32 | public function __construct(array $options, ?Connection $connection = null)
33 | {
34 | $schemaConfig = $this->createSchemaConfig($connection);
35 |
36 | parent::__construct([], [], $schemaConfig);
37 |
38 | $this->options = $options;
39 | $this->platform = $connection ? $connection->getDatabasePlatform() : null;
40 |
41 | $this->addClassTable();
42 | $this->addSecurityIdentitiesTable();
43 | $this->addObjectIdentitiesTable();
44 | $this->addObjectIdentityAncestorsTable();
45 | $this->addEntryTable();
46 | }
47 |
48 | /**
49 | * Merges ACL schema with the given schema.
50 | */
51 | public function addToSchema(BaseSchema $schema)
52 | {
53 | foreach ($this->getTables() as $table) {
54 | $schema->_addTable($table);
55 | }
56 |
57 | foreach ($this->getSequences() as $sequence) {
58 | $schema->_addSequence($sequence);
59 | }
60 | }
61 |
62 | /**
63 | * Adds the class table to the schema.
64 | */
65 | protected function addClassTable()
66 | {
67 | $table = $this->createTable($this->options['class_table_name']);
68 | $table->addColumn('id', 'integer', ['unsigned' => true, 'autoincrement' => true]);
69 | $table->addColumn('class_type', 'string', ['length' => 200]);
70 | $table->setPrimaryKey(['id']);
71 | $table->addUniqueIndex(['class_type']);
72 | }
73 |
74 | /**
75 | * Adds the entry table to the schema.
76 | */
77 | protected function addEntryTable()
78 | {
79 | $table = $this->createTable($this->options['entry_table_name']);
80 |
81 | $table->addColumn('id', 'integer', ['unsigned' => true, 'autoincrement' => true]);
82 | $table->addColumn('class_id', 'integer', ['unsigned' => true]);
83 | $table->addColumn('object_identity_id', 'integer', ['unsigned' => true, 'notnull' => false]);
84 | $table->addColumn('field_name', 'string', ['length' => 50, 'notnull' => false]);
85 | $table->addColumn('ace_order', 'smallint', ['unsigned' => true]);
86 | $table->addColumn('security_identity_id', 'integer', ['unsigned' => true]);
87 | $table->addColumn('mask', 'integer');
88 | $table->addColumn('granting', 'boolean');
89 | $table->addColumn('granting_strategy', 'string', ['length' => 30]);
90 | $table->addColumn('audit_success', 'boolean');
91 | $table->addColumn('audit_failure', 'boolean');
92 |
93 | $table->setPrimaryKey(['id']);
94 | $table->addUniqueIndex(['class_id', 'object_identity_id', 'field_name', 'ace_order']);
95 | $table->addIndex(['class_id', 'object_identity_id', 'security_identity_id']);
96 |
97 | $table->addForeignKeyConstraint($this->getTable($this->options['class_table_name']), ['class_id'], ['id'], ['onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE']);
98 | $table->addForeignKeyConstraint($this->getTable($this->options['oid_table_name']), ['object_identity_id'], ['id'], ['onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE']);
99 | $table->addForeignKeyConstraint($this->getTable($this->options['sid_table_name']), ['security_identity_id'], ['id'], ['onDelete' => 'CASCADE', 'onUpdate' => 'CASCADE']);
100 | }
101 |
102 | /**
103 | * Adds the object identity table to the schema.
104 | */
105 | protected function addObjectIdentitiesTable()
106 | {
107 | $table = $this->createTable($this->options['oid_table_name']);
108 |
109 | $table->addColumn('id', 'integer', ['unsigned' => true, 'autoincrement' => true]);
110 | $table->addColumn('class_id', 'integer', ['unsigned' => true]);
111 | $table->addColumn('object_identifier', 'string', ['length' => 100]);
112 | $table->addColumn('parent_object_identity_id', 'integer', ['unsigned' => true, 'notnull' => false]);
113 | $table->addColumn('entries_inheriting', 'boolean');
114 |
115 | $table->setPrimaryKey(['id']);
116 | $table->addUniqueIndex(['object_identifier', 'class_id']);
117 | $table->addIndex(['parent_object_identity_id']);
118 |
119 | $table->addForeignKeyConstraint($table, ['parent_object_identity_id'], ['id']);
120 | }
121 |
122 | /**
123 | * Adds the object identity relation table to the schema.
124 | */
125 | protected function addObjectIdentityAncestorsTable()
126 | {
127 | $table = $this->createTable($this->options['oid_ancestors_table_name']);
128 |
129 | $table->addColumn('object_identity_id', 'integer', ['unsigned' => true]);
130 | $table->addColumn('ancestor_id', 'integer', ['unsigned' => true]);
131 |
132 | $table->setPrimaryKey(['object_identity_id', 'ancestor_id']);
133 |
134 | $oidTable = $this->getTable($this->options['oid_table_name']);
135 | $action = 'CASCADE';
136 | if ($this->platform instanceof SQLServerPlatform) {
137 | // MS SQL Server does not support recursive cascading
138 | $action = 'NO ACTION';
139 | }
140 | $table->addForeignKeyConstraint($oidTable, ['object_identity_id'], ['id'], ['onDelete' => $action, 'onUpdate' => $action]);
141 | $table->addForeignKeyConstraint($oidTable, ['ancestor_id'], ['id'], ['onDelete' => $action, 'onUpdate' => $action]);
142 | }
143 |
144 | /**
145 | * Adds the security identity table to the schema.
146 | */
147 | protected function addSecurityIdentitiesTable()
148 | {
149 | $table = $this->createTable($this->options['sid_table_name']);
150 |
151 | $table->addColumn('id', 'integer', ['unsigned' => true, 'autoincrement' => true]);
152 | $table->addColumn('identifier', 'string', ['length' => 200]);
153 | $table->addColumn('username', 'boolean');
154 |
155 | $table->setPrimaryKey(['id']);
156 | $table->addUniqueIndex(['identifier', 'username']);
157 | }
158 |
159 | private function createSchemaConfig(?Connection $connection): ?SchemaConfig
160 | {
161 | if (null === $connection) {
162 | return null;
163 | }
164 |
165 | $schemaManager = method_exists($connection, 'createSchemaManager')
166 | ? $connection->createSchemaManager()
167 | : $connection->getSchemaManager()
168 | ;
169 |
170 | return $schemaManager->createSchemaConfig();
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/Domain/AclCacheTrait.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\AclInterface;
15 | use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
16 |
17 | /**
18 | * @author Johannes M. Schmitt
19 | *
20 | * @internal
21 | */
22 | trait AclCacheTrait
23 | {
24 | private $prefix;
25 | private $permissionGrantingStrategy;
26 |
27 | /**
28 | * Unserializes the ACL.
29 | */
30 | private function unserializeAcl(string $serialized): ?AclInterface
31 | {
32 | $acl = unserialize($serialized);
33 |
34 | if (null !== $parentId = $acl->getParentAcl()) {
35 | $parentAcl = $this->getFromCacheById($parentId);
36 |
37 | if (null === $parentAcl) {
38 | return null;
39 | }
40 |
41 | $acl->setParentAcl($parentAcl);
42 | }
43 |
44 | $reflectionProperty = new \ReflectionProperty($acl, 'permissionGrantingStrategy');
45 | $reflectionProperty->setAccessible(true);
46 | $reflectionProperty->setValue($acl, $this->permissionGrantingStrategy);
47 | $reflectionProperty->setAccessible(false);
48 |
49 | $aceAclProperty = new \ReflectionProperty(Entry::class, 'acl');
50 | $aceAclProperty->setAccessible(true);
51 |
52 | foreach ($acl->getObjectAces() as $ace) {
53 | $aceAclProperty->setValue($ace, $acl);
54 | }
55 | foreach ($acl->getClassAces() as $ace) {
56 | $aceAclProperty->setValue($ace, $acl);
57 | }
58 |
59 | $aceClassFieldProperty = new \ReflectionProperty($acl, 'classFieldAces');
60 | $aceClassFieldProperty->setAccessible(true);
61 | foreach ($aceClassFieldProperty->getValue($acl) as $aces) {
62 | foreach ($aces as $ace) {
63 | $aceAclProperty->setValue($ace, $acl);
64 | }
65 | }
66 | $aceClassFieldProperty->setAccessible(false);
67 |
68 | $aceObjectFieldProperty = new \ReflectionProperty($acl, 'objectFieldAces');
69 | $aceObjectFieldProperty->setAccessible(true);
70 | foreach ($aceObjectFieldProperty->getValue($acl) as $aces) {
71 | foreach ($aces as $ace) {
72 | $aceAclProperty->setValue($ace, $acl);
73 | }
74 | }
75 | $aceObjectFieldProperty->setAccessible(false);
76 |
77 | $aceAclProperty->setAccessible(false);
78 |
79 | return $acl;
80 | }
81 |
82 | /**
83 | * Returns the key for the object identity.
84 | */
85 | private function getDataKeyByIdentity(ObjectIdentityInterface $oid): string
86 | {
87 | return $this->prefix.md5($oid->getType()).sha1($oid->getType())
88 | .'_'.md5($oid->getIdentifier()).sha1($oid->getIdentifier());
89 | }
90 |
91 | /**
92 | * Returns the alias key for the object identity key.
93 | */
94 | private function getAliasKeyForIdentity(string $aclId): string
95 | {
96 | return $this->prefix.$aclId;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Domain/AclCollectionCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\AclProviderInterface;
15 | use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
16 | use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
17 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18 |
19 | /**
20 | * This service caches ACLs for an entire collection of objects.
21 | *
22 | * @author Johannes M. Schmitt
23 | */
24 | class AclCollectionCache
25 | {
26 | private $aclProvider;
27 | private $objectIdentityRetrievalStrategy;
28 | private $securityIdentityRetrievalStrategy;
29 |
30 | /**
31 | * Constructor.
32 | */
33 | public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy)
34 | {
35 | $this->aclProvider = $aclProvider;
36 | $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy;
37 | $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy;
38 | }
39 |
40 | /**
41 | * Batch loads ACLs for an entire collection; thus, it reduces the number
42 | * of required queries considerably.
43 | *
44 | * @param mixed $collection anything that can be passed to foreach()
45 | * @param TokenInterface[] $tokens an array of TokenInterface implementations
46 | */
47 | public function cache($collection, array $tokens = [])
48 | {
49 | $sids = [];
50 | foreach ($tokens as $token) {
51 | $sids = array_merge($sids, $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token));
52 | }
53 |
54 | $oids = [];
55 | foreach ($collection as $domainObject) {
56 | $oids[] = $this->objectIdentityRetrievalStrategy->getObjectIdentity($domainObject);
57 | }
58 |
59 | $this->aclProvider->findAcls($oids, $sids);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Domain/AuditLogger.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
15 | use Symfony\Component\Security\Acl\Model\AuditLoggerInterface;
16 | use Symfony\Component\Security\Acl\Model\EntryInterface;
17 |
18 | /**
19 | * Base audit logger implementation.
20 | *
21 | * @author Johannes M. Schmitt
22 | */
23 | abstract class AuditLogger implements AuditLoggerInterface
24 | {
25 | /**
26 | * Performs some checks if logging was requested.
27 | *
28 | * @param bool $granted
29 | */
30 | public function logIfNeeded($granted, EntryInterface $ace)
31 | {
32 | if (!$ace instanceof AuditableEntryInterface) {
33 | return;
34 | }
35 |
36 | if ($granted && $ace->isAuditSuccess()) {
37 | $this->doLog($granted, $ace);
38 | } elseif (!$granted && $ace->isAuditFailure()) {
39 | $this->doLog($granted, $ace);
40 | }
41 | }
42 |
43 | /**
44 | * This method is only called when logging is needed.
45 | *
46 | * @param bool $granted
47 | */
48 | abstract protected function doLog($granted, EntryInterface $ace);
49 | }
50 |
--------------------------------------------------------------------------------
/Domain/DoctrineAclCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Doctrine\Common\Cache\Cache;
15 | use Doctrine\Common\Cache\CacheProvider;
16 | use Symfony\Component\Security\Acl\Model\AclCacheInterface;
17 | use Symfony\Component\Security\Acl\Model\AclInterface;
18 | use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
19 | use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
20 |
21 | /**
22 | * This class is a wrapper around the actual cache implementation.
23 | *
24 | * @author Johannes M. Schmitt
25 | */
26 | class DoctrineAclCache implements AclCacheInterface
27 | {
28 | use AclCacheTrait;
29 |
30 | public const PREFIX = 'sf2_acl_';
31 |
32 | private $cache;
33 |
34 | /**
35 | * Constructor.
36 | *
37 | * @param string $prefix
38 | *
39 | * @throws \InvalidArgumentException
40 | */
41 | public function __construct(Cache $cache, PermissionGrantingStrategyInterface $permissionGrantingStrategy, $prefix = self::PREFIX)
42 | {
43 | $prefix = (string) $prefix;
44 | if ('' === $prefix) {
45 | throw new \InvalidArgumentException('$prefix cannot be empty.');
46 | }
47 |
48 | $this->cache = $cache;
49 | $this->permissionGrantingStrategy = $permissionGrantingStrategy;
50 | $this->prefix = $prefix;
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function clearCache()
57 | {
58 | if ($this->cache instanceof CacheProvider) {
59 | $this->cache->deleteAll();
60 | }
61 | }
62 |
63 | /**
64 | * {@inheritdoc}
65 | */
66 | public function evictFromCacheById($aclId)
67 | {
68 | $lookupKey = $this->getAliasKeyForIdentity($aclId);
69 | if (!$this->cache->contains($lookupKey)) {
70 | return;
71 | }
72 |
73 | $key = $this->cache->fetch($lookupKey);
74 | if ($this->cache->contains($key)) {
75 | $this->cache->delete($key);
76 | }
77 |
78 | $this->cache->delete($lookupKey);
79 | }
80 |
81 | /**
82 | * {@inheritdoc}
83 | */
84 | public function evictFromCacheByIdentity(ObjectIdentityInterface $oid)
85 | {
86 | $key = $this->getDataKeyByIdentity($oid);
87 | if (!$this->cache->contains($key)) {
88 | return;
89 | }
90 |
91 | $this->cache->delete($key);
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function getFromCacheById($aclId)
98 | {
99 | $lookupKey = $this->getAliasKeyForIdentity($aclId);
100 | if (!$this->cache->contains($lookupKey)) {
101 | return;
102 | }
103 |
104 | $key = $this->cache->fetch($lookupKey);
105 | if (!$this->cache->contains($key)) {
106 | $this->cache->delete($lookupKey);
107 |
108 | return;
109 | }
110 |
111 | return $this->unserializeAcl($this->cache->fetch($key));
112 | }
113 |
114 | /**
115 | * {@inheritdoc}
116 | */
117 | public function getFromCacheByIdentity(ObjectIdentityInterface $oid)
118 | {
119 | $key = $this->getDataKeyByIdentity($oid);
120 | if (!$this->cache->contains($key)) {
121 | return;
122 | }
123 |
124 | return $this->unserializeAcl($this->cache->fetch($key));
125 | }
126 |
127 | /**
128 | * {@inheritdoc}
129 | */
130 | public function putInCache(AclInterface $acl)
131 | {
132 | if (null === $acl->getId()) {
133 | throw new \InvalidArgumentException('Transient ACLs cannot be cached.');
134 | }
135 |
136 | if (null !== $parentAcl = $acl->getParentAcl()) {
137 | $this->putInCache($parentAcl);
138 | }
139 |
140 | $key = $this->getDataKeyByIdentity($acl->getObjectIdentity());
141 | $this->cache->save($key, serialize($acl));
142 | $this->cache->save($this->getAliasKeyForIdentity($acl->getId()), $key);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/Domain/Entry.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\AclInterface;
15 | use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
16 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
17 |
18 | /**
19 | * Auditable ACE implementation.
20 | *
21 | * @author Johannes M. Schmitt
22 | */
23 | class Entry implements AuditableEntryInterface
24 | {
25 | private $acl;
26 | private $mask;
27 | private $id;
28 | private $securityIdentity;
29 | private $strategy;
30 | private $auditFailure;
31 | private $auditSuccess;
32 | private $granting;
33 |
34 | public function __construct(?int $id, AclInterface $acl, SecurityIdentityInterface $sid, string $strategy, int $mask, bool $granting, bool $auditFailure, bool $auditSuccess)
35 | {
36 | $this->id = $id;
37 | $this->acl = $acl;
38 | $this->securityIdentity = $sid;
39 | $this->strategy = $strategy;
40 | $this->mask = $mask;
41 | $this->granting = $granting;
42 | $this->auditFailure = $auditFailure;
43 | $this->auditSuccess = $auditSuccess;
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getAcl()
50 | {
51 | return $this->acl;
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | public function getMask()
58 | {
59 | return $this->mask;
60 | }
61 |
62 | /**
63 | * {@inheritdoc}
64 | */
65 | public function getId()
66 | {
67 | return $this->id;
68 | }
69 |
70 | /**
71 | * {@inheritdoc}
72 | */
73 | public function getSecurityIdentity()
74 | {
75 | return $this->securityIdentity;
76 | }
77 |
78 | /**
79 | * {@inheritdoc}
80 | */
81 | public function getStrategy()
82 | {
83 | return $this->strategy;
84 | }
85 |
86 | /**
87 | * {@inheritdoc}
88 | */
89 | public function isAuditFailure()
90 | {
91 | return $this->auditFailure;
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function isAuditSuccess()
98 | {
99 | return $this->auditSuccess;
100 | }
101 |
102 | /**
103 | * {@inheritdoc}
104 | */
105 | public function isGranting()
106 | {
107 | return $this->granting;
108 | }
109 |
110 | /**
111 | * Turns on/off auditing on permissions denials.
112 | *
113 | * Do never call this method directly. Use the respective methods on the
114 | * AclInterface instead.
115 | *
116 | * @param bool $boolean
117 | */
118 | public function setAuditFailure($boolean)
119 | {
120 | $this->auditFailure = $boolean;
121 | }
122 |
123 | /**
124 | * Turns on/off auditing on permission grants.
125 | *
126 | * Do never call this method directly. Use the respective methods on the
127 | * AclInterface instead.
128 | *
129 | * @param bool $boolean
130 | */
131 | public function setAuditSuccess($boolean)
132 | {
133 | $this->auditSuccess = $boolean;
134 | }
135 |
136 | /**
137 | * Sets the permission mask.
138 | *
139 | * Do never call this method directly. Use the respective methods on the
140 | * AclInterface instead.
141 | *
142 | * @param int $mask
143 | */
144 | public function setMask($mask)
145 | {
146 | $this->mask = $mask;
147 | }
148 |
149 | /**
150 | * Sets the mask comparison strategy.
151 | *
152 | * Do never call this method directly. Use the respective methods on the
153 | * AclInterface instead.
154 | *
155 | * @param string $strategy
156 | */
157 | public function setStrategy($strategy)
158 | {
159 | $this->strategy = $strategy;
160 | }
161 |
162 | public function __serialize(): array
163 | {
164 | return [
165 | $this->mask,
166 | $this->id,
167 | $this->securityIdentity,
168 | $this->strategy,
169 | $this->auditFailure,
170 | $this->auditSuccess,
171 | $this->granting,
172 | ];
173 | }
174 |
175 | public function __unserialize(array $data): void
176 | {
177 | [$this->mask,
178 | $this->id,
179 | $this->securityIdentity,
180 | $this->strategy,
181 | $this->auditFailure,
182 | $this->auditSuccess,
183 | $this->granting
184 | ] = $data;
185 | }
186 |
187 | /**
188 | * @internal
189 | *
190 | * @final
191 | *
192 | * @return string
193 | */
194 | public function serialize()
195 | {
196 | return serialize($this->__serialize());
197 | }
198 |
199 | /**
200 | * @internal
201 | *
202 | * @final
203 | *
204 | * @param string $serialized
205 | */
206 | public function unserialize($serialized)
207 | {
208 | $this->__unserialize(\is_array($serialized) ? $serialized : unserialize($serialized));
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/Domain/FieldEntry.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\AclInterface;
15 | use Symfony\Component\Security\Acl\Model\FieldEntryInterface;
16 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
17 |
18 | /**
19 | * Field-aware ACE implementation which is auditable.
20 | *
21 | * @author Johannes M. Schmitt
22 | */
23 | class FieldEntry extends Entry implements FieldEntryInterface
24 | {
25 | private $field;
26 |
27 | public function __construct(?int $id, AclInterface $acl, string $field, SecurityIdentityInterface $sid, string $strategy, int $mask, bool $granting, bool $auditFailure, $auditSuccess)
28 | {
29 | parent::__construct($id, $acl, $sid, $strategy, $mask, $granting, $auditFailure, $auditSuccess);
30 |
31 | $this->field = $field;
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function getField()
38 | {
39 | return $this->field;
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | public function __serialize(): array
46 | {
47 | return [$this->field, parent::__serialize()];
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function __unserialize(array $data): void
54 | {
55 | [$this->field, $parentData] = $data;
56 | $parentData = \is_array($parentData) ? $parentData : unserialize($parentData);
57 | parent::__unserialize($parentData);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Domain/ObjectIdentity.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException;
15 | use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
16 | use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
17 | use Symfony\Component\Security\Acl\Util\ClassUtils;
18 |
19 | /**
20 | * ObjectIdentity implementation.
21 | *
22 | * @author Johannes M. Schmitt
23 | */
24 | final class ObjectIdentity implements ObjectIdentityInterface
25 | {
26 | private $identifier;
27 | private $type;
28 |
29 | /**
30 | * Constructor.
31 | *
32 | * @param string $identifier
33 | * @param string $type
34 | *
35 | * @throws \InvalidArgumentException
36 | */
37 | public function __construct($identifier, $type)
38 | {
39 | if ('' === $identifier) {
40 | throw new \InvalidArgumentException('$identifier cannot be empty.');
41 | }
42 | if (empty($type)) {
43 | throw new \InvalidArgumentException('$type cannot be empty.');
44 | }
45 |
46 | $this->identifier = $identifier;
47 | $this->type = $type;
48 | }
49 |
50 | /**
51 | * Constructs an ObjectIdentity for the given domain object.
52 | *
53 | * @param object $domainObject
54 | *
55 | * @return ObjectIdentity
56 | *
57 | * @throws InvalidDomainObjectException
58 | */
59 | public static function fromDomainObject($domainObject)
60 | {
61 | if (!\is_object($domainObject)) {
62 | throw new InvalidDomainObjectException('$domainObject must be an object.');
63 | }
64 |
65 | try {
66 | if ($domainObject instanceof DomainObjectInterface) {
67 | return new self($domainObject->getObjectIdentifier(), ClassUtils::getRealClass($domainObject));
68 | } elseif (method_exists($domainObject, 'getId')) {
69 | return new self((string) $domainObject->getId(), ClassUtils::getRealClass($domainObject));
70 | }
71 | } catch (\InvalidArgumentException $e) {
72 | throw new InvalidDomainObjectException($e->getMessage(), 0, $e);
73 | }
74 |
75 | throw new InvalidDomainObjectException('$domainObject must either implement the DomainObjectInterface, or have a method named "getId".');
76 | }
77 |
78 | /**
79 | * {@inheritdoc}
80 | */
81 | public function getIdentifier()
82 | {
83 | return $this->identifier;
84 | }
85 |
86 | /**
87 | * {@inheritdoc}
88 | */
89 | public function getType()
90 | {
91 | return $this->type;
92 | }
93 |
94 | /**
95 | * {@inheritdoc}
96 | */
97 | public function equals(ObjectIdentityInterface $identity)
98 | {
99 | // comparing the identifier with === might lead to problems, so we
100 | // waive this restriction
101 | return $this->identifier == $identity->getIdentifier()
102 | && $this->type === $identity->getType();
103 | }
104 |
105 | /**
106 | * Returns a textual representation of this object identity.
107 | *
108 | * @return string
109 | */
110 | public function __toString()
111 | {
112 | return sprintf('ObjectIdentity(%s, %s)', $this->identifier, $this->type);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Domain/ObjectIdentityRetrievalStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException;
15 | use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
16 |
17 | /**
18 | * Strategy to be used for retrieving object identities from domain objects.
19 | *
20 | * @author Johannes M. Schmitt
21 | */
22 | class ObjectIdentityRetrievalStrategy implements ObjectIdentityRetrievalStrategyInterface
23 | {
24 | /**
25 | * {@inheritdoc}
26 | */
27 | public function getObjectIdentity($domainObject)
28 | {
29 | try {
30 | return ObjectIdentity::fromDomainObject($domainObject);
31 | } catch (InvalidDomainObjectException $e) {
32 | return;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Domain/PermissionGrantingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
15 | use Symfony\Component\Security\Acl\Model\AclInterface;
16 | use Symfony\Component\Security\Acl\Model\AuditLoggerInterface;
17 | use Symfony\Component\Security\Acl\Model\EntryInterface;
18 | use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
19 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
20 |
21 | /**
22 | * The permission granting strategy to apply to the access control list.
23 | *
24 | * @author Johannes M. Schmitt
25 | */
26 | class PermissionGrantingStrategy implements PermissionGrantingStrategyInterface
27 | {
28 | public const EQUAL = 'equal';
29 | public const ALL = 'all';
30 | public const ANY = 'any';
31 |
32 | private $auditLogger;
33 |
34 | /**
35 | * Sets the audit logger.
36 | */
37 | public function setAuditLogger(AuditLoggerInterface $auditLogger)
38 | {
39 | $this->auditLogger = $auditLogger;
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | public function isGranted(AclInterface $acl, array $masks, array $sids, $administrativeMode = false)
46 | {
47 | try {
48 | try {
49 | $aces = $acl->getObjectAces();
50 |
51 | if (!$aces) {
52 | throw new NoAceFoundException();
53 | }
54 |
55 | return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
56 | } catch (NoAceFoundException $e) {
57 | $aces = $acl->getClassAces();
58 |
59 | if (!$aces) {
60 | throw $e;
61 | }
62 |
63 | return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
64 | }
65 | } catch (NoAceFoundException $e) {
66 | if ($acl->isEntriesInheriting() && null !== $parentAcl = $acl->getParentAcl()) {
67 | return $parentAcl->isGranted($masks, $sids, $administrativeMode);
68 | }
69 |
70 | throw $e;
71 | }
72 | }
73 |
74 | /**
75 | * {@inheritdoc}
76 | */
77 | public function isFieldGranted(AclInterface $acl, $field, array $masks, array $sids, $administrativeMode = false)
78 | {
79 | try {
80 | try {
81 | $aces = $acl->getObjectFieldAces($field);
82 | if (!$aces) {
83 | throw new NoAceFoundException();
84 | }
85 |
86 | return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
87 | } catch (NoAceFoundException $e) {
88 | $aces = $acl->getClassFieldAces($field);
89 | if (!$aces) {
90 | throw $e;
91 | }
92 |
93 | return $this->hasSufficientPermissions($acl, $aces, $masks, $sids, $administrativeMode);
94 | }
95 | } catch (NoAceFoundException $e) {
96 | if ($acl->isEntriesInheriting() && null !== $parentAcl = $acl->getParentAcl()) {
97 | return $parentAcl->isFieldGranted($field, $masks, $sids, $administrativeMode);
98 | }
99 |
100 | throw $e;
101 | }
102 | }
103 |
104 | /**
105 | * Makes an authorization decision.
106 | *
107 | * The order of ACEs, and SIDs is significant; the order of permission masks
108 | * not so much. It is important to note that the more specific security
109 | * identities should be at the beginning of the SIDs array in order for this
110 | * strategy to produce intuitive authorization decisions.
111 | *
112 | * First, we will iterate over permissions, then over security identities.
113 | * For each combination of permission, and identity we will test the
114 | * available ACEs until we find one which is applicable.
115 | *
116 | * The first applicable ACE will make the ultimate decision for the
117 | * permission/identity combination. If it is granting, this method will return
118 | * true, if it is denying, the method will continue to check the next
119 | * permission/identity combination.
120 | *
121 | * This process is repeated until either a granting ACE is found, or no
122 | * permission/identity combinations are left. Finally, we will either throw
123 | * an NoAceFoundException, or deny access.
124 | *
125 | * @param EntryInterface[] $aces An array of ACE to check against
126 | * @param array $masks An array of permission masks
127 | * @param SecurityIdentityInterface[] $sids An array of SecurityIdentityInterface implementations
128 | * @param bool $administrativeMode True turns off audit logging
129 | *
130 | * @return bool true, or false; either granting, or denying access respectively
131 | *
132 | * @throws NoAceFoundException
133 | */
134 | private function hasSufficientPermissions(AclInterface $acl, array $aces, array $masks, array $sids, $administrativeMode)
135 | {
136 | $firstRejectedAce = null;
137 |
138 | foreach ($masks as $requiredMask) {
139 | foreach ($sids as $sid) {
140 | foreach ($aces as $ace) {
141 | if ($sid->equals($ace->getSecurityIdentity()) && $this->isAceApplicable($requiredMask, $ace)) {
142 | if ($ace->isGranting()) {
143 | if (!$administrativeMode && null !== $this->auditLogger) {
144 | $this->auditLogger->logIfNeeded(true, $ace);
145 | }
146 |
147 | return true;
148 | }
149 |
150 | if (null === $firstRejectedAce) {
151 | $firstRejectedAce = $ace;
152 | }
153 |
154 | break 2;
155 | }
156 | }
157 | }
158 | }
159 |
160 | if (null !== $firstRejectedAce) {
161 | if (!$administrativeMode && null !== $this->auditLogger) {
162 | $this->auditLogger->logIfNeeded(false, $firstRejectedAce);
163 | }
164 |
165 | return false;
166 | }
167 |
168 | throw new NoAceFoundException();
169 | }
170 |
171 | /**
172 | * Determines whether the ACE is applicable to the given permission/security
173 | * identity combination.
174 | *
175 | * Per default, we support three different comparison strategies.
176 | *
177 | * Strategy ALL:
178 | * The ACE will be considered applicable when all the turned-on bits in the
179 | * required mask are also turned-on in the ACE mask.
180 | *
181 | * Strategy ANY:
182 | * The ACE will be considered applicable when any of the turned-on bits in
183 | * the required mask is also turned-on the in the ACE mask.
184 | *
185 | * Strategy EQUAL:
186 | * The ACE will be considered applicable when the bitmasks are equal.
187 | *
188 | * @param int $requiredMask
189 | *
190 | * @return bool
191 | *
192 | * @throws \RuntimeException if the ACE strategy is not supported
193 | */
194 | private function isAceApplicable($requiredMask, EntryInterface $ace)
195 | {
196 | $strategy = $ace->getStrategy();
197 | if (self::ALL === $strategy) {
198 | return $requiredMask === ($ace->getMask() & $requiredMask);
199 | } elseif (self::ANY === $strategy) {
200 | return 0 !== ($ace->getMask() & $requiredMask);
201 | } elseif (self::EQUAL === $strategy) {
202 | return $requiredMask === $ace->getMask();
203 | }
204 |
205 | throw new \RuntimeException(sprintf('The strategy "%s" is not supported.', $strategy));
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/Domain/PsrAclCache.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Psr\Cache\CacheItemPoolInterface;
15 | use Symfony\Component\Security\Acl\Model\AclCacheInterface;
16 | use Symfony\Component\Security\Acl\Model\AclInterface;
17 | use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
18 | use Symfony\Component\Security\Acl\Model\PermissionGrantingStrategyInterface;
19 |
20 | /**
21 | * This class is a wrapper around a PSR-6 cache implementation.
22 | *
23 | * @author Michael Babker
24 | */
25 | class PsrAclCache implements AclCacheInterface
26 | {
27 | use AclCacheTrait;
28 |
29 | public const PREFIX = 'sf_acl_';
30 |
31 | private $cache;
32 |
33 | /**
34 | * @throws \InvalidArgumentException When $prefix is empty
35 | */
36 | public function __construct(CacheItemPoolInterface $cache, PermissionGrantingStrategyInterface $permissionGrantingStrategy, string $prefix = self::PREFIX)
37 | {
38 | if ('' === $prefix) {
39 | throw new \InvalidArgumentException('$prefix cannot be empty.');
40 | }
41 |
42 | $this->cache = $cache;
43 | $this->permissionGrantingStrategy = $permissionGrantingStrategy;
44 | $this->prefix = $prefix;
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function clearCache(): void
51 | {
52 | $this->cache->clear();
53 | }
54 |
55 | /**
56 | * {@inheritdoc}
57 | */
58 | public function evictFromCacheById($aclId): void
59 | {
60 | $lookupKey = $this->getAliasKeyForIdentity($aclId);
61 | $cacheItem = $this->cache->getItem($lookupKey);
62 | if (!$cacheItem->isHit()) {
63 | return;
64 | }
65 |
66 | $this->cache->deleteItems([$cacheItem->get(), $lookupKey]);
67 | }
68 |
69 | /**
70 | * {@inheritdoc}
71 | */
72 | public function evictFromCacheByIdentity(ObjectIdentityInterface $oid): void
73 | {
74 | $this->cache->deleteItem($this->getDataKeyByIdentity($oid));
75 | }
76 |
77 | /**
78 | * {@inheritdoc}
79 | */
80 | public function getFromCacheById($aclId): ?AclInterface
81 | {
82 | $lookupKey = $this->getAliasKeyForIdentity($aclId);
83 | $lookupKeyItem = $this->cache->getItem($lookupKey);
84 | if (!$lookupKeyItem->isHit()) {
85 | return null;
86 | }
87 |
88 | $key = $lookupKeyItem->get();
89 | $keyItem = $this->cache->getItem($key);
90 | if (!$keyItem->isHit()) {
91 | $this->cache->deleteItem($lookupKey);
92 |
93 | return null;
94 | }
95 |
96 | return $this->unserializeAcl($keyItem->get());
97 | }
98 |
99 | /**
100 | * {@inheritdoc}
101 | */
102 | public function getFromCacheByIdentity(ObjectIdentityInterface $oid): ?AclInterface
103 | {
104 | $key = $this->getDataKeyByIdentity($oid);
105 | $cacheItem = $this->cache->getItem($key);
106 | if (!$cacheItem->isHit()) {
107 | return null;
108 | }
109 |
110 | return $this->unserializeAcl($cacheItem->get());
111 | }
112 |
113 | /**
114 | * {@inheritdoc}
115 | */
116 | public function putInCache(AclInterface $acl): void
117 | {
118 | if (null === $acl->getId()) {
119 | throw new \InvalidArgumentException('Transient ACLs cannot be cached.');
120 | }
121 |
122 | if (null !== $parentAcl = $acl->getParentAcl()) {
123 | $this->putInCache($parentAcl);
124 | }
125 |
126 | $key = $this->getDataKeyByIdentity($acl->getObjectIdentity());
127 | $objectIdentityItem = $this->cache->getItem($key);
128 | $objectIdentityItem->set(serialize($acl));
129 |
130 | $this->cache->saveDeferred($objectIdentityItem);
131 |
132 | $aliasKey = $this->getAliasKeyForIdentity($acl->getId());
133 | $aliasItem = $this->cache->getItem($aliasKey);
134 | $aliasItem->set($key);
135 |
136 | $this->cache->saveDeferred($aliasItem);
137 |
138 | $this->cache->commit();
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/Domain/RoleSecurityIdentity.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
15 |
16 | /**
17 | * A SecurityIdentity implementation for roles.
18 | *
19 | * @author Johannes M. Schmitt
20 | */
21 | final class RoleSecurityIdentity implements SecurityIdentityInterface
22 | {
23 | private $role;
24 |
25 | public function __construct(string $role)
26 | {
27 | $this->role = $role;
28 | }
29 |
30 | /**
31 | * Returns the role name.
32 | *
33 | * @return string
34 | */
35 | public function getRole()
36 | {
37 | return $this->role;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | public function equals(SecurityIdentityInterface $sid)
44 | {
45 | if (!$sid instanceof self) {
46 | return false;
47 | }
48 |
49 | return $this->role === $sid->getRole();
50 | }
51 |
52 | /**
53 | * Returns a textual representation of this security identity.
54 | *
55 | * This is solely used for debugging purposes, not to make an equality decision.
56 | *
57 | * @return string
58 | */
59 | public function __toString()
60 | {
61 | return sprintf('RoleSecurityIdentity(%s)', $this->role);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Domain/SecurityIdentityRetrievalStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
15 | use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
16 | use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
17 | use Symfony\Component\Security\Core\Authentication\Token\NullToken;
18 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
19 | use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
20 | use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
21 |
22 | /**
23 | * Strategy for retrieving security identities.
24 | *
25 | * @author Johannes M. Schmitt
26 | */
27 | class SecurityIdentityRetrievalStrategy implements SecurityIdentityRetrievalStrategyInterface
28 | {
29 | private $roleHierarchy;
30 | private $authenticationTrustResolver;
31 |
32 | /**
33 | * Constructor.
34 | */
35 | public function __construct(RoleHierarchyInterface $roleHierarchy, AuthenticationTrustResolverInterface $authenticationTrustResolver)
36 | {
37 | $this->roleHierarchy = $roleHierarchy;
38 | $this->authenticationTrustResolver = $authenticationTrustResolver;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | *
44 | * @return RoleSecurityIdentity[]
45 | */
46 | public function getSecurityIdentities(TokenInterface $token)
47 | {
48 | $sids = [];
49 |
50 | // add user security identity
51 | if (!$token instanceof AnonymousToken && !$token instanceof NullToken) {
52 | try {
53 | $sids[] = UserSecurityIdentity::fromToken($token);
54 | } catch (\InvalidArgumentException $e) {
55 | // ignore, user has no user security identity
56 | }
57 | }
58 |
59 | // add all reachable roles
60 | foreach ($this->roleHierarchy->getReachableRoleNames($token->getRoleNames()) as $role) {
61 | $sids[] = new RoleSecurityIdentity($role);
62 | }
63 |
64 | // add built-in special roles
65 | if ($this->authenticationTrustResolver->isFullFledged($token)) {
66 | $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_FULLY);
67 | $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED);
68 | $this->addAnonymousRoles($sids);
69 | } elseif ($this->authenticationTrustResolver->isRememberMe($token)) {
70 | $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::IS_AUTHENTICATED_REMEMBERED);
71 | $this->addAnonymousRoles($sids);
72 | } elseif ($this->isNotAuthenticated($token)) {
73 | $this->addAnonymousRoles($sids);
74 | }
75 |
76 | return $sids;
77 | }
78 |
79 | private function isNotAuthenticated(TokenInterface $token): bool
80 | {
81 | if (method_exists($this->authenticationTrustResolver, 'isAuthenticated')) {
82 | return !$this->authenticationTrustResolver->isAuthenticated($token);
83 | }
84 |
85 | return $this->authenticationTrustResolver->isAnonymous($token);
86 | }
87 |
88 | private function addAnonymousRoles(array &$sids)
89 | {
90 | $sids[] = new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY');
91 | if (\defined('\Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter::PUBLIC_ACCESS')) {
92 | $sids[] = new RoleSecurityIdentity(AuthenticatedVoter::PUBLIC_ACCESS);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Domain/UserSecurityIdentity.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
15 | use Symfony\Component\Security\Acl\Util\ClassUtils;
16 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
17 | use Symfony\Component\Security\Core\User\UserInterface;
18 |
19 | /**
20 | * A SecurityIdentity implementation used for actual users.
21 | *
22 | * @author Johannes M. Schmitt
23 | */
24 | final class UserSecurityIdentity implements SecurityIdentityInterface
25 | {
26 | private $username;
27 | private $class;
28 |
29 | /**
30 | * Constructor.
31 | *
32 | * @param string $username the username representation
33 | * @param string $class the user's fully qualified class name
34 | *
35 | * @throws \InvalidArgumentException
36 | */
37 | public function __construct($username, $class)
38 | {
39 | if ('' === $username || null === $username) {
40 | throw new \InvalidArgumentException('$username must not be empty.');
41 | }
42 | if (empty($class)) {
43 | throw new \InvalidArgumentException('$class must not be empty.');
44 | }
45 |
46 | $this->username = (string) $username;
47 | $this->class = $class;
48 | }
49 |
50 | /**
51 | * Creates a user security identity from a UserInterface.
52 | *
53 | * @return UserSecurityIdentity
54 | */
55 | public static function fromAccount(UserInterface $user)
56 | {
57 | return new self(method_exists($user, 'getUserIdentifier') ? $user->getUserIdentifier() : $user->getUsername(), ClassUtils::getRealClass($user));
58 | }
59 |
60 | /**
61 | * Creates a user security identity from a TokenInterface.
62 | *
63 | * @return UserSecurityIdentity
64 | */
65 | public static function fromToken(TokenInterface $token)
66 | {
67 | $user = $token->getUser();
68 |
69 | if ($user instanceof UserInterface) {
70 | return self::fromAccount($user);
71 | }
72 |
73 | return new self((string) $user, \is_object($user) ? ClassUtils::getRealClass($user) : ClassUtils::getRealClass($token));
74 | }
75 |
76 | /**
77 | * Returns the username.
78 | *
79 | * @return string
80 | */
81 | public function getUsername()
82 | {
83 | return $this->username;
84 | }
85 |
86 | /**
87 | * Returns the user's class name.
88 | *
89 | * @return string
90 | */
91 | public function getClass()
92 | {
93 | return $this->class;
94 | }
95 |
96 | /**
97 | * {@inheritdoc}
98 | */
99 | public function equals(SecurityIdentityInterface $sid)
100 | {
101 | if (!$sid instanceof self) {
102 | return false;
103 | }
104 |
105 | return $this->username === $sid->getUsername()
106 | && $this->class === $sid->getClass();
107 | }
108 |
109 | /**
110 | * A textual representation of this security identity.
111 | *
112 | * This is not used for equality comparison, but only for debugging.
113 | *
114 | * @return string
115 | */
116 | public function __toString()
117 | {
118 | return sprintf('UserSecurityIdentity(%s, %s)', $this->username, $this->class);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Exception/AclAlreadyExistsException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * This exception is thrown when someone tries to create an ACL for an object
16 | * identity that already has one.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class AclAlreadyExistsException extends Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/Exception/AclNotFoundException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * This exception is thrown when we cannot locate an ACL for a passed
16 | * ObjectIdentity implementation.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class AclNotFoundException extends Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/Exception/ConcurrentModificationException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * This exception is thrown whenever you change shared properties of more than
16 | * one ACL of the same class type concurrently.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class ConcurrentModificationException extends Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/Exception/Exception.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * Base ACL exception.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | class Exception extends \RuntimeException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/Exception/InvalidDomainObjectException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * This exception is thrown when ObjectIdentity fails to construct an object
16 | * identity from the passed domain object.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class InvalidDomainObjectException extends Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/Exception/NoAceFoundException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * This exception is thrown when we cannot locate an ACE that matches the
16 | * combination of permission masks and security identities.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class NoAceFoundException extends Exception
21 | {
22 | public function __construct()
23 | {
24 | parent::__construct('No applicable ACE was found.');
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Exception/NotAllAclsFoundException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | use Symfony\Component\Security\Acl\Model\AclInterface;
15 | use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
16 |
17 | /**
18 | * This exception is thrown when you have requested ACLs for multiple object
19 | * identities, but the AclProvider implementation failed to find ACLs for all
20 | * identities.
21 | *
22 | * This exception contains the partial result.
23 | *
24 | * @author Johannes M. Schmitt
25 | */
26 | class NotAllAclsFoundException extends AclNotFoundException
27 | {
28 | private $partialResult;
29 |
30 | /**
31 | * @param \SplObjectStorage $result
32 | */
33 | public function setPartialResult(\SplObjectStorage $result)
34 | {
35 | $this->partialResult = $result;
36 | }
37 |
38 | /**
39 | * Returns the partial result.
40 | *
41 | * @return \SplObjectStorage
42 | */
43 | public function getPartialResult()
44 | {
45 | return $this->partialResult;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Exception/SidNotLoadedException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Exception;
13 |
14 | /**
15 | * This exception is thrown when ACEs for an SID are requested which has not
16 | * been loaded from the database.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class SidNotLoadedException extends Exception
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2004-2021 Fabien Potencier
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Model/AclCacheInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * AclCache Interface.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface AclCacheInterface
20 | {
21 | /**
22 | * Removes an ACL from the cache.
23 | *
24 | * @param string $aclId a serialized primary key
25 | */
26 | public function evictFromCacheById($aclId);
27 |
28 | /**
29 | * Removes an ACL from the cache.
30 | *
31 | * The ACL which is returned, must reference the passed object identity.
32 | */
33 | public function evictFromCacheByIdentity(ObjectIdentityInterface $oid);
34 |
35 | /**
36 | * Retrieves an ACL for the given object identity primary key from the cache.
37 | *
38 | * @param int $aclId
39 | *
40 | * @return AclInterface|null
41 | */
42 | public function getFromCacheById($aclId);
43 |
44 | /**
45 | * Retrieves an ACL for the given object identity from the cache.
46 | *
47 | * @return AclInterface|null
48 | */
49 | public function getFromCacheByIdentity(ObjectIdentityInterface $oid);
50 |
51 | /**
52 | * Stores a new ACL in the cache.
53 | */
54 | public function putInCache(AclInterface $acl);
55 |
56 | /**
57 | * Removes all ACLs from the cache.
58 | */
59 | public function clearCache();
60 | }
61 |
--------------------------------------------------------------------------------
/Model/AclInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
15 |
16 | /**
17 | * This interface represents an access control list (ACL) for a domain object.
18 | * Each domain object can have exactly one associated ACL.
19 | *
20 | * An ACL contains all access control entries (ACE) for a given domain object.
21 | * In order to avoid needing references to the domain object itself, implementations
22 | * use ObjectIdentity implementations as an additional level of indirection.
23 | *
24 | * @author Johannes M. Schmitt
25 | *
26 | * @method array __serialize() returns all the necessary state of the object for serialization purposes
27 | * @method void __unserialize(array $data) restores the object state from an array given by {@see __serialize}
28 | */
29 | interface AclInterface extends \Serializable
30 | {
31 | /**
32 | * Returns all class-based ACEs associated with this ACL.
33 | *
34 | * @return array
35 | */
36 | public function getClassAces();
37 |
38 | /**
39 | * Returns all class-field-based ACEs associated with this ACL.
40 | *
41 | * @return array
42 | */
43 | public function getClassFieldAces(string $field);
44 |
45 | /**
46 | * Returns all object-based ACEs associated with this ACL.
47 | *
48 | * @return array
49 | */
50 | public function getObjectAces();
51 |
52 | /**
53 | * Returns all object-field-based ACEs associated with this ACL.
54 | *
55 | * @return array
56 | */
57 | public function getObjectFieldAces(string $field);
58 |
59 | /**
60 | * Returns the object identity associated with this ACL.
61 | *
62 | * @return ObjectIdentityInterface
63 | */
64 | public function getObjectIdentity();
65 |
66 | /**
67 | * Returns the parent ACL, or null if there is none.
68 | *
69 | * @return AclInterface|null
70 | */
71 | public function getParentAcl();
72 |
73 | /**
74 | * Whether this ACL is inheriting ACEs from a parent ACL.
75 | *
76 | * @return bool
77 | */
78 | public function isEntriesInheriting();
79 |
80 | /**
81 | * Determines whether field access is granted.
82 | *
83 | * @return bool
84 | */
85 | public function isFieldGranted(string $field, array $masks, array $securityIdentities, bool $administrativeMode = false);
86 |
87 | /**
88 | * Determines whether access is granted.
89 | *
90 | * @return bool
91 | *
92 | * @throws NoAceFoundException when no ACE was applicable for this request
93 | */
94 | public function isGranted(array $masks, array $securityIdentities, bool $administrativeMode = false);
95 |
96 | /**
97 | * Whether the ACL has loaded ACEs for all of the passed security identities.
98 | *
99 | * @param SecurityIdentityInterface|SecurityIdentityInterface[] $securityIdentities
100 | *
101 | * @return bool
102 | */
103 | public function isSidLoaded($securityIdentities);
104 | }
105 |
--------------------------------------------------------------------------------
/Model/AclProviderInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
15 |
16 | /**
17 | * Provides a common interface for retrieving ACLs.
18 | *
19 | * @author Johannes M. Schmitt
20 | */
21 | interface AclProviderInterface
22 | {
23 | /**
24 | * Retrieves all child object identities from the database.
25 | *
26 | * @param bool $directChildrenOnly
27 | *
28 | * @return array returns an array of child 'ObjectIdentity's
29 | */
30 | public function findChildren(ObjectIdentityInterface $parentOid, $directChildrenOnly = false);
31 |
32 | /**
33 | * Returns the ACL that belongs to the given object identity.
34 | *
35 | * @param SecurityIdentityInterface[] $sids
36 | *
37 | * @return AclInterface
38 | *
39 | * @throws AclNotFoundException when there is no ACL
40 | */
41 | public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
42 |
43 | /**
44 | * Returns the ACLs that belong to the given object identities.
45 | *
46 | * @param ObjectIdentityInterface[] $oids an array of ObjectIdentityInterface implementations
47 | * @param SecurityIdentityInterface[] $sids an array of SecurityIdentityInterface implementations
48 | *
49 | * @return \SplObjectStorage mapping the passed object identities to ACLs
50 | *
51 | * @throws AclNotFoundException when we cannot find an ACL for all identities
52 | */
53 | public function findAcls(array $oids, array $sids = []);
54 | }
55 |
--------------------------------------------------------------------------------
/Model/AuditLoggerInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * Interface for audit loggers.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface AuditLoggerInterface
20 | {
21 | /**
22 | * This method is called whenever access is granted, or denied, and
23 | * administrative mode is turned off.
24 | *
25 | * @param bool $granted
26 | */
27 | public function logIfNeeded($granted, EntryInterface $ace);
28 | }
29 |
--------------------------------------------------------------------------------
/Model/AuditableAclInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * This interface adds auditing capabilities to the ACL.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface AuditableAclInterface extends MutableAclInterface
20 | {
21 | /**
22 | * Updates auditing for class-based ACE.
23 | */
24 | public function updateClassAuditing(int $index, bool $auditSuccess, bool $auditFailure);
25 |
26 | /**
27 | * Updates auditing for class-field-based ACE.
28 | */
29 | public function updateClassFieldAuditing(int $index, string $field, bool $auditSuccess, bool $auditFailure);
30 |
31 | /**
32 | * Updates auditing for object-based ACE.
33 | */
34 | public function updateObjectAuditing(int $index, bool $auditSuccess, bool $auditFailure);
35 |
36 | /**
37 | * Updates auditing for object-field-based ACE.
38 | */
39 | public function updateObjectFieldAuditing(int $index, string $field, bool $auditSuccess, bool $auditFailure);
40 | }
41 |
--------------------------------------------------------------------------------
/Model/AuditableEntryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * ACEs can implement this interface if they support auditing capabilities.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface AuditableEntryInterface extends EntryInterface
20 | {
21 | /**
22 | * Whether auditing for successful grants is turned on.
23 | *
24 | * @return bool
25 | */
26 | public function isAuditFailure();
27 |
28 | /**
29 | * Whether auditing for successful denies is turned on.
30 | *
31 | * @return bool
32 | */
33 | public function isAuditSuccess();
34 | }
35 |
--------------------------------------------------------------------------------
/Model/DomainObjectInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * This method can be implemented by domain objects which you want to store
16 | * ACLs for if they do not have a getId() method, or getId() does not return
17 | * a unique identifier.
18 | *
19 | * @author Johannes M. Schmitt
20 | */
21 | interface DomainObjectInterface
22 | {
23 | /**
24 | * Returns a unique identifier for this domain object.
25 | *
26 | * @return string
27 | */
28 | public function getObjectIdentifier();
29 | }
30 |
--------------------------------------------------------------------------------
/Model/EntryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * This class represents an individual entry in the ACL list.
16 | *
17 | * Instances MUST be immutable, as they are returned by the ACL and should not
18 | * allow client modification.
19 | *
20 | * @author Johannes M. Schmitt
21 | *
22 | * @method array __serialize() returns all the necessary state of the object for serialization purposes
23 | * @method void __unserialize(array $data) restores the object state from an array given by {@see __serialize}
24 | */
25 | interface EntryInterface extends \Serializable
26 | {
27 | /**
28 | * The ACL this ACE is associated with.
29 | *
30 | * @return AclInterface
31 | */
32 | public function getAcl();
33 |
34 | /**
35 | * The primary key of this ACE.
36 | *
37 | * @return int|null
38 | */
39 | public function getId();
40 |
41 | /**
42 | * The permission mask of this ACE.
43 | *
44 | * @return int
45 | */
46 | public function getMask();
47 |
48 | /**
49 | * The security identity associated with this ACE.
50 | *
51 | * @return SecurityIdentityInterface
52 | */
53 | public function getSecurityIdentity();
54 |
55 | /**
56 | * The strategy for comparing masks.
57 | *
58 | * @return string
59 | */
60 | public function getStrategy();
61 |
62 | /**
63 | * Returns whether this ACE is granting, or denying.
64 | *
65 | * @return bool
66 | */
67 | public function isGranting();
68 | }
69 |
--------------------------------------------------------------------------------
/Model/FieldEntryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * Interface for entries which are restricted to specific fields.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface FieldEntryInterface extends EntryInterface
20 | {
21 | /**
22 | * Returns the field used for this entry.
23 | *
24 | * @return string
25 | */
26 | public function getField();
27 | }
28 |
--------------------------------------------------------------------------------
/Model/MutableAclInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * This interface adds mutators for the AclInterface.
16 | *
17 | * All changes to Access Control Entries must go through this interface. Access
18 | * Control Entries must never be modified directly.
19 | *
20 | * @author Johannes M. Schmitt
21 | */
22 | interface MutableAclInterface extends AclInterface
23 | {
24 | /**
25 | * Deletes a class-based ACE.
26 | */
27 | public function deleteClassAce(int $index);
28 |
29 | /**
30 | * Deletes a class-field-based ACE.
31 | */
32 | public function deleteClassFieldAce(int $index, string $field);
33 |
34 | /**
35 | * Deletes an object-based ACE.
36 | */
37 | public function deleteObjectAce(int $index);
38 |
39 | /**
40 | * Deletes an object-field-based ACE.
41 | */
42 | public function deleteObjectFieldAce(int $index, string $field);
43 |
44 | /**
45 | * Returns the primary key of this ACL.
46 | *
47 | * @return int
48 | */
49 | public function getId();
50 |
51 | /**
52 | * Inserts a class-based ACE.
53 | */
54 | public function insertClassAce(SecurityIdentityInterface $sid, int $mask, int $index = 0, bool $granting = true, ?string $strategy = null);
55 |
56 | /**
57 | * Inserts a class-field-based ACE.
58 | */
59 | public function insertClassFieldAce(string $field, SecurityIdentityInterface $sid, int $mask, int $index = 0, bool $granting = true, ?string $strategy = null);
60 |
61 | /**
62 | * Inserts an object-based ACE.
63 | */
64 | public function insertObjectAce(SecurityIdentityInterface $sid, int $mask, int $index = 0, bool $granting = true, ?string $strategy = null);
65 |
66 | /**
67 | * Inserts an object-field-based ACE.
68 | */
69 | public function insertObjectFieldAce(string $field, SecurityIdentityInterface $sid, int $mask, int $index = 0, bool $granting = true, ?string $strategy = null);
70 |
71 | /**
72 | * Sets whether entries are inherited.
73 | */
74 | public function setEntriesInheriting(bool $boolean);
75 |
76 | /**
77 | * Sets the parent ACL.
78 | */
79 | public function setParentAcl(?AclInterface $acl = null);
80 |
81 | /**
82 | * Updates a class-based ACE.
83 | *
84 | * @param string|null $strategy if null the strategy should not be changed
85 | */
86 | public function updateClassAce(int $index, int $mask, ?string $strategy = null);
87 |
88 | /**
89 | * Updates a class-field-based ACE.
90 | *
91 | * @param string|null $strategy if null the strategy should not be changed
92 | */
93 | public function updateClassFieldAce(int $index, string $field, int $mask, ?string $strategy = null);
94 |
95 | /**
96 | * Updates an object-based ACE.
97 | *
98 | * @param string|null $strategy if null the strategy should not be changed
99 | */
100 | public function updateObjectAce(int $index, int $mask, ?string $strategy = null);
101 |
102 | /**
103 | * Updates an object-field-based ACE.
104 | *
105 | * @param string|null $strategy if null the strategy should not be changed
106 | */
107 | public function updateObjectFieldAce(int $index, string $field, int $mask, ?string $strategy = null);
108 | }
109 |
--------------------------------------------------------------------------------
/Model/MutableAclProviderInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | use Symfony\Component\Security\Acl\Exception\AclAlreadyExistsException;
15 |
16 | /**
17 | * Provides support for creating and storing ACL instances.
18 | *
19 | * @author Johannes M. Schmitt
20 | */
21 | interface MutableAclProviderInterface extends AclProviderInterface
22 | {
23 | /**
24 | * {@inheritdoc}
25 | *
26 | * @return MutableAclInterface
27 | */
28 | public function findAcl(ObjectIdentityInterface $oid, array $sids = []);
29 |
30 | /**
31 | * {@inheritdoc}
32 | *
33 | * @return \SplObjectStorage mapping the passed object identities to ACLs
34 | */
35 | public function findAcls(array $oids, array $sids = []);
36 |
37 | /**
38 | * Creates a new ACL for the given object identity.
39 | *
40 | * @return MutableAclInterface
41 | *
42 | * @throws AclAlreadyExistsException when there already is an ACL for the given
43 | * object identity
44 | */
45 | public function createAcl(ObjectIdentityInterface $oid);
46 |
47 | /**
48 | * Deletes the ACL for a given object identity.
49 | *
50 | * This will automatically trigger a delete for any child ACLs. If you don't
51 | * want child ACLs to be deleted, you will have to set their parent ACL to null.
52 | */
53 | public function deleteAcl(ObjectIdentityInterface $oid);
54 |
55 | /**
56 | * Persists any changes which were made to the ACL, or any associated
57 | * access control entries.
58 | *
59 | * Changes to parent ACLs are not persisted.
60 | */
61 | public function updateAcl(MutableAclInterface $acl);
62 | }
63 |
--------------------------------------------------------------------------------
/Model/ObjectIdentityInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * Represents the identity of an individual domain object instance.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface ObjectIdentityInterface
20 | {
21 | /**
22 | * We specifically require this method so we can check for object equality
23 | * explicitly, and do not have to rely on referencial equality instead.
24 | *
25 | * Though in most cases, both checks should result in the same outcome.
26 | *
27 | * Referential Equality: $object1 === $object2
28 | * Example for Object Equality: $object1->getId() === $object2->getId()
29 | *
30 | * @return bool
31 | */
32 | public function equals(self $identity);
33 |
34 | /**
35 | * Obtains a unique identifier for this object. The identifier must not be
36 | * re-used for other objects with the same type.
37 | *
38 | * @return string cannot return null
39 | */
40 | public function getIdentifier();
41 |
42 | /**
43 | * Returns a type for the domain object. Typically, this is the PHP class name.
44 | *
45 | * @return string cannot return null
46 | */
47 | public function getType();
48 | }
49 |
--------------------------------------------------------------------------------
/Model/ObjectIdentityRetrievalStrategyInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * Retrieves the object identity for a given domain object.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface ObjectIdentityRetrievalStrategyInterface
20 | {
21 | /**
22 | * Retrieves the object identity from a domain object.
23 | *
24 | * @param object $domainObject
25 | *
26 | * @return ObjectIdentityInterface
27 | */
28 | public function getObjectIdentity($domainObject);
29 | }
30 |
--------------------------------------------------------------------------------
/Model/PermissionGrantingStrategyInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * Interface used by permission granting implementations.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface PermissionGrantingStrategyInterface
20 | {
21 | /**
22 | * Determines whether access to a domain object is to be granted.
23 | *
24 | * @param bool $administrativeMode
25 | *
26 | * @return bool
27 | */
28 | public function isGranted(AclInterface $acl, array $masks, array $sids, $administrativeMode = false);
29 |
30 | /**
31 | * Determines whether access to a domain object's field is to be granted.
32 | *
33 | * @param string $field
34 | * @param bool $administrativeMode
35 | *
36 | * @return bool
37 | */
38 | public function isFieldGranted(AclInterface $acl, $field, array $masks, array $sids, $administrativeMode = false);
39 | }
40 |
--------------------------------------------------------------------------------
/Model/SecurityIdentityInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | /**
15 | * This interface provides an additional level of indirection, so that
16 | * we can work with abstracted versions of security objects and do
17 | * not have to save the entire objects.
18 | *
19 | * @author Johannes M. Schmitt
20 | */
21 | interface SecurityIdentityInterface
22 | {
23 | /**
24 | * This method is used to compare two security identities in order to
25 | * not rely on referential equality.
26 | */
27 | public function equals(self $sid);
28 | }
29 |
--------------------------------------------------------------------------------
/Model/SecurityIdentityRetrievalStrategyInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Model;
13 |
14 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
15 |
16 | /**
17 | * Interface for retrieving security identities from tokens.
18 | *
19 | * @author Johannes M. Schmitt
20 | */
21 | interface SecurityIdentityRetrievalStrategyInterface
22 | {
23 | /**
24 | * Retrieves the available security identities for the given token.
25 | *
26 | * The order in which the security identities are returned is significant.
27 | * Typically, security identities should be ordered from most specific to
28 | * least specific.
29 | *
30 | * @return SecurityIdentityInterface[] An array of SecurityIdentityInterface implementations
31 | */
32 | public function getSecurityIdentities(TokenInterface $token);
33 | }
34 |
--------------------------------------------------------------------------------
/Permission/AbstractMaskBuilder.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Permission;
13 |
14 | /**
15 | * This abstract class implements nearly all the MaskBuilderInterface methods.
16 | */
17 | abstract class AbstractMaskBuilder implements MaskBuilderInterface
18 | {
19 | /**
20 | * @var int
21 | */
22 | protected $mask;
23 |
24 | /**
25 | * Constructor.
26 | *
27 | * @param int $mask optional; defaults to 0
28 | */
29 | public function __construct($mask = 0)
30 | {
31 | $this->set($mask);
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function set($mask)
38 | {
39 | if (!\is_int($mask)) {
40 | throw new \InvalidArgumentException('$mask must be an integer.');
41 | }
42 |
43 | $this->mask = $mask;
44 |
45 | return $this;
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function get()
52 | {
53 | return $this->mask;
54 | }
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | public function add($mask)
60 | {
61 | $this->mask |= $this->resolveMask($mask);
62 |
63 | return $this;
64 | }
65 |
66 | /**
67 | * {@inheritdoc}
68 | */
69 | public function remove($mask)
70 | {
71 | $this->mask &= ~$this->resolveMask($mask);
72 |
73 | return $this;
74 | }
75 |
76 | /**
77 | * {@inheritdoc}
78 | */
79 | public function reset()
80 | {
81 | $this->mask = 0;
82 |
83 | return $this;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Permission/BasicPermissionMap.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Permission;
13 |
14 | /**
15 | * This is basic permission map complements the masks which have been defined
16 | * on the standard implementation of the MaskBuilder.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class BasicPermissionMap implements PermissionMapInterface, MaskBuilderRetrievalInterface
21 | {
22 | public const PERMISSION_VIEW = 'VIEW';
23 | public const PERMISSION_EDIT = 'EDIT';
24 | public const PERMISSION_CREATE = 'CREATE';
25 | public const PERMISSION_DELETE = 'DELETE';
26 | public const PERMISSION_UNDELETE = 'UNDELETE';
27 | public const PERMISSION_OPERATOR = 'OPERATOR';
28 | public const PERMISSION_MASTER = 'MASTER';
29 | public const PERMISSION_OWNER = 'OWNER';
30 |
31 | protected $map;
32 |
33 | public function __construct()
34 | {
35 | $this->map = [
36 | self::PERMISSION_VIEW => [
37 | MaskBuilder::MASK_VIEW,
38 | MaskBuilder::MASK_EDIT,
39 | MaskBuilder::MASK_OPERATOR,
40 | MaskBuilder::MASK_MASTER,
41 | MaskBuilder::MASK_OWNER,
42 | ],
43 |
44 | self::PERMISSION_EDIT => [
45 | MaskBuilder::MASK_EDIT,
46 | MaskBuilder::MASK_OPERATOR,
47 | MaskBuilder::MASK_MASTER,
48 | MaskBuilder::MASK_OWNER,
49 | ],
50 |
51 | self::PERMISSION_CREATE => [
52 | MaskBuilder::MASK_CREATE,
53 | MaskBuilder::MASK_OPERATOR,
54 | MaskBuilder::MASK_MASTER,
55 | MaskBuilder::MASK_OWNER,
56 | ],
57 |
58 | self::PERMISSION_DELETE => [
59 | MaskBuilder::MASK_DELETE,
60 | MaskBuilder::MASK_OPERATOR,
61 | MaskBuilder::MASK_MASTER,
62 | MaskBuilder::MASK_OWNER,
63 | ],
64 |
65 | self::PERMISSION_UNDELETE => [
66 | MaskBuilder::MASK_UNDELETE,
67 | MaskBuilder::MASK_OPERATOR,
68 | MaskBuilder::MASK_MASTER,
69 | MaskBuilder::MASK_OWNER,
70 | ],
71 |
72 | self::PERMISSION_OPERATOR => [
73 | MaskBuilder::MASK_OPERATOR,
74 | MaskBuilder::MASK_MASTER,
75 | MaskBuilder::MASK_OWNER,
76 | ],
77 |
78 | self::PERMISSION_MASTER => [
79 | MaskBuilder::MASK_MASTER,
80 | MaskBuilder::MASK_OWNER,
81 | ],
82 |
83 | self::PERMISSION_OWNER => [
84 | MaskBuilder::MASK_OWNER,
85 | ],
86 | ];
87 | }
88 |
89 | /**
90 | * {@inheritdoc}
91 | */
92 | public function getMasks($permission, $object)
93 | {
94 | if (!isset($this->map[$permission])) {
95 | return;
96 | }
97 |
98 | return $this->map[$permission];
99 | }
100 |
101 | /**
102 | * {@inheritdoc}
103 | */
104 | public function contains($permission)
105 | {
106 | return isset($this->map[$permission]);
107 | }
108 |
109 | /**
110 | * {@inheritdoc}
111 | */
112 | public function getMaskBuilder()
113 | {
114 | return new MaskBuilder();
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Permission/MaskBuilder.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Permission;
13 |
14 | /**
15 | * This class allows you to build cumulative permissions easily, or convert
16 | * masks to a human-readable format.
17 | *
18 | *
19 | * $builder = new MaskBuilder();
20 | * $builder
21 | * ->add('view')
22 | * ->add('create')
23 | * ->add('edit')
24 | * ;
25 | * var_dump($builder->get()); // int(7)
26 | * var_dump($builder->getPattern()); // string(32) ".............................ECV"
27 | *
28 | *
29 | * We have defined some commonly used base permissions which you can use:
30 | * - VIEW: the SID is allowed to view the domain object / field
31 | * - CREATE: the SID is allowed to create new instances of the domain object / fields
32 | * - EDIT: the SID is allowed to edit existing instances of the domain object / field
33 | * - DELETE: the SID is allowed to delete domain objects
34 | * - UNDELETE: the SID is allowed to recover domain objects from trash
35 | * - OPERATOR: the SID is allowed to perform any action on the domain object
36 | * except for granting others permissions
37 | * - MASTER: the SID is allowed to perform any action on the domain object,
38 | * and is allowed to grant other SIDs any permission except for
39 | * MASTER and OWNER permissions
40 | * - OWNER: the SID is owning the domain object in question and can perform any
41 | * action on the domain object as well as grant any permission
42 | *
43 | * @author Johannes M. Schmitt
44 | */
45 | class MaskBuilder extends AbstractMaskBuilder
46 | {
47 | public const MASK_VIEW = 1; // 1 << 0
48 | public const MASK_CREATE = 2; // 1 << 1
49 | public const MASK_EDIT = 4; // 1 << 2
50 | public const MASK_DELETE = 8; // 1 << 3
51 | public const MASK_UNDELETE = 16; // 1 << 4
52 | public const MASK_OPERATOR = 32; // 1 << 5
53 | public const MASK_MASTER = 64; // 1 << 6
54 | public const MASK_OWNER = 128; // 1 << 7
55 | public const MASK_IDDQD = 1073741823; // 1 << 0 | 1 << 1 | ... | 1 << 30
56 |
57 | public const CODE_VIEW = 'V';
58 | public const CODE_CREATE = 'C';
59 | public const CODE_EDIT = 'E';
60 | public const CODE_DELETE = 'D';
61 | public const CODE_UNDELETE = 'U';
62 | public const CODE_OPERATOR = 'O';
63 | public const CODE_MASTER = 'M';
64 | public const CODE_OWNER = 'N';
65 |
66 | public const ALL_OFF = '................................';
67 | public const OFF = '.';
68 | public const ON = '*';
69 |
70 | /**
71 | * Returns a human-readable representation of the permission.
72 | *
73 | * @return string
74 | */
75 | public function getPattern()
76 | {
77 | $pattern = self::ALL_OFF;
78 | $length = \strlen($pattern);
79 | $bitmask = str_pad(decbin($this->mask), $length, '0', \STR_PAD_LEFT);
80 |
81 | for ($i = $length - 1; $i >= 0; --$i) {
82 | if ('1' === $bitmask[$i]) {
83 | try {
84 | $pattern[$i] = self::getCode(1 << ($length - $i - 1));
85 | } catch (\Exception $e) {
86 | $pattern[$i] = self::ON;
87 | }
88 | }
89 | }
90 |
91 | return $pattern;
92 | }
93 |
94 | /**
95 | * Returns the code for the passed mask.
96 | *
97 | * @param int $mask
98 | *
99 | * @return string
100 | *
101 | * @throws \InvalidArgumentException
102 | * @throws \RuntimeException
103 | */
104 | public static function getCode($mask)
105 | {
106 | if (!\is_int($mask)) {
107 | throw new \InvalidArgumentException('$mask must be an integer.');
108 | }
109 |
110 | $reflection = new \ReflectionClass(static::class);
111 | foreach ($reflection->getConstants() as $name => $cMask) {
112 | if (0 !== strpos($name, 'MASK_') || $mask !== $cMask) {
113 | continue;
114 | }
115 |
116 | if (!\defined($cName = 'static::CODE_'.substr($name, 5))) {
117 | throw new \RuntimeException('There was no code defined for this mask.');
118 | }
119 |
120 | return \constant($cName);
121 | }
122 |
123 | throw new \InvalidArgumentException(sprintf('The mask "%d" is not supported.', $mask));
124 | }
125 |
126 | /**
127 | * Returns the mask for the passed code.
128 | *
129 | * @param mixed $code
130 | *
131 | * @return int
132 | *
133 | * @throws \InvalidArgumentException
134 | */
135 | public function resolveMask($code)
136 | {
137 | if (\is_string($code)) {
138 | if (!\defined($name = sprintf('static::MASK_%s', strtoupper($code)))) {
139 | throw new \InvalidArgumentException(sprintf('The code "%s" is not supported', $code));
140 | }
141 |
142 | return \constant($name);
143 | }
144 |
145 | if (!\is_int($code)) {
146 | throw new \InvalidArgumentException('$code must be an integer.');
147 | }
148 |
149 | return $code;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/Permission/MaskBuilderInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Permission;
13 |
14 | /**
15 | * This is the interface that must be implemented by mask builders.
16 | */
17 | interface MaskBuilderInterface
18 | {
19 | /**
20 | * Set the mask of this permission.
21 | *
22 | * @param int $mask
23 | *
24 | * @return MaskBuilderInterface
25 | *
26 | * @throws \InvalidArgumentException if $mask is not an integer
27 | */
28 | public function set($mask);
29 |
30 | /**
31 | * Returns the mask of this permission.
32 | *
33 | * @return int
34 | */
35 | public function get();
36 |
37 | /**
38 | * Adds a mask to the permission.
39 | *
40 | * @param mixed $mask
41 | *
42 | * @return MaskBuilderInterface
43 | *
44 | * @throws \InvalidArgumentException
45 | */
46 | public function add($mask);
47 |
48 | /**
49 | * Removes a mask from the permission.
50 | *
51 | * @param mixed $mask
52 | *
53 | * @return MaskBuilderInterface
54 | *
55 | * @throws \InvalidArgumentException
56 | */
57 | public function remove($mask);
58 |
59 | /**
60 | * Resets the PermissionBuilder.
61 | *
62 | * @return MaskBuilderInterface
63 | */
64 | public function reset();
65 |
66 | /**
67 | * Returns the mask for the passed code.
68 | *
69 | * @param mixed $code
70 | *
71 | * @return int
72 | *
73 | * @throws \InvalidArgumentException
74 | */
75 | public function resolveMask($code);
76 | }
77 |
--------------------------------------------------------------------------------
/Permission/MaskBuilderRetrievalInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Permission;
13 |
14 | /**
15 | * Retrieves the MaskBuilder.
16 | */
17 | interface MaskBuilderRetrievalInterface
18 | {
19 | /**
20 | * Returns a new instance of the MaskBuilder used in the permissionMap.
21 | *
22 | * @return MaskBuilderInterface
23 | */
24 | public function getMaskBuilder();
25 | }
26 |
--------------------------------------------------------------------------------
/Permission/PermissionMapInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Permission;
13 |
14 | /**
15 | * This is the interface that must be implemented by permission maps.
16 | *
17 | * @author Johannes M. Schmitt
18 | */
19 | interface PermissionMapInterface
20 | {
21 | /**
22 | * Returns an array of bitmasks.
23 | *
24 | * The security identity must have been granted access to at least one of
25 | * these bitmasks.
26 | *
27 | * @param string $permission
28 | * @param object $object
29 | *
30 | * @return array|null may return null if permission/object combination is not supported
31 | */
32 | public function getMasks($permission, $object);
33 |
34 | /**
35 | * Whether this map contains the given permission.
36 | *
37 | * @param string $permission
38 | *
39 | * @return bool
40 | */
41 | public function contains($permission);
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Security Component - ACL (Access Control List)
2 | ==============================================
3 |
4 | Security provides an infrastructure for sophisticated authorization systems,
5 | which makes it possible to easily separate the actual authorization logic from
6 | so called user providers that hold the users credentials. It is inspired by
7 | the Java Spring framework.
8 |
9 | Resources
10 | ---------
11 |
12 | Documentation:
13 |
14 | https://github.com/symfony/acl-bundle/blob/main/src/Resources/doc/index.rst
15 |
16 | Tests
17 | -----
18 |
19 | You can run the unit tests with the following command:
20 |
21 | $ cd path/to/Symfony/Component/Security/Acl/
22 | $ composer.phar install --dev
23 | $ phpunit
24 |
--------------------------------------------------------------------------------
/Resources/bin/generateSql.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | require_once __DIR__.'/../../vendor/autoload.php';
13 |
14 | use Symfony\Component\Finder\Finder;
15 | use Symfony\Component\Security\Acl\Dbal\Schema;
16 |
17 | $schema = new Schema([
18 | 'class_table_name' => 'acl_classes',
19 | 'entry_table_name' => 'acl_entries',
20 | 'oid_table_name' => 'acl_object_identities',
21 | 'oid_ancestors_table_name' => 'acl_object_identity_ancestors',
22 | 'sid_table_name' => 'acl_security_identities',
23 | ]);
24 |
25 | $reflection = new ReflectionClass('Doctrine\\DBAL\\Platforms\\AbstractPlatform');
26 | $finder = new Finder();
27 | $finder->name('*Platform.php')->in(dirname($reflection->getFileName()));
28 | foreach ($finder as $file) {
29 | $className = 'Doctrine\\DBAL\\Platforms\\'.$file->getBasename('.php');
30 |
31 | $reflection = new ReflectionClass($className);
32 | if ($reflection->isAbstract()) {
33 | continue;
34 | }
35 |
36 | $platform = $reflection->newInstance();
37 | $targetFile = sprintf(__DIR__.'/../schema/%s.sql', $platform->getName());
38 | file_put_contents($targetFile, implode("\n\n", $schema->toSql($platform)));
39 | }
40 |
--------------------------------------------------------------------------------
/Resources/schema/db2.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE acl_classes (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY(id))
2 |
3 | CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type)
4 |
5 | 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))
6 |
7 | CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username)
8 |
9 | COMMENT ON COLUMN acl_security_identities.username IS '(DC2Type:boolean)'
10 |
11 | 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))
12 |
13 | CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id)
14 |
15 | CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
16 |
17 | COMMENT ON COLUMN acl_object_identities.entries_inheriting IS '(DC2Type:boolean)'
18 |
19 | CREATE TABLE acl_object_identity_ancestors (object_identity_id INTEGER NOT NULL, ancestor_id INTEGER NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id))
20 |
21 | CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
22 |
23 | CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
24 |
25 | 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))
26 |
27 | CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order)
28 |
29 | CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id)
30 |
31 | CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id)
32 |
33 | CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
34 |
35 | CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
36 |
37 | COMMENT ON COLUMN acl_entries.granting IS '(DC2Type:boolean)'
38 |
39 | COMMENT ON COLUMN acl_entries.audit_success IS '(DC2Type:boolean)'
40 |
41 | COMMENT ON COLUMN acl_entries.audit_failure IS '(DC2Type:boolean)'
42 |
43 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
44 |
45 | 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
46 |
47 | 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
48 |
49 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
50 |
51 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
52 |
53 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
--------------------------------------------------------------------------------
/Resources/schema/drizzle.sql:
--------------------------------------------------------------------------------
1 | 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
2 |
3 | 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
4 |
5 | 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
6 |
7 | 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
8 |
9 | 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
10 |
11 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
12 |
13 | 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
14 |
15 | 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
16 |
17 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
18 |
19 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
20 |
21 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
--------------------------------------------------------------------------------
/Resources/schema/mssql.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE acl_classes (id INT IDENTITY NOT NULL, class_type NVARCHAR(200) NOT NULL, PRIMARY KEY (id))
2 |
3 | CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type) WHERE class_type IS NOT NULL
4 |
5 | CREATE TABLE acl_security_identities (id INT IDENTITY NOT NULL, identifier NVARCHAR(200) NOT NULL, username BIT NOT NULL, PRIMARY KEY (id))
6 |
7 | CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username) WHERE identifier IS NOT NULL AND username IS NOT NULL
8 |
9 | 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))
10 |
11 | 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
12 |
13 | CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
14 |
15 | CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, PRIMARY KEY (object_identity_id, ancestor_id))
16 |
17 | CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
18 |
19 | CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
20 |
21 | 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))
22 |
23 | 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
24 |
25 | CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id)
26 |
27 | CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id)
28 |
29 | CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
30 |
31 | CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
32 |
33 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
34 |
35 | 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
36 |
37 | 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
38 |
39 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
40 |
41 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
42 |
43 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
--------------------------------------------------------------------------------
/Resources/schema/mysql.sql:
--------------------------------------------------------------------------------
1 | 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
2 |
3 | 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
4 |
5 | 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
6 |
7 | 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
8 |
9 | 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
10 |
11 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
12 |
13 | 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
14 |
15 | 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
16 |
17 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
18 |
19 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
20 |
21 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
--------------------------------------------------------------------------------
/Resources/schema/oracle.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE acl_classes (id NUMBER(10) NOT NULL, class_type VARCHAR2(200) NOT NULL, PRIMARY KEY(id))
2 |
3 | DECLARE
4 | constraints_Count NUMBER;
5 | BEGIN
6 | SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_CLASSES' AND CONSTRAINT_TYPE = 'P';
7 | IF constraints_Count = 0 OR constraints_Count = '' THEN
8 | EXECUTE IMMEDIATE 'ALTER TABLE ACL_CLASSES ADD CONSTRAINT ACL_CLASSES_AI_PK PRIMARY KEY (ID)';
9 | END IF;
10 | END;
11 |
12 | CREATE SEQUENCE ACL_CLASSES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1
13 |
14 | CREATE TRIGGER ACL_CLASSES_AI_PK
15 | BEFORE INSERT
16 | ON ACL_CLASSES
17 | FOR EACH ROW
18 | DECLARE
19 | last_Sequence NUMBER;
20 | last_InsertID NUMBER;
21 | BEGIN
22 | SELECT ACL_CLASSES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
23 | IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN
24 | SELECT ACL_CLASSES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
25 | ELSE
26 | SELECT NVL(Last_Number, 0) INTO last_Sequence
27 | FROM User_Sequences
28 | WHERE Sequence_Name = 'ACL_CLASSES_SEQ';
29 | SELECT :NEW.ID INTO last_InsertID FROM DUAL;
30 | WHILE (last_InsertID > last_Sequence) LOOP
31 | SELECT ACL_CLASSES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL;
32 | END LOOP;
33 | END IF;
34 | END;
35 |
36 | CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type)
37 |
38 | CREATE TABLE acl_security_identities (id NUMBER(10) NOT NULL, identifier VARCHAR2(200) NOT NULL, username NUMBER(1) NOT NULL, PRIMARY KEY(id))
39 |
40 | DECLARE
41 | constraints_Count NUMBER;
42 | BEGIN
43 | SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_SECURITY_IDENTITIES' AND CONSTRAINT_TYPE = 'P';
44 | IF constraints_Count = 0 OR constraints_Count = '' THEN
45 | EXECUTE IMMEDIATE 'ALTER TABLE ACL_SECURITY_IDENTITIES ADD CONSTRAINT ACL_SECURITY_IDENTITIES_AI_PK PRIMARY KEY (ID)';
46 | END IF;
47 | END;
48 |
49 | CREATE SEQUENCE ACL_SECURITY_IDENTITIES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1
50 |
51 | CREATE TRIGGER ACL_SECURITY_IDENTITIES_AI_PK
52 | BEFORE INSERT
53 | ON ACL_SECURITY_IDENTITIES
54 | FOR EACH ROW
55 | DECLARE
56 | last_Sequence NUMBER;
57 | last_InsertID NUMBER;
58 | BEGIN
59 | SELECT ACL_SECURITY_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
60 | IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN
61 | SELECT ACL_SECURITY_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
62 | ELSE
63 | SELECT NVL(Last_Number, 0) INTO last_Sequence
64 | FROM User_Sequences
65 | WHERE Sequence_Name = 'ACL_SECURITY_IDENTITIES_SEQ';
66 | SELECT :NEW.ID INTO last_InsertID FROM DUAL;
67 | WHILE (last_InsertID > last_Sequence) LOOP
68 | SELECT ACL_SECURITY_IDENTITIES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL;
69 | END LOOP;
70 | END IF;
71 | END;
72 |
73 | CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username)
74 |
75 | 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))
76 |
77 | DECLARE
78 | constraints_Count NUMBER;
79 | BEGIN
80 | SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_OBJECT_IDENTITIES' AND CONSTRAINT_TYPE = 'P';
81 | IF constraints_Count = 0 OR constraints_Count = '' THEN
82 | EXECUTE IMMEDIATE 'ALTER TABLE ACL_OBJECT_IDENTITIES ADD CONSTRAINT ACL_OBJECT_IDENTITIES_AI_PK PRIMARY KEY (ID)';
83 | END IF;
84 | END;
85 |
86 | CREATE SEQUENCE ACL_OBJECT_IDENTITIES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1
87 |
88 | CREATE TRIGGER ACL_OBJECT_IDENTITIES_AI_PK
89 | BEFORE INSERT
90 | ON ACL_OBJECT_IDENTITIES
91 | FOR EACH ROW
92 | DECLARE
93 | last_Sequence NUMBER;
94 | last_InsertID NUMBER;
95 | BEGIN
96 | SELECT ACL_OBJECT_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
97 | IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN
98 | SELECT ACL_OBJECT_IDENTITIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
99 | ELSE
100 | SELECT NVL(Last_Number, 0) INTO last_Sequence
101 | FROM User_Sequences
102 | WHERE Sequence_Name = 'ACL_OBJECT_IDENTITIES_SEQ';
103 | SELECT :NEW.ID INTO last_InsertID FROM DUAL;
104 | WHILE (last_InsertID > last_Sequence) LOOP
105 | SELECT ACL_OBJECT_IDENTITIES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL;
106 | END LOOP;
107 | END IF;
108 | END;
109 |
110 | CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id)
111 |
112 | CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
113 |
114 | 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))
115 |
116 | CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
117 |
118 | CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
119 |
120 | 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))
121 |
122 | DECLARE
123 | constraints_Count NUMBER;
124 | BEGIN
125 | SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ACL_ENTRIES' AND CONSTRAINT_TYPE = 'P';
126 | IF constraints_Count = 0 OR constraints_Count = '' THEN
127 | EXECUTE IMMEDIATE 'ALTER TABLE ACL_ENTRIES ADD CONSTRAINT ACL_ENTRIES_AI_PK PRIMARY KEY (ID)';
128 | END IF;
129 | END;
130 |
131 | CREATE SEQUENCE ACL_ENTRIES_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1
132 |
133 | CREATE TRIGGER ACL_ENTRIES_AI_PK
134 | BEFORE INSERT
135 | ON ACL_ENTRIES
136 | FOR EACH ROW
137 | DECLARE
138 | last_Sequence NUMBER;
139 | last_InsertID NUMBER;
140 | BEGIN
141 | SELECT ACL_ENTRIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
142 | IF (:NEW.ID IS NULL OR :NEW.ID = 0) THEN
143 | SELECT ACL_ENTRIES_SEQ.NEXTVAL INTO :NEW.ID FROM DUAL;
144 | ELSE
145 | SELECT NVL(Last_Number, 0) INTO last_Sequence
146 | FROM User_Sequences
147 | WHERE Sequence_Name = 'ACL_ENTRIES_SEQ';
148 | SELECT :NEW.ID INTO last_InsertID FROM DUAL;
149 | WHILE (last_InsertID > last_Sequence) LOOP
150 | SELECT ACL_ENTRIES_SEQ.NEXTVAL INTO last_Sequence FROM DUAL;
151 | END LOOP;
152 | END IF;
153 | END;
154 |
155 | CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order)
156 |
157 | CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id)
158 |
159 | CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id)
160 |
161 | CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
162 |
163 | CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
164 |
165 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
166 |
167 | ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE2993D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE
168 |
169 | ALTER TABLE acl_object_identity_ancestors ADD CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE
170 |
171 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON DELETE CASCADE
172 |
173 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON DELETE CASCADE
174 |
175 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON DELETE CASCADE
--------------------------------------------------------------------------------
/Resources/schema/postgresql.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE acl_classes (id SERIAL NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY(id))
2 |
3 | CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type)
4 |
5 | CREATE TABLE acl_security_identities (id SERIAL NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL, PRIMARY KEY(id))
6 |
7 | CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username)
8 |
9 | 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))
10 |
11 | CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id)
12 |
13 | CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
14 |
15 | CREATE TABLE acl_object_identity_ancestors (object_identity_id INT NOT NULL, ancestor_id INT NOT NULL, PRIMARY KEY(object_identity_id, ancestor_id))
16 |
17 | CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
18 |
19 | CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
20 |
21 | 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))
22 |
23 | CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order)
24 |
25 | CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id)
26 |
27 | CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id)
28 |
29 | CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
30 |
31 | CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
32 |
33 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) NOT DEFERRABLE INITIALLY IMMEDIATE
34 |
35 | 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
36 |
37 | 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
38 |
39 | 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
40 |
41 | 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
42 |
43 | 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
--------------------------------------------------------------------------------
/Resources/schema/sqlanywhere.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE acl_classes (id UNSIGNED INT IDENTITY NOT NULL, class_type VARCHAR(200) NOT NULL, PRIMARY KEY (id))
2 |
3 | CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type)
4 |
5 | CREATE TABLE acl_security_identities (id UNSIGNED INT IDENTITY NOT NULL, identifier VARCHAR(200) NOT NULL, username BIT NOT NULL, PRIMARY KEY (id))
6 |
7 | CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username)
8 |
9 | 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))
10 |
11 | CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id)
12 |
13 | CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
14 |
15 | 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))
16 |
17 | CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
18 |
19 | CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
20 |
21 | 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))
22 |
23 | CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order)
24 |
25 | CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id)
26 |
27 | CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id)
28 |
29 | CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
30 |
31 | CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
32 |
33 | ALTER TABLE acl_object_identities ADD CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id)
34 |
35 | 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
36 |
37 | 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
38 |
39 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE
40 |
41 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
42 |
43 | ALTER TABLE acl_entries ADD CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE
--------------------------------------------------------------------------------
/Resources/schema/sqlite.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE acl_classes (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, class_type VARCHAR(200) NOT NULL)
2 |
3 | CREATE UNIQUE INDEX UNIQ_69DD750638A36066 ON acl_classes (class_type)
4 |
5 | CREATE TABLE acl_security_identities (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, identifier VARCHAR(200) NOT NULL, username BOOLEAN NOT NULL)
6 |
7 | CREATE UNIQUE INDEX UNIQ_8835EE78772E836AF85E0677 ON acl_security_identities (identifier, username)
8 |
9 | CREATE TABLE acl_object_identities (id INTEGER PRIMARY KEY AUTOINCREMENT 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, CONSTRAINT FK_9407E54977FA751A FOREIGN KEY (parent_object_identity_id) REFERENCES acl_object_identities (id) NOT DEFERRABLE INITIALLY IMMEDIATE)
10 |
11 | CREATE UNIQUE INDEX UNIQ_9407E5494B12AD6EA000B10 ON acl_object_identities (object_identifier, class_id)
12 |
13 | CREATE INDEX IDX_9407E54977FA751A ON acl_object_identities (parent_object_identity_id)
14 |
15 | 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 UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_825DE299C671CEA1 FOREIGN KEY (ancestor_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)
16 |
17 | CREATE INDEX IDX_825DE2993D9AB4A6 ON acl_object_identity_ancestors (object_identity_id)
18 |
19 | CREATE INDEX IDX_825DE299C671CEA1 ON acl_object_identity_ancestors (ancestor_id)
20 |
21 | CREATE TABLE acl_entries (id INTEGER PRIMARY KEY AUTOINCREMENT 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, CONSTRAINT FK_46C8B806EA000B10 FOREIGN KEY (class_id) REFERENCES acl_classes (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_46C8B8063D9AB4A6 FOREIGN KEY (object_identity_id) REFERENCES acl_object_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_46C8B806DF9183C9 FOREIGN KEY (security_identity_id) REFERENCES acl_security_identities (id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)
22 |
23 | CREATE UNIQUE INDEX UNIQ_46C8B806EA000B103D9AB4A64DEF17BCE4289BF4 ON acl_entries (class_id, object_identity_id, field_name, ace_order)
24 |
25 | CREATE INDEX IDX_46C8B806EA000B103D9AB4A6DF9183C9 ON acl_entries (class_id, object_identity_id, security_identity_id)
26 |
27 | CREATE INDEX IDX_46C8B806EA000B10 ON acl_entries (class_id)
28 |
29 | CREATE INDEX IDX_46C8B8063D9AB4A6 ON acl_entries (object_identity_id)
30 |
31 | CREATE INDEX IDX_46C8B806DF9183C9 ON acl_entries (security_identity_id)
--------------------------------------------------------------------------------
/Tests/Dbal/AclProviderBenchmarkTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Dbal;
13 |
14 | use Doctrine\DBAL\Connection;
15 | use Doctrine\DBAL\DriverManager;
16 | use PHPUnit\Framework\TestCase;
17 | use Symfony\Component\Security\Acl\Dbal\AclProvider;
18 | use Symfony\Component\Security\Acl\Dbal\Schema;
19 | use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
20 | use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
21 |
22 | /**
23 | * @group benchmark
24 | */
25 | class AclProviderBenchmarkTest extends TestCase
26 | {
27 | /** @var Connection */
28 | protected $connection;
29 | protected $insertClassStmt;
30 | protected $insertSidStmt;
31 | protected $insertOidAncestorStmt;
32 | protected $insertOidStmt;
33 | protected $insertEntryStmt;
34 |
35 | protected function setUp(): void
36 | {
37 | try {
38 | $this->connection = DriverManager::getConnection([
39 | 'driver' => 'pdo_mysql',
40 | 'host' => 'localhost',
41 | 'user' => 'root',
42 | 'dbname' => 'testdb',
43 | ]);
44 | $this->connection->connect();
45 | } catch (\Exception $e) {
46 | $this->markTestSkipped('Unable to connect to the database: '.$e->getMessage());
47 | }
48 | }
49 |
50 | protected function tearDown(): void
51 | {
52 | $this->connection = null;
53 | }
54 |
55 | public function testFindAcls()
56 | {
57 | // $this->generateTestData();
58 |
59 | // get some random test object identities from the database
60 | $oids = [];
61 | $stmt = $this->connection->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');
62 | foreach ($stmt->fetchAllAssociative() as $oid) {
63 | $oids[] = new ObjectIdentity($oid['object_identifier'], $oid['class_type']);
64 | }
65 |
66 | $provider = $this->getProvider();
67 |
68 | $start = microtime(true);
69 | $provider->findAcls($oids);
70 | $time = microtime(true) - $start;
71 | echo 'Total Time: '.$time."s\n";
72 | }
73 |
74 | /**
75 | * This generates a huge amount of test data to be used mainly for benchmarking
76 | * purposes, not so much for testing. That's why it's not called by default.
77 | */
78 | protected function generateTestData()
79 | {
80 | $sm = $this->connection->createSchemaManager();
81 | $sm->dropDatabase('testdb');
82 | $sm->createDatabase('testdb');
83 | $this->connection->executeStatement('USE testdb');
84 |
85 | // import the schema
86 | $schema = new Schema($options = $this->getOptions());
87 | foreach ($schema->toSql($this->connection->getDatabasePlatform()) as $sql) {
88 | $this->connection->executeStatement($sql);
89 | }
90 |
91 | // setup prepared statements
92 | $this->insertClassStmt = $this->connection->prepare('INSERT INTO acl_classes (id, class_type) VALUES (?, ?)');
93 | $this->insertSidStmt = $this->connection->prepare('INSERT INTO acl_security_identities (id, identifier, username) VALUES (?, ?, ?)');
94 | $this->insertOidStmt = $this->connection->prepare('INSERT INTO acl_object_identities (id, class_id, object_identifier, parent_object_identity_id, entries_inheriting) VALUES (?, ?, ?, ?, ?)');
95 | $this->insertEntryStmt = $this->connection->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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
96 | $this->insertOidAncestorStmt = $this->connection->prepare('INSERT INTO acl_object_identity_ancestors (object_identity_id, ancestor_id) VALUES (?, ?)');
97 |
98 | for ($i = 0; $i < 40000; ++$i) {
99 | $this->generateAclHierarchy();
100 | }
101 | }
102 |
103 | protected function generateAclHierarchy()
104 | {
105 | $rootId = $this->generateAcl($this->chooseClassId(), null, []);
106 |
107 | $this->generateAclLevel(rand(1, 15), $rootId, [$rootId]);
108 | }
109 |
110 | protected function generateAclLevel($depth, $parentId, $ancestors)
111 | {
112 | $level = \count($ancestors);
113 | for ($i = 0, $t = rand(1, 10); $i < $t; ++$i) {
114 | $id = $this->generateAcl($this->chooseClassId(), $parentId, $ancestors);
115 |
116 | if ($level < $depth) {
117 | $this->generateAclLevel($depth, $id, array_merge($ancestors, [$id]));
118 | }
119 | }
120 | }
121 |
122 | protected function chooseClassId()
123 | {
124 | static $id = 1000;
125 |
126 | if (1000 === $id || ($id < 1500 && rand(0, 1))) {
127 | $this->insertClassStmt->executeStatement([$id, $this->getRandomString(rand(20, 100), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\_')]);
128 | ++$id;
129 |
130 | return $id - 1;
131 | } else {
132 | return rand(1000, $id - 1);
133 | }
134 | }
135 |
136 | protected function generateAcl($classId, $parentId, $ancestors)
137 | {
138 | static $id = 1000;
139 |
140 | $this->insertOidStmt->executeStatement([
141 | $id,
142 | $classId,
143 | $this->getRandomString(rand(20, 50)),
144 | $parentId,
145 | rand(0, 1),
146 | ]);
147 |
148 | $this->insertOidAncestorStmt->executeStatement([$id, $id]);
149 | foreach ($ancestors as $ancestor) {
150 | $this->insertOidAncestorStmt->executeStatement([$id, $ancestor]);
151 | }
152 |
153 | $this->generateAces($classId, $id);
154 | ++$id;
155 |
156 | return $id - 1;
157 | }
158 |
159 | protected function chooseSid()
160 | {
161 | static $id = 1000;
162 |
163 | if (1000 === $id || ($id < 11000 && rand(0, 1))) {
164 | $this->insertSidStmt->executeStatement([
165 | $id,
166 | $this->getRandomString(rand(5, 30)),
167 | rand(0, 1),
168 | ]);
169 | ++$id;
170 |
171 | return $id - 1;
172 | } else {
173 | return rand(1000, $id - 1);
174 | }
175 | }
176 |
177 | protected function generateAces($classId, $objectId)
178 | {
179 | static $id = 1000;
180 |
181 | $sids = [];
182 | $fieldOrder = [];
183 |
184 | for ($i = 0; $i <= 30; ++$i) {
185 | $fieldName = rand(0, 1) ? null : $this->getRandomString(rand(10, 20));
186 |
187 | do {
188 | $sid = $this->chooseSid();
189 | } while (\array_key_exists($sid, $sids) && \in_array($fieldName, $sids[$sid], true));
190 |
191 | $fieldOrder[$fieldName] = \array_key_exists($fieldName, $fieldOrder) ? $fieldOrder[$fieldName] + 1 : 0;
192 | if (!isset($sids[$sid])) {
193 | $sids[$sid] = [];
194 | }
195 | $sids[$sid][] = $fieldName;
196 |
197 | $strategy = rand(0, 2);
198 | if (0 === $strategy) {
199 | $strategy = PermissionGrantingStrategy::ALL;
200 | } elseif (1 === $strategy) {
201 | $strategy = PermissionGrantingStrategy::ANY;
202 | } else {
203 | $strategy = PermissionGrantingStrategy::EQUAL;
204 | }
205 |
206 | // id, cid, oid, field, order, sid, mask, granting, strategy, a success, a failure
207 | $this->insertEntryStmt->executeStatement([
208 | $id,
209 | $classId,
210 | rand(0, 5) ? $objectId : null,
211 | $fieldName,
212 | $fieldOrder[$fieldName],
213 | $sid,
214 | $this->generateMask(),
215 | rand(0, 1),
216 | $strategy,
217 | rand(0, 1),
218 | rand(0, 1),
219 | ]);
220 |
221 | ++$id;
222 | }
223 | }
224 |
225 | protected function generateMask()
226 | {
227 | $i = rand(1, 30);
228 | $mask = 0;
229 |
230 | while ($i <= 30) {
231 | $mask |= 1 << rand(0, 30);
232 | ++$i;
233 | }
234 |
235 | return $mask;
236 | }
237 |
238 | protected function getRandomString($length, $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
239 | {
240 | $s = '';
241 | $cLength = \strlen($chars);
242 |
243 | while (\strlen($s) < $length) {
244 | $s .= $chars[mt_rand(0, $cLength - 1)];
245 | }
246 |
247 | return $s;
248 | }
249 |
250 | protected function getOptions()
251 | {
252 | return [
253 | 'oid_table_name' => 'acl_object_identities',
254 | 'oid_ancestors_table_name' => 'acl_object_identity_ancestors',
255 | 'class_table_name' => 'acl_classes',
256 | 'sid_table_name' => 'acl_security_identities',
257 | 'entry_table_name' => 'acl_entries',
258 | ];
259 | }
260 |
261 | protected function getStrategy()
262 | {
263 | return new PermissionGrantingStrategy();
264 | }
265 |
266 | protected function getProvider()
267 | {
268 | return new AclProvider($this->connection, $this->getStrategy(), $this->getOptions());
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/Tests/Domain/AuditLoggerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Security\Acl\Domain\AuditLogger;
16 | use Symfony\Component\Security\Acl\Tests\Fixtures\SerializableAuditableEntryInterface;
17 |
18 | class AuditLoggerTest extends TestCase
19 | {
20 | /**
21 | * @dataProvider getTestLogData
22 | */
23 | public function testLogIfNeeded($granting, $audit)
24 | {
25 | $logger = $this->getLogger();
26 | $ace = $this->getEntry();
27 |
28 | if (true === $granting) {
29 | $ace
30 | ->expects($this->once())
31 | ->method('isAuditSuccess')
32 | ->willReturn($audit)
33 | ;
34 |
35 | $ace
36 | ->expects($this->never())
37 | ->method('isAuditFailure')
38 | ;
39 | } else {
40 | $ace
41 | ->expects($this->never())
42 | ->method('isAuditSuccess')
43 | ;
44 |
45 | $ace
46 | ->expects($this->once())
47 | ->method('isAuditFailure')
48 | ->willReturn($audit)
49 | ;
50 | }
51 |
52 | if (true === $audit) {
53 | $logger
54 | ->expects($this->once())
55 | ->method('doLog')
56 | ->with($this->equalTo($granting), $this->equalTo($ace))
57 | ;
58 | } else {
59 | $logger
60 | ->expects($this->never())
61 | ->method('doLog')
62 | ;
63 | }
64 |
65 | $logger->logIfNeeded($granting, $ace);
66 | }
67 |
68 | public function getTestLogData()
69 | {
70 | return [
71 | [true, false],
72 | [true, true],
73 | [false, false],
74 | [false, true],
75 | ];
76 | }
77 |
78 | protected function getEntry()
79 | {
80 | return $this->createMock(SerializableAuditableEntryInterface::class);
81 | }
82 |
83 | protected function getLogger()
84 | {
85 | return $this->getMockForAbstractClass(AuditLogger::class);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Tests/Domain/DoctrineAclCacheTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use Doctrine\Common\Cache\Psr6\DoctrineProvider;
15 | use PHPUnit\Framework\TestCase;
16 | use Symfony\Component\Cache\Adapter\ArrayAdapter;
17 | use Symfony\Component\Security\Acl\Domain\Acl;
18 | use Symfony\Component\Security\Acl\Domain\DoctrineAclCache;
19 | use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
20 | use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
21 | use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
22 |
23 | class DoctrineAclCacheTest extends TestCase
24 | {
25 | protected $permissionGrantingStrategy;
26 |
27 | /**
28 | * @dataProvider getEmptyValue
29 | */
30 | public function testConstructorDoesNotAcceptEmptyPrefix($empty)
31 | {
32 | $this->expectException(\InvalidArgumentException::class);
33 |
34 | new DoctrineAclCache(DoctrineProvider::wrap(new ArrayAdapter()), $this->getPermissionGrantingStrategy(), $empty);
35 | }
36 |
37 | public function getEmptyValue()
38 | {
39 | return [
40 | [null],
41 | [false],
42 | [''],
43 | ];
44 | }
45 |
46 | public function test()
47 | {
48 | $cache = $this->getCache();
49 |
50 | $aclWithParent = $this->getAcl(1);
51 | $acl = $this->getAcl();
52 |
53 | $cache->putInCache($aclWithParent);
54 | $cache->putInCache($acl);
55 |
56 | $cachedAcl = $cache->getFromCacheByIdentity($acl->getObjectIdentity());
57 | $this->assertEquals($acl->getId(), $cachedAcl->getId());
58 | $this->assertNull($acl->getParentAcl());
59 |
60 | $cachedAclWithParent = $cache->getFromCacheByIdentity($aclWithParent->getObjectIdentity());
61 | $this->assertEquals($aclWithParent->getId(), $cachedAclWithParent->getId());
62 | $this->assertNotNull($cachedParentAcl = $cachedAclWithParent->getParentAcl());
63 | $this->assertEquals($aclWithParent->getParentAcl()->getId(), $cachedParentAcl->getId());
64 | }
65 |
66 | protected function getAcl($depth = 0)
67 | {
68 | static $id = 1;
69 |
70 | $acl = new Acl($id, new ObjectIdentity($id, 'foo'), $this->getPermissionGrantingStrategy(), [], $depth > 0);
71 |
72 | // insert some ACEs
73 | $sid = new UserSecurityIdentity('johannes', 'Foo');
74 | $acl->insertClassAce($sid, 1);
75 | $acl->insertClassFieldAce('foo', $sid, 1);
76 | $acl->insertObjectAce($sid, 1);
77 | $acl->insertObjectFieldAce('foo', $sid, 1);
78 | ++$id;
79 |
80 | if ($depth > 0) {
81 | $acl->setParentAcl($this->getAcl($depth - 1));
82 | }
83 |
84 | return $acl;
85 | }
86 |
87 | protected function getPermissionGrantingStrategy()
88 | {
89 | if (null === $this->permissionGrantingStrategy) {
90 | $this->permissionGrantingStrategy = new PermissionGrantingStrategy();
91 | }
92 |
93 | return $this->permissionGrantingStrategy;
94 | }
95 |
96 | protected function getCache($cacheDriver = null, $prefix = DoctrineAclCache::PREFIX)
97 | {
98 | if (null === $cacheDriver) {
99 | $cacheDriver = DoctrineProvider::wrap(new ArrayAdapter());
100 | }
101 |
102 | return new DoctrineAclCache($cacheDriver, $this->getPermissionGrantingStrategy(), $prefix);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Tests/Domain/EntryTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Security\Acl\Domain\Entry;
16 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
17 | use Symfony\Component\Security\Acl\Tests\Fixtures\SerializableAclInterface;
18 |
19 | class EntryTest extends TestCase
20 | {
21 | public function testConstructor()
22 | {
23 | $ace = $this->getAce($acl = $this->getAcl(), $sid = $this->getSid());
24 |
25 | $this->assertEquals(123, $ace->getId());
26 | $this->assertSame($acl, $ace->getAcl());
27 | $this->assertSame($sid, $ace->getSecurityIdentity());
28 | $this->assertEquals('foostrat', $ace->getStrategy());
29 | $this->assertEquals(123456, $ace->getMask());
30 | $this->assertTrue($ace->isGranting());
31 | $this->assertTrue($ace->isAuditSuccess());
32 | $this->assertFalse($ace->isAuditFailure());
33 | }
34 |
35 | public function testSetAuditSuccess()
36 | {
37 | $ace = $this->getAce();
38 |
39 | $this->assertTrue($ace->isAuditSuccess());
40 | $ace->setAuditSuccess(false);
41 | $this->assertFalse($ace->isAuditSuccess());
42 | $ace->setAuditSuccess(true);
43 | $this->assertTrue($ace->isAuditSuccess());
44 | }
45 |
46 | public function testSetAuditFailure()
47 | {
48 | $ace = $this->getAce();
49 |
50 | $this->assertFalse($ace->isAuditFailure());
51 | $ace->setAuditFailure(true);
52 | $this->assertTrue($ace->isAuditFailure());
53 | $ace->setAuditFailure(false);
54 | $this->assertFalse($ace->isAuditFailure());
55 | }
56 |
57 | public function testSetMask()
58 | {
59 | $ace = $this->getAce();
60 |
61 | $this->assertEquals(123456, $ace->getMask());
62 | $ace->setMask(4321);
63 | $this->assertEquals(4321, $ace->getMask());
64 | }
65 |
66 | public function testSetStrategy()
67 | {
68 | $ace = $this->getAce();
69 |
70 | $this->assertEquals('foostrat', $ace->getStrategy());
71 | $ace->setStrategy('foo');
72 | $this->assertEquals('foo', $ace->getStrategy());
73 | }
74 |
75 | public function testSerializeUnserialize()
76 | {
77 | $ace = $this->getAce();
78 |
79 | $serialized = serialize($ace);
80 | $uAce = unserialize($serialized);
81 |
82 | $this->assertNull($uAce->getAcl());
83 | $this->assertInstanceOf(SecurityIdentityInterface::class, $uAce->getSecurityIdentity());
84 | $this->assertEquals($ace->getId(), $uAce->getId());
85 | $this->assertEquals($ace->getMask(), $uAce->getMask());
86 | $this->assertEquals($ace->getStrategy(), $uAce->getStrategy());
87 | $this->assertEquals($ace->isGranting(), $uAce->isGranting());
88 | $this->assertEquals($ace->isAuditSuccess(), $uAce->isAuditSuccess());
89 | $this->assertEquals($ace->isAuditFailure(), $uAce->isAuditFailure());
90 | }
91 |
92 | protected function getAce($acl = null, $sid = null)
93 | {
94 | if (null === $acl) {
95 | $acl = $this->getAcl();
96 | }
97 | if (null === $sid) {
98 | $sid = $this->getSid();
99 | }
100 |
101 | return new Entry(
102 | 123,
103 | $acl,
104 | $sid,
105 | 'foostrat',
106 | 123456,
107 | true,
108 | false,
109 | true
110 | );
111 | }
112 |
113 | protected function getAcl()
114 | {
115 | return $this->createMock(SerializableAclInterface::class);
116 | }
117 |
118 | protected function getSid()
119 | {
120 | return $this->createMock(SecurityIdentityInterface::class);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Tests/Domain/FieldEntryTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Security\Acl\Domain\FieldEntry;
16 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
17 | use Symfony\Component\Security\Acl\Tests\Fixtures\SerializableAclInterface;
18 |
19 | class FieldEntryTest extends TestCase
20 | {
21 | public function testConstructor()
22 | {
23 | $ace = $this->getAce();
24 |
25 | $this->assertEquals('foo', $ace->getField());
26 | }
27 |
28 | public function testSerializeUnserialize()
29 | {
30 | $ace = $this->getAce();
31 |
32 | $serialized = serialize($ace);
33 | $uAce = unserialize($serialized);
34 |
35 | $this->assertNull($uAce->getAcl());
36 | $this->assertInstanceOf(SecurityIdentityInterface::class, $uAce->getSecurityIdentity());
37 | $this->assertEquals($ace->getId(), $uAce->getId());
38 | $this->assertEquals($ace->getField(), $uAce->getField());
39 | $this->assertEquals($ace->getMask(), $uAce->getMask());
40 | $this->assertEquals($ace->getStrategy(), $uAce->getStrategy());
41 | $this->assertEquals($ace->isGranting(), $uAce->isGranting());
42 | $this->assertEquals($ace->isAuditSuccess(), $uAce->isAuditSuccess());
43 | $this->assertEquals($ace->isAuditFailure(), $uAce->isAuditFailure());
44 | }
45 |
46 | public function testSerializeUnserializeMoreAceWithSameSecurityIdentity()
47 | {
48 | $sid = $this->getSid();
49 |
50 | $aceFirst = $this->getAce(null, $sid);
51 | $aceSecond = $this->getAce(null, $sid);
52 |
53 | // as used in DoctrineAclCache::putInCache (line 142)
54 | $serialized = serialize([[
55 | 'fieldOne' => [$aceFirst],
56 | 'fieldTwo' => [$aceSecond],
57 | ]]);
58 |
59 | $unserialized = unserialize($serialized);
60 | $uAceFirst = $unserialized[0]['fieldOne'][0];
61 | $uAceSecond = $unserialized[0]['fieldTwo'][0];
62 |
63 | $this->assertInstanceOf(SecurityIdentityInterface::class, $uAceFirst->getSecurityIdentity());
64 | $this->assertInstanceOf(SecurityIdentityInterface::class, $uAceSecond->getSecurityIdentity());
65 | }
66 |
67 | protected function getAce($acl = null, $sid = null)
68 | {
69 | if (null === $acl) {
70 | $acl = $this->getAcl();
71 | }
72 | if (null === $sid) {
73 | $sid = $this->getSid();
74 | }
75 |
76 | return new FieldEntry(
77 | 123,
78 | $acl,
79 | 'foo',
80 | $sid,
81 | 'foostrat',
82 | 123456,
83 | true,
84 | false,
85 | true
86 | );
87 | }
88 |
89 | protected function getAcl()
90 | {
91 | return $this->createMock(SerializableAclInterface::class);
92 | }
93 |
94 | protected function getSid()
95 | {
96 | return $this->createMock(SecurityIdentityInterface::class);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Tests/Domain/ObjectIdentityRetrievalStrategyTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Domain\ObjectIdentityRetrievalStrategy;
15 |
16 | class ObjectIdentityRetrievalStrategyTest extends \PHPUnit\Framework\TestCase
17 | {
18 | public function testGetObjectIdentityReturnsNullForInvalidDomainObject()
19 | {
20 | $strategy = new ObjectIdentityRetrievalStrategy();
21 | $this->assertNull($strategy->getObjectIdentity('foo'));
22 | }
23 |
24 | public function testGetObjectIdentity()
25 | {
26 | $strategy = new ObjectIdentityRetrievalStrategy();
27 | $domainObject = new DomainObject();
28 | $objectIdentity = $strategy->getObjectIdentity($domainObject);
29 |
30 | $this->assertEquals($domainObject->getId(), $objectIdentity->getIdentifier());
31 | $this->assertEquals(\get_class($domainObject), $objectIdentity->getType());
32 | }
33 | }
34 |
35 | class DomainObject
36 | {
37 | public function getId()
38 | {
39 | return 'foo';
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Tests/Domain/ObjectIdentityTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain
13 | {
14 | use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
15 | use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
16 |
17 | class ObjectIdentityTest extends \PHPUnit\Framework\TestCase
18 | {
19 | public function testConstructor()
20 | {
21 | $id = new ObjectIdentity('fooid', 'footype');
22 |
23 | $this->assertEquals('fooid', $id->getIdentifier());
24 | $this->assertEquals('footype', $id->getType());
25 | }
26 |
27 | // Test that constructor never changes passed type, even with proxies
28 | public function testConstructorWithProxy()
29 | {
30 | $id = new ObjectIdentity('fooid', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject');
31 |
32 | $this->assertEquals('fooid', $id->getIdentifier());
33 | $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject', $id->getType());
34 | }
35 |
36 | public function testFromDomainObjectPrefersInterfaceOverGetId()
37 | {
38 | $domainObject = new class() implements DomainObjectInterface {
39 | public function getObjectIdentifier()
40 | {
41 | return 'getObjectIdentifier()';
42 | }
43 |
44 | public function getId()
45 | {
46 | return 'getId()';
47 | }
48 | };
49 |
50 | $id = ObjectIdentity::fromDomainObject($domainObject);
51 | $this->assertEquals('getObjectIdentifier()', $id->getIdentifier());
52 | }
53 |
54 | public function testFromDomainObjectWithoutInterface()
55 | {
56 | $id = ObjectIdentity::fromDomainObject(new TestDomainObject());
57 | $this->assertEquals('getId()', $id->getIdentifier());
58 | $this->assertEquals(TestDomainObject::class, $id->getType());
59 | }
60 |
61 | public function testFromDomainObjectWithProxy()
62 | {
63 | $id = ObjectIdentity::fromDomainObject(new \Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject());
64 | $this->assertEquals('getId()', $id->getIdentifier());
65 | $this->assertEquals(TestDomainObject::class, $id->getType());
66 | }
67 |
68 | public function testFromDomainObjectWithoutInterfaceEnforcesStringIdentifier()
69 | {
70 | $domainObject = new TestDomainObject();
71 | $domainObject->id = 1;
72 | $id = ObjectIdentity::fromDomainObject($domainObject);
73 |
74 | $this->assertSame('1', $id->getIdentifier());
75 | $this->assertEquals(TestDomainObject::class, $id->getType());
76 | }
77 |
78 | public function testFromDomainObjectWithoutInterfaceAllowsZeroAsIdentifier()
79 | {
80 | $domainObject = new TestDomainObject();
81 | $domainObject->id = '0';
82 | $id = ObjectIdentity::fromDomainObject($domainObject);
83 |
84 | $this->assertSame('0', $id->getIdentifier());
85 | $this->assertEquals(TestDomainObject::class, $id->getType());
86 | }
87 |
88 | /**
89 | * @dataProvider getCompareData
90 | */
91 | public function testEquals($oid1, $oid2, $equal)
92 | {
93 | if ($equal) {
94 | $this->assertTrue($oid1->equals($oid2));
95 | } else {
96 | $this->assertFalse($oid1->equals($oid2));
97 | }
98 | }
99 |
100 | public function getCompareData()
101 | {
102 | return [
103 | [new ObjectIdentity('123', 'foo'), new ObjectIdentity('123', 'foo'), true],
104 | [new ObjectIdentity('123', 'foo'), new ObjectIdentity(123, 'foo'), true],
105 | [new ObjectIdentity('1', 'foo'), new ObjectIdentity('2', 'foo'), false],
106 | [new ObjectIdentity('1', 'bla'), new ObjectIdentity('1', 'blub'), false],
107 | ];
108 | }
109 | }
110 |
111 | class TestDomainObject
112 | {
113 | public $id = 'getId()';
114 |
115 | public function getObjectIdentifier()
116 | {
117 | return 'getObjectIdentifier()';
118 | }
119 |
120 | public function getId()
121 | {
122 | return $this->id;
123 | }
124 | }
125 | }
126 |
127 | namespace Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain
128 | {
129 | class TestDomainObject extends \Symfony\Component\Security\Acl\Tests\Domain\TestDomainObject
130 | {
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Tests/Domain/PermissionGrantingStrategyTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Security\Acl\Domain\Acl;
16 | use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
17 | use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
18 | use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
19 | use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
20 | use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
21 | use Symfony\Component\Security\Acl\Model\AuditLoggerInterface;
22 |
23 | class PermissionGrantingStrategyTest extends TestCase
24 | {
25 | public function testIsGrantedObjectAcesHavePriority()
26 | {
27 | $strategy = new PermissionGrantingStrategy();
28 | $acl = $this->getAcl($strategy);
29 | $sid = new UserSecurityIdentity('johannes', 'Foo');
30 |
31 | $acl->insertClassAce($sid, 1);
32 | $acl->insertObjectAce($sid, 1, 0, false);
33 | $this->assertFalse($strategy->isGranted($acl, [1], [$sid]));
34 | }
35 |
36 | public function testIsGrantedFallsBackToClassAcesIfNoApplicableObjectAceWasFound()
37 | {
38 | $strategy = new PermissionGrantingStrategy();
39 | $acl = $this->getAcl($strategy);
40 | $sid = new UserSecurityIdentity('johannes', 'Foo');
41 |
42 | $acl->insertClassAce($sid, 1);
43 | $this->assertTrue($strategy->isGranted($acl, [1], [$sid]));
44 | }
45 |
46 | public function testIsGrantedFavorsLocalAcesOverParentAclAces()
47 | {
48 | $strategy = new PermissionGrantingStrategy();
49 | $sid = new UserSecurityIdentity('johannes', 'Foo');
50 |
51 | $acl = $this->getAcl($strategy);
52 | $acl->insertClassAce($sid, 1);
53 |
54 | $parentAcl = $this->getAcl($strategy);
55 | $acl->setParentAcl($parentAcl);
56 | $parentAcl->insertClassAce($sid, 1, 0, false);
57 |
58 | $this->assertTrue($strategy->isGranted($acl, [1], [$sid]));
59 | }
60 |
61 | public function testIsGrantedFallsBackToParentAcesIfNoLocalAcesAreApplicable()
62 | {
63 | $strategy = new PermissionGrantingStrategy();
64 | $sid = new UserSecurityIdentity('johannes', 'Foo');
65 | $anotherSid = new UserSecurityIdentity('ROLE_USER', 'Foo');
66 |
67 | $acl = $this->getAcl($strategy);
68 | $acl->insertClassAce($anotherSid, 1, 0, false);
69 |
70 | $parentAcl = $this->getAcl($strategy);
71 | $acl->setParentAcl($parentAcl);
72 | $parentAcl->insertClassAce($sid, 1);
73 |
74 | $this->assertTrue($strategy->isGranted($acl, [1], [$sid]));
75 | }
76 |
77 | public function testIsGrantedReturnsExceptionIfNoAceIsFound()
78 | {
79 | $strategy = new PermissionGrantingStrategy();
80 | $acl = $this->getAcl($strategy);
81 | $sid = new UserSecurityIdentity('johannes', 'Foo');
82 |
83 | $this->expectException(NoAceFoundException::class);
84 | $strategy->isGranted($acl, [1], [$sid]);
85 | }
86 |
87 | public function testIsGrantedFirstApplicableEntryMakesUltimateDecisionForPermissionIdentityCombination()
88 | {
89 | $strategy = new PermissionGrantingStrategy();
90 | $acl = $this->getAcl($strategy);
91 | $sid = new UserSecurityIdentity('johannes', 'Foo');
92 | $aSid = new RoleSecurityIdentity('ROLE_USER');
93 |
94 | $acl->insertClassAce($aSid, 1);
95 | $acl->insertClassAce($sid, 1, 1, false);
96 | $acl->insertClassAce($sid, 1, 2);
97 | $this->assertFalse($strategy->isGranted($acl, [1], [$sid, $aSid]));
98 |
99 | $acl->insertObjectAce($sid, 1, 0, false);
100 | $acl->insertObjectAce($aSid, 1, 1);
101 | $this->assertFalse($strategy->isGranted($acl, [1], [$sid, $aSid]));
102 | }
103 |
104 | public function testIsGrantedCallsAuditLoggerOnGrant()
105 | {
106 | $strategy = new PermissionGrantingStrategy();
107 | $acl = $this->getAcl($strategy);
108 | $sid = new UserSecurityIdentity('johannes', 'Foo');
109 |
110 | $logger = $this->createMock(AuditLoggerInterface::class);
111 | $logger
112 | ->expects($this->once())
113 | ->method('logIfNeeded')
114 | ;
115 | $strategy->setAuditLogger($logger);
116 |
117 | $acl->insertObjectAce($sid, 1);
118 | $acl->updateObjectAuditing(0, true, false);
119 |
120 | $this->assertTrue($strategy->isGranted($acl, [1], [$sid]));
121 | }
122 |
123 | public function testIsGrantedCallsAuditLoggerOnDeny()
124 | {
125 | $strategy = new PermissionGrantingStrategy();
126 | $acl = $this->getAcl($strategy);
127 | $sid = new UserSecurityIdentity('johannes', 'Foo');
128 |
129 | $logger = $this->createMock(AuditLoggerInterface::class);
130 | $logger
131 | ->expects($this->once())
132 | ->method('logIfNeeded')
133 | ;
134 | $strategy->setAuditLogger($logger);
135 |
136 | $acl->insertObjectAce($sid, 1, 0, false);
137 | $acl->updateObjectAuditing(0, false, true);
138 |
139 | $this->assertFalse($strategy->isGranted($acl, [1], [$sid]));
140 | }
141 |
142 | /**
143 | * @dataProvider getAllStrategyTests
144 | */
145 | public function testIsGrantedStrategies($maskStrategy, $aceMask, $requiredMask, $result)
146 | {
147 | $strategy = new PermissionGrantingStrategy();
148 | $acl = $this->getAcl($strategy);
149 | $sid = new UserSecurityIdentity('johannes', 'Foo');
150 |
151 | $acl->insertObjectAce($sid, $aceMask, 0, true, $maskStrategy);
152 |
153 | if (false === $result) {
154 | $this->expectException(NoAceFoundException::class);
155 | $this->expectExceptionMessage('No applicable ACE was found.');
156 |
157 | $strategy->isGranted($acl, [$requiredMask], [$sid]);
158 | } else {
159 | $this->assertTrue($strategy->isGranted($acl, [$requiredMask], [$sid]));
160 | }
161 | }
162 |
163 | public function getAllStrategyTests()
164 | {
165 | return [
166 | ['all', 1 << 0 | 1 << 1, 1 << 0, true],
167 | ['all', 1 << 0 | 1 << 1, 1 << 2, false],
168 | ['all', 1 << 0 | 1 << 10, 1 << 0 | 1 << 10, true],
169 | ['all', 1 << 0 | 1 << 1, 1 << 0 | 1 << 1 || 1 << 2, false],
170 | ['any', 1 << 0 | 1 << 1, 1 << 0, true],
171 | ['any', 1 << 0 | 1 << 1, 1 << 0 | 1 << 2, true],
172 | ['any', 1 << 0 | 1 << 1, 1 << 2, false],
173 | ['equal', 1 << 0 | 1 << 1, 1 << 0, false],
174 | ['equal', 1 << 0 | 1 << 1, 1 << 1, false],
175 | ['equal', 1 << 0 | 1 << 1, 1 << 0 | 1 << 1, true],
176 | ];
177 | }
178 |
179 | protected function getAcl($strategy)
180 | {
181 | static $id = 1;
182 |
183 | return new Acl($id++, new ObjectIdentity(1, 'Foo'), $strategy, [], true);
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/Tests/Domain/PsrAclCacheTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Cache\Adapter\ArrayAdapter;
16 | use Symfony\Component\Security\Acl\Domain\Acl;
17 | use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
18 | use Symfony\Component\Security\Acl\Domain\PermissionGrantingStrategy;
19 | use Symfony\Component\Security\Acl\Domain\PsrAclCache;
20 | use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
21 |
22 | class PsrAclCacheTest extends TestCase
23 | {
24 | protected $permissionGrantingStrategy;
25 |
26 | public function testConstructorDoesNotAcceptEmptyPrefix()
27 | {
28 | $this->expectException(\InvalidArgumentException::class);
29 |
30 | new PsrAclCache(new ArrayAdapter(), $this->getPermissionGrantingStrategy(), '');
31 | }
32 |
33 | public function test()
34 | {
35 | $cache = $this->getCache();
36 |
37 | $aclWithParent = $this->getAcl(1);
38 | $acl = $this->getAcl();
39 |
40 | $cache->putInCache($aclWithParent);
41 | $cache->putInCache($acl);
42 |
43 | $cachedAcl = $cache->getFromCacheByIdentity($acl->getObjectIdentity());
44 | $this->assertEquals($acl->getId(), $cachedAcl->getId());
45 | $this->assertNull($acl->getParentAcl());
46 |
47 | $cachedAclWithParent = $cache->getFromCacheByIdentity($aclWithParent->getObjectIdentity());
48 | $this->assertEquals($aclWithParent->getId(), $cachedAclWithParent->getId());
49 | $this->assertNotNull($cachedParentAcl = $cachedAclWithParent->getParentAcl());
50 | $this->assertEquals($aclWithParent->getParentAcl()->getId(), $cachedParentAcl->getId());
51 | }
52 |
53 | protected function getAcl($depth = 0)
54 | {
55 | static $id = 1;
56 |
57 | $acl = new Acl($id, new ObjectIdentity($id, 'foo'), $this->getPermissionGrantingStrategy(), [], $depth > 0);
58 |
59 | // insert some ACEs
60 | $sid = new UserSecurityIdentity('johannes', 'Foo');
61 | $acl->insertClassAce($sid, 1);
62 | $acl->insertClassFieldAce('foo', $sid, 1);
63 | $acl->insertObjectAce($sid, 1);
64 | $acl->insertObjectFieldAce('foo', $sid, 1);
65 | ++$id;
66 |
67 | if ($depth > 0) {
68 | $acl->setParentAcl($this->getAcl($depth - 1));
69 | }
70 |
71 | return $acl;
72 | }
73 |
74 | protected function getPermissionGrantingStrategy()
75 | {
76 | if (null === $this->permissionGrantingStrategy) {
77 | $this->permissionGrantingStrategy = new PermissionGrantingStrategy();
78 | }
79 |
80 | return $this->permissionGrantingStrategy;
81 | }
82 |
83 | protected function getCache($cacheDriver = null, $prefix = PsrAclCache::PREFIX)
84 | {
85 | if (null === $cacheDriver) {
86 | $cacheDriver = new ArrayAdapter();
87 | }
88 |
89 | return new PsrAclCache($cacheDriver, $this->getPermissionGrantingStrategy(), $prefix);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Tests/Domain/RoleSecurityIdentityTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
16 | use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
17 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
18 |
19 | class RoleSecurityIdentityTest extends TestCase
20 | {
21 | public function testConstructor()
22 | {
23 | $id = new RoleSecurityIdentity('ROLE_FOO');
24 |
25 | $this->assertEquals('ROLE_FOO', $id->getRole());
26 | }
27 |
28 | /**
29 | * @dataProvider getCompareData
30 | */
31 | public function testEquals(RoleSecurityIdentity $id1, SecurityIdentityInterface $id2, bool $equal)
32 | {
33 | if ($equal) {
34 | $this->assertTrue($id1->equals($id2));
35 | } else {
36 | $this->assertFalse($id1->equals($id2));
37 | }
38 | }
39 |
40 | public function getCompareData(): array
41 | {
42 | return [
43 | [new RoleSecurityIdentity('ROLE_FOO'), new RoleSecurityIdentity('ROLE_FOO'), true],
44 | [new RoleSecurityIdentity('ROLE_USER'), new RoleSecurityIdentity('ROLE_FOO'), false],
45 | [new RoleSecurityIdentity('ROLE_FOO'), new UserSecurityIdentity('ROLE_FOO', 'Foo'), false],
46 | ];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Tests/Domain/SecurityIdentityRetrievalStrategyTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
16 | use Symfony\Component\Security\Acl\Domain\SecurityIdentityRetrievalStrategy;
17 | use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
18 | use Symfony\Component\Security\Acl\Tests\Fixtures\Account;
19 | use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
20 | use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
21 | use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
22 | use Symfony\Component\Security\Core\Authentication\Token\NullToken;
23 | use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
24 | use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
25 | use Symfony\Component\Security\Core\User\UserInterface;
26 |
27 | class SecurityIdentityRetrievalStrategyTest extends TestCase
28 | {
29 | /**
30 | * @dataProvider getSecurityIdentityRetrievalTests
31 | */
32 | public function testGetSecurityIdentities($user, array $roles, string $authenticationStatus, array $sids)
33 | {
34 | $token = class_exists(NullToken::class) ? new NullToken() : new AnonymousToken('', '');
35 | if ('anonymous' !== $authenticationStatus) {
36 | $class = '';
37 | if (\is_string($user)) {
38 | $class = 'MyCustomTokenImpl';
39 | }
40 |
41 | $token = $this->getMockBuilder(AbstractToken::class)
42 | ->setMockClassName($class)
43 | ->getMock();
44 |
45 | $token
46 | ->expects($this->once())
47 | ->method('getRoleNames')
48 | ->willReturn(['foo'])
49 | ;
50 |
51 | $token
52 | ->expects($this->once())
53 | ->method('getUser')
54 | ->willReturn($user)
55 | ;
56 | }
57 |
58 | $strategy = $this->getStrategy($roles, $authenticationStatus);
59 | $extractedSids = $strategy->getSecurityIdentities($token);
60 |
61 | foreach ($extractedSids as $index => $extractedSid) {
62 | if (!isset($sids[$index])) {
63 | $this->fail(sprintf('Expected SID at index %d, but there was none.', $index));
64 | }
65 |
66 | if (false === $sids[$index]->equals($extractedSid)) {
67 | $this->fail(sprintf('Index: %d, expected SID "%s", but got "%s".', $index, $sids[$index], (string) $extractedSid));
68 | }
69 | }
70 | }
71 |
72 | /**
73 | * @group legacy
74 | *
75 | * @dataProvider getDeprecatedSecurityIdentityRetrievalTests
76 | */
77 | public function testDeprecatedGetSecurityIdentities($user, array $roles, string $authenticationStatus, array $sids)
78 | {
79 | if (method_exists(AuthenticationTrustResolverInterface::class, 'isAuthenticated')) {
80 | $this->markTestSkipped();
81 | }
82 |
83 | if ('anonymous' === $authenticationStatus) {
84 | $token = $this->getMockBuilder(AnonymousToken::class)
85 | ->disableOriginalConstructor()
86 | ->getMock();
87 | } else {
88 | $class = '';
89 | if (\is_string($user)) {
90 | $class = 'MyCustomTokenImpl';
91 | }
92 |
93 | $token = $this->getMockBuilder(AbstractToken::class)
94 | ->setMockClassName($class)
95 | ->getMock();
96 | }
97 |
98 | $strategy = $this->getStrategy($roles, $authenticationStatus);
99 |
100 | $token
101 | ->expects($this->once())
102 | ->method('getRoleNames')
103 | ->willReturn(['foo'])
104 | ;
105 |
106 | if ('anonymous' === $authenticationStatus) {
107 | $token
108 | ->expects($this->never())
109 | ->method('getUser')
110 | ;
111 | } else {
112 | $token
113 | ->expects($this->once())
114 | ->method('getUser')
115 | ->willReturn($user)
116 | ;
117 | }
118 |
119 | $extractedSids = $strategy->getSecurityIdentities($token);
120 |
121 | foreach ($extractedSids as $index => $extractedSid) {
122 | if (!isset($sids[$index])) {
123 | $this->fail(sprintf('Expected SID at index %d, but there was none.', $index));
124 | }
125 |
126 | if (false === $sids[$index]->equals($extractedSid)) {
127 | $this->fail(sprintf('Index: %d, expected SID "%s", but got "%s".', $index, $sids[$index], (string) $extractedSid));
128 | }
129 | }
130 | }
131 |
132 | public function getSecurityIdentityRetrievalTests(): array
133 | {
134 | $anonymousRoles = [new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY')];
135 | if (\defined('\Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter::PUBLIC_ACCESS')) {
136 | $anonymousRoles[] = new RoleSecurityIdentity(AuthenticatedVoter::PUBLIC_ACCESS);
137 | }
138 |
139 | return [
140 | [new Account('johannes'), ['ROLE_USER', 'ROLE_SUPERADMIN'], 'fullFledged', array_merge([
141 | new UserSecurityIdentity('johannes', Account::class),
142 | new RoleSecurityIdentity('ROLE_USER'),
143 | new RoleSecurityIdentity('ROLE_SUPERADMIN'),
144 | new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
145 | new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
146 | ], $anonymousRoles)],
147 | [new CustomUserImpl('johannes'), ['ROLE_FOO'], 'fullFledged', array_merge([
148 | new UserSecurityIdentity('johannes', CustomUserImpl::class),
149 | new RoleSecurityIdentity('ROLE_FOO'),
150 | new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
151 | new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
152 | ], $anonymousRoles)],
153 | [new Account('foo'), ['ROLE_FOO'], 'rememberMe', array_merge([
154 | new UserSecurityIdentity('foo', Account::class),
155 | new RoleSecurityIdentity('ROLE_FOO'),
156 | new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
157 | ], $anonymousRoles)],
158 | ['guest', [], 'anonymous', $anonymousRoles],
159 | ];
160 | }
161 |
162 | public function getDeprecatedSecurityIdentityRetrievalTests()
163 | {
164 | $anonymousRoles = [new RoleSecurityIdentity('IS_AUTHENTICATED_ANONYMOUSLY')];
165 | if (\defined('\Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter::PUBLIC_ACCESS')) {
166 | $anonymousRoles[] = new RoleSecurityIdentity(AuthenticatedVoter::PUBLIC_ACCESS);
167 | }
168 |
169 | return [
170 | ['johannes', ['ROLE_FOO'], 'fullFledged', array_merge([
171 | new UserSecurityIdentity('johannes', 'MyCustomTokenImpl'),
172 | new RoleSecurityIdentity('ROLE_FOO'),
173 | new RoleSecurityIdentity('IS_AUTHENTICATED_FULLY'),
174 | new RoleSecurityIdentity('IS_AUTHENTICATED_REMEMBERED'),
175 | ], $anonymousRoles)],
176 | ['guest', ['ROLE_FOO'], 'anonymous', array_merge([
177 | new RoleSecurityIdentity('ROLE_FOO'),
178 | ], $anonymousRoles)],
179 | ];
180 | }
181 |
182 | private function getStrategy(array $roles, string $authenticationStatus): SecurityIdentityRetrievalStrategy
183 | {
184 | $roleHierarchy = new class($roles) implements RoleHierarchyInterface {
185 | private $roles;
186 |
187 | public function __construct(array $roles)
188 | {
189 | $this->roles = $roles;
190 | }
191 |
192 | public function getReachableRoleNames(array $roles): array
193 | {
194 | return $this->roles;
195 | }
196 | };
197 |
198 | $trustResolverMockBuild = $this->getMockBuilder(AuthenticationTrustResolverInterface::class);
199 | if (\defined('\Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter::PUBLIC_ACCESS')) {
200 | if (method_exists(AuthenticationTrustResolverInterface::class, 'isAuthenticated')) {
201 | $trustResolver = $trustResolverMockBuild->getMock();
202 | } else {
203 | $trustResolver = $trustResolverMockBuild
204 | ->onlyMethods(['isAnonymous', 'isRememberMe', 'isFullFledged'])
205 | ->addMethods(['isAuthenticated'])
206 | ->getMock()
207 | ;
208 | }
209 | $trustResolver
210 | ->method('isAuthenticated')
211 | ->willReturn('anonymous' !== $authenticationStatus);
212 | } else {
213 | $trustResolver = $trustResolverMockBuild->getMock();
214 | $trustResolver
215 | ->method('isAnonymous')
216 | ->willReturn('anonymous' === $authenticationStatus);
217 | }
218 |
219 | if ('fullFledged' === $authenticationStatus) {
220 | $trustResolver
221 | ->expects($this->once())
222 | ->method('isFullFledged')
223 | ->willReturn(true)
224 | ;
225 | $trustResolver
226 | ->expects($this->never())
227 | ->method('isRememberMe')
228 | ;
229 | } elseif ('rememberMe' === $authenticationStatus) {
230 | $trustResolver
231 | ->expects($this->once())
232 | ->method('isFullFledged')
233 | ->willReturn(false)
234 | ;
235 | $trustResolver
236 | ->expects($this->once())
237 | ->method('isRememberMe')
238 | ->willReturn(true)
239 | ;
240 | } else {
241 | if (method_exists(AuthenticationTrustResolverInterface::class, 'isAuthenticated')) {
242 | $trustResolver
243 | ->method('isAuthenticated')
244 | ->willReturn(false)
245 | ;
246 | } else {
247 | $trustResolver
248 | ->method('isAnonymous')
249 | ->willReturn(true);
250 | }
251 |
252 | $trustResolver
253 | ->expects($this->once())
254 | ->method('isFullFledged')
255 | ->willReturn(false)
256 | ;
257 | $trustResolver
258 | ->expects($this->once())
259 | ->method('isRememberMe')
260 | ->willReturn(false)
261 | ;
262 | }
263 |
264 | return new SecurityIdentityRetrievalStrategy($roleHierarchy, $trustResolver);
265 | }
266 | }
267 |
268 | class CustomUserImpl implements UserInterface
269 | {
270 | protected $name;
271 |
272 | public function __construct($name)
273 | {
274 | $this->name = $name;
275 | }
276 |
277 | public function __toString()
278 | {
279 | return $this->name;
280 | }
281 |
282 | public function getRoles(): array
283 | {
284 | return [];
285 | }
286 |
287 | public function eraseCredentials(): void
288 | {
289 | }
290 |
291 | public function getUserIdentifier(): string
292 | {
293 | return $this->name;
294 | }
295 |
296 | public function getPassword()
297 | {
298 | return null;
299 | }
300 |
301 | public function getSalt()
302 | {
303 | return null;
304 | }
305 |
306 | public function getUsername(): string
307 | {
308 | return $this->getUserIdentifier();
309 | }
310 | }
311 |
--------------------------------------------------------------------------------
/Tests/Domain/UserSecurityIdentityTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Domain;
13 |
14 | use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
15 | use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
16 | use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface;
17 | use Symfony\Component\Security\Acl\Tests\Fixtures\Account;
18 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
19 |
20 | class UserSecurityIdentityTest extends \PHPUnit\Framework\TestCase
21 | {
22 | public function testConstructor()
23 | {
24 | $id = new UserSecurityIdentity('foo', 'Foo');
25 |
26 | $this->assertEquals('foo', $id->getUsername());
27 | $this->assertEquals('Foo', $id->getClass());
28 | }
29 |
30 | // Test that constructor never changes the type, even for proxies
31 | public function testConstructorWithProxy()
32 | {
33 | $id = new UserSecurityIdentity('foo', 'Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\Foo');
34 |
35 | $this->assertEquals('foo', $id->getUsername());
36 | $this->assertEquals('Acme\DemoBundle\Proxy\__CG__\Symfony\Component\Security\Acl\Tests\Domain\Foo', $id->getClass());
37 | }
38 |
39 | /**
40 | * @dataProvider getCompareData
41 | */
42 | public function testEquals(UserSecurityIdentity $id1, SecurityIdentityInterface $id2, bool $equal)
43 | {
44 | $this->assertSame($equal, $id1->equals($id2));
45 | }
46 |
47 | public function getCompareData(): array
48 | {
49 | $account = new Account('foo');
50 |
51 | $token = $this->createMock(TokenInterface::class);
52 | $token
53 | ->expects($this->any())
54 | ->method('getUser')
55 | ->willReturn($account)
56 | ;
57 |
58 | return [
59 | [new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('foo', 'Foo'), true],
60 | [new UserSecurityIdentity('foo', 'Bar'), new UserSecurityIdentity('foo', 'Foo'), false],
61 | [new UserSecurityIdentity('foo', 'Foo'), new UserSecurityIdentity('bar', 'Foo'), false],
62 | [new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromAccount($account), false],
63 | [new UserSecurityIdentity('bla', 'Foo'), new UserSecurityIdentity('blub', 'Foo'), false],
64 | [new UserSecurityIdentity('foo', 'Foo'), new RoleSecurityIdentity('foo'), false],
65 | [new UserSecurityIdentity('foo', 'Foo'), UserSecurityIdentity::fromToken($token), false],
66 | [new UserSecurityIdentity('foo', Account::class), UserSecurityIdentity::fromToken($token), true],
67 | ];
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Tests/Fixtures/Account.php:
--------------------------------------------------------------------------------
1 | identifier = $identifier;
15 | }
16 |
17 | public function getUserIdentifier(): string
18 | {
19 | return $this->identifier;
20 | }
21 |
22 | public function getUsername(): string
23 | {
24 | return $this->getUserIdentifier();
25 | }
26 |
27 | public function getRoles(): array
28 | {
29 | return ['ROLE_USER'];
30 | }
31 |
32 | public function getPassword(): ?string
33 | {
34 | return null;
35 | }
36 |
37 | public function getSalt(): ?string
38 | {
39 | return null;
40 | }
41 |
42 | public function eraseCredentials(): void
43 | {
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Tests/Fixtures/SerializableAclInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Permission;
13 |
14 | use Symfony\Component\Security\Acl\Permission\BasicPermissionMap;
15 |
16 | class BasicPermissionMapTest extends \PHPUnit\Framework\TestCase
17 | {
18 | public function testGetMasksReturnsNullWhenNotSupportedMask()
19 | {
20 | $map = new BasicPermissionMap();
21 | $this->assertNull($map->getMasks('IS_AUTHENTICATED_REMEMBERED', null));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Tests/Permission/MaskBuilderTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Tests\Permission;
13 |
14 | use Symfony\Component\Security\Acl\Permission\MaskBuilder;
15 |
16 | class MaskBuilderTest extends \PHPUnit\Framework\TestCase
17 | {
18 | /**
19 | * @dataProvider getInvalidConstructorData
20 | */
21 | public function testConstructorWithNonInteger($invalidMask)
22 | {
23 | $this->expectException(\InvalidArgumentException::class);
24 |
25 | new MaskBuilder($invalidMask);
26 | }
27 |
28 | public function getInvalidConstructorData()
29 | {
30 | return [
31 | [234.463],
32 | ['asdgasdf'],
33 | [[]],
34 | [new \stdClass()],
35 | ];
36 | }
37 |
38 | public function testConstructorWithoutArguments()
39 | {
40 | $builder = new MaskBuilder();
41 |
42 | $this->assertEquals(0, $builder->get());
43 | }
44 |
45 | public function testConstructor()
46 | {
47 | $builder = new MaskBuilder(123456);
48 |
49 | $this->assertEquals(123456, $builder->get());
50 | }
51 |
52 | public function testAddAndRemove()
53 | {
54 | $builder = new MaskBuilder();
55 |
56 | $builder
57 | ->add('view')
58 | ->add('eDiT')
59 | ->add('ownEr')
60 | ;
61 | $mask = $builder->get();
62 |
63 | $this->assertEquals(MaskBuilder::MASK_VIEW, $mask & MaskBuilder::MASK_VIEW);
64 | $this->assertEquals(MaskBuilder::MASK_EDIT, $mask & MaskBuilder::MASK_EDIT);
65 | $this->assertEquals(MaskBuilder::MASK_OWNER, $mask & MaskBuilder::MASK_OWNER);
66 | $this->assertEquals(0, $mask & MaskBuilder::MASK_MASTER);
67 | $this->assertEquals(0, $mask & MaskBuilder::MASK_CREATE);
68 | $this->assertEquals(0, $mask & MaskBuilder::MASK_DELETE);
69 | $this->assertEquals(0, $mask & MaskBuilder::MASK_UNDELETE);
70 |
71 | $builder->remove('edit')->remove('OWner');
72 | $mask = $builder->get();
73 | $this->assertEquals(0, $mask & MaskBuilder::MASK_EDIT);
74 | $this->assertEquals(0, $mask & MaskBuilder::MASK_OWNER);
75 | $this->assertEquals(MaskBuilder::MASK_VIEW, $mask & MaskBuilder::MASK_VIEW);
76 | }
77 |
78 | public function testGetPattern()
79 | {
80 | $builder = new MaskBuilder();
81 | $this->assertEquals(MaskBuilder::ALL_OFF, $builder->getPattern());
82 |
83 | $builder->add('view');
84 | $this->assertEquals(str_repeat('.', 31).'V', $builder->getPattern());
85 |
86 | $builder->add('owner');
87 | $this->assertEquals(str_repeat('.', 24).'N......V', $builder->getPattern());
88 |
89 | $builder->add(1 << 10);
90 | $this->assertEquals(str_repeat('.', 21).MaskBuilder::ON.'..N......V', $builder->getPattern());
91 | }
92 |
93 | public function testReset()
94 | {
95 | $builder = new MaskBuilder();
96 | $this->assertEquals(0, $builder->get());
97 |
98 | $builder->add('view');
99 | $this->assertTrue($builder->get() > 0);
100 |
101 | $builder->reset();
102 | $this->assertEquals(0, $builder->get());
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Util/ClassUtils.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Util;
13 |
14 | use Doctrine\Common\Util\ClassUtils as DoctrineClassUtils;
15 |
16 | /**
17 | * Class related functionality for objects that
18 | * might or might not be proxy objects at the moment.
19 | *
20 | * @see DoctrineClassUtils
21 | *
22 | * @author Johannes Schmitt
23 | * @author Iltar van der Berg
24 | */
25 | final class ClassUtils
26 | {
27 | /**
28 | * Marker for Proxy class names.
29 | *
30 | * @var string
31 | */
32 | public const MARKER = '__CG__';
33 |
34 | /**
35 | * Length of the proxy marker.
36 | *
37 | * @var int
38 | */
39 | public const MARKER_LENGTH = 6;
40 |
41 | /**
42 | * This class should not be instantiated.
43 | */
44 | private function __construct()
45 | {
46 | }
47 |
48 | /**
49 | * Gets the real class name of a class name that could be a proxy.
50 | *
51 | * @param string|object $object
52 | *
53 | * @return string
54 | */
55 | public static function getRealClass($object)
56 | {
57 | $class = \is_object($object) ? \get_class($object) : $object;
58 |
59 | if (class_exists(DoctrineClassUtils::class)) {
60 | return DoctrineClassUtils::getRealClass($class);
61 | }
62 |
63 | // fallback in case doctrine common is not installed
64 | if (false === $pos = strrpos($class, '\\'.self::MARKER.'\\')) {
65 | return $class;
66 | }
67 |
68 | return substr($class, $pos + self::MARKER_LENGTH + 2);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Voter/AclVoter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Voter;
13 |
14 | use Psr\Log\LoggerInterface;
15 | use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
16 | use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
17 | use Symfony\Component\Security\Acl\Model\AclProviderInterface;
18 | use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
19 | use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
20 | use Symfony\Component\Security\Acl\Model\SecurityIdentityRetrievalStrategyInterface;
21 | use Symfony\Component\Security\Acl\Permission\PermissionMapInterface;
22 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
23 | use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
24 |
25 | if (class_exists(\Symfony\Component\Security\Core\Security::class)) {
26 | /**
27 | * @internal
28 | */
29 | trait AclVoterTrait
30 | {
31 | public function vote(TokenInterface $token, $subject, array $attributes)
32 | {
33 | return $this->doVote($token, $subject, $attributes);
34 | }
35 | }
36 | } else {
37 | /**
38 | * @internal
39 | */
40 | trait AclVoterTrait
41 | {
42 | public function vote(TokenInterface $token, mixed $subject, array $attributes): int
43 | {
44 | return $this->doVote($token, $subject, $attributes);
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * This voter can be used as a base class for implementing your own permissions.
51 | *
52 | * @author Johannes M. Schmitt
53 | */
54 | class AclVoter implements VoterInterface
55 | {
56 | use AclVoterTrait;
57 |
58 | private $aclProvider;
59 | private $permissionMap;
60 | private $objectIdentityRetrievalStrategy;
61 | private $securityIdentityRetrievalStrategy;
62 | private $allowIfObjectIdentityUnavailable;
63 | private $logger;
64 |
65 | public function __construct(AclProviderInterface $aclProvider, ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, SecurityIdentityRetrievalStrategyInterface $sidRetrievalStrategy, PermissionMapInterface $permissionMap, ?LoggerInterface $logger = null, $allowIfObjectIdentityUnavailable = true)
66 | {
67 | $this->aclProvider = $aclProvider;
68 | $this->permissionMap = $permissionMap;
69 | $this->objectIdentityRetrievalStrategy = $oidRetrievalStrategy;
70 | $this->securityIdentityRetrievalStrategy = $sidRetrievalStrategy;
71 | $this->logger = $logger;
72 | $this->allowIfObjectIdentityUnavailable = $allowIfObjectIdentityUnavailable;
73 | }
74 |
75 | public function supportsAttribute($attribute)
76 | {
77 | return \is_string($attribute) && $this->permissionMap->contains($attribute);
78 | }
79 |
80 | private function doVote(TokenInterface $token, $subject, array $attributes): int
81 | {
82 | foreach ($attributes as $attribute) {
83 | if (!$this->supportsAttribute($attribute)) {
84 | continue;
85 | }
86 |
87 | if (null === $masks = $this->permissionMap->getMasks($attribute, $subject)) {
88 | continue;
89 | }
90 |
91 | if (null === $subject) {
92 | if (null !== $this->logger) {
93 | $this->logger->debug(sprintf('Object identity unavailable. Voting to %s.', $this->allowIfObjectIdentityUnavailable ? 'grant access' : 'abstain'));
94 | }
95 |
96 | return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
97 | } elseif ($subject instanceof FieldVote) {
98 | $field = $subject->getField();
99 | $subject = $subject->getDomainObject();
100 | } else {
101 | $field = null;
102 | }
103 |
104 | if ($subject instanceof ObjectIdentityInterface) {
105 | $oid = $subject;
106 | } elseif (null === $oid = $this->objectIdentityRetrievalStrategy->getObjectIdentity($subject)) {
107 | if (null !== $this->logger) {
108 | $this->logger->debug(sprintf('Object identity unavailable. Voting to %s.', $this->allowIfObjectIdentityUnavailable ? 'grant access' : 'abstain'));
109 | }
110 |
111 | return $this->allowIfObjectIdentityUnavailable ? self::ACCESS_GRANTED : self::ACCESS_ABSTAIN;
112 | }
113 |
114 | if (!$this->supportsClass($oid->getType())) {
115 | return self::ACCESS_ABSTAIN;
116 | }
117 |
118 | $sids = $this->securityIdentityRetrievalStrategy->getSecurityIdentities($token);
119 |
120 | try {
121 | $acl = $this->aclProvider->findAcl($oid, $sids);
122 |
123 | if (null === $field && $acl->isGranted($masks, $sids, false)) {
124 | if (null !== $this->logger) {
125 | $this->logger->debug('ACL found, permission granted. Voting to grant access.');
126 | }
127 |
128 | return self::ACCESS_GRANTED;
129 | } elseif (null !== $field && $acl->isFieldGranted($field, $masks, $sids, false)) {
130 | if (null !== $this->logger) {
131 | $this->logger->debug('ACL found, permission granted. Voting to grant access.');
132 | }
133 |
134 | return self::ACCESS_GRANTED;
135 | }
136 |
137 | if (null !== $this->logger) {
138 | $this->logger->debug('ACL found, insufficient permissions. Voting to deny access.');
139 | }
140 |
141 | return self::ACCESS_DENIED;
142 | } catch (AclNotFoundException $e) {
143 | if (null !== $this->logger) {
144 | $this->logger->debug('No ACL found for the object identity. Voting to deny access.');
145 | }
146 |
147 | return self::ACCESS_DENIED;
148 | } catch (NoAceFoundException $e) {
149 | if (null !== $this->logger) {
150 | $this->logger->debug('ACL found, no ACE applicable. Voting to deny access.');
151 | }
152 |
153 | return self::ACCESS_DENIED;
154 | }
155 | }
156 |
157 | // no attribute was supported
158 | return self::ACCESS_ABSTAIN;
159 | }
160 |
161 | /**
162 | * You can override this method when writing a voter for a specific domain
163 | * class.
164 | *
165 | * @param string $class The class name
166 | *
167 | * @return bool
168 | */
169 | public function supportsClass($class)
170 | {
171 | return true;
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/Voter/FieldVote.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Symfony\Component\Security\Acl\Voter;
13 |
14 | /**
15 | * This class is a lightweight wrapper around field vote requests which does
16 | * not violate any interface contracts.
17 | *
18 | * @author Johannes M. Schmitt
19 | */
20 | class FieldVote
21 | {
22 | private $domainObject;
23 | private $field;
24 |
25 | public function __construct($domainObject, $field)
26 | {
27 | $this->domainObject = $domainObject;
28 | $this->field = $field;
29 | }
30 |
31 | public function getDomainObject()
32 | {
33 | return $this->domainObject;
34 | }
35 |
36 | public function getField()
37 | {
38 | return $this->field;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "symfony/security-acl",
3 | "type": "library",
4 | "description": "Symfony Security Component - ACL (Access Control List)",
5 | "keywords": [],
6 | "homepage": "https://symfony.com",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Fabien Potencier",
11 | "email": "fabien@symfony.com"
12 | },
13 | {
14 | "name": "Symfony Community",
15 | "homepage": "https://symfony.com/contributors"
16 | }
17 | ],
18 | "require": {
19 | "php": ">=7.2.5",
20 | "symfony/security-core": "^4.4|^5.0|^6.0|^7.0"
21 | },
22 | "require-dev": {
23 | "symfony/cache": "^4.4|^5.0|^6.0|^7.0",
24 | "symfony/finder": "^4.4|^5.0|^6.0|^7.0",
25 | "symfony/phpunit-bridge": "^5.2|^6.0|^7.0",
26 | "doctrine/cache": "^1.11|^2.0",
27 | "doctrine/common": "^2.2|^3",
28 | "doctrine/persistence": "^1.3.3|^2|^3",
29 | "doctrine/dbal": "^2.13.1|^3.1",
30 | "psr/log": "^1|^2|^3"
31 | },
32 | "autoload": {
33 | "psr-4": { "Symfony\\Component\\Security\\Acl\\": "" },
34 | "exclude-from-classmap": [
35 | "/Tests/"
36 | ]
37 | },
38 | "conflict": {
39 | "doctrine/cache": "<1.11",
40 | "doctrine/dbal": "<2.13.1|~3.0.0"
41 | },
42 | "extra": {
43 | "branch-alias": {
44 | "dev-main": "3.x-dev"
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | ./Tests/
16 |
17 |
18 |
19 |
20 |
21 | ./
22 |
23 | ./Resources
24 | ./Tests
25 | ./vendor
26 |
27 |
28 |
29 |
30 |
31 | benchmark
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------