summaryrefslogtreecommitdiffstats
path: root/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
diff options
context:
space:
mode:
authorOlav Morken <olav.morken@uninett.no>2008-09-15 07:36:36 +0000
committerOlav Morken <olav.morken@uninett.no>2008-09-15 07:36:36 +0000
commit631cb72078d874a38ecdce9c1dfc9cc714c10511 (patch)
tree034a3a149e5af2db01346399cb0532d171d861b2 /lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
parentcd813406faefd6327984d11da7eb53e84761c450 (diff)
downloadsimplesamlphp-631cb72078d874a38ecdce9c1dfc9cc714c10511.zip
simplesamlphp-631cb72078d874a38ecdce9c1dfc9cc714c10511.tar.gz
simplesamlphp-631cb72078d874a38ecdce9c1dfc9cc714c10511.tar.bz2
Dynamic metadata source - add support for cache
git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@869 44740490-163a-0410-bde0-09ae8108e29a
Diffstat (limited to 'lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php')
-rw-r--r--lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php309
1 files changed, 182 insertions, 127 deletions
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
index 60003b5..7404de9 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerDynamicXML.php
@@ -12,74 +12,43 @@
class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Metadata_MetaDataStorageSource {
/**
- * This variable contains an associative array with the parsed metadata.
+ * The cache directory, or NULL if no cache directory is configured.
*/
- private $metadata;
- private $config;
+ private $cacheDir;
+
+
+ /**
+ * The maximum cache length, in seconds.
+ */
+ private $cacheLength;
+
/**
- * This function initializes the XML metadata source. The configuration must contain one of
- * the following options:
- * - 'file': Path to a file with the metadata. This path is relative to the simpleSAMLphp
- * base directory.
- * - 'url': URL we should download the metadata from. This is only meant for testing.
+ * This function initializes the dynamic XML metadata source.
+ *
+ * Options:
+ * - 'cachedir': Directory where metadata can be cached. Optional.
+ * - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
+ * hours (86400 seconds).
*
- * @param $config The configuration for this instance of the XML metadata source.
+ * @param array $config The configuration for this instance of the XML metadata source.
*/
protected function __construct($config) {
+ assert('is_array($config)');
- $this->config = $config;
-
- /* Get the configuration.
- $globalConfig = SimpleSAML_Configuration::getInstance();
-
- if(array_key_exists('cache', $config)) {
- $src = $globalConfig->resolvePath($config['file']);
- } elseif(array_key_exists('url', $config)) {
- $src = $config['url'];
+ if (array_key_exists('cachedir', $config)) {
+ $globalConfig = SimpleSAML_Configuration::getInstance();
+ $this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
} else {
- throw new Exception('Missing either \'file\' or \'url\' in XML metadata source configuration.');
+ $this->cacheDir = NULL;
}
-
- $SP1x = array();
- $IdP1x = array();
- $SP20 = array();
- $IdP20 = array();
-
- $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsFile($src);
- foreach($entities as $entityId => $entity) {
-
- $md = $entity->getMetadata1xSP();
- if($md !== NULL) {
- $SP1x[$entityId] = $md;
- }
-
- $md = $entity->getMetadata1xIdP();
- if($md !== NULL) {
- $IdP1x[$entityId] = $md;
- }
-
- $md = $entity->getMetadata20SP();
- if($md !== NULL) {
- $SP20[$entityId] = $md;
- }
-
- $md = $entity->getMetadata20IdP();
- if($md !== NULL) {
- $IdP20[$entityId] = $md;
- }
-
+ if (array_key_exists('cachelength', $config)) {
+ $this->cacheLength = $config['cachelength'];
+ } else {
+ $this->cacheLength = 86400;
}
- $this->metadata = array(
- 'shib13-sp-remote' => $SP1x,
- 'shib13-idp-remote' => $IdP1x,
- 'saml20-sp-remote' => $SP20,
- 'saml20-idp-remote' => $IdP20,
- );
- */
-
}
@@ -91,40 +60,130 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me
* @return An associative array with all entities in the given set.
*/
public function getMetadataSet($set) {
- /*
- if(array_key_exists($set, $this->metadata)) {
- return $this->metadata[$set];
- }
- */
/* We don't have this metadata set. */
return array();
}
-
-
- private function getCacheFilename($entityId) {
+
+
+ /**
+ * Find the cache file name for an entity,
+ *
+ * @param string $set The metadata set this entity belongs to.
+ * @param string $entityId The entity id of this entity.
+ * @return string The full path to the cache file.
+ */
+ private function getCacheFilename($set, $entityId) {
+ assert('is_string($set)');
+ assert('is_string($entityId)');
+
$cachekey = sha1($entityId);
$globalConfig = SimpleSAML_Configuration::getInstance();
- return $globalConfig->resolvePath($this->config['cachedir']) . '/' . $cachekey . '.cached.xml';
+ return $this->cacheDir . '/' . $set . '-' . $cachekey . '.cached.xml';
}
-
-
- private function getFromCache($entityId) {
- $cachefilename = $this->getCacheFilename($entityId);
+
+
+ /**
+ * Load a entity from the cache.
+ *
+ * @param string $set The metadata set this entity belongs to.
+ * @param string $entityId The entity id of this entity.
+ * @return array|NULL The associative array with the metadata for this entity, or NULL
+ * if the entity could not be found.
+ */
+ private function getFromCache($set, $entityId) {
+ assert('is_string($set)');
+ assert('is_string($entityId)');
+
+ if (empty($this->cacheDir)) {
+ return NULL;
+ }
+
+ $cachefilename = $this->getCacheFilename($set, $entityId);
if (!file_exists($cachefilename)) return NULL;
if (!is_readable($cachefilename)) throw new Exception('Could not read cache file for entity [' . $cachefilename. ']');
SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Reading cache [' . $entityId . '] => [' . $cachefilename . ']' );
- return file_get_contents($cachefilename);
+
+ /* Ensure that this metadata isn't older that the cachelength option allows. This
+ * must be verified based on the file, since this option may be changed after the
+ * file is written.
+ */
+ $stat = stat($cachefilename);
+ if ($stat['mtime'] + $this->cacheLength <= time()) {
+ SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Cache file older that the cachelength option allows.');
+ return NULL;
+ }
+
+ $rawData = file_get_contents($cachefilename);
+ if (empty($rawData)) {
+ throw new Exception('Error reading metadata from cache file "' . $cachefilename . '": ' .
+ SimpleSAML_Utilities::getLastError());
+ }
+
+ $data = unserialize($rawData);
+ if ($data === FALSE) {
+ throw new Exception('Error deserializing cached data from file "' . $cachefilename .'".');
+ }
+
+ if (!is_array($data)) {
+ throw new Exception('Cached metadata from "' . $cachefilename . '" wasn\'t an array.');
+ }
+
+ return $data;
}
-
- private function writeToCache($entityId, $xmldata) {
- $cachefilename = $this->getCacheFilename($entityId);
+
+
+ /**
+ * Save a entity to the cache.
+ *
+ * @param string $set The metadata set this entity belongs to.
+ * @param string $entityId The entity id of this entity.
+ * @param array $data The associative array with the metadata for this entity.
+ */
+ private function writeToCache($set, $entityId, $data) {
+ assert('is_string($set)');
+ assert('is_string($entityId)');
+ assert('is_array($data)');
+
+ if (empty($this->cacheDir)) {
+ return;
+ }
+
+ $cachefilename = $this->getCacheFilename($set, $entityId);
if (!is_writable(dirname($cachefilename))) throw new Exception('Could not write cache file for entity [' . $cachefilename. ']');
SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Writing cache [' . $entityId . '] => [' . $cachefilename . ']' );
- file_put_contents($cachefilename, $xmldata);
+ file_put_contents($cachefilename, serialize($data));
}
-
-
+
+
+ /**
+ * Retrieve metadata for the correct set from a SAML2Parser.
+ *
+ * @param SimpleSAML_Metadata_SAMLParser $entity A SAML2Parser representing an entity.
+ * @param string $set The metadata set we are looking for.
+ * @return array|NULL The associative array with the metadata, or NULL if no metadata for
+ * the given set was found.
+ */
+ private static function getParsedSet(SimpleSAML_Metadata_SAMLParser $entity, $set) {
+ assert('is_string($set)');
+
+ switch($set) {
+ case 'saml20-idp-remote':
+ return $entity->getMetadata20IdP();
+ case 'saml20-sp-remote':
+ return $entity->getMetadata20SP();
+ case 'shib13-idp-remote':
+ return $entity->getMetadata1xIdP();
+ case 'shib13-sp-remote':
+ return $entity->getMetadata1xSP();
+ default:
+ SimpleSAML_Logger::warning('MetaData - Handler.DynamicXML: Unknown metadata set: ' . $set);
+ }
+
+ return NULL;
+ }
+
+
/**
* Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
*
@@ -141,77 +200,73 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me
* locate the entity.
*/
public function getMetaData($index, $set) {
-
-
- SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: Loading metadata entity [' . $index . '] from [' . $set . ']' );
-
-
- /* Get the configuration. */
- $globalConfig = SimpleSAML_Configuration::getInstance();
+ assert('is_string($index)');
+ assert('is_string($set)');
if (!preg_match('@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@', $index)) {
SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: EntityID/index [' . $index . '] does not look like an URL. Skipping.' );
return NULL;
}
- $xmldata = NULL;
+ SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: Loading metadata entity [' . $index . '] from [' . $set . ']' );
- /**
- * Read from cache if cache is defined.
- */
- if (!empty($this->config['cachedir'])) {
- $xmldata = $this->getFromCache($index);
+ /* Read from cache if possible. */
+ $data = $this->getFromCache($set, $index);
+
+ if ($data !== NULL && array_key_exists('expires', $data) && $data['expires'] < time()) {
+ /* Metadata has expired. */
+ $data = NULL;
+ }
+
+ if (isset($data)) {
+ /* Metadata found in cache and not expired. */
+ SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Using cached metadata.');
+ return $data;
}
+ SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Downloading [' . $index . ']' );
+ $xmldata = file_get_contents($index);
if (empty($xmldata)) {
- $xmldata = file_get_contents($index);
-
- if (!empty($this->config['cachedir'])) {
- $this->writeToCache($index, $xmldata);
- }
+ throw new Exception('Error downloading metadata from "' . $index . '": ' .
+ SimpleSAML_Utilities::getLastError());
}
-
$entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsString($xmldata);
+ SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Completed parsing of [' .
+ $index . '] Found [' . count($entities). '] entries.' );
+ if (count($entities) === 0) {
+ throw new Exception('No entities found in "' . $index . '".');
+ }
- SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Completed parsing of [' . $index . '] Found [' . count($entities). '] entries.' );
-
- foreach($entities as $entityId => $entity) {
-
- SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Looking for [' . $index . '] found [' . $entityId . '] entries.' );
-
- switch($set) {
- case 'saml20-idp-remote' :
- $md = $entity->getMetadata20IdP();
- if ($md !== NULL) return $md;
- break;
-
- case 'saml20-sp-remote' :
- $md = $entity->getMetadata20SP();
- if ($md !== NULL) return $md;
- break;
-
- case 'shib13-idp-remote' :
- $md = $entity->getMetadata1xIdP();
- if ($md !== NULL) return $md;
- break;
-
- case 'shib13-sp-remote' :
- $md = $entity->getMetadata1xSP();
- if ($md !== NULL) return $md;
+ if (array_key_exists($index, $entities)) {
+ $entity = $entities[$index];
+ $data = self::getParsedSet($entity, $set);
+ if ($data === NULL) {
+ throw new Exception('No metadata for set "' . $set .
+ '" available from "' . $index . '".');
+ }
+ } else {
+ SimpleSAML_Logger::warning('MetaData - Handler.DynamicXML: No entity with correct' .
+ ' entity id found. Using the first entity which defines the correct' .
+ ' metadata.');
+ foreach ($entities as $entity) {
+ $data = self::getParsedSet($entity, $set);
+ if ($data !== NULL) {
break;
-
+ }
}
+ if ($data === NULL) {
+ throw new Exception('No entities defines metadata for set "' .
+ $set . '" in "' . $index . '".');
+ }
+ }
+ $this->writeToCache($set, $index, $data);
- }
-
- return NULL;
-
+ return $data;
}
-
-
+
}
?> \ No newline at end of file