Completed
Push — master ( ae5e03...0447ee )
by Jeroen
10:35 queued 04:37
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 = array();
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
     * @param Request $request
214
     *
215
     * @return bool
216
     */
217 2
    public function bindRequest(Request $request)
218
    {
219 2
        $changes = $request->request->get('permission-hidden-fields');
220
221 2
        if (empty($changes)) {
222 1
            return true;
223
        }
224
225
        // Just apply the changes to the current node (non recursively)
226 1
        $this->applyAclChangeset($this->resource, $changes, false);
227
228
        // Apply recursively (on request)
229 1
        $applyRecursive = $request->request->get('applyRecursive');
230 1
        if ($applyRecursive) {
231
            // Serialize changes & store them in DB
232 1
            $user = $this->tokenStorage->getToken()->getUser();
233 1
            $this->createAclChangeSet($this->resource, $changes, $user);
234
235 1
            $cmd = 'php ' . $this->kernel->getRootDir() . '/../bin/console kuma:acl:apply';
236 1
            $cmd .= ' --env=' . $this->kernel->getEnvironment();
237
238 1
            $this->shellHelper->runInBackground($cmd);
239
        }
240
241 1
        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 2
    public function createAclChangeSet(AbstractEntity $entity, $changes, UserInterface $user)
254
    {
255 2
        $aclChangeset = new AclChangeset();
256 2
        $aclChangeset->setRef($entity);
257 2
        $aclChangeset->setChangeset($changes);
258
        /* @var $user BaseUser */
259 2
        $aclChangeset->setUser($user);
260 2
        $this->em->persist($aclChangeset);
261 2
        $this->em->flush();
262
263 2
        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 4
    public function applyAclChangeset(AbstractEntity $entity, $changeset, $recursive = true)
274
    {
275 4
        if ($recursive) {
276 2
            if (!method_exists($entity, 'getChildren')) {
277 1
                return;
278
            }
279
280
            // Iterate over children and apply recursively
281 1
            foreach ($entity->getChildren() as $child) {
282 1
                $this->applyAclChangeset($child, $changeset);
283
            }
284
        }
285
286
        // Apply ACL modifications to node
287 3
        $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($entity);
288
289
        try {
290
            /* @var $acl MutableAclInterface */
291 3
            $acl = $this->aclProvider->findAcl($objectIdentity);
292 1
        } catch (AclNotFoundException $e) {
293
            /* @var $acl MutableAclInterface */
294 1
            $acl = $this->aclProvider->createAcl($objectIdentity);
295
        }
296
297
        // Process permissions in changeset
298 3
        foreach ($changeset as $role => $roleChanges) {
299 2
            $index = $this->getObjectAceIndex($acl, $role);
300 2
            $mask = 0;
301 2
            if (false !== $index) {
302 1
                $mask = $this->getMaskAtIndex($acl, $index);
303
            }
304 2
            foreach ($roleChanges as $type => $permissions) {
305 2
                $maskChange = new MaskBuilder();
306 2
                foreach ($permissions as $permission) {
307 2
                    $maskChange->add($permission);
308
                }
309
                switch ($type) {
310 2
                    case self::ADD:
311 1
                        $mask |= $maskChange->get();
312
313 1
                        break;
314 1
                    case self::DELETE:
315 1
                        $mask &= ~$maskChange->get();
316
317 1
                        break;
318
                }
319
            }
320 2
            if (false !== $index) {
321 1
                $acl->updateObjectAce($index, $mask);
322
            } else {
323 1
                $securityIdentity = new RoleSecurityIdentity($role);
324 1
                $acl->insertObjectAce($securityIdentity, $mask);
325
            }
326
        }
327 3
        $this->aclProvider->updateAcl($acl);
328 3
    }
329
330
    /**
331
     * Get current object ACE index for specified role.
332
     *
333
     * @param AclInterface $acl  The AclInterface
334
     * @param string       $role The role
335
     *
336
     * @return bool|int
337
     */
338 2 View Code Duplication
    private function getObjectAceIndex(AclInterface $acl, $role)
339
    {
340 2
        $objectAces = $acl->getObjectAces();
341
        /* @var $ace AuditableEntryInterface */
342 2
        foreach ($objectAces as $index => $ace) {
343 2
            $securityIdentity = $ace->getSecurityIdentity();
344 2
            if (($securityIdentity instanceof RoleSecurityIdentity) && $securityIdentity->getRole() == $role) {
345 1
                return $index;
346
            }
347
        }
348
349 1
        return false;
350
    }
351
352
    /**
353
     * Get object ACE mask at specified index.
354
     *
355
     * @param AclInterface $acl   The acl interface
356
     * @param int          $index The index
357
     *
358
     * @return bool|int
359
     */
360 2 View Code Duplication
    private function getMaskAtIndex(AclInterface $acl, $index)
361
    {
362 2
        $objectAces = $acl->getObjectAces();
363
        /* @var $ace AuditableEntryInterface */
364 2
        $ace = $objectAces[$index];
365 2
        $securityIdentity = $ace->getSecurityIdentity();
366 2
        if ($securityIdentity instanceof RoleSecurityIdentity) {
367 2
            return $ace->getMask();
368
        }
369
370 1
        return false;
371
    }
372
}
373