Skip to content

Commit

Permalink
receive logout response and session termination
Browse files Browse the repository at this point in the history
  • Loading branch information
tmilos committed Nov 17, 2016
1 parent e7b047d commit 512289b
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .php_cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ EOT;
Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header);

return Symfony\CS\Config\Config::create()
->setUsingCache(true)
->setUsingCache(false)
->level(Symfony\CS\FixerInterface::SYMFONY_LEVEL)
->fixers(array('-empty_return', '-phpdoc_no_empty_return', 'header_comment'))
->finder($finder)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

/*
* This file is part of the LightSAML-Logout package.
*
* (c) Milos Tomic <tmilos@lightsaml.com>
*
* This source file is subject to the GPL-3 license that is bundled
* with this source code in the file LICENSE.
*/

namespace LightSaml\Logout\Action\Profile\Inbound\LogoutResponse;

use LightSaml\Action\Profile\AbstractProfileAction;
use LightSaml\Context\Profile\Helper\LogHelper;
use LightSaml\Context\Profile\Helper\MessageContextHelper;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Error\LightSamlContextException;
use LightSaml\Logout\Resolver\Logout\LogoutSessionResolverInterface;
use LightSaml\State\Request\RequestStateParameters;
use LightSaml\Store\Request\RequestStateStoreInterface;
use Psr\Log\LoggerInterface;

class RemoveSsoSessionFromStoreAction extends AbstractProfileAction
{
/** @var RequestStateStoreInterface */
private $requestStore;

/** @var LogoutSessionResolverInterface */
private $logoutResolver;

/**
* @param LoggerInterface $logger
* @param RequestStateStoreInterface $requestStore
* @param LogoutSessionResolverInterface $logoutResolver
*/
public function __construct(LoggerInterface $logger, RequestStateStoreInterface $requestStore, LogoutSessionResolverInterface $logoutResolver)
{
parent::__construct($logger);

$this->requestStore = $requestStore;
$this->logoutResolver = $logoutResolver;
}

protected function doExecute(ProfileContext $context)
{
$logoutResponse = MessageContextHelper::asLogoutResponse($context->getInboundContext());
$id = $logoutResponse->getInResponseTo();
$requestState = $this->requestStore->get($id);
$partyEntityId = $requestState->getParameters()->get(RequestStateParameters::PARTY);
if ($partyEntityId && $logoutResponse->getIssuer() && $partyEntityId != $logoutResponse->getIssuer()->getValue()) {
$message = sprintf(
'LogoutRequest sent to %s but LogoutResponse for that request was issued by %s',
$partyEntityId,
$logoutResponse->getIssuer()->getValue()
);
$this->logger->critical($message, LogHelper::getActionErrorContext($context, $this, [
'sent_to' => $partyEntityId,
'received_from' => $logoutResponse->getIssuer()->getValue(),
]));
throw new LightSamlContextException($context, $message);
}

$nameId = $requestState->getParameters()->get(RequestStateParameters::NAME_ID);
$nameIdFormat = $requestState->getParameters()->get(RequestStateParameters::NAME_ID_FORMAT);
$sessionIndex = $requestState->getParameters()->get(RequestStateParameters::SESSION_INDEX);

$numberOfTerminatedSessions = $this->logoutResolver->terminateSession(
$logoutResponse->getIssuer()->getValue(),
$nameId,
$nameIdFormat,
$sessionIndex
);

$this->logger->debug(
sprintf(
'Processing LogoutResponse from %s for %s in format %s and session index %s resulted in termination of %s sso session from the store',
$partyEntityId,
$nameId,
$nameIdFormat,
$sessionIndex,
$numberOfTerminatedSessions
),
LogHelper::getActionContext($context, $this)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@

use LightSaml\Action\ActionInterface;
use LightSaml\Action\Profile\AbstractProfileAction;
use LightSaml\Context\Profile\Helper\LogHelper;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Logout\Resolver\Logout\LogoutSessionResolverInterface;
use Psr\Log\LoggerInterface;

class LogoutResolveAction extends AbstractProfileAction
{
/** @var LogoutSessionResolverInterface */
/** @var LogoutSessionResolverInterface */
protected $logoutSessionResolver;

/** @var ActionInterface */
/** @var ActionInterface */
protected $logoutProceedAction;

/**
Expand All @@ -46,12 +47,37 @@ public function __construct(
*/
protected function doExecute(ProfileContext $context)
{
$ssoSessionState = $this->logoutSessionResolver->resolve($context->getOwnEntityDescriptor()->getEntityID());
$logoutContext = $context->getLogoutContext();
$ssoSessionState = $logoutContext->getSsoSessionState();
if ($ssoSessionState) {
$this->logger->debug(
'SSO session already set',
LogHelper::getActionContext($context, $this, array(
'sso_session' => $ssoSessionState,
))
);
} else {
$this->logger->debug(
'SSO session not defined, about to resolve it',
LogHelper::getActionContext($context, $this, array())
);
$ssoSessionState = $this->logoutSessionResolver->resolve($context->getOwnEntityDescriptor()->getEntityID());
}

if ($ssoSessionState) {
$this->logger->debug(
'SSO session resolved and being used for logout profile',
LogHelper::getActionContext($context, $this, array(
'sso_session' => $ssoSessionState,
))
);
$logoutContext->setSsoSessionState($ssoSessionState);
$this->logoutProceedAction->execute($context);
} else {
$this->logger->debug(
'There is no SSO session for logout',
LogHelper::getActionContext($context, $this, array())
);
$logoutContext->setAllSsoSessionsTerminated(true);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ResolveLogoutPartyAction extends AbstractProfileAction
/** @var EntityDescriptorStoreInterface */
private $spEntityDescriptorStore;

/** @var TrustOptionsStoreInterface */
/** @var TrustOptionsStoreInterface */
protected $trustOptionsProvider;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
*/
class SetNotOnOrAfterAction extends AbstractProfileAction
{
/** @var TimeProviderInterface */
/** @var TimeProviderInterface */
protected $timeProvider;

/** @var int */
/** @var int */
protected $secondsSkew;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use LightSaml\Action\Profile\Outbound\Message\CreateMessageIssuerAction;
use LightSaml\Action\Profile\Outbound\Message\DestinationAction;
use LightSaml\Action\Profile\Outbound\Message\ResolveEndpointSloAction;
use LightSaml\Action\Profile\Outbound\Message\SaveRequestStateAction;
use LightSaml\Logout\Action\Profile\Outbound\LogoutRequest\CreateLogoutRequestAction;
use LightSaml\Logout\Action\Profile\Outbound\LogoutRequest\LogoutResolveAction;
use LightSaml\Logout\Action\Profile\Outbound\LogoutRequest\ResolveLogoutPartyAction;
Expand All @@ -31,8 +32,6 @@

class SloRequestActionBuilder extends AbstractProfileActionBuilder
{
/**
*/
protected function doInitialize()
{
$proceedActionBuilder = new CompositeActionBuilder();
Expand Down Expand Up @@ -77,6 +76,11 @@ protected function doInitialize()
$this->buildContainer->getSystemContainer()->getTimeProvider(),
120
));
$proceedActionBuilder->add(new SaveRequestStateAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getStoreContainer()->getRequestStateStore()
));

$proceedActionBuilder->add(new SignMessageAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getServiceContainer()->getSignatureResolver()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

/*
* This file is part of the LightSAML-Logout package.
*
* (c) Milos Tomic <tmilos@lightsaml.com>
*
* This source file is subject to the GPL-3 license that is bundled
* with this source code in the file LICENSE.
*/

namespace LightSaml\Logout\Builder\Action\Profile\SingleLogout;

use LightSaml\Action\Profile\FlushRequestStatesAction;
use LightSaml\Action\Profile\Inbound\Message\EntityIdFromMessageIssuerAction;
use LightSaml\Action\Profile\Inbound\Message\IssuerValidatorAction;
use LightSaml\Action\Profile\Inbound\Message\MessageSignatureValidatorAction;
use LightSaml\Action\Profile\Inbound\Message\ReceiveMessageAction;
use LightSaml\Action\Profile\Inbound\Message\ResolvePartyEntityIdAction;
use LightSaml\Action\Profile\Inbound\StatusResponse\InResponseToValidatorAction;
use LightSaml\Action\Profile\Inbound\StatusResponse\StatusAction;
use LightSaml\Builder\Action\Profile\AbstractProfileActionBuilder;
use LightSaml\Logout\Action\Profile\Inbound\LogoutResponse\RemoveSsoSessionFromStoreAction;
use LightSaml\SamlConstants;

class SloResponseActionBuilder extends AbstractProfileActionBuilder
{
protected function doInitialize()
{
$this->add(new ReceiveMessageAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getServiceContainer()->getBindingFactory()
), 100);

// Response validation
$this->add(new IssuerValidatorAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getServiceContainer()->getNameIdValidator(),
SamlConstants::NAME_ID_FORMAT_ENTITY
), 200);
$this->add(new EntityIdFromMessageIssuerAction(
$this->buildContainer->getSystemContainer()->getLogger()
));
$this->add(new ResolvePartyEntityIdAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getPartyContainer()->getSpEntityDescriptorStore(),
$this->buildContainer->getPartyContainer()->getIdpEntityDescriptorStore(),
$this->buildContainer->getPartyContainer()->getTrustOptionsStore()
));
$this->add(new InResponseToValidatorAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getStoreContainer()->getRequestStateStore()
));
$this->add(new StatusAction(
$this->buildContainer->getSystemContainer()->getLogger()
));
$this->add(new MessageSignatureValidatorAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getServiceContainer()->getSignatureValidator()
));
$this->add(new RemoveSsoSessionFromStoreAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getStoreContainer()->getRequestStateStore(),
$this->buildContainer->getServiceContainer()->getLogoutSessionResolver()
));
$this->add(new FlushRequestStatesAction(
$this->buildContainer->getSystemContainer()->getLogger(),
$this->buildContainer->getStoreContainer()->getRequestStateStore()
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of the LightSAML-Logout package.
*
* (c) Milos Tomic <tmilos@lightsaml.com>
*
* This source file is subject to the GPL-3 license that is bundled
* with this source code in the file LICENSE.
*/

namespace LightSaml\Logout\Builder\Profile\WebBrowserSlo;

use LightSaml\Builder\Profile\AbstractProfileBuilder;
use LightSaml\Context\Profile\ProfileContext;
use LightSaml\Logout\Builder\Action\Profile\SingleLogout\SloResponseActionBuilder;
use LightSaml\Logout\Profile\Profiles;

class SloResponseProfileBuilder extends AbstractProfileBuilder
{
/**
* @return string
*/
protected function getProfileId()
{
return Profiles::SLO_RECEIVE_LOGOUT_RESPONSE;
}

/**
* @return string
*/
protected function getProfileRole()
{
return ProfileContext::ROLE_NONE;
}

/**
* @return \LightSaml\Builder\Action\ActionBuilderInterface
*/
protected function getActionBuilder()
{
return new SloResponseActionBuilder($this->container);
}
}
1 change: 1 addition & 0 deletions src/LightSaml/Logout/Profile/Profiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
class Profiles
{
const SLO_SEND_LOGOUT_REQUEST = 'slo_send_logout_request';
const SLO_RECEIVE_LOGOUT_RESPONSE = 'slo_receive_logout_response';

private function __construct()
{
Expand Down
26 changes: 25 additions & 1 deletion src/LightSaml/Logout/Resolver/Logout/LogoutSessionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

class LogoutSessionResolver implements LogoutSessionResolverInterface
{
/** @var SsoStateStoreInterface */
/** @var SsoStateStoreInterface */
protected $ssoStateStore;

/**
Expand Down Expand Up @@ -47,6 +47,30 @@ public function resolve($ownEntityId)
return $result;
}

public function terminateSession($entityId, $nameId, $nameIdFormat, $sessionIndex = null)
{
$ssoState = $this->ssoStateStore->get();

$count = 0;

$ssoState->modify(function (SsoSessionState $session) use ($entityId, $nameId, $nameIdFormat, &$count) {
if (($session->getIdpEntityId() == $entityId || $session->getSpEntityId() == $entityId) &&
$session->getNameId() == $nameId &&
$session->getNameIdFormat() == $nameIdFormat
) {
++$count;

return false;
}

return true;
});

$this->ssoStateStore->set($ssoState);

return $count;
}

/**
* @param SsoState $ssoState
* @param string $ownEntityId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,14 @@ interface LogoutSessionResolverInterface
* @return SsoSessionState|null
*/
public function resolve($ownEntityId);

/**
* @param string $entityId
* @param string $nameId
* @param string $nameIdFormat
* @param string $sessionIndex
*
* @return int Number of sso sessions terminated for given arguments
*/
public function terminateSession($entityId, $nameId, $nameIdFormat, $sessionIndex = null);
}

0 comments on commit 512289b

Please sign in to comment.