AlmaAuthenticator::start()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
1
<?php
2
3
namespace App\Security;
4
5
use App\Service\AlmaApi;
6
use Symfony\Component\HttpFoundation\RedirectResponse;
7
use Symfony\Component\HttpFoundation\Request;
8
use Symfony\Component\HttpFoundation\Response;
9
use Symfony\Component\Routing\RouterInterface;
10
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
11
use Symfony\Component\Security\Core\Exception\AuthenticationException;
12
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
13
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
14
use Symfony\Component\Security\Core\Security;
15
use Symfony\Component\Security\Core\User\UserInterface;
16
use Symfony\Component\Security\Core\User\UserProviderInterface;
17
use Symfony\Component\Security\Csrf\CsrfToken;
18
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
19
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
20
21
22
/**
23
 * Provides direct authentication using the Alma API.
24
 */
25
26
class AlmaAuthenticator extends AbstractGuardAuthenticator
27
{
28
    private $router;
29
    private $csrfTokenManager;
30
    private $security;
31
    private $api;
32
33
    public function __construct(RouterInterface $router, CsrfTokenManagerInterface $csrfTokenManager, Security $security, AlmaApi $api)
34
    {
35
        $this->router = $router;
36
        $this->csrfTokenManager = $csrfTokenManager;
37
        $this->security = $security;
38
        $this->api = $api;
39
    }
40
41
    public function supports(Request $request)
42
    {
43
        if ($this->security->getUser()) {
44
            return false;
45
        }
46
        return $request->request->has('_username') && $request->request->has('_password');
47
    }
48
49
    /**
50
     * Returns a response that directs the user to authenticate.
51
     *
52
     * This is called when an anonymous request accesses a resource that
53
     * requires authentication. The job of this method is to return some
54
     * response that "helps" the user start into the authentication process.
55
     *
56
     * Examples:
57
     *  A) For a form login, you might redirect to the login page
58
     *      return new RedirectResponse('/login');
59
     *  B) For an API token authentication system, you return a 401 response
60
     *      return new Response('Auth header required', 401);
61
     *
62
     * @param Request $request The request that resulted in an AuthenticationException
63
     * @param AuthenticationException $authException The exception that started the authentication process
64
     *
65
     * @return Response
66
     */
67
    public function start(Request $request, AuthenticationException $authException = null)
68
    {
69
        return new RedirectResponse($this->router->generate('login'));
70
    }
71
72
    /**
73
     * Get the authentication credentials from the request and return them
74
     * as any type (e.g. an associate array).
75
     *
76
     * Whatever value you return here will be passed to getUser() and checkCredentials()
77
     *
78
     * For example, for a form login, you might:
79
     *
80
     *      return array(
81
     *          'username' => $request->request->get('_username'),
82
     *          'password' => $request->request->get('_password'),
83
     *      );
84
     *
85
     * Or for an API token that's on a header, you might use:
86
     *
87
     *      return array('api_key' => $request->headers->get('X-API-TOKEN'));
88
     *
89
     * @param Request $request
90
     *
91
     * @return mixed Any non-null value
92
     *
93
     * @throws \UnexpectedValueException If null is returned
94
     */
95
    public function getCredentials(Request $request)
96
    {
97
        $csrfToken = $request->request->get('_csrf_token');
98
        if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('authenticate', $csrfToken))) {
99
            throw new InvalidCsrfTokenException('Invalid CSRF token.');
100
        }
101
102
        $username = $request->request->get('_username');
103
        $password = $request->request->get('_password');
104
105
        if ($username == '' || $password == '') {
106
            throw new CustomUserMessageAuthenticationException('Username or password cannot be empty.');
107
        }
108
109
        return array(
110
            'username' => $username,
111
            'password' => $password
112
        );
113
    }
114
115
    /**
116
     * Return a UserInterface object based on the credentials.
117
     *
118
     * The *credentials* are the return value from getCredentials()
119
     *
120
     * You may throw an AuthenticationException if you wish. If you return
121
     * null, then a UsernameNotFoundException is thrown for you.
122
     *
123
     * @param mixed $credentials
124
     * @param UserProviderInterface $userProvider
125
     *
126
     * @throws AuthenticationException
127
     *
128
     * @return UserInterface|null
129
     */
130
    public function getUser($credentials, UserProviderInterface $userProvider)
131
    {
132
        return $userProvider->loadUserByUsername($credentials['username']);
133
    }
134
135
    /**
136
     * Returns true if the credentials are valid.
137
     *
138
     * If any value other than true is returned, authentication will
139
     * fail. You may also throw an AuthenticationException if you wish
140
     * to cause authentication to fail.
141
     *
142
     * The *credentials* are the return value from getCredentials()
143
     *
144
     * @param mixed $credentials
145
     * @param UserInterface $user
146
     *
147
     * @return bool
148
     *
149
     * @throws AuthenticationException
150
     */
151
    public function checkCredentials($credentials, UserInterface $user)
152
    {
153
        try {
154
            $response = $this->api->authenticateUser($credentials['username'], $credentials['password']);
155
            if ($response->getStatusCode() === 204) {
156
                return true;
157
            }
158
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
159
            throw new CustomUserMessageAuthenticationException('Invalid username or password.');
160
        }
161
        return false;
162
    }
163
164
    /**
165
     * Called when authentication executed, but failed (e.g. wrong username password).
166
     *
167
     * This should return the Response sent back to the user, like a
168
     * RedirectResponse to the login page or a 403 response.
169
     *
170
     * If you return null, the request will continue, but the user will
171
     * not be authenticated. This is probably not what you want to do.
172
     *
173
     * @param Request $request
174
     * @param AuthenticationException $exception
175
     *
176
     * @return Response|null
177
     */
178
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
179
    {
180
        $request->getSession()->set(Security::LAST_USERNAME, $request->request->get('_username'));
181
        $request->getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
182
        return new RedirectResponse($this->router->generate('login'));
183
    }
184
185
    /**
186
     * Called when authentication executed and was successful!
187
     *
188
     * This should return the Response sent back to the user, like a
189
     * RedirectResponse to the last page they visited.
190
     *
191
     * If you return null, the current request will continue, and the user
192
     * will be authenticated. This makes sense, for example, with an API.
193
     *
194
     * @param Request $request
195
     * @param TokenInterface $token
196
     * @param string $providerKey The provider (i.e. firewall) key
197
     *
198
     * @return Response|null
199
     */
200
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
201
    {
202
        return new RedirectResponse($this->router->generate('index'));
203
    }
204
205
    /**
206
     * Does this method support remember me cookies?
207
     *
208
     * Remember me cookie will be set if *all* of the following are met:
209
     *  A) This method returns true
210
     *  B) The remember_me key under your firewall is configured
211
     *  C) The "remember me" functionality is activated. This is usually
212
     *      done by having a _remember_me checkbox in your form, but
213
     *      can be configured by the "always_remember_me" and "remember_me_parameter"
214
     *      parameters under the "remember_me" firewall key
215
     *  D) The onAuthenticationSuccess method returns a Response object
216
     *
217
     * @return bool
218
     */
219
    public function supportsRememberMe()
220
    {
221
        return false;
222
    }
223
}
224