Create module metarefreshsupann from existing metarefresh
This commit is contained in:
parent
b61ef1fcaf
commit
db940492bd
|
@ -0,0 +1,165 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This script can be used to generate metadata for simpleSAMLphp
|
||||
* based on an XML metadata file.
|
||||
*/
|
||||
|
||||
|
||||
/* This is the base directory of the simpleSAMLphp installation. */
|
||||
$baseDir = dirname(dirname(dirname(dirname(__FILE__))));
|
||||
|
||||
/* Add library autoloader. */
|
||||
require_once($baseDir . '/lib/_autoload.php');
|
||||
|
||||
SimpleSAML_Session::useTransientSession(); /* No need to try to create a session here. */
|
||||
|
||||
if(!SimpleSAML_Module::isModuleEnabled('metarefresh')) {
|
||||
echo("You need to enable the metarefresh module before this script can be used.\n");
|
||||
echo("You can enable it by running the following command:\n");
|
||||
echo(' echo >"' . $baseDir . '/modules/metarefresh/enable' . "\"\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Initialize the configuration. */
|
||||
SimpleSAML_Configuration::setConfigDir($baseDir . '/config');
|
||||
|
||||
/* $outputDir contains the directory we will store the generated metadata in. */
|
||||
$outputDir = $baseDir . '/metadata-generated';
|
||||
|
||||
|
||||
/* $toStdOut is a boolean telling us wheter we will print the output to stdout instead
|
||||
* of writing it to files in $outputDir.
|
||||
*/
|
||||
$toStdOut = FALSE;
|
||||
|
||||
/* $validateFingerprint contains the fingerprint of the certificate which should have been used
|
||||
* to sign the EntityDescriptor in the metadata, or NULL if fingerprint validation shouldn't be
|
||||
* done.
|
||||
*/
|
||||
$validateFingerprint = NULL;
|
||||
|
||||
|
||||
/* This variable contains the files we will parse. */
|
||||
$files = array();
|
||||
|
||||
/* Parse arguments. */
|
||||
|
||||
$progName = array_shift($argv);
|
||||
|
||||
foreach($argv as $a) {
|
||||
if(strlen($a) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if($a[0] !== '-') {
|
||||
/* Not an option. Assume that it is a file we should parse. */
|
||||
$files[] = $a;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(strpos($a, '=') !== FALSE) {
|
||||
$p = strpos($a, '=');
|
||||
$v = substr($a, $p + 1);
|
||||
$a = substr($a, 0, $p);
|
||||
} else {
|
||||
$v = NULL;
|
||||
}
|
||||
|
||||
/* Map short options to long options. */
|
||||
$shortOptMap = array(
|
||||
'-h' => '--help',
|
||||
'-o' => '--out-dir',
|
||||
'-s' => '--stdout',
|
||||
);
|
||||
if(array_key_exists($a, $shortOptMap)) {
|
||||
$a = $shortOptMap[$a];
|
||||
}
|
||||
|
||||
switch($a) {
|
||||
case '--validate-fingerprint':
|
||||
if($v === NULL || strlen($v) === 0) {
|
||||
echo('The --validate-fingerprint option requires an parameter.' . "\n");
|
||||
echo('Please run `' . $progName . ' --help` for usage information.' . "\n");
|
||||
exit(1);
|
||||
}
|
||||
$validateFingerprint = $v;
|
||||
break;
|
||||
case '--help':
|
||||
printHelp();
|
||||
exit(0);
|
||||
case '--out-dir':
|
||||
if($v === NULL || strlen($v) === 0) {
|
||||
echo('The --out-dir option requires an parameter.' . "\n");
|
||||
echo('Please run `' . $progName . ' --help` for usage information.' . "\n");
|
||||
exit(1);
|
||||
}
|
||||
$outputDir = $baseDir . ($v[0] == '/' ? $v : '/' . $v);
|
||||
break;
|
||||
case '--stdout':
|
||||
$toStdOut = TRUE;
|
||||
break;
|
||||
default:
|
||||
echo('Unknown option: ' . $a . "\n");
|
||||
echo('Please run `' . $progName . ' --help` for usage information.' . "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(count($files) === 0) {
|
||||
echo($progName . ': Missing input files. Please run `' . $progName . ' --help` for usage information.' . "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* The metadata global variable will be filled with the metadata we extract. */
|
||||
$metaloader = new sspmod_metarefresh_MetaLoader();
|
||||
|
||||
foreach($files as $f) {
|
||||
$source = array('src' => $f);
|
||||
if (isset($validateFingerprint)) $source['validateFingerprint'] = $validateFingerprint;
|
||||
$metaloader->loadSource($source);
|
||||
}
|
||||
|
||||
if($toStdOut) {
|
||||
$metaloader->dumpMetadataStdOut();
|
||||
} else {
|
||||
$metaloader->writeMetadataFiles($outputDir);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
/**
|
||||
* This function prints the help output.
|
||||
*/
|
||||
function printHelp() {
|
||||
global $progName;
|
||||
|
||||
/* '======================================================================' */
|
||||
echo('Usage: ' . $progName . ' [options] [files]' . "\n");
|
||||
echo("\n");
|
||||
echo('This program parses a SAML metadata files and output pieces that can' . "\n");
|
||||
echo('be added to the metadata files in metadata/.' . "\n");
|
||||
echo("\n");
|
||||
echo('Options:' . "\n");
|
||||
echo(' --validate-fingerprint=<FINGERPRINT>' . "\n");
|
||||
echo(' Check the signature of the metadata,' . "\n");
|
||||
echo(' and check the fingerprint of the' . "\n");
|
||||
echo(' certificate against <FINGERPRINT>.' . "\n");
|
||||
echo(' -h, --help Print this help.' . "\n");
|
||||
echo(' -o=<DIR>, --out-dir=<DIR> Write the output to this directory. The' . "\n");
|
||||
echo(' default directory is metadata-generated/.' . "\n");
|
||||
echo(' Path will be relative to the simpleSAMLphp' . "\n");
|
||||
echo(' base directory.' . "\n");
|
||||
echo(' -s, --stdout Write the output to stdout instead of' . "\n");
|
||||
echo(' seperate files in the output directory.' . "\n");
|
||||
echo("\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
$config = array(
|
||||
|
||||
/*
|
||||
* Global blacklist: entityIDs that should be excluded from ALL sets.
|
||||
*/
|
||||
#'blacklist' = array(
|
||||
# 'http://my.own.uni/idp'
|
||||
#),
|
||||
|
||||
/*
|
||||
* Conditional GET requests
|
||||
* Efficient downloading so polling can be done more frequently.
|
||||
* Works for sources that send 'Last-Modified' or 'Etag' headers.
|
||||
* Note that the 'data' directory needs to be writable for this to work.
|
||||
*/
|
||||
#'conditionalGET' => TRUE,
|
||||
|
||||
'sets' => array(
|
||||
|
||||
'kalmar' => array(
|
||||
'cron' => array('hourly'),
|
||||
'sources' => array(
|
||||
array(
|
||||
/*
|
||||
* entityIDs that should be excluded from this src.
|
||||
*/
|
||||
#'blacklist' => array(
|
||||
# 'http://some.other.uni/idp',
|
||||
#),
|
||||
|
||||
/*
|
||||
* Whitelist: only keep these EntityIDs.
|
||||
*/
|
||||
#'whitelist' => array(
|
||||
# 'http://some.uni/idp',
|
||||
# 'http://some.other.uni/idp',
|
||||
#),
|
||||
|
||||
#'conditionalGET' => TRUE,
|
||||
'src' => 'https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral&set=saml2&exclude=norway',
|
||||
'validateFingerprint' => '59:1D:4B:46:70:46:3E:ED:A9:1F:CC:81:6D:C0:AF:2A:09:2A:A8:01',
|
||||
'template' => array(
|
||||
'tags' => array('kalmar'),
|
||||
'authproc' => array(
|
||||
51 => array('class' => 'core:AttributeMap', 'oid2name'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'expireAfter' => 60*60*24*4, // Maximum 4 days cache time.
|
||||
'outputDir' => 'metadata/metadata-kalmar-consuming/',
|
||||
|
||||
/*
|
||||
* Which output format the metadata should be saved as.
|
||||
* Can be 'flatfile' or 'serialize'. 'flatfile' is the default.
|
||||
*/
|
||||
'outputFormat' => 'flatfile',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
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.
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
/**
|
||||
* Hook to run a cron job.
|
||||
*
|
||||
* @param array &$croninfo Output
|
||||
*/
|
||||
function metarefresh_hook_cron(&$croninfo) {
|
||||
assert('is_array($croninfo)');
|
||||
assert('array_key_exists("summary", $croninfo)');
|
||||
assert('array_key_exists("tag", $croninfo)');
|
||||
|
||||
SimpleSAML_Logger::info('cron [metarefresh]: Running cron in cron tag [' . $croninfo['tag'] . '] ');
|
||||
|
||||
try {
|
||||
$config = SimpleSAML_Configuration::getInstance();
|
||||
$mconfig = SimpleSAML_Configuration::getOptionalConfig('config-metarefresh.php');
|
||||
|
||||
$sets = $mconfig->getConfigList('sets', array());
|
||||
$stateFile = $config->getPathValue('datadir', 'data/') . 'metarefresh-state.php';
|
||||
|
||||
foreach ($sets AS $setkey => $set) {
|
||||
// Only process sets where cron matches the current cron tag.
|
||||
$cronTags = $set->getArray('cron');
|
||||
if (!in_array($croninfo['tag'], $cronTags)) continue;
|
||||
|
||||
SimpleSAML_Logger::info('cron [metarefresh]: Executing set [' . $setkey . ']');
|
||||
|
||||
$expireAfter = $set->getInteger('expireAfter', NULL);
|
||||
if ($expireAfter !== NULL) {
|
||||
$expire = time() + $expireAfter;
|
||||
} else {
|
||||
$expire = NULL;
|
||||
}
|
||||
|
||||
$outputDir = $set->getString('outputDir');
|
||||
$outputDir = $config->resolvePath($outputDir);
|
||||
$outputFormat = $set->getValueValidate('outputFormat', array('flatfile', 'serialize'), 'flatfile');
|
||||
|
||||
$oldMetadataSrc = SimpleSAML_Metadata_MetaDataStorageSource::getSource(array(
|
||||
'type' => $outputFormat,
|
||||
'directory' => $outputDir,
|
||||
));
|
||||
|
||||
$metaloader = new sspmod_metarefresh_MetaLoader($expire, $stateFile, $oldMetadataSrc);
|
||||
|
||||
# Get global blacklist, whitelist and caching info
|
||||
$blacklist = $mconfig->getArray('blacklist', array());
|
||||
$whitelist = $mconfig->getArray('whitelist', array());
|
||||
$conditionalGET = $mconfig->getBoolean('conditionalGET', FALSE);
|
||||
|
||||
foreach($set->getArray('sources') AS $source) {
|
||||
|
||||
# Merge global and src specific blacklists
|
||||
if(isset($source['blacklist'])) {
|
||||
$source['blacklist'] = array_unique(array_merge($source['blacklist'], $blacklist));
|
||||
} else {
|
||||
$source['blacklist'] = $blacklist;
|
||||
}
|
||||
|
||||
# Merge global and src specific whitelists
|
||||
if(isset($source['whitelist'])) {
|
||||
$source['whitelist'] = array_unique(array_merge($source['whitelist'], $whitelist));
|
||||
} else {
|
||||
$source['whitelist'] = $whitelist;
|
||||
}
|
||||
|
||||
# Let src specific conditionalGET override global one
|
||||
if(!isset($source['conditionalGET'])) {
|
||||
$source['conditionalGET'] = $conditionalGET;
|
||||
}
|
||||
|
||||
SimpleSAML_Logger::debug('cron [metarefresh]: In set [' . $setkey . '] loading source [' . $source['src'] . ']');
|
||||
$metaloader->loadSource($source);
|
||||
}
|
||||
|
||||
// Write state information back to disk
|
||||
$metaloader->writeState();
|
||||
|
||||
switch ($outputFormat) {
|
||||
case 'flatfile':
|
||||
$metaloader->writeMetadataFiles($outputDir);
|
||||
break;
|
||||
case 'serialize':
|
||||
$metaloader->writeMetadataSerialize($outputDir);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($set->hasValue('arp')) {
|
||||
$arpconfig = SimpleSAML_Configuration::loadFromArray($set->getValue('arp'));
|
||||
$metaloader->writeARPfile($arpconfig);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$croninfo['summary'][] = 'Error during metarefresh: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
/**
|
||||
* Hook to add links to the frontpage.
|
||||
*
|
||||
* @param array &$links The links on the frontpage, split into sections.
|
||||
*/
|
||||
function metarefresh_hook_frontpage(&$links) {
|
||||
assert('is_array($links)');
|
||||
assert('array_key_exists("links", $links)');
|
||||
|
||||
$links['federation'][] = array(
|
||||
'href' => SimpleSAML_Module::getModuleURL('metarefresh/fetch.php'),
|
||||
'text' => array('en' => 'Metarefresh: fetch metadata'),
|
||||
);
|
||||
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
/*
|
||||
* @author Andreas Åkre Solberg <andreas.solberg@uninett.no>
|
||||
* @package simpleSAMLphp
|
||||
* @version $Id$
|
||||
*/
|
||||
class sspmod_metarefresh_ARP {
|
||||
|
||||
|
||||
private $metadata;
|
||||
private $attributes;
|
||||
private $prefix;
|
||||
private $suffix;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
public function __construct($metadata, $attributemap, $prefix, $suffix) {
|
||||
$this->metadata = $metadata;
|
||||
|
||||
$this->prefix = $prefix;
|
||||
$this->suffix = $suffix;
|
||||
|
||||
if (isset($attributemap)) $this->loadAttributeMap($attributemap);
|
||||
}
|
||||
|
||||
private function loadAttributeMap($attributemap) {
|
||||
$config = SimpleSAML_Configuration::getInstance();
|
||||
include($config->getPathValue('attributemap', 'attributemap/') . $attributemap . '.php');
|
||||
$this->attributes = $attributemap;
|
||||
|
||||
# print_r($attributemap); exit;
|
||||
}
|
||||
|
||||
private function surround($name) {
|
||||
$ret = '';
|
||||
if (!empty($this->prefix)) $ret .= $this->prefix;
|
||||
$ret .= $name;
|
||||
if (!empty($this->suffix)) $ret .= $this->suffix;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
private function getAttributeID($name) {
|
||||
if (empty($this->attributes)) {
|
||||
return $this->surround($name);
|
||||
}
|
||||
if (array_key_exists($name, $this->attributes)) {
|
||||
return $this->surround($this->attributes[$name]);
|
||||
}
|
||||
return $this->surround($name);
|
||||
}
|
||||
|
||||
public function getXML() {
|
||||
|
||||
$xml = '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<AttributeFilterPolicyGroup id="urn:mace:funet.fi:haka:kalmar" xmlns="urn:mace:shibboleth:2.0:afp"
|
||||
xmlns:basic="urn:mace:shibboleth:2.0:afp:mf:basic" xmlns:saml="urn:mace:shibboleth:2.0:afp:mf:saml"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:mace:shibboleth:2.0:afp classpath:/schema/shibboleth-2.0-afp.xsd
|
||||
urn:mace:shibboleth:2.0:afp:mf:basic classpath:/schema/shibboleth-2.0-afp-mf-basic.xsd
|
||||
urn:mace:shibboleth:2.0:afp:mf:saml classpath:/schema/shibboleth-2.0-afp-mf-saml.xsd">
|
||||
';
|
||||
|
||||
|
||||
foreach($this->metadata AS $metadata) {
|
||||
#$print_r($metadata); exit;
|
||||
$xml .= $this->getEntryXML($metadata['metadata']);
|
||||
}
|
||||
|
||||
$xml .= '</AttributeFilterPolicyGroup>';
|
||||
return $xml;
|
||||
}
|
||||
|
||||
private function getEntryXML($entry) {
|
||||
$entityid = $entry['entityid'];
|
||||
return ' <AttributeFilterPolicy id="' . $entityid . '">
|
||||
<PolicyRequirementRule xsi:type="basic:AttributeRequesterString" value="' . $entityid . '" />
|
||||
' . $this->getEntryXMLcontent($entry) . '
|
||||
</AttributeFilterPolicy>
|
||||
';
|
||||
}
|
||||
|
||||
private function getEntryXMLcontent($entry) {
|
||||
$ids = array();
|
||||
if (!array_key_exists('attributes', $entry))
|
||||
return '';
|
||||
|
||||
$ret = '';
|
||||
foreach($entry['attributes'] AS $a) {
|
||||
|
||||
$ret .= ' <AttributeRule attributeID="' . $this->getAttributeID($a) . '">
|
||||
<PermitValueRule xsi:type="basic:ANY" />
|
||||
</AttributeRule>
|
||||
';
|
||||
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,447 @@
|
|||
<?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",
|
||||
0644
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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, 0644);
|
||||
} 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
$this->data['header'] = $this->t('{aggregator:aggregator:aggregator_header}');
|
||||
$this->includeAtTemplateBase('includes/header.php');
|
||||
|
||||
echo('<h1>Metarefresh fetch</h1>');
|
||||
|
||||
|
||||
if (!empty($this->data['logentries'])) {
|
||||
|
||||
echo '<pre style="border: 1px solid #aaa; padding: .5em; overflow: scroll">';
|
||||
foreach($this->data['logentries'] AS $l) {
|
||||
echo $l . "\n";
|
||||
}
|
||||
echo '</pre>';
|
||||
|
||||
} else {
|
||||
echo 'No output from metarefresh.';
|
||||
}
|
||||
|
||||
|
||||
|
||||
$this->includeAtTemplateBase('includes/footer.php');
|
||||
?>
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
$config = SimpleSAML_Configuration::getInstance();
|
||||
$mconfig = SimpleSAML_Configuration::getOptionalConfig('config-metarefresh.php');
|
||||
|
||||
SimpleSAML_Utilities::requireAdmin();
|
||||
|
||||
SimpleSAML_Logger::setCaptureLog(TRUE);
|
||||
|
||||
|
||||
$sets = $mconfig->getConfigList('sets', array());
|
||||
|
||||
foreach ($sets AS $setkey => $set) {
|
||||
|
||||
SimpleSAML_Logger::info('[metarefresh]: Executing set [' . $setkey . ']');
|
||||
|
||||
try {
|
||||
|
||||
|
||||
$expireAfter = $set->getInteger('expireAfter', NULL);
|
||||
if ($expireAfter !== NULL) {
|
||||
$expire = time() + $expireAfter;
|
||||
} else {
|
||||
$expire = NULL;
|
||||
}
|
||||
|
||||
$metaloader = new sspmod_metarefresh_MetaLoader($expire);
|
||||
|
||||
# Get global black/whitelists
|
||||
$blacklist = $mconfig->getArray('blacklist', array());
|
||||
$whitelist = $mconfig->getArray('whitelist', array());
|
||||
|
||||
foreach($set->getArray('sources') AS $source) {
|
||||
|
||||
# Merge global and src specific blacklists
|
||||
if(isset($source['blacklist'])) {
|
||||
$source['blacklist'] = array_unique(array_merge($source['blacklist'], $blacklist));
|
||||
} else {
|
||||
$source['blacklist'] = $blacklist;
|
||||
}
|
||||
|
||||
# Merge global and src specific whitelists
|
||||
if(isset($source['whitelist'])) {
|
||||
$source['whitelist'] = array_unique(array_merge($source['whitelist'], $whitelist));
|
||||
} else {
|
||||
$source['whitelist'] = $whitelist;
|
||||
}
|
||||
|
||||
SimpleSAML_Logger::debug('[metarefresh]: In set [' . $setkey . '] loading source [' . $source['src'] . ']');
|
||||
$metaloader->loadSource($source);
|
||||
}
|
||||
|
||||
$outputDir = $set->getString('outputDir');
|
||||
$outputDir = $config->resolvePath($outputDir);
|
||||
|
||||
$outputFormat = $set->getValueValidate('outputFormat', array('flatfile', 'serialize'), 'flatfile');
|
||||
switch ($outputFormat) {
|
||||
case 'flatfile':
|
||||
$metaloader->writeMetadataFiles($outputDir);
|
||||
break;
|
||||
case 'serialize':
|
||||
$metaloader->writeMetadataSerialize($outputDir);
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$e = SimpleSAML_Error_Exception::fromException($e);
|
||||
$e->logWarning();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
$logentries = SimpleSAML_Logger::getCapturedLog();
|
||||
|
||||
$t = new SimpleSAML_XHTML_Template($config, 'metarefresh:fetch.tpl.php');
|
||||
$t->data['logentries'] = $logentries;
|
||||
$t->show();
|
Reference in New Issue