Open junowilderness opened 6 years ago
Here is an example in PHP from Drupal: https://cgit.drupalcode.org/cas/tree/cas.module?h=7.x-1.x#n1111 Drupal stores sessions in a database by default, but the principle would be the same.
deleted
Here is a working example for the basic case where sessions are stored on the filesystem. The hosting I use has a shared filesystem so there is only one file to delete.
There isn't a one-size-fits-all way to code the session deletion, but we could type hint an interface for doing so and implementations could do whatever is needed to delete a session, such as a database query, Redis, or whatever.
<?php
namespace App\Security\Cas;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class SingleLogoutEventSubscriber implements EventSubscriberInterface
{
/**
* @var GetResponseEvent
*/
protected $event;
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['singleLogout']
];
}
public function singleLogout(GetResponseEvent $event)
{
if ($event->getRequest()->get("logoutRequest", false)) {
$this->event = $event;
$this->processLogoutRequest();
}
}
protected function processLogoutRequest(): void
{
$xmlString = utf8_encode(urldecode($this->event->getRequest()->get("logoutRequest")));
$xmlData = new \SimpleXMLElement($xmlString);
if (is_object($xmlData)) {
$this->processXmlData($xmlData);
}
}
protected function processXmlData($xmlData): void
{
$sessions = $this->extractSessions($xmlData);
if ($sessions) {
$this->terminateSessions($sessions);
}
}
protected function extractSessions($xmlData)
{
$namespaces = $xmlData->getNameSpaces();
if (isset($namespaces['samlp'])) {
$sessions = $xmlData->children($namespaces['samlp'])->SessionIndex;
} else {
$sessions = $xmlData->xpath('SessionIndex');
}
return $sessions;
}
protected function terminateSessions($sessions): void
{
$sessionId = (string) $sessions[0];
$file = session_save_path().'/sess_'.trim($sessionId);
if (file_exists($file)) {
unlink($file);
}
$this->event->setResponse(new Response());
}
}
It should be possible to handle single logout signals from CAS servers as documented in the spec. This could perhaps be something that could be activated in the service container optionally.