Completed
Push — validate_hasAccess_6.7 ( f2557d )
by André
28:12
created

RoleService::updateRoleDraft()   C

Complexity

Conditions 9
Paths 17

Size

Total Lines 50
Code Lines 28

Duplication

Lines 3
Ratio 6 %

Importance

Changes 0
Metric Value
cc 9
eloc 28
nc 17
nop 2
dl 3
loc 50
rs 6
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
    const DEFAULT_CORE_POLICYMAP = [
51
        'content' => [
52
            'read' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Group' => true, 'Node' => true, 'Subtree' => true, 'State' => true],
53
            'diff' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Node' => true, 'Subtree' => true],
54
            'view_embed' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Node' => true, 'Subtree' => true],
55
            'create' => ['Class' => true, 'Section' => true, 'ParentOwner' => true, 'ParentGroup' => true, 'ParentClass' => true, 'ParentDepth' => true, 'Node' => true, 'Subtree' => true, 'Language' => true],
56
            'edit' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Group' => true, 'Node' => true, 'Subtree' => true, 'Language' => true, 'State' => true],
57
            'manage_locations' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Subtree' => true],
58
            'hide' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Group' => true, 'Node' => true, 'Subtree' => true, 'Language' => true],
59
            'reverserelatedlist' => null,
60
            'translate' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Node' => true, 'Subtree' => true, 'Language' => true],
61
            'remove' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Node' => true, 'Subtree' => true, 'State' => true],
62
            'versionread' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Status' => true, 'Node' => true, 'Subtree' => true],
63
            'versionremove' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Status' => true, 'Node' => true, 'Subtree' => true],
64
            'translations' =>  null,
65
            'urltranslator' =>  null,
66
            'pendinglist' =>  null,
67
            'restore' =>  null,
68
            'cleantrash' =>  null,
69
        ],
70
        'class' => [
71
            'update' => null,
72
            'create' => null,
73
            'delete' => null,
74
        ],
75
        'state' => [
76
            'assign' => ['Class' => true, 'Section' => true, 'Owner' => true, 'Group' => true, 'Node' => true, 'Subtree' => true, 'State' => true, 'NewState' => true],
77
            'administrate' => null,
78
        ],
79
        'role' => [
80
            'assign' => null,
81
            'update' => null,
82
            'create' => null,
83
            'delete' => null,
84
            'read' => null,
85
        ],
86
        'section' => [
87
            'assign' => ['Class' => true, 'Section' => true, 'Owner' => true, 'NewSection' => true],
88
            'edit' => null,
89
            'view' => null,
90
        ],
91
        'user' => [
92
            'login' => ['SiteAccess' => true],
93
            'password' => null,
94
            'preferences' => null,
95
            'register' => null,
96
            'selfedit' => null,
97
            'activation' => null,
98
        ],
99
    ];
100
101
    /**
102
     * @var \eZ\Publish\API\Repository\Repository
103
     */
104
    protected $repository;
105
106
    /**
107
     * @var \eZ\Publish\SPI\Persistence\User\Handler
108
     */
109
    protected $userHandler;
110
111
    /**
112
     * @var \eZ\Publish\Core\Repository\Helper\LimitationService
113
     */
114
    protected $limitationService;
115
116
    /**
117
     * @var \eZ\Publish\Core\Repository\Helper\RoleDomainMapper
118
     */
119
    protected $roleDomainMapper;
120
121
    /**
122
     * @var array
123
     */
124
    protected $settings;
125
126
    /**
127
     * Setups service with reference to repository object that created it & corresponding handler.
128
     *
129
     * @param \eZ\Publish\API\Repository\Repository $repository
130
     * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler
131
     * @param \eZ\Publish\Core\Repository\Helper\LimitationService $limitationService
132
     * @param \eZ\Publish\Core\Repository\Helper\RoleDomainMapper $roleDomainMapper
133
     * @param array $settings
134
     */
135
    public function __construct(
136
        RepositoryInterface $repository,
137
        Handler $userHandler,
138
        Helper\LimitationService $limitationService,
139
        Helper\RoleDomainMapper $roleDomainMapper,
140
        array $settings = array()
141
    ) {
142
        $this->repository = $repository;
143
        $this->userHandler = $userHandler;
144
        $this->limitationService = $limitationService;
145
        $this->roleDomainMapper = $roleDomainMapper;
146
        $this->settings = $settings;
147
        // Union makes sure default settings are ignored if provided in argument
148
        $this->settings['policyMap'] = isset($settings['policyMap']) ?
149
            $settings['policyMap'] + self::DEFAULT_CORE_POLICYMAP :
150
            self::DEFAULT_CORE_POLICYMAP;
151
    }
152
153
    /**
154
     * Creates a new RoleDraft.
155
     *
156
     * @since 6.0
157
     *
158
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft
159
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
160
     *         if the name of the role already exists or if limitation of the same type
161
     *         is repeated in the policy create struct or if limitation is not allowed on module/function
162
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a policy limitation in the $roleCreateStruct is not valid
163
     *
164
     * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct
165
     *
166
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
167
     */
168
    public function createRole(APIRoleCreateStruct $roleCreateStruct)
169
    {
170
        if (!is_string($roleCreateStruct->identifier) || empty($roleCreateStruct->identifier)) {
171
            throw new InvalidArgumentValue('identifier', $roleCreateStruct->identifier, 'RoleCreateStruct');
172
        }
173
174
        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...
175
            throw new UnauthorizedException('role', 'create');
176
        }
177
178
        try {
179
            $existingRole = $this->loadRoleByIdentifier($roleCreateStruct->identifier);
180
181
            throw new InvalidArgumentException(
182
                '$roleCreateStruct',
183
                "Role '{$existingRole->id}' with the specified identifier '{$roleCreateStruct->identifier}' " .
184
                'already exists'
185
            );
186
        } catch (APINotFoundException $e) {
187
            // Do nothing
188
        }
189
190
        $limitationValidationErrors = $this->validateRoleCreateStruct($roleCreateStruct);
191
        if (!empty($limitationValidationErrors)) {
192
            throw new LimitationValidationException($limitationValidationErrors);
193
        }
194
195
        $spiRoleCreateStruct = $this->roleDomainMapper->buildPersistenceRoleCreateStruct($roleCreateStruct);
196
197
        $this->repository->beginTransaction();
198
        try {
199
            $spiRole = $this->userHandler->createRole($spiRoleCreateStruct);
200
            $this->repository->commit();
201
        } catch (Exception $e) {
202
            $this->repository->rollback();
203
            throw $e;
204
        }
205
206
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
207
    }
208
209
    /**
210
     * Creates a new RoleDraft for an existing Role.
211
     *
212
     * @since 6.0
213
     *
214
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft
215
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the Role already has a RoleDraft that will need to be removed first
216
     *
217
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
218
     *
219
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
220
     */
221 View Code Duplication
    public function createRoleDraft(APIRole $role)
222
    {
223
        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...
224
            throw new UnauthorizedException('role', 'create');
225
        }
226
227
        try {
228
            $this->userHandler->loadRole($role->id, Role::STATUS_DRAFT);
229
230
            // Throw exception, so platformui et al can do conflict management. Follow-up: EZP-24719
231
            throw new InvalidArgumentException(
232
                '$role',
233
                "Cannot create a draft for role '{$role->identifier}' because another draft exists"
234
            );
235
        } catch (APINotFoundException $e) {
236
            $this->repository->beginTransaction();
237
            try {
238
                $spiRole = $this->userHandler->createRoleDraft($role->id);
239
                $this->repository->commit();
240
            } catch (Exception $e) {
241
                $this->repository->rollback();
242
                throw $e;
243
            }
244
        }
245
246
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
247
    }
248
249
    /**
250
     * Loads a RoleDraft for the given id.
251
     *
252
     * @since 6.0
253
     *
254
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this RoleDraft
255
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found
256
     *
257
     * @param mixed $id
258
     *
259
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
260
     */
261 View Code Duplication
    public function loadRoleDraft($id)
262
    {
263
        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...
264
            throw new UnauthorizedException('role', 'read');
265
        }
266
267
        $spiRole = $this->userHandler->loadRole($id, Role::STATUS_DRAFT);
268
269
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
270
    }
271
272
    /**
273
     * Loads a RoleDraft by the ID of the role it was created from.
274
     *
275
     * @param mixed $roleId ID of the role the draft was created from.
276
     *
277
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
278
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found
279
     *
280
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
281
     */
282 View Code Duplication
    public function loadRoleDraftByRoleId($roleId)
283
    {
284
        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...
285
            throw new UnauthorizedException('role', 'read');
286
        }
287
288
        $spiRole = $this->userHandler->loadRoleDraftByRoleId($roleId);
289
290
        return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole);
291
    }
292
293
    /**
294
     * Updates the properties of a RoleDraft.
295
     *
296
     * @since 6.0
297
     *
298
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a RoleDraft
299
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the identifier of the RoleDraft already exists
300
     *
301
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
302
     * @param \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct $roleUpdateStruct
303
     *
304
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
305
     */
306
    public function updateRoleDraft(APIRoleDraft $roleDraft, RoleUpdateStruct $roleUpdateStruct)
307
    {
308 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...
309
            throw new InvalidArgumentValue('identifier', $roleUpdateStruct->identifier, 'RoleUpdateStruct');
310
        }
311
312
        $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
313
314
        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...
315
            throw new UnauthorizedException('role', 'update');
316
        }
317
318
        if ($roleUpdateStruct->identifier !== null) {
319
            try {
320
                /* Throw exception if:
321
                 * - A published role with the same identifier exists, AND
322
                 * - The ID of the published role does not match the original ID of the draft
323
                */
324
                $existingSPIRole = $this->userHandler->loadRoleByIdentifier($roleUpdateStruct->identifier);
325
                $SPIRoleDraft = $this->userHandler->loadRole($loadedRoleDraft->id, Role::STATUS_DRAFT);
326
                if ($existingSPIRole->id != $SPIRoleDraft->originalId) {
327
                    throw new InvalidArgumentException(
328
                        '$roleUpdateStruct',
329
                        "Role '{$existingSPIRole->id}' with the specified identifier '{$roleUpdateStruct->identifier}' " .
330
                        'already exists'
331
                    );
332
                }
333
            } catch (APINotFoundException $e) {
334
                // Do nothing
335
            }
336
        }
337
338
        $this->repository->beginTransaction();
339
        try {
340
            $this->userHandler->updateRole(
341
                new SPIRoleUpdateStruct(
342
                    array(
343
                        'id' => $loadedRoleDraft->id,
344
                        'identifier' => $roleUpdateStruct->identifier ?: $loadedRoleDraft->identifier,
345
                    )
346
                )
347
            );
348
            $this->repository->commit();
349
        } catch (Exception $e) {
350
            $this->repository->rollback();
351
            throw $e;
352
        }
353
354
        return $this->loadRoleDraft($loadedRoleDraft->id);
355
    }
356
357
    /**
358
     * Adds a new policy to the RoleDraft.
359
     *
360
     * @since 6.0
361
     *
362
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to add  a policy
363
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy create
364
     *                                                                        struct or if limitation is not allowed on module/function
365
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyCreateStruct is not valid
366
     *
367
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
368
     * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct
369
     *
370
     * @return \eZ\Publish\API\Repository\Values\User\RoleDraft
371
     */
372 View Code Duplication
    public function addPolicyByRoleDraft(APIRoleDraft $roleDraft, APIPolicyCreateStruct $policyCreateStruct)
373
    {
374
        if (!is_string($policyCreateStruct->module) || empty($policyCreateStruct->module)) {
375
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
376
        }
377
378
        if (!is_string($policyCreateStruct->function) || empty($policyCreateStruct->function)) {
379
            throw new InvalidArgumentValue('function', $policyCreateStruct->function, 'PolicyCreateStruct');
380
        }
381
382
        if ($policyCreateStruct->module === '*' && $policyCreateStruct->function !== '*') {
383
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
384
        }
385
386
        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...
387
            throw new UnauthorizedException('role', 'update');
388
        }
389
390
        $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
391
392
        $limitations = $policyCreateStruct->getLimitations();
393
        $limitationValidationErrors = $this->validatePolicy(
394
            $policyCreateStruct->module,
395
            $policyCreateStruct->function,
396
            $limitations
397
        );
398
        if (!empty($limitationValidationErrors)) {
399
            throw new LimitationValidationException($limitationValidationErrors);
400
        }
401
402
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
403
            $policyCreateStruct->module,
404
            $policyCreateStruct->function,
405
            $limitations
406
        );
407
408
        $this->repository->beginTransaction();
409
        try {
410
            $this->userHandler->addPolicyByRoleDraft($loadedRoleDraft->id, $spiPolicy);
411
            $this->repository->commit();
412
        } catch (Exception $e) {
413
            $this->repository->rollback();
414
            throw $e;
415
        }
416
417
        return $this->loadRoleDraft($loadedRoleDraft->id);
418
    }
419
420
    /**
421
     * Removes a policy from a RoleDraft.
422
     *
423
     * @since 6.0
424
     *
425
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy
426
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if policy does not belong to the given RoleDraft
427
     *
428
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
429
     * @param PolicyDraft $policyDraft the policy to remove from the RoleDraft
430
     *
431
     * @return APIRoleDraft if the authenticated user is not allowed to remove a policy
432
     */
433 View Code Duplication
    public function removePolicyByRoleDraft(APIRoleDraft $roleDraft, PolicyDraft $policyDraft)
434
    {
435
        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...
436
            throw new UnauthorizedException('role', 'update');
437
        }
438
439
        if ($policyDraft->roleId != $roleDraft->id) {
440
            throw new InvalidArgumentException('$policy', 'Policy does not belong to the given role');
441
        }
442
443
        $this->internalDeletePolicy($policyDraft);
444
445
        return $this->loadRoleDraft($roleDraft->id);
446
    }
447
448
    /**
449
     * Updates the limitations of a policy. The module and function cannot be changed and
450
     * the limitations are replaced by the ones in $roleUpdateStruct.
451
     *
452
     * @since 6.0
453
     *
454
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a policy
455
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy update
456
     *                                                                        struct or if limitation is not allowed on module/function
457
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyUpdateStruct is not valid
458
     *
459
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
460
     * @param \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy
461
     * @param \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct $policyUpdateStruct
462
     *
463
     * @return \eZ\Publish\API\Repository\Values\User\PolicyDraft
464
     */
465
    public function updatePolicyByRoleDraft(APIRoleDraft $roleDraft, PolicyDraft $policy, APIPolicyUpdateStruct $policyUpdateStruct)
466
    {
467
        if (!is_string($policy->module)) {
468
            throw new InvalidArgumentValue('module', $policy->module, 'Policy');
469
        }
470
471
        if (!is_string($policy->function)) {
472
            throw new InvalidArgumentValue('function', $policy->function, 'Policy');
473
        }
474
475
        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...
476
            throw new UnauthorizedException('role', 'update');
477
        }
478
479
        if ($policy->roleId !== $roleDraft->id) {
480
            throw new InvalidArgumentException('$policy', "doesn't belong to provided role draft");
481
        }
482
483
        $limitations = $policyUpdateStruct->getLimitations();
484
        $limitationValidationErrors = $this->validatePolicy(
485
            $policy->module,
486
            $policy->function,
487
            $limitations
488
        );
489
        if (!empty($limitationValidationErrors)) {
490
            throw new LimitationValidationException($limitationValidationErrors);
491
        }
492
493
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
494
            $policy->module,
495
            $policy->function,
496
            $limitations
497
        );
498
        $spiPolicy->id = $policy->id;
499
        $spiPolicy->roleId = $policy->roleId;
500
        $spiPolicy->originalId = $policy->originalId;
501
502
        $this->repository->beginTransaction();
503
        try {
504
            $this->userHandler->updatePolicy($spiPolicy);
505
            $this->repository->commit();
506
        } catch (Exception $e) {
507
            $this->repository->rollback();
508
            throw $e;
509
        }
510
511
        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 511 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...
512
    }
513
514
    /**
515
     * Deletes the given RoleDraft.
516
     *
517
     * @since 6.0
518
     *
519
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this RoleDraft
520
     *
521
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
522
     */
523
    public function deleteRoleDraft(APIRoleDraft $roleDraft)
524
    {
525
        $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
526
527
        $this->repository->beginTransaction();
528
        try {
529
            $this->userHandler->deleteRole($loadedRoleDraft->id, Role::STATUS_DRAFT);
530
            $this->repository->commit();
531
        } catch (Exception $e) {
532
            $this->repository->rollback();
533
            throw $e;
534
        }
535
    }
536
537
    /**
538
     * Publishes a given RoleDraft.
539
     *
540
     * @since 6.0
541
     *
542
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to publish this RoleDraft
543
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the role draft cannot be loaded
544
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the role draft has no policies
545
     *
546
     * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft
547
     */
548 View Code Duplication
    public function publishRoleDraft(APIRoleDraft $roleDraft)
549
    {
550
        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...
551
            throw new UnauthorizedException('role', 'update');
552
        }
553
554
        try {
555
            $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id);
556
        } catch (APINotFoundException $e) {
557
            throw new BadStateException(
558
                '$roleDraft',
559
                'The role does not have a draft.',
560
                $e
561
            );
562
        }
563
564
        // TODO: Uncomment when role policy editing is done, see EZP-24711 & EZP-24713
565
        /*if (count($loadedRoleDraft->getPolicies()) === 0) {
566
            throw new InvalidArgumentException(
567
                "\$roleDraft",
568
                'The role draft should have at least one policy.'
569
            );
570
        }*/
571
572
        $this->repository->beginTransaction();
573
        try {
574
            $this->userHandler->publishRoleDraft($loadedRoleDraft->id);
575
            $this->repository->commit();
576
        } catch (Exception $e) {
577
            $this->repository->rollback();
578
            throw $e;
579
        }
580
    }
581
582
    /**
583
     * Updates the name of the role.
584
     *
585
     * @deprecated since 6.0, use {@see updateRoleDraft}
586
     *
587
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a role
588
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the name of the role already exists
589
     *
590
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
591
     * @param \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct $roleUpdateStruct
592
     *
593
     * @return \eZ\Publish\API\Repository\Values\User\Role
594
     */
595
    public function updateRole(APIRole $role, RoleUpdateStruct $roleUpdateStruct)
596
    {
597 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...
598
            throw new InvalidArgumentValue('identifier', $roleUpdateStruct->identifier, 'RoleUpdateStruct');
599
        }
600
601
        $loadedRole = $this->loadRole($role->id);
602
603
        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...
604
            throw new UnauthorizedException('role', 'update');
605
        }
606
607
        if ($roleUpdateStruct->identifier !== null) {
608
            try {
609
                $existingRole = $this->loadRoleByIdentifier($roleUpdateStruct->identifier);
610
611
                if ($existingRole->id != $loadedRole->id) {
612
                    throw new InvalidArgumentException(
613
                        '$roleUpdateStruct',
614
                        'Role with provided identifier already exists'
615
                    );
616
                }
617
            } catch (APINotFoundException $e) {
618
                // Do nothing
619
            }
620
        }
621
622
        $this->repository->beginTransaction();
623
        try {
624
            $this->userHandler->updateRole(
625
                new SPIRoleUpdateStruct(
626
                    array(
627
                        'id' => $loadedRole->id,
628
                        'identifier' => $roleUpdateStruct->identifier ?: $loadedRole->identifier,
629
                    )
630
                )
631
            );
632
            $this->repository->commit();
633
        } catch (Exception $e) {
634
            $this->repository->rollback();
635
            throw $e;
636
        }
637
638
        return $this->loadRole($loadedRole->id);
639
    }
640
641
    /**
642
     * Adds a new policy to the role.
643
     *
644
     * @deprecated since 6.0, use {@see addPolicyByRoleDraft}
645
     *
646
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to add  a policy
647
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy create
648
     *                                                                        struct or if limitation is not allowed on module/function
649
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyCreateStruct is not valid
650
     *
651
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
652
     * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct
653
     *
654
     * @return \eZ\Publish\API\Repository\Values\User\Role
655
     */
656 View Code Duplication
    public function addPolicy(APIRole $role, APIPolicyCreateStruct $policyCreateStruct)
657
    {
658
        if (!is_string($policyCreateStruct->module) || empty($policyCreateStruct->module)) {
659
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
660
        }
661
662
        if (!is_string($policyCreateStruct->function) || empty($policyCreateStruct->function)) {
663
            throw new InvalidArgumentValue('function', $policyCreateStruct->function, 'PolicyCreateStruct');
664
        }
665
666
        if ($policyCreateStruct->module === '*' && $policyCreateStruct->function !== '*') {
667
            throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct');
668
        }
669
670
        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...
671
            throw new UnauthorizedException('role', 'update');
672
        }
673
674
        $loadedRole = $this->loadRole($role->id);
675
676
        $limitations = $policyCreateStruct->getLimitations();
677
        $limitationValidationErrors = $this->validatePolicy(
678
            $policyCreateStruct->module,
679
            $policyCreateStruct->function,
680
            $limitations
681
        );
682
        if (!empty($limitationValidationErrors)) {
683
            throw new LimitationValidationException($limitationValidationErrors);
684
        }
685
686
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
687
            $policyCreateStruct->module,
688
            $policyCreateStruct->function,
689
            $limitations
690
        );
691
692
        $this->repository->beginTransaction();
693
        try {
694
            $this->userHandler->addPolicy($loadedRole->id, $spiPolicy);
695
            $this->repository->commit();
696
        } catch (Exception $e) {
697
            $this->repository->rollback();
698
            throw $e;
699
        }
700
701
        return $this->loadRole($loadedRole->id);
702
    }
703
704
    /**
705
     * Removes a policy from the role.
706
     *
707
     * @deprecated since 5.3, use {@link removePolicyByRoleDraft()} instead.
708
     *
709
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy
710
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if policy does not belong to the given role
711
     *
712
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
713
     * @param \eZ\Publish\API\Repository\Values\User\Policy $policy the policy to remove from the role
714
     *
715
     * @return \eZ\Publish\API\Repository\Values\User\Role the updated role
716
     */
717 View Code Duplication
    public function removePolicy(APIRole $role, APIPolicy $policy)
718
    {
719
        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...
720
            throw new UnauthorizedException('role', 'update');
721
        }
722
723
        if ($policy->roleId != $role->id) {
724
            throw new InvalidArgumentException('$policy', 'Policy does not belong to the given role');
725
        }
726
727
        $this->internalDeletePolicy($policy);
728
729
        return $this->loadRole($role->id);
730
    }
731
732
    /**
733
     * Deletes a policy.
734
     *
735
     * @deprecated since 6.0, use {@link removePolicyByRoleDraft()} instead.
736
     *
737
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy
738
     *
739
     * @param \eZ\Publish\API\Repository\Values\User\Policy $policy the policy to delete
740
     */
741
    public function deletePolicy(APIPolicy $policy)
742
    {
743
        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...
744
            throw new UnauthorizedException('role', 'update');
745
        }
746
747
        $this->internalDeletePolicy($policy);
748
    }
749
750
    /**
751
     * Deletes a policy.
752
     *
753
     * Used by {@link removePolicy()} and {@link deletePolicy()}
754
     *
755
     * @param APIPolicy $policy
756
     *
757
     * @throws \Exception
758
     */
759
    protected function internalDeletePolicy(APIPolicy $policy)
760
    {
761
        $this->repository->beginTransaction();
762
        try {
763
            $this->userHandler->deletePolicy($policy->id);
764
            $this->repository->commit();
765
        } catch (Exception $e) {
766
            $this->repository->rollback();
767
            throw $e;
768
        }
769
    }
770
771
    /**
772
     * Updates the limitations of a policy. The module and function cannot be changed and
773
     * the limitations are replaced by the ones in $roleUpdateStruct.
774
     *
775
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a policy
776
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy update
777
     *                                                                        struct or if limitation is not allowed on module/function
778
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyUpdateStruct is not valid
779
     *
780
     * @param \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct $policyUpdateStruct
781
     * @param \eZ\Publish\API\Repository\Values\User\Policy $policy
782
     *
783
     * @return \eZ\Publish\API\Repository\Values\User\Policy
784
     */
785
    public function updatePolicy(APIPolicy $policy, APIPolicyUpdateStruct $policyUpdateStruct)
786
    {
787
        if (!is_string($policy->module)) {
788
            throw new InvalidArgumentValue('module', $policy->module, 'Policy');
789
        }
790
791
        if (!is_string($policy->function)) {
792
            throw new InvalidArgumentValue('function', $policy->function, 'Policy');
793
        }
794
795
        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...
796
            throw new UnauthorizedException('role', 'update');
797
        }
798
799
        $limitations = $policyUpdateStruct->getLimitations();
800
        $limitationValidationErrors = $this->validatePolicy(
801
            $policy->module,
802
            $policy->function,
803
            $limitations
804
        );
805
        if (!empty($limitationValidationErrors)) {
806
            throw new LimitationValidationException($limitationValidationErrors);
807
        }
808
809
        $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject(
810
            $policy->module,
811
            $policy->function,
812
            $limitations
813
        );
814
        $spiPolicy->id = $policy->id;
815
        $spiPolicy->roleId = $policy->roleId;
816
817
        $this->repository->beginTransaction();
818
        try {
819
            $this->userHandler->updatePolicy($spiPolicy);
820
            $this->repository->commit();
821
        } catch (Exception $e) {
822
            $this->repository->rollback();
823
            throw $e;
824
        }
825
826
        return $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy);
827
    }
828
829
    /**
830
     * Loads a role for the given id.
831
     *
832
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
833
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a role with the given id was not found
834
     *
835
     * @param mixed $id
836
     *
837
     * @return \eZ\Publish\API\Repository\Values\User\Role
838
     */
839 View Code Duplication
    public function loadRole($id)
840
    {
841
        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...
842
            throw new UnauthorizedException('role', 'read');
843
        }
844
845
        $spiRole = $this->userHandler->loadRole($id);
846
847
        return $this->roleDomainMapper->buildDomainRoleObject($spiRole);
848
    }
849
850
    /**
851
     * Loads a role for the given identifier.
852
     *
853
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
854
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a role with the given name was not found
855
     *
856
     * @param string $identifier
857
     *
858
     * @return \eZ\Publish\API\Repository\Values\User\Role
859
     */
860 View Code Duplication
    public function loadRoleByIdentifier($identifier)
861
    {
862
        if (!is_string($identifier)) {
863
            throw new InvalidArgumentValue('identifier', $identifier);
864
        }
865
866
        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...
867
            throw new UnauthorizedException('role', 'read');
868
        }
869
870
        $spiRole = $this->userHandler->loadRoleByIdentifier($identifier);
871
872
        return $this->roleDomainMapper->buildDomainRoleObject($spiRole);
873
    }
874
875
    /**
876
     * Loads all roles.
877
     *
878
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the roles
879
     *
880
     * @return \eZ\Publish\API\Repository\Values\User\Role[]
881
     */
882 View Code Duplication
    public function loadRoles()
883
    {
884
        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...
885
            throw new UnauthorizedException('role', 'read');
886
        }
887
888
        $spiRoles = $this->userHandler->loadRoles();
889
890
        $roles = array();
891
        foreach ($spiRoles as $spiRole) {
892
            $roles[] = $this->roleDomainMapper->buildDomainRoleObject($spiRole);
893
        }
894
895
        return $roles;
896
    }
897
898
    /**
899
     * Deletes the given role.
900
     *
901
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this role
902
     *
903
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
904
     */
905
    public function deleteRole(APIRole $role)
906
    {
907
        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...
908
            throw new UnauthorizedException('role', 'delete');
909
        }
910
911
        $loadedRole = $this->loadRole($role->id);
912
913
        $this->repository->beginTransaction();
914
        try {
915
            $this->userHandler->deleteRole($loadedRole->id);
916
            $this->repository->commit();
917
        } catch (Exception $e) {
918
            $this->repository->rollback();
919
            throw $e;
920
        }
921
    }
922
923
    /**
924
     * Loads all policies from roles which are assigned to a user or to user groups to which the user belongs.
925
     *
926
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given id was not found
927
     *
928
     * @param mixed $userId
929
     *
930
     * @return \eZ\Publish\API\Repository\Values\User\Policy[]
931
     */
932
    public function loadPoliciesByUserId($userId)
933
    {
934
        $spiPolicies = $this->userHandler->loadPoliciesByUserId($userId);
935
936
        $policies = array();
937
        foreach ($spiPolicies as $spiPolicy) {
938
            $policies[] = $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy);
939
        }
940
941
        if (empty($policies)) {
942
            $this->userHandler->load($userId);
943
        }// For NotFoundException in case userId is invalid
944
945
        return $policies;
946
    }
947
948
    /**
949
     * Assigns a role to the given user group.
950
     *
951
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role
952
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid
953
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists
954
     *
955
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
956
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
957
     * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation)
958
     */
959 View Code Duplication
    public function assignRoleToUserGroup(APIRole $role, UserGroup $userGroup, RoleLimitation $roleLimitation = null)
960
    {
961
        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...
962
            throw new UnauthorizedException('role', 'assign');
963
        }
964
965
        if ($roleLimitation === null) {
966
            $limitation = null;
967
        } else {
968
            $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation);
969
            if (!empty($limitationValidationErrors)) {
970
                throw new LimitationValidationException($limitationValidationErrors);
971
            }
972
973
            $limitation = array($roleLimitation->getIdentifier() => $roleLimitation->limitationValues);
974
        }
975
976
        // Check if objects exists
977
        $spiRole = $this->userHandler->loadRole($role->id);
978
        $loadedUserGroup = $this->repository->getUserService()->loadUserGroup($userGroup->id);
979
980
        $limitation = $this->checkAssignmentAndFilterLimitationValues($loadedUserGroup->id, $spiRole, $limitation);
981
982
        $this->repository->beginTransaction();
983
        try {
984
            $this->userHandler->assignRole(
985
                $loadedUserGroup->id,
986
                $spiRole->id,
987
                $limitation
988
            );
989
            $this->repository->commit();
990
        } catch (Exception $e) {
991
            $this->repository->rollback();
992
            throw $e;
993
        }
994
    }
995
996
    /**
997
     * removes a role from the given user group.
998
     *
999
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role
1000
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException  If the role is not assigned to the given user group
1001
     *
1002
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
1003
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
1004
     */
1005 View Code Duplication
    public function unassignRoleFromUserGroup(APIRole $role, UserGroup $userGroup)
1006
    {
1007
        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...
1008
            throw new UnauthorizedException('role', 'assign');
1009
        }
1010
1011
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id);
1012
        $isAssigned = false;
1013
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1014
            if ($spiRoleAssignment->roleId === $role->id) {
1015
                $isAssigned = true;
1016
                break;
1017
            }
1018
        }
1019
1020
        if (!$isAssigned) {
1021
            throw new InvalidArgumentException(
1022
                '$userGroup',
1023
                'Role is not assigned to the given UserGroup'
1024
            );
1025
        }
1026
1027
        $this->repository->beginTransaction();
1028
        try {
1029
            $this->userHandler->unassignRole($userGroup->id, $role->id);
1030
            $this->repository->commit();
1031
        } catch (Exception $e) {
1032
            $this->repository->rollback();
1033
            throw $e;
1034
        }
1035
    }
1036
1037
    /**
1038
     * Assigns a role to the given user.
1039
     *
1040
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role
1041
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid
1042
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists
1043
     *
1044
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
1045
     * @param \eZ\Publish\API\Repository\Values\User\User $user
1046
     * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation)
1047
     */
1048 View Code Duplication
    public function assignRoleToUser(APIRole $role, User $user, RoleLimitation $roleLimitation = null)
1049
    {
1050
        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...
1051
            throw new UnauthorizedException('role', 'assign');
1052
        }
1053
1054
        if ($roleLimitation === null) {
1055
            $limitation = null;
1056
        } else {
1057
            $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation);
1058
            if (!empty($limitationValidationErrors)) {
1059
                throw new LimitationValidationException($limitationValidationErrors);
1060
            }
1061
1062
            $limitation = array($roleLimitation->getIdentifier() => $roleLimitation->limitationValues);
1063
        }
1064
1065
        // Check if objects exists
1066
        $spiRole = $this->userHandler->loadRole($role->id);
1067
        $spiUser = $this->userHandler->load($user->id);
1068
1069
        $limitation = $this->checkAssignmentAndFilterLimitationValues($spiUser->id, $spiRole, $limitation);
1070
1071
        $this->repository->beginTransaction();
1072
        try {
1073
            $this->userHandler->assignRole(
1074
                $spiUser->id,
1075
                $spiRole->id,
1076
                $limitation
1077
            );
1078
            $this->repository->commit();
1079
        } catch (Exception $e) {
1080
            $this->repository->rollback();
1081
            throw $e;
1082
        }
1083
    }
1084
1085
    /**
1086
     * removes a role from the given user.
1087
     *
1088
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role
1089
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the role is not assigned to the user
1090
     *
1091
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
1092
     * @param \eZ\Publish\API\Repository\Values\User\User $user
1093
     */
1094 View Code Duplication
    public function unassignRoleFromUser(APIRole $role, User $user)
1095
    {
1096
        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...
1097
            throw new UnauthorizedException('role', 'assign');
1098
        }
1099
1100
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($user->id);
1101
        $isAssigned = false;
1102
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1103
            if ($spiRoleAssignment->roleId === $role->id) {
1104
                $isAssigned = true;
1105
                break;
1106
            }
1107
        }
1108
1109
        if (!$isAssigned) {
1110
            throw new InvalidArgumentException(
1111
                '$user',
1112
                'Role is not assigned to the given User'
1113
            );
1114
        }
1115
1116
        $this->repository->beginTransaction();
1117
        try {
1118
            $this->userHandler->unassignRole($user->id, $role->id);
1119
            $this->repository->commit();
1120
        } catch (Exception $e) {
1121
            $this->repository->rollback();
1122
            throw $e;
1123
        }
1124
    }
1125
1126
    /**
1127
     * Removes the given role assignment.
1128
     *
1129
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role assignment
1130
     *
1131
     * @param \eZ\Publish\API\Repository\Values\User\RoleAssignment $roleAssignment
1132
     */
1133
    public function removeRoleAssignment(RoleAssignment $roleAssignment)
1134
    {
1135
        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...
1136
            throw new UnauthorizedException('role', 'assign');
1137
        }
1138
1139
        $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignment->id);
1140
1141
        $this->repository->beginTransaction();
1142
        try {
1143
            $this->userHandler->removeRoleAssignment($spiRoleAssignment->id);
1144
            $this->repository->commit();
1145
        } catch (Exception $e) {
1146
            $this->repository->rollback();
1147
            throw $e;
1148
        }
1149
    }
1150
1151
    /**
1152
     * Loads a role assignment for the given id.
1153
     *
1154
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role
1155
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the role assignment was not found
1156
     *
1157
     * @param mixed $roleAssignmentId
1158
     *
1159
     * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment
1160
     */
1161
    public function loadRoleAssignment($roleAssignmentId)
1162
    {
1163
        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...
1164
            throw new UnauthorizedException('role', 'read');
1165
        }
1166
1167
        $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignmentId);
1168
        $userService = $this->repository->getUserService();
1169
        $role = $this->loadRole($spiRoleAssignment->roleId);
1170
        $roleAssignment = null;
1171
1172
        // First check if the Role is assigned to a User
1173
        // If no User is found, see if it belongs to a UserGroup
1174
        try {
1175
            $user = $userService->loadUser($spiRoleAssignment->contentId);
1176
            $roleAssignment = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject(
1177
                $spiRoleAssignment,
1178
                $user,
1179
                $role
1180
            );
1181
        } catch (APINotFoundException $e) {
1182
            try {
1183
                $userGroup = $userService->loadUserGroup($spiRoleAssignment->contentId);
1184
                $roleAssignment = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1185
                    $spiRoleAssignment,
1186
                    $userGroup,
1187
                    $role
1188
                );
1189
            } catch (APINotFoundException $e) {
1190
                // Do nothing
1191
            }
1192
        }
1193
1194
        return $roleAssignment;
1195
    }
1196
1197
    /**
1198
     * Returns the assigned user and user groups to this role.
1199
     *
1200
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role
1201
     *
1202
     * @param \eZ\Publish\API\Repository\Values\User\Role $role
1203
     *
1204
     * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment[]
1205
     */
1206
    public function getRoleAssignments(APIRole $role)
1207
    {
1208
        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...
1209
            throw new UnauthorizedException('role', 'read');
1210
        }
1211
1212
        $userService = $this->repository->getUserService();
1213
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByRoleId($role->id);
1214
        $roleAssignments = array();
1215
1216
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1217
            // First check if the Role is assigned to a User
1218
            // If no User is found, see if it belongs to a UserGroup
1219
            try {
1220
                $user = $userService->loadUser($spiRoleAssignment->contentId);
1221
                $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject(
1222
                    $spiRoleAssignment,
1223
                    $user,
1224
                    $role
1225
                );
1226
            } catch (APINotFoundException $e) {
1227
                try {
1228
                    $userGroup = $userService->loadUserGroup($spiRoleAssignment->contentId);
1229
                    $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1230
                        $spiRoleAssignment,
1231
                        $userGroup,
1232
                        $role
1233
                    );
1234
                } catch (APINotFoundException $e) {
1235
                    // Do nothing
1236
                }
1237
            }
1238
        }
1239
1240
        return $roleAssignments;
1241
    }
1242
1243
    /**
1244
     * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser()
1245
     */
1246
    public function getRoleAssignmentsForUser(User $user, $inherited = false)
1247
    {
1248
        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...
1249
            throw new UnauthorizedException('role', 'read');
1250
        }
1251
1252
        $roleAssignments = array();
1253
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($user->id, $inherited);
1254
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1255
            $role = $this->loadRole($spiRoleAssignment->roleId);
1256
            if (!$inherited || $spiRoleAssignment->contentId == $user->id) {
1257
                $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject(
1258
                    $spiRoleAssignment,
1259
                    $user,
1260
                    $role
1261
                );
1262
            } else {
1263
                $userGroup = $this->repository->getUserService()->loadUserGroup($spiRoleAssignment->contentId);
1264
                $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1265
                    $spiRoleAssignment,
1266
                    $userGroup,
1267
                    $role
1268
                );
1269
            }
1270
        }
1271
1272
        return $roleAssignments;
1273
    }
1274
1275
    /**
1276
     * Returns the roles assigned to the given user group.
1277
     *
1278
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role
1279
     *
1280
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup
1281
     *
1282
     * @return \eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment[]
1283
     */
1284
    public function getRoleAssignmentsForUserGroup(UserGroup $userGroup)
1285
    {
1286
        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...
1287
            throw new UnauthorizedException('role', 'read');
1288
        }
1289
1290
        $roleAssignments = array();
1291
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id);
1292
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
1293
            $role = $this->loadRole($spiRoleAssignment->roleId);
1294
            $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject(
1295
                $spiRoleAssignment,
1296
                $userGroup,
1297
                $role
1298
            );
1299
        }
1300
1301
        return $roleAssignments;
1302
    }
1303
1304
    /**
1305
     * Instantiates a role create class.
1306
     *
1307
     * @param string $name
1308
     *
1309
     * @return \eZ\Publish\API\Repository\Values\User\RoleCreateStruct
1310
     */
1311
    public function newRoleCreateStruct($name)
1312
    {
1313
        return new RoleCreateStruct(
1314
            array(
1315
                'identifier' => $name,
1316
                'policies' => array(),
1317
            )
1318
        );
1319
    }
1320
1321
    /**
1322
     * Instantiates a policy create class.
1323
     *
1324
     * @param string $module
1325
     * @param string $function
1326
     *
1327
     * @return \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct
1328
     */
1329
    public function newPolicyCreateStruct($module, $function)
1330
    {
1331
        return new PolicyCreateStruct(
1332
            array(
1333
                'module' => $module,
1334
                'function' => $function,
1335
                'limitations' => array(),
1336
            )
1337
        );
1338
    }
1339
1340
    /**
1341
     * Instantiates a policy update class.
1342
     *
1343
     * @return \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct
1344
     */
1345
    public function newPolicyUpdateStruct()
1346
    {
1347
        return new PolicyUpdateStruct(
1348
            array(
1349
                'limitations' => array(),
1350
            )
1351
        );
1352
    }
1353
1354
    /**
1355
     * Instantiates a policy update class.
1356
     *
1357
     * @return \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct
1358
     */
1359
    public function newRoleUpdateStruct()
1360
    {
1361
        return new RoleUpdateStruct();
1362
    }
1363
1364
    /**
1365
     * Returns the LimitationType registered with the given identifier.
1366
     *
1367
     * Returns the correct implementation of API Limitation value object
1368
     * based on provided identifier
1369
     *
1370
     * @param string $identifier
1371
     *
1372
     * @return \eZ\Publish\SPI\Limitation\Type
1373
     *
1374
     * @throws \RuntimeException if there is no LimitationType with $identifier
1375
     */
1376
    public function getLimitationType($identifier)
1377
    {
1378
        return $this->limitationService->getLimitationType($identifier);
1379
    }
1380
1381
    /**
1382
     * Returns the LimitationType's assigned to a given module/function.
1383
     *
1384
     * Typically used for:
1385
     *  - Internal validation limitation value use on Policies
1386
     *  - Role admin gui for editing policy limitations incl list limitation options via valueSchema()
1387
     *
1388
     * @param string $module Legacy name of "controller", it's a unique identifier like "content"
1389
     * @param string $function Legacy name of a controller "action", it's a unique within the controller like "read"
1390
     *
1391
     * @return \eZ\Publish\SPI\Limitation\Type[]
1392
     *
1393
     * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If module/function to limitation type mapping
1394
     *                                                                 refers to a non existing identifier.
1395
     */
1396
    public function getLimitationTypesByModuleFunction($module, $function)
1397
    {
1398
        if (empty($this->settings['policyMap'][$module][$function])) {
1399
            return array();
1400
        }
1401
1402
        $types = array();
1403
        try {
1404
            foreach (array_keys($this->settings['policyMap'][$module][$function]) as $identifier) {
1405
                $types[$identifier] = $this->limitationService->getLimitationType($identifier);
1406
            }
1407
        } catch (LimitationNotFoundException $e) {
1408
            throw new BadStateException(
1409
                "{$module}/{$function}",
1410
                "policyMap configuration is referring to non existing identifier: {$identifier}",
1411
                $e
1412
            );
1413
        }
1414
1415
        return $types;
1416
    }
1417
1418
    /**
1419
     * Validates Policies and Limitations in Role create struct.
1420
     *
1421
     * @uses ::validatePolicy()
1422
     *
1423
     * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct
1424
     *
1425
     * @return \eZ\Publish\Core\FieldType\ValidationError[][][]
1426
     */
1427
    protected function validateRoleCreateStruct(APIRoleCreateStruct $roleCreateStruct)
1428
    {
1429
        $allErrors = array();
1430
        foreach ($roleCreateStruct->getPolicies() as $key => $policyCreateStruct) {
1431
            $errors = $this->validatePolicy(
1432
                $policyCreateStruct->module,
1433
                $policyCreateStruct->function,
1434
                $policyCreateStruct->getLimitations()
1435
            );
1436
1437
            if (!empty($errors)) {
1438
                $allErrors[$key] = $errors;
1439
            }
1440
        }
1441
1442
        return $allErrors;
1443
    }
1444
1445
    /**
1446
     * Validates Policy context: Limitations on a module and function.
1447
     *
1448
     * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If the same limitation is repeated or if
1449
     *                                                                   limitation is not allowed on module/function
1450
     *
1451
     * @param string $module
1452
     * @param string $function
1453
     * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations
1454
     *
1455
     * @return \eZ\Publish\Core\FieldType\ValidationError[][]
1456
     */
1457
    protected function validatePolicy($module, $function, array $limitations)
1458
    {
1459
        if ($module !== '*' && $function !== '*' && !empty($limitations)) {
1460
            $limitationSet = array();
1461
            foreach ($limitations as $limitation) {
1462
                if (isset($limitationSet[$limitation->getIdentifier()])) {
1463
                    throw new InvalidArgumentException(
1464
                        'limitations',
1465
                        "'{$limitation->getIdentifier()}' was found several times among the limitations"
1466
                    );
1467
                }
1468
1469
                if (!isset($this->settings['policyMap'][$module][$function][$limitation->getIdentifier()])) {
1470
                    throw new InvalidArgumentException(
1471
                        'policy',
1472
                        "The limitation '{$limitation->getIdentifier()}' is not applicable on '{$module}/{$function}'"
1473
                    );
1474
                }
1475
1476
                $limitationSet[$limitation->getIdentifier()] = true;
1477
            }
1478
        }
1479
1480
        return $this->limitationService->validateLimitations($limitations);
1481
    }
1482
1483
    /**
1484
     * Validate that assignments not already exists and filter validations against existing.
1485
     *
1486
     * @param mixed $contentId
1487
     * @param SPIRole $spiRole
1488
     * @param array|null $limitation
1489
     *
1490
     * @return array[]|null Filtered version of $limitation
1491
     *
1492
     * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If assignment already exists
1493
     */
1494
    protected function checkAssignmentAndFilterLimitationValues($contentId, SPIRole $spiRole, array $limitation = null)
1495
    {
1496
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($contentId);
1497
        foreach ($spiRoleAssignments as $spiAssignment) {
1498
            // Ignore assignments to other roles
1499
            if ($spiAssignment->roleId !== $spiRole->id) {
1500
                continue;
1501
            }
1502
1503
            // Throw if Role is already assigned without limitations
1504
            if ($spiAssignment->limitationIdentifier === null) {
1505
                throw new InvalidArgumentException(
1506
                    '$role',
1507
                    "Role '{$spiRole->id}' already assigned without limitations"
1508
                );
1509
            }
1510
1511
            // Ignore if we are going to assign without limitations
1512
            if ($limitation === null) {
1513
                continue;
1514
            }
1515
1516
            // Ignore if not assigned with same limitation identifier
1517
            if (!isset($limitation[$spiAssignment->limitationIdentifier])) {
1518
                continue;
1519
            }
1520
1521
            // Throw if Role is already assigned with all the same limitations
1522
            $newValues = array_diff($limitation[$spiAssignment->limitationIdentifier], $spiAssignment->values);
1523
            if (empty($newValues)) {
1524
                throw new InvalidArgumentException(
1525
                    '$role',
1526
                    "Role '{$spiRole->id}' already assigned with same '{$spiAssignment->limitationIdentifier}' value"
1527
                );
1528
            }
1529
1530
            // Continue using the filtered list of limitations
1531
            $limitation[$spiAssignment->limitationIdentifier] = $newValues;
1532
        }
1533
1534
        return $limitation;
1535
    }
1536
}
1537