Passed
Push — develop ( e2cf42...37d2d6 )
by Baptiste
03:06
created

Identity   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 57
dl 0
loc 124
ccs 61
cts 61
cp 1
rs 10
c 0
b 0
f 0
wmc 14

11 Methods

Rating   Name   Duplication   Size   Complexity  
A enableTwoFactorAuthentication() 0 23 1
A delete() 0 3 1
A identity() 0 3 1
A email() 0 3 1
A disableTwoFactorAuthentication() 0 7 1
A changePassword() 0 6 1
A create() 0 9 1
A verify() 0 3 1
A validate() 0 26 4
A twoFactorAuthenticationEnabled() 0 3 1
A __construct() 0 8 1
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 $identity;
35
    private $email;
36
    private $password;
37
    private $secretKey;
38
    private $recoveryCodes;
39
40 15
    private function __construct(
41
        Identity\Identity $identity,
42
        Email $email,
43
        Password $password
44
    ) {
45 15
        $this->identity = $identity;
46 15
        $this->email = $email;
47 15
        $this->password = $password;
48 15
    }
49
50 15
    public static function create(
51
        Identity\Identity $identity,
52
        Email $email,
53
        Password $password
54
    ): self {
55 15
        $self = new self($identity, $email, $password);
56 15
        $self->record(new IdentityWasCreated($identity, $email));
57
58 15
        return $self;
59
    }
60
61 5
    public function identity(): Identity\Identity
62
    {
63 5
        return $this->identity;
64
    }
65
66 2
    public function email(): Email
67
    {
68 2
        return $this->email;
69
    }
70
71 2
    public function changePassword(Password $password): self
72
    {
73 2
        $this->password = $password;
74 2
        $this->record(new PasswordWasChanged($this->identity));
75
76 2
        return $this;
77
    }
78
79 6
    public function verify(string $password): bool
80
    {
81 6
        return $this->password->verify($password);
82
    }
83
84 6
    public function twoFactorAuthenticationEnabled(): bool
85
    {
86 6
        return $this->secretKey instanceof SecretKey;
87
    }
88
89 6
    public function enableTwoFactorAuthentication(): self
90
    {
91 6
        $this->secretKey = new SecretKey;
92 6
        $this->recoveryCodes = Set::of(
93 6
            RecoveryCode::class,
94 6
            new RecoveryCode,
95 6
            new RecoveryCode,
96 6
            new RecoveryCode,
97 6
            new RecoveryCode,
98 6
            new RecoveryCode,
99 6
            new RecoveryCode,
100 6
            new RecoveryCode,
101 6
            new RecoveryCode,
102 6
            new RecoveryCode,
103 6
            new RecoveryCode
104
        );
105 6
        $this->record(new TwoFactorAuthenticationWasEnabled(
106 6
            $this->identity,
107 6
            $this->secretKey,
108 6
            $this->recoveryCodes
109
        ));
110
111 6
        return $this;
112
    }
113
114 2
    public function disableTwoFactorAuthentication(): self
115
    {
116 2
        $this->secretKey = null;
117 2
        $this->recoveryCodes = null;
118 2
        $this->record(new TwoFactorAuthenticationWasDisabled($this->identity));
119
120 2
        return $this;
121
    }
122
123 4
    public function validate(Code $code, OTPInterface $otp = null): bool
124
    {
125 4
        if (!$this->twoFactorAuthenticationEnabled()) {
126 1
            throw new LogicException;
127
        }
128
129 4
        $factor = new FIDOU2F((string) $this->secretKey, $otp);
130
131 4
        if ($factor->validateCode((string) $code)) {
132 1
            return true;
133
        }
134
135
        $codes = $this
136 4
            ->recoveryCodes
137
            ->filter(static function(RecoveryCode $recoveryCode) use ($code): bool {
138 4
                return $recoveryCode->equals($code);
139 4
            });
140
141 4
        if ($codes->size() === 0) {
142 3
            return false;
143
        }
144
145 2
        $this->recoveryCodes = $this->recoveryCodes->remove($codes->current());
146 2
        $this->record(new RecoveryCodeWasUsed($this->identity));
147
148 2
        return true;
149
    }
150
151 2
    public function delete(): void
152
    {
153 2
        $this->record(new IdentityWasDeleted($this->identity));
154 2
    }
155
}
156