Passed
Push — master ( 66a546...4ec22f )
by Gabor
03:10
created

Acl   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 118
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 6
dl 0
loc 118
ccs 0
cts 51
cp 0
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A isAllowed() 0 17 3
A getUserPolicies() 0 5 1
A getUserGroupPolicies() 0 15 2
C checkPolicy() 0 23 7
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2017 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Acl;
15
16
use WebHemi\Adapter\Acl\AclAdapterInterface;
17
use WebHemi\Data\Coupler\UserGroupToPolicyCoupler;
18
use WebHemi\Data\Coupler\UserToGroupCoupler;
19
use WebHemi\Data\Coupler\UserToPolicyCoupler;
20
use WebHemi\Data\Entity\AccessManagement\PolicyEntity;
21
use WebHemi\Data\Entity\AccessManagement\ResourceEntity;
22
use WebHemi\Data\Entity\ApplicationEntity;
23
use WebHemi\Data\Entity\User\UserEntity;
24
25
/**
26
 * Class Acl
27
 */
28
final class Acl implements AclAdapterInterface
29
{
30
    /** @var UserToPolicyCoupler */
31
    private $userToPolicyCoupler;
32
    /** @var UserToGroupCoupler */
33
    private $userToGroupCoupler;
34
    /** @var UserGroupToPolicyCoupler */
35
    private $userGroupToPolicyCoupler;
36
37
    /**
38
     * Acl constructor.
39
     *
40
     * @param UserToPolicyCoupler $userToPolicyCoupler
41
     * @param UserToGroupCoupler $userToGroupCoupler
42
     * @param UserGroupToPolicyCoupler $userGroupToPolicyCoupler
43
     */
44
    public function __construct(
45
        UserToPolicyCoupler $userToPolicyCoupler,
46
        UserToGroupCoupler $userToGroupCoupler,
47
        UserGroupToPolicyCoupler $userGroupToPolicyCoupler
48
    ) {
49
        $this->userToPolicyCoupler = $userToPolicyCoupler;
50
        $this->userToGroupCoupler = $userToGroupCoupler;
51
        $this->userGroupToPolicyCoupler = $userGroupToPolicyCoupler;
52
    }
53
54
    /**
55
     * Checks if a User can access to a Resource in an Application
56
     *
57
     * @param UserEntity             $userEntity
58
     * @param ResourceEntity|null    $resourceEntity
59
     * @param ApplicationEntity|null $applicationEntity
60
     * @return bool
61
     */
62
    public function isAllowed(
63
        UserEntity $userEntity,
64
        ?ResourceEntity $resourceEntity = null,
65
        ?ApplicationEntity $applicationEntity = null
66
    ) : bool {
67
        // We assume the best case: the user has access
68
        $allowed = true;
69
70
        /** @var array<PolicyEntity> $policies */
71
        $policies = array_merge($this->getUserPolicies($userEntity), $this->getUserGroupPolicies($userEntity));
72
73
        foreach ($policies as $policyEntity) {
74
            $allowed = $allowed && $this->checkPolicy($policyEntity, $applicationEntity, $resourceEntity);
75
        }
76
77
        return $allowed;
78
    }
79
80
    /**
81
     * Gets the policies assigned to the user.
82
     *
83
     * @param UserEntity $userEntity
84
     * @return array<PolicyEntity>
85
     */
86
    private function getUserPolicies(UserEntity $userEntity) : array
87
    {
88
        /** @var array<PolicyEntity> $userPolicies */
89
        return $this->userToPolicyCoupler->getEntityDependencies($userEntity);
90
    }
91
92
    /**
93
     * Gets the policies assigned to the group in which the user is.
94
     *
95
     * @param UserEntity $userEntity
96
     * @return array<PolicyEntity>
97
     */
98
    private function getUserGroupPolicies(UserEntity $userEntity) : array
99
    {
100
        /** @var array<PolicyEntity> $userGroupPolicies */
101
        $userGroupPolicies = [];
102
        /** @var array<UserGroupEntity> $userGroups */
103
        $userGroups = $this->userToGroupCoupler->getEntityDependencies($userEntity);
104
105
        foreach ($userGroups as $userGroupEntity) {
106
            /** @var array<PolicyEntity> $groupPolicies */
107
            $groupPolicies = $this->userGroupToPolicyCoupler->getEntityDependencies($userGroupEntity);
108
            $userGroupPolicies = array_merge($userGroupPolicies, $groupPolicies);
109
        }
110
111
        return $userGroupPolicies;
112
    }
113
114
    /**
115
     * Check a concrete policy.
116
     *
117
     * @param PolicyEntity           $policyEntity
118
     * @param ApplicationEntity|null $applicationEntity
119
     * @param ResourceEntity|null    $resourceEntity
120
     * @return bool
121
     */
122
    private function checkPolicy(
123
        PolicyEntity $policyEntity,
124
        ?ApplicationEntity $applicationEntity = null,
125
        ?ResourceEntity $resourceEntity = null
126
    ) : bool {
127
        $policyApplicationId = $policyEntity->getApplicationId();
128
        $policyResourceId = $policyEntity->getResourceId();
129
        $applicationId = $applicationEntity ? $applicationEntity->getApplicationId() : null;
130
        $resourceId = $resourceEntity ? $resourceEntity->getResourceId() : null;
131
132
        // The user has access when:
133
        // - user/user's group has a policy that connected to the current application OR any application AND
134
        // - user/user's group has a policy that connected to the current resource OR any resource
135
        if (($policyApplicationId == null || $policyApplicationId == $applicationId)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $policyApplicationId of type null|integer against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
136
            && ($policyResourceId == null || $policyResourceId == $resourceId)
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $policyResourceId of type null|integer against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
137
        ) {
138
            return $policyEntity->getAllowed();
139
        }
140
141
        // At this point we know that the current policy doesn't belong to this application or resource, so no need
142
        // to block the user.
143
        return true;
144
    }
145
}
146