Passed
Push — master ( 85d65b...ff1279 )
by Jan
13:27
created

User::isTFAEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/*
3
 * Copyright (C) 2020  Jan Böhmer
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU Affero General Public License as published
7
 * by the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU Affero General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Affero General Public License
16
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
 */
18
19
namespace App\Entity;
20
21
use App\Entity\Contracts\DBElementInterface;
22
use App\Repository\UserRepository;
23
use App\Validator\NoLockout;
24
use DateTime;
25
use Doctrine\ORM\Mapping as ORM;
26
use Scheb\TwoFactorBundle\Model\BackupCodeInterface;
27
use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface;
28
use Scheb\TwoFactorBundle\Model\TrustedDeviceInterface;
29
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
30
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
31
use Symfony\Component\Security\Core\User\UserInterface;
32
use Symfony\Component\Validator\Constraints as Assert;
33
34
/**
35
 * This entity describes a user that can login to the backend system (it needs an ROLE_ADMIN role however).
36
 * The login is done with the username and a user choosable password. It is possible to configure two factor authentication
37
 * methods for additional security.
38
 *
39
 * @ORM\Entity(repositoryClass=UserRepository::class)
40
 * @UniqueEntity(fields={"username"})
41
 * @NoLockout(groups={"perm_edit"})
42
 */
43
class User implements DBElementInterface, UserInterface, TwoFactorInterface, BackupCodeInterface, TrustedDeviceInterface, PasswordAuthenticatedUserInterface
44
{
45
    /**
46
     * @ORM\Id()
47
     * @ORM\GeneratedValue()
48
     * @ORM\Column(type="integer")
49
     */
50
    private $id;
51
52
    /**
53
     * @ORM\Column(type="string", length=180, unique=true)
54
     */
55
    private $username;
56
57
    /**
58
     * @var string
59
     * @ORM\Column(type="string")
60
     */
61
    private $role_description = '';
62
63
    /**
64
     * @var string
65
     * @Assert\Email()
66
     * @ORM\Column(type="string")
67
     */
68
    private $email = '';
69
70
    /**
71
     * @ORM\Column(type="json")
72
     */
73
    private $roles = ['ROLE_ADMIN'];
74
75
    /**
76
     * @ORM\Column(type="string")
77
     */
78
    private $first_name = '';
79
80
    /**
81
     * @ORM\Column(type="string")
82
     */
83
    private $last_name = '';
84
85
    /**
86
     * @var string The hashed password
87
     * @ORM\Column(type="string")
88
     */
89
    private $password;
90
91
    /**
92
     * @var string|null
93
     * @Assert\Length(min=6)
94
     */
95
    private $plain_password = null;
96
97
    /**
98
     * @ORM\Column(name="googleAuthenticatorSecret", type="string", nullable=true)
99
     */
100
    private $googleAuthenticatorSecret;
101
102
    /**
103
     * @ORM\Column(type="integer")
104
     */
105
    private $trustedVersion = 0;
106
107
    /**
108
     * @ORM\Column(type="json")
109
     */
110
    private $backupCodes = [];
111
112
    /**
113
     * @ORM\Column(type="datetime", nullable=true)
114
     */
115
    private $backupCodesDate;
116
117
    public function getId(): ?int
118
    {
119
        return $this->id;
120
    }
121
122
    /**
123
     * The name that is used to internally identify this user. Also used as login name.
124
     * Must be unique for all users.
125
     */
126
    public function getUsername(): string
127
    {
128
        return (string) $this->username;
129
    }
130
131
    /**
132
     * Returns unique user identifier (the username)
133
     * @return string
134
     */
135
    public function getUserIdentifier(): string
136
    {
137
        return (string) $this->username;
138
    }
139
140
    /**
141
     * Sets the username for this user.
142
     *
143
     * @return $this
144
     */
145
    public function setUsername(string $username): self
146
    {
147
        $this->username = $username;
148
149
        return $this;
150
    }
151
152
    /**
153
     * Returns all roles for this user.
154
     * Every user has at least the ROLE_USER role.
155
     *
156
     * @return string[]
157
     *
158
     * @see UserInterface
159
     */
160
    public function getRoles(): array
161
    {
162
        $roles = $this->roles;
163
        // guarantee every user at least has ROLE_USER
164
        $roles[] = 'ROLE_USER';
165
166
        return array_unique($roles);
167
    }
168
169
    /**
170
     * Add the given role to this user.
171
     *
172
     * @return $this
173
     */
174
    public function addRole(string $new_role): self
175
    {
176
        $this->roles[] = $new_role;
177
        $this->roles = array_unique($this->roles);
178
179
        return $this;
180
    }
181
182
    /**
183
     * Sets all roles for this user.
184
     *
185
     * @param string[] $roles
186
     *
187
     * @return $this
188
     */
189
    public function setRoles(array $roles): self
190
    {
191
        $this->roles = $roles;
192
193
        return $this;
194
    }
195
196
    /**
197
     * Returns the (hashed) password for this user.
198
     *
199
     * @see UserInterface
200
     */
201
    public function getPassword(): string
202
    {
203
        return $this->password;
204
    }
205
206
    /**
207
     * Sets the (hashed) password for this user.
208
     * Should be generated with UserPasswordEncryptorInterface.
209
     *
210
     * @return $this
211
     */
212
    public function setPassword(string $password): self
213
    {
214
        $this->password = $password;
215
216
        return $this;
217
    }
218
219
    /**
220
     * Not used.
221
     *
222
     * @see UserInterface
223
     */
224
    public function getSalt(): ?string
225
    {
226
        // not needed when using the "bcrypt" algorithm in security.yaml
227
        return null;
228
    }
229
230
    /**
231
     * @see UserInterface
232
     */
233
    public function eraseCredentials()
234
    {
235
        // If you store any temporary, sensitive data on the user, clear it here
236
        $this->plain_password = null;
237
    }
238
239
    /**
240
     * Returns the description of what this user does or why he needs an account (the function of the user).
241
     */
242
    public function getRoleDescription(): string
243
    {
244
        return $this->role_description;
245
    }
246
247
    /**
248
     * Sets the description of what this user does or why he needs an account (the function of the user).
249
     */
250
    public function setRoleDescription(string $role_description): User
251
    {
252
        $this->role_description = $role_description;
253
254
        return $this;
255
    }
256
257
    /**
258
     * Returns the email of this user.
259
     */
260
    public function getEmail(): string
261
    {
262
        return $this->email;
263
    }
264
265
    /**
266
     * Sets the email of this user.
267
     */
268
    public function setEmail(string $email): User
269
    {
270
        $this->email = $email;
271
272
        return $this;
273
    }
274
275
    /**
276
     * Return the first name of this user.
277
     */
278
    public function getFirstName(): ?string
279
    {
280
        return $this->first_name;
281
    }
282
283
    /**
284
     * Sets the first name of this user.
285
     */
286
    public function setFirstName(string $first_name): User
287
    {
288
        $this->first_name = $first_name;
289
290
        return $this;
291
    }
292
293
    /**
294
     * Returns the last name of this user.
295
     */
296
    public function getLastName(): ?string
297
    {
298
        return $this->last_name;
299
    }
300
301
    /**
302
     * Sets the last name of this user.
303
     */
304
    public function setLastName(string $last_name): User
305
    {
306
        $this->last_name = $last_name;
307
308
        return $this;
309
    }
310
311
    /**
312
     * Returns the full name of this user (in the format "first_name last_name").
313
     */
314
    public function getFullName(): string
315
    {
316
        if (empty($this->getFirstName())) {
317
            return $this->getLastName();
318
        }
319
        if (empty($this->getLastName())) {
320
            return $this->getFirstName();
321
        }
322
323
        return $this->getFirstName().' '.$this->getLastName();
324
    }
325
326
    /**
327
     * Returns the temporary saved plain password. We need this to set a password via EasyAdmin interface.
328
     * A value is only available shortly after it was set via setPlainPassword() and is deleted by eraseCredentials().
329
     *
330
     * @return string
331
     */
332
    public function getPlainPassword(): ?string
333
    {
334
        return $this->plain_password;
335
    }
336
337
    /**
338
     * Sets the temporary saved plain password. We need this to set a password via EasyAdmin interface.
339
     * The value is deleted by eraseCredentials().
340
     *
341
     * @param string $plainPassword
342
     */
343
    public function setPlainPassword(?string $plainPassword): User
344
    {
345
        $this->plain_password = $plainPassword;
346
347
        return $this;
348
    }
349
350
    /**
351
     * Returns true if this user has any Two-Factor method enabled.
352
     */
353
    public function isTFAEnabled(): bool
354
    {
355
        return $this->isGoogleAuthenticatorEnabled();
356
    }
357
358
    /**
359
     * Returns true if this user has google authentication 2FA enabled.
360
     */
361
    public function isGoogleAuthenticatorEnabled(): bool
362
    {
363
        return $this->googleAuthenticatorSecret ? true : false;
364
    }
365
366
    /**
367
     * Returns the username that should be shown to the user for this service, when using google authenticator.
368
     * Here the standard username is used.
369
     */
370
    public function getGoogleAuthenticatorUsername(): string
371
    {
372
        return $this->username;
373
    }
374
375
    /**
376
     * Returns the secret used for google authenticator 2FA.
377
     */
378
    public function getGoogleAuthenticatorSecret(): ?string
379
    {
380
        return $this->googleAuthenticatorSecret;
381
    }
382
383
    /**
384
     * Sets the secred used for google authenticator 2FA.
385
     *
386
     * @return $this
387
     */
388
    public function setGoogleAuthenticatorSecret(?string $googleAuthenticatorSecret): self
389
    {
390
        $this->googleAuthenticatorSecret = $googleAuthenticatorSecret;
391
392
        return $this;
393
    }
394
395
    /**
396
     * Returns the trusted token version used to implement trusted device 2FA.
397
     */
398
    public function getTrustedTokenVersion(): int
399
    {
400
        return $this->trustedVersion;
401
    }
402
403
    /**
404
     * Invalidate all trusted devices used by this user.
405
     *
406
     * @return $this
407
     */
408
    public function invalidateTrustedDevices(): self
409
    {
410
        ++$this->trustedVersion;
411
412
        return $this;
413
    }
414
415
    /**
416
     * Check if it is a valid backup code.
417
     */
418
    public function isBackupCode(string $code): bool
419
    {
420
        //Don't check if no backup codes are defined.
421
        if (empty($this->backupCodes)) {
422
            return false;
423
        }
424
425
        return in_array($code, $this->backupCodes, true);
426
    }
427
428
    /**
429
     * Invalidate a backup code.
430
     */
431
    public function invalidateBackupCode(string $code): void
432
    {
433
        $key = array_search($code, $this->backupCodes, true);
434
        if (false !== $key) {
435
            unset($this->backupCodes[$key]);
436
        }
437
    }
438
439
    /**
440
     * Set all backup codes of this user. BackupCodeDate will be updated.
441
     *
442
     * @return $this
443
     */
444
    public function setBackupCodes(array $codes): self
445
    {
446
        $this->backupCodes = $codes;
447
        $this->backupCodesDate = new DateTime();
448
449
        return $this;
450
    }
451
452
    /**
453
     * Returns the date when the backup codes where generated.
454
     *
455
     * @return DateTime
456
     */
457
    public function getBackupCodesDate(): ?DateTime
458
    {
459
        return $this->backupCodesDate;
460
    }
461
462
    /**
463
     * Returns all backup codes of this user.
464
     */
465
    public function getBackupCodes(): array
466
    {
467
        return $this->backupCodes ?? [];
468
    }
469
470
    public function __toString(): string
471
    {
472
        return $this->getFullName().' ('.$this->username.')';
473
    }
474
}
475