Completed
Push — master ( f021c8...431426 )
by Hugues
16:56
created

User::isEmailConfirmed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace HMLB\UserBundle\User;
4
5
use DateTime;
6
use HMLB\DDD\Entity\AggregateRoot;
7
use HMLB\DDD\Entity\Identity;
8
use HMLB\DDD\Validation\Assertion;
9
use HMLB\UserBundle\Event\EmailChanged;
10
use HMLB\UserBundle\Event\EmailConfirmed;
11
use HMLB\UserBundle\Event\EmailValidationRequested;
12
use HMLB\UserBundle\Event\PasswordChanged;
13
use HMLB\UserBundle\Event\PasswordReset;
14
use HMLB\UserBundle\Event\PasswordResetRequested;
15
use HMLB\UserBundle\Event\UserRegistered;
16
use HMLB\UserBundle\Exception\EmailAlreadyConfirmedException;
17
use HMLB\UserBundle\Exception\InvalidEmailConfirmationTokenException;
18
use HMLB\UserBundle\Exception\InvalidPasswordResettingTokenException;
19
use HMLB\UserBundle\Exception\PasswordResettingNotRequestedException;
20
use SimpleBus\Message\Recorder\ContainsRecordedMessages;
21
use SimpleBus\Message\Recorder\PrivateMessageRecorderCapabilities;
22
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
23
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
24
25
class User implements AdvancedUserInterface, AggregateRoot, ContainsRecordedMessages
26
{
27
    use PrivateMessageRecorderCapabilities;
28
29
    /**
30
     * @var Identity
31
     */
32
    protected $id;
33
34
    /**
35
     * @var Role[]
36
     */
37
    protected $roles = [];
38
39
    /**
40
     * @var string
41
     */
42
    protected $username;
43
44
    /**
45
     * @var string
46
     */
47
    protected $usernameCanonical;
48
49
    /**
50
     * @var string
51
     */
52
    protected $email;
53
54
    /**
55
     * @var string
56
     */
57
    protected $emailCanonical;
58
59
    /**
60
     * @var bool
61
     */
62
    protected $enabled = false;
63
64
    /**
65
     * The salt to use for hashing.
66
     *
67
     * @var string
68
     */
69
    protected $salt;
70
71
    /**
72
     * Encrypted password. Must be persisted.
73
     *
74
     * @var string
75
     */
76
    protected $password;
77
78
    /**
79
     * @var DateTime
80
     */
81
    protected $lastLogin;
82
83
    /**
84
     * Random string sent to the user email address in order to verify it.
85
     *
86
     * @var string
87
     */
88
    protected $confirmationToken;
89
90
    /**
91
     * Random string sent to the user email address in order to reset the password.
92
     *
93
     * @var string
94
     */
95
    protected $resettingToken;
96
97
    /**
98
     * @var DateTime
99
     */
100
    protected $passwordRequestedAt;
101
102
    /**
103
     * @var DateTime
104
     */
105
    protected $created;
106
107
    /**
108
     * @var DateTime
109
     */
110
    protected $updated;
111
112
    /**
113
     * @var bool
114
     */
115
    protected $locked = false;
116
117
    /**
118
     * @var bool
119
     */
120
    protected $expired = false;
121
122
    /**
123
     * @var DateTime
124
     */
125
    protected $expiresAt;
126
127
    /**
128
     * @var bool
129
     */
130
    protected $credentialsExpired = false;
131
132
    /**
133
     * @var DateTime
134
     */
135
    protected $credentialsExpireAt;
136
137
    /**
138
     * @param string                       $username
139
     * @param string                       $email
140
     * @param string                       $plainPassword
141
     * @param UserPasswordEncoderInterface $encoder
142
     * @param array                        $roles
143
     * @param bool                         $enable
144
     */
145
    protected function __construct(
146
        string $username,
147
        string $email,
148
        string $plainPassword,
149
        UserPasswordEncoderInterface $encoder,
150
        array $roles = [],
151
        bool $enable = true
152
    ) {
153
        Assertion::email($email);
154
        $this->id = new Identity();
155
        $this->roles = $roles;
156
        $this->salt = $this->generateToken();
157
        $this->confirmationToken = $this->generateToken();
158
        $this->created = new DateTime();
159
        $this->username = $username;
160
        $this->email = $email;
161
        if ($enable) {
162
            $this->enable();
163
        }
164
        $this->updateCanonicalFields();
165
        $this->updatePassword($plainPassword, $encoder);
166
    }
167
168
    public static function register(
169
        $username,
170
        $email,
171
        $plainPassword,
172
        UserPasswordEncoderInterface $encoder,
173
        array $roles = ['ROLE_USER']
174
    ): User
175
    {
176
        $user = new static($username, $email, $plainPassword, $encoder, $roles);
177
        $user->recordUserRegistered();
178
        $user->enabled = true;
179
180
        return $user;
181
    }
182
183
    /**
184
     * Getter de id.
185
     *
186
     * @return Identity
187
     */
188
    public function getId(): Identity
189
    {
190
        return is_string($this->id) ? new Identity($this->id) : $this->id;
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function getRoles(): array
197
    {
198
        return $this->roles;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function getPassword(): string
205
    {
206
        return $this->password;
207
    }
208
209
    /**
210
     * Returns the salt that was originally used to encode the password.
211
     *
212
     * This can return null if the password was not encoded using a salt.
213
     *
214
     * @return string|null The salt
215
     */
216
    public function getSalt(): string
217
    {
218
        return $this->salt;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224
    public function getUsername(): string
225
    {
226
        return $this->username;
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function eraseCredentials()
233
    {
234
        //No-op
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function isAccountNonExpired(): bool
241
    {
242
        if (true === $this->expired) {
243
            return false;
244
        }
245
246
        if (null !== $this->expiresAt && $this->expiresAt->getTimestamp() < time()) {
247
            return false;
248
        }
249
250
        return true;
251
    }
252
253
    /**
254
     * {@inheritdoc}
255
     */
256
    public function isAccountNonLocked(): bool
257
    {
258
        return !$this->locked;
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     */
264
    public function isCredentialsNonExpired(): bool
265
    {
266
        if (true === $this->credentialsExpired) {
267
            return false;
268
        }
269
270
        if (null !== $this->credentialsExpireAt && $this->credentialsExpireAt->getTimestamp() < time()) {
271
            return false;
272
        }
273
274
        return true;
275
    }
276
277
    /**
278
     * Change the email address of the user.
279
     *
280
     * @param string $email
281
     */
282
    public function changeEmail(string $email)
283
    {
284
        Assertion::email($email);
285
        $oldEmail = $this->email;
286
        $this->email = $email;
287
        $this->emailCanonical = self::canonicalize($this->email);
288
        $this->updated = new DateTime();
289
        $this->record(new EmailChanged($this->getId(), $oldEmail, $this->email));
290
    }
291
292
    /**
293
     * @param string                       $password
294
     * @param UserPasswordEncoderInterface $encoder
295
     */
296
    public function changePassword(string $password, UserPasswordEncoderInterface $encoder)
297
    {
298
        $oldPassword = $this->password;
299
        $this->updatePassword($password, $encoder);
300
        $this->updated = new DateTime();
301
        $this->record(new PasswordChanged($this->id, $oldPassword, $password));
302
    }
303
304
    /**
305
     * {@inheritdoc}
306
     */
307
    public function isEnabled(): bool
308
    {
309
        return $this->enabled;
310
    }
311
312
    /**
313
     * Enable
314
     */
315
    public function enable()
316
    {
317
        $this->enabled = true;
318
    }
319
320
    /**
321
     * Disable
322
     */
323
    public function disable()
324
    {
325
        $this->enabled = false;
326
    }
327
328
    /**
329
     *
330
     * @return bool
331
     *
332
     */
333
    public function isEmailConfirmed(): bool
334
    {
335
        return null === $this->confirmationToken;
336
    }
337
338
    public function requestEmailConfirmation()
339
    {
340
        $this->confirmationToken = $this->generateToken();
341
        $this->updated = new DateTime();
342
        $this->record(new EmailValidationRequested($this->id, $this->confirmationToken));
343
    }
344
345
    public function confirmEmail(string $confirmationToken)
346
    {
347
        if ($this->isEmailConfirmed()) {
348
            throw new EmailAlreadyConfirmedException();
349
        }
350
351
        if ($confirmationToken !== $this->confirmationToken) {
352
            throw new InvalidEmailConfirmationTokenException();
353
        }
354
355
        $this->confirmationToken = null;
356
        $this->updated = new DateTime();
357
        $this->record(new EmailConfirmed($this->id, $this->email));
358
    }
359
360
    public function isPasswordResetRequested(): bool
361
    {
362
        return null !== $this->resettingToken;
363
    }
364
365
    public function requestPasswordReset()
366
    {
367
        $this->resettingToken = $this->generateToken();
368
        $this->passwordRequestedAt = new DateTime();
369
        $this->updated = new DateTime();
370
        $this->record(new PasswordResetRequested($this->id, $this->resettingToken));
371
    }
372
373
    public function resetPassword(string $resettingToken, string $password, UserPasswordEncoderInterface $encoder)
374
    {
375
        if (!$this->isPasswordResetRequested()) {
376
            throw new PasswordResettingNotRequestedException();
377
        }
378
379
        if ($resettingToken !== $this->resettingToken) {
380
            throw new InvalidPasswordResettingTokenException();
381
        }
382
383
        $this->resettingToken = null;
384
385
        $oldPassword = $this->password;
386
        $this->updatePassword($password, $encoder);
387
        $this->updated = new DateTime();
388
        $this->record(new PasswordReset($this->id, $oldPassword, $password));
389
390
        //We validate email if password has been confirmed because the token has been sent to the email adress.
391
        if (!$this->isEmailConfirmed()) {
392
            $this->confirmEmail($this->confirmationToken);
393
        }
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
399
    private function updateCanonicalFields()
400
    {
401
        $this->usernameCanonical = self::canonicalize($this->username);
402
        $this->emailCanonical = self::canonicalize($this->email);
403
    }
404
405
    /**
406
     * @param string                       $plainPassword
407
     * @param UserPasswordEncoderInterface $encoder
408
     */
409
    private function updatePassword(string $plainPassword, UserPasswordEncoderInterface $encoder)
410
    {
411
        $this->password = $encoder->encodePassword($this, $plainPassword);
412
    }
413
414
    /**
415
     * Generate token string for salt, email validation or password resetting.
416
     * @return string
417
     *
418
     */
419
    private function generateToken(): string
420
    {
421
        return base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
422
    }
423
424
    protected function recordUserRegistered()
425
    {
426
        $this->record(new UserRegistered($this->getId(), $this->getEmail(), $this->getUsername()));
427
    }
428
429
    /**
430
     * @param $string
431
     *
432
     * @return string
433
     */
434
    public static function canonicalize(string $string): string
435
    {
436
        $trimmed = trim($string);
437
438
        return mb_convert_case($trimmed, MB_CASE_LOWER, mb_detect_encoding($string));
439
    }
440
441
    /**
442
     * @return string
443
     */
444
    public function getEmail(): string
445
    {
446
        return $this->email;
447
    }
448
449
    /**
450
     * Getter de lastLogin
451
     *
452
     * @return DateTime
453
     */
454
    public function getLastLogin()
455
    {
456
        return $this->lastLogin;
457
    }
458
459
    /**
460
     * Getter de confirmationToken
461
     *
462
     * @return string
463
     */
464
    public function getConfirmationToken()
465
    {
466
        return $this->confirmationToken;
467
    }
468
469
    /**
470
     * Getter de resettingToken
471
     *
472
     * @return string
473
     */
474
    public function getResettingToken()
475
    {
476
        return $this->resettingToken;
477
    }
478
479
    /**
480
     * Getter de created
481
     *
482
     * @return DateTime
483
     */
484
    public function getCreated()
485
    {
486
        return $this->created;
487
    }
488
489
    /**
490
     * Getter de updated
491
     *
492
     * @return DateTime
493
     */
494
    public function getUpdated()
495
    {
496
        return $this->updated;
497
    }
498
}
499