PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tenants/apps/samlservice/deploy-samlservice/samlservice/modules/aggregator2/lib/EntitySource.php

https://github.com/hpgihan/cronus
PHP | 273 lines | 139 code | 59 blank | 75 comment | 23 complexity | dda52502db39a16a9c80d68832741d19 MD5 | raw file
  1. <?php
  2. /**
  3. * Class for loading metadata from files and URLs.
  4. *
  5. * @package simpleSAMLphp
  6. * @version $Id$
  7. */
  8. class sspmod_aggregator2_EntitySource {
  9. /**
  10. * Our log "location".
  11. *
  12. * @var string
  13. */
  14. protected $logLoc;
  15. /**
  16. * The aggregator we belong to.
  17. *
  18. * @var sspmod_aggregator2_Aggregator
  19. */
  20. protected $aggregator;
  21. /**
  22. * The URL we should fetch it from.
  23. *
  24. * @var string
  25. */
  26. protected $url;
  27. /**
  28. * The SSL CA file that should be used to validate the connection.
  29. *
  30. * @var string|NULL
  31. */
  32. protected $sslCAFile;
  33. /**
  34. * The certificate we should use to validate downloaded metadata.
  35. *
  36. * @var string|NULL
  37. */
  38. protected $certificate;
  39. /**
  40. * The parsed metadata.
  41. *
  42. * @var SAML2_XML_md_EntitiesDescriptor|SAML2_XML_md_EntityDescriptor|NULL
  43. */
  44. protected $metadata;
  45. /**
  46. * The cache ID.
  47. *
  48. * @var string
  49. */
  50. protected $cacheId;
  51. /**
  52. * The cache tag.
  53. *
  54. * @var string
  55. */
  56. protected $cacheTag;
  57. /**
  58. * Whether we have attempted to update the cache already.
  59. *
  60. * @var bool
  61. */
  62. protected $updateAttempted;
  63. /**
  64. * Initialize this EntitySource.
  65. *
  66. * @param SimpleSAML_Configuration $config The configuration.
  67. */
  68. public function __construct(sspmod_aggregator2_Aggregator $aggregator, SimpleSAML_Configuration $config) {
  69. $this->logLoc = 'aggregator2:' . $aggregator->getId() . ': ';
  70. $this->aggregator = $aggregator;
  71. $this->url = $config->getString('url');
  72. $this->sslCAFile = $config->getString('ssl.cafile', NULL);
  73. if ($this->sslCAFile === NULL) {
  74. $this->sslCAFile = $aggregator->getCAFile();
  75. }
  76. $this->certificate = $config->getString('cert', NULL);
  77. $this->cacheId = sha1($this->url);
  78. $this->cacheTag = sha1(serialize($config));
  79. }
  80. /**
  81. * Retrieve and parse the metadata.
  82. *
  83. * @return SAML2_XML_md_EntitiesDescriptor|SAML2_XML_md_EntityDescriptor|NULL
  84. * The downloaded metadata or NULL if we were unable to download or parse it.
  85. */
  86. private function downloadMetadata() {
  87. SimpleSAML_Logger::debug($this->logLoc . 'Downloading metadata from ' .
  88. var_export($this->url, TRUE));
  89. $context = array('ssl' => array());
  90. if ($this->sslCAFile !== NULL) {
  91. $context['ssl']['cafile'] = SimpleSAML_Utilities::resolveCert($this->sslCAFile);
  92. SimpleSAML_Logger::debug($this->logLoc . 'Validating https connection against CA certificate(s) found in ' .
  93. var_export($context['ssl']['cafile'], TRUE));
  94. $context['ssl']['verify_peer'] = TRUE;
  95. $context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST);
  96. }
  97. $data = SimpleSAML_Utilities::fetch($this->url, $context);
  98. if ($data === FALSE || $data === NULL) {
  99. SimpleSAML_Logger::error($this->logLoc . 'Unable to load metadata from ' .
  100. var_export($this->url, TRUE));
  101. return NULL;
  102. }
  103. $doc = new DOMDocument();
  104. $res = $doc->loadXML($data);
  105. if (!$res) {
  106. SimpleSAML_Logger::error($this->logLoc . 'Error parsing XML from ' .
  107. var_export($this->url, TRUE));
  108. return NULL;
  109. }
  110. $root = SAML2_Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor');
  111. if (count($root) === 0) {
  112. SimpleSAML_Logger::error($this->logLoc . 'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from ' .
  113. var_export($this->url, TRUE));
  114. return NULL;
  115. }
  116. if (count($root) > 1) {
  117. SimpleSAML_Logger::error($this->logLoc . 'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from ' .
  118. var_export($this->url, TRUE));
  119. return NULL;
  120. }
  121. $root = $root[0];
  122. try {
  123. if ($root->localName === 'EntityDescriptor') {
  124. $md = new SAML2_XML_md_EntityDescriptor($root);
  125. } else {
  126. $md = new SAML2_XML_md_EntitiesDescriptor($root);
  127. }
  128. } catch (Exception $e) {
  129. SimpleSAML_Logger::error($this->logLoc . 'Unable to parse metadata from ' .
  130. var_export($this->url, TRUE) . ': ' . $e->getMessage());
  131. return NULL;
  132. }
  133. if ($this->certificate !== NULL) {
  134. $file = SimpleSAML_Utilities::resolveCert($this->certificate);
  135. $certData = file_get_contents($file);
  136. if ($certData === FALSE) {
  137. throw new SimpleSAML_Error_Exception('Error loading certificate from ' . var_export($file, TRUE));
  138. }
  139. /* Extract the public key from the certificate for validation. */
  140. $key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
  141. $key->loadKey($file, TRUE);
  142. if (!$md->validate($key)) {
  143. SimpleSAML_Logger::error($this->logLoc . 'Error validating signature on metadata.');
  144. return NULL;
  145. }
  146. SimpleSAML_Logger::debug($this->logLoc . 'Validated signature on metadata from ' . var_export($this->url, TRUE));
  147. }
  148. return $md;
  149. }
  150. /**
  151. * Attempt to update our cache file.
  152. */
  153. public function updateCache() {
  154. if ($this->updateAttempted) {
  155. return;
  156. }
  157. $this->updateAttempted = TRUE;
  158. $this->metadata = $this->downloadMetadata();
  159. if ($this->metadata === NULL) {
  160. return;
  161. }
  162. $expires = time() + 24*60*60; /* Default expires in one day. */
  163. if ($this->metadata->validUntil !== NULL && $this->metadata->validUntil < $expires) {
  164. $expires = $this->metadata->validUntil;
  165. }
  166. if ($this->metadata->cacheDuration !== NULL) {
  167. try {
  168. $durationTo = SimpleSAML_Utilities::parseDuration($this->metadata->cacheDuration);
  169. } catch (Exception $e) {
  170. SimpleSAML_Logger::warning($this->logLoc . 'Invalid cacheDuration in metadata from ' .
  171. var_export($this->url, TRUE) . ': ' . var_export($this->metadata->cacheDuration, TRUE));
  172. return;
  173. }
  174. if ($durationTo < $expires) {
  175. $expires = $durationTo;
  176. }
  177. }
  178. $metadataSerialized = serialize($this->metadata);
  179. $this->aggregator->addCacheItem($this->cacheId, $metadataSerialized, $expires, $this->cacheTag);
  180. }
  181. /**
  182. * Retrieve the metadata file.
  183. *
  184. * This function will check its cached copy, to see whether it can be used.
  185. *
  186. * @return SAML2_XML_md_EntityDescriptor|SAML2_XML_md_EntitiesDescriptor|NULL The downloaded metadata.
  187. */
  188. public function getMetadata() {
  189. if ($this->metadata !== NULL) {
  190. /* We have already downloaded the metdata. */
  191. return $this->metadata;
  192. }
  193. if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) {
  194. $this->updateCache();
  195. if ($this->metadata !== NULL) {
  196. return $this->metadata;
  197. }
  198. /* We were unable to update the cache - use cached metadata. */
  199. }
  200. $cacheFile = $this->aggregator->getCacheFile($this->cacheId);
  201. if (!file_exists($cacheFile)) {
  202. SimpleSAML_Logger::error($this->logLoc . 'No cached metadata available.');
  203. return NULL;
  204. }
  205. SimpleSAML_Logger::debug($this->logLoc . 'Using cached metadata from ' .
  206. var_export($cacheFile, TRUE));
  207. $metadata = file_get_contents($cacheFile);
  208. if ($metadata !== NULL) {
  209. $this->metadata = unserialize($metadata);
  210. return $this->metadata;
  211. }
  212. return NULL;
  213. }
  214. }