Passed
Push — master ( 442946...5eb80e )
by Eugene
03:20
created

AclMiddleware::isAllowedForUser()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 9
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 9
loc 9
ccs 0
cts 4
cp 0
rs 9.6666
cc 2
eloc 4
nc 2
nop 3
crap 6
1
<?php
2
namespace Staticus\Acl;
3
4
use Staticus\Auth\User;
5
use Staticus\Auth\UserInterface;
6
use Staticus\Config\ConfigInterface;
7
use Psr\Http\Message\ResponseInterface;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Staticus\Diactoros\Response\ResourceDoResponse;
10
use Staticus\Exceptions\WrongRequestException;
11
use Staticus\Resources\Middlewares\PrepareResourceMiddlewareAbstract;
12
use Staticus\Resources\ResourceDOInterface;
13
use Zend\Diactoros\Response\EmptyResponse;
14
use Zend\Permissions\Acl\Resource\ResourceInterface;
15
use Zend\Stratigility\MiddlewareInterface;
16
17
class AclMiddleware implements MiddlewareInterface
18
{
19
    protected $config;
20
21
    /**
22
     * @var AclServiceInterface|AclService
23
     */
24
    protected $service;
25
26
    /**
27
     * @var UserInterface|User
28
     */
29
    protected $user;
30
31
    public function __construct(ConfigInterface $config, AclServiceInterface $service, UserInterface $user)
32
    {
33
        $this->config = $config->get('acl');
34
        $this->service = $service;
35
        $this->user = $user;
36
    }
37
38
    public function __invoke(
39
        ServerRequestInterface $request,
40
        ResponseInterface $response,
41
        callable $next = null
42
    )
43
    {
44
        /** @var $response ResourceDoResponse */
45
        $this->checkResponseOrDie($response);
46
        $action = $this->getAction($request);
47
        $resourceDO = $response->getContent();
48
        $resourceNamespace = $resourceDO->getNamespace();
49
        $userNamespace = $this->user->getNamespace();
50
        $AclResourceCommon = get_class($resourceDO);
51
        $AclResourceUnique = $resourceDO instanceof ResourceInterface
52
            ? $resourceDO->getResourceId()
53
            : null;
54
55
        if (
56
            // User have access to this type of resources regardless namespaces
57
            $this->isAllowedForUser($AclResourceCommon, $action, '')
58
59
            // User have access to this unique resource regardless namespaces
60
            || $this->isAllowedForUser($AclResourceUnique, $action, '')
61
62
            // User have access to this resource type in common namespace
63
            || (
64
                !$resourceNamespace
65
                && $this->isAllowedForUser($AclResourceCommon, $action, ResourceDOInterface::NAMESPACES_WILDCARD)
66
            )
67
68
            // User have access to this resource type in concrete selected namespace
69
            || (
70
                $resourceNamespace
71
                && $this->isAllowedForUser($AclResourceCommon, $action, $resourceNamespace)
72
            )
73
            || (
74
                // This is a user home namespace
75
                $resourceNamespace === $userNamespace
76
77
                // User have access to the current action in his own namespace
78
                && $this->isAllowedForUser($AclResourceCommon, $action, UserInterface::NAMESPACES_WILDCARD)
79
            )
80
            || (
81
                // This is an another user namespace
82
                $resourceNamespace !== $userNamespace
83
                && 0 === strpos($resourceNamespace, UserInterface::NAMESPACES)
84
85
                // User have access to the current action in his own namespace
86
                && $this->isAllowedForGuest($AclResourceCommon, $action, UserInterface::NAMESPACES_WILDCARD)
87
            )
88
        ) {
89
90
            return $next($request, $response);
91
        }
92
93
        return new EmptyResponse(403);
94
    }
95
96
97
    /**
98
     * @param ResponseInterface $response
99
     */
100
    protected function checkResponseOrDie(ResponseInterface $response)
101
    {
102
        if (!$this->isSupportedResponse($response)) {
103
104
            // something like PrepareResourceMiddleware should be called before this
105
            throw new WrongRequestException(
106
                'Unsupported type of the response for ACL. Resource preparing layer must be called before this.');
107
        }
108
    }
109
110
    /**
111
     * @param ResponseInterface $response
112
     * @return bool
113
     */
114
    protected function isSupportedResponse(ResponseInterface $response)
115
    {
116
        return $response instanceof ResourceDoResponse;
117
    }
118
119
    /**
120
     * @param ServerRequestInterface $request
121
     * @return string
122
     */
123
    protected function getAction(ServerRequestInterface $request)
124
    {
125
        if (PrepareResourceMiddlewareAbstract::getParamFromRequest(Actions::ACTION_SEARCH, $request)) {
126
127
            return Actions::ACTION_SEARCH;
128
        }
129
        if (PrepareResourceMiddlewareAbstract::getParamFromRequest(Actions::ACTION_LIST, $request)) {
130
131
            return Actions::ACTION_LIST;
132
        }
133
134
        $method = $request->getMethod();
135
        switch ($method) {
136
            case 'GET':
137
                $action = Actions::ACTION_READ;
138
                break;
139
            case 'POST':
140
                $action = Actions::ACTION_WRITE;
141
                break;
142
            case 'DELETE':
143
                $action = Actions::ACTION_DELETE;
144
                break;
145
            default:
146
                throw new WrongRequestException('Unknown access control action');
147
        }
148
149
        return $action;
150
    }
151
152 View Code Duplication
    protected function isAllowedForUser($aclResource, $action, $namespace = '')
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in 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...
153
    {
154
        if (!$this->service->acl()->hasResource($namespace . $aclResource)) {
155
156
            return false;
157
        }
158
159
        return $this->user->can($namespace . $aclResource, $action);
160
    }
161
162 View Code Duplication
    protected function isAllowedForGuest($aclResource, $action, $namespace = '')
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in 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...
163
    {
164
        if (!$aclResource || !$this->service->acl()->hasResource($namespace . $aclResource)) {
165
166
            return false;
167
        }
168
169
        return $this->service->acl()->isAllowed(Roles::GUEST, $namespace . $aclResource, $action);
170
    }
171
}