Passed
Push — 1.0 ( 07d681...376964 )
by Pol
03:57
created

CasGuardAuthenticator   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Test Coverage

Coverage 87.88%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 65
c 2
b 0
f 0
dl 0
loc 193
ccs 58
cts 66
cp 0.8788
rs 10
wmc 20

11 Methods

Rating   Name   Duplication   Size   Complexity  
A start() 0 20 3
A onAuthenticationFailure() 0 18 2
A getCredentials() 0 11 2
A __construct() 0 6 1
A checkCredentials() 0 15 3
A onAuthenticationSuccess() 0 7 1
A onLogoutSuccess() 0 13 2
A supports() 0 6 1
A toPsr() 0 7 1
A getUser() 0 13 3
A supportsRememberMe() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EcPhp\CasBundle\Security;
6
7
use EcPhp\CasBundle\Security\Core\User\CasUserProviderInterface;
8
use EcPhp\CasLib\CasInterface;
9
use EcPhp\CasLib\Introspection\Contract\ServiceValidate;
10
use EcPhp\CasLib\Utils\Uri;
11
use InvalidArgumentException;
12
use Psr\Http\Message\ServerRequestInterface;
13
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
14
use Symfony\Component\HttpFoundation\JsonResponse;
15
use Symfony\Component\HttpFoundation\RedirectResponse;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
19
use Symfony\Component\Security\Core\Exception\AuthenticationException;
20
use Symfony\Component\Security\Core\User\UserInterface;
21
use Symfony\Component\Security\Core\User\UserProviderInterface;
22
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
23
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
24
25
class CasGuardAuthenticator extends AbstractGuardAuthenticator implements LogoutSuccessHandlerInterface
26
{
27
    /**
28
     * The ecphp/cas-lib library.
29
     *
30
     * @var \EcPhp\CasLib\CasInterface
31
     */
32
    private $cas;
33
34
    /**
35
     * @var \Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface
36
     */
37
    private $httpMessageFactory;
38
39 11
    public function __construct(
40
        CasInterface $cas,
41
        HttpMessageFactoryInterface $httpMessageFactory
42
    ) {
43 11
        $this->cas = $cas;
44 11
        $this->httpMessageFactory = $httpMessageFactory;
45 11
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 1
    public function checkCredentials($credentials, UserInterface $user)
51
    {
52
        try {
53 1
            $introspect = $this->cas->detect($credentials);
54 1
        } catch (InvalidArgumentException $exception) {
55 1
            throw new AuthenticationException($exception->getMessage());
56
        }
57
58 1
        if (false === ($introspect instanceof ServiceValidate)) {
59 1
            throw new AuthenticationException(
60 1
                'Failure in the returned response'
61
            );
62
        }
63
64 1
        return true;
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function getCredentials(Request $request)
71
    {
72
        $response = $this
73
            ->cas->withServerRequest($this->toPsr($request))
74
            ->requestTicketValidation();
75
76
        if (null === $response) {
77
            throw new AuthenticationException('Unable to authenticate the user with such service ticket.');
78
        }
79
80
        return $response;
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 1
    public function getUser($credentials, UserProviderInterface $userProvider)
87
    {
88 1
        if (false === ($userProvider instanceof CasUserProviderInterface)) {
89 1
            throw new AuthenticationException('Unable to load the user through the given User Provider.');
90
        }
91
92
        try {
93 1
            $user = $userProvider->loadUserByResponse($credentials);
94 1
        } catch (AuthenticationException $exception) {
95 1
            throw $exception;
96
        }
97
98 1
        return $user;
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104 1
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
105
    {
106 1
        $uri = $this->toPsr($request)->getUri();
107
108 1
        if (true === Uri::hasParams($uri, 'ticket')) {
109
            // Remove the ticket parameter.
110 1
            $uri = Uri::removeParams(
111 1
                $uri,
112 1
                'ticket'
113
            );
114
115
            // Add the renew parameter to force login again.
116 1
            $uri = Uri::withParam($uri, 'renew', 'true');
117
118 1
            return new RedirectResponse((string) $uri);
119
        }
120
121 1
        return null;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127 1
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
128
    {
129 1
        return new RedirectResponse(
130 1
            (string) Uri::removeParams(
131 1
                $this->toPsr($request)->getUri(),
132 1
                'ticket',
133 1
                'renew'
134
            )
135
        );
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141 1
    public function onLogoutSuccess(Request $request)
142
    {
143
        $response = $this
144 1
            ->cas
145 1
            ->logout();
146
147 1
        if (null === $response) {
148
            throw new AuthenticationException('Unable to trigger the logout procedure');
149
        }
150
151 1
        return new RedirectResponse(
152
            $response
153 1
                ->getHeaderLine('location')
154
        );
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 2
    public function start(Request $request, ?AuthenticationException $authException = null)
161
    {
162 2
        if (true === $request->isXmlHttpRequest()) {
163 1
            return new JsonResponse(
164 1
                ['message' => 'Authentication required'],
165 1
                Response::HTTP_UNAUTHORIZED
166
            );
167
        }
168
169
        $response = $this
170 1
            ->cas
171 1
            ->login();
172
173 1
        if (null === $response) {
174
            throw new AuthenticationException('Unable to trigger the login procedure');
175
        }
176
177 1
        return new RedirectResponse(
178
            $response
179 1
                ->getHeaderLine('location')
180
        );
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186 2
    public function supports(Request $request)
187
    {
188
        return $this
189 2
            ->cas
190 2
            ->withServerRequest($this->toPsr($request))
191 2
            ->supportAuthentication();
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197 1
    public function supportsRememberMe()
198
    {
199 1
        return false;
200
    }
201
202
    /**
203
     * Convert a Symfony request into a PSR Request.
204
     *
205
     * @param \Symfony\Component\HttpFoundation\Request $request
206
     *   The Symfony request.
207
     *
208
     * @return \Psr\Http\Message\ServerRequestInterface
209
     *   The PSR request.
210
     */
211 4
    private function toPsr(Request $request): ServerRequestInterface
212
    {
213
        // As we cannot decorate the Symfony Request object, we convert it into
214
        // a PSR Request so we can override the PSR HTTP Message factory if
215
        // needed.
216
        // See the reasons at https://github.com/ecphp/cas-lib/issues/5
217 4
        return $this->httpMessageFactory->createRequest($request);
218
    }
219
}
220