Skip to content
Snippets Groups Projects
Commit 631cb720 authored by Olav Morken's avatar Olav Morken
Browse files

Dynamic metadata source - add support for cache

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@869 44740490-163a-0410-bde0-09ae8108e29a
parent cd813406
No related branches found
No related tags found
No related merge requests found
...@@ -12,74 +12,43 @@ ...@@ -12,74 +12,43 @@
class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Metadata_MetaDataStorageSource { 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 $cacheDir;
private $config;
/**
* The maximum cache length, in seconds.
*/
private $cacheLength;
/** /**
* This function initializes the XML metadata source. The configuration must contain one of * This function initializes the dynamic XML metadata source.
* the following options: *
* - 'file': Path to a file with the metadata. This path is relative to the simpleSAMLphp * Options:
* base directory. * - 'cachedir': Directory where metadata can be cached. Optional.
* - 'url': URL we should download the metadata from. This is only meant for testing. * - '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) { protected function __construct($config) {
assert('is_array($config)');
$this->config = $config; if (array_key_exists('cachedir', $config)) {
$globalConfig = SimpleSAML_Configuration::getInstance();
/* Get the configuration. $this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
$globalConfig = SimpleSAML_Configuration::getInstance();
if(array_key_exists('cache', $config)) {
$src = $globalConfig->resolvePath($config['file']);
} elseif(array_key_exists('url', $config)) {
$src = $config['url'];
} else { } else {
throw new Exception('Missing either \'file\' or \'url\' in XML metadata source configuration.'); $this->cacheDir = NULL;
} }
if (array_key_exists('cachelength', $config)) {
$SP1x = array(); $this->cacheLength = $config['cachelength'];
$IdP1x = array(); } else {
$SP20 = array(); $this->cacheLength = 86400;
$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;
}
} }
$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 ...@@ -91,40 +60,130 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me
* @return An associative array with all entities in the given set. * @return An associative array with all entities in the given set.
*/ */
public function getMetadataSet($set) { public function getMetadataSet($set) {
/*
if(array_key_exists($set, $this->metadata)) {
return $this->metadata[$set];
}
*/
/* We don't have this metadata set. */ /* We don't have this metadata set. */
return array(); 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); $cachekey = sha1($entityId);
$globalConfig = SimpleSAML_Configuration::getInstance(); $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 (!file_exists($cachefilename)) return NULL;
if (!is_readable($cachefilename)) throw new Exception('Could not read cache file for entity [' . $cachefilename. ']'); 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 . ']' ); 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. ']'); 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 . ']' ); 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. * Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
* *
...@@ -141,77 +200,73 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me ...@@ -141,77 +200,73 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerDynamicXML extends SimpleSAML_Me
* locate the entity. * locate the entity.
*/ */
public function getMetaData($index, $set) { public function getMetaData($index, $set) {
assert('is_string($index)');
assert('is_string($set)');
SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: Loading metadata entity [' . $index . '] from [' . $set . ']' );
/* Get the configuration. */
$globalConfig = SimpleSAML_Configuration::getInstance();
if (!preg_match('@(https?://([-\w\.]+)+(:\d+)?(/([\w/_\.]*(\?\S+)?)?)?)@', $index)) { 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.' ); SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: EntityID/index [' . $index . '] does not look like an URL. Skipping.' );
return NULL; return NULL;
} }
$xmldata = NULL; SimpleSAML_Logger::info('MetaData - Handler.DynamicXML: Loading metadata entity [' . $index . '] from [' . $set . ']' );
/** /* Read from cache if possible. */
* Read from cache if cache is defined. $data = $this->getFromCache($set, $index);
*/
if (!empty($this->config['cachedir'])) { if ($data !== NULL && array_key_exists('expires', $data) && $data['expires'] < time()) {
$xmldata = $this->getFromCache($index); /* 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)) { if (empty($xmldata)) {
$xmldata = file_get_contents($index); throw new Exception('Error downloading metadata from "' . $index . '": ' .
SimpleSAML_Utilities::getLastError());
if (!empty($this->config['cachedir'])) {
$this->writeToCache($index, $xmldata);
}
} }
$entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsString($xmldata); $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.' ); if (array_key_exists($index, $entities)) {
$entity = $entities[$index];
foreach($entities as $entityId => $entity) { $data = self::getParsedSet($entity, $set);
if ($data === NULL) {
SimpleSAML_Logger::debug('MetaData - Handler.DynamicXML: Looking for [' . $index . '] found [' . $entityId . '] entries.' ); throw new Exception('No metadata for set "' . $set .
'" available from "' . $index . '".');
switch($set) { }
case 'saml20-idp-remote' : } else {
$md = $entity->getMetadata20IdP(); SimpleSAML_Logger::warning('MetaData - Handler.DynamicXML: No entity with correct' .
if ($md !== NULL) return $md; ' entity id found. Using the first entity which defines the correct' .
break; ' metadata.');
foreach ($entities as $entity) {
case 'saml20-sp-remote' : $data = self::getParsedSet($entity, $set);
$md = $entity->getMetadata20SP(); if ($data !== NULL) {
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;
break; break;
}
} }
if ($data === NULL) {
throw new Exception('No entities defines metadata for set "' .
$set . '" in "' . $index . '".');
}
}
$this->writeToCache($set, $index, $data);
} return $data;
return NULL;
} }
} }
?> ?>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment