diff --git a/modules/metaedit/config-template/module_metaedit.php b/modules/metaedit/config-template/module_metaedit.php new file mode 100644 index 0000000000000000000000000000000000000000..f482280e65f07b663449046a7e63eeb7707093c1 --- /dev/null +++ b/modules/metaedit/config-template/module_metaedit.php @@ -0,0 +1,12 @@ +<?php +/* + * The configuration of simpleSAMLphp statistics package + */ + +$config = array ( + 'admins' => array('andreas@rnd.feide.no'), + 'metahandlerConfig' => array('directory' => 'metadata/metaedit'), + 'auth' => 'saml2', + 'useridattr' => 'eduPersonPrincipalName', +); + diff --git a/modules/metaedit/default-disable b/modules/metaedit/default-disable new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/modules/metaedit/hooks/hook_frontpage.php b/modules/metaedit/hooks/hook_frontpage.php new file mode 100644 index 0000000000000000000000000000000000000000..21ca8be7ce22347c87f099c8c6a647f63770875e --- /dev/null +++ b/modules/metaedit/hooks/hook_frontpage.php @@ -0,0 +1,18 @@ +<?php +/** + * Hook to add the modinfo module to the frontpage. + * + * @param array &$links The links on the frontpage, split into sections. + */ +function metaedit_hook_frontpage(&$links) { + assert('is_array($links)'); + assert('array_key_exists("links", $links)'); + + $links['links']['metaedit'] = array( + 'href' => SimpleSAML_Module::getModuleURL('metaedit/index.php'), + 'text' => array('en' => 'Metadata registry', 'no' => 'Metadata registrering'), + 'shorttext' => array('en' => 'Metadata registry', 'no' => 'Metadata registrering'), + ); + +} +?> \ No newline at end of file diff --git a/modules/metaedit/lib/MetaEditor.php b/modules/metaedit/lib/MetaEditor.php new file mode 100644 index 0000000000000000000000000000000000000000..ac83b235d6bb7d0ec940f39654b742e876717d5d --- /dev/null +++ b/modules/metaedit/lib/MetaEditor.php @@ -0,0 +1,148 @@ +<?php + +/** + * Editor for metadata + * + * @author Andreas Ă…kre Solberg <andreas@uninett.no>, UNINETT AS. + * @package simpleSAMLphp + * @version $Id$ + */ +class sspmod_metaedit_MetaEditor { + + + protected function getStandardField($request, &$metadata, $key) { + if (array_key_exists('field_' . $key, $request)) { + $metadata[$key] = $request['field_' . $key]; + } else { + if (isset($metadata[$key])) unset($metadata[$key]); + } + } + + public function formToMeta($request, $metadata = array(), $override = NULL) { + $this->getStandardField($request, $metadata, 'entityid'); + $this->getStandardField($request, $metadata, 'name'); + $this->getStandardField($request, $metadata, 'description'); + $this->getStandardField($request, $metadata, 'AssertionConsumerService'); + $this->getStandardField($request, $metadata, 'SingleLogoutService'); + // $this->getStandardField($request, $metadata, 'certFingerprint'); + $metadata['updated'] = time(); + + if ($override) { + foreach($override AS $key => $value) { + $metadata[$key] = $value; + } + } + + return $metadata; + } + + protected function requireStandardField($request, $key) { + if (!array_key_exists('field_' . $key, $request)) + throw new Exception('Required field [' . $key . '] was missing.'); + if (empty($request['field_' . $key])) + throw new Exception('Required field [' . $key . '] was empty.'); + } + + public function checkForm($request) { + $this->requireStandardField($request, 'entityid'); + $this->requireStandardField($request, 'name'); + } + + + protected function header($name) { + return '<tr ><td> </td><td class="header">' . $name . '</td></tr>'; + + } + + protected function readonlyDateField($metadata, $key, $name) { + $value = '<span style="color: #aaa">Not set</a>'; + if (array_key_exists($key, $metadata)) + $value = date('j. F Y, G:i', $metadata[$key]); + return '<tr> + <td class="name">' . $name . '</td> + <td class="data">' . $value . '</td></tr>'; + + } + + protected function readonlyField($metadata, $key, $name) { + $value = ''; + if (array_key_exists($key, $metadata)) + $value = $metadata[$key]; + return '<tr> + <td class="name">' . $name . '</td> + <td class="data">' . htmlspecialchars($value) . '</td></tr>'; + + } + + protected function hiddenField($key, $value) { + return '<input type="hidden" name="' . $key . '" value="' . htmlspecialchars($value) . '" />'; + } + + protected function flattenLanguageField(&$metadata, $key) { + if (array_key_exists($key, $metadata)) { + if (is_array($metadata[$key])) { + if (isset($metadata[$key]['en'])) { + $metadata[$key] = $metadata[$key]['en']; + } else { + unset($metadata[$key]); + } + } + } + } + + protected function standardField($metadata, $key, $name, $textarea = FALSE) { + $value = ''; + if (array_key_exists($key, $metadata)) { + $value = htmlspecialchars($metadata[$key]); + } + + if ($textarea) { + return '<tr><td class="name">' . $name . '</td><td class="data"> + <textarea name="field_' . $key . '" rows="5" cols="50">' . $value . '</textarea></td></tr>'; + + } else { + return '<tr><td class="name">' . $name . '</td><td class="data"> + <input type="text" size="60" name="field_' . $key . '" value="' . $value . '" /></td></tr>'; + + } + } + + public function metaToForm($metadata) { + $this->flattenLanguageField($metadata, 'name'); + $this->flattenLanguageField($metadata, 'description'); + return '<form action="edit.php" method="post">' . + + (array_key_exists('entityid', $metadata) ? + $this->hiddenField('was-entityid', $metadata['entityid']) : + '') . + + '<div id="tabdiv">' . + '<ul>' . + '<li><a href="#basic">Name and descrition</a></li>' . + '<li><a href="#saml">SAML 2.0</a></li>' . + // '<li><a href="#attributes">Attributes</a></li>' . + // '<li><a href="#orgs">Organizations</a></li>' . + // '<li><a href="#contacts">Contacts</a></li>' . + '</ul>' . + '<div id="basic"><table class="formtable">' . + $this->standardField($metadata, 'entityid', 'EntityID') . + $this->standardField($metadata, 'name', 'Name of service') . + $this->standardField($metadata, 'description', 'Description of service', TRUE) . + $this->readonlyField($metadata, 'owner', 'Owner') . + $this->readonlyDateField($metadata, 'updated', 'Last updated') . + $this->readonlyDateField($metadata, 'expire', 'Expire') . + + '</table></div><div id="saml"><table class="formtable">' . + $this->standardField($metadata, 'AssertionConsumerService', 'AssertionConsumerService endpoint') . + $this->standardField($metadata, 'SingleLogoutService', 'SingleLogoutService endpoint') . + // $this->standardField($metadata, 'certFingerprint', 'Certificate Fingerprint') . + + '</table></div>' . + '</div>' . + '<input type="submit" name="submit" value="Save" style="margin-top: 5px" />' . + '</form>'; + } + +} + + diff --git a/modules/metaedit/templates/formedit.php b/modules/metaedit/templates/formedit.php new file mode 100644 index 0000000000000000000000000000000000000000..a90a92451eacaa33add238ecb4b8c5a1519c7bd7 --- /dev/null +++ b/modules/metaedit/templates/formedit.php @@ -0,0 +1,21 @@ +<?php + +$this->data['jquery'] = array('version' => '1.6', 'core' => TRUE, 'ui' => TRUE, 'css' => TRUE); +$this->data['head'] = '<link rel="stylesheet" type="text/css" href="/' . $this->data['baseurlpath'] . 'module.php/metaedit/resources/style.css" />' . "\n"; +$this->data['head'] .= '<script type="text/javascript"> +$(document).ready(function() { + $("#tabdiv").tabs(); +}); +</script>'; + +$this->includeAtTemplateBase('includes/header.php'); + + +echo('<h1>Metadata Editor</h1>'); + +echo($this->data['form']); + +echo('<p style="float: right"><a href="index.php">Return to entity listing <strong>without saving...</strong></a></p>'); + +$this->includeAtTemplateBase('includes/footer.php'); + diff --git a/modules/metaedit/templates/metalist.php b/modules/metaedit/templates/metalist.php new file mode 100644 index 0000000000000000000000000000000000000000..af75ad9f8bb0c642cd3b91bcb9a74bfaac2194ac --- /dev/null +++ b/modules/metaedit/templates/metalist.php @@ -0,0 +1,55 @@ +<?php + +$this->data['jquery'] = array('version' => '1.6', 'core' => TRUE, 'ui' => TRUE, 'css' => TRUE); +$this->data['head'] = '<link rel="stylesheet" type="text/css" href="/' . $this->data['baseurlpath'] . 'module.php/metaedit/resources/style.css" />' . "\n"; +// $this->data['head'] .= '<script type="text/javascript"> +// $(document).ready(function() { +// $("#tabdiv").tabs(); +// }); +// </script>'; + +$this->includeAtTemplateBase('includes/header.php'); + + +echo('<h1>Metadata Registry</h1>'); + +echo('<p>Here you can register new SAML entities. You are successfully logged in as ' . $this->data['userid'] . '</p>'); + +echo('<h2>Your entries</h2>'); +echo('<table class="metalist" style="width: 100%">'); +$i = 0; $rows = array('odd', 'even'); +foreach($this->data['metadata']['mine'] AS $md ) { + $i++; + echo('<tr class="' . $rows[$i % 2] . '"> + <td>' . $md['name'] . '</td> + <td><tt>' . $md['entityid'] . '</tt></td> + <td> + <a href="edit.php?entityid=' . urlencode($md['entityid']) . '">edit</a> + <a href="index.php?delete=' . urlencode($md['entityid']) . '">delete</a> + </td></tr>'); +} +if ($i == 0) { + echo('<tr><td colspan="3">No entries registered</td></tr>'); +} +echo('</table>'); + +echo('<p><a href="edit.php">Add new entity</a> | <a href="xmlimport.php">Add from SAML 2.0 XML metadata</a></p>'); + +echo('<h2>Other entries</h2>'); +echo('<table class="metalist" style="width: 100%">'); +$i = 0; $rows = array('odd', 'even'); +foreach($this->data['metadata']['others'] AS $md ) { + $i++; + echo('<tr class="' . $rows[$i % 2] . '"> + <td>' . $md['name'] . '</td> + <td><tt>' . $md['entityid'] . '</tt></td> + <td>' . (isset($md['owner']) ? $md['owner'] : 'No owner') . ' + </td></tr>'); +} +if ($i == 0) { + echo('<tr><td colspan="3">No entries registered</td></tr>'); +} +echo('</table>'); + +$this->includeAtTemplateBase('includes/footer.php'); + diff --git a/modules/metaedit/templates/saved.php b/modules/metaedit/templates/saved.php new file mode 100644 index 0000000000000000000000000000000000000000..f9b10ba8106acd85113094c3a508d58fdb6c87b0 --- /dev/null +++ b/modules/metaedit/templates/saved.php @@ -0,0 +1,15 @@ +<?php + + + +$this->includeAtTemplateBase('includes/header.php'); + + +echo('<h1>Metadata successfully saved</h1>'); + +echo('<p><a href="index.php">Go back to metadata registry listing</a></p>'); + + + +$this->includeAtTemplateBase('includes/footer.php'); + diff --git a/modules/metaedit/templates/xmlimport.tpl.php b/modules/metaedit/templates/xmlimport.tpl.php new file mode 100644 index 0000000000000000000000000000000000000000..6ade0113be43a58cb81d1d519c5b1716729dc550 --- /dev/null +++ b/modules/metaedit/templates/xmlimport.tpl.php @@ -0,0 +1,26 @@ +<?php + +// $this->data['jquery'] = array('version' => '1.6', 'core' => TRUE, 'ui' => TRUE, 'css' => TRUE); +// $this->data['head'] = '<link rel="stylesheet" type="text/css" href="/' . $this->data['baseurlpath'] . 'module.php/metaedit/resources/style.css" />' . "\n"; +// $this->data['head'] .= '<script type="text/javascript"> +// $(document).ready(function() { +// $("#tabdiv").tabs(); +// }); +// </script>'; + +$this->includeAtTemplateBase('includes/header.php'); + + +echo('<h1>Import SAML 2.0 XML Metadata</h1>'); + +echo('<form method="get" action="edit.php">'); +echo('<p>Paste in SAML 2.0 XML Metadata for the entity that you would like to add.</p>'); +echo('<textarea style="height: 200px; width: 90%; border: 1px solid #aaa;" cols="50" rows="5" name="xmlmetadata"></textarea>'); +echo('<input type="submit" style="margin-top: .5em" name="metasubmit" value="Import metadata" />'); +echo('</form>'); + + +echo('<p style="float: right"><a href="index.php">Return to entity listing</a></p>'); + +$this->includeAtTemplateBase('includes/footer.php'); + diff --git a/modules/metaedit/www/edit.php b/modules/metaedit/www/edit.php new file mode 100644 index 0000000000000000000000000000000000000000..5beb272af3d3e7d62d9b8fe874ea90e6c2c58c88 --- /dev/null +++ b/modules/metaedit/www/edit.php @@ -0,0 +1,80 @@ +<?php + +/* Load simpleSAMLphp, configuration and metadata */ +$config = SimpleSAML_Configuration::getInstance(); +$session = SimpleSAML_Session::getInstance(); +$metaconfig = SimpleSAML_Configuration::getConfig('module_metaedit.php'); + +$mdh = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($metaconfig->getValue('metahandlerConfig', NULL)); + +$authsource = $metaconfig->getValue('auth', 'login-admin'); +$useridattr = $metaconfig->getValue('useridattr', 'eduPersonPrincipalName'); + +if ($session->isValid($authsource)) { + $attributes = $session->getAttributes(); + // Check if userid exists + if (!isset($attributes[$useridattr])) + throw new Exception('User ID is missing'); + $userid = $attributes[$useridattr][0]; +} else { + SimpleSAML_Auth_Default::initLogin($authsource, SimpleSAML_Utilities::selfURL()); +} + +function requireOwnership($metadata, $userid) { + if (!isset($metadata['owner'])) + throw new Exception('Metadata has no owner. Which means no one is granted access, not even you.'); + if ($metadata['owner'] !== $userid) + throw new Exception('Metadata has an owner that is not equal to your userid, hence you are not granted access.'); +} + + +if (array_key_exists('entityid', $_REQUEST)) { + $metadata = $mdh->getMetadata($_REQUEST['entityid'], 'saml20-sp-remote'); + requireOwnership($metadata, $userid); +} elseif(array_key_exists('xmlmetadata', $_REQUEST)) { + + $xmldata = $_REQUEST['xmlmetadata']; + SimpleSAML_Utilities::validateXMLDocument($xmldata, 'saml-meta'); + $entities = SimpleSAML_Metadata_SAMLParser::parseDescriptorsString($xmldata); + $entity = array_pop($entities); + $metadata = $entity->getMetadata20SP(); + +} else { + $metadata = array( + 'owner' => $userid, + ); +} + + +$editor = new sspmod_metaedit_MetaEditor(); + + +if (isset($_POST['submit'])) { + $editor->checkForm($_POST); + $metadata = $editor->formToMeta($_POST, array(), array('owner' => $userid)); + + if (isset($_REQUEST['was-entityid']) && $_REQUEST['was-entityid'] !== $metadata['entityid']) { + $premetadata = $mdh->getMetadata($_REQUEST['was-entityid'], 'saml20-sp-remote'); + requireOwnership($premetadata, $userid); + $mdh->deleteMetadata($_REQUEST['was-entityid'], 'saml20-sp-remote'); + } + + $testmetadata = NULL; + try { + $testmetadata = $mdh->getMetadata($metadata['entityid'], 'saml20-sp-remote'); + } catch(Exception $e) {} + if ($testmetadata) requireOwnership($testmetadata, $userid); + + $mdh->saveMetadata($metadata['entityid'], 'saml20-sp-remote', $metadata); + + $template = new SimpleSAML_XHTML_Template($config, 'metaedit:saved.php'); + $template->show(); + exit; +} + +$form = $editor->metaToForm($metadata); + +$template = new SimpleSAML_XHTML_Template($config, 'metaedit:formedit.php'); +$template->data['form'] = $form; +$template->show(); + diff --git a/modules/metaedit/www/index.php b/modules/metaedit/www/index.php new file mode 100644 index 0000000000000000000000000000000000000000..bd2fb282722f9e0764c8d2dc113b7c661b81c6b9 --- /dev/null +++ b/modules/metaedit/www/index.php @@ -0,0 +1,54 @@ +<?php + +/* Load simpleSAMLphp, configuration and metadata */ +$config = SimpleSAML_Configuration::getInstance(); +$session = SimpleSAML_Session::getInstance(); +$metaconfig = SimpleSAML_Configuration::getConfig('module_metaedit.php'); + +$mdh = new SimpleSAML_Metadata_MetaDataStorageHandlerSerialize($metaconfig->getValue('metahandlerConfig', NULL)); + +$authsource = $metaconfig->getValue('auth', 'login-admin'); +$useridattr = $metaconfig->getValue('useridattr', 'eduPersonPrincipalName'); + +if ($session->isValid($authsource)) { + $attributes = $session->getAttributes(); + // Check if userid exists + if (!isset($attributes[$useridattr])) + throw new Exception('User ID is missing'); + $userid = $attributes[$useridattr][0]; +} else { + SimpleSAML_Auth_Default::initLogin($authsource, SimpleSAML_Utilities::selfURL()); +} + +function requireOwnership($metadata, $userid) { + if (!isset($metadata['owner'])) + throw new Exception('Metadata has no owner. Which means no one is granted access, not even you.'); + if ($metadata['owner'] !== $userid) + throw new Exception('Metadata has an owner that is not equal to your userid, hence you are not granted access.'); +} + + +if (isset($_REQUEST['delete'])) { + $premetadata = $mdh->getMetadata($_REQUEST['delete'], 'saml20-sp-remote'); + requireOwnership($premetadata, $userid); + $mdh->deleteMetadata($_REQUEST['delete'], 'saml20-sp-remote'); +} + + +$list = $mdh->getMetadataSet('saml20-sp-remote'); + +$slist = array('mine' => array(), 'others' => array()); +foreach($list AS $listitem) { + if (array_key_exists('owner', $listitem)) { + if ($listitem['owner'] === $userid) { + $slist['mine'][] = $listitem; continue; + } + } + $slist['others'][] = $listitem; +} + + +$template = new SimpleSAML_XHTML_Template($config, 'metaedit:metalist.php'); +$template->data['metadata'] = $slist; +$template->data['userid'] = $userid; +$template->show(); diff --git a/modules/metaedit/www/resources/style.css b/modules/metaedit/www/resources/style.css new file mode 100644 index 0000000000000000000000000000000000000000..1240db06576e85af71049ead441122ca26354a04 --- /dev/null +++ b/modules/metaedit/www/resources/style.css @@ -0,0 +1,37 @@ +table.formtable { + width: 100%; +} +table.formtable tr td.name { + text-align: right; + vertical-align: top; + padding-right: .6em; +} +table.formtable tr td.value { + text-align: left; + padding: 0px; +} +table.formtable tr td.header { + padding-left: 5px; + padding-top: 8px; + font-weight: bold; + font-size: 110%; +} + +table.formtable tr td input,table.formtable tr td textarea { + width: 90%; + border: 1px solid #bbb; + margin: 2px 5px; + padding: 2px 4px; +} + + +table.metalist { + border: 1px solid #aaa; + border-collapse: collapse; +} +table.metalist tr td { + padding: 2px 5px; +} +table.metalist tr.even td { + background: #e5e5e5; +} \ No newline at end of file diff --git a/modules/metaedit/www/xmlimport.php b/modules/metaedit/www/xmlimport.php new file mode 100644 index 0000000000000000000000000000000000000000..12f6072116b802eb5e9e1f39632db8ee3ff293ce --- /dev/null +++ b/modules/metaedit/www/xmlimport.php @@ -0,0 +1,9 @@ +<?php + + +/* Load simpleSAMLphp, configuration and metadata */ +$config = SimpleSAML_Configuration::getInstance(); +$session = SimpleSAML_Session::getInstance(); + +$template = new SimpleSAML_XHTML_Template($config, 'metaedit:xmlimport.tpl.php'); +$template->show();