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 |
|
return null; |
123
|
|
|
} |
124
|
1 |
|
|
125
|
|
|
/** |
126
|
1 |
|
* {@inheritdoc} |
127
|
1 |
|
*/ |
128
|
1 |
|
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey) |
129
|
|
|
{ |
130
|
1 |
|
return new RedirectResponse( |
131
|
|
|
(string) Uri::removeParams( |
132
|
|
|
$this->toPsr($request)->getUri(), |
133
|
|
|
'ticket', |
134
|
1 |
|
'renew' |
135
|
|
|
) |
136
|
1 |
|
); |
137
|
|
|
} |
138
|
|
|
|
139
|
1 |
|
/** |
140
|
|
|
* {@inheritdoc} |
141
|
|
|
*/ |
142
|
|
|
public function onLogoutSuccess(Request $request) |
143
|
|
|
{ |
144
|
|
|
$response = $this |
145
|
1 |
|
->cas |
146
|
|
|
->logout(); |
147
|
1 |
|
|
148
|
1 |
|
if (null === $response) { |
149
|
1 |
|
throw new AuthenticationException('Unable to trigger the logout procedure'); |
150
|
1 |
|
} |
151
|
|
|
|
152
|
1 |
|
return new RedirectResponse( |
153
|
1 |
|
$response |
154
|
|
|
->getHeaderLine('location') |
155
|
|
|
); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* {@inheritdoc} |
160
|
|
|
*/ |
161
|
1 |
|
public function start(Request $request, ?AuthenticationException $authException = null) |
162
|
|
|
{ |
163
|
|
|
if (true === $request->isXmlHttpRequest()) { |
164
|
1 |
|
return new JsonResponse( |
165
|
1 |
|
['message' => 'Authentication required'], |
166
|
|
|
Response::HTTP_UNAUTHORIZED |
167
|
1 |
|
); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
$response = $this |
171
|
1 |
|
->cas |
172
|
|
|
->login(); |
173
|
1 |
|
|
174
|
|
|
if (null === $response) { |
175
|
|
|
throw new AuthenticationException('Unable to trigger the login procedure'); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
return new RedirectResponse( |
179
|
|
|
$response |
180
|
2 |
|
->getHeaderLine('location') |
181
|
|
|
); |
182
|
2 |
|
} |
183
|
1 |
|
|
184
|
1 |
|
/** |
185
|
1 |
|
* {@inheritdoc} |
186
|
|
|
*/ |
187
|
|
|
public function supports(Request $request) |
188
|
|
|
{ |
189
|
|
|
return $this |
190
|
1 |
|
->cas |
191
|
1 |
|
->withServerRequest($this->toPsr($request)) |
192
|
|
|
->supportAuthentication(); |
193
|
1 |
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* {@inheritdoc} |
197
|
1 |
|
*/ |
198
|
|
|
public function supportsRememberMe() |
199
|
1 |
|
{ |
200
|
|
|
return false; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Convert a Symfony request into a PSR Request. |
205
|
|
|
* |
206
|
2 |
|
* @param \Symfony\Component\HttpFoundation\Request $request |
207
|
|
|
* The Symfony request. |
208
|
|
|
* |
209
|
2 |
|
* @return \Psr\Http\Message\ServerRequestInterface |
210
|
2 |
|
* The PSR request. |
211
|
|
|
*/ |
212
|
2 |
|
private function toPsr(Request $request): ServerRequestInterface |
213
|
2 |
|
{ |
214
|
2 |
|
// As we cannot decorate the Symfony Request object, we convert it into |
215
|
2 |
|
// a PSR Request so we can override the PSR HTTP Message factory if |
216
|
|
|
// needed. |
217
|
|
|
// See the reasons at https://github.com/ecphp/cas-lib/issues/5 |
218
|
2 |
|
return $this->httpMessageFactory->createRequest($request); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.