AclMiddleware::checkResponseOrDie()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 5
cp 0
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
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
    protected function isAllowedForUser($aclResource, $action, $namespace = '')
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
    protected function isAllowedForGuest($aclResource, $action, $namespace = '')
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
}