Completed
Push — master ( 61acac...62012c )
by Gorka
03:59
created

User::isRememberPasswordTokenExpired()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
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\UserInvitationTokenRegenerated;
17
use BenGorUser\User\Domain\Model\Event\UserInvited;
18
use BenGorUser\User\Domain\Model\Event\UserLoggedIn;
19
use BenGorUser\User\Domain\Model\Event\UserLoggedOut;
20
use BenGorUser\User\Domain\Model\Event\UserRegistered;
21
use BenGorUser\User\Domain\Model\Event\UserRememberPasswordRequested;
22
use BenGorUser\User\Domain\Model\Event\UserRoleGranted;
23
use BenGorUser\User\Domain\Model\Event\UserRoleRevoked;
24
use BenGorUser\User\Domain\Model\Exception\UserInactiveException;
25
use BenGorUser\User\Domain\Model\Exception\UserInvitationAlreadyAcceptedException;
26
use BenGorUser\User\Domain\Model\Exception\UserPasswordInvalidException;
27
use BenGorUser\User\Domain\Model\Exception\UserRoleAlreadyGrantedException;
28
use BenGorUser\User\Domain\Model\Exception\UserRoleAlreadyRevokedException;
29
use BenGorUser\User\Domain\Model\Exception\UserRoleInvalidException;
30
use BenGorUser\User\Domain\Model\Exception\UserTokenExpiredException;
31
use BenGorUser\User\Domain\Model\Exception\UserTokenNotFoundException;
32
33
/**
34
 * User domain class.
35
 *
36
 * @author Beñat Espiña <[email protected]>
37
 * @author Gorka Laucirica <[email protected]>
38
 */
39
class User extends UserAggregateRoot
40
{
41
    /**
42
     * The id.
43
     *
44
     * @var UserId
45
     */
46
    protected $id;
47
48
    /**
49
     * The confirmation token.
50
     *
51
     * @var UserToken
52
     */
53
    protected $confirmationToken;
54
55
    /**
56
     * Created on.
57
     *
58
     * @var \DateTimeInterface
59
     */
60
    protected $createdOn;
61
62
    /**
63
     * The email.
64
     *
65
     * @var UserEmail
66
     */
67
    protected $email;
68
69
    /**
70
     * The invitation token.
71
     *
72
     * @var UserToken
73
     */
74
    protected $invitationToken;
75
76
    /**
77
     * The last login.
78
     *
79
     * @var \DateTimeInterface|null
80
     */
81
    protected $lastLogin;
82
83
    /**
84
     * The password.
85
     *
86
     * @var UserPassword
87
     */
88
    protected $password;
89
90
    /**
91
     * The remember password token.
92
     *
93
     * @var UserToken
94
     */
95
    protected $rememberPasswordToken;
96
97
    /**
98
     * Array which contains roles.
99
     *
100
     * @var UserRole[]
101
     */
102
    protected $roles;
103
104
    /**
105
     * Updated on.
106
     *
107
     * @var \DateTimeInterface
108
     */
109
    protected $updatedOn;
110
111
    /**
112
     * Constructor.
113
     *
114
     * @param UserId            $anId      The id
115
     * @param UserEmail         $anEmail   The email
116
     * @param array             $userRoles Array which contains the roles
117
     * @param UserPassword|null $aPassword The encoded password
118
     */
119
    protected function __construct(UserId $anId, UserEmail $anEmail, array $userRoles, UserPassword $aPassword = null)
120
    {
121
        $this->id = $anId;
122
        $this->email = $anEmail;
123
        $this->password = $aPassword;
124
        $this->createdOn = new \DateTimeImmutable();
125
        $this->updatedOn = new \DateTimeImmutable();
126
127
        $this->roles = [];
128
        foreach ($userRoles as $userRole) {
129
            $this->grant($userRole);
130
        }
131
    }
132
133
    /**
134
     * Sign up user.
135
     *
136
     * @param UserId       $anId      The id
137
     * @param UserEmail    $anEmail   The email
138
     * @param UserPassword $aPassword The encoded password
139
     * @param array        $userRoles Array which contains the roles
140
     *
141
     * @return static
142
     */
143 View Code Duplication
    public static function signUp(UserId $anId, UserEmail $anEmail, UserPassword $aPassword, array $userRoles)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
144
    {
145
        $user = new static($anId, $anEmail, $userRoles, $aPassword);
146
        $user->confirmationToken = new UserToken();
147
        $user->publish(
148
            new UserRegistered(
149
                $user->id(),
150
                $user->email(),
151
                $user->confirmationToken()
152
            )
153
        );
154
155
        return $user;
156
    }
157
158
    /**
159
     * Invites user.
160
     *
161
     * @param UserId    $anId      The id
162
     * @param UserEmail $anEmail   The email
163
     * @param array     $userRoles Array which contains the roles
164
     *
165
     * @return static
166
     */
167 View Code Duplication
    public static function invite(UserId $anId, UserEmail $anEmail, array $userRoles)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
168
    {
169
        $user = new static($anId, $anEmail, $userRoles);
170
        $user->invitationToken = new UserToken();
171
        $user->publish(
172
            new UserInvited(
173
                $user->id(),
174
                $user->email(),
175
                $user->invitationToken()
176
            )
177
        );
178
179
        return $user;
180
    }
181
182
    /**
183
     * Gets the id.
184
     *
185
     * @return UserId
186
     */
187
    public function id()
188
    {
189
        return $this->id;
190
    }
191
192
    /**
193
     * Accepts the invitation request.
194
     *
195
     * @throws UserTokenExpiredException when the token is expired
196
     */
197
    public function acceptInvitation()
198
    {
199
        if ($this->isInvitationTokenAccepted()) {
200
            throw new UserInvitationAlreadyAcceptedException();
201
        }
202
        if ($this->isInvitationTokenExpired()) {
203
            throw new UserTokenExpiredException();
204
        }
205
        $this->invitationToken = null;
206
        $this->updatedOn = new \DateTimeImmutable();
207
        $this->publish(
208
            new UserRegistered(
209
                $this->id(),
210
                $this->email()
211
            )
212
        );
213
    }
214
215
    /**
216
     * Updates the user password.
217
     *
218
     * @param UserPassword $aPassword The old password
219
     *
220
     * @throws UserTokenExpiredException when the token is expired
221
     */
222
    public function changePassword(UserPassword $aPassword)
223
    {
224
        $this->password = $aPassword;
225
        $this->rememberPasswordToken = null;
226
        $this->updatedOn = new \DateTimeImmutable();
227
    }
228
229
    /**
230
     * Gets the confirmation token.
231
     *
232
     * @return UserToken
233
     */
234
    public function confirmationToken()
235
    {
236
        return $this->confirmationToken;
237
    }
238
239
    /**
240
     * Gets the created on.
241
     *
242
     * @return \DateTimeInterface
243
     */
244
    public function createdOn()
245
    {
246
        return $this->createdOn;
247
    }
248
249
    /**
250
     * Gets the email.
251
     *
252
     * @return UserEmail
253
     */
254
    public function email()
255
    {
256
        return $this->email;
257
    }
258
259
    /**
260
     * Enables the user account.
261
     */
262
    public function enableAccount()
263
    {
264
        $this->confirmationToken = null;
265
        $this->updatedOn = new \DateTimeImmutable();
266
267
        $this->publish(
268
            new UserEnabled(
269
                $this->id,
270
                $this->email
271
            )
272
        );
273
    }
274
275
    /**
276
     * Adds the given role.
277
     *
278
     * @param UserRole $aRole The user role
279
     *
280
     * @throws UserRoleInvalidException        when the user is role is invalid
281
     * @throws UserRoleAlreadyGrantedException when the user role is already granted
282
     */
283
    public function grant(UserRole $aRole)
284
    {
285
        if (false === $this->isRoleAllowed($aRole)) {
286
            throw new UserRoleInvalidException();
287
        }
288
        if (true === $this->isGranted($aRole)) {
289
            throw new UserRoleAlreadyGrantedException();
290
        }
291
        $this->roles[] = $aRole;
292
        $this->updatedOn = new \DateTimeImmutable();
293
294
        $this->publish(
295
            new UserRoleGranted(
296
                $this->id,
297
                $this->email,
298
                $aRole
299
            )
300
        );
301
    }
302
303
    /**
304
     * Gets the invitation token.
305
     *
306
     * @return UserToken
307
     */
308
    public function invitationToken()
309
    {
310
        return $this->invitationToken;
311
    }
312
313
    /**
314
     * Checks if the user is enabled or not.
315
     *
316
     * @return bool
317
     */
318
    public function isEnabled()
319
    {
320
        return null === $this->confirmationToken || null === $this->confirmationToken->token();
321
    }
322
323
    /**
324
     * Checks if the user has the given role.
325
     *
326
     * @param UserRole $aRole The user role
327
     *
328
     * @return bool
329
     */
330
    public function isGranted(UserRole $aRole)
331
    {
332
        foreach ($this->roles as $role) {
333
            if ($role->equals($aRole)) {
334
                return true;
335
            }
336
        }
337
338
        return false;
339
    }
340
341
    /**
342
     * Checks if the invitation token is accepted or not.
343
     *
344
     * @return bool
345
     */
346
    public function isInvitationTokenAccepted()
347
    {
348
        return null === $this->invitationToken;
349
    }
350
351
    /**
352
     * Checks if the invitation token is expired or not.
353
     *
354
     * @throws UserTokenNotFoundException when the invitation token does not exist
355
     *
356
     * @return bool
357
     */
358
    public function isInvitationTokenExpired()
359
    {
360
        if (!$this->invitationToken instanceof UserToken) {
361
            throw new UserTokenNotFoundException();
362
        }
363
364
        return $this->invitationToken->isExpired(
365
            $this->invitationTokenLifetime()
366
        );
367
    }
368
369
    /**
370
     * Checks if the remember password token is expired or not.
371
     *
372
     * @throws UserTokenNotFoundException when the remember password token does not exist
373
     *
374
     * @return bool
375
     */
376
    public function isRememberPasswordTokenExpired()
377
    {
378
        if (!$this->rememberPasswordToken instanceof UserToken) {
379
            throw new UserTokenNotFoundException();
380
        }
381
382
        return $this->rememberPasswordToken->isExpired(
383
            $this->rememberPasswordTokenLifetime()
384
        );
385
    }
386
387
    /**
388
     * Checks if the role given appears between allowed roles.
389
     *
390
     * @param UserRole $aRole The user role
391
     *
392
     * @return bool
393
     */
394
    public function isRoleAllowed(UserRole $aRole)
395
    {
396
        return in_array($aRole->role(), static::availableRoles(), true);
397
    }
398
399
    /**
400
     * Gets the last login.
401
     *
402
     * @return \DateTimeInterface
0 ignored issues
show
Documentation introduced by
Should the return type not be \DateTimeInterface|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
403
     */
404
    public function lastLogin()
405
    {
406
        return $this->lastLogin;
407
    }
408
409
    /**
410
     * Validates user login for the given password.
411
     *
412
     * @param string              $aPlainPassword Plain password used to log in
413
     * @param UserPasswordEncoder $anEncoder      The encoder used to encode the password
414
     *
415
     * @throws UserInactiveException        when the user is not enabled
416
     * @throws UserPasswordInvalidException when the user password is invalid
417
     */
418
    public function login($aPlainPassword, UserPasswordEncoder $anEncoder)
419
    {
420
        if (false === $this->isEnabled()) {
421
            throw new UserInactiveException();
422
        }
423
        if (false === $this->password()->equals($aPlainPassword, $anEncoder)) {
424
            throw new UserPasswordInvalidException();
425
        }
426
        $this->lastLogin = new \DateTimeImmutable();
427
428
        $this->publish(
429
            new UserLoggedIn(
430
                $this->id,
431
                $this->email
432
            )
433
        );
434
    }
435
436
    /**
437
     * Updated the user state after logout.
438
     *
439
     * @throws UserInactiveException when the user is not enabled
440
     */
441
    public function logout()
442
    {
443
        if (false === $this->isEnabled()) {
444
            throw new UserInactiveException();
445
        }
446
447
        $this->publish(
448
            new UserLoggedOut(
449
                $this->id,
450
                $this->email
451
            )
452
        );
453
    }
454
455
    /**
456
     * Gets the password.
457
     *
458
     * @return UserPassword
459
     */
460
    public function password()
461
    {
462
        return $this->password;
463
    }
464
465
    /**
466
     * Updates the invitation token in case a user has
467
     * been already invited and has lost the token.
468
     *
469
     * @throws UserInvitationAlreadyAcceptedException in case user has already accepted the invitation
470
     */
471
    public function regenerateInvitationToken()
472
    {
473
        if (null === $this->invitationToken) {
474
            throw new UserInvitationAlreadyAcceptedException();
475
        }
476
        $this->invitationToken = new UserToken();
477
478
        $this->publish(
479
            new UserInvitationTokenRegenerated(
480
                $this->id,
481
                $this->email,
482
                $this->invitationToken
483
            )
484
        );
485
    }
486
487
    /**
488
     * Gets the remember password token.
489
     *
490
     * @return UserToken
491
     */
492
    public function rememberPasswordToken()
493
    {
494
        return $this->rememberPasswordToken;
495
    }
496
497
    /**
498
     * Remembers the password.
499
     */
500
    public function rememberPassword()
501
    {
502
        $this->rememberPasswordToken = new UserToken();
503
504
        $this->publish(
505
            new UserRememberPasswordRequested(
506
                $this->id,
507
                $this->email,
508
                $this->rememberPasswordToken
509
            )
510
        );
511
    }
512
513
    /**
514
     * Removes the given role.
515
     *
516
     * @param UserRole $aRole The user role
517
     *
518
     * @throws UserRoleInvalidException        when the role is invalid
519
     * @throws UserRoleAlreadyRevokedException when the role is already revoked
520
     */
521
    public function revoke(UserRole $aRole)
522
    {
523
        if (false === $this->isRoleAllowed($aRole)) {
524
            throw new UserRoleInvalidException();
525
        }
526
        foreach ($this->roles as $key => $role) {
527
            if ($role->equals($aRole)) {
528
                unset($this->roles[$key]);
529
                $this->roles = array_values($this->roles);
530
                break;
531
            }
532
            throw new UserRoleAlreadyRevokedException();
533
        }
534
        $this->updatedOn = new \DateTimeImmutable();
535
        $this->publish(
536
            new UserRoleRevoked(
537
                $this->id,
538
                $this->email,
539
                $aRole
540
            )
541
        );
542
    }
543
544
    /**
545
     * Gets the roles.
546
     *
547
     * @return UserRole[]
548
     */
549
    public function roles()
550
    {
551
        return $this->roles;
552
    }
553
554
    /**
555
     * Gets the updated on.
556
     *
557
     * @return \DateTimeInterface
558
     */
559
    public function updatedOn()
560
    {
561
        return $this->updatedOn;
562
    }
563
564
    /**
565
     * Gets the available roles in scalar type.
566
     *
567
     * This method is an extension point that it allows
568
     * to add more roles easily in the domain.
569
     *
570
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
571
     */
572
    public static function availableRoles()
573
    {
574
        return ['ROLE_USER', 'ROLE_ADMIN'];
575
    }
576
577
    /**
578
     * Extension point that determines the lifetime
579
     * of the invitation token in seconds.
580
     *
581
     * @return int
582
     */
583
    protected function invitationTokenLifetime()
584
    {
585
        return 604800; // 1 week
586
    }
587
588
    /**
589
     * Extension point that determines the lifetime
590
     * of the remember password token in seconds.
591
     *
592
     * @return int
593
     */
594
    protected function rememberPasswordTokenLifetime()
595
    {
596
        return 3600; // 1 hour
597
    }
598
}
599