Passed
Push — master ( 6b549f...1ce121 )
by Pol
09:48 queued 07:32
created

CasGuardAuthenticator   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 142
Duplicated Lines 0 %

Test Coverage

Coverage 88.14%

Importance

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

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A start() 0 20 3
A onAuthenticationFailure() 0 18 2
A getCredentials() 0 11 2
A checkCredentials() 0 15 3
A onAuthenticationSuccess() 0 7 1
A supports() 0 6 1
A toPsr() 0 7 1
A getUser() 0 13 3
A supportsRememberMe() 0 3 1
1
<?php
2
3
/**
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 *
7
 * @see https://github.com/ecphp
8
 */
9
10
declare(strict_types=1);
11
12
namespace EcPhp\CasBundle\Security;
13
14
use EcPhp\CasBundle\Security\Core\User\CasUserProviderInterface;
15
use EcPhp\CasLib\CasInterface;
16
use EcPhp\CasLib\Introspection\Contract\ServiceValidate;
17
use EcPhp\CasLib\Utils\Uri;
18
use InvalidArgumentException;
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\ServerRequestInterface;
21
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
22
use Symfony\Component\HttpFoundation\JsonResponse;
23
use Symfony\Component\HttpFoundation\RedirectResponse;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\HttpFoundation\Response;
26
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
27
use Symfony\Component\Security\Core\Exception\AuthenticationException;
28
use Symfony\Component\Security\Core\User\UserInterface;
29
use Symfony\Component\Security\Core\User\UserProviderInterface;
30
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
31
32
final class CasGuardAuthenticator extends AbstractGuardAuthenticator
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Securi...tractGuardAuthenticator has been deprecated: since Symfony 5.3, use the new authenticator system instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

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