Passed
Push — master ( 22a454...7e0f83 )
by Gabor
02:59
created

AclMiddleware::__invoke()   C

Complexity

Conditions 13
Paths 22

Size

Total Lines 69
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 69
rs 5.6955
cc 13
eloc 35
nc 22
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @copyright 2012 - 2016 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
13
namespace WebHemi\Middleware\Security;
14
15
use Exception;
16
use WebHemi\Adapter\Auth\AuthAdapterInterface;
17
use WebHemi\Adapter\Http\ResponseInterface;
18
use WebHemi\Adapter\Http\ServerRequestInterface;
19
use WebHemi\Application\EnvironmentManager;
20
use WebHemi\Auth\Result;
21
use WebHemi\Data\Coupler\UserToGroupCoupler;
22
use WebHemi\Data\Coupler\UserToPolicyCoupler;
23
use WebHemi\Data\Coupler\UserGroupToPolicyCoupler;
24
use WebHemi\Data\Entity\ApplicationEntity;
25
use WebHemi\Data\Entity\AccessManagement\PolicyEntity;
26
use WebHemi\Data\Entity\AccessManagement\ResourceEntity;
27
use WebHemi\Data\Entity\User\UserEntity;
28
use WebHemi\Data\Storage\AccessManagement\ResourceStorage;
29
use WebHemi\Data\Storage\ApplicationStorage;
30
use WebHemi\Middleware\MiddlewareInterface;
31
use WebHemi\Middleware\Action;
32
33
/**
34
 * Class AclMiddleware.
35
 */
36
class AclMiddleware implements MiddlewareInterface
37
{
38
    /** @var EnvironmentManager */
39
    private $environmentManager;
40
    /** @var AuthAdapterInterface */
41
    private $authAdapter;
42
    /** @var UserToPolicyCoupler */
43
    private $userToPolicyCoupler;
44
    /** @var UserToGroupCoupler */
45
    private $userToGroupCoupler;
46
    /** @var UserGroupToPolicyCoupler */
47
    private $userGroupToPolicyCoupler;
48
    private $applicationStorage;
49
    private $resourceStorage;
50
    /** @var array */
51
    private $middlewareWhiteList = [
52
        Action\Auth\LoginAction::class,
53
        Action\Auth\LogoutAction::class,
54
    ];
55
56
    /**
57
     * AclMiddleware constructor.
58
     * @param EnvironmentManager       $environmentManager
59
     * @param AuthAdapterInterface     $authAdapter
60
     * @param UserToPolicyCoupler      $userToPolicyCoupler
61
     * @param UserToGroupCoupler       $userToGroupCoupler
62
     * @param UserGroupToPolicyCoupler $userGroupToPolicyCoupler
63
     * @param ApplicationStorage       $applicationStorage
64
     * @param ResourceStorage          $resourceStorage
65
     */
66
    public function __construct(
67
        EnvironmentManager $environmentManager,
68
        AuthAdapterInterface $authAdapter,
69
        UserToPolicyCoupler $userToPolicyCoupler,
70
        UserToGroupCoupler $userToGroupCoupler,
71
        UserGroupToPolicyCoupler $userGroupToPolicyCoupler,
72
        ApplicationStorage $applicationStorage,
73
        ResourceStorage $resourceStorage
74
    ) {
75
        $this->environmentManager = $environmentManager;
76
        $this->authAdapter = $authAdapter;
77
        $this->userToPolicyCoupler = $userToPolicyCoupler;
78
        $this->userToGroupCoupler = $userToGroupCoupler;
79
        $this->userGroupToPolicyCoupler = $userGroupToPolicyCoupler;
80
        $this->applicationStorage = $applicationStorage;
81
        $this->resourceStorage = $resourceStorage;
82
    }
83
84
    /**
85
     * A middleware is a callable. It can do whatever is appropriate with the Request and Response objects.
86
     * The only hard requirement is that a middleware MUST return an instance of \Psr\Http\Message\ResponseInterface.
87
     * Each middleware SHOULD invoke the next middleware and pass it Request and Response objects as arguments.
88
     *
89
     * @param ServerRequestInterface $request
90
     * @param ResponseInterface      $response
91
     * @throws Exception
92
     * @return ResponseInterface
93
     */
94
    public function __invoke(ServerRequestInterface &$request, ResponseInterface $response)
95
    {
96
        $actionMiddleware = $request->getAttribute(ServerRequestInterface::REQUEST_ATTR_RESOLVED_ACTION_CLASS);
97
        $identity = false;
98
99
        if (in_array($actionMiddleware, $this->middlewareWhiteList)) {
100
            return $response;
101
        }
102
103
        if (!$this->authAdapter->hasIdentity()) {
104
            /** @var Result $result */
105
            $result = $this->authAdapter->authenticate();
106
107
            if ($result->isValid()) {
108
                $identity = $result->getIdentity();
109
                $this->authAdapter->setIdentity($identity);
0 ignored issues
show
Bug introduced by
It seems like $identity defined by $result->getIdentity() on line 108 can be null; however, WebHemi\Adapter\Auth\Aut...nterface::setIdentity() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
110
            }
111
        } else {
112
            $identity = $this->authAdapter->getIdentity();
113
        }
114
115
        if ($identity instanceof UserEntity) {
116
            /** @var array<UserGroupEntity> $userGroups */
117
            $userGroups = $this->userToGroupCoupler->getEntityDependencies($identity);
118
            /** @var array<PolicyEntity> $policies */
119
            $policies = $this->userToPolicyCoupler->getEntityDependencies($identity);
120
121
            foreach ($userGroups as $userGroupEntity) {
122
                /** @var array<PolicyEntity> $userGroupPolicies */
123
                $userGroupPolicies = $this->userGroupToPolicyCoupler->getEntityDependencies($userGroupEntity);
124
                $policies = array_merge($userGroupPolicies, $policies);
125
            }
126
127
            $selectedApplication = $this->environmentManager->getSelectedApplication();
128
            /** @var ApplicationEntity $applicationEntity */
129
            $applicationEntity = $this->applicationStorage->getApplicationByName($selectedApplication);
130
            /** @var ResourceEntity $resourceEntity */
131
            $resourceEntity = $this->resourceStorage->getResourceByName($actionMiddleware);
132
133
            // We assume the worst case: no access
134
            $hasAccess = false;
135
136
            /** @var PolicyEntity $policyEntity */
137
            foreach ($policies as $policyEntity) {
138
                $policyApplication = $policyEntity->getApplicationId();
139
                $policyResource = $policyEntity->getResourceId();
140
141
                // The user has access when:
142
                // - user has a policy which allows access AND
143
                // - user has a policy that connected to the current application OR any application AND
144
                // - user has a policy that connected to the current resource OR any resource
145
                if ($policyEntity->getAllowed() &&
146
                    ($policyApplication == null || $policyApplication == $applicationEntity->getApplicationId()) &&
147
                    ($policyResource == null || $policyResource == $resourceEntity->getResourceId())
148
                ) {
149
                    $hasAccess = true;
150
                    break;
151
                }
152
            }
153
154
            if (!$hasAccess) {
155
                $response = $response->withStatus(403, 'Forbidden');
156
            }
157
        } else {
158
            $response = $response->withStatus(401, 'Unauthorized');
159
        }
160
161
        return $response;
162
    }
163
}
164