Passed
Push — master ( d3cc39...f2a84f )
by Melech
03:59
created

AbstractAuthenticator   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
eloc 34
dl 0
loc 122
rs 10
c 1
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A setAuthenticatedUsers() 0 5 1
A authenticate() 0 17 3
A getImpersonated() 0 11 2
A __construct() 0 6 1
A getAuthenticatedUsers() 0 3 1
A getAuthenticated() 0 11 2
A unauthenticate() 0 13 3
A isAuthenticated() 0 4 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Auth;
15
16
use Valkyrja\Auth\Contract\Authenticator as Contract;
17
use Valkyrja\Auth\Data\Attempt\Contract\AuthenticationAttempt;
18
use Valkyrja\Auth\Data\Contract\AuthenticatedUsers;
19
use Valkyrja\Auth\Data\Retrieval\RetrievalById;
20
use Valkyrja\Auth\Entity\Contract\User;
21
use Valkyrja\Auth\Exception\InvalidAuthenticationException;
22
use Valkyrja\Auth\Hasher\Contract\PasswordHasher;
23
use Valkyrja\Auth\Store\Contract\Store;
24
25
/**
26
 * Abstract Class AbstractAuthenticator.
27
 *
28
 * @author Melech Mizrachi
29
 *
30
 * @template U of User
31
 *
32
 * @implements Contract<U>
33
 */
34
abstract class AbstractAuthenticator implements Contract
35
{
36
    /** @var U|null */
0 ignored issues
show
Bug introduced by
The type Valkyrja\Auth\U was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
    protected User|null $current = null;
38
    /** @var U|null */
39
    protected User|null $impersonated = null;
40
41
    /**
42
     * @param Store<U>        $store  The store
43
     * @param class-string<U> $entity The user entity
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<U> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<U>.
Loading history...
44
     */
45
    public function __construct(
46
        protected Store $store,
47
        protected PasswordHasher $hasher,
48
        protected string $entity,
49
        protected AuthenticatedUsers $authenticatedUsers = new Data\AuthenticatedUsers(),
50
    ) {
51
    }
52
53
    /**
54
     * @inheritDoc
55
     */
56
    public function isAuthenticated(): bool
57
    {
58
        return $this->authenticatedUsers->hasCurrent()
59
            || $this->authenticatedUsers->isImpersonating();
60
    }
61
62
    /**
63
     * Get the current authenticated user if one exists.
64
     *
65
     * @return U|null
66
     */
67
    public function getAuthenticated(): User|null
68
    {
69
        $id = $this->authenticatedUsers->getCurrent();
70
71
        if ($id === null) {
72
            return null;
73
        }
74
75
        return $this->current ??= $this->store->retrieve(
76
            retrieval: new RetrievalById($id),
77
            user: $this->entity
78
        );
79
    }
80
81
    /**
82
     * Get the current impersonated user if one exists.
83
     *
84
     * @return U|null
85
     */
86
    public function getImpersonated(): User|null
87
    {
88
        $id = $this->authenticatedUsers->getImpersonated();
89
90
        if ($id === null) {
91
            return null;
92
        }
93
94
        return $this->impersonated ??= $this->store->retrieve(
95
            retrieval: new RetrievalById($id),
96
            user: $this->entity
97
        );
98
    }
99
100
    /**
101
     * @inheritDoc
102
     */
103
    public function getAuthenticatedUsers(): AuthenticatedUsers
104
    {
105
        return $this->authenticatedUsers;
106
    }
107
108
    /**
109
     * @inheritDoc
110
     */
111
    public function setAuthenticatedUsers(AuthenticatedUsers $authenticatedUsers): static
112
    {
113
        $this->authenticatedUsers = $authenticatedUsers;
114
115
        return $this;
116
    }
117
118
    /**
119
     * @inheritDoc
120
     */
121
    public function authenticate(AuthenticationAttempt $attempt): User
122
    {
123
        $user = $this->store->retrieve($attempt->getRetrieval(), $this->entity);
124
125
        if ($user === null) {
126
            throw new InvalidAuthenticationException('User not found');
127
        }
128
129
        if (! $this->hasher->confirmPassword($attempt->getPassword(), $user->getPasswordValue())) {
130
            throw new InvalidAuthenticationException('Incorrect password');
131
        }
132
133
        $this->authenticatedUsers->setCurrent($user->getIdValue());
134
135
        $this->current = $user;
0 ignored issues
show
Documentation Bug introduced by
It seems like $user of type Valkyrja\Auth\Entity\Contract\User is incompatible with the declared type Valkyrja\Auth\U|null of property $current.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
136
137
        return $user;
138
    }
139
140
    /**
141
     * @inheritDoc
142
     */
143
    public function unauthenticate(int|string $id): static
144
    {
145
        $this->authenticatedUsers->remove($id);
146
147
        if ($this->current?->getIdValue() === $id) {
148
            $this->current = null;
149
        }
150
151
        if ($this->impersonated?->getIdValue() === $id) {
152
            $this->impersonated = null;
153
        }
154
155
        return $this;
156
    }
157
}
158