/** * Initialize an EntitiesDescriptor. * * @param \DOMElement|null $xml The XML element we should load. */ public function __construct(\DOMElement $xml = null) { parent::__construct($xml); if ($xml === null) { return; } if ($xml->hasAttribute('ID')) { $this->ID = $xml->getAttribute('ID'); } if ($xml->hasAttribute('validUntil')) { $this->validUntil = Utils::xsDateTimeToTimestamp($xml->getAttribute('validUntil')); } if ($xml->hasAttribute('cacheDuration')) { $this->cacheDuration = $xml->getAttribute('cacheDuration'); } if ($xml->hasAttribute('Name')) { $this->Name = $xml->getAttribute('Name'); } $this->Extensions = Extensions::getList($xml); foreach (Utils::xpQuery($xml, './saml_metadata:EntityDescriptor|./saml_metadata:EntitiesDescriptor') as $node) { if ($node->localName === 'EntityDescriptor') { $this->children[] = new EntityDescriptor($node); } else { $this->children[] = new EntitiesDescriptor($node); } } }
public function testMarshallingOfSimpleRequest() { $document = new \DOMDocument(); $document->loadXML(<<<AUTHNREQUEST <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_306f8ec5b618f361c70b6ffb1480eade" Version="2.0" IssueInstant="2004-12-05T09:21:59Z" Destination="https://idp.example.org/SAML2/SSO/Artifact" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" AssertionConsumerServiceURL="https://sp.example.com/SAML2/SSO/Artifact"> <saml:Issuer>https://sp.example.com/SAML2</saml:Issuer> </samlp:AuthnRequest> AUTHNREQUEST ); $authnRequest = new AuthnRequest($document->documentElement); $expectedIssueInstant = Utils::xsDateTimeToTimestamp('2004-12-05T09:21:59Z'); $this->assertEquals($expectedIssueInstant, $authnRequest->getIssueInstant()); $this->assertEquals('https://idp.example.org/SAML2/SSO/Artifact', $authnRequest->getDestination()); $this->assertEquals('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact', $authnRequest->getProtocolBinding()); $this->assertEquals('https://sp.example.com/SAML2/SSO/Artifact', $authnRequest->getAssertionConsumerServiceURL()); $this->assertEquals('https://sp.example.com/SAML2', $authnRequest->getIssuer()); }
/** * Initialize a AffiliationDescriptor. * * @param \DOMElement|null $xml The XML element we should load. * @throws \Exception */ public function __construct(\DOMElement $xml = null) { parent::__construct($xml); if ($xml === null) { return; } if (!$xml->hasAttribute('affiliationOwnerID')) { throw new \Exception('Missing affiliationOwnerID on AffiliationDescriptor.'); } $this->affiliationOwnerID = $xml->getAttribute('affiliationOwnerID'); if ($xml->hasAttribute('ID')) { $this->ID = $xml->getAttribute('ID'); } if ($xml->hasAttribute('validUntil')) { $this->validUntil = Utils::xsDateTimeToTimestamp($xml->getAttribute('validUntil')); } if ($xml->hasAttribute('cacheDuration')) { $this->cacheDuration = $xml->getAttribute('cacheDuration'); } $this->Extensions = Extensions::getList($xml); $this->AffiliateMember = Utils::extractStrings($xml, Constants::NS_MD, 'AffiliateMember'); if (empty($this->AffiliateMember)) { throw new \Exception('Missing AffiliateMember in AffiliationDescriptor.'); } foreach (Utils::xpQuery($xml, './saml_metadata:KeyDescriptor') as $kd) { $this->KeyDescriptor[] = new KeyDescriptor($kd); } }
/** * Constructor for SAML 2 logout request messages. * * @param \DOMElement|null $xml The input message. * @throws \Exception */ public function __construct(\DOMElement $xml = null) { parent::__construct('LogoutRequest', $xml); $this->sessionIndexes = array(); if ($xml === null) { return; } if ($xml->hasAttribute('NotOnOrAfter')) { $this->notOnOrAfter = Utils::xsDateTimeToTimestamp($xml->getAttribute('NotOnOrAfter')); } $nameId = Utils::xpQuery($xml, './saml_assertion:NameID | ./saml_assertion:EncryptedID/xenc:EncryptedData'); if (empty($nameId)) { throw new \Exception('Missing <saml:NameID> or <saml:EncryptedID> in <samlp:LogoutRequest>.'); } elseif (count($nameId) > 1) { throw new \Exception('More than one <saml:NameID> or <saml:EncryptedD> in <samlp:LogoutRequest>.'); } $nameId = $nameId[0]; if ($nameId->localName === 'EncryptedData') { /* The NameID element is encrypted. */ $this->encryptedNameId = $nameId; } else { $this->nameId = Utils::parseNameId($nameId); } $sessionIndexes = Utils::xpQuery($xml, './saml_protocol:SessionIndex'); foreach ($sessionIndexes as $sessionIndex) { $this->sessionIndexes[] = trim($sessionIndex->textContent); } }
/** * Create/parse a mdrpi:RegistrationInfo element. * * @param \DOMElement|null $xml The XML element we should load. * @throws \Exception */ public function __construct(\DOMElement $xml = null) { if ($xml === null) { return; } if (!$xml->hasAttribute('registrationAuthority')) { throw new \Exception('Missing required attribute "registrationAuthority" in mdrpi:RegistrationInfo element.'); } $this->registrationAuthority = $xml->getAttribute('registrationAuthority'); if ($xml->hasAttribute('registrationInstant')) { $this->registrationInstant = Utils::xsDateTimeToTimestamp($xml->getAttribute('registrationInstant')); } $this->RegistrationPolicy = Utils::extractLocalizedStrings($xml, Common::NS_MDRPI, 'RegistrationPolicy'); }
/** * Create/parse a mdrpi:PublicationInfo element. * * @param \DOMElement|null $xml The XML element we should load. * @throws \Exception */ public function __construct(\DOMElement $xml = null) { if ($xml === null) { return; } if (!$xml->hasAttribute('publisher')) { throw new \Exception('Missing required attribute "publisher" in mdrpi:PublicationInfo element.'); } $this->publisher = $xml->getAttribute('publisher'); if ($xml->hasAttribute('creationInstant')) { $this->creationInstant = Utils::xsDateTimeToTimestamp($xml->getAttribute('creationInstant')); } if ($xml->hasAttribute('publicationId')) { $this->publicationId = $xml->getAttribute('publicationId'); } $this->UsagePolicy = Utils::extractLocalizedStrings($xml, Common::NS_MDRPI, 'UsagePolicy'); }
/** * Initialize (and parse) a SubjectConfirmationData element. * * @param \DOMElement|null $xml The XML element we should load. */ public function __construct(\DOMElement $xml = null) { if ($xml === null) { return; } if ($xml->hasAttribute('NotBefore')) { $this->NotBefore = Utils::xsDateTimeToTimestamp($xml->getAttribute('NotBefore')); } if ($xml->hasAttribute('NotOnOrAfter')) { $this->NotOnOrAfter = Utils::xsDateTimeToTimestamp($xml->getAttribute('NotOnOrAfter')); } if ($xml->hasAttribute('Recipient')) { $this->Recipient = $xml->getAttribute('Recipient'); } if ($xml->hasAttribute('InResponseTo')) { $this->InResponseTo = $xml->getAttribute('InResponseTo'); } if ($xml->hasAttribute('Address')) { $this->Address = $xml->getAttribute('Address'); } for ($n = $xml->firstChild; $n !== null; $n = $n->nextSibling) { if (!$n instanceof \DOMElement) { continue; } if ($n->namespaceURI !== XMLSecurityDSig::XMLDSIGNS) { $this->info[] = new Chunk($n); continue; } switch ($n->localName) { case 'KeyInfo': $this->info[] = new KeyInfo($n); break; default: $this->info[] = new Chunk($n); break; } } }
/** * Add extensions to the metadata. * * @param SimpleSAML_Configuration $metadata The metadata to get extensions from. * @param \SAML2\XML\md\RoleDescriptor $e Reference to the element where the Extensions element should be included. */ private function addExtensions(SimpleSAML_Configuration $metadata, \SAML2\XML\md\RoleDescriptor $e) { if ($metadata->hasValue('tags')) { $a = new \SAML2\XML\saml\Attribute(); $a->Name = 'tags'; foreach ($metadata->getArray('tags') as $tag) { $a->AttributeValue[] = new \SAML2\XML\saml\AttributeValue($tag); } $e->Extensions[] = $a; } if ($metadata->hasValue('hint.cidr')) { $a = new \SAML2\XML\saml\Attribute(); $a->Name = 'hint.cidr'; foreach ($metadata->getArray('hint.cidr') as $hint) { $a->AttributeValue[] = new \SAML2\XML\saml\AttributeValue($hint); } $e->Extensions[] = $a; } if ($metadata->hasValue('scope')) { foreach ($metadata->getArray('scope') as $scopetext) { $s = new \SAML2\XML\shibmd\Scope(); $s->scope = $scopetext; // Check whether $ ^ ( ) * | \ are in a scope -> assume regex. if (1 === preg_match('/[\\$\\^\\)\\(\\*\\|\\\\]/', $scopetext)) { $s->regexp = true; } else { $s->regexp = false; } $e->Extensions[] = $s; } } if ($metadata->hasValue('EntityAttributes')) { $ea = new \SAML2\XML\mdattr\EntityAttributes(); foreach ($metadata->getArray('EntityAttributes') as $attributeName => $attributeValues) { $a = new \SAML2\XML\saml\Attribute(); $a->Name = $attributeName; $a->NameFormat = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'; // Attribute names that is not URI is prefixed as this: '{nameformat}name' if (preg_match('/^\\{(.*?)\\}(.*)$/', $attributeName, $matches)) { $a->Name = $matches[2]; $nameFormat = $matches[1]; if ($nameFormat !== \SAML2\Constants::NAMEFORMAT_UNSPECIFIED) { $a->NameFormat = $nameFormat; } } foreach ($attributeValues as $attributeValue) { $a->AttributeValue[] = new \SAML2\XML\saml\AttributeValue($attributeValue); } $ea->children[] = $a; } $this->entityDescriptor->Extensions[] = $ea; } if ($metadata->hasValue('RegistrationInfo')) { $ri = new \SAML2\XML\mdrpi\RegistrationInfo(); foreach ($metadata->getArray('RegistrationInfo') as $riName => $riValues) { switch ($riName) { case 'authority': $ri->registrationAuthority = $riValues; break; case 'instant': $ri->registrationInstant = \SAML2\Utils::xsDateTimeToTimestamp($riValues); break; case 'policies': $ri->RegistrationPolicy = $riValues; break; } } $this->entityDescriptor->Extensions[] = $ri; } if ($metadata->hasValue('UIInfo')) { $ui = new \SAML2\XML\mdui\UIInfo(); foreach ($metadata->getArray('UIInfo') as $uiName => $uiValues) { switch ($uiName) { case 'DisplayName': $ui->DisplayName = $uiValues; break; case 'Description': $ui->Description = $uiValues; break; case 'InformationURL': $ui->InformationURL = $uiValues; break; case 'PrivacyStatementURL': $ui->PrivacyStatementURL = $uiValues; break; case 'Keywords': foreach ($uiValues as $lang => $keywords) { $uiItem = new \SAML2\XML\mdui\Keywords(); $uiItem->lang = $lang; $uiItem->Keywords = $keywords; $ui->Keywords[] = $uiItem; } break; case 'Logo': foreach ($uiValues as $logo) { $uiItem = new \SAML2\XML\mdui\Logo(); $uiItem->url = $logo['url']; $uiItem->width = $logo['width']; $uiItem->height = $logo['height']; if (isset($logo['lang'])) { $uiItem->lang = $logo['lang']; } $ui->Logo[] = $uiItem; } break; } } $e->Extensions[] = $ui; } if ($metadata->hasValue('DiscoHints')) { $dh = new \SAML2\XML\mdui\DiscoHints(); foreach ($metadata->getArray('DiscoHints') as $dhName => $dhValues) { switch ($dhName) { case 'IPHint': $dh->IPHint = $dhValues; break; case 'DomainHint': $dh->DomainHint = $dhValues; break; case 'GeolocationHint': $dh->GeolocationHint = $dhValues; break; } } $e->Extensions[] = $dh; } }
/** * Initialize a message. * * This constructor takes an optional parameter with a \DOMElement. If this * parameter is given, the message will be initialized with data from that * XML element. * * If no XML element is given, the message is initialized with suitable * default values. * * @param string $tagName The tag name of the root element. * @param \DOMElement|null $xml The input message. * @throws \Exception */ protected function __construct($tagName, \DOMElement $xml = null) { assert('is_string($tagName)'); $this->tagName = $tagName; $this->id = Utils::getContainer()->generateId(); $this->issueInstant = Temporal::getTime(); $this->certificates = array(); $this->validators = array(); if ($xml === null) { return; } if (!$xml->hasAttribute('ID')) { throw new \Exception('Missing ID attribute on SAML message.'); } $this->id = $xml->getAttribute('ID'); if ($xml->getAttribute('Version') !== '2.0') { /* Currently a very strict check. */ throw new \Exception('Unsupported version: ' . $xml->getAttribute('Version')); } $this->issueInstant = Utils::xsDateTimeToTimestamp($xml->getAttribute('IssueInstant')); if ($xml->hasAttribute('Destination')) { $this->destination = $xml->getAttribute('Destination'); } if ($xml->hasAttribute('Consent')) { $this->consent = $xml->getAttribute('Consent'); } $issuer = Utils::xpQuery($xml, './saml_assertion:Issuer'); if (!empty($issuer)) { $this->issuer = trim($issuer[0]->textContent); } /* Validate the signature element of the message. */ try { $sig = Utils::validateElement($xml); if ($sig !== false) { $this->messageContainedSignatureUponConstruction = true; $this->certificates = $sig['Certificates']; $this->validators[] = array('Function' => array('\\SAML2\\Utils', 'validateSignature'), 'Data' => $sig); } } catch (\Exception $e) { /* Ignore signature validation errors. */ } $this->extensions = Extensions::getList($xml); }
/** * Initialize a message. * * This constructor takes an optional parameter with a \DOMElement. If this * parameter is given, the message will be initialized with data from that * XML element. * * If no XML element is given, the message is initialized with suitable * default values. * * @param string $tagName The tag name of the root element * @param \DOMElement|null $xml The input message * * @throws \Exception */ protected function __construct($tagName, \DOMElement $xml = null) { assert('is_string($tagName)'); $this->tagName = $tagName; $this->id = Utils::getContainer()->generateId(); $this->issueInstant = Temporal::getTime(); $this->certificates = array(); $this->validators = array(); if ($xml === null) { return; } if (!$xml->hasAttribute('ID')) { throw new \Exception('Missing ID attribute on SAML message.'); } $this->id = $xml->getAttribute('ID'); if ($xml->getAttribute('Version') !== '2.0') { /* Currently a very strict check. */ throw new \Exception('Unsupported version: ' . $xml->getAttribute('Version')); } $this->issueInstant = Utils::xsDateTimeToTimestamp($xml->getAttribute('IssueInstant')); if ($xml->hasAttribute('Destination')) { $this->destination = $xml->getAttribute('Destination'); } if ($xml->hasAttribute('Consent')) { $this->consent = $xml->getAttribute('Consent'); } $issuer = Utils::xpQuery($xml, './saml_assertion:Issuer'); if (!empty($issuer)) { $this->issuer = new XML\saml\Issuer($issuer[0]); if ($this->issuer->Format === Constants::NAMEID_ENTITY) { $this->issuer = $this->issuer->value; } } $this->validateSignature($xml); $this->extensions = Extensions::getList($xml); }
/** * Initialize a RoleDescriptor. * * @param string $elementName The name of this element. * @param \DOMElement|null $xml The XML element we should load. * @throws \Exception */ protected function __construct($elementName, \DOMElement $xml = null) { assert('is_string($elementName)'); parent::__construct($xml); $this->elementName = $elementName; if ($xml === null) { return; } if ($xml->hasAttribute('ID')) { $this->ID = $xml->getAttribute('ID'); } if ($xml->hasAttribute('validUntil')) { $this->validUntil = Utils::xsDateTimeToTimestamp($xml->getAttribute('validUntil')); } if ($xml->hasAttribute('cacheDuration')) { $this->cacheDuration = $xml->getAttribute('cacheDuration'); } if (!$xml->hasAttribute('protocolSupportEnumeration')) { throw new \Exception('Missing protocolSupportEnumeration attribute on ' . $xml->localName); } $this->protocolSupportEnumeration = preg_split('/[\\s]+/', $xml->getAttribute('protocolSupportEnumeration')); if ($xml->hasAttribute('errorURL')) { $this->errorURL = $xml->getAttribute('errorURL'); } $this->Extensions = Extensions::getList($xml); foreach (Utils::xpQuery($xml, './saml_metadata:KeyDescriptor') as $kd) { $this->KeyDescriptor[] = new KeyDescriptor($kd); } $organization = Utils::xpQuery($xml, './saml_metadata:Organization'); if (count($organization) > 1) { throw new \Exception('More than one Organization in the entity.'); } elseif (!empty($organization)) { $this->Organization = new Organization($organization[0]); } foreach (Utils::xpQuery($xml, './saml_metadata:ContactPerson') as $cp) { $this->contactPersons[] = new ContactPerson($cp); } }
/** * Parse AuthnStatement in assertion. * * @param \DOMElement $xml The assertion XML element. * @throws \Exception */ private function parseAuthnStatement(\DOMElement $xml) { $authnStatements = Utils::xpQuery($xml, './saml_assertion:AuthnStatement'); if (empty($authnStatements)) { $this->authnInstant = null; return; } elseif (count($authnStatements) > 1) { throw new \Exception('More than one <saml:AuthnStatement> in <saml:Assertion> not supported.'); } $authnStatement = $authnStatements[0]; if (!$authnStatement->hasAttribute('AuthnInstant')) { throw new \Exception('Missing required AuthnInstant attribute on <saml:AuthnStatement>.'); } $this->authnInstant = Utils::xsDateTimeToTimestamp($authnStatement->getAttribute('AuthnInstant')); if ($authnStatement->hasAttribute('SessionNotOnOrAfter')) { $this->sessionNotOnOrAfter = Utils::xsDateTimeToTimestamp($authnStatement->getAttribute('SessionNotOnOrAfter')); } if ($authnStatement->hasAttribute('SessionIndex')) { $this->sessionIndex = $authnStatement->getAttribute('SessionIndex'); } $this->parseAuthnContext($authnStatement); }
/** * Check if we are currently between the given date & time conditions. * * Note that this function allows a 10-minute leap from the initial time as marked by $start. * * @param string|null $start A SAML2 timestamp marking the start of the period to check. Defaults to null, in which * case there's no limitations in the past. * @param string|null $end A SAML2 timestamp marking the end of the period to check. Defaults to null, in which * case there's no limitations in the future. * * @return bool True if the current time belongs to the period specified by $start and $end. False otherwise. * * @see \SAML2\Utils::xsDateTimeToTimestamp. * * @author Andreas Solberg, UNINETT AS <*****@*****.**> * @author Olav Morken, UNINETT AS <*****@*****.**> */ protected static function checkDateConditions($start = null, $end = null) { $currentTime = time(); if (!empty($start)) { $startTime = \SAML2\Utils::xsDateTimeToTimestamp($start); // allow for a 10 minute difference in time if ($startTime < 0 || $startTime - 600 > $currentTime) { return false; } } if (!empty($end)) { $endTime = \SAML2\Utils::xsDateTimeToTimestamp($end); if ($endTime < 0 || $endTime <= $currentTime) { return false; } } return true; }
/** * Initialize an EntitiyDescriptor. * * @param \DOMElement|null $xml The XML element we should load. * @throws \Exception */ public function __construct(\DOMElement $xml = null) { parent::__construct($xml); if ($xml === null) { return; } if (!$xml->hasAttribute('entityID')) { throw new \Exception('Missing required attribute entityID on EntityDescriptor.'); } $this->entityID = $xml->getAttribute('entityID'); if ($xml->hasAttribute('ID')) { $this->ID = $xml->getAttribute('ID'); } if ($xml->hasAttribute('validUntil')) { $this->validUntil = Utils::xsDateTimeToTimestamp($xml->getAttribute('validUntil')); } if ($xml->hasAttribute('cacheDuration')) { $this->cacheDuration = $xml->getAttribute('cacheDuration'); } $this->Extensions = Extensions::getList($xml); for ($node = $xml->firstChild; $node !== null; $node = $node->nextSibling) { if (!$node instanceof \DOMElement) { continue; } if ($node->namespaceURI !== Constants::NS_MD) { continue; } switch ($node->localName) { case 'RoleDescriptor': $this->RoleDescriptor[] = new UnknownRoleDescriptor($node); break; case 'IDPSSODescriptor': $this->RoleDescriptor[] = new IDPSSODescriptor($node); break; case 'SPSSODescriptor': $this->RoleDescriptor[] = new SPSSODescriptor($node); break; case 'AuthnAuthorityDescriptor': $this->RoleDescriptor[] = new AuthnAuthorityDescriptor($node); break; case 'AttributeAuthorityDescriptor': $this->RoleDescriptor[] = new AttributeAuthorityDescriptor($node); break; case 'PDPDescriptor': $this->RoleDescriptor[] = new PDPDescriptor($node); break; } } $affiliationDescriptor = Utils::xpQuery($xml, './saml_metadata:AffiliationDescriptor'); if (count($affiliationDescriptor) > 1) { throw new \Exception('More than one AffiliationDescriptor in the entity.'); } elseif (!empty($affiliationDescriptor)) { $this->AffiliationDescriptor = new AffiliationDescriptor($affiliationDescriptor[0]); } if (empty($this->RoleDescriptor) && is_null($this->AffiliationDescriptor)) { throw new \Exception('Must have either one of the RoleDescriptors or an AffiliationDescriptor in EntityDescriptor.'); } elseif (!empty($this->RoleDescriptor) && !is_null($this->AffiliationDescriptor)) { throw new \Exception('AffiliationDescriptor cannot be combined with other RoleDescriptor elements in EntityDescriptor.'); } $organization = Utils::xpQuery($xml, './saml_metadata:Organization'); if (count($organization) > 1) { throw new \Exception('More than one Organization in the entity.'); } elseif (!empty($organization)) { $this->Organization = new Organization($organization[0]); } foreach (Utils::xpQuery($xml, './saml_metadata:ContactPerson') as $cp) { $this->ContactPerson[] = new ContactPerson($cp); } foreach (Utils::xpQuery($xml, './saml_metadata:AdditionalMetadataLocation') as $aml) { $this->AdditionalMetadataLocation[] = new AdditionalMetadataLocation($aml); } }