Completed
Branch master (e35419)
by Gaetano
06:40
created

RoleManager::matchRoles()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.0312

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 3
nop 2
dl 0
loc 17
ccs 7
cts 8
cp 0.875
crap 4.0312
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use eZ\Publish\API\Repository\Values\User\Role;
6
use eZ\Publish\API\Repository\RoleService;
7
use eZ\Publish\API\Repository\UserService;
8
use Kaliop\eZMigrationBundle\API\Collection\RoleCollection;
9
use Kaliop\eZMigrationBundle\API\MigrationGeneratorInterface;
10
use Kaliop\eZMigrationBundle\Core\Helper\LimitationConverter;
11
use Kaliop\eZMigrationBundle\Core\Matcher\RoleMatcher;
12
use eZ\Publish\API\Repository\Values\User\Limitation;
13
14
/**
15
 * Handles role migrations.
16
 */
17
class RoleManager extends RepositoryExecutor implements MigrationGeneratorInterface
18
{
19
    protected $supportedStepTypes = array('role');
20
    protected $supportedActions = array('create', 'load', 'update', 'delete');
21
22
    protected $limitationConverter;
23
    protected $roleMatcher;
24
25 80
    public function __construct(RoleMatcher $roleMatcher, LimitationConverter $limitationConverter)
26
    {
27 80
        $this->roleMatcher = $roleMatcher;
28 80
        $this->limitationConverter = $limitationConverter;
29 80
    }
30
31
    /**
32
     * Method to handle the create operation of the migration instructions
33
     */
34 1
    protected function create($step)
35
    {
36 1
        $roleService = $this->repository->getRoleService();
37 1
        $userService = $this->repository->getUserService();
38
39 1
        $roleName = $this->referenceResolver->resolveReference($step->dsl['name']);
40 1
        $roleCreateStruct = $roleService->newRoleCreateStruct($roleName);
41
42
        // Publish new role
43 1
        $role = $roleService->createRole($roleCreateStruct);
44 1
        if (is_callable(array($roleService, 'publishRoleDraft'))) {
45 1
            $roleService->publishRoleDraft($role);
0 ignored issues
show
Bug introduced by
The method publishRoleDraft() does not exist on eZ\Publish\API\Repository\RoleService. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

45
            $roleService->/** @scrutinizer ignore-call */ 
46
                          publishRoleDraft($role);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
46
        }
47
48 1
        if (isset($step->dsl['policies'])) {
49 1
            foreach ($step->dsl['policies'] as $key => $ymlPolicy) {
50 1
                $this->addPolicy($role, $roleService, $ymlPolicy);
51
            }
52
        }
53
54 1
        if (isset($step->dsl['assign'])) {
55
            $this->assignRole($role, $roleService, $userService, $step->dsl['assign']);
56
        }
57
58 1
        $this->setReferences($role, $step);
59
60 1
        return $role;
61
    }
62
63
    protected function load($step)
64
    {
65
        $roleCollection = $this->matchRoles('load', $step);
66
67
        $this->setReferences($roleCollection, $step);
68
69
        return $roleCollection;
70
    }
71
72
    /**
73
     * Method to handle the update operation of the migration instructions
74
     */
75 1
    protected function update($step)
76
    {
77 1
        $roleCollection = $this->matchRoles('update', $step);
78
79 1
        if (count($roleCollection) > 1 && isset($step->dsl['references'])) {
80
            throw new \Exception("Can not execute Role update because multiple roles match, and a references section is specified in the dsl. References can be set when only 1 role matches");
81
        }
82
83 1
        if (count($roleCollection) > 1 && isset($step->dsl['new_name'])) {
84
            throw new \Exception("Can not execute Role update because multiple roles match, and a new_name is specified in the dsl.");
85
        }
86
87 1
        $roleService = $this->repository->getRoleService();
88 1
        $userService = $this->repository->getUserService();
89
90
        /** @var \eZ\Publish\API\Repository\Values\User\Role $role */
91 1
        foreach ($roleCollection as $key => $role) {
92
93
            // Updating role name
94 1
            if (isset($step->dsl['new_name'])) {
95
                $update = $roleService->newRoleUpdateStruct();
96
                $newRoleName = $this->referenceResolver->resolveReference($step->dsl['new_name']);
97
                $update->identifier = $this->referenceResolver->resolveReference($newRoleName);
98
                $role = $roleService->updateRole($role, $update);
99
            }
100
101 1
            if (isset($step->dsl['policies'])) {
102 1
                $ymlPolicies = $step->dsl['policies'];
103
104
                // Removing all policies so we can add them back.
105
                // TODO: Check and update policies instead of remove and add.
106 1
                $policies = $role->getPolicies();
107 1
                foreach ($policies as $policy) {
108
                    $roleService->deletePolicy($policy);
109
                }
110
111 1
                foreach ($ymlPolicies as $ymlPolicy) {
112 1
                    $this->addPolicy($role, $roleService, $ymlPolicy);
113
                }
114
            }
115
116 1
            if (isset($step->dsl['assign'])) {
117 1
                $this->assignRole($role, $roleService, $userService, $step->dsl['assign']);
118
            }
119
120 1
            $roleCollection[$key] = $role;
121
        }
122
123 1
        $this->setReferences($roleCollection, $step);
124
125 1
        return $roleCollection;
126
    }
127
128
    /**
129
     * Method to handle the delete operation of the migration instructions
130
     */
131 1
    protected function delete($step)
132
    {
133 1
        $roleCollection = $this->matchRoles('delete', $step);
134
135 1
        $this->setReferences($roleCollection, $step);
136
137 1
        $roleService = $this->repository->getRoleService();
138
139 1
        foreach ($roleCollection as $role) {
140 1
            $roleService->deleteRole($role);
141
        }
142
143 1
        return $roleCollection;
144
    }
145
146
    /**
147
     * @param string $action
148
     * @return RoleCollection
149
     * @throws \Exception
150
     */
151 1
    protected function matchRoles($action, $step)
152
    {
153 1
        if (!isset($step->dsl['name']) && !isset($step->dsl['match'])) {
154
            throw new \Exception("The name of a role or a match condition is required to $action it");
155
        }
156
157
        // Backwards compat
158 1
        if (isset($step->dsl['match'])) {
159 1
            $match = $step->dsl['match'];
160
        } else {
161 1
            $match = array('identifier' => $step->dsl['name']);
162
        }
163
164
        // convert the references passed in the match
165 1
        $match = $this->resolveReferencesRecursively($match);
166
167 1
        return $this->roleMatcher->match($match);
168
    }
169
170
    /**
171
     * @param Role $role
172
     * @param array $references the definitions of the references to set
173
     * @throws \InvalidArgumentException When trying to assign a reference to an unsupported attribute
174
     * @return array key: the reference names, values: the reference values
175
     */
176 1
    protected function getReferencesValues($role, array $references, $step)
177
    {
178 1
        $refs = array();
179
180 1
        foreach ($references as $reference) {
181 1
            switch ($reference['attribute']) {
182 1
                case 'role_id':
183 1
                case 'id':
184 1
                    $value = $role->id;
185 1
                    break;
186 1
                case 'identifier':
187
                case 'role_identifier':
188 1
                    $value = $role->identifier;
189 1
                    break;
190
                default:
191
                    throw new \InvalidArgumentException('Role Manager does not support setting references for attribute ' . $reference['attribute']);
192
            }
193
194 1
            $refs[$reference['identifier']] = $value;
195
        }
196
197 1
        return $refs;
198
    }
199
200
    /**
201
     * @param array $matchCondition
202
     * @param string $mode
203
     * @param array $context
204
     * @throws \Exception
205
     * @return array
206
     */
207 5
    public function generateMigration(array $matchCondition, $mode, array $context = array())
208
    {
209 5
        $previousUserId = $this->loginUser($this->getAdminUserIdentifierFromContext($context));
210 5
        $roleCollection = $this->roleMatcher->match($matchCondition);
211 5
        $data = array();
212
213
        /** @var \eZ\Publish\API\Repository\Values\User\Role $role */
214 5
        foreach ($roleCollection as $role) {
215
            $roleData = array(
216 5
                'type' => reset($this->supportedStepTypes),
217 5
                'mode' => $mode
218
            );
219
220
            switch ($mode) {
221 5
                case 'create':
222 2
                    $roleData = array_merge(
223 2
                        $roleData,
224
                        array(
225 2
                            'name' => $role->identifier
226
                        )
227
                    );
228 2
                    break;
229 3
                case 'update':
230 2
                case 'delete':
231 3
                    $roleData = array_merge(
232 3
                        $roleData,
233
                        array(
234
                            'match' => array(
235 3
                                RoleMatcher::MATCH_ROLE_IDENTIFIER => $role->identifier
236
                            )
237
                        )
238
                    );
239 3
                    break;
240
                default:
241
                    throw new \Exception("Executor 'role' doesn't support mode '$mode'");
242
            }
243
244 5
            if ($mode != 'delete') {
245 3
                $policies = array();
246 3
                foreach ($role->getPolicies() as $policy) {
247 3
                    $limitations = array();
248
249 3
                    foreach ($policy->getLimitations() as $limitation) {
250 3
                        if (!($limitation instanceof Limitation)) {
251
                            throw new \Exception("The role contains an invalid limitation for policy {$policy->module}/{$policy->function}, we can not reliably generate its definition.");
252
                        }
253 3
                        $limitations[] = $this->limitationConverter->getLimitationArrayWithIdentifiers($limitation);
254
                    }
255
256 3
                    $policies[] = array(
257 3
                        'module' => $policy->module,
258 3
                        'function' => $policy->function,
259 3
                        'limitations' => $limitations
260
                    );
261
                }
262
263 3
                $roleData = array_merge(
264 3
                    $roleData,
265
                    array(
266 3
                        'policies' => $policies
267
                    )
268
                );
269
            }
270
271 5
            $data[] = $roleData;
272
        }
273
274 5
        $this->loginUser($previousUserId);
275 5
        return $data;
276
    }
277
278
    /**
279
     * Create a new Limitation object based on the type and value in the $limitation array.
280
     *
281
     * <pre>
282
     * $limitation = array(
283
     *  'identifier' => Type of the limitation
284
     *  'values' => array(Values to base the limitation on)
285
     * )
286
     * </pre>
287
     *
288
     * @param \eZ\Publish\API\Repository\RoleService $roleService
289
     * @param array $limitation
290
     * @return Limitation
291
     */
292 1
    protected function createLimitation(RoleService $roleService, array $limitation)
293
    {
294 1
        $limitationType = $roleService->getLimitationType($limitation['identifier']);
295
296 1
        $limitationValue = is_array($limitation['values']) ? $limitation['values'] : array($limitation['values']);
297
298 1
        foreach ($limitationValue as $id => $value) {
299 1
            $limitationValue[$id] = $this->referenceResolver->resolveReference($value);
300
        }
301 1
        $limitationValue = $this->limitationConverter->resolveLimitationValue($limitation['identifier'], $limitationValue);
302 1
        return $limitationType->buildValue($limitationValue);
303
    }
304
305
    /**
306
     * Assign a role to users and groups in the assignment array.
307
     *
308
     * <pre>
309
     * $assignments = array(
310
     *      array(
311
     *          'type' => 'user',
312
     *          'ids' => array(user ids),
313
     *          'limitation' => array(limitations)
314
     *      )
315
     * )
316
     * </pre>
317
     *
318
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
319
     * @param \eZ\Publish\API\Repository\RoleService $roleService
320
     * @param \eZ\Publish\API\Repository\UserService $userService
321
     * @param array $assignments
322
     */
323 1
    protected function assignRole(Role $role, RoleService $roleService, UserService $userService, array $assignments)
324
    {
325 1
        foreach ($assignments as $assign) {
326 1
            switch ($assign['type']) {
327 1
                case 'user':
328 1
                    foreach ($assign['ids'] as $userId) {
329 1
                        $userId = $this->referenceResolver->resolveReference($userId);
330
331 1
                        $user = $userService->loadUser($userId);
332
333 1
                        if (!isset($assign['limitations'])) {
334
                            $roleService->assignRoleToUser($role, $user);
335
                        } else {
336 1
                            foreach ($assign['limitations'] as $limitation) {
337 1
                                $limitationObject = $this->createLimitation($roleService, $limitation);
338 1
                                $roleService->assignRoleToUser($role, $user, $limitationObject);
339
                            }
340
                        }
341
                    }
342 1
                    break;
343
                case 'group':
344
                    foreach ($assign['ids'] as $groupId) {
345
                        $groupId = $this->referenceResolver->resolveReference($groupId);
346
347
                        $group = $userService->loadUserGroup($groupId);
348
349
                        if (!isset($assign['limitations'])) {
350
                            // q: why are we swallowing exceptions here ?
351
                            //try {
352
                                $roleService->assignRoleToUserGroup($role, $group);
353
                            //} catch (InvalidArgumentException $e) {}
354
                        } else {
355
                            foreach ($assign['limitations'] as $limitation) {
356
                                $limitationObject = $this->createLimitation($roleService, $limitation);
357
                                // q: why are we swallowing exceptions here ?
358
                                //try {
359
                                    $roleService->assignRoleToUserGroup($role, $group, $limitationObject);
360
                                //} catch (InvalidArgumentException $e) {}
361
                            }
362
                        }
363
                    }
364 1
                    break;
365
            }
366
        }
367 1
    }
368
369
    /**
370
     * Add new policies to the $role Role.
371
     *
372
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
373
     * @param \eZ\Publish\API\Repository\RoleService $roleService
374
     * @param array $policy
375
     */
376 1
    protected function addPolicy(Role $role, RoleService $roleService, array $policy)
377
    {
378 1
        $policyCreateStruct = $roleService->newPolicyCreateStruct($policy['module'], $policy['function']);
379
380 1
        if (array_key_exists('limitations', $policy)) {
381 1
            foreach ($policy['limitations'] as $limitation) {
382 1
                $limitationObject = $this->createLimitation($roleService, $limitation);
383 1
                $policyCreateStruct->addLimitation($limitationObject);
384
            }
385
        }
386
387 1
        $roleService->addPolicy($role, $policyCreateStruct);
388 1
    }
389
}
390