1
|
|
|
<?php |
2
|
|
|
namespace App\Common; |
3
|
|
|
|
4
|
|
|
use Zend\Permissions\Acl\Acl as ZendAcl; |
5
|
|
|
use Zend\Permissions\Acl\Role\GenericRole as GenericRole; |
6
|
|
|
use Zend\Permissions\Acl\Resource\GenericResource as GenericResource; |
7
|
|
|
|
8
|
|
|
final class Acl extends ZendAcl |
9
|
|
|
{ |
10
|
|
|
/** |
11
|
|
|
* @var string privilege to make POST request to the specified resource |
12
|
|
|
*/ |
13
|
|
|
const PRIVILEGE_POST = 'post'; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @var string privilege to mekt GET request to the specified resource |
17
|
|
|
*/ |
18
|
|
|
const PRIVILEGE_GET = 'get'; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var string guard can be applied to resources |
22
|
|
|
*/ |
23
|
|
|
const GUARD_TYPE_RESOURCE = 'resources'; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var string guard can be applied to routes |
27
|
|
|
*/ |
28
|
|
|
const GUARD_TYPE_ROUTE = 'routes'; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var guard can be applied to callables |
32
|
|
|
*/ |
33
|
|
|
const GUARD_TYPE_CALLABLE = 'callables'; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Acl constructor. |
37
|
|
|
* |
38
|
|
|
* @param $configuration ACL configuration - see app settings ACL section |
39
|
|
|
* @throws \Exception |
40
|
|
|
*/ |
41
|
|
|
public function __construct($configuration) |
42
|
|
|
{ |
43
|
|
|
// setup roles |
44
|
|
|
foreach ($configuration['roles'] as $role => $parents) { |
45
|
|
|
$this->addRole(new GenericRole($role), $parents); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
// setup resources |
49
|
|
|
if (array_key_exists('resources', $configuration)) { |
50
|
|
|
foreach ($configuration['resources'] as $resource => $parent) { |
51
|
|
|
// create resource |
52
|
|
|
$this->addResource(new GenericResource($resource), $parent); |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
// setup guards |
57
|
|
|
foreach ($configuration['guards'] as $guardType => $guardRules) { |
58
|
|
|
foreach ($guardRules as $rule) { |
59
|
|
|
// parse rule into parts |
60
|
|
|
list($resource, $roles, $privileges) = $this->parseRule($guardType, $rule); |
61
|
|
|
|
62
|
|
|
if ($guardType != static::GUARD_TYPE_RESOURCE) { |
63
|
|
|
// resources were already build earlier |
64
|
|
|
$resource = static::buildResourceName($guardType, $resource); |
65
|
|
|
$this->addResource(new GenericResource($resource)); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
// allow access to this resource |
69
|
|
|
$this->allow($roles, $resource, $privileges); |
70
|
|
|
} |
71
|
|
|
} |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Verify guard rule correctness |
76
|
|
|
* |
77
|
|
|
* @param $guardType |
78
|
|
|
* @param array $rule |
79
|
|
|
* @return bool |
80
|
|
|
* @throws \Exception |
81
|
|
|
*/ |
82
|
|
|
protected function verifyRule($guardType, array $rule) |
83
|
|
|
{ |
84
|
|
|
switch ($guardType) { |
85
|
|
|
case static::GUARD_TYPE_RESOURCE: |
86
|
|
|
// resources guards must have 2 parts |
87
|
|
|
|
88
|
|
|
case static::GUARD_TYPE_CALLABLE: |
89
|
|
|
// callables guards must have 2 parts |
90
|
|
|
// 'callables' => [ |
|
|
|
|
91
|
|
|
// // resource, roles, privileges |
92
|
|
|
// ['App\Controller\CrudController', ['user']], |
|
|
|
|
93
|
|
|
// ['App\Controller\CrudController:actionIndex', ['user']], |
|
|
|
|
94
|
|
|
if (count($rule) == 2) { |
95
|
|
|
// all OK |
96
|
|
|
return true; |
97
|
|
|
} |
98
|
|
|
break; |
99
|
|
|
|
100
|
|
|
case static::GUARD_TYPE_ROUTE: |
101
|
|
|
// routes guards must have 3 parts |
102
|
|
|
// 'routes' => [ |
|
|
|
|
103
|
|
|
// // resource, roles, privileges |
104
|
|
|
// ['/api/token', ['guest'], ['post']], |
|
|
|
|
105
|
|
|
// ['/api/user', ['user'], ['get']], |
|
|
|
|
106
|
|
|
if (count($rule) == 3) { |
107
|
|
|
// all OK |
108
|
|
|
return true; |
109
|
|
|
} |
110
|
|
|
break; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
// unknown guard type or incorrect rule structure |
114
|
|
|
throw new \Exception('Error Processing Request'); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Parse rule description into separate parts |
119
|
|
|
* |
120
|
|
|
* @param $guardType |
121
|
|
|
* @param $rule |
122
|
|
|
* @return array |
123
|
|
|
*/ |
124
|
|
|
protected function parseRule($guardType, $rule) |
125
|
|
|
{ |
126
|
|
|
$this->verifyRule($guardType, $rule); |
127
|
|
|
|
128
|
|
|
// resource name is located in $rule[0] |
129
|
|
|
// roles array is located in $rule[1] |
130
|
|
|
// privileges are located in $rule[2] |
131
|
|
|
// return lower-cased array of privileges |
132
|
|
|
|
133
|
|
|
$resource = $rule[0]; |
134
|
|
|
$roles = $rule[1]; |
135
|
|
|
$privileges = count($rule) == 3 ? array_map('strtolower', $rule[2]) : null; |
136
|
|
|
|
137
|
|
|
return [$resource, $roles, $privileges]; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Build name of the resource based on guard type |
142
|
|
|
* |
143
|
|
|
* @param $guardType guard type |
144
|
|
|
* @param $base base part of the resource name to be built |
145
|
|
|
* @return string ready resource name |
146
|
|
|
* @throws \Exception |
147
|
|
|
*/ |
148
|
|
|
public static function buildResourceName($guardType, $base) |
149
|
|
|
{ |
150
|
|
|
switch ($guardType) { |
151
|
|
|
case static::GUARD_TYPE_CALLABLE: |
152
|
|
|
return "callable//$base"; |
|
|
|
|
153
|
|
|
|
154
|
|
|
case static::GUARD_TYPE_ROUTE: |
155
|
|
|
return "route//$base"; |
|
|
|
|
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
// unknown guard type |
159
|
|
|
throw new \Exception('Error Processing Request'); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Get one of PRIVILEGE_XXX constants based on HTTP method - GET, POST, PUT, etc |
164
|
|
|
* |
165
|
|
|
* @param $method HTTP method GET, POST, PUT, etc |
166
|
|
|
* @return null|string |
167
|
|
|
*/ |
168
|
|
|
public static function getPrivilegeByHTTPMethod($method) |
169
|
|
|
{ |
170
|
|
|
switch (strtolower($method)) { |
171
|
|
|
case 'post': |
172
|
|
|
return static::PRIVILEGE_POST; |
173
|
|
|
|
174
|
|
|
case 'get': |
175
|
|
|
return static::PRIVILEGE_GET; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
return null; |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.