AuthenticationComponent::logout()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2.0014

Importance

Changes 0
Metric Value
cc 2
eloc 13
nc 2
nop 0
dl 0
loc 21
ccs 13
cts 14
cp 0.9286
crap 2.0014
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11
 * @link          https://cakephp.org CakePHP(tm) Project
12
 * @since         1.0.0
13
 * @license       https://opensource.org/licenses/mit-license.php MIT License
14
 */
15
namespace Phauthentic\Authentication\Controller\Component;
16
17
use Cake\Controller\Component;
18
use Cake\Event\EventDispatcherInterface;
19
use Cake\Event\EventDispatcherTrait;
20
use Cake\Routing\Router;
21
use Cake\Utility\Hash;
22
use Exception;
23
use Phauthentic\Authentication\AuthenticationServiceInterface;
24
use Phauthentic\Authentication\Authenticator\Exception\UnauthenticatedException;
25
use Phauthentic\Authentication\Authenticator\PersistenceInterface;
26
use Phauthentic\Authentication\Authenticator\ResultInterface;
27
use Phauthentic\Authentication\Authenticator\StatelessInterface;
28
use Phauthentic\Authentication\Identity\IdentityInterface;
29
use RuntimeException;
30
31
/**
32
 * Controller Component for interacting with Authentication.
33
 *
34
 */
35
class AuthenticationComponent extends Component implements EventDispatcherInterface
36
{
37
    use EventDispatcherTrait;
38
39
    /**
40
     * Configuration options
41
     *
42
     * - `logoutRedirect` - The route/URL to direct users to after logout()
43
     * - `requireIdentity` - By default AuthenticationComponent will require an
44
     *   identity to be present whenever it is active. You can set the option to
45
     *   false to disable that behavior. See allowUnauthenticated() as well.
46
     *
47
     * @var array
48
     */
49
    protected $_defaultConfig = [
50
        'logoutRedirect' => false,
51
        'requireIdentity' => true,
52
        'identityAttribute' => 'identity',
53
        'serviceAttribute' => 'authentication'
54
    ];
55
56
    /**
57
     * List of actions that don't require authentication.
58
     *
59
     * @var array
60
     */
61
    protected $unauthenticatedActions = [];
62
63
    /**
64
     * Authentication service instance.
65
     *
66
     * @var AuthenticationServiceInterface
67
     */
68
    protected $_authentication;
69
70
    /**
71
     * Initialize component.
72
     *
73
     * @param array $config The config data.
74
     * @return void
75
     */
76 17
    public function initialize(array $config)
77
    {
78 17
        $controller = $this->getController();
79 17
        $this->setEventManager($controller->getEventManager());
80 17
    }
81
82
    /**
83
     * Triggers the Authentication.afterIdentify event for non stateless adapters that are not persistent either
84
     *
85
     * @return void
86
     */
87 5
    public function beforeFilter()
88
    {
89 5
        $authentication = $this->getAuthenticationService();
90 5
        $provider = $authentication->getSuccessfulAuthenticator();
91
92 5
        if ($provider === null ||
93 1
            $provider instanceof PersistenceInterface ||
94 5
            $provider instanceof StatelessInterface
95
        ) {
96 4
            return;
97
        }
98
99 1
        $this->dispatchEvent('Authentication.afterIdentify', [
100 1
            'provider' => $provider,
101 1
            'identity' => $this->getIdentity(),
102 1
            'service' => $authentication
103 1
        ], $this->getController());
104 1
    }
105
106
    /**
107
     * Returns authentication service.
108
     *
109
     * @return AuthenticationServiceInterface
110
     * @throws \Exception
111
     */
112 12
    public function getAuthenticationService()
113
    {
114 12
        $controller = $this->getController();
115 12
        $service = $controller->request->getAttribute($this->getConfig('serviceAttribute'));
116 12
        if ($service === null) {
117 1
            throw new Exception('The request object does not contain the required `authentication` attribute');
118
        }
119
120 11
        if (!($service instanceof AuthenticationServiceInterface)) {
121 1
            throw new Exception('Authentication service does not implement ' . AuthenticationServiceInterface::class);
122
        }
123
124 10
        return $service;
125
    }
126
127
    /**
128
     * Start up event handler
129
     *
130
     * @return void
131
     * @throws Exception when request is missing or has an invalid AuthenticationService
132
     * @throws UnauthenticatedException when requireIdentity is true and request is missing an identity
133
     */
134 5
    public function startup()
135
    {
136 5
        if (!$this->getConfig('requireIdentity')) {
137 1
            return;
138
        }
139
140 4
        $request = $this->getController()->request;
141 4
        $action = $request->getParam('action');
142 4
        if (in_array($action, $this->unauthenticatedActions)) {
143 1
            return;
144
        }
145
146 3
        $identity = $request->getAttribute($this->getConfig('identityAttribute'));
147 3
        if (!$identity) {
148 2
            throw new UnauthenticatedException();
149
        }
150 1
    }
151
152
    /**
153
     * Set the list of actions that don't require an authentication identity to be present.
154
     *
155
     * Actions not in this list will require an identity to be present. Any
156
     * valid identity will pass this constraint.
157
     *
158
     * @param array $actions The action list.
159
     * @return $this
160
     */
161 4
    public function allowUnauthenticated(array $actions)
162
    {
163 4
        $this->unauthenticatedActions = $actions;
164
165 4
        return $this;
166
    }
167
168
    /**
169
     * Add to the list of actions that don't require an authentication identity to be present.
170
     *
171
     * @param array $actions The action or actions to append.
172
     * @return $this
173
     */
174 1
    public function addUnauthenticatedActions(array $actions)
175
    {
176 1
        $this->unauthenticatedActions = array_merge($this->unauthenticatedActions, $actions);
177 1
        $this->unauthenticatedActions = array_values(array_unique($this->unauthenticatedActions));
178
179 1
        return $this;
180
    }
181
182
    /**
183
     * Get the current list of actions that don't require authentication.
184
     *
185
     * @return array
186
     */
187 1
    public function getUnauthenticatedActions()
188
    {
189 1
        return $this->unauthenticatedActions;
190
    }
191
192
    /**
193
     * Gets the result of the last authenticate() call.
194
     *
195
     * @return ResultInterface|null Authentication result interface
196
     */
197 1
    public function getResult()
198
    {
199 1
        return $this->getAuthenticationService()->getResult();
200
    }
201
202
    /**
203
     * Returns the identity used in the authentication attempt.
204
     *
205
     * @return IdentityInterface|null
206
     */
207 6
    public function getIdentity()
208
    {
209 6
        $controller = $this->getController();
210 6
        $identity = $controller->request->getAttribute($this->getConfig('identityAttribute'));
211
212 6
        return $identity;
213
    }
214
215
    /**
216
     * Returns the identity used in the authentication attempt.
217
     *
218
     * @param string $path Path to return from the data.
219
     * @return mixed
220
     * @throws \RuntimeException If the identity has not been found.
221
     */
222 2
    public function getIdentityData($path)
223
    {
224 2
        $identity = $this->getIdentity();
225
226 2
        if ($identity === null) {
227 1
            throw new RuntimeException('The identity has not been found.');
228
        }
229
230 1
        return Hash::get($identity, $path);
231
    }
232
233
    /**
234
     * Set identity data to all authenticators that are loaded and support persistence.
235
     *
236
     * @param IdentityInterface $identity Identity to persist.
237
     * @return $this
238
     */
239 1
    public function setIdentity(IdentityInterface $identity)
240
    {
241 1
        $controller = $this->getController();
242
243 1
        $result = $this->getAuthenticationService()->persistIdentity(
244 1
            $controller->request,
245 1
            $controller->response,
246 1
            $identity
247
        );
248
249
        $request = $result
250 1
            ->getRequest()
251 1
            ->withAttribute($this->getConfig('identityAttribute'), $identity);
252 1
        $controller->setRequest($request);
253 1
        $controller->response = $result->getResponse();
254
255 1
        return $this;
256
    }
257
258
    /**
259
     * Log a user out.
260
     *
261
     * Triggers the `Authentication.logout` event.
262
     *
263
     * @return string|null Returns null or `logoutRedirect`.
264
     */
265 1
    public function logout()
266
    {
267 1
        $controller = $this->getController();
268 1
        $result = $this->getAuthenticationService()->clearIdentity(
269 1
            $controller->request,
270 1
            $controller->response
271
        );
272
273 1
        $controller->request = $result
274 1
            ->getRequest()
275 1
            ->withoutAttribute($this->getConfig('identityAttribute'));
276 1
        $controller->response = $result->getResponse();
277
278 1
        $this->dispatchEvent('Authentication.logout', [], $controller);
279
280 1
        $logoutRedirect = $this->getConfig('logoutRedirect');
281 1
        if ($logoutRedirect === false) {
282 1
            return null;
283
        }
284
285
        return Router::normalize($logoutRedirect);
286
    }
287
}