Completed
Push — master ( 1b817e...6c15b7 )
by Jeroen
39:29
created

Helper/Security/Acl/Permission/PermissionAdmin.php (2 issues)

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[]
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...
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[]
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
    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';
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