Completed
Push — master ( 97fb46...770b11 )
by Gaetano
15:34 queued 07:06
created

RoleManager::generateMigration()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 69
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 9.0128

Importance

Changes 0
Metric Value
cc 9
eloc 43
nc 11
nop 3
dl 0
loc 69
ccs 35
cts 37
cp 0.9459
crap 9.0128
rs 7.6764
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\API\EnumerableMatcherInterface;
11
use Kaliop\eZMigrationBundle\Core\Helper\LimitationConverter;
12
use Kaliop\eZMigrationBundle\Core\Matcher\RoleMatcher;
13
use eZ\Publish\API\Repository\Values\User\Limitation;
14
15
/**
16
 * Handles role migrations.
17
 */
18
class RoleManager extends RepositoryExecutor implements MigrationGeneratorInterface, EnumerableMatcherInterface
19
{
20
    protected $supportedStepTypes = array('role');
21
    protected $supportedActions = array('create', 'load', 'update', 'delete');
22
23
    protected $limitationConverter;
24
    protected $roleMatcher;
25
26 96
    public function __construct(RoleMatcher $roleMatcher, LimitationConverter $limitationConverter)
27
    {
28 96
        $this->roleMatcher = $roleMatcher;
29 96
        $this->limitationConverter = $limitationConverter;
30 96
    }
31
32
    /**
33
     * Method to handle the create operation of the migration instructions
34
     */
35 1
    protected function create($step)
36
    {
37 1
        $roleService = $this->repository->getRoleService();
38 1
        $userService = $this->repository->getUserService();
39
40 1
        $roleName = $this->referenceResolver->resolveReference($step->dsl['name']);
41 1
        $roleCreateStruct = $roleService->newRoleCreateStruct($roleName);
42
43
        // Publish new role
44 1
        $role = $roleService->createRole($roleCreateStruct);
45 1
        if (is_callable(array($roleService, 'publishRoleDraft'))) {
46 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

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