AbstractTokenAuthenticator   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 77.46%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 24
c 1
b 1
f 0
lcom 1
cbo 14
dl 0
loc 172
ccs 55
cts 71
cp 0.7746
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A supportsToken() 0 4 2
A createToken() 0 10 2
B authenticateToken() 0 37 6
A onAuthenticationFailure() 0 15 3
B checkAuthentication() 0 22 5
B retrieveUser() 0 26 5
1
<?php
2
3
namespace TreeHouse\KeystoneBundle\Security\Authentication;
4
5
use Symfony\Component\HttpFoundation\JsonResponse;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\HttpFoundation\Response;
8
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
9
use Symfony\Component\Security\Core\Exception\AccountStatusException;
10
use Symfony\Component\Security\Core\Exception\AuthenticationException;
11
use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
12
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
13
use Symfony\Component\Security\Core\User\UserCheckerInterface;
14
use Symfony\Component\Security\Core\User\UserInterface;
15
use Symfony\Component\Security\Core\User\UserProviderInterface;
16
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
17
use TreeHouse\KeystoneBundle\Exception\TokenExpiredException;
18
use TreeHouse\KeystoneBundle\Manager\TokenManager;
19
use TreeHouse\KeystoneBundle\Model\Token;
20
use TreeHouse\KeystoneBundle\Security\Authentication\Token\PreAuthenticatedToken;
21
22
abstract class AbstractTokenAuthenticator implements AuthenticationFailureHandlerInterface
23
{
24
    /**
25
     * @var TokenManager
26
     */
27
    protected $tokenManager;
28
29
    /**
30
     * @var UserCheckerInterface
31
     */
32
    protected $userChecker;
33
34
    /**
35
     * @param TokenManager         $tokenManager
36
     * @param UserCheckerInterface $userChecker
37
     */
38 18
    public function __construct(TokenManager $tokenManager, UserCheckerInterface $userChecker)
39
    {
40 18
        $this->tokenManager = $tokenManager;
41 18
        $this->userChecker = $userChecker;
42 18
    }
43
44
    /**
45
     * @inheritdoc
46
     */
47 10
    public function supportsToken(TokenInterface $token, $providerKey)
48
    {
49 10
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
50
    }
51
52
    /**
53
     * @inheritdoc
54
     */
55 8
    public function createToken(Request $request, $providerKey)
56
    {
57 8
        if (!$request->headers->has('X-Auth-Token')) {
58 2
            return null;
59
        }
60
61 6
        $authToken = (string) $request->headers->get('X-Auth-Token');
62
63 6
        return new PreAuthenticatedToken($authToken, $providerKey);
0 ignored issues
show
Documentation introduced by
$authToken is of type string, but the function expects a array.

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...
64
    }
65
66
    /**
67
     * @inheritdoc
68
     */
69 6
    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
70
    {
71
        /* @var PreAuthenticatedToken $token */
72 6
        $authToken = $token->getToken();
73 6
        if (empty($authToken)) {
74
            $authToken = 'NONE_PROVIDED';
75
        }
76
77 6
        $tokenEntity = $this->tokenManager->findById($authToken);
78 6
        if (!$tokenEntity) {
79 2
            throw new BadCredentialsException('Bad token');
80
        }
81
82 4
        if (false === $this->tokenManager->isExpired($tokenEntity)) {
83 2
            throw new TokenExpiredException('Token expired');
84
        }
85
86 2
        $user = $this->retrieveUser($userProvider, $tokenEntity);
87
88 2
        if (!$user instanceof UserInterface) {
89
            throw new AuthenticationServiceException('retrieveUser() must return a UserInterface.');
90
        }
91
92
        try {
93 2
            $this->userChecker->checkPreAuth($user);
94 2
            $this->checkAuthentication($user, $tokenEntity, $token);
95 2
            $this->userChecker->checkPostAuth($user);
96 1
        } catch (BadCredentialsException $e) {
97
            throw new BadCredentialsException('Bad credentials', 0, $e);
98
        }
99
100 2
        $authenticatedToken = new PreAuthenticatedToken($token->getToken(), $providerKey, $user->getRoles());
0 ignored issues
show
Documentation introduced by
$token->getToken() is of type string, but the function expects a array.

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...
101 2
        $authenticatedToken->setUser($user);
102 2
        $authenticatedToken->setAttributes($token->getAttributes());
103
104 2
        return $authenticatedToken;
105
    }
106
107
    /**
108
     * For correct http status codes see documentation at http://developer.openstack.org/api-ref-identity-v2.html.
109
     *
110
     * @inheritdoc
111
     */
112 4
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
113
    {
114 4
        $errorMessage = 'Authentication Failed';
115 4
        $responseCode = Response::HTTP_BAD_REQUEST;
116
117 4
        if ($exception instanceof TokenExpiredException) {
118 2
            $errorMessage = 'Token expired';
119 2
            $responseCode = Response::HTTP_UNAUTHORIZED;
120 3
        } elseif ($exception instanceof AccountStatusException) {
121
            $errorMessage = 'Account disabled';
122
            $responseCode = Response::HTTP_FORBIDDEN;
123
        }
124
125 4
        return new JsonResponse(['error' => $errorMessage], $responseCode);
126
    }
127
128
    /**
129
     * @param UserInterface         $user
130
     * @param Token                 $tokenEntity
131
     * @param PreAuthenticatedToken $token
132
     *
133
     * @throws BadCredentialsException
134
     */
135 2
    protected function checkAuthentication(UserInterface $user, Token $tokenEntity, PreAuthenticatedToken $token)
136
    {
137 2
        $currentUser = $token->getUser();
138 2
        if ($currentUser instanceof UserInterface) {
139
            if ($currentUser->getPassword() !== $user->getPassword()) {
140
                throw new BadCredentialsException('The credentials were changed from another session.');
141
            }
142
        } else {
143 2
            if ('' === ($presentedToken = $token->getToken())) {
144
                throw new BadCredentialsException('The presented token cannot be empty.');
145
            }
146
147 2
            list($class, $username, $expires, $hash) = $this->tokenManager->getEncoder()->decodeHash($tokenEntity->getHash());
148
149 2
            $username = base64_decode($username, true);
150
151 2
            $hash2 = $this->tokenManager->getEncoder()->generateHash($class, $username, $user->getPassword(), $expires);
152 2
            if (false === $this->tokenManager->getEncoder()->compareHashes($hash, $hash2)) {
153
                throw new BadCredentialsException('The presented token is invalid.');
154
            }
155
        }
156 2
    }
157
158
    /**
159
     * @param UserProviderInterface $userProvider
160
     * @param Token                 $token
161
     *
162
     * @throws AuthenticationException
163
     * @throws AuthenticationServiceException
164
     *
165
     * @return UserInterface
166
     */
167 2
    protected function retrieveUser(UserProviderInterface $userProvider, Token $token)
168
    {
169 2
        $parts = $this->tokenManager->getEncoder()->decodeHash($token->getHash());
170
171 2
        if (count($parts) !== 4) {
172
            throw new AuthenticationException('The hash is invalid.');
173
        }
174
175 2
        list($class, $username, $expires, $hash) = $parts;
0 ignored issues
show
Unused Code introduced by
The assignment to $class is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $expires is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $hash is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
176
177 2
        if (false === $username = base64_decode($username, true)) {
178
            throw new AuthenticationException('$username contains a character from outside the base64 alphabet.');
179
        }
180
181
        try {
182 2
            $user = $userProvider->loadUserByUsername($username);
183 1
        } catch (\Exception $e) {
184
            throw new AuthenticationServiceException($e->getMessage(), 0, $e);
185
        }
186
187 2
        if (!$user instanceof UserInterface) {
188
            throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
189
        }
190
191 2
        return $user;
192
    }
193
}
194