Passed
Push — master ( 2aa844...aea408 )
by Florian
06:11
created

AuthenticationService::resetInternalState()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types=1);
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
namespace Phauthentic\Authentication;
17
18
use ArrayAccess;
19
use Phauthentic\Authentication\Authenticator\AuthenticatorCollectionInterface;
20
use Phauthentic\Authentication\Authenticator\AuthenticatorInterface;
21
use Phauthentic\Authentication\Authenticator\Failure;
22
use Phauthentic\Authentication\Authenticator\PersistenceInterface;
23
use Phauthentic\Authentication\Authenticator\ResultInterface;
24
use Phauthentic\Authentication\Authenticator\StatelessInterface;
25
use Phauthentic\Authentication\Identity\IdentityFactoryInterface;
26
use Phauthentic\Authentication\Identity\IdentityInterface;
27
use Psr\Http\Message\ResponseInterface;
28
use Psr\Http\Message\ServerRequestInterface;
29
use RuntimeException;
30
31
/**
32
 * Authentication Service
33
 */
34
class AuthenticationService implements AuthenticationServiceInterface
35
{
36
    /**
37
     * Authenticator collection
38
     *
39
     * @var \Phauthentic\Authentication\Authenticator\AuthenticatorCollection
40
     */
41
    protected $authenticators;
42
43
    /**
44
     * Authenticator that successfully authenticated the identity.
45
     *
46
     * @var \Phauthentic\Authentication\Authenticator\AuthenticatorInterface|null
47
     */
48
    protected $successfulAuthenticator;
49
50
    /**
51
     * A list of failed authenticators after an authentication attempt
52
     *
53
     * @var \Phauthentic\Authentication\Authenticator\FailureInterface[]
54
     */
55
    protected $failures = [];
56
57
    /**
58
     * Identity object.
59
     *
60
     * @var \Phauthentic\Authentication\Identity\IdentityInterface|null
61
     */
62
    protected $identity;
63
64
    /**
65
     * Result of the last authenticate() call.
66
     *
67
     * @var \Phauthentic\Authentication\Authenticator\ResultInterface|null
68
     */
69
    protected $result;
70
71
    /**
72
     * Identity factory used to instantiate an identity object
73
     *
74
     * @var \Phauthentic\Authentication\Identity\IdentityFactoryInterface
75
     */
76
    protected $identityFactory;
77
78
    /**
79
     * Constructor
80
     *
81
     * @param \Phauthentic\Authentication\Authenticator\AuthenticatorCollection $authenticators Authenticator collection.
82
     * @param \Phauthentic\Authentication\Identity\IdentityFactoryInterface $factory Identity factory.
83
     */
84 30
    public function __construct(
85
        AuthenticatorCollectionInterface $authenticators,
86
        IdentityFactoryInterface $factory
87
    ) {
88 30
        $this->authenticators = $authenticators;
0 ignored issues
show
Documentation Bug introduced by
$authenticators is of type Phauthentic\Authenticati...atorCollectionInterface, but the property $authenticators was declared to be of type Phauthentic\Authenticati...AuthenticatorCollection. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

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