Completed
Push — master ( a918e3...c15b8c )
by Beñat
03:06
created

User::acceptInvitation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 11
rs 9.4285
cc 1
eloc 7
nc 1
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\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)
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...
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)
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...
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
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...
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
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...
501
     */
502
    public static function availableRoles()
503
    {
504
        return ['ROLE_USER', 'ROLE_ADMIN'];
505
    }
506
}
507