Passed
Push — master ( 2c4349...5c635f )
by Pol
21:59 queued 19:27
created

CasGuardAuthenticator   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Test Coverage

Coverage 88.14%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 57
c 2
b 0
f 0
dl 0
loc 166
ccs 52
cts 59
cp 0.8814
rs 10
wmc 18

10 Methods

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