예제 #1
0
 /**
  * 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);
         }
     }
 }
예제 #2
0
    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());
    }
예제 #3
0
 /**
  * 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);
     }
 }
예제 #4
0
 /**
  * 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);
     }
 }
예제 #5
0
 /**
  * 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');
 }
예제 #6
0
 /**
  * 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');
 }
예제 #7
0
 /**
  * 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;
         }
     }
 }
예제 #8
0
 /**
  * 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;
     }
 }
예제 #9
0
파일: Message.php 프로젝트: SysBind/saml2
 /**
  * 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);
 }
예제 #10
0
 /**
  * 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);
 }
예제 #11
0
 /**
  * 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);
     }
 }
예제 #12
0
 /**
  * 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);
 }
예제 #13
0
 /**
  * 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;
 }
예제 #14
0
 /**
  * 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);
     }
 }