24 'urn:oasis:names:tc:SAML:1.0:protocol',
25 'urn:oasis:names:tc:SAML:1.1:protocol',
35 'urn:oasis:names:tc:SAML:2.0:protocol',
165 \
SAML2\XML\md\EntityDescriptor $entityElement,
168 array $parentExtensions = null
170 assert(
'is_null($maxExpireTime) || is_int($maxExpireTime)');
172 $this->spDescriptors =
array();
173 $this->idpDescriptors =
array();
175 $e = $entityElement->toXML();
176 $e = $e->ownerDocument->saveXML($e);
177 $this->entityDescriptor = base64_encode($e);
178 $this->entityId = $entityElement->entityID;
180 $expireTime = self::getExpireTime($entityElement, $maxExpireTime);
183 $this->validators[] = $entityElement;
186 $ext = self::processExtensions($entityElement, $parentExtensions);
187 $this->scopes = $ext[
'scope'];
188 $this->tags = $ext[
'tags'];
189 $this->entityAttributes = $ext[
'EntityAttributes'];
190 $this->registrationInfo = $ext[
'RegistrationInfo'];
193 foreach ($entityElement->RoleDescriptor as $child) {
195 if ($child instanceof \
SAML2\XML\md\SPSSODescriptor) {
197 } elseif ($child instanceof \
SAML2\XML\md\IDPSSODescriptor) {
199 } elseif ($child instanceof \
SAML2\XML\md\AttributeAuthorityDescriptor) {
204 if ($entityElement->Organization) {
208 if (!empty($entityElement->ContactPerson)) {
209 foreach ($entityElement->ContactPerson as $contact) {
234 return self::parseDocument($doc);
251 throw new Exception(
'Failed to parse XML string.');
254 return self::parseDocument($doc);
267 assert(
'$document instanceof DOMDocument');
269 $entityElement = self::findEntityDescriptor($document);
271 return self::parseElement($entityElement);
285 assert(
'$entityElement instanceof \SAML2\XML\md\EntityDescriptor');
305 if (
$file === null) {
306 throw new Exception(
'Cannot open file NULL. File name not specified.');
317 if ($doc->documentElement === null) {
318 throw new Exception(
'Opened file is not an XML document: '.
$file);
321 return self::parseDescriptorsElement($doc->documentElement);
341 throw new Exception(
'Failed to parse XML string.');
344 return self::parseDescriptorsElement($doc->documentElement);
361 if ($element === null) {
362 throw new Exception(
'Document was empty.');
365 if (
SimpleSAML\Utils\XML::isDOMNodeOfType($element,
'EntityDescriptor',
'@md') ===
true) {
366 return self::processDescriptorsElement(
new \
SAML2\XML\md\EntityDescriptor($element));
367 } elseif (
SimpleSAML\Utils\XML::isDOMNodeOfType($element,
'EntitiesDescriptor',
'@md') ===
true) {
368 return self::processDescriptorsElement(
new \
SAML2\XML\md\EntitiesDescriptor($element));
370 throw new Exception(
'Unexpected root node: ['.$element->namespaceURI.
']:'.$element->localName);
387 private static function processDescriptorsElement(
389 $maxExpireTime = null,
393 assert(
'is_null($maxExpireTime) || is_int($maxExpireTime)');
395 if ($element instanceof \
SAML2\XML\md\EntityDescriptor) {
402 assert(
'$element instanceof \SAML2\XML\md\EntitiesDescriptor');
404 $extensions = self::processExtensions($element, $parentExtensions);
405 $expTime = self::getExpireTime($element, $maxExpireTime);
410 foreach ($element->children as $child) {
411 $ret += self::processDescriptorsElement($child, $expTime,
$validators, $extensions);
433 $expire = $element->validUntil;
435 if ($maxExpireTime !== null && (
$expire === null || $maxExpireTime <
$expire)) {
461 if (!empty($this->organizationName)) {
465 if (!empty($this->organizationDisplayName)) {
469 if (!empty($this->organizationURL)) {
489 assert(
'array_key_exists("scope", $roleDescriptor)');
490 assert(
'array_key_exists("tags", $roleDescriptor)');
492 $scopes = array_merge($this->scopes, array_diff($roleDescriptor[
'scope'], $this->scopes));
497 $tags = array_merge($this->tags, array_diff($roleDescriptor[
'tags'], $this->tags));
499 $metadata[
'tags'] =
$tags;
503 if (!empty($this->registrationInfo)) {
507 if (!empty($this->entityAttributes)) {
511 if (
SimpleSAML\Utils\Config\Metadata::isHiddenFromDiscovery($metadata)) {
512 $metadata[
'hide.from.discovery'] =
true;
516 if (!empty($roleDescriptor[
'UIInfo'])) {
517 $metadata[
'UIInfo'] = $roleDescriptor[
'UIInfo'];
520 if (!empty($roleDescriptor[
'DiscoHints'])) {
521 $metadata[
'DiscoHints'] = $roleDescriptor[
'DiscoHints'];
541 $ret[
'metadata-set'] =
'shib13-sp-remote';
546 if (count($spd) === 0) {
554 if (array_key_exists(
'expire', $spd)) {
555 $ret[
'expire'] = $spd[
'expire'];
559 $ret[
'AssertionConsumerService'] = $spd[
'AssertionConsumerService'];
562 if (array_key_exists(
'attributes', $spd)) {
563 $ret[
'attributes'] = $spd[
'attributes'];
565 if (array_key_exists(
'attributes.required', $spd)) {
566 $ret[
'attributes.required'] = $spd[
'attributes.required'];
568 if (array_key_exists(
'attributes.NameFormat', $spd)) {
569 $ret[
'attributes.NameFormat'] = $spd[
'attributes.NameFormat'];
573 if (array_key_exists(
'name', $spd)) {
574 $ret[
'name'] = $spd[
'name'];
576 if (array_key_exists(
'description', $spd)) {
577 $ret[
'description'] = $spd[
'description'];
581 if (!empty($spd[
'keys'])) {
582 $ret[
'keys'] = $spd[
'keys'];
589 if (!empty(
$ret[
'UIInfo'][
'DisplayName'])) {
590 $ret[
'name'] =
$ret[
'UIInfo'][
'DisplayName'];
615 $ret[
'metadata-set'] =
'shib13-idp-remote';
619 if (count(
$idp) === 0) {
627 if (array_key_exists(
'expire',
$idp)) {
632 $ret[
'SingleSignOnService'] =
$idp[
'SingleSignOnService'];
635 $ret[
'ArtifactResolutionService'] =
$idp[
'ArtifactResolutionService'];
638 if (!empty(
$idp[
'keys'])) {
646 if (!empty(
$ret[
'UIInfo'][
'DisplayName'])) {
647 $ret[
'name'] =
$ret[
'UIInfo'][
'DisplayName'];
671 $ret[
'metadata-set'] =
'saml20-sp-remote';
675 if (count($spd) === 0) {
683 if (array_key_exists(
'expire', $spd)) {
684 $ret[
'expire'] = $spd[
'expire'];
688 $ret[
'AssertionConsumerService'] = $spd[
'AssertionConsumerService'];
692 $ret[
'SingleLogoutService'] = $spd[
'SingleLogoutService'];
696 if (count($spd[
'nameIDFormats']) > 0) {
698 $ret[
'NameIDFormat'] = $spd[
'nameIDFormats'][0];
702 if (array_key_exists(
'attributes', $spd)) {
703 $ret[
'attributes'] = $spd[
'attributes'];
705 if (array_key_exists(
'attributes.required', $spd)) {
706 $ret[
'attributes.required'] = $spd[
'attributes.required'];
708 if (array_key_exists(
'attributes.NameFormat', $spd)) {
709 $ret[
'attributes.NameFormat'] = $spd[
'attributes.NameFormat'];
713 if (array_key_exists(
'name', $spd)) {
714 $ret[
'name'] = $spd[
'name'];
716 if (array_key_exists(
'description', $spd)) {
717 $ret[
'description'] = $spd[
'description'];
721 if (!empty($spd[
'keys'])) {
722 $ret[
'keys'] = $spd[
'keys'];
726 if (array_key_exists(
'AuthnRequestsSigned', $spd)) {
727 $ret[
'validate.authnrequest'] = $spd[
'AuthnRequestsSigned'];
731 if (array_key_exists(
'WantAssertionsSigned', $spd)) {
732 $ret[
'saml20.sign.assertion'] = $spd[
'WantAssertionsSigned'];
739 if (!empty(
$ret[
'UIInfo'][
'DisplayName'])) {
740 $ret[
'name'] =
$ret[
'UIInfo'][
'DisplayName'];
768 $ret[
'metadata-set'] =
'saml20-idp-remote';
772 if (count(
$idp) === 0) {
780 if (array_key_exists(
'expire',
$idp)) {
785 if (
$idp[
'WantAuthnRequestsSigned']) {
786 $ret[
'sign.authnrequest'] =
true;
790 $ret[
'SingleSignOnService'] =
$idp[
'SingleSignOnService'];
793 $ret[
'SingleLogoutService'] =
$idp[
'SingleLogoutService'];
796 $ret[
'ArtifactResolutionService'] =
$idp[
'ArtifactResolutionService'];
799 $ret[
'NameIDFormats'] =
$idp[
'nameIDFormats'];
802 if (!empty(
$idp[
'keys'])) {
810 if (!empty(
$ret[
'UIInfo'][
'DisplayName'])) {
811 $ret[
'name'] =
$ret[
'UIInfo'][
'DisplayName'];
845 assert(
'is_null($expireTime) || is_int($expireTime)');
849 $expireTime = self::getExpireTime($element, $expireTime);
851 if ($expireTime !== null) {
853 $ret[
'expire'] = $expireTime;
856 $ret[
'protocols'] = $element->protocolSupportEnumeration;
860 foreach ($element->KeyDescriptor as $kd) {
861 $key = self::parseKeyDescriptor($kd);
867 $ext = self::processExtensions($element);
868 $ret[
'scope'] = $ext[
'scope'];
869 $ret[
'tags'] = $ext[
'tags'];
870 $ret[
'EntityAttributes'] = $ext[
'EntityAttributes'];
871 $ret[
'UIInfo'] = $ext[
'UIInfo'];
872 $ret[
'DiscoHints'] = $ext[
'DiscoHints'];
896 assert(
'is_null($expireTime) || is_int($expireTime)');
898 $sd = self::parseRoleDescriptorType($element, $expireTime);
901 $sd[
'SingleLogoutService'] = self::extractEndpoints($element->SingleLogoutService);
904 $sd[
'ArtifactResolutionService'] = self::extractEndpoints($element->ArtifactResolutionService);
908 $sd[
'nameIDFormats'] = $element->NameIDFormat;
923 assert(
'is_null($expireTime) || is_int($expireTime)');
925 $sp = self::parseSSODescriptor($element, $expireTime);
928 $sp[
'AssertionConsumerService'] = self::extractEndpoints($element->AssertionConsumerService);
931 $attcs = $element->AttributeConsumingService;
932 if (count($attcs) > 0) {
933 self::parseAttributeConsumerService($attcs[0], $sp);
937 if ($element->AuthnRequestsSigned !== null) {
938 $sp[
'AuthnRequestsSigned'] = $element->AuthnRequestsSigned;
942 if ($element->WantAssertionsSigned !== null) {
943 $sp[
'WantAssertionsSigned'] = $element->WantAssertionsSigned;
946 $this->spDescriptors[] = $sp;
959 assert(
'is_null($expireTime) || is_int($expireTime)');
961 $idp = self::parseSSODescriptor($element, $expireTime);
964 $idp[
'SingleSignOnService'] = self::extractEndpoints($element->SingleSignOnService);
966 if ($element->WantAuthnRequestsSigned) {
967 $idp[
'WantAuthnRequestsSigned'] =
true;
969 $idp[
'WantAuthnRequestsSigned'] =
false;
972 $this->idpDescriptors[] =
$idp;
984 \
SAML2\XML\md\AttributeAuthorityDescriptor $element,
987 assert(
'is_null($expireTime) || is_int($expireTime)');
989 $aad = self::parseRoleDescriptorType($element, $expireTime);
991 $aad[
'metadata-set'] =
'attributeauthority-remote';
993 $aad[
'AttributeService'] = self::extractEndpoints($element->AttributeService);
994 $aad[
'AssertionIDRequestService'] = self::extractEndpoints($element->AssertionIDRequestService);
995 $aad[
'NameIDFormat'] = $element->NameIDFormat;
997 $this->attributeAuthorityDescriptors[] = $aad;
1015 'EntityAttributes' =>
array(),
1016 'RegistrationInfo' =>
array(),
1017 'UIInfo' =>
array(),
1018 'DiscoHints' =>
array(),
1022 if (($element instanceof \
SAML2\XML\md\EntityDescriptor || $element instanceof \
SAML2\XML\md\EntitiesDescriptor)
1023 && !empty($parentExtensions[
'RegistrationInfo'])) {
1024 $ret[
'RegistrationInfo'] = $parentExtensions[
'RegistrationInfo'];
1027 foreach ($element->Extensions as $e) {
1029 if ($e instanceof \
SAML2\XML\shibmd\Scope) {
1030 $ret[
'scope'][] = $e->scope;
1035 if ($element instanceof \
SAML2\XML\md\EntityDescriptor ||
1036 $element instanceof \
SAML2\XML\md\EntitiesDescriptor) {
1039 if ($e instanceof \
SAML2\XML\mdrpi\RegistrationInfo) {
1041 if (isset(
$ret[
'RegistrationInfo'][
'registrationAuthority'])
1042 &&
$ret[
'RegistrationInfo'][
'registrationAuthority'] !== $e->registrationAuthority) {
1044 .
$ret[
'RegistrationInfo'][
'registrationAuthority'] .
"' with '{$e->registrationAuthority}'");
1046 $ret[
'RegistrationInfo'][
'registrationAuthority'] = $e->registrationAuthority;
1049 if ($e instanceof \
SAML2\XML\mdattr\EntityAttributes && !empty($e->children)) {
1050 foreach ($e->children as $attr) {
1053 if ($attr instanceof \
SAML2\XML\saml\Attribute) {
1054 if (empty($attr->Name) || empty($attr->AttributeValue)) {
1059 $name = $attr->Name;
1060 if (empty($attr->NameFormat)) {
1061 $name =
'{'.\SAML2\Constants::NAMEFORMAT_UNSPECIFIED.
'}'.$attr->Name;
1062 } elseif ($attr->NameFormat !==
'urn:oasis:names:tc:SAML:2.0:attrname-format:uri') {
1063 $name =
'{'.$attr->NameFormat.
'}'.$attr->Name;
1067 foreach ($attr->AttributeValue as $attrvalue) {
1068 $values[] = $attrvalue->getString();
1071 $ret[
'EntityAttributes'][
$name] = $values;
1078 if ($element instanceof \
SAML2\XML\md\RoleDescriptor) {
1079 if ($e instanceof \
SAML2\XML\mdui\UIInfo) {
1081 $ret[
'UIInfo'][
'DisplayName'] = $e->DisplayName;
1082 $ret[
'UIInfo'][
'Description'] = $e->Description;
1083 $ret[
'UIInfo'][
'InformationURL'] = $e->InformationURL;
1084 $ret[
'UIInfo'][
'PrivacyStatementURL'] = $e->PrivacyStatementURL;
1086 foreach ($e->Keywords as $uiItem) {
1087 if (!($uiItem instanceof \
SAML2\XML\mdui\Keywords)
1088 || empty($uiItem->Keywords)
1089 || empty($uiItem->lang)
1093 $ret[
'UIInfo'][
'Keywords'][$uiItem->lang] = $uiItem->Keywords;
1095 foreach ($e->Logo as $uiItem) {
1096 if (!($uiItem instanceof \
SAML2\XML\mdui\Logo)
1097 || empty($uiItem->url)
1098 || empty($uiItem->height)
1099 || empty($uiItem->width)
1104 'url' => $uiItem->url,
1105 'height' => $uiItem->height,
1106 'width' => $uiItem->width,
1108 if (!empty($uiItem->lang)) {
1109 $logo[
'lang'] = $uiItem->lang;
1111 $ret[
'UIInfo'][
'Logo'][] = $logo;
1117 if ($element instanceof \
SAML2\XML\md\IDPSSODescriptor) {
1119 if ($e instanceof \
SAML2\XML\mdui\DiscoHints) {
1120 $ret[
'DiscoHints'][
'IPHint'] = $e->IPHint;
1121 $ret[
'DiscoHints'][
'DomainHint'] = $e->DomainHint;
1122 $ret[
'DiscoHints'][
'GeolocationHint'] = $e->GeolocationHint;
1126 if (!($e instanceof \
SAML2\XML\Chunk)) {
1130 if ($e->localName ===
'Attribute' && $e->namespaceURI === \
SAML2\Constants::NS_SAML) {
1131 $attribute = $e->getXML();
1133 $name = $attribute->getAttribute(
'Name');
1134 $values = array_map(
1135 array(
'SimpleSAML\Utils\XML',
'getDOMText'),
1136 SimpleSAML\Utils\XML::getDOMChildren($attribute,
'AttributeValue',
'@saml2')
1139 if (
$name ===
'tags') {
1140 foreach ($values as $tagname) {
1141 if (!empty($tagname)) {
1142 $ret[
'tags'][] = $tagname;
1159 $this->organizationName = $element->OrganizationName;
1160 $this->organizationDisplayName = $element->OrganizationDisplayName;
1161 $this->organizationURL = $element->OrganizationURL;
1173 $contactPerson =
array();
1174 if (!empty($element->contactType)) {
1175 $contactPerson[
'contactType'] = $element->contactType;
1177 if (!empty($element->Company)) {
1178 $contactPerson[
'company'] = $element->Company;
1180 if (!empty($element->GivenName)) {
1181 $contactPerson[
'givenName'] = $element->GivenName;
1183 if (!empty($element->SurName)) {
1184 $contactPerson[
'surName'] = $element->SurName;
1186 if (!empty($element->EmailAddress)) {
1187 $contactPerson[
'emailAddress'] = $element->EmailAddress;
1189 if (!empty($element->TelephoneNumber)) {
1190 $contactPerson[
'telephoneNumber'] = $element->TelephoneNumber;
1192 if (!empty($contactPerson)) {
1193 $this->contacts[] = $contactPerson;
1206 assert(
'is_array($sp)');
1208 $sp[
'name'] = $element->ServiceName;
1209 $sp[
'description'] = $element->ServiceDescription;
1212 $sp[
'attributes'] =
array();
1213 $sp[
'attributes.required'] =
array();
1214 foreach ($element->RequestedAttribute as $child) {
1215 $attrname = $child->Name;
1216 $sp[
'attributes'][] = $attrname;
1218 if ($child->isRequired !== null && $child->isRequired ===
true) {
1219 $sp[
'attributes.required'][] = $attrname;
1222 if ($child->NameFormat !== null) {
1223 $attrformat = $child->NameFormat;
1230 } elseif (
$format !== $attrformat) {
1235 if (empty($sp[
'attributes'])) {
1237 unset($sp[
'attributes']);
1239 if (empty($sp[
'attributes.required'])) {
1240 unset($sp[
'attributes.required']);
1244 $sp[
'attributes.NameFormat'] =
$format;
1267 $ep[
'Binding'] = $element->Binding;
1268 $ep[
'Location'] = $element->Location;
1270 if ($element->ResponseLocation !== null) {
1271 $ep[
'ResponseLocation'] = $element->ResponseLocation;
1274 if ($element instanceof \
SAML2\XML\md\IndexedEndpointType) {
1275 $ep[
'index'] = $element->index;
1277 if ($element->isDefault !== null) {
1278 $ep[
'isDefault'] = $element->isDefault;
1296 foreach ($endpoints as $ep) {
1297 $ret[] = self::parseGenericEndpoint($ep);
1322 if ($kd->use ===
'encryption') {
1323 $r[
'encryption'] =
true;
1324 $r[
'signing'] =
false;
1325 } elseif ($kd->use ===
'signing') {
1326 $r[
'encryption'] =
false;
1327 $r[
'signing'] =
true;
1329 $r[
'encryption'] =
true;
1330 $r[
'signing'] =
true;
1333 $keyInfo = $kd->KeyInfo;
1335 foreach ($keyInfo->info as
$i) {
1336 if ($i instanceof \
SAML2\XML\ds\X509Data) {
1337 foreach ($i->data as
$d) {
1338 if ($d instanceof \
SAML2\XML\ds\X509Certificate) {
1339 $r[
'type'] =
'X509Certificate';
1340 $r[
'X509Certificate'] = $d->certificate;
1360 assert(
'is_array($protocols)');
1364 foreach ($this->spDescriptors as $spd) {
1365 $sharedProtocols = array_intersect($protocols, $spd[
'protocols']);
1366 if (count($sharedProtocols) > 0) {
1384 assert(
'is_array($protocols)');
1388 foreach ($this->idpDescriptors as $idpd) {
1389 $sharedProtocols = array_intersect($protocols, $idpd[
'protocols']);
1390 if (count($sharedProtocols) > 0) {
1412 assert(
'$doc instanceof DOMDocument');
1415 $ed = $doc->documentElement;
1418 throw new Exception(
'Failed to load SAML metadata from empty XML document.');
1421 if (
SimpleSAML\Utils\XML::isDOMNodeOfType($ed,
'EntityDescriptor',
'@md') ===
false) {
1422 throw new Exception(
'Expected first element in the metadata document to be an EntityDescriptor element.');
1425 return new \SAML2\XML\md\EntityDescriptor($ed);
1441 assert(
'is_string($cert)');
1443 if (!file_exists($certFile)) {
1445 'Could not find certificate file ['.$certFile.
'], which is needed to validate signature' 1448 $certData = file_get_contents($certFile);
1450 foreach ($this->validators as $validator) {
1451 $key =
new XMLSecurityKey(XMLSecurityKey::RSA_SHA1,
array(
'type' =>
'public'));
1452 $key->loadKey($certData);
1454 if ($validator->validate(
$key)) {
1478 assert(
'is_string($fingerprint)');
1480 $fingerprint = strtolower(str_replace(
":",
"", $fingerprint));
1482 $candidates =
array();
1483 foreach ($this->validators as $validator) {
1484 foreach ($validator->getValidatingCertificates() as $cert) {
1486 $fp = strtolower(sha1(base64_decode($cert)));
1487 $candidates[] = $fp;
1488 if ($fp === $fingerprint) {
$metadata['__DYNAMIC:1__']
Attribute-related utility methods.
static fetch($url, $context=array(), $getHeaders=false)
Helper function to retrieve a file or URL with proxy support, also supporting proxy basic authorizati...
const NAMEFORMAT_UNSPECIFIED
The interpretation of the attribute name is left to individual implementations.
Create styles array
The data for the language used.
static getCertPath($path)
Resolves a path that may be relative to the cert-directory.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d