Completed
Pull Request — 5.0 (#2072)
by Jeroen
33:11
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
    {
104
        $this->em                   = $em;
105
        $this->tokenStorage         = $tokenStorage;
106
        $this->aclProvider          = $aclProvider;
107
        $this->oidRetrievalStrategy = $oidRetrievalStrategy;
108
        $this->eventDispatcher      = $eventDispatcher;
109
        $this->shellHelper          = $shellHelper;
110
        $this->kernel               = $kernel;
111
    }
112
113
    /**
114
     * Initialize permission admin with specified entity.
115
     *
116
     * @param AbstractEntity         $resource      The object which has the permissions
117
     * @param PermissionMapInterface $permissionMap The permission map to use
118
     */
119
    public function initialize(AbstractEntity $resource, PermissionMapInterface $permissionMap)
120
    {
121
        $this->resource      = $resource;
122
        $this->permissionMap = $permissionMap;
123
        $this->permissions   = array();
124
125
        // Init permissions
126
        try {
127
            $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($this->resource);
128
            /* @var $acl AclInterface */
129
            $acl            = $this->aclProvider->findAcl($objectIdentity);
130
            $objectAces     = $acl->getObjectAces();
131
            /* @var $ace AuditableEntryInterface */
132 View Code Duplication
            foreach ($objectAces as $ace) {
133
                $securityIdentity = $ace->getSecurityIdentity();
134
                if ($securityIdentity instanceof RoleSecurityIdentity) {
135
                    $this->permissions[$securityIdentity->getRole()] = new MaskBuilder($ace->getMask());
136
                }
137
            }
138
        } catch (AclNotFoundException $e) {
139
            // No Acl found - do nothing (or should we initialize with default values here?)
140
        }
141
    }
142
143
    /**
144
     * Get permissions.
145
     *
146
     * @return MaskBuilder[]
147
     */
148
    public function getPermissions()
149
    {
150
        return $this->permissions;
151
    }
152
153
    /**
154
     * Get permission for specified role.
155
     *
156
     * @param RoleInterface|string $role
157
     *
158
     * @return MaskBuilder|null
159
     */
160
    public function getPermission($role)
161
    {
162
        if ($role instanceof RoleInterface) {
163
            $role = $role->getRole();
164
        }
165
        if (isset($this->permissions[$role])) {
166
            return $this->permissions[$role];
167
        }
168
169
        return null;
170
    }
171
172
    /**
173
     * Get all roles.
174
     *
175
     * @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...
176
     */
177
    public function getAllRoles()
178
    {
179
        return $this->em->getRepository('KunstmaanAdminBundle:Role')->findAll();
180
    }
181
182
    /**
183
     * Get all manageable roles for pages
184
     *
185
     * @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...
186
     */
187
    public function getManageableRolesForPages()
188
    {
189
        $roles = $this->em->getRepository('KunstmaanAdminBundle:Role')->findAll();
190
191
        if (($token = $this->tokenStorage->getToken()) && ($user = $token->getUser())) {
192
            if ($user && !$user->isSuperAdmin() && ($superAdminRole = array_keys($roles, 'ROLE_SUPER_ADMIN'))) {
193
                $superAdminRole = current($superAdminRole);
194
                unset($roles[$superAdminRole]);
195
            }
196
        }
197
198
        return $roles;
199
    }
200
201
    /**
202
     * Get possible permissions.
203
     *
204
     * @return array
205
     */
206
    public function getPossiblePermissions()
207
    {
208
        return $this->permissionMap->getPossiblePermissions();
209
    }
210
211
    /**
212
     * Handle form entry of permission changes.
213
     *
214
     * @param Request $request
215
     *
216
     * @return bool
217
     */
218
    public function bindRequest(Request $request)
219
    {
220
        $changes = $request->request->get('permission-hidden-fields');
221
222
        if (empty($changes)) {
223
            return true;
224
        }
225
226
        // Just apply the changes to the current node (non recursively)
227
        $this->applyAclChangeset($this->resource, $changes, false);
228
229
        // Apply recursively (on request)
230
        $applyRecursive = $request->request->get('applyRecursive');
231
        if ($applyRecursive) {
232
            // Serialize changes & store them in DB
233
            $user = $this->tokenStorage->getToken()->getUser();
234
            $this->createAclChangeSet($this->resource, $changes, $user);
235
236
            $cmd = 'php ' . $this->kernel->getRootDir() . '/../bin/console kuma:acl:apply';
237
            $cmd .= ' --env=' . $this->kernel->getEnvironment();
238
239
            $this->shellHelper->runInBackground($cmd);
240
        }
241
242
        return true;
243
    }
244
245
    /**
246
     * Create a new ACL changeset.
247
     *
248
     * @param AbstractEntity $entity  The entity
249
     * @param array          $changes The changes
250
     * @param UserInterface  $user    The user
251
     *
252
     * @return AclChangeset
253
     */
254
    public function createAclChangeSet(AbstractEntity $entity, $changes, UserInterface $user)
255
    {
256
        $aclChangeset = new AclChangeset();
257
        $aclChangeset->setRef($entity);
258
        $aclChangeset->setChangeset($changes);
259
        /* @var $user BaseUser */
260
        $aclChangeset->setUser($user);
261
        $this->em->persist($aclChangeset);
262
        $this->em->flush();
263
264
        return $aclChangeset;
265
    }
266
267
    /**
268
     * Apply the specified ACL changeset.
269
     *
270
     * @param AbstractEntity $entity    The entity
271
     * @param array          $changeset The changeset
272
     * @param bool           $recursive The recursive
273
     */
274
    public function applyAclChangeset(AbstractEntity $entity, $changeset, $recursive = true)
275
    {
276
        if ($recursive) {
277
            if (!method_exists($entity, 'getChildren')) {
278
                return;
279
            }
280
281
            // Iterate over children and apply recursively
282
            /** @noinspection PhpUndefinedMethodInspection */
283
            foreach ($entity->getChildren() as $child) {
284
                $this->applyAclChangeset($child, $changeset);
285
            }
286
        }
287
288
        // Apply ACL modifications to node
289
        $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($entity);
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
                        break;
314
                    case self::DELETE:
315
                        $mask = $mask & ~$maskChange->get();
316
                        break;
317
                }
318
            }
319
            if (false !== $index) {
320
                $acl->updateObjectAce($index, $mask);
321
            } else {
322
                $securityIdentity = new RoleSecurityIdentity($role);
323
                $acl->insertObjectAce($securityIdentity, $mask);
324
            }
325
        }
326
        $this->aclProvider->updateAcl($acl);
327
    }
328
329
    /**
330
     * Get current object ACE index for specified role.
331
     *
332
     * @param AclInterface $acl  The AclInterface
333
     * @param string       $role The role
334
     *
335
     * @return bool|int
336
     */
337 View Code Duplication
    private function getObjectAceIndex(AclInterface $acl, $role)
338
    {
339
        $objectAces = $acl->getObjectAces();
340
        /* @var $ace AuditableEntryInterface */
341
        foreach ($objectAces as $index => $ace) {
342
            $securityIdentity = $ace->getSecurityIdentity();
343
            if ($securityIdentity instanceof RoleSecurityIdentity) {
344
                if ($securityIdentity->getRole() == $role) {
345
                    return $index;
346
                }
347
            }
348
        }
349
350
        return false;
351
    }
352
353
    /**
354
     * Get object ACE mask at specified index.
355
     *
356
     * @param AclInterface $acl   The acl interface
357
     * @param int          $index The index
358
     *
359
     * @return bool|int
360
     */
361 View Code Duplication
    private function getMaskAtIndex(AclInterface $acl, $index)
362
    {
363
        $objectAces       = $acl->getObjectAces();
364
        /* @var $ace AuditableEntryInterface */
365
        $ace              = $objectAces[$index];
366
        $securityIdentity = $ace->getSecurityIdentity();
367
        if ($securityIdentity instanceof RoleSecurityIdentity) {
368
            return $ace->getMask();
369
        }
370
371
        return false;
372
    }
373
}
374