Passed
Push — 1.0 ( a44497...ab48d1 )
by Pol
13:07
created

CasGuardAuthenticator::toPsr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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