AuthenticationService   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 28
eloc 58
dl 0
loc 244
ccs 66
cts 66
cp 1
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A persistIdentity() 0 18 5
A authenticate() 0 30 5
A buildIdentity() 0 3 1
A clearIdentity() 0 14 3
A authenticators() 0 3 1
A checkAuthenticators() 0 5 2
A getResult() 0 3 1
A resetInternalState() 0 6 1
A getIdentity() 0 16 6
A getFailures() 0 3 1
A getSuccessfulAuthenticator() 0 3 1
A __construct() 0 6 1
1
<?php
2
3
/**
4
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
5
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
6
 *
7
 * Licensed under The MIT License
8
 * For full copyright and license information, please see the LICENSE.txt
9
 * Redistributions of files must retain the above copyright notice.
10
 *
11
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
12
 * @link          https://cakephp.org CakePHP(tm) Project
13
 * @since         1.0.0
14
 * @license       https://opensource.org/licenses/mit-license.php MIT License
15
 */
16
17
declare(strict_types=1);
18
19
namespace Phauthentic\Authentication;
20
21
use ArrayAccess;
22
use Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface;
23
use Phauthentic\Authentication\Authenticator\AuthenticatorInterface;
24
use Phauthentic\Authentication\Authenticator\Failure;
25
use Phauthentic\Authentication\Authenticator\PersistenceInterface;
26
use Phauthentic\Authentication\Authenticator\ResultInterface;
27
use Phauthentic\Authentication\Authenticator\StatelessInterface;
28
use Phauthentic\Authentication\Identity\IdentityFactoryInterface;
29
use Phauthentic\Authentication\Identity\IdentityInterface;
30
use Psr\Http\Message\ResponseInterface;
31
use Psr\Http\Message\ServerRequestInterface;
32
use RuntimeException;
33
34
/**
35
 * Authentication Service
36
 */
37
class AuthenticationService implements AuthenticationServiceInterface
38
{
39
    /**
40
     * Authenticator collection
41
     *
42
     * @var \Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface<\Phauthentic\Authentication\Authenticator\AuthenticatorInterface>
43
     */
44
    protected AuthenticatorCollectionInterface $authenticators;
45
46
    /**
47
     * Authenticator that successfully authenticated the identity.
48
     *
49
     * @var \Phauthentic\Authentication\Authenticator\AuthenticatorInterface|null
50
     */
51
    protected ?AuthenticatorInterface $successfulAuthenticator;
52
53
    /**
54
     * A list of failed authenticators after an authentication attempt
55
     *
56
     * @var \Phauthentic\Authentication\Authenticator\FailureInterface[]
57
     */
58
    protected array $failures = [];
59
60
    /**
61
     * Identity object.
62
     *
63
     * @var \Phauthentic\Authentication\Identity\IdentityInterface|null
64
     */
65
    protected ?IdentityInterface $identity;
66
67
    /**
68
     * Result of the last authenticate() call.
69
     *
70
     * @var \Phauthentic\Authentication\Authenticator\ResultInterface|null
71
     */
72
    protected ?ResultInterface $result;
73
74
    /**
75
     * Identity factory used to instantiate an identity object
76
     *
77
     * @var \Phauthentic\Authentication\Identity\IdentityFactoryInterface
78
     */
79
    protected IdentityFactoryInterface $identityFactory;
80
81
    /**
82
     * Constructor
83
     *
84 40
     * @param \Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface<\Phauthentic\Authentication\Authenticator\AuthenticatorInterface> $authenticators Authenticator collection.
85
     * @param \Phauthentic\Authentication\Identity\IdentityFactoryInterface $factory Identity factory.
86
     */
87
    public function __construct(
88 40
        AuthenticatorCollectionInterface $authenticators,
89 40
        IdentityFactoryInterface $factory
90 40
    ) {
91
        $this->authenticators = $authenticators;
92
        $this->identityFactory = $factory;
93
    }
94
95
    /**
96
     * Access the authenticator collection
97 36
     *
98
     * @return \Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface<\Phauthentic\Authentication\Authenticator\AuthenticatorInterface>
99 36
     */
100
    public function authenticators(): AuthenticatorCollectionInterface
101
    {
102
        return $this->authenticators;
103
    }
104
105
    /**
106
     * Checks if at least one authenticator is in the collection
107
     *
108 28
     * @throws \RuntimeException
109
     * @return void
110 28
     */
111 4
    protected function checkAuthenticators(): void
112 4
    {
113
        if ($this->authenticators()->isEmpty()) {
114
            throw new RuntimeException(
115 24
                'No authenticators loaded. You need to load at least one authenticator.'
116
            );
117
        }
118
    }
119
120
    /**
121
     * {@inheritDoc}
122 28
     *
123
     * @throws \RuntimeException Throws a runtime exception when no authenticators are loaded.
124 28
     */
125 24
    public function authenticate(ServerRequestInterface $request): bool
126 24
    {
127 24
        $this->checkAuthenticators();
128
        $this->identity = null;
129 24
        $this->successfulAuthenticator = null;
130 24
        $this->failures = [];
131
132 24
        $result = null;
133 24
        foreach ($this->authenticators() as $authenticator) {
134 16
            /* @var $authenticator \Phauthentic\Authentication\Authenticator\AuthenticatorInterface */
135 16
            $result = $authenticator->authenticate($request);
136
            if ($result->isValid()) {
137 16
                $this->successfulAuthenticator = $authenticator;
138
                $this->result = $result;
139
140 24
                return true;
141 24
            }
142 4
143
            if ($result->isValid() === false) {
144
                if ($authenticator instanceof StatelessInterface) {
145 20
                    $authenticator->unauthorizedChallenge($request);
146
                }
147
148
                $this->failures[] = new Failure($authenticator, $result);
149 4
            }
150
        }
151 4
152
        $this->result = $result;
153
154
        return false;
155
    }
156
157 16
    /**
158
     * {@inheritDoc}
159 16
     */
160
    public function getFailures(): array
161
    {
162
        return $this->failures;
163
    }
164
165
    /**
166
     * Clears the identity from authenticators that store them and the request
167
     *
168
     * @param \Psr\Http\Message\ServerRequestInterface $request The request.
169 4
     * @param \Psr\Http\Message\ResponseInterface $response The response.
170
     * @return \Phauthentic\Authentication\PersistenceResultInterface Return an array containing the request and response objects.
171
     */
172
    public function clearIdentity(
173 4
        ServerRequestInterface $request,
174 4
        ResponseInterface $response
175 4
    ): PersistenceResultInterface {
176
        foreach ($this->authenticators() as $authenticator) {
177
            if ($authenticator instanceof PersistenceInterface) {
178
                $response = $authenticator->clearIdentity($request, $response);
179
            }
180 4
        }
181
182 4
183
        $this->resetInternalState();
184
185
        return new PersistenceResult($request, $response);
186
    }
187
188
    /**
189
     * Resets the internal state of the service
190 4
     *
191
     * @return void
192 4
     */
193 4
    protected function resetInternalState(): void
194 4
    {
195 4
        $this->identity = null;
196 4
        $this->result = null;
197
        $this->successfulAuthenticator = null;
198
        $this->failures = [];
199
    }
200
201
    /**
202
     * Sets identity data and persists it in the authenticators that support it.
203
     *
204
     * @param \Psr\Http\Message\ServerRequestInterface $request The request.
205
     * @param \Psr\Http\Message\ResponseInterface $response The response.
206 8
     * @param \Phauthentic\Authentication\Identity\IdentityInterface|null $identity Identity.
207
     * @return \Phauthentic\Authentication\PersistenceResultInterface
208
     */
209
    public function persistIdentity(
210
        ServerRequestInterface $request,
211 8
        ResponseInterface $response,
212 4
        IdentityInterface $identity = null
213
    ): PersistenceResultInterface {
214
        if ($identity === null) {
215 8
            $identity = $this->getIdentity();
216 8
        }
217 8
218 8
        if ($identity !== null) {
219
            foreach ($this->authenticators() as $authenticator) {
220
                if ($authenticator instanceof PersistenceInterface) {
221
                    $response = $authenticator->persistIdentity($request, $response, $identity->getOriginalData());
222
                }
223 8
            }
224
        }
225
226
        return new PersistenceResult($request, $response);
227
    }
228
229
    /**
230
     * Gets the successful authenticator instance if one was successful after calling authenticate
231 20
     *
232
     * @return \Phauthentic\Authentication\Authenticator\AuthenticatorInterface|null
233 20
     */
234
    public function getSuccessfulAuthenticator(): ?AuthenticatorInterface
235
    {
236
        return $this->successfulAuthenticator;
237
    }
238
239
    /**
240
     * Gets the result of the last authenticate() call.
241 20
     *
242
     * @return \Phauthentic\Authentication\Authenticator\ResultInterface|null Authentication result interface
243 20
     */
244
    public function getResult(): ?ResultInterface
245
    {
246
        return $this->result;
247
    }
248
249
    /**
250
     * Gets an identity object
251 24
     *
252
     * @return null|\Phauthentic\Authentication\Identity\IdentityInterface
253 24
     */
254 8
    public function getIdentity(): ?IdentityInterface
255
    {
256
        if ($this->result === null || !$this->result->isValid()) {
257 16
            return null;
258 16
        }
259 4
260
        $data = $this->result->getData();
261
        if ($data instanceof IdentityInterface || $data === null) {
262 12
            return $data;
263 12
        }
264
265
        if ($this->identity === null) {
266 12
            $this->identity = $this->buildIdentity($data);
267
        }
268
269
        return $this->identity;
270
    }
271
272
    /**
273
     * Builds the identity object
274
     *
275 16
     * @param \ArrayAccess $data Identity data
276
     * @return \Phauthentic\Authentication\Identity\IdentityInterface
277 16
     */
278
    public function buildIdentity(ArrayAccess $data): IdentityInterface
279
    {
280
        return $this->identityFactory->create($data);
281
    }
282
}
283