Completed
Push — EZP-31383-roles-copying ( d0932a )
by
unknown
12:44
created

Handler::createRole()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the UserHandler interface.
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\Persistence\Legacy\User;
10
11
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
12
use eZ\Publish\SPI\Persistence\User;
13
use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct;
14
use eZ\Publish\SPI\Persistence\User\Handler as BaseUserHandler;
15
use eZ\Publish\SPI\Persistence\User\Role;
16
use eZ\Publish\SPI\Persistence\User\RoleCreateStruct;
17
use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct;
18
use eZ\Publish\SPI\Persistence\User\Policy;
19
use eZ\Publish\Core\Persistence\Legacy\Exception\RoleNotFound;
20
use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway as RoleGateway;
21
use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter;
22
use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound;
23
use LogicException;
24
25
/**
26
 * Storage Engine handler for user module.
27
 */
28
class Handler implements BaseUserHandler
29
{
30
    /**
31
     * Gateway for storing user data.
32
     *
33
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Gateway
34
     */
35
    protected $userGateway;
36
37
    /**
38
     * Gateway for storing role data.
39
     *
40
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway
41
     */
42
    protected $roleGateway;
43
44
    /**
45
     * Mapper for user related objects.
46
     *
47
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Mapper
48
     */
49
    protected $mapper;
50
51
    /** @var \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter */
52
    protected $limitationConverter;
53
54
    /**
55
     * Construct from userGateway.
56
     *
57
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Gateway $userGateway
58
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway $roleGateway
59
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Mapper $mapper
60
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter $limitationConverter
61
     */
62
    public function __construct(Gateway $userGateway, RoleGateway $roleGateway, Mapper $mapper, LimitationConverter $limitationConverter)
63
    {
64
        $this->userGateway = $userGateway;
65
        $this->roleGateway = $roleGateway;
66
        $this->mapper = $mapper;
67
        $this->limitationConverter = $limitationConverter;
68
    }
69
70
    /**
71
     * Create a user.
72
     *
73
     * The User struct used to create the user will contain an ID which is used
74
     * to reference the user.
75
     *
76
     * @param \eZ\Publish\SPI\Persistence\User $user
77
     *
78
     * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
79
     */
80
    public function create(User $user)
81
    {
82
        throw new NotImplementedException('This method should not be called, creation is done via content handler.');
83
    }
84
85
    /**
86
     * Loads user with user ID.
87
     *
88
     * @param mixed $userId
89
     *
90
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
91
     *
92
     * @return \eZ\Publish\SPI\Persistence\User
93
     */
94 View Code Duplication
    public function load($userId)
95
    {
96
        $data = $this->userGateway->load($userId);
97
98
        if (empty($data)) {
99
            throw new NotFound('user', $userId);
100
        }
101
102
        return $this->mapper->mapUser(reset($data));
103
    }
104
105
    /**
106
     * Loads user with user login.
107
     *
108
     * @param string $login
109
     *
110
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
111
     *
112
     * @return \eZ\Publish\SPI\Persistence\User
113
     */
114 View Code Duplication
    public function loadByLogin($login)
115
    {
116
        $data = $this->userGateway->loadByLogin($login);
117
118
        if (empty($data)) {
119
            throw new NotFound('user', $login);
120
        } elseif (count($data) > 1) {
121
            throw new LogicException("Found more then one user with login '{$login}'");
122
        }
123
124
        return $this->mapper->mapUser($data[0]);
125
    }
126
127
    /**
128
     * Loads user(s) with user email.
129
     *
130
     * As earlier eZ Publish versions supported several users having same email (ini config),
131
     * this function may return several users.
132
     *
133
     * @param string $email
134
     *
135
     * @return \eZ\Publish\SPI\Persistence\User
136
     */
137 View Code Duplication
    public function loadByEmail(string $email): User
138
    {
139
        $data = $this->userGateway->loadByEmail($email);
140
141
        if (empty($data)) {
142
            throw new NotFound('user', $email);
143
        } elseif (count($data) > 1) {
144
            throw new LogicException("Found more then one user with login '{$email}'");
145
        }
146
147
        return $this->mapper->mapUser($data[0]);
148
    }
149
150
    /**
151
     * Loads user(s) with user email.
152
     *
153
     * As earlier eZ Publish versions supported several users having same email (ini config),
154
     * this function may return several users.
155
     *
156
     * @param string $email
157
     *
158
     * @return \eZ\Publish\SPI\Persistence\User[]
159
     */
160
    public function loadUsersByEmail(string $email): array
161
    {
162
        $data = $this->userGateway->loadByEmail($email);
163
164
        if (empty($data)) {
165
            return [];
166
        }
167
168
        return $this->mapper->mapUsers($data);
169
    }
170
171
    /**
172
     * Loads user with user hash.
173
     *
174
     * @param string $hash
175
     *
176
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
177
     *
178
     * @return \eZ\Publish\SPI\Persistence\User
179
     */
180 View Code Duplication
    public function loadUserByToken($hash)
181
    {
182
        $data = $this->userGateway->loadUserByToken($hash);
183
184
        if (empty($data)) {
185
            throw new NotFound('user', $hash);
186
        }
187
188
        return $this->mapper->mapUser(reset($data));
189
    }
190
191
    /**
192
     * Update the user information specified by the user struct.
193
     *
194
     * @param \eZ\Publish\SPI\Persistence\User $user
195
     *
196
     * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
197
     */
198
    public function update(User $user)
199
    {
200
        throw new NotImplementedException('This method should not be called, update is done via content handler.');
201
    }
202
203
    /**
204
     * Update the user token information specified by the userToken struct.
205
     *
206
     * @param \eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct $userTokenUpdateStruct
207
     */
208
    public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct)
209
    {
210
        $this->userGateway->updateUserToken($userTokenUpdateStruct);
211
    }
212
213
    /**
214
     * Expires user account key with user hash.
215
     *
216
     * @param string $hash
217
     */
218
    public function expireUserToken($hash)
219
    {
220
        $this->userGateway->expireUserToken($hash);
221
    }
222
223
    /**
224
     * Delete user with the given ID.
225
     *
226
     * @param mixed $userId
227
     *
228
     * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
229
     */
230
    public function delete($userId)
231
    {
232
        throw new NotImplementedException('This method should not be called, delete is done via content handler.');
233
    }
234
235
    /**
236
     * Create new role draft.
237
     *
238
     * Sets status to Role::STATUS_DRAFT on the new returned draft.
239
     *
240
     * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct
241
     *
242
     * @return \eZ\Publish\SPI\Persistence\User\Role
243
     */
244
    public function createRole(RoleCreateStruct $createStruct)
245
    {
246
        return $this->internalCreateRole($createStruct);
247
    }
248
249
    /**
250
     * Creates a draft of existing defined role.
251
     *
252
     * Sets status to Role::STATUS_DRAFT on the new returned draft.
253
     *
254
     * @param mixed $roleId
255
     *
256
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with defined status is not found
257
     *
258
     * @return \eZ\Publish\SPI\Persistence\User\Role
259
     */
260
    public function createRoleDraft($roleId)
261
    {
262
        $createStruct = $this->mapper->createCreateStructFromRole(
263
            $this->loadRole($roleId)
264
        );
265
266
        return $this->internalCreateRole($createStruct, $roleId);
267
    }
268
269
    /**
270
     * Internal method for creating Role.
271
     *
272
     * Used by self::createRole() and self::createRoleDraft()
273
     *
274
     * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct
275
     * @param mixed|null $roleId Used by self::createRoleDraft() to retain Role id in the draft
276
     *
277
     * @return \eZ\Publish\SPI\Persistence\User\Role
278
     */
279
    protected function internalCreateRole(RoleCreateStruct $createStruct, $roleId = null)
280
    {
281
        $createStruct = clone $createStruct;
282
        $role = $this->mapper->createRoleFromCreateStruct(
283
            $createStruct
284
        );
285
        $role->id = $roleId;
286
        $role->status = Role::STATUS_DRAFT;
287
288
        $this->roleGateway->createRole($role);
289
290
        foreach ($role->policies as $policy) {
291
            $this->addPolicyByRoleDraft($role->id, $policy);
292
        }
293
294
        return $role;
295
    }
296
297
    /**
298
     * Copies an existing role.
299
     */
300
    public function copyRole(User\RoleCopyStruct $copyStruct): Role
301
    {
302
        $role = $this->mapper->createRoleFromCopyStruct(
303
            $copyStruct
304
        );
305
306
        $this->roleGateway->copyRole($role);
307
308
        foreach ($role->policies as $policy) {
309
            $this->addPolicy($role->id, $policy);
310
        }
311
312
        return $role;
313
    }
314
315
    /**
316
     * Loads a specified role (draft) by $roleId and $status.
317
     *
318
     * @param mixed $roleId
319
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
320
     *
321
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with given status does not exist
322
     *
323
     * @return \eZ\Publish\SPI\Persistence\User\Role
324
     */
325 View Code Duplication
    public function loadRole($roleId, $status = Role::STATUS_DEFINED)
326
    {
327
        $data = $this->roleGateway->loadRole($roleId, $status);
328
329
        if (empty($data)) {
330
            throw new RoleNotFound($roleId, $status);
331
        }
332
333
        $role = $this->mapper->mapRole($data);
334
        foreach ($role->policies as $policy) {
335
            $this->limitationConverter->toSPI($policy);
336
        }
337
338
        return $role;
339
    }
340
341
    /**
342
     * Loads a specified role (draft) by $identifier and $status.
343
     *
344
     * @param string $identifier
345
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
346
     *
347
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found
348
     *
349
     * @return \eZ\Publish\SPI\Persistence\User\Role
350
     */
351 View Code Duplication
    public function loadRoleByIdentifier($identifier, $status = Role::STATUS_DEFINED)
352
    {
353
        $data = $this->roleGateway->loadRoleByIdentifier($identifier, $status);
354
355
        if (empty($data)) {
356
            throw new RoleNotFound($identifier, $status);
357
        }
358
359
        $role = $this->mapper->mapRole($data);
360
        foreach ($role->policies as $policy) {
361
            $this->limitationConverter->toSPI($policy);
362
        }
363
364
        return $role;
365
    }
366
367
    /**
368
     * Loads a role draft by the original role ID.
369
     *
370
     * @param mixed $roleId ID of the role the draft was created from.
371
     *
372
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found
373
     *
374
     * @return \eZ\Publish\SPI\Persistence\User\Role
375
     */
376 View Code Duplication
    public function loadRoleDraftByRoleId($roleId)
377
    {
378
        $data = $this->roleGateway->loadRoleDraftByRoleId($roleId);
379
380
        if (empty($data)) {
381
            throw new RoleNotFound($roleId, Role::STATUS_DRAFT);
382
        }
383
384
        $role = $this->mapper->mapRole($data);
385
        foreach ($role->policies as $policy) {
386
            $this->limitationConverter->toSPI($policy);
387
        }
388
389
        return $role;
390
    }
391
392
    /**
393
     * Loads all roles.
394
     *
395
     * @return \eZ\Publish\SPI\Persistence\User\Role[]
396
     */
397 View Code Duplication
    public function loadRoles()
398
    {
399
        $data = $this->roleGateway->loadRoles();
400
401
        $roles = $this->mapper->mapRoles($data);
402
        foreach ($roles as $role) {
403
            foreach ($role->policies as $policy) {
404
                $this->limitationConverter->toSPI($policy);
405
            }
406
        }
407
408
        return $roles;
409
    }
410
411
    /**
412
     * Update role (draft).
413
     *
414
     * @param \eZ\Publish\SPI\Persistence\User\RoleUpdateStruct $role
415
     */
416
    public function updateRole(RoleUpdateStruct $role)
417
    {
418
        $this->roleGateway->updateRole($role);
419
    }
420
421
    /**
422
     * Delete the specified role (draft).
423
     *
424
     * @param mixed $roleId
425
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
426
     */
427
    public function deleteRole($roleId, $status = Role::STATUS_DEFINED)
428
    {
429
        $role = $this->loadRole($roleId, $status);
430
431
        foreach ($role->policies as $policy) {
432
            $this->roleGateway->removePolicy($policy->id);
433
        }
434
435
        $this->roleGateway->deleteRole($role->id, $status);
436
    }
437
438
    /**
439
     * Publish the specified role draft.
440
     *
441
     * @param mixed $roleDraftId
442
     */
443
    public function publishRoleDraft($roleDraftId)
444
    {
445
        $roleDraft = $this->loadRole($roleDraftId, Role::STATUS_DRAFT);
446
447
        try {
448
            $originalRoleId = $roleDraft->originalId;
449
            $role = $this->loadRole($originalRoleId);
450
            $roleAssignments = $this->loadRoleAssignmentsByRoleId($role->id);
451
            $this->deleteRole($role->id);
452
453
            foreach ($roleAssignments as $roleAssignment) {
454
                if (empty($roleAssignment->limitationIdentifier)) {
455
                    $this->assignRole($roleAssignment->contentId, $originalRoleId);
456
                } else {
457
                    $this->assignRole(
458
                        $roleAssignment->contentId,
459
                        $originalRoleId,
460
                        [$roleAssignment->limitationIdentifier => $roleAssignment->values]
461
                    );
462
                }
463
            }
464
            $this->roleGateway->publishRoleDraft($roleDraft->id, $role->id);
465
        } catch (NotFound $e) {
466
            // If no published role is found, only publishing is needed, without specifying original role ID as there is none.
467
            $this->roleGateway->publishRoleDraft($roleDraft->id);
468
        }
469
    }
470
471
    /**
472
     * Adds a policy to a role draft.
473
     *
474
     * @param mixed $roleId
475
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
476
     *
477
     * @return \eZ\Publish\SPI\Persistence\User\Policy
478
     */
479
    public function addPolicyByRoleDraft($roleId, Policy $policy)
480
    {
481
        $legacyPolicy = clone $policy;
482
        $legacyPolicy->originalId = $policy->id;
483
        $this->limitationConverter->toLegacy($legacyPolicy);
484
485
        $this->roleGateway->addPolicy($roleId, $legacyPolicy);
486
        $policy->id = $legacyPolicy->id;
487
        $policy->originalId = $legacyPolicy->originalId;
488
        $policy->roleId = $legacyPolicy->roleId;
489
490
        return $policy;
491
    }
492
493
    /**
494
     * Adds a policy to a role.
495
     *
496
     * @param mixed $roleId
497
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
498
     *
499
     * @return \eZ\Publish\SPI\Persistence\User\Policy
500
     */
501
    public function addPolicy($roleId, Policy $policy)
502
    {
503
        $legacyPolicy = clone $policy;
504
        $this->limitationConverter->toLegacy($legacyPolicy);
505
506
        $this->roleGateway->addPolicy($roleId, $legacyPolicy);
507
        $policy->id = $legacyPolicy->id;
508
        $policy->roleId = $legacyPolicy->roleId;
509
510
        return $policy;
511
    }
512
513
    /**
514
     * Update a policy.
515
     *
516
     * Replaces limitations values with new values.
517
     *
518
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
519
     */
520
    public function updatePolicy(Policy $policy)
521
    {
522
        $policy = clone $policy;
523
        $this->limitationConverter->toLegacy($policy);
524
525
        $this->roleGateway->removePolicyLimitations($policy->id);
526
        $this->roleGateway->addPolicyLimitations($policy->id, $policy->limitations === '*' ? [] : $policy->limitations);
0 ignored issues
show
Bug introduced by
It seems like $policy->limitations ===... : $policy->limitations can also be of type string; however, eZ\Publish\Core\Persiste...:addPolicyLimitations() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
527
    }
528
529
    /**
530
     * Removes a policy from a role.
531
     *
532
     * @param mixed $policyId
533
     * @param mixed $roleId
534
     */
535
    public function deletePolicy($policyId, $roleId)
536
    {
537
        // Each policy can only be associated to exactly one role. Thus it is
538
        // sufficient to use the policyId for identification and just remove
539
        // the policy completely.
540
        $this->roleGateway->removePolicy($policyId);
541
    }
542
543
    /**
544
     * Returns the user policies associated with the user (including inherited policies from user groups).
545
     *
546
     * @param mixed $userId
547
     *
548
     * @return \eZ\Publish\SPI\Persistence\User\Policy[]
549
     */
550 View Code Duplication
    public function loadPoliciesByUserId($userId)
551
    {
552
        $data = $this->roleGateway->loadPoliciesByUserId($userId);
553
554
        $policies = $this->mapper->mapPolicies($data);
555
556
        foreach ($policies as $policy) {
557
            $this->limitationConverter->toSPI($policy);
558
        }
559
560
        return $policies;
561
    }
562
563
    /**
564
     * Assigns role to a user or user group with given limitations.
565
     *
566
     * The limitation array looks like:
567
     * <code>
568
     *  array(
569
     *      'Subtree' => array(
570
     *          '/1/2/',
571
     *          '/1/4/',
572
     *      ),
573
     *      'Foo' => array( 'Bar' ),
574
     *      …
575
     *  )
576
     * </code>
577
     *
578
     * Where the keys are the limitation identifiers, and the respective values
579
     * are an array of limitation values. The limitation parameter is optional.
580
     *
581
     * @param mixed $contentId The groupId or userId to assign the role to.
582
     * @param mixed $roleId
583
     * @param array $limitation
584
     */
585
    public function assignRole($contentId, $roleId, array $limitation = null)
586
    {
587
        $limitation = $limitation ?: ['' => ['']];
588
        $this->userGateway->assignRole($contentId, $roleId, $limitation);
589
    }
590
591
    /**
592
     * Un-assign a role.
593
     *
594
     * @param mixed $contentId The user or user group Id to un-assign the role from.
595
     * @param mixed $roleId
596
     */
597
    public function unassignRole($contentId, $roleId)
598
    {
599
        $this->userGateway->removeRole($contentId, $roleId);
600
    }
601
602
    /**
603
     * Un-assign a role by assignment ID.
604
     *
605
     * @param mixed $roleAssignmentId The assignment ID.
606
     */
607
    public function removeRoleAssignment($roleAssignmentId)
608
    {
609
        $this->userGateway->removeRoleAssignmentById($roleAssignmentId);
610
    }
611
612
    /**
613
     * Loads role assignment for specified assignment ID.
614
     *
615
     * @param mixed $roleAssignmentId
616
     *
617
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role assignment is not found
618
     *
619
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment
620
     */
621
    public function loadRoleAssignment($roleAssignmentId)
622
    {
623
        $data = $this->roleGateway->loadRoleAssignment($roleAssignmentId);
624
625
        if (empty($data)) {
626
            throw new NotFound('roleAssignment', $roleAssignmentId);
627
        }
628
629
        return $this->mapper->mapRoleAssignments($data)[0];
630
    }
631
632
    /**
633
     * Loads roles assignments Role.
634
     *
635
     * Role Assignments with same roleId and limitationIdentifier will be merged together into one.
636
     *
637
     * @param mixed $roleId
638
     *
639
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[]
640
     */
641 View Code Duplication
    public function loadRoleAssignmentsByRoleId($roleId)
642
    {
643
        $data = $this->roleGateway->loadRoleAssignmentsByRoleId($roleId);
644
645
        if (empty($data)) {
646
            return [];
647
        }
648
649
        return $this->mapper->mapRoleAssignments($data);
650
    }
651
652
    /**
653
     * Loads roles assignments to a user/group.
654
     *
655
     * Role Assignments with same roleId and limitationIdentifier will be merged together into one.
656
     *
657
     * @param mixed $groupId In legacy storage engine this is the content object id roles are assigned to in ezuser_role.
658
     *                      By the nature of legacy this can currently also be used to get by $userId.
659
     * @param bool $inherit If true also return inherited role assignments from user groups.
660
     *
661
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[]
662
     */
663 View Code Duplication
    public function loadRoleAssignmentsByGroupId($groupId, $inherit = false)
664
    {
665
        $data = $this->roleGateway->loadRoleAssignmentsByGroupId($groupId, $inherit);
666
667
        if (empty($data)) {
668
            return [];
669
        }
670
671
        return $this->mapper->mapRoleAssignments($data);
672
    }
673
}
674