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

aggregator2-module

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2264 44740490-163a-0410-bde0-09ae8108e29a
parent 00ea2891
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env php
<?php
require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/lib/_autoload.php');
$name = basename($argv[0]);
if ($argc < 2) {
fprintf(STDERR, "$name: Missing id of aggregator.\n");
exit(1);
}
$id = $argv[1];
$aggregator = sspmod_aggregator2_Aggregator::getAggregator($id);
$aggregator->updateCache();
<?php
/* This is the configuration file for the aggregator2-module. */
$config = array(
/*
* 'example' will be one set of aggregated metadata.
* The aggregated metadata can be retrieved from:
* https://.../simplesaml/module.php/aggregator2/get.php?id=example
*/
'example' => array(
/* 'sources' is an array with the places we want to fetch metadata from. */
'sources' => array(
/* Metadata validated by the https-certificate of the server. */
array(
/* The URL we should fetch the metadata from. */
'url' => 'https://sp.example.org/metadata.xml',
/*
* To enable validation of the https-certificate, we must
* specify a file with valid CA certificates.
*
* This can be an absolute path, or a path relative to the
* cert-directory.
*/
'ssl.cafile' => '/etc/ssl/certs/ca-certificates.crt',
),
/* Metadata validated by its signature. */
array(
/* The URL we should fetch the metadata from. */
'url' => 'http://idp.example.org/metadata.xml',
/*
* To verify the signature in the metadata, we must specify
* a certificate that should be used. Note: This cannot
* be a CA certificate.
*
* This can be an absolute path, or a path relative to the
* cert-directory.
*/
'cert' => 'idp.example.org.crt',
),
/* Metadata from a file. */
array(
'url' => '/var/simplesaml/somemetadata.xml',
),
),
/*
* Update this metadata during this cron tag.
*
* For this option to work, you must configure the cron-module,
* and also add a cache directory.
*
* This option is optional. If cron is not configured, the metadata
* caches will be updated when receiving requests for metadata.
*/
'cron.tag' => 'hourly',
/*
* The directory we will store downloaded and generated metadata.
* This directory must be writeable by the web-server.
*
* This option is optional, but if unspecified, every request for the
* aggregated metadata will result in the aggregator fetching and
* parsing all metadata sources.
*/
'cache.directory' => '/var/cache/simplesaml-aggregator2',
/*
* This is the number of seconds we will cache the metadata file we generate.
* This should be a longer time than the interval between each time the cron
* job is executed.
*
* This option is optional. If unspecified, the metadata will be generated
* on every request.
*/
'cache.generated' => 24*60*60,
/*
* The generated metadata will have a validUntil set to the time it is generated
* plus this number of seconds.
*/
'valid.length' => 7*24*60*60,
/*
* The private key we should use to sign the metadata, in pem-format.
*
* This is optional. If it is not specified, the metadata will not be signed.
*/
'sign.privatekey' => 'metadata.pem',
/*
* The password for the private key.
*
* Optional, the private key is assumed to be unencrypted if this option
* isn't set.
*/
'sign.privatekey_pass' => 'secret',
/*
* The certificate that corresponds to the private key.
*
* If specified, the certificate will be included in the signature in the metadata.
*/
'sign.certificate' => 'metadata.crt',
),
);
This file indicates that the default state of this module
is disabled. To enable, create a file named enable in the
same directory as this file.
aggregator2 Module
==================
This is an experimental module for aggregating metadata.
It is designed to preserve most of the common metadata items, and also attempt to preserve unknown elements.
*Note*: This aggregator only works on XML metadata, and does its work independently of the of other parts of simpleSAMLphp, such as the `metarefresh` module.
Configuration
-------------
This module is configured through the `config/module_aggregator2.php` configuration file.
An example file is available in `modules/aggregator2/config-templates/`:
cd /var/simplesaml
cp modules/aggregator2/config-templates/module_aggregator2.php config/
The configuration file contains one or more aggregators in the configuration array.
The index in the configuration array gives the identifier of the aggregator.
### Aggregator entry configuration
The aggregator can be configured with the following options:
`sources`
: Array which describes which metadata we should download.
`cron.tag`
: Can be used to periodically run an update.
Only useful when you have enabled caching of metadata.
`cache.directory`
: A path to a directory where the aggregator will cache downloaded and generated metadata.
This directory must be writeable by the webserver.
`cache.generated`
: The number of seconds generated metadata should be cached.
If this option is unset, the generated metadata will not be cached.
`valid.length`
: The number of seconds the generated metadata should be valid.
This is used to set the validUntil attribute on the generated metadata.
The default is one week.
: *Note*: The `cache.generated` option must be smaller than this option, otherwise you will end up returning outdated metadata.
`ssl.cafile`
: This option enables validation of the server certificate when fetching metadata over https.
It must be set to a path to a PEM-file which contains one or more valid CA certificates.
The path can be absolute, or it can be relative to the `cert`-directory.
: *Note*: This option can be overridden for each metadata source.
`sign.privatekey`
: The private key that should be used to sign the metadata, in PEM format.
The path to the private key can be absolute, or it can be relative to the `cert`-directory.
`sign.privatekey_pass`
: The password for the private key.
If this option is unset, the private key is assumed to be unencrypted.
`sign.certificate`
: The certificate which contains the public key corresponding to the private key, in PEM format.
This certificate is included in the generated metadata.
The path to the certificate can be absolute, or it can be relative to the `cert`-directory.
### Aggregator source configuration
`url`
: The URL the metadata should be fetched from.
`ssl.cafile`
: This option enables validation of the server certificate when fetching metadata over https.
It must be the path to a PEM-file which contains one or more valid CA certificates.
The path can be absolute, or it can be relative to the `cert`-directory.
: *Note*: This option overrides the aggregator option.
`cert`
: Check the signature on the metadata against the specified certificate.
The path to the certificate can be absolute, or it can be relative to the `cert`-directory.
: *Note*: This can not be a CA certificate.
Validation against a CA certificate is not supported.
Retrieving aggregated metadata
------------------------------
The metadata can be downloaded from the following location:
http://<server>/simplesaml/modules.php/aggregator2/get.php?id=<aggregator id>
Asynchronous metadata updates
-----------------------------
By default, the `aggregator2` module will update the metadata when receiving a request.
For performance reasons, it is recommended to run the updates asynchronously.
By doing this, the aggregated metadata will be generated in the background.
To enable this, you must configure a cache directory with the `cache.directory` option.
This directory must be writeable by the web server.
You can then enable caching of generated metadata by setting the `cache.generated` option to the number of seconds the metadata can be cached.
You will now have a configuration that caches both downloaded and generated metadata.
It will however still update the metadata when the user accesses the aggregator endpoint
To update the generated metadata in the background, you must add a `cron.tag` option.
This option must reference a cron tag entry configured in `module_cron.php`.
Once this is done, your aggregated metadata will be updated everytime that cron entry is executed.
<?php
/**
* cron hook to update aggregator2 metadata.
*
* @param array &$croninfo Output
*/
function aggregator2_hook_cron(&$croninfo) {
assert('is_array($croninfo)');
assert('array_key_exists("summary", $croninfo)');
assert('array_key_exists("tag", $croninfo)');
$cronTag = $croninfo['tag'];
$config = SimpleSAML_Configuration::getConfig('module_aggregator2.php');
$config = $config->toArray();
foreach ($config as $id => $c) {
if (!isset($c['cron.tag'])) {
continue;
}
if ($c['cron.tag'] !== $cronTag) {
continue;
}
try {
$a = sspmod_aggregator2_Aggregator::getAggregator($id);
$a->updateCache();
} catch (Exception $e) {
$croninfo['summary'][] = 'Error during aggregator2 cacheupdate: ' . $e->getMessage();
}
}
}
<?php
/**
* Class which implements a basic metadata aggregator.
*
* @package simpleSAMLphp
* @version $Id$
*/
class sspmod_aggregator2_Aggregator {
/**
* The ID of this aggregator.
*
* @var string
*/
protected $id;
/**
* Our log "location".
*
* @var string
*/
protected $logLoc;
/**
* Which cron-tag this should be updated in.
*
* @var string|NULL
*/
protected $cronTag;
/**
* Absolute path to a cache directory.
*
* @var string|NULL
*/
protected $cacheDirectory;
/**
* The entity sources.
*
* Array of sspmod_aggregator2_EntitySource objects.
*
* @var array
*/
protected $sources = array();
/**
* How long the generated metadata should be valid, as a number of seconds.
*
* This is used to set the validUntil attribute on the generated EntityDescriptor.
*
* @var int
*/
protected $validLength;
/**
* Duration we should cache generated metadata.
*
* @var int
*/
protected $cacheGenerated;
/**
* The key we should use to sign the metadata.
*
* @var string|NULL
*/
protected $signKey;
/**
* The password for the private key.
*
* @var string|NULL
*/
protected $signKeyPass;
/**
* The certificate of the key we sign the metadata with.
*
* @var string|NULL
*/
protected $signCert;
/**
* The CA certificate file that should be used to validate https-connections.
*
* @var string|NULL
*/
protected $sslCAFile;
/**
* The cache ID for our generated metadata.
*
* @var string
*/
protected $cacheId;
/**
* The cache tag for our generated metadata.
*
* This tag is used to make sure that a config change
* invalidates our cached metadata.
*
* @var string
*/
protected $cacheTag;
/**
* Initialize this aggregator.
*
* @param string $id The id of this aggregator.
* @param SimpleSAML_Configuration $config The configuration for this aggregator.
*/
protected function __construct($id, SimpleSAML_Configuration $config) {
assert('is_string($id)');
$this->id = $id;
$this->logLoc = 'aggregator2:' . $this->id . ': ';
$this->cronTag = $config->getString('cron.tag', NULL);
$this->cacheDirectory = $config->getString('cache.directory', NULL);
if ($this->cacheDirectory !== NULL) {
$this->cacheDirectory = SimpleSAML_Utilities::resolvePath($this->cacheDirectory);
}
$this->cacheGenerated = $config->getInteger('cache.generated', NULL);
if ($this->cacheGenerated !== NULL) {
$this->cacheId = sha1($this->id);
$this->cacheTag = sha1(serialize($config));
}
$this->validLength = $config->getInteger('valid.length', 7*24*60*60);
$globalConfig = SimpleSAML_Configuration::getInstance();
$certDir = $globalConfig->getPathValue('certdir', 'cert/');
$signKey = $config->getString('sign.privatekey', NULL);
if ($signKey !== NULL) {
$signKey = SimpleSAML_Utilities::resolvePath($signKey, $certDir);
$this->signKey = @file_get_contents($signKey);
if ($this->signKey === NULL) {
throw new SimpleSAML_Error_Exception('Unable to load private key from ' . var_export($signKey, TRUE));
}
}
$this->signKeyPass = $config->getString('sign.privatekey_pass', NULL);
$signCert = $config->getString('sign.certificate', NULL);
if ($signCert !== NULL) {
$signCert = SimpleSAML_Utilities::resolvePath($signCert, $certDir);
$this->signCert = @file_get_contents($signCert);
if ($this->signCert === NULL) {
throw new SimpleSAML_Error_Exception('Unable to load certificate file from ' . var_export($signCert, TRUE));
}
}
$this->sslCAFile = $config->getString('ssl.cafile', NULL);
$this->initSources($config->getConfigList('sources'));
}
/**
* Populate the sources array.
*
* This is called from the constructor, and can be overridden in subclasses.
*
* @param array $sources The sources as an array of SimpleSAML_Configuration objects.
*/
protected function initSources(array $sources) {
foreach ($sources as $source) {
$this->sources[] = new sspmod_aggregator2_EntitySource($this, $source);
}
}
/**
* Return an instance of the aggregator with the given id.
*
* @param string $id The id of the aggregator.
*/
public static function getAggregator($id) {
assert('is_string($id)');
$config = SimpleSAML_Configuration::getConfig('module_aggregator2.php');
return new sspmod_aggregator2_Aggregator($id, $config->getConfigItem($id));
}
/**
* Retrieve the ID of the aggregator.
*
* @return string The ID of this aggregator.
*/
public function getId() {
return $this->id;
}
/**
* Add an item to the cache.
*
* @param string $id The identifier of this data.
* @param string $data The data.
* @param int $expires The timestamp the data expires.
* @param string|NULL $tag An extra tag that can be used to verify the validity of the cached data.
*/
public function addCacheItem($id, $data, $expires, $tag = NULL) {
assert('is_string($id)');
assert('is_string($data)');
assert('is_int($expires)');
assert('is_null($tag) || is_string($tag)');
$cacheFile = $this->cacheDirectory . '/' . $id;
try {
SimpleSAML_Utilities::writeFile($cacheFile, $data);
} catch (Exception $e) {
SimpleSAML_Logger::warning($this->logLoc . 'Unable to write to cache file ' . var_export($cacheFile, TRUE));
return;
}
$expireInfo = (string)$expires;
if ($tag !== NULL) {
$expireInfo .= ':' . $tag;
}
$expireFile = $cacheFile . '.expire';
try {
SimpleSAML_Utilities::writeFile($expireFile, $expireInfo);
} catch (Exception $e) {
SimpleSAML_Logger::warning($this->logLoc . 'Unable to write expiration info to ' . var_export($expireFile, TRUE));
return $metadata;
}
}
/**
* Check validity of cached data.
*
* @param string $id The identifier of this data.
* @param string $tag The tag that was passed to addCacheItem.
* @return bool TRUE if the data is valid, FALSE if not.
*/
public function isCacheValid($id, $tag = NULL) {
assert('is_string($id)');
assert('is_null($tag) || is_string($tag)');
$cacheFile = $this->cacheDirectory . '/' . $id;
if (!file_exists($cacheFile)) {
return FALSE;
}
$expireFile = $cacheFile . '.expire';
if (!file_exists($expireFile)) {
return FALSE;
}
$expireData = @file_get_contents($expireFile);
if ($expireData === FALSE) {
return FALSE;
}
$expireData = explode(':', $expireData, 2);
$expireTime = (int)$expireData[0];
if ($expireTime <= time()) {
return FALSE;
}
if (count($expireData) === 1) {
$expireTag = NULL;
} else {
$expireTag = $expireData[1];
}
if ($expireTag !== $tag) {
return FALSE;
}
return TRUE;
}
/**
* Get the cache item.
*
* @param string $id The identifier of this data.
* @param string $tag The tag that was passed to addCacheItem.
* @return string|NULL The cache item, or NULL if it isn't cached or if it is expired.
*/
public function getCacheItem($id, $tag = NULL) {
assert('is_string($id)');
assert('is_null($tag) || is_string($tag)');
if (!$this->isCacheValid($id, $tag)) {
return NULL;
}
$cacheFile = $this->cacheDirectory . '/' . $id;
return @file_get_contents($cacheFile);
}
/**
* Get the cache filename for the specific id.
*
* @param string $id The identifier of the cached data.
* @return string|NULL The filename, or NULL if the cache file doesn't exist.
*/
public function getCacheFile($id) {
assert('is_string($id)');
$cacheFile = $this->cacheDirectory . '/' . $id;
if (!file_exists($cacheFile)) {
return NULL;
}
return $cacheFile;
}
/**
* Retrieve the SSL CA file path, if it is set.
*
* @return string|NULL The SSL CA file path.
*/
public function getCAFile() {
return $this->sslCAFile;
}
/**
* Sign the generated EntitiesDescriptor.
*/
protected function addSignature(SAML2_SignedElement $element) {
if ($this->signKey === NULL) {
return;
}
$privateKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type' => 'private'));
if ($this->signKeyPass !== NULL) {
$privateKey->passphrase = $this->signKeyPass;
}
$privateKey->loadKey($this->signKey, FALSE);
$element->setSignatureKey($privateKey);
if ($this->signCert !== NULL) {
$element->setCertificates(array($this->signCert));
}
}
/**
* Retrieve all entities as an EntitiesDescriptor.
*
* @return SAML2_XML_md_EntitiesDescriptor The entities.
*/
protected function getEntitiesDescriptor() {
$ret = new SAML2_XML_md_EntitiesDescriptor();
foreach ($this->sources as $source) {
$m = $source->getMetadata();
if ($m === NULL) {
continue;
}
$ret->children[] = $m;
}
$ret->validUntil = time() + $this->validLength;
return $ret;
}
/**
* Retrieve the complete, signed metadata as text.
*
* This function will write the new metadata to the cache file, but will not return
* the cached metadata.
*
* @return string The metadata, as text.
*/
public function updateCachedMetadata() {
$ed = $this->getEntitiesDescriptor();
$this->addSignature($ed);
$xml = $ed->toXML();
$xml = $xml->ownerDocument->saveXML($xml);
if ($this->cacheGenerated !== NULL) {
SimpleSAML_Logger::debug($this->logLoc . 'Saving generated metadata to cache.');
$this->addCacheItem($this->cacheId, $xml, time() + $this->cacheGenerated, $this->cacheTag);
}
return $xml;
}
/**
* Retrieve the complete, signed metadata as text.
*
* @return string The metadata, as text.
*/
public function getMetadata() {
if ($this->cacheGenerated !== NULL) {
$xml = $this->getCacheItem($this->cacheId, $this->cacheTag);
if ($xml !== NULL) {
SimpleSAML_Logger::debug($this->logLoc . 'Loaded generated metadata from cache.');
return $xml;
}
}
return $this->updateCachedMetadata();
}
/**
* Update the cached copy of our metadata.
*/
public function updateCache() {
foreach ($this->sources as $source) {
$source->updateCache();
}
$this->updateCachedMetadata();
}
}
<?php
/**
* Class for loading metadata from files and URLs.
*
* @package simpleSAMLphp
* @version $Id$
*/
class sspmod_aggregator2_EntitySource {
/**
* Our log "location".
*
* @var string
*/
protected $logLoc;
/**
* The aggregator we belong to.
*
* @var sspmod_aggregator2_Aggregator
*/
protected $aggregator;
/**
* The URL we should fetch it from.
*
* @var string
*/
protected $url;
/**
* The SSL CA file that should be used to validate the connection.
*
* @var string|NULL
*/
protected $sslCAFile;
/**
* The certificate we should use to validate downloaded metadata.
*
* @var string|NULL
*/
protected $certificate;
/**
* The parsed metadata.
*
* @var SAML2_XML_md_EntitiesDescriptor|SAML2_XML_md_EntityDescriptor|NULL
*/
protected $metadata;
/**
* The cache ID.
*
* @var string
*/
protected $cacheId;
/**
* The cache tag.
*
* @var string
*/
protected $cacheTag;
/**
* Whether we have attempted to update the cache already.
*
* @var bool
*/
protected $updateAttempted;
/**
* Initialize this EntitySource.
*
* @param SimpleSAML_Configuration $config The configuration.
*/
public function __construct(sspmod_aggregator2_Aggregator $aggregator, SimpleSAML_Configuration $config) {
$this->logLoc = 'aggregator2:' . $aggregator->getId() . ': ';
$this->aggregator = $aggregator;
$this->url = $config->getString('url');
$this->sslCAFile = $config->getString('ssl.cafile', NULL);
if ($this->sslCAFile === NULL) {
$this->sslCAFile = $aggregator->getCAFile();
}
$this->certificate = $config->getString('cert', NULL);
$this->cacheId = sha1($this->url);
$this->cacheTag = sha1(serialize($config));
}
/**
* Retrieve and parse the metadata.
*
* @return SAML2_XML_md_EntitiesDescriptor|SAML2_XML_md_EntityDescriptor|NULL
* The downloaded metadata or NULL if we were unable to download or parse it.
*/
private function downloadMetadata() {
SimpleSAML_Logger::debug($this->logLoc . 'Downloading metadata from ' .
var_export($this->url, TRUE));
$context = array('ssl' => array());
if ($this->sslCAFile !== NULL) {
$context['ssl']['cafile'] = SimpleSAML_Utilities::resolveCert($this->sslCAFile);
SimpleSAML_Logger::debug($this->logLoc . 'Validating https connection against CA certificate(s) found in ' .
var_export($context['ssl']['cafile'], TRUE));
$context['ssl']['verify_peer'] = TRUE;
$context['ssl']['CN_match'] = parse_url($this->url, PHP_URL_HOST);
}
$context = stream_context_create($context);
$data = file_get_contents($this->url, 0, $context);
if ($data === FALSE || $data === NULL) {
SimpleSAML_Logger::error($this->logLoc . 'Unable to load metadata from ' .
var_export($this->url, TRUE));
return NULL;
}
$doc = new DOMDocument();
$res = $doc->loadXML($data);
if (!$res) {
SimpleSAML_Logger::error($this->logLoc . 'Error parsing XML from ' .
var_export($this->url, TRUE));
return NULL;
}
$root = SAML2_Utils::xpQuery($doc->firstChild, '/saml_metadata:EntityDescriptor|/saml_metadata:EntitiesDescriptor');
if (count($root) === 0) {
SimpleSAML_Logger::error($this->logLoc . 'No <EntityDescriptor> or <EntitiesDescriptor> in metadata from ' .
var_export($this->url, TRUE));
return NULL;
}
if (count($root) > 1) {
SimpleSAML_Logger::error($this->logLoc . 'More than one <EntityDescriptor> or <EntitiesDescriptor> in metadata from ' .
var_export($this->url, TRUE));
return NULL;
}
$root = $root[0];
try {
if ($root->localName === 'EntityDescriptor') {
$md = new SAML2_XML_md_EntityDescriptor($root);
} else {
$md = new SAML2_XML_md_EntitiesDescriptor($root);
}
} catch (Exception $e) {
SimpleSAML_Logger::error($this->logLoc . 'Unable to parse metadata from ' .
var_export($this->url, TRUE) . ': ' . $e->getMessage());
return NULL;
}
if ($this->certificate !== NULL) {
$file = SimpleSAML_Utilities::resolveCert($this->certificate);
$certData = file_get_contents($file);
if ($certData === FALSE) {
throw new SimpleSAML_Error_Exception('Error loading certificate from ' . var_export($file, TRUE));
}
/* Extract the public key from the certificate for validation. */
$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
$key->loadKey($file, TRUE);
if (!$md->validate($key)) {
SimpleSAML_Logger::error($this->logLoc . 'Error validating signature on metadata.');
return NULL;
}
SimpleSAML_Logger::debug($this->logLoc . 'Validated signature on metadata from ' . var_export($this->url, TRUE));
}
return $md;
}
/**
* Attempt to update our cache file.
*/
public function updateCache() {
if ($this->updateAttempted) {
return;
}
$this->updateAttempted = TRUE;
$this->metadata = $this->downloadMetadata();
if ($this->metadata === NULL) {
return;
}
$expires = time() + 24*60*60; /* Default expires in one day. */
if ($this->metadata->validUntil !== NULL && $this->metadata->validUntil < $expires) {
$expires = $this->metadata->validUntil;
}
if ($this->metadata->cacheDuration !== NULL) {
try {
$durationTo = SimpleSAML_Utilities::parseDuration($this->metadata->cacheDuration);
} catch (Exception $e) {
SimpleSAML_Logger::warning($this->logLoc . 'Invalid cacheDuration in metadata from ' .
var_export($this->url, TRUE) . ': ' . var_export($this->metadata->cacheDuration, TRUE));
return;
}
if ($durationTo < $expires) {
$expires = $durationTo;
}
}
$metadataSerialized = serialize($this->metadata);
$this->aggregator->addCacheItem($this->cacheId, $metadataSerialized, $expires, $this->cacheTag);
}
/**
* Retrieve the metadata file.
*
* This function will check its cached copy, to see whether it can be used.
*
* @return SAML2_XML_md_EntityDescriptor|SAML2_XML_md_EntitiesDescriptor|NULL The downloaded metadata.
*/
public function getMetadata() {
if ($this->metadata !== NULL) {
/* We have already downloaded the metdata. */
return $this->metadata;
}
if (!$this->aggregator->isCacheValid($this->cacheId, $this->cacheTag)) {
$this->updateCache();
if ($this->metadata !== NULL) {
return $this->metadata;
}
/* We were unable to update the cache - use cached metadata. */
}
$cacheFile = $this->aggregator->getCacheFile($this->cacheId);
if (!file_exists($cacheFile)) {
SimpleSAML_Logger::error($this->logLoc . 'No cached metadata available.');
return NULL;
}
SimpleSAML_Logger::debug($this->logLoc . 'Using cached metadata from ' .
var_export($cacheFile, TRUE));
$metadata = file_get_contents($cacheFile);
if ($metadata !== NULL) {
$this->metadata = unserialize($metadata);
return $this->metadata;
}
return NULL;
}
}
<?php
if (!isset($_REQUEST['id'])) {
throw new SimpleSAML_Error_BadRequest('Missing required id-parameter.');
}
$id = (string)$_REQUEST['id'];
$aggregator = sspmod_aggregator2_Aggregator::getAggregator($id);
$xml = $aggregator->getMetadata();
header('Content-Type: application/samlmetadata+xml');
header('Content-Length: ' . strlen($xml));
echo($xml);
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