Completed
Push — master ( 1de9b7...830752 )
by Kristof
38:46 queued 24:09
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
    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
        $this->em = $em;
104
        $this->tokenStorage = $tokenStorage;
105
        $this->aclProvider = $aclProvider;
106
        $this->oidRetrievalStrategy = $oidRetrievalStrategy;
107
        $this->eventDispatcher = $eventDispatcher;
108
        $this->shellHelper = $shellHelper;
109
        $this->kernel = $kernel;
110
    }
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
    public function initialize(AbstractEntity $resource, PermissionMapInterface $permissionMap)
119
    {
120
        $this->resource = $resource;
121
        $this->permissionMap = $permissionMap;
122
        $this->permissions = array();
123
124
        // Init permissions
125
        try {
126
            $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($this->resource);
127
            /* @var $acl AclInterface */
128
            $acl = $this->aclProvider->findAcl($objectIdentity);
129
            $objectAces = $acl->getObjectAces();
130
            /* @var $ace AuditableEntryInterface */
131 View Code Duplication
            foreach ($objectAces as $ace) {
132
                $securityIdentity = $ace->getSecurityIdentity();
133
                if ($securityIdentity instanceof RoleSecurityIdentity) {
134
                    $this->permissions[$securityIdentity->getRole()] = new MaskBuilder($ace->getMask());
135
                }
136
            }
137
        } catch (AclNotFoundException $e) {
138
            // No Acl found - do nothing (or should we initialize with default values here?)
139
        }
140
    }
141
142
    /**
143
     * Get permissions.
144
     *
145
     * @return MaskBuilder[]
146
     */
147
    public function getPermissions()
148
    {
149
        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
    public function getPermission($role)
160
    {
161
        if ($role instanceof RoleInterface || $role instanceof \Symfony\Component\Security\Core\Role\Role) {
162
            $role = $role->getRole();
163
        }
164
        if (isset($this->permissions[$role])) {
165
            return $this->permissions[$role];
166
        }
167
168
        return null;
169
    }
170
171
    /**
172
     * Get all roles.
173
     *
174
     * @return Role[]
175
     */
176
    public function getAllRoles()
177
    {
178
        return $this->em->getRepository('KunstmaanAdminBundle:Role')->findAll();
179
    }
180
181
    /**
182
     * Get all manageable roles for pages
183
     *
184
     * @return Role[]
185
     */
186
    public function getManageableRolesForPages()
187
    {
188
        $roles = $this->em->getRepository('KunstmaanAdminBundle:Role')->findAll();
189
190
        if (($token = $this->tokenStorage->getToken()) && ($user = $token->getUser())) {
191
            if ($user && !$user->isSuperAdmin() && ($superAdminRole = array_keys($roles, 'ROLE_SUPER_ADMIN'))) {
192
                $superAdminRole = current($superAdminRole);
193
                unset($roles[$superAdminRole]);
194
            }
195
        }
196
197
        return $roles;
198
    }
199
200
    /**
201
     * Get possible permissions.
202
     *
203
     * @return array
204
     */
205
    public function getPossiblePermissions()
206
    {
207
        return $this->permissionMap->getPossiblePermissions();
208
    }
209
210
    /**
211
     * Handle form entry of permission changes.
212
     *
213
     * @param Request $request
214
     *
215
     * @return bool
216
     */
217
    public function bindRequest(Request $request)
218
    {
219
        $changes = $request->request->get('permission-hidden-fields');
220
221
        if (empty($changes)) {
222
            return true;
223
        }
224
225
        // Just apply the changes to the current node (non recursively)
226
        $this->applyAclChangeset($this->resource, $changes, false);
227
228
        // Apply recursively (on request)
229
        $applyRecursive = $request->request->get('applyRecursive');
230
        if ($applyRecursive) {
231
            // Serialize changes & store them in DB
232
            $user = $this->tokenStorage->getToken()->getUser();
233
            $this->createAclChangeSet($this->resource, $changes, $user);
234
235
            $cmd = 'php ' . $this->kernel->getRootDir() . '/../bin/console kuma:acl:apply';
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\HttpKe...Interface::getRootDir() has been deprecated with message: since Symfony 4.2

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
236
            $cmd .= ' --env=' . $this->kernel->getEnvironment();
237
238
            $this->shellHelper->runInBackground($cmd);
239
        }
240
241
        return true;
242
    }
243
244
    /**
245
     * Create a new ACL changeset.
246
     *
247
     * @param AbstractEntity $entity  The entity
248
     * @param array          $changes The changes
249
     * @param UserInterface  $user    The user
250
     *
251
     * @return AclChangeset
252
     */
253
    public function createAclChangeSet(AbstractEntity $entity, $changes, UserInterface $user)
254
    {
255
        $aclChangeset = new AclChangeset();
256
        $aclChangeset->setRef($entity);
257
        $aclChangeset->setChangeset($changes);
258
        /* @var $user BaseUser */
259
        $aclChangeset->setUser($user);
260
        $this->em->persist($aclChangeset);
261
        $this->em->flush();
262
263
        return $aclChangeset;
264
    }
265
266
    /**
267
     * Apply the specified ACL changeset.
268
     *
269
     * @param AbstractEntity $entity    The entity
270
     * @param array          $changeset The changeset
271
     * @param bool           $recursive The recursive
272
     */
273
    public function applyAclChangeset(AbstractEntity $entity, $changeset, $recursive = true)
274
    {
275
        if ($recursive) {
276
            if (!method_exists($entity, 'getChildren')) {
277
                return;
278
            }
279
280
            // Iterate over children and apply recursively
281
            /* @noinspection PhpUndefinedMethodInspection */
282
            foreach ($entity->getChildren() as $child) {
283
                $this->applyAclChangeset($child, $changeset);
284
            }
285
        }
286
287
        // Apply ACL modifications to node
288
        $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($entity);
289
290
        try {
291
            /* @var $acl MutableAclInterface */
292
            $acl = $this->aclProvider->findAcl($objectIdentity);
293
        } catch (AclNotFoundException $e) {
294
            /* @var $acl MutableAclInterface */
295
            $acl = $this->aclProvider->createAcl($objectIdentity);
296
        }
297
298
        // Process permissions in changeset
299
        foreach ($changeset as $role => $roleChanges) {
300
            $index = $this->getObjectAceIndex($acl, $role);
301
            $mask = 0;
302
            if (false !== $index) {
303
                $mask = $this->getMaskAtIndex($acl, $index);
304
            }
305
            foreach ($roleChanges as $type => $permissions) {
306
                $maskChange = new MaskBuilder();
307
                foreach ($permissions as $permission) {
308
                    $maskChange->add($permission);
309
                }
310
                switch ($type) {
311
                    case self::ADD:
312
                        $mask = $mask | $maskChange->get();
313
314
                        break;
315
                    case self::DELETE:
316
                        $mask = $mask & ~$maskChange->get();
317
318
                        break;
319
                }
320
            }
321
            if (false !== $index) {
322
                $acl->updateObjectAce($index, $mask);
323
            } else {
324
                $securityIdentity = new RoleSecurityIdentity($role);
325
                $acl->insertObjectAce($securityIdentity, $mask);
326
            }
327
        }
328
        $this->aclProvider->updateAcl($acl);
329
    }
330
331
    /**
332
     * Get current object ACE index for specified role.
333
     *
334
     * @param AclInterface $acl  The AclInterface
335
     * @param string       $role The role
336
     *
337
     * @return bool|int
338
     */
339 View Code Duplication
    private function getObjectAceIndex(AclInterface $acl, $role)
340
    {
341
        $objectAces = $acl->getObjectAces();
342
        /* @var $ace AuditableEntryInterface */
343
        foreach ($objectAces as $index => $ace) {
344
            $securityIdentity = $ace->getSecurityIdentity();
345
            if ($securityIdentity instanceof RoleSecurityIdentity) {
346
                if ($securityIdentity->getRole() == $role) {
347
                    return $index;
348
                }
349
            }
350
        }
351
352
        return false;
353
    }
354
355
    /**
356
     * Get object ACE mask at specified index.
357
     *
358
     * @param AclInterface $acl   The acl interface
359
     * @param int          $index The index
360
     *
361
     * @return bool|int
362
     */
363 View Code Duplication
    private function getMaskAtIndex(AclInterface $acl, $index)
364
    {
365
        $objectAces = $acl->getObjectAces();
366
        /* @var $ace AuditableEntryInterface */
367
        $ace = $objectAces[$index];
368
        $securityIdentity = $ace->getSecurityIdentity();
369
        if ($securityIdentity instanceof RoleSecurityIdentity) {
370
            return $ace->getMask();
371
        }
372
373
        return false;
374
    }
375
}
376