Passed
Push — develop ( 3b27a1...7bbf61 )
by Baptiste
02:37
created

Identity::enableTwoFactorAuthentication()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 18
nc 1
nop 0
dl 0
loc 23
ccs 19
cts 19
cp 1
crap 1
rs 9.0856
c 0
b 0
f 0
1
<?php
2
declare(strict_types = 1);
3
4
namespace PersonalGalaxy\Identity\Entity;
5
6
use PersonalGalaxy\Identity\{
7
    Entity\Identity\Email,
8
    Entity\Identity\Password,
9
    Entity\Identity\SecretKey,
10
    Entity\Identity\RecoveryCode,
11
    Event\IdentityWasCreated,
12
    Event\Identity\PasswordWasChanged,
13
    Event\Identity\TwoFactorAuthenticationWasEnabled,
14
    Event\Identity\TwoFactorAuthenticationWasDisabled,
15
    Event\Identity\RecoveryCodeWasUsed,
16
    Event\IdentityWasDeleted,
17
    TwoFactorAuthentication\Code,
18
    Exception\LogicException,
19
};
20
use Innmind\EventBus\{
21
    ContainsRecordedEventsInterface,
22
    EventRecorder,
23
};
24
use Innmind\Immutable\Set;
25
use ParagonIE\MultiFactor\{
26
    OTP\OTPInterface,
27
    FIDOU2F,
28
};
29
30
final class Identity implements ContainsRecordedEventsInterface
31
{
32
    use EventRecorder;
33
34
    private $email;
35
    private $password;
36
    private $secretKey;
37
38 13
    private function __construct(Email $email, Password $password)
39
    {
40 13
        $this->email = $email;
41 13
        $this->password = $password;
42 13
    }
43
44 13
    public static function create(Email $email, Password $password): self
45
    {
46 13
        $self = new self($email, $password);
47 13
        $self->record(new IdentityWasCreated($email));
48
49 13
        return $self;
50
    }
51
52 5
    public function email(): Email
53
    {
54 5
        return $this->email;
55
    }
56
57 2
    public function changePassword(Password $password): self
58
    {
59 2
        $this->password = $password;
60 2
        $this->record(new PasswordWasChanged($this->email));
61
62 2
        return $this;
63
    }
64
65 4
    public function verify(string $password): bool
66
    {
67 4
        return $this->password->verify($password);
68
    }
69
70 6
    public function twoFactorAuthenticationEnabled(): bool
71
    {
72 6
        return $this->secretKey instanceof SecretKey;
73
    }
74
75 6
    public function enableTwoFactorAuthentication(): self
76
    {
77 6
        $this->secretKey = new SecretKey;
78 6
        $this->recoveryCodes = Set::of(
0 ignored issues
show
Bug Best Practice introduced by
The property recoveryCodes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
79 6
            RecoveryCode::class,
80 6
            new RecoveryCode,
81 6
            new RecoveryCode,
82 6
            new RecoveryCode,
83 6
            new RecoveryCode,
84 6
            new RecoveryCode,
85 6
            new RecoveryCode,
86 6
            new RecoveryCode,
87 6
            new RecoveryCode,
88 6
            new RecoveryCode,
89 6
            new RecoveryCode
90
        );
91 6
        $this->record(new TwoFactorAuthenticationWasEnabled(
92 6
            $this->email,
93 6
            $this->secretKey,
94 6
            $this->recoveryCodes
95
        ));
96
97 6
        return $this;
98
    }
99
100 2
    public function disableTwoFactorAuthentication(): self
101
    {
102 2
        $this->secretKey = null;
103 2
        $this->recoveryCodes = null;
0 ignored issues
show
Bug Best Practice introduced by
The property recoveryCodes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
104 2
        $this->record(new TwoFactorAuthenticationWasDisabled($this->email));
105
106 2
        return $this;
107
    }
108
109 4
    public function validate(Code $code, OTPInterface $otp = null): bool
110
    {
111 4
        if (!$this->twoFactorAuthenticationEnabled()) {
112 1
            throw new LogicException;
113
        }
114
115 4
        $factor = new FIDOU2F((string) $this->secretKey, $otp);
116
117 4
        if ($factor->validateCode((string) $code)) {
118 1
            return true;
119
        }
120
121
        $codes = $this
122 4
            ->recoveryCodes
123 4
            ->filter(static function(RecoveryCode $recoveryCode) use ($code): bool {
124 4
                return $recoveryCode->equals($code);
125 4
            });
126
127 4
        if ($codes->size() === 0) {
128 3
            return false;
129
        }
130
131 2
        $this->recoveryCodes = $this->recoveryCodes->remove($codes->current());
0 ignored issues
show
Bug Best Practice introduced by
The property recoveryCodes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
132 2
        $this->record(new RecoveryCodeWasUsed($this->email));
133
134 2
        return true;
135
    }
136
137 2
    public function delete(): void
138
    {
139 2
        $this->record(new IdentityWasDeleted($this->email));
140 2
    }
141
}
142