1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Magister\Services\Auth; |
4
|
|
|
|
5
|
|
|
use Magister\Services\Contracts\Auth\Authenticable as UserContract; |
6
|
|
|
use Magister\Services\Contracts\Auth\Guard as GuardContract; |
7
|
|
|
use Magister\Services\Contracts\Auth\UserProvider; |
8
|
|
|
use Magister\Services\Contracts\Events\Dispatcher; |
9
|
|
|
use Magister\Services\Cookie\CookieJar; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Class Guard. |
13
|
|
|
*/ |
14
|
|
|
class Guard implements GuardContract |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* The currently authenticated user. |
18
|
|
|
* |
19
|
|
|
* @var \Magister\Services\Contracts\Auth\Authenticable |
20
|
|
|
*/ |
21
|
|
|
protected $user; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* The user provider implementation. |
25
|
|
|
* |
26
|
|
|
* @var \Magister\Services\Contracts\Auth\UserProvider |
27
|
|
|
*/ |
28
|
|
|
protected $provider; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The Magister cookie creator service. |
32
|
|
|
* |
33
|
|
|
* @var \Magister\Services\Cookie\CookieJar |
34
|
|
|
*/ |
35
|
|
|
protected $cookie; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* The event dispatcher instance. |
39
|
|
|
* |
40
|
|
|
* @var \Magister\Services\Contracts\Events\Dispatcher |
41
|
|
|
*/ |
42
|
|
|
protected $events; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Indicates if the logout method has been called. |
46
|
|
|
* |
47
|
|
|
* @var bool |
48
|
|
|
*/ |
49
|
|
|
protected $loggedOut = false; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Create a new guard instance. |
53
|
|
|
* |
54
|
|
|
* @param \Magister\Services\Contracts\Auth\UserProvider $provider |
55
|
|
|
*/ |
56
|
|
|
public function __construct(UserProvider $provider) |
57
|
|
|
{ |
58
|
|
|
$this->provider = $provider; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Determine if the current user is authenticated. |
63
|
|
|
* |
64
|
|
|
* @return bool |
65
|
|
|
*/ |
66
|
|
|
public function check() |
67
|
|
|
{ |
68
|
|
|
return !is_null($this->user()); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Determine if the current user is a guest. |
73
|
|
|
* |
74
|
|
|
* @return bool |
75
|
|
|
*/ |
76
|
|
|
public function guest() |
77
|
|
|
{ |
78
|
|
|
return !$this->check(); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Get the currently authenticated user. |
83
|
|
|
* |
84
|
|
|
* @return \Magister\Services\Contracts\Auth\Authenticable|null |
85
|
|
|
*/ |
86
|
|
|
public function user() |
87
|
|
|
{ |
88
|
|
|
if ($this->loggedOut) { |
89
|
|
|
return; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
// If we have already retrieved the user for the current request we can just |
93
|
|
|
// return it back immediately. We do not want to pull the user data every |
94
|
|
|
// request into the method because that would tremendously slow an app. |
95
|
|
|
if (!is_null($this->user)) { |
96
|
|
|
return $this->user; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
$id = $this->cookie->get($this->getName()); |
100
|
|
|
|
101
|
|
|
$user = null; |
102
|
|
|
|
103
|
|
|
if (!is_null($id)) { |
104
|
|
|
$user = $this->provider->retrieveByToken(); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
return $this->user = $user; |
|
|
|
|
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Get the id for the currently authenticated user. |
112
|
|
|
* |
113
|
|
|
* @return int|null |
114
|
|
|
*/ |
115
|
|
|
public function id() |
116
|
|
|
{ |
117
|
|
|
if ($this->loggedOut) { |
118
|
|
|
return; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
$id = $this->cookie->get($this->getName()); |
122
|
|
|
|
123
|
|
|
if (is_null($id) && $this->user()) { |
124
|
|
|
$id = $this->user()->getAuthIdentifier(); |
|
|
|
|
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
return $id; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Attempt to authenticate a user using the given credentials. |
132
|
|
|
* |
133
|
|
|
* @param array $credentials |
134
|
|
|
* @param bool $login |
135
|
|
|
* |
136
|
|
|
* @return bool |
137
|
|
|
*/ |
138
|
|
|
public function attempt(array $credentials = [], $login = true) |
139
|
|
|
{ |
140
|
|
|
$user = $this->provider->retrieveByCredentials($credentials); |
141
|
|
|
|
142
|
|
|
if ($this->hasValidCredentials($user)) { |
143
|
|
|
if ($login) { |
144
|
|
|
$this->login($user); |
|
|
|
|
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return true; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
return false; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Determine if the user matches the credentials. |
155
|
|
|
* |
156
|
|
|
* @param mixed $user |
157
|
|
|
* |
158
|
|
|
* @return bool |
159
|
|
|
*/ |
160
|
|
|
protected function hasValidCredentials($user) |
161
|
|
|
{ |
162
|
|
|
return !is_null($user); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Log a user into the application. |
167
|
|
|
* |
168
|
|
|
* @param \Magister\Services\Contracts\Auth\Authenticable $user |
169
|
|
|
* |
170
|
|
|
* @return void |
171
|
|
|
*/ |
172
|
|
|
public function login(UserContract $user) |
173
|
|
|
{ |
174
|
|
|
$this->updateSession($user->getAuthIdentifier()); |
175
|
|
|
|
176
|
|
|
// If we have an event dispatcher instance set we will fire an event so that |
177
|
|
|
// any listeners will hook into the authentication events and run actions |
178
|
|
|
// based on the login and logout events fired from the guard instances. |
179
|
|
|
$this->fireLoginEvent($user); |
180
|
|
|
|
181
|
|
|
$this->setUser($user); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Fire the login event if the dispatcher is set. |
186
|
|
|
* |
187
|
|
|
* @param \Magister\Services\Contracts\Auth\Authenticable $user |
188
|
|
|
* |
189
|
|
|
* @return void |
190
|
|
|
*/ |
191
|
|
|
protected function fireLoginEvent($user) |
192
|
|
|
{ |
193
|
|
|
if (isset($this->events)) { |
194
|
|
|
$this->events->fire('auth.login', $user); |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Update the session with the given cookie. |
200
|
|
|
* |
201
|
|
|
* @param string $id |
202
|
|
|
* |
203
|
|
|
* @return void |
204
|
|
|
*/ |
205
|
|
|
protected function updateSession($id) |
206
|
|
|
{ |
207
|
|
|
$this->cookie->make($this->getName(), $id); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Log the user out of the application. |
212
|
|
|
* |
213
|
|
|
* @return void |
214
|
|
|
*/ |
215
|
|
|
public function logout() |
216
|
|
|
{ |
217
|
|
|
$user = $this->user(); |
218
|
|
|
|
219
|
|
|
// If we have an event dispatcher instance, we can fire off the logout event |
220
|
|
|
// so any further processing can be done. This allows the developer to be |
221
|
|
|
// listening for anytime a user signs out of this application manually. |
222
|
|
|
$this->clearUserDataFromStorage(); |
223
|
|
|
|
224
|
|
|
if (isset($this->events)) { |
225
|
|
|
$this->events->fire('auth.logout', $user); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
// Once we have fired the logout event we will clear the users out of memory |
229
|
|
|
// so they are no longer available as the user is no longer considered as |
230
|
|
|
// being signed into this application and should not be available here. |
231
|
|
|
$this->user = null; |
232
|
|
|
|
233
|
|
|
$this->loggedOut = true; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Remove the user data from the session and cookies. |
238
|
|
|
* |
239
|
|
|
* @return void |
240
|
|
|
*/ |
241
|
|
|
protected function clearUserDataFromStorage() |
242
|
|
|
{ |
243
|
|
|
$this->getCookieJar()->forget($this->getName()); |
244
|
|
|
|
245
|
|
|
$recaller = $this->getRecallerName(); |
246
|
|
|
|
247
|
|
|
$this->getCookieJar()->forget($recaller); |
248
|
|
|
|
249
|
|
|
$this->provider->removeToken(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Get the name of the cookie used to store the "recaller". |
254
|
|
|
* |
255
|
|
|
* @return string |
256
|
|
|
*/ |
257
|
|
|
public function getRecallerName() |
258
|
|
|
{ |
259
|
|
|
return 'SESSION_ID'; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Get a unique identifier for the auth session value. |
264
|
|
|
* |
265
|
|
|
* @return string |
266
|
|
|
*/ |
267
|
|
|
public function getName() |
268
|
|
|
{ |
269
|
|
|
return 'login_'.md5(get_class($this)); |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Set the current user. |
274
|
|
|
* |
275
|
|
|
* @param \Magister\Services\Contracts\Auth\Authenticable $user |
276
|
|
|
* |
277
|
|
|
* @return void |
278
|
|
|
*/ |
279
|
|
|
public function setUser(UserContract $user) |
280
|
|
|
{ |
281
|
|
|
$this->user = $user; |
282
|
|
|
|
283
|
|
|
$this->loggedOut = false; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Return the currently cached user. |
288
|
|
|
* |
289
|
|
|
* @return mixed|null |
290
|
|
|
*/ |
291
|
|
|
public function getUser() |
292
|
|
|
{ |
293
|
|
|
return $this->user; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Set the cookie creator instance used by the guard. |
298
|
|
|
* |
299
|
|
|
* @param \Magister\Services\Cookie\CookieJar $cookie |
300
|
|
|
* |
301
|
|
|
* @return void |
302
|
|
|
*/ |
303
|
|
|
public function setCookieJar(CookieJar $cookie) |
304
|
|
|
{ |
305
|
|
|
$this->cookie = $cookie; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Get the cookie creator instance used by the guard. |
310
|
|
|
* |
311
|
|
|
* @throws \RuntimeException |
312
|
|
|
* |
313
|
|
|
* @return \Magister\Services\Cookie\CookieJar |
314
|
|
|
*/ |
315
|
|
|
public function getCookieJar() |
316
|
|
|
{ |
317
|
|
|
if (!isset($this->cookie)) { |
318
|
|
|
throw new \RuntimeException('Cookie jar has not been set.'); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
return $this->cookie; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Set the event dispatcher instance. |
326
|
|
|
* |
327
|
|
|
* @param \Magister\Services\Contracts\Events\Dispatcher $events |
328
|
|
|
* |
329
|
|
|
* @return void |
330
|
|
|
*/ |
331
|
|
|
public function setDispatcher(Dispatcher $events) |
332
|
|
|
{ |
333
|
|
|
$this->events = $events; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Get the event dispatcher instance. |
338
|
|
|
* |
339
|
|
|
* @return \Magister\Services\Contracts\Events\Dispatcher |
340
|
|
|
*/ |
341
|
|
|
public function getDispatcher() |
342
|
|
|
{ |
343
|
|
|
return $this->events; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Set the user provider used by the guard. |
348
|
|
|
* |
349
|
|
|
* @param \Magister\Services\Contracts\Auth\UserProvider $provider |
350
|
|
|
* |
351
|
|
|
* @return void |
352
|
|
|
*/ |
353
|
|
|
public function setProvider(UserProvider $provider) |
354
|
|
|
{ |
355
|
|
|
$this->provider = $provider; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Get the user provider used by the guard. |
360
|
|
|
* |
361
|
|
|
* @return \Magister\Services\Contracts\Auth\UserProvider |
362
|
|
|
*/ |
363
|
|
|
public function getProvider() |
364
|
|
|
{ |
365
|
|
|
return $this->provider; |
366
|
|
|
} |
367
|
|
|
} |
368
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.