Passed
Push — master ( 3c8625...c93166 )
by Divine Niiquaye
12:46
created

RememberMeAuthenticator::authenticate()   B

Complexity

Conditions 10
Paths 48

Size

Total Lines 44
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 23
nc 48
nop 2
dl 0
loc 44
ccs 0
cts 24
cp 0
crap 110
rs 7.6666
c 1
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Biurad opensource projects.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 *
17
 */
18
19
namespace Biurad\Security\Authenticator;
20
21
use Biurad\Security\Handler\RememberMeHandler;
22
use Biurad\Security\Interfaces\AuthenticatorInterface;
23
use Psr\Http\Message\ResponseInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
use Psr\Log\LoggerInterface;
26
use Symfony\Component\HttpFoundation\Cookie;
27
use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken;
28
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
29
use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
30
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
31
use Symfony\Component\Security\Core\Exception\AuthenticationException;
32
use Symfony\Component\Security\Core\Exception\CookieTheftException;
33
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
34
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
35
use Symfony\Component\Security\Core\User\UserProviderInterface;
36
37
/**
38
 * The RememberMe *Authenticator* performs remember me authentication.
39
 *
40
 * @author Divine Niiquaye Ibok <[email protected]>
41
 */
42
class RememberMeAuthenticator implements AuthenticatorInterface
43
{
44
    private RememberMeHandler $rememberMeHandler;
45
    private UserProviderInterface $userProvider;
46
    private ?TokenInterface $token = null;
47
    private ?LoggerInterface $logger;
48
    private bool $allowMultipleRememberMeTokens;
49
50
    public function __construct(
51
        RememberMeHandler $rememberMeHandler,
52
        UserProviderInterface $userProvider,
53
        bool $allowMultipleRememberMeTokens = false,
54
        LoggerInterface $logger = null
55
    ) {
56
        $this->logger = $logger;
57
        $this->userProvider = $userProvider;
58
        $this->rememberMeHandler = $rememberMeHandler;
59
        $this->allowMultipleRememberMeTokens = $allowMultipleRememberMeTokens;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65
    public function setToken(?TokenInterface $token): void
66
    {
67
        $this->token = $token;
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73
    public function supports(ServerRequestInterface $request): bool
74
    {
75
        return null === $this->token && 'GET' === $request->getMethod();
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function authenticate(ServerRequestInterface $request, array $credentials): ?TokenInterface
82
    {
83
        $loadedUsers = $cookies = [];
84
        $identifiers = urldecode($request->getCookieParams()[RememberMeHandler::USERS_ID] ?? '');
85
86
        if (\str_contains($identifiers, '|')) {
87
            $identifiers = \explode('|', $identifiers);
88
        }
89
90
        foreach ((array) $identifiers as $identifier) {
91
            $rawCookie = $request->getCookieParams()[$this->rememberMeHandler->getCookieName() . $identifier] ?? null;
92
93
            if (null !== $rawCookie) {
94
                [$loadedUser, $cookie] = $this->rememberMeHandler->consumeRememberMeCookie($rawCookie, $this->userProvider);
95
                $loadedUsers[] = $loadedUser;
96
97
                if (null !== $cookie) {
98
                    $cookies[] = $cookie->withSecure('https' === $request->getUri()->getScheme());
99
                }
100
            }
101
        }
102
103
        if (!empty($loadedUsers)) {
104
            $firstUser = \array_shift($loadedUsers);
105
            $token = new RememberMeToken($firstUser, 'main', $this->rememberMeHandler->getSecret());
106
107
            if (\count($loadedUsers) > 0) {
108
                if (!$this->allowMultipleRememberMeTokens) {
109
                    throw new CookieTheftException('Multiple remember me tokens were received, but multiple tokens are not allowed.');
110
                }
111
112
                foreach ($loadedUsers as $user) {
113
                    $token = new SwitchUserToken($user, 'main', $user->getRoles(), $token);
114
                }
115
            }
116
117
            if (\count($cookies) > 0) {
118
                $token->setAttribute(RememberMeHandler::REMEMBER_ME, $cookies);
119
            }
120
121
            return $token;
122
        }
123
124
        return null;
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function failure(ServerRequestInterface $request, AuthenticationException $exception): ?ResponseInterface
131
    {
132
        if (null !== $this->logger) {
133
            if ($exception instanceof UserNotFoundException) {
134
                $this->logger->info('User for remember-me cookie not found.', ['exception' => $exception]);
135
            } elseif ($exception instanceof UnsupportedUserException) {
136
                $this->logger->warning('User class for remember-me cookie not supported.', ['exception' => $exception]);
137
            } elseif (!$exception instanceof CookieTheftException) {
138
                $this->logger->debug('Remember me authentication failed.', ['exception' => $exception]);
139
            }
140
        }
141
142
        return null;
143
    }
144
145
    /**
146
     * List of cookie which should be cleared if a user is logged out.
147
     *
148
     * @return array<int,Cookie>
149
     */
150
    public function clearCookies(ServerRequestInterface $request): array
151
    {
152
        return $this->rememberMeHandler->clearRememberMeCookies($request);
153
    }
154
}
155