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->isAllowed($AclResourceCommon, $action, '') |
58
|
|
|
|
59
|
|
|
// User have access to this unique resource regardless namespaces |
60
|
|
|
|| $this->isAllowed($AclResourceUnique, $action, '') |
61
|
|
|
|
62
|
|
|
// User have access to this resource type in common namespace |
63
|
|
|
|| ( |
64
|
|
|
!$resourceNamespace |
65
|
|
|
&& $this->isAllowed($AclResourceCommon, $action, ResourceDOInterface::NAMESPACES_WILDCARD) |
66
|
|
|
) |
67
|
|
|
|
68
|
|
|
// User have access to this resource type in concrete selected namespace |
69
|
|
|
|| ( |
70
|
|
|
$resourceNamespace |
71
|
|
|
&& $this->isAllowed($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->isAllowed($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 isAllowed($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
|
|
View Code Duplication |
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
|
|
|
} |
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.