Skip to content

Commit

Permalink
Add ForceAuh and IsPassive support. Requested in SAML-Toolkits#49
Browse files Browse the repository at this point in the history
  • Loading branch information
pitbulk committed Jan 13, 2015
1 parent 7e70d82 commit df2a40c
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 14 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,12 @@ $auth = new OneLogin_Saml2_Auth();
$auth->login($newTargetUrl);
```

The login method can recieve 3 more optional parameters:

* $parameters An array of parameters that will be added to the GET in the HTTP-Redirect.
* $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
* $isPassive When true the AuthNReuqest will set the Ispassive='true'

#### The SP Endpoints ####

Related to the SP there are 3 important views: The metadata view, the ACS view and the SLS view. The toolkit
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "onelogin/php-saml",
"description": "OneLogin PHP SAML Toolkit",
"license": "MIT",
"version": "2.1.0",
"version": "2.3.0",
"homepage": "https://onelogin.zendesk.com/hc/en-us/sections/200245634-SAML-Toolkits",
"keywords": ["saml", "saml2", "onelogin"],
"autoload": {
Expand Down Expand Up @@ -31,6 +31,6 @@
"suggest": {
"lib-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)",
"ext-mcrypt": "Install mcrypt and php5-mcrypt libs in order to support encryption",
"ext-gettext": "Install gettext and php5-gettext libs to handle translations"
"ext-gettext": "Install gettext and php5-gettext libs to handle translations"
}
}
6 changes: 3 additions & 3 deletions lib/Saml2/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public function processResponse($requestId = null)
* @param boolean $keepLocalSession When false will destroy the local session, otherwise will keep it
* @param string $requestId The ID of the LogoutRequest sent by this SP to the IdP
*/
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer=false)
public function processSLO($keepLocalSession = false, $requestId = null, $retrieveParametersFromServer = false)
{
$this->_errors = array();
if (isset($_GET) && isset($_GET['SAMLResponse'])) {
Expand Down Expand Up @@ -280,11 +280,11 @@ public function getAttribute($name)
* @param string $returnTo The target URL the user should be returned to after login.
* @param array $parameters Extra parameters to be added to the GET
*/
public function login($returnTo = null, $parameters = array())
public function login($returnTo = null, $parameters = array(), $forceAuthn = false, $isPassive = false)
{
assert('is_array($parameters)');

$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings);
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, $forceAuthn, $isPassive);

$samlRequest = $authnRequest->getRequest();
$parameters['SAMLRequest'] = $samlRequest;
Expand Down
20 changes: 18 additions & 2 deletions lib/Saml2/AuthnRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class OneLogin_Saml2_AuthnRequest
*
* @param OneLogin_Saml2_Settings $settings Settings
*/
public function __construct(OneLogin_Saml2_Settings $settings)
public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false)
{
$this->_settings = $settings;

Expand Down Expand Up @@ -62,6 +62,22 @@ public function __construct(OneLogin_Saml2_Settings $settings)
}
}

$forceAuthnStr = '';
if ($forceAuthn) {
$forceAuthnStr = <<<FORCEAUTHN
ForceAuthn="true"
FORCEAUTHN;
}

$isPassiveStr = '';
if ($isPassive) {
$isPassiveStr = <<<ISPASSIVE
IsPassive="true"
ISPASSIVE;
}

$requestedAuthnStr = '';
if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) {
if ($security['requestedAuthnContext'] === true) {
Expand All @@ -85,7 +101,7 @@ public function __construct(OneLogin_Saml2_Settings $settings)
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="$id"
Version="2.0"
{$providerNameStr}
{$providerNameStr}{$forceAuthnStr}{$isPassiveStr}
IssueInstant="$issueInstant"
Destination="{$idpData['singleSignOnService']['url']}"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Expand Down
4 changes: 2 additions & 2 deletions lib/Saml2/version.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"php-saml": {
"version": "2.2.0",
"released": "01/10/2014"
"version": "2.3.0",
"released": "13/01/2015"
}
}
162 changes: 162 additions & 0 deletions tests/src/OneLogin/Saml2/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,168 @@ public function testLoginSigned()
}
}

/**
* Tests the login method of the OneLogin_Saml2_Auth class
* Case Logout with no parameters. A AuthN Request is built with ForceAuthn and redirect executed
*
* @covers OneLogin_Saml2_Auth::login
* @runInSeparateProcess
*/
public function testLoginForceAuthN()
{
$settingsDir = TEST_ROOT .'/settings/';
include $settingsDir.'settings1.php';

$settingsInfo['security']['authnRequestsSigned'] = true;

$auth = new OneLogin_Saml2_Auth($settingsInfo);

try {
// The Header of the redirect produces an Exception
$returnTo = 'http://example.com/returnto';
$auth->login($returnTo);
// Do not ever get here
$this->assertFalse(true);
} catch (Exception $e) {
$this->assertContains('Cannot modify header information', $e->getMessage());
$trace = $e->getTrace();
$targetUrl = getUrlFromRedirect($trace);
$parsedQuery = getParamsFromUrl($targetUrl);

$ssoUrl = $settingsInfo['idp']['singleSignOnService']['url'];
$this->assertContains($ssoUrl, $targetUrl);
$this->assertArrayHasKey('SAMLRequest', $parsedQuery);
$encodedRequest = $parsedQuery['SAMLRequest'];
$decoded = base64_decode($encodedRequest);
$request = gzinflate($decoded);
$this->assertNotContains('ForceAuthn="true"', $request);
}

try {
// The Header of the redirect produces an Exception
$returnTo = 'http://example.com/returnto';

$auth->login($returnTo, array(), false, false);
// Do not ever get here
$this->assertFalse(true);
} catch (Exception $e) {
$this->assertContains('Cannot modify header information', $e->getMessage());
$trace2 = $e->getTrace();
$targetUrl2 = getUrlFromRedirect($trace2);
$parsedQuery2 = getParamsFromUrl($targetUrl2);

$ssoUrl2 = $settingsInfo['idp']['singleSignOnService']['url'];
$this->assertContains($ssoUrl2, $targetUrl2);
$this->assertArrayHasKey('SAMLRequest', $parsedQuery2);
$encodedRequest2 = $parsedQuery2['SAMLRequest'];
$decoded2 = base64_decode($encodedRequest2);
$request2 = gzinflate($decoded2);
$this->assertNotContains('ForceAuthn="true"', $request2);
}

try {
// The Header of the redirect produces an Exception
$returnTo = 'http://example.com/returnto';
$auth->login($returnTo, array(), true, false);
// Do not ever get here
$this->assertFalse(true);
} catch (Exception $e) {
$this->assertContains('Cannot modify header information', $e->getMessage());
$trace3 = $e->getTrace();
$targetUrl3 = getUrlFromRedirect($trace3);
$parsedQuery3 = getParamsFromUrl($targetUrl3);

$ssoUrl3 = $settingsInfo['idp']['singleSignOnService']['url'];
$this->assertContains($ssoUrl3, $targetUrl3);
$this->assertArrayHasKey('SAMLRequest', $parsedQuery3);
$encodedRequest3 = $parsedQuery3['SAMLRequest'];
$decoded3 = base64_decode($encodedRequest3);
$request3 = gzinflate($decoded3);
$this->assertContains('ForceAuthn="true"', $request3);
}

}

/**
* Tests the login method of the OneLogin_Saml2_Auth class
* Case Logout with no parameters. A AuthN Request is built with IsPassive and redirect executed
*
* @covers OneLogin_Saml2_Auth::login
* @runInSeparateProcess
*/
public function testLoginIsPassive()
{
$settingsDir = TEST_ROOT .'/settings/';
include $settingsDir.'settings1.php';

$settingsInfo['security']['authnRequestsSigned'] = true;

$auth = new OneLogin_Saml2_Auth($settingsInfo);

try {
// The Header of the redirect produces an Exception
$returnTo = 'http://example.com/returnto';
$auth->login($returnTo);
// Do not ever get here
$this->assertFalse(true);
} catch (Exception $e) {
$this->assertContains('Cannot modify header information', $e->getMessage());
$trace = $e->getTrace();
$targetUrl = getUrlFromRedirect($trace);
$parsedQuery = getParamsFromUrl($targetUrl);

$ssoUrl = $settingsInfo['idp']['singleSignOnService']['url'];
$this->assertContains($ssoUrl, $targetUrl);
$this->assertArrayHasKey('SAMLRequest', $parsedQuery);
$encodedRequest = $parsedQuery['SAMLRequest'];
$decoded = base64_decode($encodedRequest);
$request = gzinflate($decoded);
$this->assertNotContains('IsPassive="true"', $request);
}

try {
// The Header of the redirect produces an Exception
$returnTo = 'http://example.com/returnto';
$auth->login($returnTo, array(), false, false);
// Do not ever get here
$this->assertFalse(true);
} catch (Exception $e) {
$this->assertContains('Cannot modify header information', $e->getMessage());
$trace2 = $e->getTrace();
$targetUrl2 = getUrlFromRedirect($trace2);
$parsedQuery2 = getParamsFromUrl($targetUrl2);

$ssoUrl2 = $settingsInfo['idp']['singleSignOnService']['url'];
$this->assertContains($ssoUrl2, $targetUrl2);
$this->assertArrayHasKey('SAMLRequest', $parsedQuery2);
$encodedRequest2 = $parsedQuery2['SAMLRequest'];
$decoded2 = base64_decode($encodedRequest2);
$request2 = gzinflate($decoded2);
$this->assertNotContains('IsPassive="true"', $request2);
}

try {
// The Header of the redirect produces an Exception
$returnTo = 'http://example.com/returnto';
$auth->login($returnTo, array(), false, true);
// Do not ever get here
$this->assertFalse(true);
} catch (Exception $e) {
$this->assertContains('Cannot modify header information', $e->getMessage());
$trace3 = $e->getTrace();
$targetUrl3 = getUrlFromRedirect($trace3);
$parsedQuery3 = getParamsFromUrl($targetUrl3);

$ssoUrl3 = $settingsInfo['idp']['singleSignOnService']['url'];
$this->assertContains($ssoUrl3, $targetUrl3);
$this->assertArrayHasKey('SAMLRequest', $parsedQuery3);
$encodedRequest3 = $parsedQuery3['SAMLRequest'];
$decoded3 = base64_decode($encodedRequest3);
$request3 = gzinflate($decoded3);
$this->assertContains('IsPassive="true"', $request3);
}
}

/**
* Tests the logout method of the OneLogin_Saml2_Auth class
* Case Logout with no parameters. A logout Request is built and redirect executed
Expand Down
78 changes: 73 additions & 5 deletions tests/src/OneLogin/Saml2/AuthnRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function setUp()
*/
public function testCreateDeflatedSAMLRequestURLParameter()
{
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings, false, false);
$authnRequest = new OneLogin_Saml2_AuthnRequest($this->_settings);
$parameters = array('SAMLRequest' => $authnRequest->getRequest());
$authUrl = OneLogin_Saml2_Utils::redirect('http://idp.example.com/SSOService.php', $parameters, true);
$this->assertRegExp('#^http://idp\.example\.com\/SSOService\.php\?SAMLRequest=#', $authUrl);
Expand All @@ -39,13 +39,19 @@ public function testCreateDeflatedSAMLRequestURLParameter()
$this->assertRegExp('#^<samlp:AuthnRequest#', $inflated);
}

/**
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
* The creation of a deflated SAML Request with AuthNContext
*
* @covers OneLogin_Saml2_AuthnRequest
*/
public function testAuthNContext()
{
$settingsDir = TEST_ROOT .'/settings/';
include $settingsDir.'settings1.php';

$settings = new OneLogin_Saml2_Settings($settingsInfo);
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings, false, false);
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
$encodedRequest = $authnRequest->getRequest();
$decoded = base64_decode($encodedRequest);
$request = gzinflate($decoded);
Expand All @@ -54,7 +60,7 @@ public function testAuthNContext()

$settingsInfo['security']['requestedAuthnContext']= true;
$settings2 = new OneLogin_Saml2_Settings($settingsInfo);
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings2, false, false);
$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings2);
$encodedRequest2 = $authnRequest2->getRequest();
$decoded2 = base64_decode($encodedRequest2);
$request2 = gzinflate($decoded2);
Expand All @@ -63,7 +69,7 @@ public function testAuthNContext()

$settingsInfo['security']['requestedAuthnContext'] = false;
$settings3 = new OneLogin_Saml2_Settings($settingsInfo);
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings3, false, false);
$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings3);
$encodedRequest3 = $authnRequest3->getRequest();
$decoded3 = base64_decode($encodedRequest3);
$request3 = gzinflate($decoded3);
Expand All @@ -72,7 +78,7 @@ public function testAuthNContext()

$settingsInfo['security']['requestedAuthnContext']= array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509');
$settings4 = new OneLogin_Saml2_Settings($settingsInfo);
$authnRequest4 = new OneLogin_Saml2_AuthnRequest($settings4, false, false);
$authnRequest4 = new OneLogin_Saml2_AuthnRequest($settings4);
$encodedRequest4 = $authnRequest4->getRequest();
$decoded4 = base64_decode($encodedRequest4);
$request4 = gzinflate($decoded4);
Expand All @@ -82,6 +88,68 @@ public function testAuthNContext()
$this->assertContains('<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:X509</saml:AuthnContextClassRef>', $request4);
}

/**
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
* The creation of a deflated SAML Request with ForceAuthn
*
* @covers OneLogin_Saml2_AuthnRequest
*/
public function testForceAuthN()
{
$settingsDir = TEST_ROOT .'/settings/';
include $settingsDir.'settings1.php';

$settings = new OneLogin_Saml2_Settings($settingsInfo);
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
$encodedRequest = $authnRequest->getRequest();
$decoded = base64_decode($encodedRequest);
$request = gzinflate($decoded);
$this->assertNotContains('ForceAuthn="true"', $request);

$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings, false, false);
$encodedRequest2 = $authnRequest2->getRequest();
$decoded2 = base64_decode($encodedRequest2);
$request2 = gzinflate($decoded2);
$this->assertNotContains('ForceAuthn="true"', $request2);

$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings, true, false);
$encodedRequest3 = $authnRequest3->getRequest();
$decoded3 = base64_decode($encodedRequest3);
$request3 = gzinflate($decoded3);
$this->assertContains('ForceAuthn="true"', $request3);
}

/**
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
* The creation of a deflated SAML Request with isPassive
*
* @covers OneLogin_Saml2_AuthnRequest
*/
public function testIsPassive()
{
$settingsDir = TEST_ROOT .'/settings/';
include $settingsDir.'settings1.php';

$settings = new OneLogin_Saml2_Settings($settingsInfo);
$authnRequest = new OneLogin_Saml2_AuthnRequest($settings);
$encodedRequest = $authnRequest->getRequest();
$decoded = base64_decode($encodedRequest);
$request = gzinflate($decoded);
$this->assertNotContains('IsPassive="true"', $request);

$authnRequest2 = new OneLogin_Saml2_AuthnRequest($settings, false, false);
$encodedRequest2 = $authnRequest2->getRequest();
$decoded2 = base64_decode($encodedRequest2);
$request2 = gzinflate($decoded2);
$this->assertNotContains('IsPassive="true"', $request2);

$authnRequest3 = new OneLogin_Saml2_AuthnRequest($settings, false, true);
$encodedRequest3 = $authnRequest3->getRequest();
$decoded3 = base64_decode($encodedRequest3);
$request3 = gzinflate($decoded3);
$this->assertContains('IsPassive="true"', $request3);
}

/**
* Tests the OneLogin_Saml2_AuthnRequest Constructor.
* The creation of a deflated SAML Request
Expand Down

0 comments on commit df2a40c

Please sign in to comment.