Completed
Push — master ( a5ee66...02276a )
by André
150:34 queued 126:19
created

RoleService::assignRoleToUserGroup()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 36
Code Lines 23

Duplication

Lines 36
Ratio 100 %

Importance

Changes 0
Metric Value
cc 5
eloc 23
nc 8
nop 3
dl 36
loc 36
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the eZ\Publish\Core\Repository\RoleService class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Repository;
10
11
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
12
use eZ\Publish\API\Repository\Repository as RepositoryInterface;
13
use eZ\Publish\API\Repository\RoleService as RoleServiceInterface;
14
use eZ\Publish\API\Repository\Values\User\Limitation;
15
use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation;
16
use eZ\Publish\API\Repository\Values\User\Policy as APIPolicy;
17
use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct as APIPolicyCreateStruct;
18
use eZ\Publish\API\Repository\Values\User\PolicyDraft;
19
use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct as APIPolicyUpdateStruct;
20
use eZ\Publish\API\Repository\Values\User\Role as APIRole;
21
use eZ\Publish\API\Repository\Values\User\RoleAssignment;
22
use eZ\Publish\API\Repository\Values\User\RoleCreateStruct as APIRoleCreateStruct;
23
use eZ\Publish\API\Repository\Values\User\RoleDraft as APIRoleDraft;
24
use eZ\Publish\API\Repository\Values\User\RoleUpdateStruct;
25
use eZ\Publish\API\Repository\Values\User\User;
26
use eZ\Publish\API\Repository\Values\User\UserGroup;
27
use eZ\Publish\Core\Base\Exceptions\BadStateException;
28
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
29
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
30
use eZ\Publish\Core\Base\Exceptions\LimitationValidationException;
31
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
32
use eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException;
33
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
34
use eZ\Publish\Core\Repository\Values\User\Policy;
35
use eZ\Publish\Core\Repository\Values\User\PolicyCreateStruct;
36
use eZ\Publish\Core\Repository\Values\User\PolicyUpdateStruct;
37
use eZ\Publish\Core\Repository\Values\User\Role;
38
use eZ\Publish\Core\Repository\Values\User\RoleCreateStruct;
39
use eZ\Publish\Core\Repository\Values\User\RoleDraft;
40
use eZ\Publish\SPI\Persistence\User\Handler;
41
use eZ\Publish\SPI\Persistence\User\Role as SPIRole;
42
use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct as SPIRoleUpdateStruct;
43
use Exception;
44
45
/**
46
 * This service provides methods for managing Roles and Policies.
47
 */
48
class RoleService implements RoleServiceInterface
49
{
50
    /**
51
     * @var \eZ\Publish\API\Repository\Repository
52
     */
53
    protected $repository;
54
55
    /**
56
     * @var \eZ\Publish\SPI\Persistence\User\Handler
57
     */
58
    protected $userHandler;
59
60
    /**
61
     * @var \eZ\Publish\Core\Repository\Helper\LimitationService
62
     */
63
    protected $limitationService;
64
65
    /**
66
     * @var \eZ\Publish\Core\Repository\Helper\RoleDomainMapper
67
     */
68
    protected $roleDomainMapper;
69
70
    /**
71
     * @var array
72
     */
73
    protected $settings;
74
75
    /**
76
     * Setups service with reference to repository object that created it & corresponding handler.
77
     *
78
     * @param \eZ\Publish\API\Repository\Repository $repository
79
     * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler
80
     * @param \eZ\Publish\Core\Repository\Helper\LimitationService $limitationService
81
     * @param \eZ\Publish\Core\Repository\Helper\RoleDomainMapper $roleDomainMapper
82
     * @param array $settings
83
     */
84 View Code Duplication
    public function __construct(
85
        RepositoryInterface $repository,
86
        Handler $userHandler,
87
        Helper\LimitationService $limitationService,
88
        Helper\RoleDomainMapper $roleDomainMapper,
89
        array $settings = array()
90
    ) {
91
        $this->repository = $repository;
92
        $this->userHandler = $userHandler;
93
        $this->limitationService = $limitationService;
94
        $this->roleDomainMapper = $roleDomainMapper;
95
        $this->settings = $settings;
96
    }
97
98
    /**
99
     * Creates a new RoleDraft.
100
     *
101
     * @since 6.0
102
     *
103
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft
104
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
105
     *         if the name of the role already exists or if limitation of the same type
106
     *         is repeated in the policy create struct or if limitation is not allowed on module/function
107
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a policy limitation in the $roleCreateStruct is not valid
108
     *
109
     * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct
110
     *
111
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
112
     */
113
    public function createRole(APIRoleCreateStruct $roleCreateStruct)
114
    {
115
        if (!is_string($roleCreateStruct->identifier) || empty($roleCreateStruct->identifier)) {
116
            throw new InvalidArgumentValue('identifier', $roleCreateStruct->identifier, 'RoleCreateStruct');
117
        }
118
119
        if ($this->repository->hasAccess('role', 'create') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
120
            throw new UnauthorizedException('role', 'create');
121
        }
122
123
        try {
124
            $existingRole = $this->loadRoleByIdentifier($roleCreateStruct->identifier);
125
126
            throw new InvalidArgumentException(
127
                '$roleCreateStruct',
128
                "Role '{$existingRole->id}' with the specified identifier '{$roleCreateStruct->identifier}' " .
129
                'already exists'
130
            );
131
        } catch (APINotFoundException $e) {
132
            // Do nothing
133
        }
134
135
        $limitationValidationErrors = $this->validateRoleCreateStruct($roleCreateStruct);
136
        if (!empty($limitationValidationErrors)) {
137
            throw new LimitationValidationException($limitationValidationErrors);
138
        }
139
140
        $spiRoleCreateStruct = $this->roleDomainMapper->buildPersistenceRoleCreateStruct($roleCreateStruct);
141
142
        $this->repository->beginTransaction();
143
        try {
144
            $spiRole = $this->userHandler->createRole($spiRoleCreateStruct);
145
            $this->repository->commit();
146
        } catch (Exception $e) {
147
            $this->repository->rollback();
148
            throw $e;
149
        }
150
151
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
152
    }
153
154
    /**
155
     * Creates a new RoleDraft for an existing Role.
156
     *
157
     * @since 6.0
158
     *
159
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft
160
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the Role already has a RoleDraft that will need to be removed first
161
     *
162
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
163
     *
164
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
165
     */
166 View Code Duplication
    public function createRoleDraft(APIRole $role)
167
    {
168
        if ($this->repository->hasAccess('role', 'create') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
169
            throw new UnauthorizedException('role', 'create');
170
        }
171
172
        try {
173
            $this->userHandler->loadRole($role->id, Role::STATUS_DRAFT);
174
175
            // Throw exception, so platformui et al can do conflict management. Follow-up: EZP-24719
176
            throw new InvalidArgumentException(
177
                '$role',
178
                "Cannot create a draft for role '{$role->identifier}' because another draft exists"
179
            );
180
        } catch (APINotFoundException $e) {
181
            $this->repository->beginTransaction();
182
            try {
183
                $spiRole = $this->userHandler->createRoleDraft($role->id);
184
                $this->repository->commit();
185
            } catch (Exception $e) {
186
                $this->repository->rollback();
187
                throw $e;
188
            }
189
        }
190
191
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
192
    }
193
194
    /**
195
     * Loads a RoleDraft for the given id.
196
     *
197
     * @since 6.0
198
     *
199
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this RoleDraft
200
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found
201
     *
202
     * @param mixed $id
203
     *
204
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
205
     */
206 View Code Duplication
    public function loadRoleDraft($id)
207
    {
208
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
209
            throw new UnauthorizedException('role', 'read');
210
        }
211
212
        $spiRole = $this->userHandler->loadRole($id, Role::STATUS_DRAFT);
213
214
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
215
    }
216
217
    /**
218
     * Loads a RoleDraft by the ID of the role it was created from.
219
     *
220
     * @param mixed $roleId ID of the role the draft was created from.
221
     *
222
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
223
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found
224
     *
225
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
226
     */
227 View Code Duplication
    public function loadRoleDraftByRoleId($roleId)
228
    {
229
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
230
            throw new UnauthorizedException('role', 'read');
231
        }
232
233
        $spiRole = $this->userHandler->loadRoleDraftByRoleId($roleId);
234
235
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
236
    }
237
238
    /**
239
     * Updates the properties of a RoleDraft.
240
     *
241
     * @since 6.0
242
     *
243
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a RoleDraft
244
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the identifier of the RoleDraft already exists
245
     *
246
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
247
     * @param \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct $roleUpdateStruct
248
     *
249
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
250
     */
251
    public function updateRoleDraft(APIRoleDraft $roleDraft, RoleUpdateStruct $roleUpdateStruct)
252
    {
253 View Code Duplication
        if ($roleUpdateStruct->identifier !== null && !is_string($roleUpdateStruct->identifier)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
            throw new InvalidArgumentValue('identifier', $roleUpdateStruct->identifier, 'RoleUpdateStruct');
255
        }
256
257
        $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
258
259
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
260
            throw new UnauthorizedException('role', 'update');
261
        }
262
263
        if ($roleUpdateStruct->identifier !== null) {
264
            try {
265
                /* Throw exception if:
266
                 * - A published role with the same identifier exists, AND
267
                 * - The ID of the published role does not match the original ID of the draft
268
                */
269
                $existingSPIRole = $this->userHandler->loadRoleByIdentifier($roleUpdateStruct->identifier);
270
                $SPIRoleDraft = $this->userHandler->loadRole($loadedRoleDraft->id, Role::STATUS_DRAFT);
271
                if ($existingSPIRole->id != $SPIRoleDraft->originalId) {
272
                    throw new InvalidArgumentException(
273
                        '$roleUpdateStruct',
274
                        "Role '{$existingSPIRole->id}' with the specified identifier '{$roleUpdateStruct->identifier}' " .
275
                        'already exists'
276
                    );
277
                }
278
            } catch (APINotFoundException $e) {
279
                // Do nothing
280
            }
281
        }
282
283
        $this->repository->beginTransaction();
284
        try {
285
            $this->userHandler->updateRole(
286
                new SPIRoleUpdateStruct(
287
                    array(
288
                        'id' => $loadedRoleDraft->id,
289
                        'identifier' => $roleUpdateStruct->identifier ?: $loadedRoleDraft->identifier,
290
                    )
291
                )
292
            );
293
            $this->repository->commit();
294
        } catch (Exception $e) {
295
            $this->repository->rollback();
296
            throw $e;
297
        }
298
299
        return $this->loadRoleDraft($loadedRoleDraft->id);
300
    }
301
302
    /**
303
     * Adds a new policy to the RoleDraft.
304
     *
305
     * @since 6.0
306
     *
307
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to add  a policy
308
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy create
309
     *                                                                        struct or if limitation is not allowed on module/function
310
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyCreateStruct is not valid
311
     *
312
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
313
     * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct
314
     *
315
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
316
     */
317 View Code Duplication
    public function addPolicyByRoleDraft(APIRoleDraft $roleDraft, APIPolicyCreateStruct $policyCreateStruct)
318
    {
319
        if (!is_string($policyCreateStruct->module) || empty($policyCreateStruct->module)) {
320
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
321
        }
322
323
        if (!is_string($policyCreateStruct->function) || empty($policyCreateStruct->function)) {
324
            throw new InvalidArgumentValue('function', $policyCreateStruct->function, 'PolicyCreateStruct');
325
        }
326
327
        if ($policyCreateStruct->module === '*' && $policyCreateStruct->function !== '*') {
328
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
329
        }
330
331
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
332
            throw new UnauthorizedException('role', 'update');
333
        }
334
335
        $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
336
337
        $limitations = $policyCreateStruct->getLimitations();
338
        $limitationValidationErrors = $this->validatePolicy(
339
            $policyCreateStruct->module,
340
            $policyCreateStruct->function,
341
            $limitations
342
        );
343
        if (!empty($limitationValidationErrors)) {
344
            throw new LimitationValidationException($limitationValidationErrors);
345
        }
346
347
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
348
            $policyCreateStruct->module,
349
            $policyCreateStruct->function,
350
            $limitations
351
        );
352
353
        $this->repository->beginTransaction();
354
        try {
355
            $this->userHandler->addPolicyByRoleDraft($loadedRoleDraft->id, $spiPolicy);
356
            $this->repository->commit();
357
        } catch (Exception $e) {
358
            $this->repository->rollback();
359
            throw $e;
360
        }
361
362
        return $this->loadRoleDraft($loadedRoleDraft->id);
363
    }
364
365
    /**
366
     * Removes a policy from a RoleDraft.
367
     *
368
     * @since 6.0
369
     *
370
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy
371
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if policy does not belong to the given RoleDraft
372
     *
373
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
374
     * @param PolicyDraft $policyDraft the policy to remove from the RoleDraft
375
     *
376
     * @return APIRoleDraft if the authenticated user is not allowed to remove a policy
377
     */
378
    public function removePolicyByRoleDraft(APIRoleDraft $roleDraft, PolicyDraft $policyDraft)
379
    {
380
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
381
            throw new UnauthorizedException('role', 'update');
382
        }
383
384
        if ($policyDraft->roleId != $roleDraft->id) {
385
            throw new InvalidArgumentException('$policy', 'Policy does not belong to the given role');
386
        }
387
388
        $this->internalDeletePolicy($policyDraft);
389
390
        return $this->loadRoleDraft($roleDraft->id);
391
    }
392
393
    /**
394
     * Updates the limitations of a policy. The module and function cannot be changed and
395
     * the limitations are replaced by the ones in $roleUpdateStruct.
396
     *
397
     * @since 6.0
398
     *
399
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a policy
400
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy update
401
     *                                                                        struct or if limitation is not allowed on module/function
402
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyUpdateStruct is not valid
403
     *
404
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
405
     * @param \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy
406
     * @param \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct $policyUpdateStruct
407
     *
408
     * @return \eZ\Publish\API\Repository\Values\User\PolicyDraft
409
     */
410
    public function updatePolicyByRoleDraft(APIRoleDraft $roleDraft, PolicyDraft $policy, APIPolicyUpdateStruct $policyUpdateStruct)
411
    {
412
        if (!is_string($policy->module)) {
413
            throw new InvalidArgumentValue('module', $policy->module, 'Policy');
414
        }
415
416
        if (!is_string($policy->function)) {
417
            throw new InvalidArgumentValue('function', $policy->function, 'Policy');
418
        }
419
420
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
421
            throw new UnauthorizedException('role', 'update');
422
        }
423
424
        if ($policy->roleId !== $roleDraft->id) {
425
            throw new InvalidArgumentException('$policy', "doesn't belong to provided role draft");
426
        }
427
428
        $limitations = $policyUpdateStruct->getLimitations();
429
        $limitationValidationErrors = $this->validatePolicy(
430
            $policy->module,
431
            $policy->function,
432
            $limitations
433
        );
434
        if (!empty($limitationValidationErrors)) {
435
            throw new LimitationValidationException($limitationValidationErrors);
436
        }
437
438
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
439
            $policy->module,
440
            $policy->function,
441
            $limitations
442
        );
443
        $spiPolicy->id = $policy->id;
444
        $spiPolicy->roleId = $policy->roleId;
445
        $spiPolicy->originalId = $policy->originalId;
446
447
        $this->repository->beginTransaction();
448
        try {
449
            $this->userHandler->updatePolicy($spiPolicy);
450
            $this->repository->commit();
451
        } catch (Exception $e) {
452
            $this->repository->rollback();
453
            throw $e;
454
        }
455
456
        return $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->roleDomainMapper-...licyObject($spiPolicy); of type eZ\Publish\Core\Reposito...tory\Values\User\Policy adds the type eZ\Publish\Core\Repository\Values\User\Policy to the return on line 456 which is incompatible with the return type declared by the interface eZ\Publish\API\Repositor...updatePolicyByRoleDraft of type eZ\Publish\API\Repository\Values\User\PolicyDraft.
Loading history...
457
    }
458
459
    /**
460
     * Deletes the given RoleDraft.
461
     *
462
     * @since 6.0
463
     *
464
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this RoleDraft
465
     *
466
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
467
     */
468 View Code Duplication
    public function deleteRoleDraft(APIRoleDraft $roleDraft)
469
    {
470
        $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
471
472
        $this->repository->beginTransaction();
473
        try {
474
            $this->userHandler->deleteRole($loadedRoleDraft->id, Role::STATUS_DRAFT);
475
            $this->repository->commit();
476
        } catch (Exception $e) {
477
            $this->repository->rollback();
478
            throw $e;
479
        }
480
    }
481
482
    /**
483
     * Publishes a given RoleDraft.
484
     *
485
     * @since 6.0
486
     *
487
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to publish this RoleDraft
488
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the role draft cannot be loaded
489
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the role draft has no policies
490
     *
491
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
492
     */
493 View Code Duplication
    public function publishRoleDraft(APIRoleDraft $roleDraft)
494
    {
495
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
496
            throw new UnauthorizedException('role', 'update');
497
        }
498
499
        try {
500
            $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
501
        } catch (APINotFoundException $e) {
502
            throw new BadStateException(
503
                '$roleDraft',
504
                'The role does not have a draft.',
505
                $e
506
            );
507
        }
508
509
        // TODO: Uncomment when role policy editing is done, see EZP-24711 & EZP-24713
510
        /*if (count($loadedRoleDraft->getPolicies()) === 0) {
511
            throw new InvalidArgumentException(
512
                "\$roleDraft",
513
                'The role draft should have at least one policy.'
514
            );
515
        }*/
516
517
        $this->repository->beginTransaction();
518
        try {
519
            $this->userHandler->publishRoleDraft($loadedRoleDraft->id);
520
            $this->repository->commit();
521
        } catch (Exception $e) {
522
            $this->repository->rollback();
523
            throw $e;
524
        }
525
    }
526
527
    /**
528
     * Updates the name of the role.
529
     *
530
     * @deprecated since 6.0, use {@see updateRoleDraft}
531
     *
532
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a role
533
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the name of the role already exists
534
     *
535
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
536
     * @param \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct $roleUpdateStruct
537
     *
538
     * @return \eZ\Publish\API\Repository\Values\User\Role
539
     */
540
    public function updateRole(APIRole $role, RoleUpdateStruct $roleUpdateStruct)
541
    {
542 View Code Duplication
        if ($roleUpdateStruct->identifier !== null && !is_string($roleUpdateStruct->identifier)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
543
            throw new InvalidArgumentValue('identifier', $roleUpdateStruct->identifier, 'RoleUpdateStruct');
544
        }
545
546
        $loadedRole = $this->loadRole($role->id);
547
548
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
549
            throw new UnauthorizedException('role', 'update');
550
        }
551
552
        if ($roleUpdateStruct->identifier !== null) {
553
            try {
554
                $existingRole = $this->loadRoleByIdentifier($roleUpdateStruct->identifier);
555
556
                if ($existingRole->id != $loadedRole->id) {
557
                    throw new InvalidArgumentException(
558
                        '$roleUpdateStruct',
559
                        'Role with provided identifier already exists'
560
                    );
561
                }
562
            } catch (APINotFoundException $e) {
563
                // Do nothing
564
            }
565
        }
566
567
        $this->repository->beginTransaction();
568
        try {
569
            $this->userHandler->updateRole(
570
                new SPIRoleUpdateStruct(
571
                    array(
572
                        'id' => $loadedRole->id,
573
                        'identifier' => $roleUpdateStruct->identifier ?: $loadedRole->identifier,
574
                    )
575
                )
576
            );
577
            $this->repository->commit();
578
        } catch (Exception $e) {
579
            $this->repository->rollback();
580
            throw $e;
581
        }
582
583
        return $this->loadRole($loadedRole->id);
584
    }
585
586
    /**
587
     * Adds a new policy to the role.
588
     *
589
     * @deprecated since 6.0, use {@see addPolicyByRoleDraft}
590
     *
591
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to add  a policy
592
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy create
593
     *                                                                        struct or if limitation is not allowed on module/function
594
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyCreateStruct is not valid
595
     *
596
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
597
     * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct
598
     *
599
     * @return \eZ\Publish\API\Repository\Values\User\Role
600
     */
601 View Code Duplication
    public function addPolicy(APIRole $role, APIPolicyCreateStruct $policyCreateStruct)
602
    {
603
        if (!is_string($policyCreateStruct->module) || empty($policyCreateStruct->module)) {
604
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
605
        }
606
607
        if (!is_string($policyCreateStruct->function) || empty($policyCreateStruct->function)) {
608
            throw new InvalidArgumentValue('function', $policyCreateStruct->function, 'PolicyCreateStruct');
609
        }
610
611
        if ($policyCreateStruct->module === '*' && $policyCreateStruct->function !== '*') {
612
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
613
        }
614
615
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
616
            throw new UnauthorizedException('role', 'update');
617
        }
618
619
        $loadedRole = $this->loadRole($role->id);
620
621
        $limitations = $policyCreateStruct->getLimitations();
622
        $limitationValidationErrors = $this->validatePolicy(
623
            $policyCreateStruct->module,
624
            $policyCreateStruct->function,
625
            $limitations
626
        );
627
        if (!empty($limitationValidationErrors)) {
628
            throw new LimitationValidationException($limitationValidationErrors);
629
        }
630
631
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
632
            $policyCreateStruct->module,
633
            $policyCreateStruct->function,
634
            $limitations
635
        );
636
637
        $this->repository->beginTransaction();
638
        try {
639
            $this->userHandler->addPolicy($loadedRole->id, $spiPolicy);
640
            $this->repository->commit();
641
        } catch (Exception $e) {
642
            $this->repository->rollback();
643
            throw $e;
644
        }
645
646
        return $this->loadRole($loadedRole->id);
647
    }
648
649
    /**
650
     * Deletes a policy.
651
     *
652
     * @deprecated since 6.0, use {@link removePolicyByRoleDraft()} instead.
653
     *
654
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy
655
     *
656
     * @param \eZ\Publish\API\Repository\Values\User\Policy $policy the policy to delete
657
     */
658
    public function deletePolicy(APIPolicy $policy)
659
    {
660
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
661
            throw new UnauthorizedException('role', 'update');
662
        }
663
664
        $this->internalDeletePolicy($policy);
665
    }
666
667
    /**
668
     * Deletes a policy.
669
     *
670
     * Used by {@link removePolicy()} and {@link deletePolicy()}
671
     *
672
     * @param APIPolicy $policy
673
     *
674
     * @throws \Exception
675
     */
676 View Code Duplication
    protected function internalDeletePolicy(APIPolicy $policy)
677
    {
678
        $this->repository->beginTransaction();
679
        try {
680
            $this->userHandler->deletePolicy($policy->id, $policy->roleId);
681
            $this->repository->commit();
682
        } catch (Exception $e) {
683
            $this->repository->rollback();
684
            throw $e;
685
        }
686
    }
687
688
    /**
689
     * Updates the limitations of a policy. The module and function cannot be changed and
690
     * the limitations are replaced by the ones in $roleUpdateStruct.
691
     *
692
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a policy
693
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy update
694
     *                                                                        struct or if limitation is not allowed on module/function
695
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyUpdateStruct is not valid
696
     *
697
     * @param \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct $policyUpdateStruct
698
     * @param \eZ\Publish\API\Repository\Values\User\Policy $policy
699
     *
700
     * @return \eZ\Publish\API\Repository\Values\User\Policy
701
     */
702
    public function updatePolicy(APIPolicy $policy, APIPolicyUpdateStruct $policyUpdateStruct)
703
    {
704
        if (!is_string($policy->module)) {
705
            throw new InvalidArgumentValue('module', $policy->module, 'Policy');
706
        }
707
708
        if (!is_string($policy->function)) {
709
            throw new InvalidArgumentValue('function', $policy->function, 'Policy');
710
        }
711
712
        if ($this->repository->hasAccess('role', 'update') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
713
            throw new UnauthorizedException('role', 'update');
714
        }
715
716
        $limitations = $policyUpdateStruct->getLimitations();
717
        $limitationValidationErrors = $this->validatePolicy(
718
            $policy->module,
719
            $policy->function,
720
            $limitations
721
        );
722
        if (!empty($limitationValidationErrors)) {
723
            throw new LimitationValidationException($limitationValidationErrors);
724
        }
725
726
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
727
            $policy->module,
728
            $policy->function,
729
            $limitations
730
        );
731
        $spiPolicy->id = $policy->id;
732
        $spiPolicy->roleId = $policy->roleId;
733
734
        $this->repository->beginTransaction();
735
        try {
736
            $this->userHandler->updatePolicy($spiPolicy);
737
            $this->repository->commit();
738
        } catch (Exception $e) {
739
            $this->repository->rollback();
740
            throw $e;
741
        }
742
743
        return $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy);
744
    }
745
746
    /**
747
     * Loads a role for the given id.
748
     *
749
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
750
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a role with the given id was not found
751
     *
752
     * @param mixed $id
753
     *
754
     * @return \eZ\Publish\API\Repository\Values\User\Role
755
     */
756 View Code Duplication
    public function loadRole($id)
757
    {
758
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
759
            throw new UnauthorizedException('role', 'read');
760
        }
761
762
        $spiRole = $this->userHandler->loadRole($id);
763
764
        return $this->roleDomainMapper->buildDomainRoleObject($spiRole);
765
    }
766
767
    /**
768
     * Loads a role for the given identifier.
769
     *
770
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
771
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a role with the given name was not found
772
     *
773
     * @param string $identifier
774
     *
775
     * @return \eZ\Publish\API\Repository\Values\User\Role
776
     */
777 View Code Duplication
    public function loadRoleByIdentifier($identifier)
778
    {
779
        if (!is_string($identifier)) {
780
            throw new InvalidArgumentValue('identifier', $identifier);
781
        }
782
783
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
784
            throw new UnauthorizedException('role', 'read');
785
        }
786
787
        $spiRole = $this->userHandler->loadRoleByIdentifier($identifier);
788
789
        return $this->roleDomainMapper->buildDomainRoleObject($spiRole);
790
    }
791
792
    /**
793
     * Loads all roles.
794
     *
795
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the roles
796
     *
797
     * @return \eZ\Publish\API\Repository\Values\User\Role[]
798
     */
799 View Code Duplication
    public function loadRoles()
800
    {
801
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
802
            throw new UnauthorizedException('role', 'read');
803
        }
804
805
        $spiRoles = $this->userHandler->loadRoles();
806
807
        $roles = array();
808
        foreach ($spiRoles as $spiRole) {
809
            $roles[] = $this->roleDomainMapper->buildDomainRoleObject($spiRole);
810
        }
811
812
        return $roles;
813
    }
814
815
    /**
816
     * Deletes the given role.
817
     *
818
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this role
819
     *
820
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
821
     */
822
    public function deleteRole(APIRole $role)
823
    {
824
        if ($this->repository->hasAccess('role', 'delete') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
825
            throw new UnauthorizedException('role', 'delete');
826
        }
827
828
        $loadedRole = $this->loadRole($role->id);
829
830
        $this->repository->beginTransaction();
831
        try {
832
            $this->userHandler->deleteRole($loadedRole->id);
833
            $this->repository->commit();
834
        } catch (Exception $e) {
835
            $this->repository->rollback();
836
            throw $e;
837
        }
838
    }
839
840
    /**
841
     * Loads all policies from roles which are assigned to a user or to user groups to which the user belongs.
842
     *
843
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given id was not found
844
     *
845
     * @param mixed $userId
846
     *
847
     * @return \eZ\Publish\API\Repository\Values\User\Policy[]
848
     */
849
    public function loadPoliciesByUserId($userId)
850
    {
851
        $spiPolicies = $this->userHandler->loadPoliciesByUserId($userId);
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\SPI\Persisten...:loadPoliciesByUserId() has been deprecated with message: Since 6.8, not currently in use as permission system needs to know about role assignment limitations.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
852
853
        $policies = array();
854
        foreach ($spiPolicies as $spiPolicy) {
855
            $policies[] = $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy);
856
        }
857
858
        if (empty($policies)) {
859
            $this->userHandler->load($userId);
860
        }// For NotFoundException in case userId is invalid
861
862
        return $policies;
863
    }
864
865
    /**
866
     * Assigns a role to the given user group.
867
     *
868
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role
869
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid
870
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists
871
     *
872
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
873
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
874
     * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation)
875
     */
876 View Code Duplication
    public function assignRoleToUserGroup(APIRole $role, UserGroup $userGroup, RoleLimitation $roleLimitation = null)
877
    {
878
        if ($this->repository->canUser('role', 'assign', $userGroup, $role) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
879
            throw new UnauthorizedException('role', 'assign');
880
        }
881
882
        if ($roleLimitation === null) {
883
            $limitation = null;
884
        } else {
885
            $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation);
886
            if (!empty($limitationValidationErrors)) {
887
                throw new LimitationValidationException($limitationValidationErrors);
888
            }
889
890
            $limitation = array($roleLimitation->getIdentifier() => $roleLimitation->limitationValues);
891
        }
892
893
        // Check if objects exists
894
        $spiRole = $this->userHandler->loadRole($role->id);
895
        $loadedUserGroup = $this->repository->getUserService()->loadUserGroup($userGroup->id);
896
897
        $limitation = $this->checkAssignmentAndFilterLimitationValues($loadedUserGroup->id, $spiRole, $limitation);
898
899
        $this->repository->beginTransaction();
900
        try {
901
            $this->userHandler->assignRole(
902
                $loadedUserGroup->id,
903
                $spiRole->id,
904
                $limitation
905
            );
906
            $this->repository->commit();
907
        } catch (Exception $e) {
908
            $this->repository->rollback();
909
            throw $e;
910
        }
911
    }
912
913
    /**
914
     * removes a role from the given user group.
915
     *
916
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role
917
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException  If the role is not assigned to the given user group
918
     *
919
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
920
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
921
     */
922 View Code Duplication
    public function unassignRoleFromUserGroup(APIRole $role, UserGroup $userGroup)
923
    {
924
        if ($this->repository->canUser('role', 'assign', $userGroup, $role) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
925
            throw new UnauthorizedException('role', 'assign');
926
        }
927
928
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id);
929
        $isAssigned = false;
930
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
931
            if ($spiRoleAssignment->roleId === $role->id) {
932
                $isAssigned = true;
933
                break;
934
            }
935
        }
936
937
        if (!$isAssigned) {
938
            throw new InvalidArgumentException(
939
                '$userGroup',
940
                'Role is not assigned to the given UserGroup'
941
            );
942
        }
943
944
        $this->repository->beginTransaction();
945
        try {
946
            $this->userHandler->unassignRole($userGroup->id, $role->id);
947
            $this->repository->commit();
948
        } catch (Exception $e) {
949
            $this->repository->rollback();
950
            throw $e;
951
        }
952
    }
953
954
    /**
955
     * Assigns a role to the given user.
956
     *
957
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role
958
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid
959
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists
960
     *
961
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
962
     * @param \eZ\Publish\API\Repository\Values\User\User $user
963
     * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation)
964
     */
965 View Code Duplication
    public function assignRoleToUser(APIRole $role, User $user, RoleLimitation $roleLimitation = null)
966
    {
967
        if ($this->repository->canUser('role', 'assign', $user, $role) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
968
            throw new UnauthorizedException('role', 'assign');
969
        }
970
971
        if ($roleLimitation === null) {
972
            $limitation = null;
973
        } else {
974
            $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation);
975
            if (!empty($limitationValidationErrors)) {
976
                throw new LimitationValidationException($limitationValidationErrors);
977
            }
978
979
            $limitation = array($roleLimitation->getIdentifier() => $roleLimitation->limitationValues);
980
        }
981
982
        // Check if objects exists
983
        $spiRole = $this->userHandler->loadRole($role->id);
984
        $spiUser = $this->userHandler->load($user->id);
985
986
        $limitation = $this->checkAssignmentAndFilterLimitationValues($spiUser->id, $spiRole, $limitation);
987
988
        $this->repository->beginTransaction();
989
        try {
990
            $this->userHandler->assignRole(
991
                $spiUser->id,
992
                $spiRole->id,
993
                $limitation
994
            );
995
            $this->repository->commit();
996
        } catch (Exception $e) {
997
            $this->repository->rollback();
998
            throw $e;
999
        }
1000
    }
1001
1002
    /**
1003
     * removes a role from the given user.
1004
     *
1005
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role
1006
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the role is not assigned to the user
1007
     *
1008
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
1009
     * @param \eZ\Publish\API\Repository\Values\User\User $user
1010
     */
1011 View Code Duplication
    public function unassignRoleFromUser(APIRole $role, User $user)
1012
    {
1013
        if ($this->repository->canUser('role', 'assign', $user, $role) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1014
            throw new UnauthorizedException('role', 'assign');
1015
        }
1016
1017
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($user->id);
1018
        $isAssigned = false;
1019
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1020
            if ($spiRoleAssignment->roleId === $role->id) {
1021
                $isAssigned = true;
1022
                break;
1023
            }
1024
        }
1025
1026
        if (!$isAssigned) {
1027
            throw new InvalidArgumentException(
1028
                '$user',
1029
                'Role is not assigned to the given User'
1030
            );
1031
        }
1032
1033
        $this->repository->beginTransaction();
1034
        try {
1035
            $this->userHandler->unassignRole($user->id, $role->id);
1036
            $this->repository->commit();
1037
        } catch (Exception $e) {
1038
            $this->repository->rollback();
1039
            throw $e;
1040
        }
1041
    }
1042
1043
    /**
1044
     * Removes the given role assignment.
1045
     *
1046
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role assignment
1047
     *
1048
     * @param \eZ\Publish\API\Repository\Values\User\RoleAssignment $roleAssignment
1049
     */
1050
    public function removeRoleAssignment(RoleAssignment $roleAssignment)
1051
    {
1052
        if ($this->repository->canUser('role', 'assign', $roleAssignment) !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::canUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::canUser() instead. Indicates if the current user is allowed to perform an action given by the function on the given
objects. Example: canUser( 'content', 'edit', $content, $location ); This will check edit permission on content given the specific location, if skipped if will check on all locations. Example2: canUser( 'section', 'assign', $content, $section ); Check if user has access to assign $content to $section.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1053
            throw new UnauthorizedException('role', 'assign');
1054
        }
1055
1056
        $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignment->id);
1057
1058
        $this->repository->beginTransaction();
1059
        try {
1060
            $this->userHandler->removeRoleAssignment($spiRoleAssignment->id);
1061
            $this->repository->commit();
1062
        } catch (Exception $e) {
1063
            $this->repository->rollback();
1064
            throw $e;
1065
        }
1066
    }
1067
1068
    /**
1069
     * Loads a role assignment for the given id.
1070
     *
1071
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
1072
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the role assignment was not found
1073
     *
1074
     * @param mixed $roleAssignmentId
1075
     *
1076
     * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment
1077
     */
1078
    public function loadRoleAssignment($roleAssignmentId)
1079
    {
1080
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1081
            throw new UnauthorizedException('role', 'read');
1082
        }
1083
1084
        $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignmentId);
1085
        $userService = $this->repository->getUserService();
1086
        $role = $this->loadRole($spiRoleAssignment->roleId);
1087
        $roleAssignment = null;
1088
1089
        // First check if the Role is assigned to a User
1090
        // If no User is found, see if it belongs to a UserGroup
1091
        try {
1092
            $user = $userService->loadUser($spiRoleAssignment->contentId);
1093
            $roleAssignment = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject(
1094
                $spiRoleAssignment,
1095
                $user,
1096
                $role
1097
            );
1098
        } catch (APINotFoundException $e) {
1099
            try {
1100
                $userGroup = $userService->loadUserGroup($spiRoleAssignment->contentId);
1101
                $roleAssignment = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1102
                    $spiRoleAssignment,
1103
                    $userGroup,
1104
                    $role
1105
                );
1106
            } catch (APINotFoundException $e) {
1107
                // Do nothing
1108
            }
1109
        }
1110
1111
        return $roleAssignment;
1112
    }
1113
1114
    /**
1115
     * Returns the assigned user and user groups to this role.
1116
     *
1117
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role
1118
     *
1119
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
1120
     *
1121
     * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment[]
1122
     */
1123
    public function getRoleAssignments(APIRole $role)
1124
    {
1125
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1126
            throw new UnauthorizedException('role', 'read');
1127
        }
1128
1129
        $userService = $this->repository->getUserService();
1130
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByRoleId($role->id);
1131
        $roleAssignments = array();
1132
1133
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1134
            // First check if the Role is assigned to a User
1135
            // If no User is found, see if it belongs to a UserGroup
1136
            try {
1137
                $user = $userService->loadUser($spiRoleAssignment->contentId);
1138
                $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject(
1139
                    $spiRoleAssignment,
1140
                    $user,
1141
                    $role
1142
                );
1143
            } catch (APINotFoundException $e) {
1144
                try {
1145
                    $userGroup = $userService->loadUserGroup($spiRoleAssignment->contentId);
1146
                    $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1147
                        $spiRoleAssignment,
1148
                        $userGroup,
1149
                        $role
1150
                    );
1151
                } catch (APINotFoundException $e) {
1152
                    // Do nothing
1153
                }
1154
            }
1155
        }
1156
1157
        return $roleAssignments;
1158
    }
1159
1160
    /**
1161
     * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser()
1162
     */
1163
    public function getRoleAssignmentsForUser(User $user, $inherited = false)
1164
    {
1165
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1166
            throw new UnauthorizedException('role', 'read');
1167
        }
1168
1169
        $roleAssignments = array();
1170
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($user->id, $inherited);
1171
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1172
            $role = $this->loadRole($spiRoleAssignment->roleId);
1173
            if (!$inherited || $spiRoleAssignment->contentId == $user->id) {
1174
                $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject(
1175
                    $spiRoleAssignment,
1176
                    $user,
1177
                    $role
1178
                );
1179
            } else {
1180
                $userGroup = $this->repository->getUserService()->loadUserGroup($spiRoleAssignment->contentId);
1181
                $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1182
                    $spiRoleAssignment,
1183
                    $userGroup,
1184
                    $role
1185
                );
1186
            }
1187
        }
1188
1189
        return $roleAssignments;
1190
    }
1191
1192
    /**
1193
     * Returns the roles assigned to the given user group.
1194
     *
1195
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role
1196
     *
1197
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
1198
     *
1199
     * @return \eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment[]
1200
     */
1201
    public function getRoleAssignmentsForUserGroup(UserGroup $userGroup)
1202
    {
1203
        if ($this->repository->hasAccess('role', 'read') !== true) {
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repository\Repository::hasAccess() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::hasAccess() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1204
            throw new UnauthorizedException('role', 'read');
1205
        }
1206
1207
        $roleAssignments = array();
1208
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id);
1209
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1210
            $role = $this->loadRole($spiRoleAssignment->roleId);
1211
            $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1212
                $spiRoleAssignment,
1213
                $userGroup,
1214
                $role
1215
            );
1216
        }
1217
1218
        return $roleAssignments;
1219
    }
1220
1221
    /**
1222
     * Instantiates a role create class.
1223
     *
1224
     * @param string $name
1225
     *
1226
     * @return \eZ\Publish\API\Repository\Values\User\RoleCreateStruct
1227
     */
1228
    public function newRoleCreateStruct($name)
1229
    {
1230
        return new RoleCreateStruct(
1231
            array(
1232
                'identifier' => $name,
1233
                'policies' => array(),
1234
            )
1235
        );
1236
    }
1237
1238
    /**
1239
     * Instantiates a policy create class.
1240
     *
1241
     * @param string $module
1242
     * @param string $function
1243
     *
1244
     * @return \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct
1245
     */
1246
    public function newPolicyCreateStruct($module, $function)
1247
    {
1248
        return new PolicyCreateStruct(
1249
            array(
1250
                'module' => $module,
1251
                'function' => $function,
1252
                'limitations' => array(),
1253
            )
1254
        );
1255
    }
1256
1257
    /**
1258
     * Instantiates a policy update class.
1259
     *
1260
     * @return \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct
1261
     */
1262
    public function newPolicyUpdateStruct()
1263
    {
1264
        return new PolicyUpdateStruct(
1265
            array(
1266
                'limitations' => array(),
1267
            )
1268
        );
1269
    }
1270
1271
    /**
1272
     * Instantiates a policy update class.
1273
     *
1274
     * @return \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct
1275
     */
1276
    public function newRoleUpdateStruct()
1277
    {
1278
        return new RoleUpdateStruct();
1279
    }
1280
1281
    /**
1282
     * Returns the LimitationType registered with the given identifier.
1283
     *
1284
     * Returns the correct implementation of API Limitation value object
1285
     * based on provided identifier
1286
     *
1287
     * @param string $identifier
1288
     *
1289
     * @return \eZ\Publish\SPI\Limitation\Type
1290
     *
1291
     * @throws \RuntimeException if there is no LimitationType with $identifier
1292
     */
1293
    public function getLimitationType($identifier)
1294
    {
1295
        return $this->limitationService->getLimitationType($identifier);
1296
    }
1297
1298
    /**
1299
     * Returns the LimitationType's assigned to a given module/function.
1300
     *
1301
     * Typically used for:
1302
     *  - Internal validation limitation value use on Policies
1303
     *  - Role admin gui for editing policy limitations incl list limitation options via valueSchema()
1304
     *
1305
     * @param string $module Legacy name of "controller", it's a unique identifier like "content"
1306
     * @param string $function Legacy name of a controller "action", it's a unique within the controller like "read"
1307
     *
1308
     * @return \eZ\Publish\SPI\Limitation\Type[]
1309
     *
1310
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If module/function to limitation type mapping
1311
     *                                                                 refers to a non existing identifier.
1312
     */
1313
    public function getLimitationTypesByModuleFunction($module, $function)
1314
    {
1315
        if (empty($this->settings['policyMap'][$module][$function])) {
1316
            return array();
1317
        }
1318
1319
        $types = array();
1320
        try {
1321
            foreach (array_keys($this->settings['policyMap'][$module][$function]) as $identifier) {
1322
                $types[$identifier] = $this->limitationService->getLimitationType($identifier);
1323
            }
1324
        } catch (LimitationNotFoundException $e) {
1325
            throw new BadStateException(
1326
                "{$module}/{$function}",
1327
                "policyMap configuration is referring to non existing identifier: {$identifier}",
1328
                $e
1329
            );
1330
        }
1331
1332
        return $types;
1333
    }
1334
1335
    /**
1336
     * Validates Policies and Limitations in Role create struct.
1337
     *
1338
     * @uses ::validatePolicy()
1339
     *
1340
     * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct
1341
     *
1342
     * @return \eZ\Publish\Core\FieldType\ValidationError[][][]
1343
     */
1344
    protected function validateRoleCreateStruct(APIRoleCreateStruct $roleCreateStruct)
1345
    {
1346
        $allErrors = array();
1347
        foreach ($roleCreateStruct->getPolicies() as $key => $policyCreateStruct) {
1348
            $errors = $this->validatePolicy(
1349
                $policyCreateStruct->module,
1350
                $policyCreateStruct->function,
1351
                $policyCreateStruct->getLimitations()
1352
            );
1353
1354
            if (!empty($errors)) {
1355
                $allErrors[$key] = $errors;
1356
            }
1357
        }
1358
1359
        return $allErrors;
1360
    }
1361
1362
    /**
1363
     * Validates Policy context: Limitations on a module and function.
1364
     *
1365
     * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If the same limitation is repeated or if
1366
     *                                                                   limitation is not allowed on module/function
1367
     *
1368
     * @param string $module
1369
     * @param string $function
1370
     * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations
1371
     *
1372
     * @return \eZ\Publish\Core\FieldType\ValidationError[][]
1373
     */
1374
    protected function validatePolicy($module, $function, array $limitations)
1375
    {
1376
        if ($module !== '*' && $function !== '*' && !empty($limitations)) {
1377
            $limitationSet = array();
1378
            foreach ($limitations as $limitation) {
1379
                if (isset($limitationSet[$limitation->getIdentifier()])) {
1380
                    throw new InvalidArgumentException(
1381
                        'limitations',
1382
                        "'{$limitation->getIdentifier()}' was found several times among the limitations"
1383
                    );
1384
                }
1385
1386
                if (!isset($this->settings['policyMap'][$module][$function][$limitation->getIdentifier()])) {
1387
                    throw new InvalidArgumentException(
1388
                        'policy',
1389
                        "The limitation '{$limitation->getIdentifier()}' is not applicable on '{$module}/{$function}'"
1390
                    );
1391
                }
1392
1393
                $limitationSet[$limitation->getIdentifier()] = true;
1394
            }
1395
        }
1396
1397
        return $this->limitationService->validateLimitations($limitations);
1398
    }
1399
1400
    /**
1401
     * Validate that assignments not already exists and filter validations against existing.
1402
     *
1403
     * @param mixed $contentId
1404
     * @param SPIRole $spiRole
1405
     * @param array|null $limitation
1406
     *
1407
     * @return array[]|null Filtered version of $limitation
1408
     *
1409
     * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If assignment already exists
1410
     */
1411
    protected function checkAssignmentAndFilterLimitationValues($contentId, SPIRole $spiRole, array $limitation = null)
1412
    {
1413
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($contentId);
1414
        foreach ($spiRoleAssignments as $spiAssignment) {
1415
            // Ignore assignments to other roles
1416
            if ($spiAssignment->roleId !== $spiRole->id) {
1417
                continue;
1418
            }
1419
1420
            // Throw if Role is already assigned without limitations
1421
            if ($spiAssignment->limitationIdentifier === null) {
1422
                throw new InvalidArgumentException(
1423
                    '$role',
1424
                    "Role '{$spiRole->id}' already assigned without limitations"
1425
                );
1426
            }
1427
1428
            // Ignore if we are going to assign without limitations
1429
            if ($limitation === null) {
1430
                continue;
1431
            }
1432
1433
            // Ignore if not assigned with same limitation identifier
1434
            if (!isset($limitation[$spiAssignment->limitationIdentifier])) {
1435
                continue;
1436
            }
1437
1438
            // Throw if Role is already assigned with all the same limitations
1439
            $newValues = array_diff($limitation[$spiAssignment->limitationIdentifier], $spiAssignment->values);
1440
            if (empty($newValues)) {
1441
                throw new InvalidArgumentException(
1442
                    '$role',
1443
                    "Role '{$spiRole->id}' already assigned with same '{$spiAssignment->limitationIdentifier}' value"
1444
                );
1445
            }
1446
1447
            // Continue using the filtered list of limitations
1448
            $limitation[$spiAssignment->limitationIdentifier] = $newValues;
1449
        }
1450
1451
        return $limitation;
1452
    }
1453
}
1454