_settings = $settings;
$baseURL = $this->_settings->getBaseURL();
if (!empty($baseURL)) {
OneLogin_Saml2_Utils::setBaseURL($baseURL);
}
if (!isset($request) || empty($request)) {
$spData = $this->_settings->getSPData();
$idpData = $this->_settings->getIdPData();
$security = $this->_settings->getSecurityData();
$id = OneLogin_Saml2_Utils::generateUniqueID();
$this->id = $id;
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
$cert = null;
if (isset($security['nameIdEncrypted']) && $security['nameIdEncrypted']) {
$existsMultiX509Enc = isset($idpData['x509certMulti']) && isset($idpData['x509certMulti']['encryption']) && !empty($idpData['x509certMulti']['encryption']);
if ($existsMultiX509Enc) {
$cert = $idpData['x509certMulti']['encryption'][0];
} else {
$cert = $idpData['x509cert'];
}
}
if (!empty($nameId)) {
if (empty($nameIdFormat) &&
$spData['NameIDFormat'] != OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED) {
$nameIdFormat = $spData['NameIDFormat'];
}
} else {
$nameId = $idpData['entityId'];
$nameIdFormat = OneLogin_Saml2_Constants::NAMEID_ENTITY;
}
/* From saml-core-2.0-os 8.3.6, when the entity Format is used:
"The NameQualifier, SPNameQualifier, and SPProvidedID attributes MUST be omitted.
*/
if (!empty($nameIdFormat) && $nameIdFormat == OneLogin_Saml2_Constants::NAMEID_ENTITY) {
$nameIdNameQualifier = null;
$nameIdSPNameQualifier = null;
}
// NameID Format UNSPECIFIED omitted
if (!empty($nameIdFormat) && $nameIdFormat == OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED) {
$nameIdFormat = null;
}
$nameIdObj = OneLogin_Saml2_Utils::generateNameId(
$nameId,
$nameIdSPNameQualifier,
$nameIdFormat,
$cert,
$nameIdNameQualifier
);
$sessionIndexStr = isset($sessionIndex) ? "{$sessionIndex}" : "";
$spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES);
$logoutRequest = <<
{$spEntityId}
{$nameIdObj}
{$sessionIndexStr}
LOGOUTREQUEST;
} else {
$decoded = base64_decode($request);
// We try to inflate
$inflated = @gzinflate($decoded);
if ($inflated != false) {
$logoutRequest = $inflated;
} else {
$logoutRequest = $decoded;
}
$this->id = self::getID($logoutRequest);
}
$this->_logoutRequest = $logoutRequest;
}
/**
* Returns the Logout Request defated, base64encoded, unsigned
*
* @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it.
*
* @return string Deflated base64 encoded Logout Request
*/
public function getRequest($deflate = null)
{
$subject = $this->_logoutRequest;
if (is_null($deflate)) {
$deflate = $this->_settings->shouldCompressRequests();
}
if ($deflate) {
$subject = gzdeflate($this->_logoutRequest);
}
return base64_encode($subject);
}
/**
* Returns the ID of the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
*
* @return string ID
*
* @throws OneLogin_Saml2_Error
*/
public static function getID($request)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
if (false === $dom) {
throw new OneLogin_Saml2_Error(
"LogoutRequest could not be processed",
OneLogin_Saml2_Error::SAML_LOGOUTREQUEST_INVALID
);
}
}
$id = $dom->documentElement->getAttribute('ID');
return $id;
}
/**
* Gets the NameID Data of the the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
* @param string|null $key The SP key
*
* @return array Name ID Data (Value, Format, NameQualifier, SPNameQualifier)
*
* @throws OneLogin_Saml2_Error
* @throws OneLogin_Saml2_ValidationError
*/
public static function getNameIdData($request, $key = null)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$encryptedEntries = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/saml:EncryptedID');
if ($encryptedEntries->length == 1) {
$encryptedDataNodes = $encryptedEntries->item(0)->getElementsByTagName('EncryptedData');
$encryptedData = $encryptedDataNodes->item(0);
if (empty($key)) {
throw new OneLogin_Saml2_Error(
"Private Key is required in order to decrypt the NameID, check settings",
OneLogin_Saml2_Error::PRIVATE_KEY_NOT_FOUND
);
}
$seckey = new XMLSecurityKey(XMLSecurityKey::RSA_1_5, array('type'=>'private'));
$seckey->loadKey($key);
$nameId = OneLogin_Saml2_Utils::decryptElement($encryptedData, $seckey);
} else {
$entries = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/saml:NameID');
if ($entries->length == 1) {
$nameId = $entries->item(0);
}
}
if (!isset($nameId)) {
throw new OneLogin_Saml2_ValidationError(
"NameID not found in the Logout Request",
OneLogin_Saml2_ValidationError::NO_NAMEID
);
}
$nameIdData = array();
$nameIdData['Value'] = $nameId->nodeValue;
foreach (array('Format', 'SPNameQualifier', 'NameQualifier') as $attr) {
if ($nameId->hasAttribute($attr)) {
$nameIdData[$attr] = $nameId->getAttribute($attr);
}
}
return $nameIdData;
}
/**
* Gets the NameID of the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
* @param string|null $key The SP key
*
* @return string Name ID Value
*
* @throws OneLogin_Saml2_Error
* @throws OneLogin_Saml2_ValidationError
*/
public static function getNameId($request, $key = null)
{
$nameId = self::getNameIdData($request, $key);
return $nameId['Value'];
}
/**
* Gets the Issuer of the Logout Request.
*
* @param string|DOMDocument $request Logout Request Message
*
* @return string|null $issuer The Issuer
* @throws Exception
*/
public static function getIssuer($request)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$issuer = null;
$issuerNodes = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/saml:Issuer');
if ($issuerNodes->length == 1) {
$issuer = $issuerNodes->item(0)->textContent;
}
return $issuer;
}
/**
* Gets the SessionIndexes from the Logout Request.
* Notice: Our Constructor only support 1 SessionIndex but this parser
* extracts an array of all the SessionIndex found on a
* Logout Request, that could be many.
*
* @param string|DOMDocument $request Logout Request Message
*
* @return array The SessionIndex value
*
* @throws Exception
*/
public static function getSessionIndexes($request)
{
if ($request instanceof DOMDocument) {
$dom = $request;
} else {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $request);
}
$sessionIndexes = array();
$sessionIndexNodes = OneLogin_Saml2_Utils::query($dom, '/samlp:LogoutRequest/samlp:SessionIndex');
foreach ($sessionIndexNodes as $sessionIndexNode) {
$sessionIndexes[] = $sessionIndexNode->textContent;
}
return $sessionIndexes;
}
/**
* Checks if the Logout Request recieved is valid.
*
* @param bool $retrieveParametersFromServer
*
* @return bool If the Logout Request is or not valid
*/
public function isValid($retrieveParametersFromServer = false)
{
$this->_error = null;
try {
$dom = new DOMDocument();
$dom = OneLogin_Saml2_Utils::loadXML($dom, $this->_logoutRequest);
$idpData = $this->_settings->getIdPData();
$idPEntityId = $idpData['entityId'];
if ($this->_settings->isStrict()) {
$security = $this->_settings->getSecurityData();
if ($security['wantXMLValidation']) {
$res = OneLogin_Saml2_Utils::validateXML($dom, 'saml-schema-protocol-2.0.xsd', $this->_settings->isDebugActive(), $this->_settings->getSchemasPath());
if (!$res instanceof DOMDocument) {
throw new OneLogin_Saml2_ValidationError(
"Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd",
OneLogin_Saml2_ValidationError::INVALID_XML_FORMAT
);
}
}
$currentURL = OneLogin_Saml2_Utils::getSelfRoutedURLNoQuery();
// Check NotOnOrAfter
if ($dom->documentElement->hasAttribute('NotOnOrAfter')) {
$na = OneLogin_Saml2_Utils::parseSAML2Time($dom->documentElement->getAttribute('NotOnOrAfter'));
if ($na <= time()) {
throw new OneLogin_Saml2_ValidationError(
"Could not validate timestamp: expired. Check system clock.",
OneLogin_Saml2_ValidationError::RESPONSE_EXPIRED
);
}
}
// Check destination
if ($dom->documentElement->hasAttribute('Destination')) {
$destination = $dom->documentElement->getAttribute('Destination');
if (empty($destination)) {
if (!$security['relaxDestinationValidation']) {
throw new OneLogin_Saml2_ValidationError(
"The LogoutRequest has an empty Destination value",
OneLogin_Saml2_ValidationError::EMPTY_DESTINATION
);
}
} else {
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURL);
if (strncmp($destination, $currentURL, $urlComparisonLength) !== 0) {
$currentURLNoRouted = OneLogin_Saml2_Utils::getSelfURLNoQuery();
$urlComparisonLength = $security['destinationStrictlyMatches'] ? strlen($destination) : strlen($currentURLNoRouted);
if (strncmp($destination, $currentURLNoRouted, $urlComparisonLength) !== 0) {
throw new OneLogin_Saml2_ValidationError(
"The LogoutRequest was received at $currentURL instead of $destination",
OneLogin_Saml2_ValidationError::WRONG_DESTINATION
);
}
}
}
}
$nameId = static::getNameId($dom, $this->_settings->getSPkey());
// Check issuer
$issuer = static::getIssuer($dom);
if (!empty($issuer) && $issuer != $idPEntityId) {
throw new OneLogin_Saml2_ValidationError(
"Invalid issuer in the Logout Request",
OneLogin_Saml2_ValidationError::WRONG_ISSUER
);
}
if ($security['wantMessagesSigned'] && !isset($_GET['Signature'])) {
throw new OneLogin_Saml2_ValidationError(
"The Message of the Logout Request is not signed and the SP require it",
OneLogin_Saml2_ValidationError::NO_SIGNED_MESSAGE
);
}
}
if (isset($_GET['Signature'])) {
$signatureValid = OneLogin_Saml2_Utils::validateBinarySign("SAMLRequest", $_GET, $idpData, $retrieveParametersFromServer);
if (!$signatureValid) {
throw new OneLogin_Saml2_ValidationError(
"Signature validation failed. Logout Request rejected",
OneLogin_Saml2_ValidationError::INVALID_SIGNATURE
);
}
}
return true;
} catch (Exception $e) {
$this->_error = $e->getMessage();
$debug = $this->_settings->isDebugActive();
if ($debug) {
echo htmlentities($this->_error);
}
return false;
}
}
/**
* After execute a validation process, if fails this method returns the cause
*
* @return string Cause
*/
public function getError()
{
return $this->_error;
}
/**
* Returns the XML that will be sent as part of the request
* or that was received at the SP
*
* @return string
*/
public function getXML()
{
return $this->_logoutRequest;
}
}