Completed
Push — master ( 798ee0...2329f6 )
by Lucas
08:18
created

SecurityAuthenticator::createToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 1
eloc 6
nc 1
nop 2
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
    public function __construct(
75
        $securityRequired,
76
        $securityTestUsername,
77
        $allowAnonymous,
78
        AuthenticationProvider $userProvider,
79
        StrategyInterface $extractionStrategy,
80
        Logger $logger
81
    ) {
82
83
        $this->securityRequired     = $securityRequired;
84
        $this->securityTestUsername = $securityTestUsername;
85
        $this->allowAnonymous       = $allowAnonymous;
86
        $this->userProvider         = $userProvider;
87
        $this->extractionStrategy   = $extractionStrategy;
88
89
        $this->logger = $logger;
90
    }
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
    public function createToken(Request $request, $providerKey)
99
    {
100
        // look for an apikey query parameter
101
        $apiKey = $this->extractionStrategy->apply($request);
102
103
        return new PreAuthenticatedToken(
104
            'anon.',
105
            $apiKey,
106
            $providerKey
107
        );
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
    public function authenticateToken(
120
        TokenInterface $token,
121
        UserProviderInterface $userProvider,
122
        $providerKey
123
    ) {
124
        $username = $token->getCredentials();
125
        $securityUser = false;
126
127
        // If no username in Strategy, check if required.
128
        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
        if ($user = $this->userProvider->loadUserByUsername($username)) {
137
            $securityUser = new SecurityUser($user, [SecurityUser::ROLE_USER]);
138
        } 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
        if (!$securityUser) {
147
            if ($this->allowAnonymous) {
148
                $this->logger->info('Authentication, loading anonymous user.');
149
                $securityUser = new SecurityUser(new AnonymousUser(), [SecurityUser::ROLE_ANONYMOUS]);
150
            } else {
151
                $this->logger->warning(sprintf('Authentication key "%s" could not be resolved.', $username));
152
                throw new AuthenticationException(
153
                    sprintf('Authentication key "%s" could not be resolved.', $username)
154
                );
155
            }
156
        }
157
158
        return new PreAuthenticatedToken(
159
            $securityUser,
160
            $username,
161
            $providerKey,
162
            $securityUser->getRoles()
163
        );
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
    public function supportsToken(TokenInterface $token, $providerKey)
174
    {
175
        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
187
     */
188
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
189
    {
190
        return new Response(
191
            $exception->getMessageKey(),
192
            Response::HTTP_NETWORK_AUTHENTICATION_REQUIRED
193
        );
194
    }
195
}
196