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

Scoping for authsources. Includes support for IDPList, ProxyCount,

AuthenticatingAuthority and RequesterID.


git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2278 44740490-163a-0410-bde0-09ae8108e29a
parent b660db38
No related branches found
No related tags found
No related merge requests found
......@@ -105,7 +105,7 @@ The [`saml:SP`](./saml:sp) authentication source also defines some parameters.
# Send a passive authentication request.
$auth->login(array(
'saml:IsPassive' => TRUE,
'isPassive' => TRUE,
'ErrorURL' => 'https://.../error_handler.php',
));
......
......@@ -131,6 +131,13 @@ class SAML2_Assertion implements SAML2_SignedElement {
*/
private $authnContext;
/**
* The list of AuthenticatingAuthorities for this assertion.
*
* @var array
*/
private $AuthenticatingAuthority;
/**
* The attributes, as an associative array.
......@@ -192,6 +199,7 @@ class SAML2_Assertion implements SAML2_SignedElement {
$this->attributes = array();
$this->nameFormat = SAML2_Const::NAMEFORMAT_UNSPECIFIED;
$this->certificates = array();
$this->AuthenticatingAuthority = array();
if ($xml === NULL) {
return;
......@@ -416,6 +424,8 @@ class SAML2_Assertion implements SAML2_SignedElement {
} else {
$this->authnContext = trim($accr[0]->textContent);
}
$this->AuthenticatingAuthority = SAML2_Utils::extractStrings($ac, './saml_assertion:AuthenticatingAuthority');
}
......@@ -874,6 +884,29 @@ class SAML2_Assertion implements SAML2_SignedElement {
}
/**
* Retrieve the AuthenticatingAuthority.
*
*
* @return array
*/
public function getAuthenticatingAuthority() {
return $this->AuthenticatingAuthority;
}
/**
* Set the AuthenticatingAuthority
*
*
* @param array.
*/
public function setAuthenticatingAuthority($AuthenticatingAuthority) {
$this->AuthenticatingAuthority = $AuthenticatingAuthority;
}
/**
* Retrieve all attributes.
*
......@@ -1105,6 +1138,7 @@ class SAML2_Assertion implements SAML2_SignedElement {
$as->appendChild($ac);
SAML2_Utils::addString($ac, SAML2_Const::NS_SAML, 'saml:AuthnContextClassRef', $this->authnContext);
SAML2_Utils::addStrings($ac, SAML2_Const::NS_SAML, 'saml:AuthenticatingAuthority', false, $this->AuthenticatingAuthority);
}
......
......@@ -37,6 +37,21 @@ class SAML2_AuthnRequest extends SAML2_Request {
*/
private $IDPList = array();
/**
* The ProxyCount in this request's scoping element
*
* @var int
*/
private $ProxyCount = null;
/**
* The RequesterID list in this request's scoping element
*
* @var array
*/
private $RequesterID = array();
/**
* The URL of the asertion consumer service where the response should be delivered.
*
......@@ -128,13 +143,27 @@ class SAML2_AuthnRequest extends SAML2_Request {
$this->requestedAuthnContext = $rac;
}
$idpEntries = SAML2_Utils::xpQuery($xml, './saml_protocol:Scoping/saml_protocol:IDPList/saml_protocol:IDPEntry');
$scoping = SAML2_Utils::xpQuery($xml, './saml_protocol:Scoping');
if (!empty($scoping)) {
$scoping =$scoping[0];
if ($scoping->hasAttribute('ProxyCount')) {
$this->ProxyCount = (int)$scoping->getAttribute('ProxyCount');
}
$idpEntries = SAML2_Utils::xpQuery($scoping, './saml_protocol:IDPList/saml_protocol:IDPEntry');
foreach($idpEntries as $idpEntry) {
if (!$idpEntry->hasAttribute('ProviderID')) {
throw new Exception("Could not get ProviderID from Scoping/IDPEntry element in AuthnRequest object");
foreach($idpEntries as $idpEntry) {
if (!$idpEntry->hasAttribute('ProviderID')) {
throw new Exception("Could not get ProviderID from Scoping/IDPEntry element in AuthnRequest object");
}
$this->IDPList[] = $idpEntry->getAttribute('ProviderID');
}
$requesterIDs = SAML2_Utils::xpQuery($scoping, './saml_protocol:RequesterID');
foreach ($requesterIDs as $requesterID) {
$this->RequesterID[] = trim($requesterID->textContent);
}
$this->IDPList[] = $idpEntry->getAttribute('ProviderID');
}
}
......@@ -234,6 +263,22 @@ class SAML2_AuthnRequest extends SAML2_Request {
return $this->IDPList;
}
public function setProxyCount($ProxyCount) {
assert('is_int($ProxyCount)');
$this->ProxyCount = $ProxyCount;
}
public function getProxyCount() {
return $this->ProxyCount;
}
public function setRequesterID(array $RequesterID) {
$this->RequesterID = $RequesterID;
}
public function getRequesterID() {
return $this->RequesterID;
}
/**
* Retrieve the value of the AssertionConsumerServiceURL attribute.
......@@ -352,16 +397,25 @@ class SAML2_AuthnRequest extends SAML2_Request {
}
}
if (count($this->IDPList) > 0) {
if ($this->ProxyCount !== null || count($this->IDPList) > 0 || count($this->RequesterID) > 0) {
$scoping = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'Scoping');
$idplist = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'IDPList');
foreach ($this->IDPList as $provider) {
$idpEntry = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'IDPEntry');
$idpEntry->setAttribute('ProviderID', $provider);
$idplist->appendChild($idpEntry);
if ($this->ProxyCount !== null) {
$scoping->setAttribute('ProxyCount', $this->ProxyCount);
}
if (count($this->IDPList) > 0) {
$idplist = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'IDPList');
foreach ($this->IDPList as $provider) {
$idpEntry = $this->document->createElementNS(SAML2_Const::NS_SAMLP, 'IDPEntry');
$idpEntry->setAttribute('ProviderID', $provider);
$idplist->appendChild($idpEntry);
}
$scoping->appendChild($idplist);
$root->appendChild($scoping);
}
if (count($this->RequesterID) > 0) {
SAML2_Utils::addStrings($scoping, SAML2_Const::NS_SAMLP, 'RequesterID', FALSE, $this->RequesterID);
}
$scoping->appendChild($idplist);
$root->appendChild($scoping);
}
return $root;
......
......@@ -135,6 +135,12 @@ class SAML2_Const {
*/
const STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout';
/**
* Second-level status code for ProxyCountExceeded.
*/
const STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded';
}
?>
\ No newline at end of file
......@@ -393,6 +393,8 @@ class SimpleSAML_IdP {
if (isset($state['ForceAuthn']) && (bool)$state['ForceAuthn']) {
/* Force authentication is in effect. */
$needAuth = TRUE;
} elseif (isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) > 0) {
$needAuth = TRUE;
} else {
$needAuth = !$this->isAuthenticated();
}
......
......@@ -22,7 +22,7 @@ All these parameters override the equivalent option from the configuration.
: *Note*: SAML 2 specific.
`saml:ForceAuthn`
`ForceAuthnn`
: Force authentication allows you to force re-authentication of users even if the user has a SSO session at the IdP.
: *Note*: SAML 2 specific.
......@@ -30,7 +30,7 @@ All these parameters override the equivalent option from the configuration.
`saml:idp`
: The entity ID of the IdP we should send an authentication request to.
`saml:IsPassive`
`isPassive`
: Send a passive authentication request.
: *Note*: SAML 2 specific.
......
......@@ -175,6 +175,10 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source {
* @param array $state The state array for the current authentication.
*/
private function startSSO2(SimpleSAML_Configuration $idpMetadata, array $state) {
if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] < 0) {
SimpleSAML_Auth_State::throwException($state, new SimpleSAML_Error_ProxyCountExceeded("ProxyCountExceeded"));
}
$ar = sspmod_saml2_Message::buildAuthnRequest($this->metadata, $idpMetadata);
......@@ -190,12 +194,12 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source {
$ar->setRequestedAuthnContext(array('AuthnContextClassRef' => $accr));
}
if (isset($state['saml:ForceAuthn'])) {
$ar->setForceAuthn((bool)$state['saml:ForceAuthn']);
if (isset($state['ForceAuthnn'])) {
$ar->setForceAuthn((bool)$state['ForceAuthn']);
}
if (isset($state['saml:IsPassive'])) {
$ar->setIsPassive((bool)$state['saml:IsPassive']);
if (isset($state['isPassive'])) {
$ar->setIsPassive((bool)$state['isPassive']);
}
if (isset($state['saml:NameIDPolicy'])) {
......@@ -205,12 +209,37 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source {
));
}
if (isset($state['saml:IDPList'])) {
$IDPList = $state['saml:IDPList'];
}
$ar->setIDPList(array_unique(array_merge($this->metadata->getArray('IDPList', array()),
$idpMetadata->getArray('IDPList', array()),
(array) $IDPList)));
if (isset($state['saml:ProxyCount']) && $state['saml:ProxyCount'] !== null) {
$ar->setProxyCount($state['saml:ProxyCount']);
} elseif ($idpMetadata->getInteger('ProxyCount', null) !== null) {
$ar->setProxyCount($idpMetadata->getInteger('ProxyCount', null));
} elseif ($this->metadata->getInteger('ProxyCount', null) !== null) {
$ar->setProxyCount($this->metadata->getInteger('ProxyCount', null));
}
$requesterID = array();
if (isset($state['saml:RequesterID'])) {
$requesterID = $state['saml:RequesterID'];
}
if (isset($state['core:SP'])) {
$requesterID[] = $state['core:SP'];
}
$ar->setRequesterID($requesterID);
$id = SimpleSAML_Auth_State::saveState($state, 'saml:sp:sso', TRUE);
$ar->setId($id);
SimpleSAML_Logger::debug('Sending SAML 2 AuthnRequest to ' . var_export($idpMetadata->getString('entityid'), TRUE));
$b = new SAML2_HTTPRedirect();
$b->setDestination(sspmod_SAML2_Message::getDebugDestination());
$b->send($ar);
......@@ -291,6 +320,10 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source {
$idp = (string)$state['saml:idp'];
}
if ($idp === NULL && isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) == 1) {
$idp = $state['saml:IDPList'][0];
}
if ($idp === NULL) {
$this->startDisco($state);
assert('FALSE');
......@@ -374,7 +407,7 @@ class sspmod_saml_Auth_Source_SP extends SimpleSAML_Auth_Source {
assert('is_string($idp)');
assert('array_key_exists("LogoutState", $state)');
assert('array_key_exists("saml:logout:Type", $state["LogoutState"])');
$idpMetadata = $this->getIdpMetadata($idp);
$spMetadataArray = $this->metadata->toArray();
......
......@@ -48,6 +48,10 @@ class sspmod_saml_IdP_SAML2 {
$assertion = sspmod_saml2_Message::buildAssertion($idpMetadata, $spMetadata, $attributes, $consumerURL);
$assertion->setInResponseTo($requestId);
if (isset($state['saml:AuthenticatingAuthority'])) {
$assertion->setAuthenticatingAuthority($state['saml:AuthenticatingAuthority']);
}
/* Create the session association (for logout). */
$association = array(
......@@ -242,6 +246,9 @@ class sspmod_saml_IdP_SAML2 {
$requestId = $request->getId();
$IDPList = $request->getIDPList();
$ProxyCount = $request->getProxyCount();
if ($ProxyCount !== null) $ProxyCount--;
$RequesterID = $request->getRequesterID();
$forceAuthn = $request->getForceAuthn();
$isPassive = $request->getIsPassive();
$consumerURL = $request->getAssertionConsumerServiceURL();
......@@ -285,6 +292,7 @@ class sspmod_saml_IdP_SAML2 {
}
$IDPList = array_unique(array_merge($IDPList, $spMetadata->getArrayizeString('IDPList', array())));
if ($ProxyCount == null) $ProxyCount = $spMetadata->getInteger('ProxyCount', null);
if (!$forceAuthn) {
$forceAuthn = $spMetadata->getBoolean('ForceAuthn', FALSE);
......@@ -311,7 +319,9 @@ class sspmod_saml_IdP_SAML2 {
'saml:RelayState' => $relayState,
'saml:RequestId' => $requestId,
'saml:IDPList' => $IDPList,
'ForceAuthn' => $forceAuthn,
'saml:ProxyCount' => $ProxyCount,
'saml:RequesterID' => $RequesterID,
'ForceAuthnn' => $forceAuthn,
'isPassive' => $isPassive,
'saml:ConsumerURL' => $consumerURL,
'saml:Binding' => $protocolBinding,
......
......@@ -32,7 +32,6 @@ if (!empty($stateId)) {
);
}
$idp = $response->getIssuer();
if ($idp === NULL) {
throw new Exception('Missing <saml:Issuer> in message delivered to AssertionConsumerService.');
......@@ -63,6 +62,9 @@ $logoutState = array(
'saml:logout:SessionIndex' => $sessionIndex,
);
$state['LogoutState'] = $logoutState;
$state['saml:AuthenticatingAuthority'] = $assertion->getAuthenticatingAuthority();
$state['saml:AuthenticatingAuthority'][] = $idp;
$state['PersistentAuthData'][] = 'saml:AuthenticatingAuthority';
$source->handleResponse($state, $idp, $assertion->getAttributes());
assert('FALSE');
......@@ -112,6 +112,13 @@ class sspmod_saml2_Error extends SimpleSAML_Error_Exception {
$exception->getMessage(),
$exception
);
} elseif ($exception instanceof SimpleSAML_Error_ProxyCountExceeded) {
$e = new self(
SAML2_Const::STATUS_RESPONDER,
SAML2_Const::STATUS_PROXY_COUNT_EXCEEDED,
$exception->getMessage(),
$exception
);
} else {
$e = new self(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment