Completed
Pull Request — develop (#413)
by Narcotic
12:15
created

SecurityAuthenticator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1
Metric Value
dl 0
loc 17
ccs 8
cts 8
cp 1
rs 9.4285
cc 1
eloc 13
nc 1
nop 6
crap 1
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;
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
    SimplePreAuthenticatorInterface,
0 ignored issues
show
Deprecated Code introduced by
The interface Symfony\Component\Securi...eAuthenticatorInterface has been deprecated with message: Since version 2.8, to be removed in 3.0. Use the same interface from Security\Http\Authentication instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
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 18
    public function __construct(
75
        $securityRequired,
76
        $securityTestUsername,
77
        $allowAnonymous,
78
        AuthenticationProvider $userProvider,
79
        StrategyInterface $extractionStrategy,
80
        Logger $logger
81
    ) {
82
83 18
        $this->securityRequired     = $securityRequired;
84 18
        $this->securityTestUsername = $securityTestUsername;
85 18
        $this->allowAnonymous       = $allowAnonymous;
86 18
        $this->userProvider         = $userProvider;
87 18
        $this->extractionStrategy   = $extractionStrategy;
88
89 18
        $this->logger = $logger;
90 18
    }
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 10
    public function createToken(Request $request, $providerKey)
99
    {
100
        // look for an apikey query parameter
101 10
        $apiKey = $this->extractionStrategy->apply($request);
102
103 10
        return new PreAuthenticatedToken(
104 10
            'anon.',
105 10
            $apiKey,
106
            $providerKey
107 10
        );
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 4
        $securityUser = false;
126
127
        // If no username in Strategy, check if required.
128 4
        if ($this->securityRequired && !$username) {
129
            $this->logger->warning('Authentication key is required.');
130
            throw new AuthenticationException(
131
                sprintf('Authentication key is required.')
132
            );
133
        }
134
135
        /** @var SecurityUser $securityUser */
136 4
        if ($user = $this->userProvider->loadUserByUsername($username)) {
137 2
            $securityUser = new SecurityUser($user, [SecurityUser::ROLE_USER]);
138 4
        } elseif ($this->securityTestUsername) {
139
            $this->logger->info('Authentication, loading test user: '.$this->securityTestUsername);
140
            if ($user = $this->userProvider->loadUserByUsername($this->securityTestUsername)) {
141
                $securityUser = new SecurityUser($user, [SecurityUser::ROLE_USER]);
142
            }
143
        }
144
145
        // Check if allow Anonymous
146 4
        if (!$securityUser) {
147 2
            if ($this->allowAnonymous) {
148
                $this->logger->info('Authentication, loading anonymous user.');
149
                $securityUser = new SecurityUser(new AnonymousUser(), [SecurityUser::ROLE_ANONYMOUS]);
150
            } else {
151 2
                $this->logger->warning(sprintf('Authentication key "%s" could not be resolved.', $username));
152 2
                throw new AuthenticationException(
153 2
                    sprintf('Authentication key "%s" could not be resolved.', $username)
154 2
                );
155
            }
156
        }
157
158 2
        return new PreAuthenticatedToken(
159 2
            $securityUser,
160 2
            $username,
161 2
            $providerKey,
162 2
            $securityUser->getRoles()
163 2
        );
164
165
    }
166
167
    /**
168
     * @param TokenInterface $token       token to check
169
     * @param string         $providerKey provider to check against
170
     *
171
     * @return bool
172
     */
173 2
    public function supportsToken(TokenInterface $token, $providerKey)
174
    {
175 2
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
176
    }
177
178
    /**
179
     * This is called when an interactive authentication attempt fails. This is
180
     * called by authentication listeners inheriting from
181
     * AbstractAuthenticationListener.
182
     *
183
     * @param Request                 $request   original request
184
     * @param AuthenticationException $exception exception from auth attempt
185
     *
186
     * @return Response|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Response.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
187
     */
188 2
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
189
    {
190 2
        return new Response(
191 2
            $exception->getMessageKey(),
192
            Response::HTTP_NETWORK_AUTHENTICATION_REQUIRED
193 2
        );
194
    }
195
}
196