user_saml/3rdparty/vendor/onelogin/php-saml/lib/Saml2/LogoutResponse.php
Lukas Reschke 04c7b4187a
Bump to 2.10.5
Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
2017-03-16 16:27:07 +01:00

332 lines
11 KiB
PHP

<?php
/**
* SAML 2 Logout Response
*
*/
class OneLogin_Saml2_LogoutResponse
{
/**
* Contains the ID of the Logout Response
* @var string
*/
public $id;
/**
* Object that represents the setting info
* @var OneLogin_Saml2_Settings
*/
protected $_settings;
/**
* The decoded, unprocessed XML response provided to the constructor.
* @var string
*/
protected $_logoutResponse;
/**
* A DOMDocument class loaded from the SAML LogoutResponse.
* @var DomDocument
*/
public $document;
/**
* After execute a validation process, if it fails, this var contains the cause
* @var string
*/
private $_error;
/**
* Constructs a Logout Response object (Initialize params from settings and if provided
* load the Logout Response.
*
* @param OneLogin_Saml2_Settings $settings Settings.
* @param string|null $response An UUEncoded SAML Logout response from the IdP.
*/
public function __construct(OneLogin_Saml2_Settings $settings, $response = null)
{
$this->_settings = $settings;
$baseURL = $this->_settings->getBaseURL();
if (!empty($baseURL)) {
OneLogin_Saml2_Utils::setBaseURL($baseURL);
}
if ($response) {
$decoded = base64_decode($response);
$inflated = @gzinflate($decoded);
if ($inflated != false) {
$this->_logoutResponse = $inflated;
} else {
$this->_logoutResponse = $decoded;
}
$this->document = new DOMDocument();
$this->document = OneLogin_Saml2_Utils::loadXML($this->document, $this->_logoutResponse);
if ($this->document->documentElement->hasAttribute('ID')) {
$this->id = $this->document->documentElement->getAttribute('ID');
}
}
}
/**
* Gets the Issuer of the Logout Response.
*
* @return string|null $issuer The Issuer
*/
public function getIssuer()
{
$issuer = null;
$issuerNodes = $this->_query('/samlp:LogoutResponse/saml:Issuer');
if ($issuerNodes->length == 1) {
$issuer = $issuerNodes->item(0)->textContent;
}
return $issuer;
}
/**
* Gets the Status of the Logout Response.
*
* @return string The Status
*/
public function getStatus()
{
$entries = $this->_query('/samlp:LogoutResponse/samlp:Status/samlp:StatusCode');
if ($entries->length != 1) {
return null;
}
$status = $entries->item(0)->getAttribute('Value');
return $status;
}
/**
* Determines if the SAML LogoutResponse is valid
*
* @param string|null $requestId The ID of the LogoutRequest sent by this SP to the IdP
* @param bool $retrieveParametersFromServer
*
* @return bool Returns if the SAML LogoutResponse is or not valid
*
* @throws Exception
*/
public function isValid($requestId = null, $retrieveParametersFromServer = false)
{
$this->_error = null;
try {
$idpData = $this->_settings->getIdPData();
$idPEntityId = $idpData['entityId'];
if ($this->_settings->isStrict()) {
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$res = OneLogin_Saml2_Utils::validateXML($this->document, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive());
if (!$res instanceof DOMDocument) {
throw new OneLogin_Saml2_ValidationError(
"Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd",
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
}
// Check if the InResponseTo of the Logout Response matchs the ID of the Logout Request (requestId) if provided
if (isset($requestId) && $this->document->documentElement->hasAttribute('InResponseTo')) {
$inResponseTo = $this->document->documentElement->getAttribute('InResponseTo');
if ($requestId != $inResponseTo) {
throw new OneLogin_Saml2_ValidationError(
"The InResponseTo of the Logout Response: $inResponseTo, does not match the ID of the Logout request sent by the SP: $requestId",
OneLogin_Saml2_ValidationError::WRONG_INRESPONSETO
);
}
}
// Check issuer
$issuer = $this->getIssuer();
if (!empty($issuer) && $issuer != $idPEntityId) {
throw new OneLogin_Saml2_ValidationError(
"Invalid issuer in the Logout Response",
OneLogin_Saml2_ValidationError::WRONG_ISSUER
);
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
// Check destination
if ($this->document->documentElement->hasAttribute('Destination')) {
$destination = $this->document->documentElement->getAttribute('Destination');
if (!empty($destination)) {
if (strpos($destination, $currentURL) === false) {
throw new OneLogin_Saml2_ValidationError(
"The LogoutResponse was received at $currentURL instead of $destination",
OneLogin_Saml2_ValidationError::WRONG_DESTINATION
);
}
}
}
if ($security['wantMessagesSigned']) {
if (!isset($_GET['Signature'])) {
throw new OneLogin_Saml2_ValidationError(
"The Message of the Logout Response is not signed and the SP requires it",
OneLogin_Saml2_ValidationError::NO_SIGNED_MESSAGE
);
}
}
}
if (isset($_GET['Signature'])) {
if (!isset($_GET['SigAlg'])) {
$signAlg = XMLSecurityKey::RSA_SHA1;
} else {
$signAlg = $_GET['SigAlg'];
}
if ($retrieveParametersFromServer) {
$signedQuery = 'SAMLResponse='.OneLogin_Saml2_Utils::extractOriginalQueryParam('SAMLResponse');
if (isset($_GET['RelayState'])) {
$signedQuery .= '&RelayState='.OneLogin_Saml2_Utils::extractOriginalQueryParam('RelayState');
}
$signedQuery .= '&SigAlg='.OneLogin_Saml2_Utils::extractOriginalQueryParam('SigAlg');
} else {
$signedQuery = 'SAMLResponse='.urlencode($_GET['SAMLResponse']);
if (isset($_GET['RelayState'])) {
$signedQuery .= '&RelayState='.urlencode($_GET['RelayState']);
}
$signedQuery .= '&SigAlg='.urlencode($signAlg);
}
if (!isset($idpData['x509cert']) || empty($idpData['x509cert'])) {
throw new OneLogin_Saml2_Error(
"In order to validate the sign on the Logout Response, the x509cert of the IdP is required",
OneLogin_Saml2_Error::CERT_NOT_FOUND
);
}
$cert = $idpData['x509cert'];
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'public'));
$objKey->loadKey($cert, false, true);
if ($signAlg != XMLSecurityKey::RSA_SHA1) {
try {
$objKey = OneLogin_Saml2_Utils::castKey($objKey, $signAlg, 'public');
} catch (Exception $e) {
throw new OneLogin_Saml2_ValidationError(
"Invalid signAlg in the recieved Logout Response",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
if ($objKey->verifySignature($signedQuery, base64_decode($_GET['Signature'])) !== 1) {
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. Logout Response rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
return true;
} catch (Exception $e) {
$this->_error = $e->getMessage();
$debug = $this->_settings->isDebugActive();
if ($debug) {
echo $this->_error;
}
return false;
}
}
/**
* Extracts a node from the DOMDocument (Logout Response Menssage)
*
* @param string $query Xpath Expresion
*
* @return DOMNodeList The queried node
*/
private function _query($query)
{
return OneLogin_Saml2_Utils::query($this->document, $query);
}
/**
* Generates a Logout Response object.
*
* @param string $inResponseTo InResponseTo value for the Logout Response.
*/
public function build($inResponseTo)
{
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$this->id = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$logoutResponse = <<<LOGOUTRESPONSE
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="{$this->id}"
Version="2.0"
IssueInstant="{$issueInstant}"
Destination="{$idpData['singleLogoutService']['url']}"
InResponseTo="{$inResponseTo}"
>
<saml:Issuer>{$spData['entityId']}</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>
</samlp:LogoutResponse>
LOGOUTRESPONSE;
$this->_logoutResponse = $logoutResponse;
}
/**
* Returns a Logout Response object.
*
* @param bool|null $deflate Whether or not we should 'gzdeflate' the response body before we return it.
*
* @return string Logout Response deflated and base64 encoded
*/
public function getResponse($deflate = null)
{
$subject = $this->_logoutResponse;
if (is_null($deflate)) {
$deflate = $this->_settings->shouldCompressResponses();
}
if ($deflate) {
$subject = gzdeflate($this->_logoutResponse);
}
return base64_encode($subject);
}
/* After execute a validation process, if fails this method returns the cause.
*
* @return string Cause
*/
public function getError()
{
return $this->_error;
}
/**
* @return the ID of the Response
*/
public function getId()
{
return $this->id;
}
/**
* Returns the XML that will be sent as part of the response
* or that was received at the SP
*
* @return string
*/
public function getXML()
{
return $this->_logoutResponse;
}
}