Skip to content
Snippets Groups Projects
Commit 8efb1c98 authored by Mads Freek Petersen's avatar Mads Freek Petersen
Browse files

Added support for IsPassive flag.

If NoPassive is returned (ie. user not logged in) AuthnResponse.process creates
a valid session with an empty set of attributes.


git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@537 44740490-163a-0410-bde0-09ae8108e29a
parent 21e4d3aa
No related branches found
No related tags found
No related merge requests found
...@@ -20,6 +20,7 @@ class SimpleSAML_XML_SAML20_AuthnRequest { ...@@ -20,6 +20,7 @@ class SimpleSAML_XML_SAML20_AuthnRequest {
private $message = null; private $message = null;
private $dom; private $dom;
private $relayState = null; private $relayState = null;
private $isPassive = 'false';
const PROTOCOL = 'saml2'; const PROTOCOL = 'saml2';
...@@ -107,6 +108,43 @@ class SimpleSAML_XML_SAML20_AuthnRequest { ...@@ -107,6 +108,43 @@ class SimpleSAML_XML_SAML20_AuthnRequest {
} }
/**
* This function sets the IsPassive flag
*
*/
public function setIsPassive($isPassive) {
$this->isPassive = $isPassive ? 'true' : 'false';
}
/**
* This function retrieves the IsPassive flag from this authentication request.
*
* @return The IsPassive flag from this authentication request.
*/
public function getIsPassive() {
$dom = $this->getDOM();
if (empty($dom)) {
throw new Exception("Could not get message DOM in AuthnRequest object");
}
$root = $dom->documentElement;
if(!$root->hasAttribute('IsPassive')) {
/* ForceAuthn defaults to false. */
return FALSE;
}
$fa = $root->getAttribute('IsPassive');
if($fa === 'true') {
return TRUE;
} elseif($fa === 'false') {
return FALSE;
} else {
throw new Exception('Invalid value of IsPassive attribute in SAML2 AuthnRequest.');
}
}
/** /**
* This function retrieves the ForceAuthn flag from this authentication request. * This function retrieves the ForceAuthn flag from this authentication request.
* *
...@@ -204,7 +242,7 @@ class SimpleSAML_XML_SAML20_AuthnRequest { ...@@ -204,7 +242,7 @@ class SimpleSAML_XML_SAML20_AuthnRequest {
$authnRequest = '<samlp:AuthnRequest $authnRequest = '<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="' . $id . '" Version="2.0" ID="' . $id . '" Version="2.0"
IssueInstant="' . $issueInstant . '" ForceAuthn="' . $forceauthn . '" IssueInstant="' . $issueInstant . '" ForceAuthn="' . $forceauthn . '" IsPassive="' . $this->isPassive . '"
Destination="' . htmlspecialchars($destination) . '" Destination="' . htmlspecialchars($destination) . '"
ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
AssertionConsumerServiceURL="' . htmlspecialchars($assertionConsumerServiceURL) . '"> AssertionConsumerServiceURL="' . htmlspecialchars($assertionConsumerServiceURL) . '">
......
...@@ -141,11 +141,23 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ...@@ -141,11 +141,23 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
} }
/**
* This function finds the status of this response.
*/
public function findstatus() {
$status = $this->doXPathQuery('/samlp:Response/samlp:Status/samlp:StatusCode')->item(0);
if($status != NULL) {
return $status->getAttribute('Value');
}
throw new Exception('Unable to determine the status of this SAML2 AuthnResponse message.: ' . $this->getXML());
}
/** /**
* This function finds the issuer of this response. It will first search the Response element, * This function finds the issuer of this response. It will first search the Response element,
* and if it isn't found there, it will search all Assertion elements. * and if it isn't found there, it will search all Assertion elements.
*/ */
private function findIssuer() { public function findIssuer() {
/* First check the Response element. */ /* First check the Response element. */
$issuer = $this->doXPathQuery('/samlp:Response/saml:Issuer')->item(0); $issuer = $this->doXPathQuery('/samlp:Response/saml:Issuer')->item(0);
...@@ -244,7 +256,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ...@@ -244,7 +256,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
if (isset($md['certificate'])) { if (isset($md['certificate'])) {
$publickey = @file_get_contents($this->configuration->getPathValue('certdir') . $md['certificate']); $publickey = @file_get_contents($this->configuration->getPathValue('certdir') . $md['certificate']);
if (!$publickey) { if (!$publickey) {
throw new Exception("Optional saml20-idp-remote metadata 'certificate' set, but no certificate found"); throw new Exception("Saml20-idp-remote id: " . $this-issuer . " 'certificate' set to ': " . $md['certificate'] . "', but no certificate found");
} }
} }
/* Validate the signature. */ /* Validate the signature. */
...@@ -476,32 +488,43 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ...@@ -476,32 +488,43 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
* current session if it is valid. It throws an exception if it is invalid. * current session if it is valid. It throws an exception if it is invalid.
*/ */
public function process() { public function process() {
/* Find the issuer of this response. */ $status = $this->findstatus();
$this->issuer = $this->findIssuer(); if ($status == 'urn:oasis:names:tc:SAML:2.0:status:Success' ) {
/* Find the issuer of this response. */
$this->decryptAssertion(); $this->issuer = $this->findIssuer();
/* Validate the signature element. */ $this->decryptAssertion();
$this->validateSignature();
/* Validate the signature element. */
/* Process all assertions. */ $this->validateSignature();
$assertions = $this->doXPathQuery('/samlp:Response/saml:Assertion');
foreach($assertions as $assertion) { /* Process all assertions. */
$this->processAssertion($assertion); $assertions = $this->doXPathQuery('/samlp:Response/saml:Assertion');
} foreach($assertions as $assertion) {
$this->processAssertion($assertion);
if($this->nameid === NULL) { }
throw new Exception('No nameID found in AuthnResponse.');
if($this->nameid === NULL) {
throw new Exception('No nameID found in AuthnResponse.');
}
/* Update the session information */
SimpleSAML_Session::init(true, 'saml2');
$session = SimpleSAML_Session::getInstance();
$session->setAttributes($this->attributes);
$session->setNameID($this->nameid);
$session->setSessionIndex($this->sessionIndex);
$session->setIdP($this->issuer);
} elseif ($status == 'urn:oasis:names:tc:SAML:2.0:status:NoPassive') {
/* Do not process the authResponse when NoPassive is sent - we continue with an empty set of attributes.
Some day we will be able to tell the application what happened */
SimpleSAML_Session::init(true, 'saml2');
$session = SimpleSAML_Session::getInstance();
$session->setAttributes(array());
} else {
SimpleSAML_Utilities::fatalError($session->getTrackID(), 'RESPONSESTATUSNOSUCCESS', new Exception("Status = " . $status));
} }
/* Update the session information */
SimpleSAML_Session::init(true, 'saml2');
$session = SimpleSAML_Session::getInstance();
$session->setAttributes($this->attributes);
$session->setNameID($this->nameid);
$session->setSessionIndex($this->sessionIndex);
$session->setIdP($this->issuer);
} }
...@@ -546,7 +569,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ...@@ -546,7 +569,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
* *
* @return AuthenticationResponse as string * @return AuthenticationResponse as string
*/ */
public function generate($idpentityid, $spentityid, $inresponseto, $nameid, $attributes) { public function generate($idpentityid, $spentityid, $inresponseto, $nameid, $attributes, $status = 'Success') {
/** /**
* Retrieving metadata for the two specific entity IDs. * Retrieving metadata for the two specific entity IDs.
...@@ -597,25 +620,10 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ...@@ -597,25 +620,10 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
} else { } else {
$nameid = $this->generateNameID($nameidformat, self::generateID(), $spnamequalifier); $nameid = $this->generateNameID($nameidformat, self::generateID(), $spnamequalifier);
} }
/** $assertion = "";
* Generating the response. if ($status === 'Success') {
*/ $assertion = '<saml:Assertion Version="2.0"
$authnResponse = '<samlp:Response
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
ID="' . $id . '"
InResponseTo="' . htmlspecialchars($inresponseto) . '" Version="2.0"
IssueInstant="' . $issueInstant . '"
Destination="' . htmlspecialchars($destination) . '">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . htmlspecialchars($issuer) . '</saml:Issuer>
<samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<samlp:StatusCode xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>
<saml:Assertion Version="2.0"
ID="' . $assertionid . '" IssueInstant="' . $issueInstant . '"> ID="' . $assertionid . '" IssueInstant="' . $issueInstant . '">
<saml:Issuer>' . htmlspecialchars($issuer) . '</saml:Issuer> <saml:Issuer>' . htmlspecialchars($issuer) . '</saml:Issuer>
<saml:Subject> <saml:Subject>
...@@ -638,9 +646,29 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse { ...@@ -638,9 +646,29 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
</saml:AuthnContext> </saml:AuthnContext>
</saml:AuthnStatement> </saml:AuthnStatement>
' . $attributestatement. ' ' . $attributestatement. '
</saml:Assertion> </saml:Assertion>';
</samlp:Response> }
';
/**
* Generating the response.
*/
$authnResponse = '<samlp:Response
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
ID="' . $id . '"
InResponseTo="' . htmlspecialchars($inresponseto) . '" Version="2.0"
IssueInstant="' . $issueInstant . '"
Destination="' . htmlspecialchars($destination) . '">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . htmlspecialchars($issuer) . '</saml:Issuer>
<samlp:Status xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<samlp:StatusCode xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
Value="urn:oasis:names:tc:SAML:2.0:status:' . $status . '" />
</samlp:Status>'
. $assertion .
'</samlp:Response>';
return $authnResponse; return $authnResponse;
} }
......
...@@ -104,9 +104,17 @@ if (isset($_GET['SAMLRequest'])) { ...@@ -104,9 +104,17 @@ if (isset($_GET['SAMLRequest'])) {
$forceAuthn = TRUE; $forceAuthn = TRUE;
} }
if($forceAuthn) { $isPassive = $authnrequest->getIsPassive();
/*
* The ForceAuthn flag was set to true in the authentication request
* and IsPassive was not - IsPassive overrides ForceAuthn thus the check
*
*/
if($forceAuthn && !$isPassive) {
/* ForceAuthn is enabled. Mark the request as needing authentication. This flag /* ForceAuthn is enabled. Mark the request as needing authentication. This flag
* will be cleared by a call to setAuthenticated(TRUE, ...) to the current session. * will be cleared by a call to setAuthenticated(TRUE, ...) to the current session.
*
*/ */
$requestcache['NeedAuthentication'] = TRUE; $requestcache['NeedAuthentication'] = TRUE;
} }
...@@ -175,7 +183,7 @@ if (!isset($session) || !$session->isValid($authority) ) { ...@@ -175,7 +183,7 @@ if (!isset($session) || !$session->isValid($authority) ) {
$needAuth = FALSE; $needAuth = FALSE;
} }
if($needAuth) { if($needAuth && !$isPassive) {
SimpleSAML_Logger::info('SAML2.0 - IdP.SSOService: Will go to authentication module ' . $idpmetadata['auth']); SimpleSAML_Logger::info('SAML2.0 - IdP.SSOService: Will go to authentication module ' . $idpmetadata['auth']);
...@@ -204,9 +212,21 @@ if($needAuth) { ...@@ -204,9 +212,21 @@ if($needAuth) {
SimpleSAML_Logger::info('SAML2.0 - IdP.SSOService: Sending back AuthnResponse to ' . $spentityid); SimpleSAML_Logger::info('SAML2.0 - IdP.SSOService: Sending back AuthnResponse to ' . $spentityid);
if ($isPassive) {
/* Generate an SAML 2.0 AuthNResponse message
With statusCode: urn:oasis:names:tc:SAML:2.0:status:NoPassive
*/
$ar = new SimpleSAML_XML_SAML20_AuthnResponse($config, $metadata);
$authnResponseXML = $ar->generate($idpentityid, $spentityid, $requestid, null, array(), 'NoPassive');
// Sending the AuthNResponse using HTTP-Post SAML 2.0 binding
$httppost = new SimpleSAML_Bindings_SAML20_HTTPPost($config, $metadata);
$httppost->sendResponse($authnResponseXML, $idpentityid, $spentityid,
isset($requestcache['RelayState']) ? $requestcache['RelayState'] : null
);
exit;
}
/* /*
* Attribute handling * Attribute handling
*/ */
......
...@@ -64,6 +64,9 @@ try { ...@@ -64,6 +64,9 @@ try {
$sr = new SimpleSAML_XML_SAML20_AuthnRequest($config, $metadata); $sr = new SimpleSAML_XML_SAML20_AuthnRequest($config, $metadata);
if (isset($_GET['IsPassive'])) {
$sr->setIsPassive($_GET['IsPassive']);
};
$md = $metadata->getMetaData($idpentityid, 'saml20-idp-remote'); $md = $metadata->getMetaData($idpentityid, 'saml20-idp-remote');
$req = $sr->generate($spentityid, $md['SingleSignOnService']); $req = $sr->generate($spentityid, $md['SingleSignOnService']);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment