diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSAML2Meta.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSAML2Meta.php index c62c5e9a0fa9893a9a71af050fce34d86c4ec98f..904a71d4ee445e7b7b5a5edae0d3cc17c2ef13c4 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSAML2Meta.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerSAML2Meta.php @@ -11,6 +11,7 @@ require_once('SimpleSAML/Configuration.php'); require_once('SimpleSAML/Utilities.php'); +require_once('SimpleSAML/XML/Parser.php'); require_once('SimpleSAML/Metadata/MetaDataStorageHandler.php'); /** @@ -19,6 +20,7 @@ require_once('SimpleSAML/Metadata/MetaDataStorageHandler.php'); class SimpleSAML_Metadata_MetaDataStorageHandlerSAML2Meta extends SimpleSAML_Metadata_MetaDataStorageHandler { + static var $cachedfiles = array(); /* This constructor is included in case it is needed in the the * future. Including it now allows us to write parent::__construct() in @@ -32,65 +34,50 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSAML2Meta extends SimpleSAML_Met $metadata = null; if (!in_array($set, array( 'saml20-sp-hosted', 'saml20-sp-remote','saml20-idp-hosted', 'saml20-idp-remote', - 'shib13-sp-hosted', 'shib13-sp-remote', 'shib13-idp-hosted', 'shib13-idp-remote', - 'openid-provider'))) { + 'shib13-sp-hosted', 'shib13-sp-remote', 'shib13-idp-hosted', 'shib13-idp-remote' ))) { throw new Exception('Trying to load illegal set of Meta data [' . $set . ']'); } + $settofile = array( + 'saml20-sp-hosted' => 'saml20-hosted', + 'saml20-idp-hosted' => 'saml20-hosted', + 'saml20-sp-remote' => 'saml20-remote', + 'saml20-idp-remote' => 'saml20-remote', + 'shib13-sp-hosted' => 'shib13-hosted', + 'shib13-idp-hosted' => 'shib13-hosted', + 'shib13-sp-remote' => 'shib13-remote', + 'shib13-idp-remote' => 'shib13-remote' + ); + /* Get the configuration. */ $config = SimpleSAML_Configuration::getInstance(); assert($config instanceof SimpleSAML_Configuration); - - $metadatasetfile = $config->getBaseDir() . '/' . - $config->getValue('metadatadir') . '/xml/' . $set . '.xml'; - - - if (!file_exists($metadatasetfile)) throw new Exception('Could not find SAML 2.0 Metadata file :'. $metadatasetfile); - - #$metadata = file_get_contents($metadatasetfile); - - // for now testing with the shib aai metadata... - $metadata = file_get_contents("http://www.switch.ch/aai/federation/SWITCHaai/metadata.switchaai_signed.xml"); - echo '<pre>'; - - $simplexml_metadata = new SimpleXMLElement($metadata); - $simplexml_metadata->registerXPathNamespace('saml2meta', 'urn:oasis:names:tc:SAML:2.0:metadata'); - - $idpentities = $simplexml_metadata->xpath('/saml2meta:EntitiesDescriptor/saml2meta:EntityDescriptor[./saml2meta:IDPSSODescriptor]'); - - if (!$idpentities) throw new Exception('Could not find any entity descriptors in the meta data file: ' . $metadatasetfile); - foreach ($idpentities as $idpentity) { - echo 'Entity: ' . $idpentity['entityID'][0] . "\n"; - - $newmeta = array('entityid' => (string) $idpentity['entityID']); - - #$idpentity['xmlns'] = 'urn:oasis:names:tc:SAML:2.0:metadata'; - - $namespaces = $idpentity->getNamespaces(); - - foreach ($namespaces AS $prefix => $ns) { - $newmeta[($prefix === '') ? 'xmlns' : 'xmlns:' . $prefix)] = $ns; - } - - $simplexml_metadata_entry = new SimpleXMLElement($idpentity->asXML()); - $simplexml_metadata_entry->registerXPathNamespace('saml2meta', 'urn:oasis:names:tc:SAML:2.0:metadata'); - - - $entry = $simplexml_metadata_entry->xpath("/saml2meta:EntityDescriptor/saml2meta:IDPSSODescriptor/saml2meta:SingleSignOnService[@Binding='urn:mace:shibboleth:1.0:profiles:AuthnRequest']/@Location"); - - $newmeta['SingleSignOnService'] = (string)$entry[0]['Location']; - - echo 'Entry: '; - print_r($newmeta); - } + $metadatasetfile = $config->getBaseDir() . '' . + $config->getValue('metadatadir') . 'xml/' . $settofile[$set] . '.xml'; - //echo htmlentities($metadata); - echo '</pre>'; - exit(); + if (array_key_exists($metadatasetfile, $cachedfiles)) { + $metadata = self::$cachedfiles[$metadatasetfile]; + } else { + if (!file_exists($metadatasetfile)) throw new Exception('Could not find SAML 2.0 Metadata file :'. $metadatasetfile); + // for now testing with the shib aai metadata... + //$metadataxml = file_get_contents("http://www.switch.ch/aai/federation/SWITCHaai/metadata.switchaai_signed.xml"); + $metadata = file_get_contents($metadatasetfile); + } + $metadata = null; + switch ($set) { + case 'saml20-idp-remote' : $metadata = $this->getmetadata_saml20idpremote($metadataxml); break; + case 'saml20-idp-hosted' : throw new Exception('Meta data parsing for SAML 2.0 IdP Hosted not yet implemented.'); + case 'saml20-sp-remote' : throw new Exception('Meta data parsing for SAML 2.0 SP Remote not yet implemented.'); + case 'saml20-sp-hosted' : throw new Exception('Meta data parsing for SAML 2.0 SP Hosted not yet implemented.'); + case 'shib13-idp-remote' : $metadata = getmetadata_shib13idpremote($metadataxml); break; + case 'shib13-idp-hosted' : throw new Exception('Meta data parsing for Shib 1.3 IdP Hosted not yet implemented.'); + case 'shib13-sp-remote' : throw new Exception('Meta data parsing for Shib 1.3 SP Remote not yet implemented.'); + case 'shib13-sp-hosted' : throw new Exception('Meta data parsing for Shib 1.3 SP Hosted not yet implemented.'); + } if (!is_array($metadata)) { @@ -109,6 +96,66 @@ class SimpleSAML_Metadata_MetaDataStorageHandlerSAML2Meta extends SimpleSAML_Met } + private function getmetadata_saml20idpremote($metadataxml) { + // Create a parser for the metadata document. + $metadata_parser = new SimpleSAML_XML_Parser($metadataxml); + + // Get all entries in the metadata. + $idpentities = $metadata_parser->simplexml->xpath('/saml2meta:EntitiesDescriptor/saml2meta:EntityDescriptor[./saml2meta:IDPSSODescriptor]'); + if (!$idpentities) throw new Exception('Could not find any entity descriptors in the meta data file: ' . $metadatasetfile); + + // Array to hold the resulting metadata, to return at the end of this function. + $metadata = array(); + + // Traverse all entries. + foreach ($idpentities as $idpentity) { + try { + $entityid = (string) $idpentity['entityID']; + if (!$entityid) throw new Exception('Could not find entityID in element'); + + $metadata[$entityid] = array('entityid' => $entityid); + $metadata_entry = SimpleSAML_XML_Parser::fromSimpleXMLElement($idpentity); + + $metadata[$entityid]['SingleSignOnService'] = $metadata_entry->getValue("/saml2meta:EntityDescriptor/saml2meta:IDPSSODescriptor/saml2meta:SingleSignOnService[@Binding='urn:mace:shibboleth:1.0:profiles:AuthnRequest']/@Location", true); + + } catch (Exception $e) { + echo 'Error reading one metadata entry: ' . $e; + } + + } + return $metadata; + } + + private function getmetadata_shib13idpremote($metadataxml) { + // Create a parser for the metadata document. + $metadata_parser = new SimpleSAML_XML_Parser($metadataxml); + + // Get all entries in the metadata. + $idpentities = $metadata_parser->simplexml->xpath('/saml2meta:EntitiesDescriptor/saml2meta:EntityDescriptor[./saml2meta:IDPSSODescriptor]'); + if (!$idpentities) throw new Exception('Could not find any entity descriptors in the meta data file: ' . $metadatasetfile); + + // Array to hold the resulting metadata, to return at the end of this function. + $metadata = array(); + + // Traverse all entries. + foreach ($idpentities as $idpentity) { + try { + $entityid = (string) $idpentity['entityID']; + if (!$entityid) throw new Exception('Could not find entityID in element'); + + $metadata[$entityid] = array('entityid' => $entityid); + $metadata_entry = SimpleSAML_XML_Parser::fromSimpleXMLElement($idpentity); + + $metadata[$entityid]['SingleSignOnService'] = $metadata_entry->getValue("/saml2meta:EntityDescriptor/saml2meta:IDPSSODescriptor/saml2meta:SingleSignOnService[@Binding='urn:mace:shibboleth:1.0:profiles:AuthnRequest']/@Location", true); + + } catch (Exception $e) { + echo 'Error reading one metadata entry: ' . $e; + } + + } + return $metadata; + } + public function getMetaData($entityid = null, $set = 'saml20-sp-hosted') { if (!isset($entityid)) { return $this->getMetaDataCurrent($set); diff --git a/lib/SimpleSAML/XML/Parser.php b/lib/SimpleSAML/XML/Parser.php new file mode 100644 index 0000000000000000000000000000000000000000..9bb82e834fd23293a851fa1932989b495023a42f --- /dev/null +++ b/lib/SimpleSAML/XML/Parser.php @@ -0,0 +1,55 @@ +<?php + +/* + * This file is part of simpleSAMLphp. See the file COPYING in the + * root of the distribution for licence information. + * + * This file will help doing XPath queries in SAML 2 XML documents. + */ + + +/** + * Configuration of SimpleSAMLphp + * + * This class should be extending SimpleXMLElement, but it is not because of some bugs: + * http://bugs.php.net/bug.php?id=32188&edit=1 + */ +class SimpleSAML_XML_Parser { + + var $simplexml = null; + + function __construct($xml) { + #parent::construct($xml); + $this->simplexml = new SimpleXMLElement($xml); + $this->simplexml->registerXPathNamespace('saml2meta', 'urn:oasis:names:tc:SAML:2.0:metadata'); + } + + public static function fromSimpleXMLElement(SimpleXMLElement $element) { + + // Traverse all existing namespaces in element. + $namespaces = $element->getNamespaces(); + foreach ($namespaces AS $prefix => $ns) { + $element[(($prefix === '') ? 'xmlns' : 'xmlns:' . $prefix)] = $ns; + } + + /* Create a new parser with the xml document where the namespace definitions + * are added. + */ + $parser = new SimpleSAML_XML_Parser($element->asXML()); + return $parser; + + } + + public function getValue($xpath, $required = false) { + + $result = $this->simplexml->xpath($xpath); + if (! $result or !is_array($result)) { + if ($required) throw new Exception('Could not get value from XML document using the following XPath expression: ' . $xpath); + else return null; + } + return (string) $result[0]; + } + +} + +?> \ No newline at end of file diff --git a/templates/default/en/admin-metadatalist.php b/templates/default/en/admin-metadatalist.php new file mode 100644 index 0000000000000000000000000000000000000000..22fd84de30cbf13a4ba147f8793ea9c575177a07 --- /dev/null +++ b/templates/default/en/admin-metadatalist.php @@ -0,0 +1,91 @@ +<?php $this->includeAtTemplateBase('includes/header.php'); ?> + + <div id="header"> + <h1>Metadata overview</h1> + <div id="poweredby"><img src="/<?php echo $data['baseurlpath']; ?>resources/icons/bino.png" alt="Bino" /></div> + </div> + + <div id="content"> + + <h2><?php if (isset($data['header'])) { echo $data['header']; } else { echo "Metadata overview"; } ?></h2> + + <p>Here is a list of metadata that is configured for your installation.</p> + + <p>[ <a href="../">Go back to installation main page</a> ]</p> + + <?php + + + function showEntry($header, $list) { + + echo '<h3>' . $header . '</h3>'; + + foreach ($list AS $entityid => $entity) { + $name = $entityid; + if (isset($entity['optional.found']['name'])) $name = $entity['optional.found']['name']; + + //print_r($entity); + + echo '<h4>' . $name . '</h4>'; + if (isset($entity['optional.found']['description'])) { + echo '<p>' . $entity['optional.found']['description'] . '</p>'; + } + + echo '<p>Required fields</p>'; + echo '<table style="width: 100%; border: 1px solid #eee"><tr><th>Key</th><th>Value</th></tr>'; + foreach ($entity['required.found'] AS $key => $value) { + echo '<tr><td>' . $key . '</td><td>' . $value . '</td></tr>'; + } + echo '</table>'; + + if (count($entity['required.notfound']) > 0) { + echo '<p>The following required fields was not found:<ul>'; + foreach ($entity['required.notfound'] AS $key) { + echo '<li>' . $key . '</li>'; + } + echo '</ul>'; + } + + if (count($entity['optional.found']) > 0) { + echo '<p>Optional fields</p>'; + echo '<table><tr><th>Key</th><th>Value</th></tr>'; + foreach ($entity['optional.found'] AS $key => $value) { + echo '<tr><td>' . $key . '</td><td>' . $value . '</td></tr>'; + } + echo '</table>'; + } + + if (count($entity['optional.notfound']) > 0) { + echo '<p>The following optional fields was not found:<ul>'; + foreach ($entity['optional.notfound'] AS $key) { + echo '<li>' . $key . '</li>'; + } + echo '</ul>'; + } + + if (count($entity['leftovers']) > 0) { + echo '<p>The following fields was not reckognized:<ul>'; + foreach ($entity['leftovers'] AS $key => $value) { + echo '<li>' . $key . '</li>'; + } + echo '</ul>'; + } + + } + } + + + if (array_key_exists('metadata.saml20-sp-hosted', $data)) + showEntry('SAML 2.0 Service Provider (Hosted)', $data['metadata.saml20-sp-hosted']); + if (array_key_exists('metadata.saml20-sp-remote', $data)) + showEntry('SAML 2.0 Service Provider (Remote)', $data['metadata.saml20-sp-remote']); + if (array_key_exists('metadata.saml20-idp-hosted', $data)) + showEntry('SAML 2.0 Identity Provider (Hosted)', $data['metadata.saml20-idp-hosted']); + if (array_key_exists('metadata.saml20-idp-remote', $data)) + showEntry('SAML 2.0 Identity Provider (Remote)', $data['metadata.saml20-idp-remote']); + + + ?> + + +<?php $this->includeAtTemplateBase('includes/footer.php'); ?> diff --git a/www/admin/metadata.php b/www/admin/metadata.php index 2ce1d3e6c4cbae44fed6bb331a0ab59e73809d97..2c354f6d260d9ee037e41eaccb2ff2857a9d2935 100644 --- a/www/admin/metadata.php +++ b/www/admin/metadata.php @@ -25,6 +25,7 @@ try { if ($config->getValue('enable.saml20-sp') === true) { $results = array(); + /* $metalist = $metadata->getList('saml20-sp-hosted'); foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry, @@ -33,7 +34,7 @@ try { ); } $et->data['metadata.saml20-sp-hosted'] = $results; - + */ $metalist = $metadata->getList('saml20-idp-remote'); foreach ($metalist AS $entityid => $mentry) { $results[$entityid] = SimpleSAML_Utilities::checkAssocArrayRules($mentry,