Test Failed
Pull Request — master (#11)
by Florian
07:04 queued 04:09
created

AuthenticationService::buildIdentity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 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
     * @param \Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface<\Phauthentic\Authentication\Authenticator\AuthenticatorInterface> $authenticators Authenticator collection.
85
     * @param \Phauthentic\Authentication\Identity\IdentityFactoryInterface $factory Identity factory.
86
     */
87 10
    public function __construct(
88
        AuthenticatorCollectionInterface $authenticators,
89
        IdentityFactoryInterface $factory
90
    ) {
91 10
        $this->authenticators = $authenticators;
92 10
        $this->identityFactory = $factory;
93
    }
94
95
    /**
96
     * Access the authenticator collection
97
     *
98
     * @return \Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface<\Phauthentic\Authentication\Authenticator\AuthenticatorInterface>
99
     */
100 9
    public function authenticators(): AuthenticatorCollectionInterface
101
    {
102 9
        return $this->authenticators;
103
    }
104
105
    /**
106
     * Checks if at least one authenticator is in the collection
107
     *
108
     * @throws \RuntimeException
109
     * @return void
110
     */
111 7
    protected function checkAuthenticators(): void
112
    {
113 7
        if ($this->authenticators()->isEmpty()) {
114 1
            throw new RuntimeException(
115 1
                'No authenticators loaded. You need to load at least one authenticator.'
116 1
            );
117
        }
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     *
123
     * @throws \RuntimeException Throws a runtime exception when no authenticators are loaded.
124
     */
125 7
    public function authenticate(ServerRequestInterface $request): bool
126
    {
127 7
        $this->checkAuthenticators();
128 6
        $this->identity = null;
129 6
        $this->successfulAuthenticator = null;
130 6
        $this->failures = [];
131
132 6
        $result = null;
133 6
        foreach ($this->authenticators() as $authenticator) {
134
            /* @var $authenticator \Phauthentic\Authentication\Authenticator\AuthenticatorInterface */
135 6
            $result = $authenticator->authenticate($request);
136 6
            if ($result->isValid()) {
137 4
                $this->successfulAuthenticator = $authenticator;
138 4
                $this->result = $result;
139
140 4
                return true;
141
            }
142
143 6
            if ($result->isValid() === false) {
144 6
                if ($authenticator instanceof StatelessInterface) {
145 1
                    $authenticator->unauthorizedChallenge($request);
146
                }
147
148 5
                $this->failures[] = new Failure($authenticator, $result);
149
            }
150
        }
151
152 1
        $this->result = $result;
153
154 1
        return false;
155
    }
156
157
    /**
158
     * {@inheritDoc}
159
     */
160 4
    public function getFailures(): array
161
    {
162 4
        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
     * @param \Psr\Http\Message\ResponseInterface $response The response.
170
     * @return \Phauthentic\Authentication\PersistenceResultInterface Return an array containing the request and response objects.
171
     */
172 1
    public function clearIdentity(
173
        ServerRequestInterface $request,
174
        ResponseInterface $response
175
    ): PersistenceResultInterface {
176 1
        foreach ($this->authenticators() as $authenticator) {
177 1
            if ($authenticator instanceof PersistenceInterface) {
178 1
                $response = $authenticator->clearIdentity($request, $response);
179
            }
180
        }
181
182
183 1
        $this->resetInternalState();
184
185 1
        return new PersistenceResult($request, $response);
186
    }
187
188
    /**
189
     * Resets the internal state of the service
190
     *
191
     * @return void
192
     */
193 1
    protected function resetInternalState(): void
194
    {
195 1
        $this->identity = null;
196 1
        $this->result = null;
197 1
        $this->successfulAuthenticator = null;
198 1
        $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
     * @param \Phauthentic\Authentication\Identity\IdentityInterface|null $identity Identity.
207
     * @return \Phauthentic\Authentication\PersistenceResultInterface
208
     */
209 2
    public function persistIdentity(
210
        ServerRequestInterface $request,
211
        ResponseInterface $response,
212
        IdentityInterface $identity = null
213
    ): PersistenceResultInterface {
214 2
        if ($identity === null) {
215 1
            $identity = $this->getIdentity();
216
        }
217
218 2
        if ($identity !== null) {
219 2
            foreach ($this->authenticators() as $authenticator) {
220 2
                if ($authenticator instanceof PersistenceInterface) {
221 2
                    $response = $authenticator->persistIdentity($request, $response, $identity->getOriginalData());
222
                }
223
            }
224
        }
225
226 2
        return new PersistenceResult($request, $response);
227
    }
228
229
    /**
230
     * Gets the successful authenticator instance if one was successful after calling authenticate
231
     *
232
     * @return \Phauthentic\Authentication\Authenticator\AuthenticatorInterface|null
233
     */
234 5
    public function getSuccessfulAuthenticator(): ?AuthenticatorInterface
235
    {
236 5
        return $this->successfulAuthenticator;
237
    }
238
239
    /**
240
     * Gets the result of the last authenticate() call.
241
     *
242
     * @return \Phauthentic\Authentication\Authenticator\ResultInterface|null Authentication result interface
243
     */
244 5
    public function getResult(): ?ResultInterface
245
    {
246 5
        return $this->result;
247
    }
248
249
    /**
250
     * Gets an identity object
251
     *
252
     * @return null|\Phauthentic\Authentication\Identity\IdentityInterface
253
     */
254 6
    public function getIdentity(): ?IdentityInterface
255
    {
256 6
        if ($this->result === null || !$this->result->isValid()) {
257 2
            return null;
258
        }
259
260 4
        $data = $this->result->getData();
261 4
        if ($data instanceof IdentityInterface || $data === null) {
262 1
            return $data;
263
        }
264
265 3
        if ($this->identity === null) {
266 3
            $this->identity = $this->buildIdentity($data);
267
        }
268
269 3
        return $this->identity;
270
    }
271
272
    /**
273
     * Builds the identity object
274
     *
275
     * @param \ArrayAccess $data Identity data
276
     * @return \Phauthentic\Authentication\Identity\IdentityInterface
277
     */
278 4
    public function buildIdentity(ArrayAccess $data): IdentityInterface
279
    {
280 4
        return $this->identityFactory->create($data);
281
    }
282
}
283