Skip to content
This repository was archived by the owner on Jan 5, 2022. It is now read-only.

Commit

Permalink
Adjusted acs endpoint to extract NameQualifier and SPNameQualifier fr…
Browse files Browse the repository at this point in the history
…om SAMLResponse. Adjusted single logout service to provide NameQualifier and SPNameQualifier to logout method. Add getNameIdNameQualifier to Auth and SamlResponse. Extend logout method from Auth and LogoutRequest constructor to support SPNameQualifier parameter. Align LogoutRequest constructor with SAML specs
  • Loading branch information
pitbulk committed Jun 13, 2019
1 parent f34e85f commit 4348bd7
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 14 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ if (!$auth->isAuthenticated()) {
$_SESSION['samlUserdata'] = $auth->getAttributes();
$_SESSION['samlNameId'] = $auth->getNameId();
$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat();
$_SESSION['samlNameidNameQualifier' = $auth->getNameIdNameQualifier();
$_SESSION['samlNameidSPNameQualifier' = $auth->getNameIdSPNameQualifier();
$_SESSION['samlSessionIndex'] = $auth->getSessionIndex();

if (isset($_POST['RelayState']) && OneLogin_Saml2_Utils::getSelfURL() != $_POST['RelayState']) {
Expand Down Expand Up @@ -980,14 +982,16 @@ $auth = new OneLogin_Saml2_Auth();
$auth->logout(); // Method that sent the Logout Request.
```

Also there are six optional parameters that can be set:
Also there are eight optional parameters that can be set:
* `$returnTo` - The target URL the user should be returned to after logout.
* `$parameters` - Extra parameters to be added to the GET.
* `$name_id` - That will be used to build the LogoutRequest. If `name_id` parameter is not set and the auth object processed a
SAML Response with a `NameId`, then this `NameId` will be used.
* `$session_index` - SessionIndex that identifies the session of the user.
* `$stay` - True if we want to stay (returns the url string) False to redirect.
* `$nameIdFormat` - The NameID Format will be set in the LogoutRequest.
* `$nameIdNameQualifier` - The NameID NameQualifier will be set in the LogoutRequest.
* `$nameIdSPNameQualifier` - The NameID SP NameQualifier will be set in the LogoutRequest.

The Logout Request will be sent signed or unsigned based on the security
info of the `advanced_settings.php` (`'logoutRequestSigned'`).
Expand All @@ -1014,6 +1018,9 @@ $paramters = array();
$nameId = null;
$sessionIndex = null;
$nameIdFormat = null;
$nameIdNameQualifier = null;
$nameIdSPNameQualifier = null;
if (isset($_SESSION['samlNameId'])) {
$nameId = $_SESSION['samlNameId'];
}
Expand All @@ -1023,7 +1030,13 @@ if (isset($_SESSION['samlSessionIndex'])) {
if (isset($_SESSION['samlNameIdFormat'])) {
$nameIdFormat = $_SESSION['samlNameIdFormat'];
}
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat);
if (isset($_SESSION['samlNameIdNameQualifier'])) {
$nameIdNameQualifier = $_SESSION['samlNameIdNameQualifier'];
}
if (isset($_SESSION['samlNameIdSPNameQualifier'])) {
$nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier'];
}
$auth->logout($returnTo, $paramters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);
```

If a match on the future LogoutResponse ID and the LogoutRequest ID to be sent is required, that LogoutRequest ID must to be extracted and stored.
Expand Down Expand Up @@ -1282,6 +1295,9 @@ Main class of OneLogin PHP Toolkit
* `getAttributes` - Returns the set of SAML attributes.
* `getAttribute` - Returns the requested SAML attribute
* `getNameId` - Returns the nameID
* `getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP.
* `getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String.
* `getNameIdNameSPQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String.
* `getSessionIndex` - Gets the SessionIndex from the AuthnStatement.
* `getErrors` - Returns if there were any error
* `getSSOurl` - Gets the SSO url.
Expand Down Expand Up @@ -1318,6 +1334,8 @@ SAML 2 Authentication Response class
IdP.
* `getNameId` - Gets the NameID provided by the SAML response from the IdP.
* `getNameIdFormat` - Gets the NameID Format provided by the SAML response from the IdP.
* `getNameIdNameQualifier` - Gets the NameID NameQualifier provided from the SAML Response String.
* `getNameIdNameSPQualifier` - Gets the NameID SP NameQualifier provided from the SAML Response String.
* `getSessionNotOnOrAfter` - Gets the SessionNotOnOrAfter from the
AuthnStatement
* `getSessionIndex` - Gets the SessionIndex from the AuthnStatement.
Expand Down
16 changes: 12 additions & 4 deletions demo1/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,20 @@
if (isset($_SESSION['samlNameId'])) {
$nameId = $_SESSION['samlNameId'];
}
if (isset($_SESSION['samlSessionIndex'])) {
$sessionIndex = $_SESSION['samlSessionIndex'];
}
if (isset($_SESSION['samlNameIdFormat'])) {
$nameIdFormat = $_SESSION['samlNameIdFormat'];
}
if (isset($_SESSION['samlNameIdNameQualifier'])) {
$nameIdNameQualifier = $_SESSION['samlNameIdNameQualifier'];
}
if (isset($_SESSION['samlNameIdSPNameQualifier'])) {
$nameIdSPNameQualifier = $_SESSION['samlNameIdSPNameQualifier'];
}
if (isset($_SESSION['samlSessionIndex'])) {
$sessionIndex = $_SESSION['samlSessionIndex'];
}

$auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat);
$auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);

# If LogoutRequest ID need to be saved in order to later validate it, do instead
# $sloBuiltUrl = $auth->logout(null, $paramters, $nameId, $sessionIndex, true);
Expand Down Expand Up @@ -75,6 +81,8 @@
$_SESSION['samlUserdata'] = $auth->getAttributes();
$_SESSION['samlNameId'] = $auth->getNameId();
$_SESSION['samlNameIdFormat'] = $auth->getNameIdFormat();
$_SESSION['samlNameIdNameQualifier'] = $auth->getNameIdNameQualifier();
$_SESSION['samlNameIdSPNameQualifier'] = $auth->getNameIdSPNameQualifier();
$_SESSION['samlSessionIndex'] = $auth->getSessionIndex();
unset($_SESSION['AuthNRequestID']);
if (isset($_POST['RelayState']) && OneLogin_Saml2_Utils::getSelfURL() != $_POST['RelayState']) {
Expand Down
24 changes: 21 additions & 3 deletions lib/Saml2/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class OneLogin_Saml2_Auth
*/
private $_nameidNameQualifier;

/**
* NameID SP NameQualifier
*
* @var string
*/
private $_nameidSPNameQualifier;

/**
* If user is authenticated.
*
Expand Down Expand Up @@ -197,6 +204,7 @@ public function processResponse($requestId = null)
$this->_nameid = $response->getNameId();
$this->_nameidFormat = $response->getNameIdFormat();
$this->_nameidNameQualifier = $response->getNameIdNameQualifier();
$this->_nameidSPNameQualifier = $response->getNameIdSPNameQualifier();
$this->_authenticated = true;
$this->_sessionIndex = $response->getSessionIndex();
$this->_sessionExpiration = $response->getSessionNotOnOrAfter();
Expand Down Expand Up @@ -380,6 +388,16 @@ public function getNameIdNameQualifier()
return $this->_nameidNameQualifier;
}

/**
* Returns the nameID SP NameQualifier
*
* @return string The nameID SP NameQualifier of the assertion
*/
public function getNameIdSPNameQualifier()
{
return $this->_nameidSPNameQualifier;
}

/**
* Returns the SessionIndex
*
Expand Down Expand Up @@ -513,7 +531,7 @@ public function login($returnTo = null, $parameters = array(), $forceAuthn = fal
*
* @throws OneLogin_Saml2_Error
*/
public function logout($returnTo = null, $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null, $nameIdNameQualifier = null)
public function logout($returnTo = null, $parameters = array(), $nameId = null, $sessionIndex = null, $stay = false, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null)
{
assert('is_array($parameters)');

Expand All @@ -532,7 +550,7 @@ public function logout($returnTo = null, $parameters = array(), $nameId = null,
$nameIdFormat = $this->_nameidFormat;
}

$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat, $nameIdNameQualifier);
$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, null, $nameId, $sessionIndex, $nameIdFormat, $nameIdNameQualifier, $nameIdSPNameQualifier);

$this->_lastRequest = $logoutRequest->getXML();
$this->_lastRequestID = $logoutRequest->id;
Expand Down Expand Up @@ -650,7 +668,7 @@ public function buildResponseSignature($samlResponse, $relayState, $signAlgorith
*
* @throws OneLogin_Saml2_Error
*/
private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $type="SAMLRequest")
private function buildMessageSignature($samlMessage, $relayState, $signAlgorithm = XMLSecurityKey::RSA_SHA256, $type = "SAMLRequest")
{
$key = $this->_settings->getSPkey();
if (empty($key)) {
Expand Down
20 changes: 15 additions & 5 deletions lib/Saml2/LogoutRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ class OneLogin_Saml2_LogoutRequest
* @param string|null $sessionIndex The SessionIndex (taken from the SAML Response in the SSO process).
* @param string|null $nameIdFormat The NameID Format will be set in the LogoutRequest.
* @param string|null $nameIdNameQualifier The NameID NameQualifier will be set in the LogoutRequest.
* @param string|null $nameIdSPNameQualifier The NameID SP NameQualifier will be set in the LogoutRequest.
*
* @throws OneLogin_Saml2_Error
*/
public function __construct(OneLogin_Saml2_Settings $settings, $request = null, $nameId = null, $sessionIndex = null, $nameIdFormat = null, $nameIdNameQualifier = null)
public function __construct(OneLogin_Saml2_Settings $settings, $request = null, $nameId = null, $sessionIndex = null, $nameIdFormat = null, $nameIdNameQualifier = null, $nameIdSPNameQualifier = null)
{
$this->_settings = $settings;

Expand All @@ -59,7 +60,6 @@ public function __construct(OneLogin_Saml2_Settings $settings, $request = null,
$id = OneLogin_Saml2_Utils::generateUniqueID();
$this->id = $id;

$nameIdValue = OneLogin_Saml2_Utils::generateUniqueID();
$issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());

$cert = null;
Expand All @@ -78,16 +78,26 @@ public function __construct(OneLogin_Saml2_Settings $settings, $request = null,
$spData['NameIDFormat'] != OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED) {
$nameIdFormat = $spData['NameIDFormat'];
}
$spNameQualifier = null;
} else {
$nameId = $idpData['entityId'];
$nameIdFormat = OneLogin_Saml2_Constants::NAMEID_ENTITY;
$spNameQualifier = $spData['entityId'];
}

/* 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,
$spNameQualifier,
$nameIdSPNameQualifier,
$nameIdFormat,
$cert,
$nameIdNameQualifier
Expand Down
17 changes: 17 additions & 0 deletions lib/Saml2/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,23 @@ public function getNameIdNameQualifier()
return $nameIdNameQualifier;
}

/**
* Gets the NameID SP NameQualifier provided by the SAML response from the IdP.
*
* @return string|null NameID SP NameQualifier
*
* @throws ValidationError
*/
public function getNameIdSPNameQualifier()
{
$nameIdSPNameQualifier = null;
$nameIdData = $this->getNameIdData();
if (!empty($nameIdData) && isset($nameIdData['SPNameQualifier'])) {
$nameIdSPNameQualifier = $nameIdData['SPNameQualifier'];
}
return $nameIdSPNameQualifier;
}

/**
* Gets the SessionNotOnOrAfter from the AuthnStatement.
* Could be used to set the local session expiration
Expand Down
61 changes: 61 additions & 0 deletions tests/src/OneLogin/Saml2/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ public function testProcessNoResponse()
* @covers OneLogin_Saml2_Auth::getAttribute
* @covers OneLogin_Saml2_Auth::getNameId
* @covers OneLogin_Saml2_Auth::getNameIdFormat
* @covers OneLogin_Saml2_Auth::getNameIdNameQualifier
* @covers OneLogin_Saml2_Auth::getNameIdSPNameQualifier
* @covers OneLogin_Saml2_Auth::getErrors
* @covers OneLogin_Saml2_Auth::getSessionIndex
* @covers OneLogin_Saml2_Auth::getSessionExpiration
Expand All @@ -136,6 +138,8 @@ public function testProcessResponseInvalid()
$this->assertEmpty($this->_auth->getAttributes());
$this->assertNull($this->_auth->getNameId());
$this->assertNull($this->_auth->getNameIdFormat());
$this->assertNull($this->_auth->getNameIdNameQualifier());
$this->assertNull($this->_auth->getNameIdSPNameQualifier());
$this->assertNull($this->_auth->getSessionIndex());
$this->assertNull($this->_auth->getSessionExpiration());
$this->assertNull($this->_auth->getAttribute('uid'));
Expand Down Expand Up @@ -209,6 +213,63 @@ public function testProcessResponseValid()
$this->assertEquals('2655106621', $sessionExpiration);
}

/**
* Tests the getNameIdNameQualifier method of the Auth class
* Case found
* @covers OneLogin_Saml2_Auth::getNameIdNameQualifier
*/
public function testGetNameIdNameQualifier()
{
$message = file_get_contents(TEST_ROOT . '/data/responses/valid_response_with_namequalifier.xml.base64');
$_POST['SAMLResponse'] = $message;
$this->assertNull($this->_auth->getNameIdNameQualifier());
$this->_auth->processResponse();
$this->assertTrue($this->_auth->isAuthenticated());
$this->assertEquals('https://test.example.com/saml/metadata', $this->_auth->getNameIdNameQualifier());
}
/**
* Tests the getNameIdNameQualifier method of the Auth class
* Case Null
* @covers OneLogin_Saml2_Auth::getNameIdNameQualifier
*/
public function testGetNameIdNameQualifier2()
{
$message = file_get_contents(TEST_ROOT . '/data/responses/valid_response.xml.base64');
$_POST['SAMLResponse'] = $message;
$this->assertNull($this->_auth->getNameIdNameQualifier());
$this->_auth->processResponse();
$this->assertTrue($this->_auth->isAuthenticated());
$this->assertNull($this->_auth->getNameIdNameQualifier());
}
/**
* Tests the getNameIdSPNameQualifier method of the Auth class
* Case Found
* @covers OneLogin_Saml2_Auth::getNameIdSPNameQualifier
*/
public function testGetNameIdSPNameQualifier()
{
$message = file_get_contents(TEST_ROOT . '/data/responses/valid_response_with_namequalifier.xml.base64');
$_POST['SAMLResponse'] = $message;
$this->assertNull($this->_auth->getNameIdSPNameQualifier());
$this->_auth->processResponse();
$this->assertTrue($this->_auth->isAuthenticated());
$this->assertNull($this->_auth->getNameIdSPNameQualifier());
}
/**
* Tests the getNameIdSPNameQualifier method of the Auth class
* Case Null
* @covers OneLogin_Saml2_Auth::getNameIdSPNameQualifier
*/
public function testGetNameIdSPNameQualifier2()
{
$message = file_get_contents(TEST_ROOT . '/data/responses/valid_response.xml.base64');
$_POST['SAMLResponse'] = $message;
$this->assertNull($this->_auth->getNameIdSPNameQualifier());
$this->_auth->processResponse();
$this->assertTrue($this->_auth->isAuthenticated());
$this->assertEquals('http://stuff.com/endpoints/metadata.php', $this->_auth->getNameIdSPNameQualifier());
}

/**
* Tests the getAttributes and getAttributesWithFriendlyName methods
* @covers OneLogin_Saml2_Auth::getAttributes
Expand Down
18 changes: 18 additions & 0 deletions tests/src/OneLogin/Saml2/LogoutRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,24 @@ public function testGetNameIdData()
$this->assertContains('NameID not found in the Logout Request', $e->getMessage());
}

$logoutRequest = new OneLogin_Saml2_LogoutRequest($this->_settings, null, "ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", null, OneLogin_Saml2_Constants::NAMEID_PERSISTENT, $this->_settings->getIdPData()['entityId'], $this->_settings->getSPData()['entityId']);
$logoutRequestStr = $logoutRequest->getXML();
$this->assertContains('ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c', $logoutRequestStr);
$this->assertContains('Format="'.OneLogin_Saml2_Constants::NAMEID_PERSISTENT, $logoutRequestStr);
$this->assertContains('NameQualifier="'.$this->_settings->getIdPData()['entityId'], $logoutRequestStr);
$this->assertContains('SPNameQualifier="'.$this->_settings->getSPData()['entityId'], $logoutRequestStr);
$logoutRequest2 = new OneLogin_Saml2_LogoutRequest($this->_settings, null, "ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", null, OneLogin_Saml2_Constants::NAMEID_ENTITY, $this->_settings->getIdPData()['entityId'], $this->_settings->getSPData()['entityId']);
$logoutRequestStr2 = $logoutRequest2->getXML();
$this->assertContains('ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c', $logoutRequestStr2);
$this->assertContains('Format="'.OneLogin_Saml2_Constants::NAMEID_ENTITY, $logoutRequestStr2);
$this->assertNotContains('NameQualifier', $logoutRequestStr2);
$this->assertNotContains('SPNameQualifier', $logoutRequestStr2);
$logoutRequest3 = new OneLogin_Saml2_LogoutRequest($this->_settings, null, "ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c", null, OneLogin_Saml2_Constants::NAMEID_UNSPECIFIED);
$logoutRequestStr3 = $logoutRequest3->getXML();
$this->assertContains('ONELOGIN_1e442c129e1f822c8096086a1103c5ee2c7cae1c', $logoutRequestStr3);
$this->assertNotContains('Format', $logoutRequestStr3);
$this->assertNotContains('NameQualifier', $logoutRequestStr3);
$this->assertNotContains('SPNameQualifier', $logoutRequestStr3);
}

/**
Expand Down
29 changes: 29 additions & 0 deletions tests/src/OneLogin/Saml2/ResponseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,35 @@ public function testGetNameIdNameQualifier()
}
}

/**
* Tests the getNameIdSPNameQualifier method of the Response
*
* @covers OneLogin_Saml2_Response::getNameIdSPNameQualifier
*/
public function testGetNameIdSPNameQualifier()
{
$xml = file_get_contents(TEST_ROOT . '/data/responses/response1.xml.base64');
$response = new OneLogin_Saml2_Response($this->_settings, $xml);
$this->assertNull($response->getNameIdSPNameQualifier());
$xml2 = file_get_contents(TEST_ROOT . '/data/responses/response_encrypted_nameid.xml.base64');
$response2 = new OneLogin_Saml2_Response($this->_settings, $xml2);
$this->assertEquals('http://stuff.com/endpoints/metadata.php', $response2->getNameIdSPNameQualifier());
$xml3 = file_get_contents(TEST_ROOT . '/data/responses/valid_encrypted_assertion.xml.base64');
$response3 = new OneLogin_Saml2_Response($this->_settings, $xml3);
$this->assertEquals('http://stuff.com/endpoints/metadata.php', $response3->getNameIdSPNameQualifier());
$xml4 = file_get_contents(TEST_ROOT . '/data/responses/valid_response.xml.base64');
$response4 = new OneLogin_Saml2_Response($this->_settings, $xml4);
$this->assertEquals('http://stuff.com/endpoints/metadata.php', $response4->getNameIdSPNameQualifier());
$xml5 = file_get_contents(TEST_ROOT . '/data/responses/invalids/no_nameid.xml.base64');
$response5 = new OneLogin_Saml2_Response($this->_settings, $xml5);
try {
$nameId5 = $response5->getNameIdSPNameQualifier();
$this->fail('ValidationError was not raised');
} catch (OneLogin_Saml2_ValidationError $e) {
$this->assertContains('NameID not found in the assertion of the Response', $e->getMessage());
}
}

/**
* Tests the getNameIdData method of the OneLogin_Saml2_Response
*
Expand Down

0 comments on commit 4348bd7

Please sign in to comment.