Skip to content
Snippets Groups Projects
Commit 43c54481 authored by Jaime Pérez Crespo's avatar Jaime Pérez Crespo
Browse files

Merge pull request #146 from sitya/master

Adding MDX metadata handler
parents 6978e6ff 6f292990
No related branches found
No related tags found
No related merge requests found
......@@ -560,6 +560,17 @@ $config = array(
* - 'file': Path to the XML file with the metadata.
* - 'url': The URL to fetch metadata from. THIS IS ONLY FOR DEBUGGING - THERE IS NO CACHING OF THE RESPONSE.
*
* MDX metadata handler:
* This metadata handler looks up for the metadata of an entity at the given MDX server.
* The MDX metadata handler defines the following options:
* - 'type': This is always 'mdx'.
* - 'server': URL of the MDX server (url:port). Mandatory.
* - 'validateFingerprint': The fingerprint of the certificate used to sign the metadata.
* You don't need this option if you don't want to validate the signature on the metadata. Optional.
* - 'cachedir': Directory where metadata can be cached. Optional.
* - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
* hours (86400 seconds). Optional.
*
*
* Examples:
*
......@@ -577,6 +588,10 @@ $config = array(
* array('type' => 'xml', 'file' => 'idp.example.org-idpMeta.xml'),
* ),
*
* This example defines an mdx source.
* 'metadata.sources' => array(
* array('type' => 'mdx', server => 'http://mdx.server.com:8080', 'cachedir' => '/var/simplesamlphp/mdx-cache', 'cachelength' => 86400)
* ),
*
* Default:
* 'metadata.sources' => array(
......
<?php
/**
* This class implements SAML Metadata Exchange Protocol
*
* @author Andreas Åkre Solberg, UNINETT AS.
* @author Olav Morken, UNINETT AS.
* @author Tamas Frank, NIIFI
* @package simpleSAMLphp
*/
class SimpleSAML_Metadata_MetaDataStorageHandlerMDX extends SimpleSAML_Metadata_MetaDataStorageSource {
/**
* The URL of MDX server (url:port)
*/
private $server;
/**
* The fingerprint of the certificate used to sign the metadata. You don't need this option if you don't want to validate the signature on the metadata.
*/
private $validateFingerprint;
/**
* The cache directory, or NULL if no cache directory is configured.
*/
private $cacheDir;
/**
* The maximum cache length, in seconds.
*/
private $cacheLength;
/**
* This function initializes the dynamic XML metadata source.
*
* Options:
* - 'server': URL of the MDX server (url:port). Mandatory.
* - 'validateFingerprint': The fingerprint of the certificate used to sign the metadata.
* You don't need this option if you don't want to validate the signature on the metadata. Optional.
* - 'cachedir': Directory where metadata can be cached. Optional.
* - 'cachelength': Maximum time metadata cah be cached, in seconds. Default to 24
* hours (86400 seconds).
*
* @param array $config The configuration for this instance of the XML metadata source.
*/
protected function __construct($config) {
assert('is_array($config)');
if (!array_key_exists('server', $config)){
throw new Exception('Error, the $server parameter of the config has not been set!');
} else {
$this->server = $config['server'];
}
if (array_key_exists('validateFingerprint', $config)) {
$this->validateFingerprint = $config['validateFingerprint'];
} else {
$this->validateFingerprint = NULL;
}
if (array_key_exists('cachedir', $config)) {
$globalConfig = SimpleSAML_Configuration::getInstance();
$this->cacheDir = $globalConfig->resolvePath($config['cachedir']);
} else {
$this->cacheDir = NULL;
}
if (array_key_exists('cachelength', $config)) {
$this->cacheLength = $config['cachelength'];
} else {
$this->cacheLength = 86400;
}
}
/**
* This function returns an associative array with metadata for all entities in the given set. The
* key of the array is the entity id.
*
* @param $set The set we want to list metadata for.
* @return An associative array with all entities in the given set.
*/
public function getMetadataSet($set) {
/* We don't have this metadata set. */
return array();
}
/**
* 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 $this->cacheDir . '/' . $set . '-' . $cachekey . '.cached.xml';
}
/**
* 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.MDX: Reading cache [' . $entityId . '] => [' . $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.MDX: 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;
}
/**
* 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.MDX: Writing cache [' . $entityId . '] => [' . $cachefilename . ']' );
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();
case 'attributeauthority-remote':
$ret = $entity->getAttributeAuthorities();
return $ret[0];
default:
SimpleSAML_Logger::warning('MetaData - Handler.MDX: Unknown metadata set: ' . $set);
}
return NULL;
}
/**
* Overriding this function from the superclass SimpleSAML_Metadata_MetaDataStorageSource.
*
* This function retrieves metadata for the given entity id in the given set of metadata.
* It will return NULL if it is unable to locate the metadata.
*
* This class implements this function using the getMetadataSet-function. A subclass should
* override this function if it doesn't implement the getMetadataSet function, or if the
* implementation of getMetadataSet is slow.
*
* @param $index The entityId or metaindex we are looking up.
* @param $set The set we are looking for metadata in.
* @return An associative array with metadata for the given entity, or NULL if we are unable to
* locate the entity.
*/
public function getMetaData($index, $set) {
assert('is_string($index)');
assert('is_string($set)');
SimpleSAML_Logger::info('MetaData - Handler.MDX: Loading metadata entity [' . $index . '] from [' . $set . ']' );
/* 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.MDX: Using cached metadata for: ' . $index . '.');
return $data;
}
/* Look at Metadata Query Protocol: https://github.com/iay/md-query/blob/master/draft-young-md-query.txt */
$mdx_url = $this->server . '/entities/' . urlencode( $index);
SimpleSAML_Logger::debug('MetaData - Handler.MDX: Downloading metadata for "'. $index .'" from [' . $mdx_url . ']' );
try {
$xmldata = SimpleSAML_Utilities::fetch($mdx_url);
} catch(Exception $e) {
SimpleSAML_Logger::warning('Fetching metadata for ' . $index . ': ' . $e->getMessage());
}
if (empty($xmldata)) {
throw new Exception('Error downloading metadata for "'. $index .'" from "' . $mdx_url . '": ' .
SimpleSAML_Utilities::getLastError());
}
$entity = SimpleSAML_Metadata_SAMLParser::parseString($xmldata);
SimpleSAML_Logger::debug('MetaData - Handler.MDX: Completed parsing of [' . $mdx_url . ']' );
if( $this->validateFingerprint !== NULL) {
if(!$entity->validateFingerprint($this->validateFingerprint)) {
throw new Exception ('Error, could not verify signature for entity: ' . $index . '".');
}
}
$data = self::getParsedSet($entity, $set);
if ($data === NULL) {
throw new Exception('No metadata for set "' . $set . '" available from "' . $index . '".');
}
$this->writeToCache($set, $index, $data);
return $data;
}
}
......@@ -66,6 +66,8 @@ abstract class SimpleSAML_Metadata_MetaDataStorageSource {
return new SimpleSAML_Metadata_MetaDataStorageHandlerXML($sourceConfig);
case 'serialize':
return new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($sourceConfig);
case 'mdx':
return new SimpleSAML_Metadata_MetaDataStorageHandlerMDX($sourceConfig);
default:
throw new Exception('Invalid metadata source type: "' . $type . '".');
}
......
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