Completed
Push — ezp-31420-merge-up ( ec14fb...141a64 )
by
unknown
40:13 queued 27:42
created

Handler::loadRoles()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 13
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 0
dl 13
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Persistence\Legacy\User;
8
9
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
10
use eZ\Publish\SPI\Persistence\User;
11
use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct;
12
use eZ\Publish\SPI\Persistence\User\Handler as BaseUserHandler;
13
use eZ\Publish\SPI\Persistence\User\Role;
14
use eZ\Publish\SPI\Persistence\User\RoleCreateStruct;
15
use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct;
16
use eZ\Publish\SPI\Persistence\User\Policy;
17
use eZ\Publish\Core\Persistence\Legacy\Exception\RoleNotFound;
18
use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway as RoleGateway;
19
use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter;
20
use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound;
21
use LogicException;
22
23
/**
24
 * Storage Engine handler for user module.
25
 */
26
class Handler implements BaseUserHandler
27
{
28
    /**
29
     * Gateway for storing user data.
30
     *
31
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Gateway
32
     */
33
    protected $userGateway;
34
35
    /**
36
     * Gateway for storing role data.
37
     *
38
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway
39
     */
40
    protected $roleGateway;
41
42
    /**
43
     * Mapper for user related objects.
44
     *
45
     * @var \eZ\Publish\Core\Persistence\Legacy\User\Mapper
46
     */
47
    protected $mapper;
48
49
    /** @var \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter */
50
    protected $limitationConverter;
51
52
    /**
53
     * Construct from userGateway.
54
     *
55
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Gateway $userGateway
56
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway $roleGateway
57
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Mapper $mapper
58
     * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter $limitationConverter
59
     */
60
    public function __construct(Gateway $userGateway, RoleGateway $roleGateway, Mapper $mapper, LimitationConverter $limitationConverter)
61
    {
62
        $this->userGateway = $userGateway;
63
        $this->roleGateway = $roleGateway;
64
        $this->mapper = $mapper;
65
        $this->limitationConverter = $limitationConverter;
66
    }
67
68
    /**
69
     * Create a user.
70
     *
71
     * The User struct used to create the user will contain an ID which is used
72
     * to reference the user.
73
     *
74
     * @param \eZ\Publish\SPI\Persistence\User $user
75
     *
76
     * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
77
     */
78
    public function create(User $user)
79
    {
80
        throw new NotImplementedException('This method should not be called, creation is done via content handler.');
81
    }
82
83
    /**
84
     * Loads user with user ID.
85
     *
86
     * @param mixed $userId
87
     *
88
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
89
     *
90
     * @return \eZ\Publish\SPI\Persistence\User
91
     */
92 View Code Duplication
    public function load($userId)
93
    {
94
        $data = $this->userGateway->load($userId);
95
96
        if (empty($data)) {
97
            throw new NotFound('user', $userId);
98
        }
99
100
        return $this->mapper->mapUser(reset($data));
101
    }
102
103
    /**
104
     * Loads user with user login.
105
     *
106
     * @param string $login
107
     *
108
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
109
     *
110
     * @return \eZ\Publish\SPI\Persistence\User
111
     */
112 View Code Duplication
    public function loadByLogin($login)
113
    {
114
        $data = $this->userGateway->loadByLogin($login);
115
116
        if (empty($data)) {
117
            throw new NotFound('user', $login);
118
        } elseif (count($data) > 1) {
119
            throw new LogicException("Found more then one user with login '{$login}'");
120
        }
121
122
        return $this->mapper->mapUser($data[0]);
123
    }
124
125
    /**
126
     * Loads user(s) with user email.
127
     *
128
     * As earlier eZ Publish versions supported several users having same email (ini config),
129
     * this function may return several users.
130
     *
131
     * @param string $email
132
     *
133
     * @return \eZ\Publish\SPI\Persistence\User
134
     */
135 View Code Duplication
    public function loadByEmail(string $email): User
136
    {
137
        $data = $this->userGateway->loadByEmail($email);
138
139
        if (empty($data)) {
140
            throw new NotFound('user', $email);
141
        } elseif (count($data) > 1) {
142
            throw new LogicException("Found more then one user with login '{$email}'");
143
        }
144
145
        return $this->mapper->mapUser($data[0]);
146
    }
147
148
    /**
149
     * Loads user(s) with user email.
150
     *
151
     * As earlier eZ Publish versions supported several users having same email (ini config),
152
     * this function may return several users.
153
     *
154
     * @param string $email
155
     *
156
     * @return \eZ\Publish\SPI\Persistence\User[]
157
     */
158
    public function loadUsersByEmail(string $email): array
159
    {
160
        $data = $this->userGateway->loadByEmail($email);
161
162
        if (empty($data)) {
163
            return [];
164
        }
165
166
        return $this->mapper->mapUsers($data);
167
    }
168
169
    /**
170
     * Loads user with user hash.
171
     *
172
     * @param string $hash
173
     *
174
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found
175
     *
176
     * @return \eZ\Publish\SPI\Persistence\User
177
     */
178 View Code Duplication
    public function loadUserByToken($hash)
179
    {
180
        $data = $this->userGateway->loadUserByToken($hash);
181
182
        if (empty($data)) {
183
            throw new NotFound('user', $hash);
184
        }
185
186
        return $this->mapper->mapUser(reset($data));
187
    }
188
189
    /**
190
     * Update the user information specified by the user struct.
191
     *
192
     * @param \eZ\Publish\SPI\Persistence\User $user
193
     *
194
     * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
195
     */
196
    public function update(User $user)
197
    {
198
        throw new NotImplementedException('This method should not be called, update is done via content handler.');
199
    }
200
201
    /**
202
     * Update the user token information specified by the userToken struct.
203
     *
204
     * @param \eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct $userTokenUpdateStruct
205
     */
206
    public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct)
207
    {
208
        $this->userGateway->updateUserToken($userTokenUpdateStruct);
209
    }
210
211
    /**
212
     * Expires user account key with user hash.
213
     *
214
     * @param string $hash
215
     */
216
    public function expireUserToken($hash)
217
    {
218
        $this->userGateway->expireUserToken($hash);
219
    }
220
221
    /**
222
     * Delete user with the given ID.
223
     *
224
     * @param mixed $userId
225
     *
226
     * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
227
     */
228
    public function delete($userId)
229
    {
230
        throw new NotImplementedException('This method should not be called, delete is done via content handler.');
231
    }
232
233
    /**
234
     * Create new role draft.
235
     *
236
     * Sets status to Role::STATUS_DRAFT on the new returned draft.
237
     *
238
     * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct
239
     *
240
     * @return \eZ\Publish\SPI\Persistence\User\Role
241
     */
242
    public function createRole(RoleCreateStruct $createStruct)
243
    {
244
        return $this->internalCreateRole($createStruct);
245
    }
246
247
    /**
248
     * Creates a draft of existing defined role.
249
     *
250
     * Sets status to Role::STATUS_DRAFT on the new returned draft.
251
     *
252
     * @param mixed $roleId
253
     *
254
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with defined status is not found
255
     *
256
     * @return \eZ\Publish\SPI\Persistence\User\Role
257
     */
258
    public function createRoleDraft($roleId)
259
    {
260
        $createStruct = $this->mapper->createCreateStructFromRole(
261
            $this->loadRole($roleId)
262
        );
263
264
        return $this->internalCreateRole($createStruct, $roleId);
265
    }
266
267
    /**
268
     * Internal method for creating Role.
269
     *
270
     * Used by self::createRole() and self::createRoleDraft()
271
     *
272
     * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct
273
     * @param mixed|null $roleId Used by self::createRoleDraft() to retain Role id in the draft
274
     *
275
     * @return \eZ\Publish\SPI\Persistence\User\Role
276
     */
277
    protected function internalCreateRole(RoleCreateStruct $createStruct, $roleId = null)
278
    {
279
        $createStruct = clone $createStruct;
280
        $role = $this->mapper->createRoleFromCreateStruct(
281
            $createStruct
282
        );
283
        $role->id = $roleId;
284
        $role->status = Role::STATUS_DRAFT;
285
286
        $this->roleGateway->createRole($role);
287
288
        foreach ($role->policies as $policy) {
289
            $this->addPolicyByRoleDraft($role->id, $policy);
290
        }
291
292
        return $role;
293
    }
294
295
    /**
296
     * Loads a specified role (draft) by $roleId and $status.
297
     *
298
     * @param mixed $roleId
299
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
300
     *
301
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with given status does not exist
302
     *
303
     * @return \eZ\Publish\SPI\Persistence\User\Role
304
     */
305 View Code Duplication
    public function loadRole($roleId, $status = Role::STATUS_DEFINED)
306
    {
307
        $data = $this->roleGateway->loadRole($roleId, $status);
308
309
        if (empty($data)) {
310
            throw new RoleNotFound($roleId, $status);
311
        }
312
313
        $role = $this->mapper->mapRole($data);
314
        foreach ($role->policies as $policy) {
315
            $this->limitationConverter->toSPI($policy);
316
        }
317
318
        return $role;
319
    }
320
321
    /**
322
     * Loads a specified role (draft) by $identifier and $status.
323
     *
324
     * @param string $identifier
325
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
326
     *
327
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found
328
     *
329
     * @return \eZ\Publish\SPI\Persistence\User\Role
330
     */
331 View Code Duplication
    public function loadRoleByIdentifier($identifier, $status = Role::STATUS_DEFINED)
332
    {
333
        $data = $this->roleGateway->loadRoleByIdentifier($identifier, $status);
334
335
        if (empty($data)) {
336
            throw new RoleNotFound($identifier, $status);
337
        }
338
339
        $role = $this->mapper->mapRole($data);
340
        foreach ($role->policies as $policy) {
341
            $this->limitationConverter->toSPI($policy);
342
        }
343
344
        return $role;
345
    }
346
347
    /**
348
     * Loads a role draft by the original role ID.
349
     *
350
     * @param mixed $roleId ID of the role the draft was created from.
351
     *
352
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found
353
     *
354
     * @return \eZ\Publish\SPI\Persistence\User\Role
355
     */
356 View Code Duplication
    public function loadRoleDraftByRoleId($roleId)
357
    {
358
        $data = $this->roleGateway->loadRoleDraftByRoleId($roleId);
359
360
        if (empty($data)) {
361
            throw new RoleNotFound($roleId, Role::STATUS_DRAFT);
362
        }
363
364
        $role = $this->mapper->mapRole($data);
365
        foreach ($role->policies as $policy) {
366
            $this->limitationConverter->toSPI($policy);
367
        }
368
369
        return $role;
370
    }
371
372
    /**
373
     * Loads all roles.
374
     *
375
     * @return \eZ\Publish\SPI\Persistence\User\Role[]
376
     */
377 View Code Duplication
    public function loadRoles()
378
    {
379
        $data = $this->roleGateway->loadRoles();
380
381
        $roles = $this->mapper->mapRoles($data);
382
        foreach ($roles as $role) {
383
            foreach ($role->policies as $policy) {
384
                $this->limitationConverter->toSPI($policy);
385
            }
386
        }
387
388
        return $roles;
389
    }
390
391
    /**
392
     * Update role (draft).
393
     *
394
     * @param \eZ\Publish\SPI\Persistence\User\RoleUpdateStruct $role
395
     */
396
    public function updateRole(RoleUpdateStruct $role)
397
    {
398
        $this->roleGateway->updateRole($role);
399
    }
400
401
    /**
402
     * Delete the specified role (draft).
403
     *
404
     * @param mixed $roleId
405
     * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT
406
     */
407
    public function deleteRole($roleId, $status = Role::STATUS_DEFINED)
408
    {
409
        $role = $this->loadRole($roleId, $status);
410
411
        foreach ($role->policies as $policy) {
412
            $this->roleGateway->removePolicy($policy->id);
413
        }
414
415
        $this->roleGateway->deleteRole($role->id, $status);
416
    }
417
418
    /**
419
     * Publish the specified role draft.
420
     *
421
     * @param mixed $roleDraftId
422
     */
423
    public function publishRoleDraft($roleDraftId)
424
    {
425
        $roleDraft = $this->loadRole($roleDraftId, Role::STATUS_DRAFT);
426
427
        try {
428
            $originalRoleId = $roleDraft->originalId;
429
            $role = $this->loadRole($originalRoleId);
430
            $roleAssignments = $this->loadRoleAssignmentsByRoleId($role->id);
431
            $this->deleteRole($role->id);
432
433
            foreach ($roleAssignments as $roleAssignment) {
434
                if (empty($roleAssignment->limitationIdentifier)) {
435
                    $this->assignRole($roleAssignment->contentId, $originalRoleId);
436
                } else {
437
                    $this->assignRole(
438
                        $roleAssignment->contentId,
439
                        $originalRoleId,
440
                        [$roleAssignment->limitationIdentifier => $roleAssignment->values]
441
                    );
442
                }
443
            }
444
            $this->roleGateway->publishRoleDraft($roleDraft->id, $role->id);
445
        } catch (NotFound $e) {
446
            // If no published role is found, only publishing is needed, without specifying original role ID as there is none.
447
            $this->roleGateway->publishRoleDraft($roleDraft->id);
448
        }
449
    }
450
451
    /**
452
     * Adds a policy to a role draft.
453
     *
454
     * @param mixed $roleId
455
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
456
     *
457
     * @return \eZ\Publish\SPI\Persistence\User\Policy
458
     */
459
    public function addPolicyByRoleDraft($roleId, Policy $policy)
460
    {
461
        $legacyPolicy = clone $policy;
462
        $legacyPolicy->originalId = $policy->id;
463
        $this->limitationConverter->toLegacy($legacyPolicy);
464
465
        $this->roleGateway->addPolicy($roleId, $legacyPolicy);
466
        $policy->id = $legacyPolicy->id;
467
        $policy->originalId = $legacyPolicy->originalId;
468
        $policy->roleId = $legacyPolicy->roleId;
469
470
        return $policy;
471
    }
472
473
    /**
474
     * Adds a policy to a role.
475
     *
476
     * @param mixed $roleId
477
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
478
     *
479
     * @return \eZ\Publish\SPI\Persistence\User\Policy
480
     */
481
    public function addPolicy($roleId, Policy $policy)
482
    {
483
        $legacyPolicy = clone $policy;
484
        $this->limitationConverter->toLegacy($legacyPolicy);
485
486
        $this->roleGateway->addPolicy($roleId, $legacyPolicy);
487
        $policy->id = $legacyPolicy->id;
488
        $policy->roleId = $legacyPolicy->roleId;
489
490
        return $policy;
491
    }
492
493
    /**
494
     * Update a policy.
495
     *
496
     * Replaces limitations values with new values.
497
     *
498
     * @param \eZ\Publish\SPI\Persistence\User\Policy $policy
499
     */
500
    public function updatePolicy(Policy $policy)
501
    {
502
        $policy = clone $policy;
503
        $this->limitationConverter->toLegacy($policy);
504
505
        $this->roleGateway->removePolicyLimitations($policy->id);
506
        $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...
507
    }
508
509
    /**
510
     * Removes a policy from a role.
511
     *
512
     * @param mixed $policyId
513
     * @param mixed $roleId
514
     */
515
    public function deletePolicy($policyId, $roleId)
516
    {
517
        // Each policy can only be associated to exactly one role. Thus it is
518
        // sufficient to use the policyId for identification and just remove
519
        // the policy completely.
520
        $this->roleGateway->removePolicy($policyId);
521
    }
522
523
    /**
524
     * Returns the user policies associated with the user (including inherited policies from user groups).
525
     *
526
     * @param mixed $userId
527
     *
528
     * @return \eZ\Publish\SPI\Persistence\User\Policy[]
529
     */
530 View Code Duplication
    public function loadPoliciesByUserId($userId)
531
    {
532
        $data = $this->roleGateway->loadPoliciesByUserId($userId);
533
534
        $policies = $this->mapper->mapPolicies($data);
535
536
        foreach ($policies as $policy) {
537
            $this->limitationConverter->toSPI($policy);
538
        }
539
540
        return $policies;
541
    }
542
543
    /**
544
     * Assigns role to a user or user group with given limitations.
545
     *
546
     * The limitation array looks like:
547
     * <code>
548
     *  array(
549
     *      'Subtree' => array(
550
     *          '/1/2/',
551
     *          '/1/4/',
552
     *      ),
553
     *      'Foo' => array( 'Bar' ),
554
     *      …
555
     *  )
556
     * </code>
557
     *
558
     * Where the keys are the limitation identifiers, and the respective values
559
     * are an array of limitation values. The limitation parameter is optional.
560
     *
561
     * @param mixed $contentId The groupId or userId to assign the role to.
562
     * @param mixed $roleId
563
     * @param array $limitation
564
     */
565
    public function assignRole($contentId, $roleId, array $limitation = null)
566
    {
567
        $limitation = $limitation ?: ['' => ['']];
568
        $this->userGateway->assignRole($contentId, $roleId, $limitation);
569
    }
570
571
    /**
572
     * Un-assign a role.
573
     *
574
     * @param mixed $contentId The user or user group Id to un-assign the role from.
575
     * @param mixed $roleId
576
     */
577
    public function unassignRole($contentId, $roleId)
578
    {
579
        $this->userGateway->removeRole($contentId, $roleId);
580
    }
581
582
    /**
583
     * Un-assign a role by assignment ID.
584
     *
585
     * @param mixed $roleAssignmentId The assignment ID.
586
     */
587
    public function removeRoleAssignment($roleAssignmentId)
588
    {
589
        $this->userGateway->removeRoleAssignmentById($roleAssignmentId);
590
    }
591
592
    /**
593
     * Loads role assignment for specified assignment ID.
594
     *
595
     * @param mixed $roleAssignmentId
596
     *
597
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role assignment is not found
598
     *
599
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment
600
     */
601
    public function loadRoleAssignment($roleAssignmentId)
602
    {
603
        $data = $this->roleGateway->loadRoleAssignment($roleAssignmentId);
604
605
        if (empty($data)) {
606
            throw new NotFound('roleAssignment', $roleAssignmentId);
607
        }
608
609
        return $this->mapper->mapRoleAssignments($data)[0];
610
    }
611
612
    /**
613
     * Loads roles assignments Role.
614
     *
615
     * Role Assignments with same roleId and limitationIdentifier will be merged together into one.
616
     *
617
     * @param mixed $roleId
618
     *
619
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[]
620
     */
621 View Code Duplication
    public function loadRoleAssignmentsByRoleId($roleId)
622
    {
623
        $data = $this->roleGateway->loadRoleAssignmentsByRoleId($roleId);
624
625
        if (empty($data)) {
626
            return [];
627
        }
628
629
        return $this->mapper->mapRoleAssignments($data);
630
    }
631
632
    /**
633
     * Loads roles assignments to a user/group.
634
     *
635
     * Role Assignments with same roleId and limitationIdentifier will be merged together into one.
636
     *
637
     * @param mixed $groupId In legacy storage engine this is the content object id roles are assigned to in ezuser_role.
638
     *                      By the nature of legacy this can currently also be used to get by $userId.
639
     * @param bool $inherit If true also return inherited role assignments from user groups.
640
     *
641
     * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[]
642
     */
643 View Code Duplication
    public function loadRoleAssignmentsByGroupId($groupId, $inherit = false)
644
    {
645
        $data = $this->roleGateway->loadRoleAssignmentsByGroupId($groupId, $inherit);
646
647
        if (empty($data)) {
648
            return [];
649
        }
650
651
        return $this->mapper->mapRoleAssignments($data);
652
    }
653
}
654