ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SAMLParser.php
Go to the documentation of this file.
1 <?php
2 
16 {
22  private static $SAML1xProtocols = array(
23  'urn:oasis:names:tc:SAML:1.0:protocol',
24  'urn:oasis:names:tc:SAML:1.1:protocol',
25  );
26 
32  private static $SAML20Protocols = array(
33  'urn:oasis:names:tc:SAML:2.0:protocol',
34  );
35 
41  private $entityId;
42 
53  private $spDescriptors;
54 
63  private $idpDescriptors;
64 
70  private $attributeAuthorityDescriptors = array();
71 
79  private $organizationName = array();
80 
88  private $organizationDisplayName = array();
89 
96  private $organizationURL = array();
97 
103  private $contacts = array();
104 
108  private $scopes;
109 
114 
120 
124  private $tags;
125 
131  private $validators = array();
132 
139 
149  private function __construct(
150  \SAML2\XML\md\EntityDescriptor $entityElement,
151  $maxExpireTime,
152  array $validators = array(),
153  array $parentExtensions = array()
154  ) {
155  assert($maxExpireTime === null || is_int($maxExpireTime));
156 
157  $this->spDescriptors = array();
158  $this->idpDescriptors = array();
159 
160  $e = $entityElement->toXML();
161  $e = $e->ownerDocument->saveXML($e);
162  $this->entityDescriptor = base64_encode($e);
163  $this->entityId = $entityElement->entityID;
164 
165  $expireTime = self::getExpireTime($entityElement, $maxExpireTime);
166 
167  $this->validators = $validators;
168  $this->validators[] = $entityElement;
169 
170  // process Extensions element, if it exists
171  $ext = self::processExtensions($entityElement, $parentExtensions);
172  $this->scopes = $ext['scope'];
173  $this->tags = $ext['tags'];
174  $this->entityAttributes = $ext['EntityAttributes'];
175  $this->registrationInfo = $ext['RegistrationInfo'];
176 
177  // look over the RoleDescriptors
178  foreach ($entityElement->RoleDescriptor as $child) {
179  if ($child instanceof \SAML2\XML\md\SPSSODescriptor) {
180  $this->processSPSSODescriptor($child, $expireTime);
181  } elseif ($child instanceof \SAML2\XML\md\IDPSSODescriptor) {
182  $this->processIDPSSODescriptor($child, $expireTime);
183  } elseif ($child instanceof \SAML2\XML\md\AttributeAuthorityDescriptor) {
184  $this->processAttributeAuthorityDescriptor($child, $expireTime);
185  }
186  }
187 
188  if ($entityElement->Organization) {
189  $this->processOrganization($entityElement->Organization);
190  }
191 
192  if (!empty($entityElement->ContactPerson)) {
193  foreach ($entityElement->ContactPerson as $contact) {
194  $this->processContactPerson($contact);
195  }
196  }
197  }
198 
199 
208  public static function parseFile($file)
209  {
211 
212  try {
214  } catch (\Exception $e) {
215  throw new Exception('Failed to read XML from file: '.$file);
216  }
217 
218  return self::parseDocument($doc);
219  }
220 
221 
230  public static function parseString($metadata)
231  {
232  try {
234  } catch (\Exception $e) {
235  throw new Exception('Failed to parse XML string.');
236  }
237 
238  return self::parseDocument($doc);
239  }
240 
241 
249  public static function parseDocument($document)
250  {
251  assert($document instanceof DOMDocument);
252 
253  $entityElement = self::findEntityDescriptor($document);
254 
255  return self::parseElement($entityElement);
256  }
257 
258 
267  public static function parseElement($entityElement)
268  {
269  assert($entityElement instanceof \SAML2\XML\md\EntityDescriptor);
270  return new SimpleSAML_Metadata_SAMLParser($entityElement, null, array());
271  }
272 
273 
285  public static function parseDescriptorsFile($file)
286  {
287  if ($file === null) {
288  throw new Exception('Cannot open file NULL. File name not specified.');
289  }
290 
292 
293  try {
295  } catch (\Exception $e) {
296  throw new Exception('Failed to read XML from file: '.$file);
297  }
298 
299  if ($doc->documentElement === null) {
300  throw new Exception('Opened file is not an XML document: '.$file);
301  }
302 
303  return self::parseDescriptorsElement($doc->documentElement);
304  }
305 
306 
318  public static function parseDescriptorsString($string)
319  {
320  try {
322  } catch (\Exception $e) {
323  throw new Exception('Failed to parse XML string.');
324  }
325 
326  return self::parseDescriptorsElement($doc->documentElement);
327  }
328 
329 
341  public static function parseDescriptorsElement(DOMElement $element = null)
342  {
343  if ($element === null) {
344  throw new Exception('Document was empty.');
345  }
346 
347  if (SimpleSAML\Utils\XML::isDOMNodeOfType($element, 'EntityDescriptor', '@md') === true) {
348  return self::processDescriptorsElement(new \SAML2\XML\md\EntityDescriptor($element));
349  } elseif (SimpleSAML\Utils\XML::isDOMNodeOfType($element, 'EntitiesDescriptor', '@md') === true) {
350  return self::processDescriptorsElement(new \SAML2\XML\md\EntitiesDescriptor($element));
351  } else {
352  throw new Exception('Unexpected root node: ['.$element->namespaceURI.']:'.$element->localName);
353  }
354  }
355 
356 
369  private static function processDescriptorsElement(
370  $element,
371  $maxExpireTime = null,
372  array $validators = array(),
373  array $parentExtensions = array()
374  ) {
375  assert($maxExpireTime === null || is_int($maxExpireTime));
376 
377  if ($element instanceof \SAML2\XML\md\EntityDescriptor) {
378  $ret = new SimpleSAML_Metadata_SAMLParser($element, $maxExpireTime, $validators, $parentExtensions);
379  $ret = array($ret->getEntityId() => $ret);
381  return $ret;
382  }
383 
384  assert($element instanceof \SAML2\XML\md\EntitiesDescriptor);
385 
386  $extensions = self::processExtensions($element, $parentExtensions);
387  $expTime = self::getExpireTime($element, $maxExpireTime);
388 
389  $validators[] = $element;
390 
391  $ret = array();
392  foreach ($element->children as $child) {
393  $ret += self::processDescriptorsElement($child, $expTime, $validators, $extensions);
394  }
395 
396  return $ret;
397  }
398 
399 
412  private static function getExpireTime($element, $maxExpireTime)
413  {
414  // validUntil may be null
415  $expire = $element->validUntil;
416 
417  if ($maxExpireTime !== null && ($expire === null || $maxExpireTime < $expire)) {
418  $expire = $maxExpireTime;
419  }
420 
421  return $expire;
422  }
423 
424 
430  public function getEntityId()
431  {
432  return $this->entityId;
433  }
434 
435 
436  private function getMetadataCommon()
437  {
438  $ret = array();
439  $ret['entityid'] = $this->entityId;
440  $ret['entityDescriptor'] = $this->entityDescriptor;
441 
442  // add organizational metadata
443  if (!empty($this->organizationName)) {
444  $ret['description'] = $this->organizationName;
445  $ret['OrganizationName'] = $this->organizationName;
446  }
447  if (!empty($this->organizationDisplayName)) {
449  $ret['OrganizationDisplayName'] = $this->organizationDisplayName;
450  }
451  if (!empty($this->organizationURL)) {
452  $ret['url'] = $this->organizationURL;
453  $ret['OrganizationURL'] = $this->organizationURL;
454  }
455 
456  //add contact metadata
457  $ret['contacts'] = $this->contacts;
458 
459  return $ret;
460  }
461 
462 
469  private function addExtensions(array &$metadata, array $roleDescriptor)
470  {
471  assert(array_key_exists('scope', $roleDescriptor));
472  assert(array_key_exists('tags', $roleDescriptor));
473 
474  $scopes = array_merge($this->scopes, array_diff($roleDescriptor['scope'], $this->scopes));
475  if (!empty($scopes)) {
476  $metadata['scope'] = $scopes;
477  }
478 
479  $tags = array_merge($this->tags, array_diff($roleDescriptor['tags'], $this->tags));
480  if (!empty($tags)) {
481  $metadata['tags'] = $tags;
482  }
483 
484 
485  if (!empty($this->registrationInfo)) {
486  $metadata['RegistrationInfo'] = $this->registrationInfo;
487  }
488 
489  if (!empty($this->entityAttributes)) {
490  $metadata['EntityAttributes'] = $this->entityAttributes;
491 
492  // check for entity categories
493  if (SimpleSAML\Utils\Config\Metadata::isHiddenFromDiscovery($metadata)) {
494  $metadata['hide.from.discovery'] = true;
495  }
496  }
497 
498  if (!empty($roleDescriptor['UIInfo'])) {
499  $metadata['UIInfo'] = $roleDescriptor['UIInfo'];
500  }
501 
502  if (!empty($roleDescriptor['DiscoHints'])) {
503  $metadata['DiscoHints'] = $roleDescriptor['DiscoHints'];
504  }
505  }
506 
507 
520  public function getMetadata1xSP()
521  {
522  $ret = $this->getMetadataCommon();
523  $ret['metadata-set'] = 'shib13-sp-remote';
524 
525 
526  // find SP information which supports one of the SAML 1.x protocols
527  $spd = $this->getSPDescriptors(self::$SAML1xProtocols);
528  if (count($spd) === 0) {
529  return null;
530  }
531 
532  // we currently only look at the first SPDescriptor which supports SAML 1.x
533  $spd = $spd[0];
534 
535  // add expire time to metadata
536  if (array_key_exists('expire', $spd)) {
537  $ret['expire'] = $spd['expire'];
538  }
539 
540  // find the assertion consumer service endpoints
541  $ret['AssertionConsumerService'] = $spd['AssertionConsumerService'];
542 
543  // add the list of attributes the SP should receive
544  if (array_key_exists('attributes', $spd)) {
545  $ret['attributes'] = $spd['attributes'];
546  }
547  if (array_key_exists('attributes.required', $spd)) {
548  $ret['attributes.required'] = $spd['attributes.required'];
549  }
550  if (array_key_exists('attributes.NameFormat', $spd)) {
551  $ret['attributes.NameFormat'] = $spd['attributes.NameFormat'];
552  }
553 
554  // add name & description
555  if (array_key_exists('name', $spd)) {
556  $ret['name'] = $spd['name'];
557  }
558  if (array_key_exists('description', $spd)) {
559  $ret['description'] = $spd['description'];
560  }
561 
562  // add public keys
563  if (!empty($spd['keys'])) {
564  $ret['keys'] = $spd['keys'];
565  }
566 
567  // add extensions
568  $this->addExtensions($ret, $spd);
569 
570  // prioritize mdui:DisplayName as the name if available
571  if (!empty($ret['UIInfo']['DisplayName'])) {
572  $ret['name'] = $ret['UIInfo']['DisplayName'];
573  }
574 
575  return $ret;
576  }
577 
578 
594  public function getMetadata1xIdP()
595  {
596  $ret = $this->getMetadataCommon();
597  $ret['metadata-set'] = 'shib13-idp-remote';
598 
599  // find IdP information which supports the SAML 1.x protocol
600  $idp = $this->getIdPDescriptors(self::$SAML1xProtocols);
601  if (count($idp) === 0) {
602  return null;
603  }
604 
605  // we currently only look at the first IDP descriptor which supports SAML 1.x
606  $idp = $idp[0];
607 
608  // fdd expire time to metadata
609  if (array_key_exists('expire', $idp)) {
610  $ret['expire'] = $idp['expire'];
611  }
612 
613  // find the SSO service endpoints
614  $ret['SingleSignOnService'] = $idp['SingleSignOnService'];
615 
616  // find the ArtifactResolutionService endpoint
617  $ret['ArtifactResolutionService'] = $idp['ArtifactResolutionService'];
618 
619  // add public keys
620  if (!empty($idp['keys'])) {
621  $ret['keys'] = $idp['keys'];
622  }
623 
624  // add extensions
625  $this->addExtensions($ret, $idp);
626 
627  // prioritize mdui:DisplayName as the name if available
628  if (!empty($ret['UIInfo']['DisplayName'])) {
629  $ret['name'] = $ret['UIInfo']['DisplayName'];
630  }
631 
632  return $ret;
633  }
634 
635 
650  public function getMetadata20SP()
651  {
652  $ret = $this->getMetadataCommon();
653  $ret['metadata-set'] = 'saml20-sp-remote';
654 
655  // find SP information which supports the SAML 2.0 protocol
656  $spd = $this->getSPDescriptors(self::$SAML20Protocols);
657  if (count($spd) === 0) {
658  return null;
659  }
660 
661  // we currently only look at the first SPDescriptor which supports SAML 2.0
662  $spd = $spd[0];
663 
664  // add expire time to metadata
665  if (array_key_exists('expire', $spd)) {
666  $ret['expire'] = $spd['expire'];
667  }
668 
669  // find the assertion consumer service endpoints
670  $ret['AssertionConsumerService'] = $spd['AssertionConsumerService'];
671 
672 
673  // find the single logout service endpoint
674  $ret['SingleLogoutService'] = $spd['SingleLogoutService'];
675 
676 
677  // find the NameIDFormat. This may not exist
678  if (count($spd['nameIDFormats']) > 0) {
679  // SimpleSAMLphp currently only supports a single NameIDFormat pr. SP. We use the first one
680  $ret['NameIDFormat'] = $spd['nameIDFormats'][0];
681  }
682 
683  // add the list of attributes the SP should receive
684  if (array_key_exists('attributes', $spd)) {
685  $ret['attributes'] = $spd['attributes'];
686  }
687  if (array_key_exists('attributes.required', $spd)) {
688  $ret['attributes.required'] = $spd['attributes.required'];
689  }
690  if (array_key_exists('attributes.NameFormat', $spd)) {
691  $ret['attributes.NameFormat'] = $spd['attributes.NameFormat'];
692  }
693  if (array_key_exists('attributes.index', $spd)) {
694  $ret['attributes.index'] = $spd['attributes.index'];
695  }
696  if (array_key_exists('attributes.isDefault', $spd)) {
697  $ret['attributes.isDefault'] = $spd['attributes.isDefault'];
698  }
699 
700  // add name & description
701  if (array_key_exists('name', $spd)) {
702  $ret['name'] = $spd['name'];
703  }
704  if (array_key_exists('description', $spd)) {
705  $ret['description'] = $spd['description'];
706  }
707 
708  // add public keys
709  if (!empty($spd['keys'])) {
710  $ret['keys'] = $spd['keys'];
711  }
712 
713  // add validate.authnrequest
714  if (array_key_exists('AuthnRequestsSigned', $spd)) {
715  $ret['validate.authnrequest'] = $spd['AuthnRequestsSigned'];
716  }
717 
718  // add saml20.sign.assertion
719  if (array_key_exists('WantAssertionsSigned', $spd)) {
720  $ret['saml20.sign.assertion'] = $spd['WantAssertionsSigned'];
721  }
722 
723  // add extensions
724  $this->addExtensions($ret, $spd);
725 
726  // prioritize mdui:DisplayName as the name if available
727  if (!empty($ret['UIInfo']['DisplayName'])) {
728  $ret['name'] = $ret['UIInfo']['DisplayName'];
729  }
730 
731  return $ret;
732  }
733 
734 
753  public function getMetadata20IdP()
754  {
755  $ret = $this->getMetadataCommon();
756  $ret['metadata-set'] = 'saml20-idp-remote';
757 
758  // find IdP information which supports the SAML 2.0 protocol
759  $idp = $this->getIdPDescriptors(self::$SAML20Protocols);
760  if (count($idp) === 0) {
761  return null;
762  }
763 
764  // we currently only look at the first IDP descriptor which supports SAML 2.0
765  $idp = $idp[0];
766 
767  // add expire time to metadata
768  if (array_key_exists('expire', $idp)) {
769  $ret['expire'] = $idp['expire'];
770  }
771 
772  // enable redirect.sign if WantAuthnRequestsSigned is enabled
773  if ($idp['WantAuthnRequestsSigned']) {
774  $ret['sign.authnrequest'] = true;
775  }
776 
777  // find the SSO service endpoint
778  $ret['SingleSignOnService'] = $idp['SingleSignOnService'];
779 
780  // find the single logout service endpoint
781  $ret['SingleLogoutService'] = $idp['SingleLogoutService'];
782 
783  // find the ArtifactResolutionService endpoint
784  $ret['ArtifactResolutionService'] = $idp['ArtifactResolutionService'];
785 
786  // add supported nameIDFormats
787  $ret['NameIDFormats'] = $idp['nameIDFormats'];
788 
789  // add public keys
790  if (!empty($idp['keys'])) {
791  $ret['keys'] = $idp['keys'];
792  }
793 
794  // add extensions
795  $this->addExtensions($ret, $idp);
796 
797  // prioritize mdui:DisplayName as the name if available
798  if (!empty($ret['UIInfo']['DisplayName'])) {
799  $ret['name'] = $ret['UIInfo']['DisplayName'];
800  }
801 
802  return $ret;
803  }
804 
805 
811  public function getAttributeAuthorities()
812  {
814  }
815 
816 
831  private static function parseRoleDescriptorType(\SAML2\XML\md\RoleDescriptor $element, $expireTime)
832  {
833  assert($expireTime === null || is_int($expireTime));
834 
835  $ret = array();
836 
837  $expireTime = self::getExpireTime($element, $expireTime);
838 
839  if ($expireTime !== null) {
840  // we got an expired timestamp, either from this element or one of the parent elements
841  $ret['expire'] = $expireTime;
842  }
843 
844  $ret['protocols'] = $element->protocolSupportEnumeration;
845 
846  // process KeyDescriptor elements
847  $ret['keys'] = array();
848  foreach ($element->KeyDescriptor as $kd) {
849  $key = self::parseKeyDescriptor($kd);
850  if ($key !== null) {
851  $ret['keys'][] = $key;
852  }
853  }
854 
855  $ext = self::processExtensions($element);
856  $ret['scope'] = $ext['scope'];
857  $ret['tags'] = $ext['tags'];
858  $ret['EntityAttributes'] = $ext['EntityAttributes'];
859  $ret['UIInfo'] = $ext['UIInfo'];
860  $ret['DiscoHints'] = $ext['DiscoHints'];
861 
862  return $ret;
863  }
864 
865 
882  private static function parseSSODescriptor(\SAML2\XML\md\SSODescriptorType $element, $expireTime)
883  {
884  assert($expireTime === null || is_int($expireTime));
885 
886  $sd = self::parseRoleDescriptorType($element, $expireTime);
887 
888  // find all SingleLogoutService elements
889  $sd['SingleLogoutService'] = self::extractEndpoints($element->SingleLogoutService);
890 
891  // find all ArtifactResolutionService elements
892  $sd['ArtifactResolutionService'] = self::extractEndpoints($element->ArtifactResolutionService);
893 
894 
895  // process NameIDFormat elements
896  $sd['nameIDFormats'] = $element->NameIDFormat;
897 
898  return $sd;
899  }
900 
901 
909  private function processSPSSODescriptor(\SAML2\XML\md\SPSSODescriptor $element, $expireTime)
910  {
911  assert($expireTime === null || is_int($expireTime));
912 
913  $sp = self::parseSSODescriptor($element, $expireTime);
914 
915  // find all AssertionConsumerService elements
916  $sp['AssertionConsumerService'] = self::extractEndpoints($element->AssertionConsumerService);
917 
918  // find all the attributes and SP name...
919  $attcs = $element->AttributeConsumingService;
920  if (count($attcs) > 0) {
921  self::parseAttributeConsumerService($attcs[0], $sp);
922  }
923 
924  // check AuthnRequestsSigned
925  if ($element->AuthnRequestsSigned !== null) {
926  $sp['AuthnRequestsSigned'] = $element->AuthnRequestsSigned;
927  }
928 
929  // check WantAssertionsSigned
930  if ($element->WantAssertionsSigned !== null) {
931  $sp['WantAssertionsSigned'] = $element->WantAssertionsSigned;
932  }
933 
934  $this->spDescriptors[] = $sp;
935  }
936 
937 
945  private function processIDPSSODescriptor(\SAML2\XML\md\IDPSSODescriptor $element, $expireTime)
946  {
947  assert($expireTime === null || is_int($expireTime));
948 
949  $idp = self::parseSSODescriptor($element, $expireTime);
950 
951  // find all SingleSignOnService elements
952  $idp['SingleSignOnService'] = self::extractEndpoints($element->SingleSignOnService);
953 
954  if ($element->WantAuthnRequestsSigned) {
955  $idp['WantAuthnRequestsSigned'] = true;
956  } else {
957  $idp['WantAuthnRequestsSigned'] = false;
958  }
959 
960  $this->idpDescriptors[] = $idp;
961  }
962 
963 
972  \SAML2\XML\md\AttributeAuthorityDescriptor $element,
973  $expireTime
974  ) {
975  assert($expireTime === null || is_int($expireTime));
976 
977  $aad = self::parseRoleDescriptorType($element, $expireTime);
978  $aad['entityid'] = $this->entityId;
979  $aad['metadata-set'] = 'attributeauthority-remote';
980 
981  $aad['AttributeService'] = self::extractEndpoints($element->AttributeService);
982  $aad['AssertionIDRequestService'] = self::extractEndpoints($element->AssertionIDRequestService);
983  $aad['NameIDFormat'] = $element->NameIDFormat;
984 
985  $this->attributeAuthorityDescriptors[] = $aad;
986  }
987 
988 
998  private static function processExtensions($element, $parentExtensions = array())
999  {
1000  $ret = array(
1001  'scope' => array(),
1002  'tags' => array(),
1003  'EntityAttributes' => array(),
1004  'RegistrationInfo' => array(),
1005  'UIInfo' => array(),
1006  'DiscoHints' => array(),
1007  );
1008 
1009  // Some extensions may get inherited from a parent element
1010  if (($element instanceof \SAML2\XML\md\EntityDescriptor || $element instanceof \SAML2\XML\md\EntitiesDescriptor)
1011  && !empty($parentExtensions['RegistrationInfo'])) {
1012  $ret['RegistrationInfo'] = $parentExtensions['RegistrationInfo'];
1013  }
1014 
1015  foreach ($element->Extensions as $e) {
1016  if ($e instanceof \SAML2\XML\shibmd\Scope) {
1017  $ret['scope'][] = $e->scope;
1018  continue;
1019  }
1020 
1021  // Entity Attributes are only allowed at entity level extensions and not at RoleDescriptor level
1022  if ($element instanceof \SAML2\XML\md\EntityDescriptor ||
1023  $element instanceof \SAML2\XML\md\EntitiesDescriptor) {
1024  if ($e instanceof \SAML2\XML\mdrpi\RegistrationInfo) {
1025  // Registration Authority cannot be overridden (warn only if override attempts to change the value)
1026  if (isset($ret['RegistrationInfo']['registrationAuthority'])
1027  && $ret['RegistrationInfo']['registrationAuthority'] !== $e->registrationAuthority) {
1028  SimpleSAML\Logger::warning('Invalid attempt to override registrationAuthority \''
1029  . $ret['RegistrationInfo']['registrationAuthority'] . "' with '{$e->registrationAuthority}'");
1030  } else {
1031  $ret['RegistrationInfo']['registrationAuthority'] = $e->registrationAuthority;
1032  }
1033  }
1034  if ($e instanceof \SAML2\XML\mdattr\EntityAttributes && !empty($e->children)) {
1035  foreach ($e->children as $attr) {
1036  // only saml:Attribute are currently supported here. The specifications also allows
1037  // saml:Assertions, which more complex processing
1038  if ($attr instanceof \SAML2\XML\saml\Attribute) {
1039  if (empty($attr->Name) || empty($attr->AttributeValue)) {
1040  continue;
1041  }
1042 
1043  // attribute names that is not URI is prefixed as this: '{nameformat}name'
1044  $name = $attr->Name;
1045  if (empty($attr->NameFormat)) {
1046  $name = '{'.\SAML2\Constants::NAMEFORMAT_UNSPECIFIED.'}'.$attr->Name;
1047  } elseif ($attr->NameFormat !== 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri') {
1048  $name = '{'.$attr->NameFormat.'}'.$attr->Name;
1049  }
1050 
1051  $values = array();
1052  foreach ($attr->AttributeValue as $attrvalue) {
1053  $values[] = $attrvalue->getString();
1054  }
1055 
1056  $ret['EntityAttributes'][$name] = $values;
1057  }
1058  }
1059  }
1060  }
1061 
1062  // UIInfo elements are only allowed at RoleDescriptor level extensions
1063  if ($element instanceof \SAML2\XML\md\RoleDescriptor) {
1064  if ($e instanceof \SAML2\XML\mdui\UIInfo) {
1065  $ret['UIInfo']['DisplayName'] = $e->DisplayName;
1066  $ret['UIInfo']['Description'] = $e->Description;
1067  $ret['UIInfo']['InformationURL'] = $e->InformationURL;
1068  $ret['UIInfo']['PrivacyStatementURL'] = $e->PrivacyStatementURL;
1069 
1070  foreach ($e->Keywords as $uiItem) {
1071  if (!($uiItem instanceof \SAML2\XML\mdui\Keywords)
1072  || empty($uiItem->Keywords)
1073  || empty($uiItem->lang)
1074  ) {
1075  continue;
1076  }
1077  $ret['UIInfo']['Keywords'][$uiItem->lang] = $uiItem->Keywords;
1078  }
1079  foreach ($e->Logo as $uiItem) {
1080  if (!($uiItem instanceof \SAML2\XML\mdui\Logo)
1081  || empty($uiItem->url)
1082  || empty($uiItem->height)
1083  || empty($uiItem->width)
1084  ) {
1085  continue;
1086  }
1087  $logo = array(
1088  'url' => $uiItem->url,
1089  'height' => $uiItem->height,
1090  'width' => $uiItem->width,
1091  );
1092  if (!empty($uiItem->lang)) {
1093  $logo['lang'] = $uiItem->lang;
1094  }
1095  $ret['UIInfo']['Logo'][] = $logo;
1096  }
1097  }
1098  }
1099 
1100  // DiscoHints elements are only allowed at IDPSSODescriptor level extensions
1101  if ($element instanceof \SAML2\XML\md\IDPSSODescriptor) {
1102  if ($e instanceof \SAML2\XML\mdui\DiscoHints) {
1103  $ret['DiscoHints']['IPHint'] = $e->IPHint;
1104  $ret['DiscoHints']['DomainHint'] = $e->DomainHint;
1105  $ret['DiscoHints']['GeolocationHint'] = $e->GeolocationHint;
1106  }
1107  }
1108 
1109  if (!($e instanceof \SAML2\XML\Chunk)) {
1110  continue;
1111  }
1112 
1113  if ($e->localName === 'Attribute' && $e->namespaceURI === \SAML2\Constants::NS_SAML) {
1114  $attribute = $e->xml;
1115 
1116  $name = $attribute->getAttribute('Name');
1117  $values = array_map(
1118  array('SimpleSAML\Utils\XML', 'getDOMText'),
1119  SimpleSAML\Utils\XML::getDOMChildren($attribute, 'AttributeValue', '@saml2')
1120  );
1121 
1122  if ($name === 'tags') {
1123  foreach ($values as $tagname) {
1124  if (!empty($tagname)) {
1125  $ret['tags'][] = $tagname;
1126  }
1127  }
1128  }
1129  }
1130  }
1131  return $ret;
1132  }
1133 
1134 
1140  private function processOrganization(\SAML2\XML\md\Organization $element)
1141  {
1142  $this->organizationName = $element->OrganizationName;
1143  $this->organizationDisplayName = $element->OrganizationDisplayName;
1144  $this->organizationURL = $element->OrganizationURL;
1145  }
1146 
1147 
1154  private function processContactPerson(\SAML2\XML\md\ContactPerson $element)
1155  {
1156  $contactPerson = array();
1157  if (!empty($element->contactType)) {
1158  $contactPerson['contactType'] = $element->contactType;
1159  }
1160  if (!empty($element->Company)) {
1161  $contactPerson['company'] = $element->Company;
1162  }
1163  if (!empty($element->GivenName)) {
1164  $contactPerson['givenName'] = $element->GivenName;
1165  }
1166  if (!empty($element->SurName)) {
1167  $contactPerson['surName'] = $element->SurName;
1168  }
1169  if (!empty($element->EmailAddress)) {
1170  $contactPerson['emailAddress'] = $element->EmailAddress;
1171  }
1172  if (!empty($element->TelephoneNumber)) {
1173  $contactPerson['telephoneNumber'] = $element->TelephoneNumber;
1174  }
1175  if (!empty($contactPerson)) {
1176  $this->contacts[] = $contactPerson;
1177  }
1178  }
1179 
1180 
1187  private static function parseAttributeConsumerService(\SAML2\XML\md\AttributeConsumingService $element, &$sp)
1188  {
1189  assert(is_array($sp));
1190 
1191  $sp['name'] = $element->ServiceName;
1192  $sp['description'] = $element->ServiceDescription;
1193 
1194  $format = null;
1195  $sp['attributes'] = array();
1196  $sp['attributes.required'] = array();
1197  foreach ($element->RequestedAttribute as $child) {
1198  $attrname = $child->Name;
1199  $sp['attributes'][] = $attrname;
1200 
1201  if ($child->isRequired !== null && $child->isRequired === true) {
1202  $sp['attributes.required'][] = $attrname;
1203  }
1204 
1205  if ($child->NameFormat !== null) {
1206  $attrformat = $child->NameFormat;
1207  } else {
1209  }
1210 
1211  if ($format === null) {
1212  $format = $attrformat;
1213  } elseif ($format !== $attrformat) {
1215  }
1216  }
1217 
1218  if (empty($sp['attributes'])) {
1219  // a really invalid configuration: all AttributeConsumingServices should have one or more attributes
1220  unset($sp['attributes']);
1221  }
1222  if (empty($sp['attributes.required'])) {
1223  unset($sp['attributes.required']);
1224  }
1225 
1226  if ($format !== \SAML2\Constants::NAMEFORMAT_UNSPECIFIED && $format !== null) {
1227  $sp['attributes.NameFormat'] = $format;
1228  }
1229  }
1230 
1231 
1246  private static function parseGenericEndpoint(\SAML2\XML\md\EndpointType $element)
1247  {
1248  $ep = array();
1249 
1250  $ep['Binding'] = $element->Binding;
1251  $ep['Location'] = $element->Location;
1252 
1253  if ($element->ResponseLocation !== null) {
1254  $ep['ResponseLocation'] = $element->ResponseLocation;
1255  }
1256 
1257  if ($element instanceof \SAML2\XML\md\IndexedEndpointType) {
1258  $ep['index'] = $element->index;
1259 
1260  if ($element->isDefault !== null) {
1261  $ep['isDefault'] = $element->isDefault;
1262  }
1263  }
1264 
1265  return $ep;
1266  }
1267 
1268 
1276  private static function extractEndpoints(array $endpoints)
1277  {
1278  $ret = array();
1279  foreach ($endpoints as $ep) {
1280  $ret[] = self::parseGenericEndpoint($ep);
1281  }
1282 
1283  return $ret;
1284  }
1285 
1286 
1301  private static function parseKeyDescriptor(\SAML2\XML\md\KeyDescriptor $kd)
1302  {
1303  $r = array();
1304 
1305  if ($kd->use === 'encryption') {
1306  $r['encryption'] = true;
1307  $r['signing'] = false;
1308  } elseif ($kd->use === 'signing') {
1309  $r['encryption'] = false;
1310  $r['signing'] = true;
1311  } else {
1312  $r['encryption'] = true;
1313  $r['signing'] = true;
1314  }
1315 
1316  $keyInfo = $kd->KeyInfo;
1317 
1318  foreach ($keyInfo->info as $i) {
1319  if ($i instanceof \SAML2\XML\ds\X509Data) {
1320  foreach ($i->data as $d) {
1321  if ($d instanceof \SAML2\XML\ds\X509Certificate) {
1322  $r['type'] = 'X509Certificate';
1323  $r['X509Certificate'] = $d->certificate;
1324  return $r;
1325  }
1326  }
1327  }
1328  }
1329 
1330  return null;
1331  }
1332 
1333 
1341  private function getSPDescriptors($protocols)
1342  {
1343  assert(is_array($protocols));
1344 
1345  $ret = array();
1346 
1347  foreach ($this->spDescriptors as $spd) {
1348  $sharedProtocols = array_intersect($protocols, $spd['protocols']);
1349  if (count($sharedProtocols) > 0) {
1350  $ret[] = $spd;
1351  }
1352  }
1353 
1354  return $ret;
1355  }
1356 
1357 
1365  private function getIdPDescriptors($protocols)
1366  {
1367  assert(is_array($protocols));
1368 
1369  $ret = array();
1370 
1371  foreach ($this->idpDescriptors as $idpd) {
1372  $sharedProtocols = array_intersect($protocols, $idpd['protocols']);
1373  if (count($sharedProtocols) > 0) {
1374  $ret[] = $idpd;
1375  }
1376  }
1377 
1378  return $ret;
1379  }
1380 
1381 
1393  private static function findEntityDescriptor($doc)
1394  {
1395  assert($doc instanceof DOMDocument);
1396 
1397  // find the EntityDescriptor DOMElement. This should be the first (and only) child of the DOMDocument
1398  $ed = $doc->documentElement;
1399 
1400  if ($ed === null) {
1401  throw new Exception('Failed to load SAML metadata from empty XML document.');
1402  }
1403 
1404  if (SimpleSAML\Utils\XML::isDOMNodeOfType($ed, 'EntityDescriptor', '@md') === false) {
1405  throw new Exception('Expected first element in the metadata document to be an EntityDescriptor element.');
1406  }
1407 
1408  return new \SAML2\XML\md\EntityDescriptor($ed);
1409  }
1410 
1411 
1422  {
1423  foreach ($certificates as $cert) {
1424  assert(is_string($cert));
1425  $certFile = \SimpleSAML\Utils\Config::getCertPath($cert);
1426  if (!file_exists($certFile)) {
1427  throw new Exception(
1428  'Could not find certificate file ['.$certFile.'], which is needed to validate signature'
1429  );
1430  }
1431  $certData = file_get_contents($certFile);
1432 
1433  foreach ($this->validators as $validator) {
1434  $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'public'));
1435  $key->loadKey($certData);
1436  try {
1437  if ($validator->validate($key)) {
1438  return true;
1439  }
1440  } catch (Exception $e) {
1441  // this certificate did not sign this element, skip
1442  }
1443  }
1444  }
1445  SimpleSAML\Logger::debug('Could not validate signature');
1446  return false;
1447  }
1448 
1449 
1459  public function validateFingerprint($fingerprint)
1460  {
1461  assert(is_string($fingerprint));
1462 
1463  $fingerprint = strtolower(str_replace(":", "", $fingerprint));
1464 
1465  $candidates = array();
1466  foreach ($this->validators as $validator) {
1467  foreach ($validator->getValidatingCertificates() as $cert) {
1468  $fp = strtolower(sha1(base64_decode($cert)));
1469  $candidates[] = $fp;
1470  if ($fp === $fingerprint) {
1471  return true;
1472  }
1473  }
1474  }
1475  SimpleSAML\Logger::debug('Fingerprint was ['.$fingerprint.'] not one of ['.join(', ', $candidates).']');
1476  return false;
1477  }
1478 }
static parseAttributeConsumerService(\SAML2\XML\md\AttributeConsumingService $element, &$sp)
This function parses AttributeConsumerService elements.
$expire
Definition: saml2-acs.php:140
static parseString($metadata)
This function parses a string which contains XML encoded metadata.
Definition: SAMLParser.php:230
getMetadata1xSP()
This function returns the metadata for SAML 1.x SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:520
getIdPDescriptors($protocols)
This function finds IdP descriptors which supports one of the given protocols.
$format
Definition: metadata.php:141
static parseDocument($document)
This function parses a DOMDocument which is assumed to contain a single EntityDescriptor element...
Definition: SAMLParser.php:249
static parseKeyDescriptor(\SAML2\XML\md\KeyDescriptor $kd)
This function parses a KeyDescriptor element.
processAttributeAuthorityDescriptor(\SAML2\XML\md\AttributeAuthorityDescriptor $element, $expireTime)
This function extracts metadata from a AttributeAuthorityDescriptor element.
Definition: SAMLParser.php:971
static processExtensions($element, $parentExtensions=array())
Parse an Extensions element.
Definition: SAMLParser.php:998
static findEntityDescriptor($doc)
This function locates the EntityDescriptor node in a DOMDocument.
static debug($string)
Definition: Logger.php:211
static parseGenericEndpoint(\SAML2\XML\md\EndpointType $element)
This function is a generic endpoint element parser.
processContactPerson(\SAML2\XML\md\ContactPerson $element)
Parse and process a ContactPerson element.
$certificates
Definition: metarefresh.php:39
$metadata['__DYNAMIC:1__']
static parseRoleDescriptorType(\SAML2\XML\md\RoleDescriptor $element, $expireTime)
Parse a RoleDescriptorType element.
Definition: SAMLParser.php:831
processOrganization(\SAML2\XML\md\Organization $element)
Parse and process a Organization element.
getAttributeAuthorities()
Retrieve AttributeAuthorities from the metadata.
Definition: SAMLParser.php:811
static parseDescriptorsFile($file)
This function parses a file where the root node is either an EntityDescriptor element or an EntitiesD...
Definition: SAMLParser.php:285
static parseDescriptorsString($string)
This function parses a string with XML data.
Definition: SAMLParser.php:318
Attribute-related utility methods.
processIDPSSODescriptor(\SAML2\XML\md\IDPSSODescriptor $element, $expireTime)
This function extracts metadata from a IDPSSODescriptor element.
Definition: SAMLParser.php:945
getMetadata1xIdP()
This function returns the metadata for SAML 1.x IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:594
$r
Definition: example_031.php:79
getSPDescriptors($protocols)
This function finds SP descriptors which supports one of the given protocols.
static warning($string)
Definition: Logger.php:177
$values
static parseFile($file)
This function parses a file which contains XML encoded metadata.
Definition: SAMLParser.php:208
getMetadata20IdP()
This function returns the metadata for SAML 2.0 IdPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:753
validateFingerprint($fingerprint)
This function checks if this EntityDescriptor was signed with a certificate with the given fingerprin...
static fetch($url, $context=array(), $getHeaders=false)
Helper function to retrieve a file or URL with proxy support, also supporting proxy basic authorizati...
Definition: HTTP.php:408
const NAMEFORMAT_UNSPECIFIED
The interpretation of the attribute name is left to individual implementations.
Definition: Constants.php:146
__construct(\SAML2\XML\md\EntityDescriptor $entityElement, $maxExpireTime, array $validators=array(), array $parentExtensions=array())
This is the constructor for the SAMLParser class.
Definition: SAMLParser.php:149
addExtensions(array &$metadata, array $roleDescriptor)
Add data parsed from extensions to metadata.
Definition: SAMLParser.php:469
This is class for parsing of SAML 1.x and SAML 2.0 metadata.
Definition: SAMLParser.php:15
$idp
Definition: prp.php:13
static getCertPath($path)
Resolves a path that may be relative to the cert-directory.
Definition: Config.php:22
$ret
Definition: parser.php:6
$i
Definition: disco.tpl.php:19
static extractEndpoints(array $endpoints)
Extract generic endpoints.
static getExpireTime($element, $maxExpireTime)
Determine how long a given element can be cached.
Definition: SAMLParser.php:412
validateSignature($certificates)
If this EntityDescriptor was signed this function use the public key to check the signature...
getMetadata20SP()
This function returns the metadata for SAML 2.0 SPs in the format SimpleSAMLphp expects.
Definition: SAMLParser.php:650
static parseDescriptorsElement(DOMElement $element=null)
This function parses a DOMElement which represents either an EntityDescriptor element or an EntitiesD...
Definition: SAMLParser.php:341
$key
Definition: croninfo.php:18
processSPSSODescriptor(\SAML2\XML\md\SPSSODescriptor $element, $expireTime)
This function extracts metadata from a SPSSODescriptor element.
Definition: SAMLParser.php:909
static parseElement($entityElement)
This function parses a object which represents a EntityDescriptor element.
Definition: SAMLParser.php:267
getEntityId()
This function returns the entity id of this parsed entity.
Definition: SAMLParser.php:430
for($i=6; $i< 13; $i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
static parseSSODescriptor(\SAML2\XML\md\SSODescriptorType $element, $expireTime)
This function extracts metadata from a SSODescriptor element.
Definition: SAMLParser.php:882
$data
Definition: bench.php:6