Completed
Push — feature/evo-2472-whoami ( 39cc08...5611b4 )
by Jan
14:59
created

SecurityAuthenticator::authenticateToken()   C

Complexity

Conditions 8
Paths 9

Size

Total Lines 42
Code Lines 24

Duplication

Lines 12
Ratio 28.57 %

Code Coverage

Tests 19
CRAP Score 9.2485

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 12
loc 42
rs 5.3846
ccs 19
cts 26
cp 0.7308
cc 8
eloc 24
nc 9
nop 3
crap 9.2485
1
<?php
2
/**
3
 * auth interface for authing against an airlock key of some sorts
4
 */
5
6
namespace Graviton\SecurityBundle\Authentication;
7
8
use Graviton\SecurityBundle\Authentication\Strategies\StrategyInterface;
9
use Graviton\SecurityBundle\Authentication\Provider\AuthenticationProvider;
10
use Graviton\SecurityBundle\Entities\AnonymousUser;
11
use Graviton\SecurityBundle\Entities\SecurityUser;
12
use Psr\Log\LoggerInterface as Logger;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface as SimplePreAuthInterface;
16
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
17
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18
use Symfony\Component\Security\Core\Exception\AuthenticationException;
19
use Symfony\Component\Security\Core\User\UserProviderInterface;
20
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
21
22
/**
23
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
24
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
25
 * @link     http://swisscom.ch
26
 */
27
final class SecurityAuthenticator implements
28
    SimplePreAuthInterface,
29
    AuthenticationFailureHandlerInterface
30
{
31
32
    /**
33
     * Authentication can be required to use any service
34
     * @var bool,
35
     */
36
    protected $securityRequired;
37
38
    /**
39
     * Authentication can use a test user if no user found
40
     * @var bool,
41
     */
42
    protected $securityTestUsername;
43
44
    /**
45
     * Authentication can allow not identified users to get information
46
     * @var bool,
47
     */
48
    protected $allowAnonymous;
49
50
    /**
51
     * @var AuthenticationProvider
52
     */
53
    protected $userProvider;
54
55
    /**
56
     * @var StrategyInterface
57
     */
58
    protected $extractionStrategy;
59
60
    /**
61
     * @var Logger
62
     */
63
    protected $logger;
64
65
66
    /**
67
     * @param boolean                   $securityRequired   user provider to use
68
     * @param string                    $securityTestUsername  user for testing
69
     * @param boolean                   $allowAnonymous     user provider to use
70
     * @param AuthenticationProvider    $userProvider       user provider to use
71
     * @param StrategyInterface         $extractionStrategy auth strategy to use
72
     * @param Logger                    $logger             logger to user for logging errors
73
     */
74 15
    public function __construct(
75
        $securityRequired,
76
        $securityTestUsername,
77
        $allowAnonymous,
78
        AuthenticationProvider $userProvider,
79
        StrategyInterface $extractionStrategy,
80
        Logger $logger
81
    ) {
82
83 15
        $this->securityRequired     = $securityRequired;
84 15
        $this->securityTestUsername = $securityTestUsername;
85 15
        $this->allowAnonymous       = $allowAnonymous;
86 15
        $this->userProvider         = $userProvider;
87 15
        $this->extractionStrategy   = $extractionStrategy;
88
89 15
        $this->logger = $logger;
90 15
    }
91
92
    /**
93
     * @param Request $request     request to authenticate
94
     * @param string  $providerKey provider key to auth with
95
     *
96
     * @return \Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken
97
     */
98 7
    public function createToken(Request $request, $providerKey)
99
    {
100
        // look for an apikey query parameter
101 7
        $apiKey = $this->extractionStrategy->apply($request);
102
103 7
        return new PreAuthenticatedToken(
104 7
            'anon.',
105 7
            $apiKey,
106
            $providerKey
107 7
        );
108
    }
109
110
    /**
111
     * Tries to authenticate the provided token
112
     *
113
     * @param TokenInterface        $token        token to authenticate
114
     * @param UserProviderInterface $userProvider provider to auth against
115
     * @param string                $providerKey  key to auth with
116
     *
117
     * @return \Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken
118
     */
119 4
    public function authenticateToken(
120
        TokenInterface $token,
121
        UserProviderInterface $userProvider,
122
        $providerKey)
123
    {
124 4
        $username = $token->getCredentials();
125
126
        // If no username in Strategy, check if required.
127 4 View Code Duplication
        if ( $this->securityRequired && !$username ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
            $this->logger->warning('Authentication key is required.');
129
            throw new AuthenticationException(
130
                sprintf('Authentication key is required.')
131
            );
132
        }
133
134
        /** @var SecurityUser $securityUser */
135 4
        $securityUser = $this->userProvider->loadUserByUsername($username);
136
137 4
        if ( !$securityUser && $this->securityTestUsername ) {
138
            $this->logger->info('Authentication, loading test user: '.$this->securityTestUsername);
139
            $securityUser = $this->userProvider->loadUserByUsername($this->securityTestUsername);
140
        }
141
142 4
        if (!$securityUser && $this->allowAnonymous) {
143 2
            $this->logger->info('Authentication, loading anonymous user.');
144 2
            $securityUser = new SecurityUser(new AnonymousUser(), ['ROLE_GRAVITON_ANONYMOUS'] );
0 ignored issues
show
Documentation introduced by
array('ROLE_GRAVITON_ANONYMOUS') is of type array<integer,string,{"0":"string"}>, but the function expects a array<integer,object<Sym...curity\Core\Role\Role>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
145 2
        }
146
147 4 View Code Duplication
        if (!$securityUser) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
148 1
            $this->logger->warning(sprintf('Authentication key "%s" could not be resolved.', $username));
149 1
            throw new AuthenticationException(
150 1
                sprintf('Authentication key "%s" could not be resolved.', $username)
151 1
            );
152
        }
153
154 3
        return new PreAuthenticatedToken(
155 3
            $securityUser, $username,
156 3
            $providerKey,
157 3
            $securityUser->getRoles()
158 3
        );
159
160
    }
161
162
    /**
163
     * @param TokenInterface $token       token to check
164
     * @param string         $providerKey provider to check against
165
     *
166
     * @return bool
167
     */
168 3
    public function supportsToken(TokenInterface $token, $providerKey)
169
    {
170 3
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
171
    }
172
173
    /**
174
     * This is called when an interactive authentication attempt fails. This is
175
     * called by authentication listeners inheriting from
176
     * AbstractAuthenticationListener.
177
     *
178
     * @param Request                 $request   original request
179
     * @param AuthenticationException $exception exception from auth attempt
180
     *
181
     * @return Response|null
182
     */
183 1
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
184
    {
185 1
        return new Response(
186 1
            $exception->getMessageKey(),
187
            Response::HTTP_NETWORK_AUTHENTICATION_REQUIRED
188 1
        );
189
    }
190
}
191