Viewing file:      MetaLoader.php (13.34 KB)      -rwxrwxr-x Select action/file-type:    (+) |   (+) |   (+) | Code (+) | Session (+) |   (+) | SDB (+) |   (+) |   (+) |   (+) |   (+) |   (+) |
 
<?php /*  * @author Andreas Åkre Solberg <andreas.solberg@uninett.no>  * @package simpleSAMLphp  * @version $Id$  */ class sspmod_metarefresh_MetaLoader {
 
      private $expire;     private $metadata;     private $oldMetadataSrc;     private $stateFile;     private $changed;     private static $types = array('saml20-idp-remote', 'saml20-sp-remote',         'shib13-idp-remote', 'shib13-sp-remote', 'attributeauthority-remote');
 
      /**      * Constructor      *      * @param array $sources     Sources...      * @param       */     public function __construct($expire = NULL, $stateFile = NULL, $oldMetadataSrc = NULL) {         $this->expire = $expire;         $this->metadata = array();         $this->oldMetadataSrc = $oldMetadataSrc;         $this->stateFile = $stateFile;         $this->changed = FALSE;
          // Read file containing $state from disk         if(is_readable($stateFile)) {             require($stateFile);         }
          $this->state = (isset($state)) ? $state : array();
      }
      /**      * This function processes a SAML metadata file.      *      * @param $source      */     public function loadSource($source) {
          if (preg_match('@^https?://@i', $source['src'])) {             // Build new HTTP context             $context = $this->createContext($source);
              // GET!             try {                 list($data, $responseHeaders) = SimpleSAML_Utilities::fetch($source['src'], $context, TRUE);             } catch(Exception $e) {                 SimpleSAML_Logger::warning('metarefresh: ' . $e->getMessage());             }
              // We have response headers, so the request succeeded             if(!isset($responseHeaders)) {                 // No response headers, this means the request failed in some way, so re-use old data                 SimpleSAML_Logger::debug('No response from ' . $source['src'] . ' - attempting to re-use cached metadata');                 $this->addCachedMetadata($source);                 return;             } elseif(preg_match('@^HTTP/1\.[01]\s304\s@', $responseHeaders[0])) {                 // 304 response                 SimpleSAML_Logger::debug('Received HTTP 304 (Not Modified) - attempting to re-use cached metadata');                 $this->addCachedMetadata($source);                 return;             } elseif(!preg_match('@^HTTP/1\.[01]\s200\s@', $responseHeaders[0])) {                 // Other error.                 SimpleSAML_Logger::debug('Error from ' . $source['src'] . ' - attempting to re-use cached metadata');                 $this->addCachedMetadata($source);                 return;             }         } else {             /* Local file. */             $data = file_get_contents($source['src']);             $responseHeaders = NULL;         }
          /* Everything OK. Proceed. */         if (isset($source['conditionalGET']) && $source['conditionalGET']) {             // Stale or no metadata, so a fresh copy             SimpleSAML_Logger::debug('Downloaded fresh copy');         }
          $entities = $this->loadXML($data, $source);
          foreach($entities as $entity) {
              if(isset($source['blacklist'])) {                 if(!empty($source['blacklist']) && in_array($entity->getEntityID(), $source['blacklist'])) {                     SimpleSAML_Logger::info('Skipping "' .  $entity->getEntityID() . '" - blacklisted.' . "\n");                     continue;                 }             }
              if(isset($source['whitelist'])) {                 if(!empty($source['whitelist']) && !in_array($entity->getEntityID(), $source['whitelist'])) {                     SimpleSAML_Logger::info('Skipping "' .  $entity->getEntityID() . '" - not in the whitelist.' . "\n");                     continue;                 }             }
              if(array_key_exists('validateFingerprint', $source) && $source['validateFingerprint'] !== NULL) {                 if(!$entity->validateFingerprint($source['validateFingerprint'])) {                     SimpleSAML_Logger::info('Skipping "' . $entity->getEntityId() . '" - could not verify signature.' . "\n");                     continue;                 }             }
              $template = NULL;             if (array_key_exists('template', $source)) $template = $source['template'];
              $this->addMetadata($source['src'], $entity->getMetadata1xSP(), 'shib13-sp-remote', $template);             $this->addMetadata($source['src'], $entity->getMetadata1xIdP(), 'shib13-idp-remote', $template);             $this->addMetadata($source['src'], $entity->getMetadata20SP(), 'saml20-sp-remote', $template);             $this->addMetadata($source['src'], $entity->getMetadata20IdP(), 'saml20-idp-remote', $template);             $attributeAuthorities = $entity->getAttributeAuthorities();             if (!empty($attributeAuthorities)) {                 $this->addMetadata($source['src'], $attributeAuthorities[0], 'attributeauthority-remote', $template);             }         }
          $this->saveState($source, $responseHeaders);     }
      /**      * Create HTTP context, with any available caches taken into account      */     private function createContext($source) {
          $context = NULL;
          $config = SimpleSAML_Configuration::getInstance();         $name = $config->getString('technicalcontact_name', NULL);         $mail = $config->getString('technicalcontact_email', NULL);
          $rawheader = "User-Agent: SimpleSAMLphp metarefresh, run by $name <$mail>\r\n";
          if (isset($source['conditionalGET']) && $source['conditionalGET']) {             if(array_key_exists($source['src'], $this->state)) {
                  $sourceState = $this->state[$source['src']];
                  if(isset($sourceState['last-modified'])) {                     $rawheader .= 'If-Modified-Since: ' . $sourceState['last-modified'] . "\r\n";                 }
                  if(isset($sourceState['etag'])) {                     $rawheader .= 'If-None-Match: ' . $sourceState['etag'] . "\r\n";                 }             }         }
          return array('http' => array('header' => $rawheader));     }
 
      private function addCachedMetadata($source) {         if(isset($this->oldMetadataSrc)) {             foreach(self::$types as $type) {                 foreach($this->oldMetadataSrc->getMetadataSet($type) as $entity) {                     if(array_key_exists('metarefresh:src', $entity)) {                         if($entity['metarefresh:src'] == $source['src']) {                             //SimpleSAML_Logger::debug('Re-using cached metadata for ' . $entity['entityid']);                             $this->addMetadata($source['src'], $entity, $type);                         }                     }                 }             }         }     }
 
      /**      * Store caching state data for a source      */     private function saveState($source, $responseHeaders) {
          if (isset($source['conditionalGET']) && $source['conditionalGET']) {
              // Headers section             $candidates = array('last-modified', 'etag');
              foreach($candidates as $candidate) {                 if(array_key_exists($candidate, $responseHeaders)) {                     $this->state[$source['src']][$candidate] = $responseHeaders[$candidate];                 }             }
              if(!empty($this->state[$source['src']])) {                 // Timestamp when this src was requested.                 $this->state[$source['src']]['requested_at'] = $this->getTime();
                  $this->changed = TRUE;             }         }     }
 
      /**      * Parse XML metadata and return entities      */     private function loadXML($data, $source) {         $entities = array();         try {             $doc = new DOMDocument();             $res = $doc->loadXML($data);             if($res !== TRUE) {                 throw new Exception('Failed to read XML from ' . $source['src']);             }             if($doc->documentElement ===  NULL) throw new Exception('Opened file is not an XML document: ' . $source['src']);             $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsElement($doc->documentElement);         } catch(Exception $e) {             SimpleSAML_Logger::warning('metarefresh: Failed to retrieve metadata. ' . $e->getMessage());         }         return $entities;     }
 
      /**      * This function writes the state array back to disk      */     public function writeState() {         if($this->changed) {             SimpleSAML_Logger::debug('Writing: ' . $this->stateFile);             SimpleSAML_Utilities::writeFile(                 $this->stateFile,                 "<?php\n/* This file was generated by the metarefresh module at ".$this->getTime() . ".\n".                 " Do not update it manually as it will get overwritten. */\n".                 '$state = ' . var_export($this->state, TRUE) . ";\n?>\n"             );         }     }
 
      /**      * This function writes the metadata to stdout.      */     public function dumpMetadataStdOut() {              foreach($this->metadata as $category => $elements) {                  echo('/* The following data should be added to metadata/' . $category . '.php. */' . "\n");                       foreach($elements as $m) {                 $filename = $m['filename'];                 $entityID = $m['metadata']['entityid'];                      echo("\n");                 echo('/* The following metadata was generated from ' . $filename . ' on ' . $this->getTime() . '. */' . "\n");                 echo('$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], TRUE) . ';' . "\n");             }                       echo("\n");             echo('/* End of data which should be added to metadata/' . $category . '.php. */' . "\n");             echo("\n");         }     }
           /**      * This function adds metadata from the specified file to the list of metadata.      * This function will return without making any changes if $metadata is NULL.      *      * @param $filename The filename the metadata comes from.      * @param $metadata The metadata.      * @param $type The metadata type.      */     private function addMetadata($filename, $metadata, $type, $template = NULL) {              if($metadata === NULL) {             return;         }              if (isset($template)) { //             foreach($metadata AS $mkey => $mentry) { //                 echo '<pre>'; print_r($metadata); exit; //                 $metadata[$mkey] = array_merge($mentry, $template); //             }             $metadata = array_merge($metadata, $template);         }              $metadata['metarefresh:src'] = $filename;         if(!array_key_exists($type, $this->metadata)) {             $this->metadata[$type] = array();         }                  // If expire is defined in constructor...         if (!empty($this->expire)) {                          // If expire is already in metadata             if (array_key_exists('expire', $metadata)) {                              // Override metadata expire with more restrictive global config-                 if ($this->expire < $metadata['expire'])                     $metadata['expire'] = $this->expire;                                  // If expire is not already in metadata use global config             } else {                 $metadata['expire'] = $this->expire;             }         }         
               $this->metadata[$type][] = array('filename' => $filename, 'metadata' => $metadata);     }
 
      /**      * This function writes the metadata to an ARP file      */     function writeARPfile($config) {                  assert('is_a($config, \'SimpleSAML_Configuration\')');                  $arpfile = $config->getValue('arpfile');         $types = array('saml20-sp-remote');                  $md = array();         foreach($this->metadata as $category => $elements) {             if (!in_array($category, $types)) continue;             $md = array_merge($md, $elements);         }                  #$metadata, $attributemap, $prefix, $suffix         $arp = new sspmod_metarefresh_ARP($md,              $config->getValue('attributemap', ''),               $config->getValue('prefix', ''),               $config->getValue('suffix', '')         );                           $arpxml = $arp->getXML();
          SimpleSAML_Logger::info('Writing ARP file: ' . $arpfile . "\n");         file_put_contents($arpfile, $arpxml);
      }               /**      * This function writes the metadata to to separate files in the output directory.      */     function writeMetadataFiles($outputDir) {              while(strlen($outputDir) > 0 && $outputDir[strlen($outputDir) - 1] === '/') {             $outputDir = substr($outputDir, 0, strlen($outputDir) - 1);         }              if(!file_exists($outputDir)) {             SimpleSAML_Logger::info('Creating directory: ' . $outputDir . "\n");             $res = @mkdir($outputDir, 0777, TRUE);             if ($res === FALSE) {                 throw new Exception('Error creating directory: ' . $outputDir);             }         }              foreach(self::$types as $type) {
              $filename = $outputDir . '/' . $type . '.php';
              if(array_key_exists($type, $this->metadata)) {                 $elements = $this->metadata[$type];                 SimpleSAML_Logger::debug('Writing: ' . $filename);
                  $content  = '<?php' . "\n" . '/* This file was generated by the metarefresh module at '. $this->getTime() . "\n";                 $content .= ' Do not update it manually as it will get overwritten' . "\n" . '*/' . "\n";
                  foreach($elements as $m) {                     $entityID = $m['metadata']['entityid'];                     $content .= "\n";                     $content .= '$metadata[\'' . addslashes($entityID) . '\'] = ' . var_export($m['metadata'], TRUE) . ';' . "\n";                 }
                  $content .= "\n" . '?>';
                  SimpleSAML_Utilities::writeFile($filename, $content);             } elseif(is_file($filename)) {                 if(unlink($filename)) {                     SimpleSAML_Logger::debug('Deleting stale metadata file: ' . $filename);                 } else {                     SimpleSAML_Logger::warning('Could not delete stale metadata file: ' . $filename);                 }             }         }     }
 
      /**      * Save metadata for loading with the 'serialize' metadata loader.      *      * @param string $outputDir  The directory we should save the metadata to.      */     public function writeMetadataSerialize($outputDir) {         assert('is_string($outputDir)');
          $metaHandler = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize(array('directory' => $outputDir));
          /* First we add all the metadata entries to the metadata handler. */         foreach ($this->metadata as $set => $elements) {             foreach ($elements as $m) {                 $entityId = $m['metadata']['entityid'];
                  SimpleSAML_Logger::debug('metarefresh: Add metadata entry ' .                     var_export($entityId, TRUE) . ' in set ' . var_export($set, TRUE) . '.');                 $metaHandler->saveMetadata($entityId, $set, $m['metadata']);             }         }
          /* Then we delete old entries which should no longer exist. */         $ct = time();         foreach ($metaHandler->getMetadataSets() as $set) {             foreach ($metaHandler->getMetadataSet($set) as $entityId => $metadata) {                 if (!array_key_exists('expire', $metadata)) {                     SimpleSAML_Logger::warning('metarefresh: Metadata entry without expire timestamp: ' . var_export($entityId, TRUE) .                          ' in set ' . var_export($set, TRUE) . '.');                     continue;                 }                 if ($metadata['expire'] > $ct) {                     continue;                 }                 SimpleSAML_Logger::debug('metarefresh: ' . $entityId . ' expired ' . date('l jS \of F Y h:i:s A', $metadata['expire']) );                 SimpleSAML_Logger::debug('metarefresh: Delete expired metadata entry ' .                     var_export($entityId, TRUE) . ' in set ' . var_export($set, TRUE) . '. (' . ($ct - $metadata['expire']) . ' sec)');                 $metaHandler->deleteMetadata($entityId, $set);             }         }     }
 
      private function getTime() {         /* The current date, as a string. */         date_default_timezone_set('UTC');         $when = date('Y-m-d\\TH:i:s\\Z');         return $when;     }
  }
  ?>
  |