1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the BenGorUser package. |
5
|
|
|
* |
6
|
|
|
* (c) Beñat Espiña <[email protected]> |
7
|
|
|
* (c) Gorka Laucirica <[email protected]> |
8
|
|
|
* |
9
|
|
|
* For the full copyright and license information, please view the LICENSE |
10
|
|
|
* file that was distributed with this source code. |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace BenGorUser\User\Domain\Model; |
14
|
|
|
|
15
|
|
|
use BenGorUser\User\Domain\Model\Event\UserEnabled; |
16
|
|
|
use BenGorUser\User\Domain\Model\Event\UserInvited; |
17
|
|
|
use BenGorUser\User\Domain\Model\Event\UserLoggedIn; |
18
|
|
|
use BenGorUser\User\Domain\Model\Event\UserLoggedOut; |
19
|
|
|
use BenGorUser\User\Domain\Model\Event\UserRegistered; |
20
|
|
|
use BenGorUser\User\Domain\Model\Event\UserRememberPasswordRequested; |
21
|
|
|
use BenGorUser\User\Domain\Model\Event\UserRoleGranted; |
22
|
|
|
use BenGorUser\User\Domain\Model\Event\UserRoleRevoked; |
23
|
|
|
use BenGorUser\User\Domain\Model\Exception\UserInactiveException; |
24
|
|
|
use BenGorUser\User\Domain\Model\Exception\UserInvitationAlreadyAcceptedException; |
25
|
|
|
use BenGorUser\User\Domain\Model\Exception\UserPasswordInvalidException; |
26
|
|
|
use BenGorUser\User\Domain\Model\Exception\UserRoleAlreadyGrantedException; |
27
|
|
|
use BenGorUser\User\Domain\Model\Exception\UserRoleAlreadyRevokedException; |
28
|
|
|
use BenGorUser\User\Domain\Model\Exception\UserRoleInvalidException; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* User domain class. |
32
|
|
|
* |
33
|
|
|
* @author Beñat Espiña <[email protected]> |
34
|
|
|
* @author Gorka Laucirica <[email protected]> |
35
|
|
|
*/ |
36
|
|
|
class User extends UserAggregateRoot |
37
|
|
|
{ |
38
|
|
|
/** |
39
|
|
|
* The id. |
40
|
|
|
* |
41
|
|
|
* @var UserId |
42
|
|
|
*/ |
43
|
|
|
protected $id; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* The confirmation token. |
47
|
|
|
* |
48
|
|
|
* @var UserToken |
49
|
|
|
*/ |
50
|
|
|
protected $confirmationToken; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Created on. |
54
|
|
|
* |
55
|
|
|
* @var \DateTimeInterface |
56
|
|
|
*/ |
57
|
|
|
protected $createdOn; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* The email. |
61
|
|
|
* |
62
|
|
|
* @var UserEmail |
63
|
|
|
*/ |
64
|
|
|
protected $email; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* The invitation token. |
68
|
|
|
* |
69
|
|
|
* @var UserToken |
70
|
|
|
*/ |
71
|
|
|
protected $invitationToken; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* The last login. |
75
|
|
|
* |
76
|
|
|
* @var \DateTimeInterface|null |
77
|
|
|
*/ |
78
|
|
|
protected $lastLogin; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* The password. |
82
|
|
|
* |
83
|
|
|
* @var UserPassword |
84
|
|
|
*/ |
85
|
|
|
protected $password; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* The remember password token. |
89
|
|
|
* |
90
|
|
|
* @var UserToken |
91
|
|
|
*/ |
92
|
|
|
protected $rememberPasswordToken; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Array which contains roles. |
96
|
|
|
* |
97
|
|
|
* @var UserRole[] |
98
|
|
|
*/ |
99
|
|
|
protected $roles; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Updated on. |
103
|
|
|
* |
104
|
|
|
* @var \DateTimeInterface |
105
|
|
|
*/ |
106
|
|
|
protected $updatedOn; |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Constructor. |
110
|
|
|
* |
111
|
|
|
* @param UserId $anId The id |
112
|
|
|
* @param UserEmail $anEmail The email |
113
|
|
|
* @param array $userRoles Array which contains the roles |
114
|
|
|
* @param UserPassword|null $aPassword The encoded password |
115
|
|
|
*/ |
116
|
|
|
protected function __construct( |
117
|
|
|
UserId $anId, |
118
|
|
|
UserEmail $anEmail, |
119
|
|
|
array $userRoles, |
120
|
|
|
UserPassword $aPassword = null |
121
|
|
|
) { |
122
|
|
|
$this->id = $anId; |
123
|
|
|
$this->email = $anEmail; |
124
|
|
|
$this->password = $aPassword; |
125
|
|
|
$this->confirmationToken = new UserToken(); |
126
|
|
|
$this->createdOn = new \DateTimeImmutable(); |
127
|
|
|
$this->updatedOn = new \DateTimeImmutable(); |
128
|
|
|
|
129
|
|
|
$this->roles = []; |
130
|
|
|
foreach ($userRoles as $userRole) { |
131
|
|
|
$this->grant($userRole); |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Sign up user. |
137
|
|
|
* |
138
|
|
|
* @param UserId $anId The id |
139
|
|
|
* @param UserEmail $anEmail The email |
140
|
|
|
* @param UserPassword $aPassword The encoded password |
141
|
|
|
* @param array $userRoles Array which contains the roles |
142
|
|
|
* |
143
|
|
|
* @return static |
144
|
|
|
*/ |
145
|
|
View Code Duplication |
public static function signUp(UserId $anId, UserEmail $anEmail, UserPassword $aPassword, array $userRoles) |
|
|
|
|
146
|
|
|
{ |
147
|
|
|
$user = new static($anId, $anEmail, $userRoles, $aPassword); |
148
|
|
|
$user->publish( |
149
|
|
|
new UserRegistered( |
150
|
|
|
$user->id(), |
151
|
|
|
$user->email(), |
152
|
|
|
$user->confirmationToken() |
153
|
|
|
) |
154
|
|
|
); |
155
|
|
|
|
156
|
|
|
return $user; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Invites user. |
161
|
|
|
* |
162
|
|
|
* @param UserId $anId The id |
163
|
|
|
* @param UserEmail $anEmail The email |
164
|
|
|
* @param array $userRoles Array which contains the roles |
165
|
|
|
* |
166
|
|
|
* @return static |
167
|
|
|
*/ |
168
|
|
View Code Duplication |
public static function invite(UserId $anId, UserEmail $anEmail, array $userRoles) |
|
|
|
|
169
|
|
|
{ |
170
|
|
|
$user = new static($anId, $anEmail, $userRoles); |
171
|
|
|
$user->invitationToken = new UserToken(); |
172
|
|
|
$user->publish( |
173
|
|
|
new UserInvited( |
174
|
|
|
$user->id(), |
175
|
|
|
$user->email(), |
176
|
|
|
$user->invitationToken() |
177
|
|
|
) |
178
|
|
|
); |
179
|
|
|
|
180
|
|
|
return $user; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Gets the id. |
185
|
|
|
* |
186
|
|
|
* @return UserId |
187
|
|
|
*/ |
188
|
|
|
public function id() |
189
|
|
|
{ |
190
|
|
|
return $this->id; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Accepts the invitation request. |
195
|
|
|
*/ |
196
|
|
|
public function acceptInvitation() |
197
|
|
|
{ |
198
|
|
|
$this->invitationToken = null; |
199
|
|
|
$this->updatedOn = new \DateTimeImmutable(); |
200
|
|
|
$this->publish( |
201
|
|
|
new UserRegistered( |
202
|
|
|
$this->id(), |
203
|
|
|
$this->email() |
204
|
|
|
) |
205
|
|
|
); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Updates the user password. |
210
|
|
|
* |
211
|
|
|
* @param UserPassword $aPassword The old password |
212
|
|
|
*/ |
213
|
|
|
public function changePassword(UserPassword $aPassword) |
214
|
|
|
{ |
215
|
|
|
$this->password = $aPassword; |
216
|
|
|
$this->rememberPasswordToken = null; |
217
|
|
|
$this->updatedOn = new \DateTimeImmutable(); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Gets the confirmation token. |
222
|
|
|
* |
223
|
|
|
* @return UserToken |
224
|
|
|
*/ |
225
|
|
|
public function confirmationToken() |
226
|
|
|
{ |
227
|
|
|
return $this->confirmationToken; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Gets the created on. |
232
|
|
|
* |
233
|
|
|
* @return \DateTimeInterface |
234
|
|
|
*/ |
235
|
|
|
public function createdOn() |
236
|
|
|
{ |
237
|
|
|
return $this->createdOn; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Gets the email. |
242
|
|
|
* |
243
|
|
|
* @return UserEmail |
244
|
|
|
*/ |
245
|
|
|
public function email() |
246
|
|
|
{ |
247
|
|
|
return $this->email; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Enables the user account. |
252
|
|
|
*/ |
253
|
|
|
public function enableAccount() |
254
|
|
|
{ |
255
|
|
|
$this->confirmationToken = null; |
256
|
|
|
$this->updatedOn = new \DateTimeImmutable(); |
257
|
|
|
|
258
|
|
|
$this->publish( |
259
|
|
|
new UserEnabled( |
260
|
|
|
$this->id, |
261
|
|
|
$this->email |
262
|
|
|
) |
263
|
|
|
); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Adds the given role. |
268
|
|
|
* |
269
|
|
|
* @param UserRole $aRole The user role |
270
|
|
|
*/ |
271
|
|
|
public function grant(UserRole $aRole) |
272
|
|
|
{ |
273
|
|
|
if (false === $this->isRoleAllowed($aRole)) { |
274
|
|
|
throw new UserRoleInvalidException(); |
275
|
|
|
} |
276
|
|
|
if (true === $this->isGranted($aRole)) { |
277
|
|
|
throw new UserRoleAlreadyGrantedException(); |
278
|
|
|
} |
279
|
|
|
$this->roles[] = $aRole; |
280
|
|
|
$this->updatedOn = new \DateTimeImmutable(); |
281
|
|
|
|
282
|
|
|
$this->publish( |
283
|
|
|
new UserRoleGranted( |
284
|
|
|
$this->id, |
285
|
|
|
$this->email, |
286
|
|
|
$aRole |
287
|
|
|
) |
288
|
|
|
); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Gets the invitation token. |
293
|
|
|
* |
294
|
|
|
* @return UserToken |
295
|
|
|
*/ |
296
|
|
|
public function invitationToken() |
297
|
|
|
{ |
298
|
|
|
return $this->invitationToken; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Checks if the user is enabled or not. |
303
|
|
|
* |
304
|
|
|
* @return bool |
305
|
|
|
*/ |
306
|
|
|
public function isEnabled() |
307
|
|
|
{ |
308
|
|
|
return null === $this->confirmationToken || null === $this->confirmationToken->token(); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Checks if the user has the given role. |
313
|
|
|
* |
314
|
|
|
* @param UserRole $aRole The user role |
315
|
|
|
* |
316
|
|
|
* @return bool |
317
|
|
|
*/ |
318
|
|
|
public function isGranted(UserRole $aRole) |
319
|
|
|
{ |
320
|
|
|
foreach ($this->roles as $role) { |
321
|
|
|
if ($role->equals($aRole)) { |
322
|
|
|
return true; |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
return false; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Checks if the role given appears between allowed roles. |
331
|
|
|
* |
332
|
|
|
* @param UserRole $aRole The user role |
333
|
|
|
* |
334
|
|
|
* @return bool |
335
|
|
|
*/ |
336
|
|
|
public function isRoleAllowed(UserRole $aRole) |
337
|
|
|
{ |
338
|
|
|
return in_array($aRole->role(), static::availableRoles(), true); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Gets the last login. |
343
|
|
|
* |
344
|
|
|
* @return \DateTimeInterface |
|
|
|
|
345
|
|
|
*/ |
346
|
|
|
public function lastLogin() |
347
|
|
|
{ |
348
|
|
|
return $this->lastLogin; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Validates user login for the given password. |
353
|
|
|
* |
354
|
|
|
* @param string $aPlainPassword Plain password used to log in |
355
|
|
|
* @param UserPasswordEncoder $anEncoder The encoder used to encode the password |
356
|
|
|
* |
357
|
|
|
* @throws UserInactiveException when the user is not enabled |
358
|
|
|
* @throws UserPasswordInvalidException when the user password is invalid |
359
|
|
|
*/ |
360
|
|
|
public function login($aPlainPassword, UserPasswordEncoder $anEncoder) |
361
|
|
|
{ |
362
|
|
|
if (false === $this->isEnabled()) { |
363
|
|
|
throw new UserInactiveException(); |
364
|
|
|
} |
365
|
|
|
if (false === $this->password()->equals($aPlainPassword, $anEncoder)) { |
366
|
|
|
throw new UserPasswordInvalidException(); |
367
|
|
|
} |
368
|
|
|
$this->lastLogin = new \DateTimeImmutable(); |
369
|
|
|
|
370
|
|
|
$this->publish( |
371
|
|
|
new UserLoggedIn( |
372
|
|
|
$this->id, |
373
|
|
|
$this->email |
374
|
|
|
) |
375
|
|
|
); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Updated the user state after logout. |
380
|
|
|
* |
381
|
|
|
* @throws UserInactiveException when the user is not enabled |
382
|
|
|
*/ |
383
|
|
|
public function logout() |
384
|
|
|
{ |
385
|
|
|
if (false === $this->isEnabled()) { |
386
|
|
|
throw new UserInactiveException(); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
$this->publish( |
390
|
|
|
new UserLoggedOut( |
391
|
|
|
$this->id, |
392
|
|
|
$this->email |
393
|
|
|
) |
394
|
|
|
); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Gets the password. |
399
|
|
|
* |
400
|
|
|
* @return UserPassword |
401
|
|
|
*/ |
402
|
|
|
public function password() |
403
|
|
|
{ |
404
|
|
|
return $this->password; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Updates the invitation token in case a user has |
409
|
|
|
* been already invited and has lost the token. |
410
|
|
|
* |
411
|
|
|
* @throws UserInvitationAlreadyAcceptedException in case user has already accepted the invitation |
412
|
|
|
*/ |
413
|
|
|
public function regenerateInvitationToken() |
414
|
|
|
{ |
415
|
|
|
if (null === $this->invitationToken) { |
416
|
|
|
throw new UserInvitationAlreadyAcceptedException(); |
417
|
|
|
} |
418
|
|
|
$this->invitationToken = new UserToken(); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Gets the remember password token. |
423
|
|
|
* |
424
|
|
|
* @return UserToken |
425
|
|
|
*/ |
426
|
|
|
public function rememberPasswordToken() |
427
|
|
|
{ |
428
|
|
|
return $this->rememberPasswordToken; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* Remembers the password. |
433
|
|
|
*/ |
434
|
|
|
public function rememberPassword() |
435
|
|
|
{ |
436
|
|
|
$this->rememberPasswordToken = new UserToken(); |
437
|
|
|
|
438
|
|
|
$this->publish( |
439
|
|
|
new UserRememberPasswordRequested( |
440
|
|
|
$this->id, |
441
|
|
|
$this->email, |
442
|
|
|
$this->rememberPasswordToken |
443
|
|
|
) |
444
|
|
|
); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Removes the given role. |
449
|
|
|
* |
450
|
|
|
* @param UserRole $aRole The user role |
451
|
|
|
*/ |
452
|
|
|
public function revoke(UserRole $aRole) |
453
|
|
|
{ |
454
|
|
|
if (false === $this->isRoleAllowed($aRole)) { |
455
|
|
|
throw new UserRoleInvalidException(); |
456
|
|
|
} |
457
|
|
|
foreach ($this->roles as $key => $role) { |
458
|
|
|
if ($role->equals($aRole)) { |
459
|
|
|
unset($this->roles[$key]); |
460
|
|
|
break; |
461
|
|
|
} |
462
|
|
|
throw new UserRoleAlreadyRevokedException(); |
463
|
|
|
} |
464
|
|
|
$this->updatedOn = new \DateTimeImmutable(); |
465
|
|
|
$this->publish( |
466
|
|
|
new UserRoleRevoked( |
467
|
|
|
$this->id, |
468
|
|
|
$this->email, |
469
|
|
|
$aRole |
470
|
|
|
) |
471
|
|
|
); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
/** |
475
|
|
|
* Gets the roles. |
476
|
|
|
* |
477
|
|
|
* @return UserRole[] |
478
|
|
|
*/ |
479
|
|
|
public function roles() |
480
|
|
|
{ |
481
|
|
|
return $this->roles; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Gets the updated on. |
486
|
|
|
* |
487
|
|
|
* @return \DateTimeInterface |
488
|
|
|
*/ |
489
|
|
|
public function updatedOn() |
490
|
|
|
{ |
491
|
|
|
return $this->updatedOn; |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Gets the available roles in scalar type. |
496
|
|
|
* |
497
|
|
|
* This method is an extension point that it allows |
498
|
|
|
* to add more roles easily in the domain. |
499
|
|
|
* |
500
|
|
|
* @return array |
|
|
|
|
501
|
|
|
*/ |
502
|
|
|
public static function availableRoles() |
503
|
|
|
{ |
504
|
|
|
return ['ROLE_USER', 'ROLE_ADMIN']; |
505
|
|
|
} |
506
|
|
|
} |
507
|
|
|
|
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.