<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class CasAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterface
{
protected $userProvider;
protected $casUrl;
public function __construct(CasUserProvider $userProvider, string $casUrl)
{
$this->userProvider = $userProvider;
$this->casUrl = $casUrl;
}
public function supports(Request $request): bool
{
return (bool) $request->get('ticket');
}
public function authenticate(Request $request): Passport
{
$url = $this->casUrl . 'serviceValidate?ticket=' . $request->get('ticket') . '&service=' . urlencode($this->removeCasTicket($request->getUri()));
$client = HttpClient::create();
$response = $client->request('GET', $url);
$string = $response->getContent();
$xml = new \SimpleXMLElement($string, 0, false, 'cas', true);
if (isset($xml->authenticationSuccess)) {
$data = (array) $xml->authenticationSuccess;
$username = $data['user'];
$user = $this->userProvider->loadUserByIdentifier($username);
return new SelfValidatingPassport(new UserBadge($user->getUserIdentifier()));
} else {
throw new CustomUserMessageAuthenticationException('Ticket not recognized');
}
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse($this->casUrl . 'login?service=' . urlencode($request->getUri()));
}
protected function removeCasTicket(string $uri): string
{
$parsed_url = parse_url($uri);
if (empty($parsed_url['query'])) {
return $uri;
}
parse_str($parsed_url['query'], $query_params);
if (! isset($query_params['ticket'])) {
return $uri;
}
unset($query_params['ticket']);
if (empty($query_params)) {
unset($parsed_url['query']);
} else {
$parsed_url['query'] = http_build_query($query_params);
}
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
}