1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\User; |
4
|
|
|
|
5
|
|
|
use RuntimeException; |
6
|
|
|
|
7
|
|
|
// From Pimple |
8
|
|
|
use Pimple\Container; |
9
|
|
|
|
10
|
|
|
// From 'charcoal-user' |
11
|
|
|
use Charcoal\User\Authenticator; |
12
|
|
|
use Charcoal\User\Authorizer; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* An implementation, as Trait, of the {@see \Charcoal\User\AuthAwareInterface}. |
16
|
|
|
*/ |
17
|
|
|
trait AuthAwareTrait |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* @var Authenticator |
21
|
|
|
*/ |
22
|
|
|
private $authenticator; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var Authorizer |
26
|
|
|
*/ |
27
|
|
|
private $authorizer; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var string[] |
31
|
|
|
*/ |
32
|
|
|
private $requiredAclPermissions = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @param Container $container The DI container. |
36
|
|
|
* @return void |
37
|
|
|
*/ |
38
|
|
|
protected function setAuthDependencies(Container $container) |
39
|
|
|
{ |
40
|
|
|
$this->setAuthenticator($container['admin/authenticator']); |
41
|
|
|
$this->setAuthorizer($container['admin/authorizer']); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Set the authentication service. |
46
|
|
|
* |
47
|
|
|
* @param Authenticator $authenticator The authentication service. |
48
|
|
|
* @return AuthAwareInterface |
49
|
|
|
*/ |
50
|
|
|
protected function setAuthenticator(Authenticator $authenticator) |
51
|
|
|
{ |
52
|
|
|
$this->authenticator = $authenticator; |
53
|
|
|
|
54
|
|
|
return $this; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Retrieve the authentication service. |
59
|
|
|
* |
60
|
|
|
* @throws RuntimeException If the authenticator was not previously set. |
61
|
|
|
* @return Authenticator |
62
|
|
|
*/ |
63
|
|
View Code Duplication |
protected function authenticator() |
|
|
|
|
64
|
|
|
{ |
65
|
|
|
if (!$this->authenticator) { |
66
|
|
|
throw new RuntimeException(sprintf( |
67
|
|
|
'Authenticator service is not defined for "%s"', |
68
|
|
|
get_class($this) |
69
|
|
|
)); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
return $this->authenticator; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Set the authorization service. |
77
|
|
|
* |
78
|
|
|
* @param Authorizer $authorizer The authorization service. |
79
|
|
|
* @return AuthAwareInterface |
80
|
|
|
*/ |
81
|
|
|
protected function setAuthorizer(Authorizer $authorizer) |
82
|
|
|
{ |
83
|
|
|
$this->authorizer = $authorizer; |
84
|
|
|
|
85
|
|
|
return $this; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Retrieve the authorization service. |
90
|
|
|
* |
91
|
|
|
* @throws RuntimeException If the authorizer was not previously set. |
92
|
|
|
* @return Authorizer |
93
|
|
|
*/ |
94
|
|
View Code Duplication |
protected function authorizer() |
|
|
|
|
95
|
|
|
{ |
96
|
|
|
if (!$this->authenticator) { |
97
|
|
|
throw new RuntimeException(sprintf( |
98
|
|
|
'Authorizer service is not defined for "%s"', |
99
|
|
|
get_class($this) |
100
|
|
|
)); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
return $this->authorizer; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @param string[]|string|null $permissions The list of required permissions. |
108
|
|
|
* @throws InvalidArgumentException If the permissions are not an array or a comma-separated string. |
109
|
|
|
* @return AuthAwareTrait Chainable |
|
|
|
|
110
|
|
|
*/ |
111
|
|
|
protected function setRequiredAclPermissions($permissions) |
112
|
|
|
{ |
113
|
|
|
if ($permissions === null || !$permissions) { |
114
|
|
|
$this->permissions = []; |
|
|
|
|
115
|
|
|
return $this; |
116
|
|
|
} |
117
|
|
|
if (is_string($permissions)) { |
118
|
|
|
$permissions = explode(',', $permissions); |
119
|
|
|
$permissions = array_map('trim', $permissions); |
120
|
|
|
} |
121
|
|
|
if (!is_array($permissions)) { |
122
|
|
|
throw new InvalidArgumentException( |
123
|
|
|
'Invalid ACL permissions. Must be an array of permissions or a comma-separated string or permissions.' |
124
|
|
|
); |
125
|
|
|
} |
126
|
|
|
$this->requiredAclPermissions = $permissions; |
127
|
|
|
return $this; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @return string[] |
132
|
|
|
*/ |
133
|
|
|
protected function requiredAclPermissions() |
134
|
|
|
{ |
135
|
|
|
return $this->requiredAclPermissions; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @return boolean |
140
|
|
|
*/ |
141
|
|
|
public function isAuthorized() |
142
|
|
|
{ |
143
|
|
|
return $this->hasPermissions($this->requiredAclPermissions()); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* @param array $permissions The list of required permissions to check. |
148
|
|
|
* @return boolean |
149
|
|
|
*/ |
150
|
|
|
public function hasPermissions(array $permissions) |
151
|
|
|
{ |
152
|
|
|
if (empty($permissions)) { |
153
|
|
|
return true; |
154
|
|
|
} |
155
|
|
|
$authUser = $this->authenticator()->authenticate(); |
156
|
|
|
if (!$authUser) { |
157
|
|
|
return false; |
158
|
|
|
} |
159
|
|
|
$authorized = $this->authorizer()->userAllowed($authUser, $permissions); |
160
|
|
|
return $authorized; |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
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.