Skip to content

Commit

Permalink
Merge pull request #137 from bjorpe/master
Browse files Browse the repository at this point in the history
Support for Conditions/AudienceRestriction/Audience in AuthnRequest
  • Loading branch information
thijskh authored Nov 23, 2018
2 parents 2330914 + df18268 commit 30e9c1b
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/SAML2/AuthnRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ class AuthnRequest extends Request
*/
private $requestedAuthnContext;

/**
* Audiences to send in the request.
*
* @var array
*/
private $audiences;

/**
* @var \SAML2\XML\saml\SubjectConfirmation[]
*/
Expand Down Expand Up @@ -168,6 +175,7 @@ public function __construct(\DOMElement $xml = null)
$this->parseNameIdPolicy($xml);
$this->parseRequestedAuthnContext($xml);
$this->parseScoping($xml);
$this->parseConditions($xml);
}

/**
Expand Down Expand Up @@ -295,6 +303,30 @@ protected function parseScoping(\DOMElement $xml)
}
}

/**
* @param \DOMElement $xml
*/
protected function parseConditions(\DOMElement $xml)
{
$conditions = Utils::xpQuery($xml, './saml_assertion:Conditions');
if (empty($conditions)) {
return;
}
$conditions = $conditions[0];

$ar = Utils::xpQuery($conditions, './saml_assertion:AudienceRestriction');
if (empty($ar)) {
return;
}
$ar = $ar[0];

$audiences = Utils::xpQuery($ar, './saml_assertion:Audience');
$this->audiences = array();
foreach ($audiences as $a) {
$this->audiences[] = trim($a->textContent);
}
}

/**
* Retrieve the NameIdPolicy.
*
Expand Down Expand Up @@ -404,6 +436,30 @@ public function setIsPassive($isPassive)
$this->isPassive = $isPassive;
}

/**
* Retrieve the audiences from the request.
*
* This may be null, in which case no audience is included.
*
* @return array|null The audiences.
*/
public function getAudiences()
{
return $this->audiences;
}

/**
* Set the audiences to send in the request.
*
* This may be null, in which case no audience will be sent.
*
* @param array|null $audiences The audiences.
*/
public function setAudiences(array $audiences = null)
{
$this->audiences = $audiences;
}


/**
* This function sets the scoping for the request.
Expand Down Expand Up @@ -732,6 +788,8 @@ public function toUnsignedXML()
$root->appendChild($nameIdPolicy);
}

$this->addConditions($root);

$rac = $this->requestedAuthnContext;
if (!empty($rac) && !empty($rac['AuthnContextClassRef'])) {
$e = $this->document->createElementNS(Constants::NS_SAMLP, 'RequestedAuthnContext');
Expand Down Expand Up @@ -806,4 +864,24 @@ private function addSubject(\DOMElement $root)
$sc->toXML($subject);
}
}

/**
* Add a Conditions-node to the request.
*
* @param \DOMElement $root The request element we should add the conditions to.
*/
private function addConditions(\DOMElement $root)
{
if ($this->audiences !== null) {
$document = $root->ownerDocument;

$conditions = $document->createElementNS(Constants::NS_SAML, 'saml:Conditions');
$root->appendChild($conditions);

$ar = $document->createElementNS(Constants::NS_SAML, 'saml:AudienceRestriction');
$conditions->appendChild($ar);

Utils::addStrings($ar, Constants::NS_SAML, 'saml:Audience', false, $this->audiences);
}
}
}
65 changes: 65 additions & 0 deletions tests/SAML2/AuthnRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -984,4 +984,69 @@ public function testEmptySubjectThrowsException()
$this->setExpectedException('Exception', 'Missing <saml:NameID> or <saml:EncryptedID> in <saml:Subject>');
$authnRequest = new AuthnRequest(DOMDocumentFactory::fromString($xml)->documentElement);
}

/**
* Test setting audiences.
*/
public function testAudiencesAreAddedCorrectly()
{
// basic AuthnRequest
$request = new AuthnRequest();
$request->setIssuer('https://gateway.example.org/saml20/sp/metadata');
$request->setDestination('https://tiqr.example.org/idp/profile/saml2/Redirect/SSO');
$request->setAudiences(array('https://sp1.example.org', 'https://sp2.example.org'));

$expectedStructureDocument = <<<AUTHNREQUEST
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID=""
Version=""
IssueInstant=""
Destination="https://tiqr.example.org/idp/profile/saml2/Redirect/SSO">
<saml:Issuer>https://gateway.example.org/saml20/sp/metadata</saml:Issuer>
<saml:Conditions>
<saml:AudienceRestriction>
<saml:Audience>https://sp1.example.org</saml:Audience>
<saml:Audience>https://sp2.example.org</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
</samlp:AuthnRequest>
AUTHNREQUEST;

$expectedStructure = DOMDocumentFactory::fromString($expectedStructureDocument)->documentElement;
$requestStructure = $request->toUnsignedXML();

$this->assertEqualXMLStructure($expectedStructure, $requestStructure);
}

/**
* Test reading audiences.
*/
public function testAudiencesAreReadCorrectly()
{
$expectedAudiences = array('https://sp1.example.org', 'https://sp2.example.org');

$xmlRequest = <<<AUTHNREQUEST
<samlp:AuthnRequest
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_1234567890abvdefghijkl"
Version="2.0"
IssueInstant="2015-05-11T09:02:36Z"
Destination="https://tiqr.example.org/idp/profile/saml2/Redirect/SSO">
<saml:Issuer>https://gateway.example.org/saml20/sp/metadata</saml:Issuer>
<saml:Conditions>
<saml:AudienceRestriction>
<saml:Audience>https://sp1.example.org</saml:Audience>
<saml:Audience>https://sp2.example.org</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
</samlp:AuthnRequest>
AUTHNREQUEST;

$authnRequest = new AuthnRequest(DOMDocumentFactory::fromString($xmlRequest)->firstChild);

$this->assertEquals($expectedAudiences, $authnRequest->getAudiences());
}
}

0 comments on commit 30e9c1b

Please sign in to comment.