RemoteUserAuthenticator::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
nc 1
nop 5
dl 0
loc 12
ccs 0
cts 6
cp 0
crap 2
rs 10
c 1
b 0
f 0
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\Interfaces\AuthenticatorInterface;
22
use Biurad\Security\Interfaces\FailureHandlerInterface;
23
use Psr\Http\Message\ResponseInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
use Psr\Log\LoggerInterface;
26
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
27
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
28
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
29
use Symfony\Component\Security\Core\Exception\AuthenticationException;
30
use Symfony\Component\Security\Core\User\UserProviderInterface;
31
32
/**
33
 * This authenticator authenticates a remote user. (example by the
34
 * webserver) X.509 certificates.
35
 *
36
 * @author Divine Niiquaye Ibok <[email protected]>
37
 */
38
class RemoteUserAuthenticator implements AuthenticatorInterface, FailureHandlerInterface
39
{
40
    private string $userKey;
41
    private string $credentialsKey;
42
    private UserProviderInterface $userProvider;
43
    private TokenStorageInterface $tokenStorage;
44
    private ?LoggerInterface $logger;
45
46
    public function __construct(
47
        UserProviderInterface $userProvider,
48
        TokenStorageInterface $tokenStorage,
49
        string $userKey = 'SSL_CLIENT_S_DN_Email',
50
        string $credentialsKey = 'SSL_CLIENT_S_DN',
51
        LoggerInterface $logger = null
52
    ) {
53
        $this->userKey = $userKey;
54
        $this->credentialsKey = $credentialsKey;
55
        $this->userProvider = $userProvider;
56
        $this->tokenStorage = $tokenStorage;
57
        $this->logger = $logger;
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function supports(ServerRequestInterface $request): bool
64
    {
65
        if (!$username = ($request->getServerParams()[$this->userKey] ?? null)) {
66
            $username = $request->getServerParams()[$this->credentialsKey] ?? null;
67
68
            if (null !== $username && \preg_match('#emailAddress=([^,/@]++@[^,/]++)#', $username, $matches)) {
69
                $username = $matches[1];
70
            }
71
        }
72
73
        if (empty($username)) {
74
            if (null !== $this->logger) {
75
                $this->logger->debug('Skipping pre-authenticated authenticator no username could be extracted.', ['authenticator' => static::class]);
76
            }
77
78
            return false;
79
        }
80
81
        // do not overwrite already stored tokens from the same user (i.e. from the session)
82
        if (
83
            null !== ($token = $this->tokenStorage->getToken()) &&
84
            (!$token instanceof PreAuthenticatedToken || $token->getUserIdentifier() === $username)
85
        ) {
86
            if (null !== $this->logger) {
87
                $this->logger->debug('Skipping pre-authenticated authenticator as the user already has an existing session.', ['authenticator' => static::class]);
88
            }
89
90
            return false;
91
        }
92
93
        $this->userKey = $username;
94
95
        return true;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function authenticate(ServerRequestInterface $request, array $credentials, string $firewallName): ?TokenInterface
102
    {
103
        $user = $this->userProvider->loadUserByIdentifier($this->userKey);
104
105
        return $token ?? new PreAuthenticatedToken($user, $firewallName, $user->getRoles());
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $token seems to never exist and therefore isset should always be false.
Loading history...
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function failure(ServerRequestInterface $request, AuthenticationException $exception): ?ResponseInterface
112
    {
113
        $token =$this->tokenStorage->getToken();
0 ignored issues
show
Coding Style introduced by
Expected at least 1 space after "="; 0 found
Loading history...
114
115
        if ($token instanceof PreAuthenticatedToken) {
116
            $this->tokenStorage->setToken();
0 ignored issues
show
Bug introduced by
The call to Symfony\Component\Securi...geInterface::setToken() has too few arguments starting with token. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

116
            $this->tokenStorage->/** @scrutinizer ignore-call */ 
117
                                 setToken();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
117
118
            if (null !== $this->logger) {
119
                if (null === $exception) {
120
                    $this->logger->info(\sprintf('Cleared pre-authenticated token, due missing user in credentials: %s, %s', $this->userKey, $this->credentialsKey));
121
                } else {
122
                    $this->logger->info('Cleared pre-authenticated token due to an exception.', ['exception' => $exception]);
123
                }
124
            }
125
        }
126
127
        return null;
128
    }
129
}
130