Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

Helper/Security/Acl/Permission/PermissionAdmin.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\AdminBundle\Helper\Security\Acl\Permission;
4
5
use Doctrine\ORM\EntityManager;
6
use Kunstmaan\AdminBundle\Entity\AbstractEntity;
7
use Kunstmaan\AdminBundle\Entity\AclChangeset;
8
use Kunstmaan\AdminBundle\Entity\Role;
9
use Kunstmaan\UtilitiesBundle\Helper\Shell\Shell;
10
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\HttpKernel\KernelInterface;
13
use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity;
14
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
15
use Symfony\Component\Security\Acl\Model\AclInterface;
16
use Symfony\Component\Security\Acl\Model\AclProviderInterface;
17
use Symfony\Component\Security\Acl\Model\AuditableEntryInterface;
18
use Symfony\Component\Security\Acl\Model\MutableAclInterface;
19
use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface;
20
use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface;
21
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
22
use Symfony\Component\Security\Core\Role\RoleInterface;
23
use Symfony\Component\Security\Core\User\UserInterface;
24
25
/**
26
 * Helper to manage the permissions on a certain entity
27
 */
28
class PermissionAdmin
29
{
30
    const ADD = 'ADD';
31
    const DELETE = 'DEL';
32
33
    /**
34
     * @var AbstractEntity
35
     */
36
    protected $resource = null;
37
38
    /**
39
     * @var EntityManager
40
     */
41
    protected $em = null;
42
43
    /**
44
     * @var TokenStorageInterface
45
     */
46
    protected $tokenStorage = null;
47
48
    /**
49
     * @var MutableAclProviderInterface
50
     */
51
    protected $aclProvider = null;
52
53
    /**
54
     * @var ObjectIdentityRetrievalStrategyInterface
55
     */
56
    protected $oidRetrievalStrategy = null;
57
58
    /**
59
     * @var PermissionMap
60
     */
61
    protected $permissionMap = null;
62
63
    /**
64
     * @var array
65
     */
66
    protected $permissions = null;
67
68
    /**
69
     * @var EventDispatcherInterface
70
     */
71
    protected $eventDispatcher = null;
72
73
    /**
74
     * @var KernelInterface
75
     */
76
    protected $kernel;
77
78
    /**
79
     * @var Shell
80
     */
81
    protected $shellHelper;
82
83
    /**
84
     * Constructor
85
     *
86
     * @param EntityManager                            $em                   The EntityManager
87
     * @param TokenStorageInterface                    $tokenStorage         The token storage
88
     * @param AclProviderInterface                     $aclProvider          The ACL provider
89
     * @param ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy The object retrieval strategy
90
     * @param EventDispatcherInterface                 $eventDispatcher      The event dispatcher
91
     * @param Shell                                    $shellHelper          The shell helper
92
     * @param KernelInterface                          $kernel               The kernel
93
     */
94 14
    public function __construct(
95
        EntityManager $em,
96
        TokenStorageInterface $tokenStorage,
97
        AclProviderInterface $aclProvider,
98
        ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy,
99
        EventDispatcherInterface $eventDispatcher,
100
        Shell $shellHelper,
101
        KernelInterface $kernel
102
    ) {
103 14
        $this->em = $em;
104 14
        $this->tokenStorage = $tokenStorage;
105 14
        $this->aclProvider = $aclProvider;
106 14
        $this->oidRetrievalStrategy = $oidRetrievalStrategy;
107 14
        $this->eventDispatcher = $eventDispatcher;
108 14
        $this->shellHelper = $shellHelper;
109 14
        $this->kernel = $kernel;
110 14
    }
111
112
    /**
113
     * Initialize permission admin with specified entity.
114
     *
115
     * @param AbstractEntity         $resource      The object which has the permissions
116
     * @param PermissionMapInterface $permissionMap The permission map to use
117
     */
118 12
    public function initialize(AbstractEntity $resource, PermissionMapInterface $permissionMap)
119
    {
120 12
        $this->resource = $resource;
121 12
        $this->permissionMap = $permissionMap;
122 12
        $this->permissions = [];
123
124
        // Init permissions
125
        try {
126 12
            $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($this->resource);
127
            /* @var $acl AclInterface */
128 11
            $acl = $this->aclProvider->findAcl($objectIdentity);
129 11
            $objectAces = $acl->getObjectAces();
130
            /* @var $ace AuditableEntryInterface */
131 11 View Code Duplication
            foreach ($objectAces as $ace) {
132 11
                $securityIdentity = $ace->getSecurityIdentity();
133 11
                if ($securityIdentity instanceof RoleSecurityIdentity) {
134 11
                    $this->permissions[$securityIdentity->getRole()] = new MaskBuilder($ace->getMask());
135
                }
136
            }
137 1
        } catch (AclNotFoundException $e) {
138
            // No Acl found - do nothing (or should we initialize with default values here?)
139
        }
140 12
    }
141
142
    /**
143
     * Get permissions.
144
     *
145
     * @return MaskBuilder[]
146
     */
147 1
    public function getPermissions()
148
    {
149 1
        return $this->permissions;
150
    }
151
152
    /**
153
     * Get permission for specified role.
154
     *
155
     * @param RoleInterface|string $role
156
     *
157
     * @return MaskBuilder|null
158
     */
159 3
    public function getPermission($role)
160
    {
161 3
        if ($role instanceof RoleInterface || $role instanceof \Symfony\Component\Security\Core\Role\Role) {
162 1
            $role = $role->getRole();
163
        }
164 3
        if (isset($this->permissions[$role])) {
165 2
            return $this->permissions[$role];
166
        }
167
168 1
        return null;
169
    }
170
171
    /**
172
     * Get all roles.
173
     *
174
     * @return Role[]
175
     */
176 1
    public function getAllRoles()
177
    {
178 1
        return $this->em->getRepository(Role::class)->findAll();
179
    }
180
181
    /**
182
     * Get all manageable roles for pages
183
     *
184
     * @return Role[]
0 ignored issues
show
Should the return type not be object[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
185
     */
186 1
    public function getManageableRolesForPages()
187
    {
188 1
        $roles = $this->em->getRepository(Role::class)->findAll();
189
190 1
        if (($token = $this->tokenStorage->getToken()) && ($user = $token->getUser())) {
191 1
            if ($user && !$user->isSuperAdmin() && ($superAdminRole = array_keys($roles, 'ROLE_SUPER_ADMIN'))) {
192 1
                $superAdminRole = current($superAdminRole);
193 1
                unset($roles[$superAdminRole]);
194
            }
195
        }
196
197 1
        return $roles;
198
    }
199
200
    /**
201
     * Get possible permissions.
202
     *
203
     * @return array
204
     */
205 1
    public function getPossiblePermissions()
206
    {
207 1
        return $this->permissionMap->getPossiblePermissions();
208
    }
209
210
    /**
211
     * Handle form entry of permission changes.
212
     *
213
     * @return bool
214
     */
215 2
    public function bindRequest(Request $request)
216
    {
217 2
        $changes = $request->request->get('permission-hidden-fields');
218
219 2
        if (empty($changes)) {
220 1
            return true;
221
        }
222
223
        // Just apply the changes to the current node (non recursively)
224 1
        $this->applyAclChangeset($this->resource, $changes, false);
225
226
        // Apply recursively (on request)
227 1
        $applyRecursive = $request->request->get('applyRecursive');
228 1
        if ($applyRecursive) {
229
            // Serialize changes & store them in DB
230 1
            $user = $this->tokenStorage->getToken()->getUser();
231 1
            $this->createAclChangeSet($this->resource, $changes, $user);
232
233 1
            $cmd = 'php ' . $this->kernel->getRootDir() . '/../bin/console kuma:acl:apply';
234 1
            $cmd .= ' --env=' . $this->kernel->getEnvironment();
235
236 1
            $this->shellHelper->runInBackground($cmd);
237
        }
238
239 1
        return true;
240
    }
241
242
    /**
243
     * Create a new ACL changeset.
244
     *
245
     * @param AbstractEntity $entity  The entity
246
     * @param array          $changes The changes
247
     * @param UserInterface  $user    The user
248
     *
249
     * @return AclChangeset
250
     */
251 2
    public function createAclChangeSet(AbstractEntity $entity, $changes, UserInterface $user)
252
    {
253 2
        $aclChangeset = new AclChangeset();
254 2
        $aclChangeset->setRef($entity);
255 2
        $aclChangeset->setChangeset($changes);
256
        /* @var $user BaseUser */
257 2
        $aclChangeset->setUser($user);
258 2
        $this->em->persist($aclChangeset);
259 2
        $this->em->flush();
260
261 2
        return $aclChangeset;
262
    }
263
264
    /**
265
     * Apply the specified ACL changeset.
266
     *
267
     * @param AbstractEntity $entity    The entity
268
     * @param array          $changeset The changeset
269
     * @param bool           $recursive The recursive
270
     */
271 4
    public function applyAclChangeset(AbstractEntity $entity, $changeset, $recursive = true)
272
    {
273 4
        if ($recursive) {
274 2
            if (!method_exists($entity, 'getChildren')) {
275 1
                return;
276
            }
277
278
            // Iterate over children and apply recursively
279 1
            foreach ($entity->getChildren() as $child) {
280 1
                $this->applyAclChangeset($child, $changeset);
281
            }
282
        }
283
284
        // Apply ACL modifications to node
285 3
        $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($entity);
286
287
        try {
288
            /* @var $acl MutableAclInterface */
289 3
            $acl = $this->aclProvider->findAcl($objectIdentity);
290 1
        } catch (AclNotFoundException $e) {
291
            /* @var $acl MutableAclInterface */
292 1
            $acl = $this->aclProvider->createAcl($objectIdentity);
293
        }
294
295
        // Process permissions in changeset
296 3
        foreach ($changeset as $role => $roleChanges) {
297 2
            $index = $this->getObjectAceIndex($acl, $role);
298 2
            $mask = 0;
299 2
            if (false !== $index) {
300 1
                $mask = $this->getMaskAtIndex($acl, $index);
301
            }
302 2
            foreach ($roleChanges as $type => $permissions) {
303 2
                $maskChange = new MaskBuilder();
304 2
                foreach ($permissions as $permission) {
305 2
                    $maskChange->add($permission);
306
                }
307
                switch ($type) {
308 2
                    case self::ADD:
309 1
                        $mask |= $maskChange->get();
310
311 1
                        break;
312 1
                    case self::DELETE:
313 1
                        $mask &= ~$maskChange->get();
314
315 1
                        break;
316
                }
317
            }
318 2
            if (false !== $index) {
319 1
                $acl->updateObjectAce($index, $mask);
320
            } else {
321 1
                $securityIdentity = new RoleSecurityIdentity($role);
322 1
                $acl->insertObjectAce($securityIdentity, $mask);
323
            }
324
        }
325 3
        $this->aclProvider->updateAcl($acl);
326 3
    }
327
328
    /**
329
     * Get current object ACE index for specified role.
330
     *
331
     * @param AclInterface $acl  The AclInterface
332
     * @param string       $role The role
333
     *
334
     * @return bool|int
335
     */
336 2 View Code Duplication
    private function getObjectAceIndex(AclInterface $acl, $role)
337
    {
338 2
        $objectAces = $acl->getObjectAces();
339
        /* @var $ace AuditableEntryInterface */
340 2
        foreach ($objectAces as $index => $ace) {
341 2
            $securityIdentity = $ace->getSecurityIdentity();
342 2
            if (($securityIdentity instanceof RoleSecurityIdentity) && $securityIdentity->getRole() == $role) {
343 1
                return $index;
344
            }
345
        }
346
347 1
        return false;
348
    }
349
350
    /**
351
     * Get object ACE mask at specified index.
352
     *
353
     * @param AclInterface $acl   The acl interface
354
     * @param int          $index The index
355
     *
356
     * @return bool|int
357
     */
358 2 View Code Duplication
    private function getMaskAtIndex(AclInterface $acl, $index)
359
    {
360 2
        $objectAces = $acl->getObjectAces();
361
        /* @var $ace AuditableEntryInterface */
362 2
        $ace = $objectAces[$index];
363 2
        $securityIdentity = $ace->getSecurityIdentity();
364 2
        if ($securityIdentity instanceof RoleSecurityIdentity) {
365 2
            return $ace->getMask();
366
        }
367
368 1
        return false;
369
    }
370
}
371