summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/module.php77
-rw-r--r--modules/core/lib/ModuleDefinition.php122
-rw-r--r--modules/core/lib/ModuleInstaller.php188
-rw-r--r--modules/modinfo/dictionaries/modinfo.definition.json10
-rw-r--r--modules/modinfo/templates/modlist.php40
-rw-r--r--modules/modinfo/www/index.php12
-rw-r--r--www/resources/default.css9
7 files changed, 453 insertions, 5 deletions
diff --git a/bin/module.php b/bin/module.php
new file mode 100755
index 0000000..f69cafb
--- /dev/null
+++ b/bin/module.php
@@ -0,0 +1,77 @@
+#!/usr/bin/env php
+<?php
+
+/* This is the base directory of the simpleSAMLphp installation. */
+$baseDir = dirname(dirname(__FILE__));
+
+/* Add library autoloader. */
+require_once($baseDir . '/lib/_autoload.php');
+
+if (count($argv) < 1) {
+ echo "Wrong number of parameters. Run: " . $argv[0] . " [install,show] url [branch]\n"; exit;
+}
+
+// Needed in order to make session_start to be called before output is printed.
+$session = SimpleSAML_Session::getInstance();
+$config = SimpleSAML_Configuration::getConfig('config.php');
+
+
+$action = $argv[1];
+
+
+function getModinfo() {
+ global $argv;
+ if (count($argv) < 2)
+ throw new Exception('Missing second parameter: URL/ID');
+ return sspmod_core_ModuleDefinition::load($argv[2]);
+}
+
+function getBranch() {
+ global $argv;
+ if (isset($argv[3])) return $argv[3];
+ return NULL;
+}
+
+switch($action) {
+ case 'install':
+ $mod = getModinfo();
+ $installer = new sspmod_core_ModuleInstaller($mod);
+ $installer->install(getBranch());
+ break;
+
+ case 'remove':
+ $mod = getModinfo();
+ $installer = new sspmod_core_ModuleInstaller($mod);
+ $installer->remove(getBranch());
+ break;
+
+ case 'upgrade':
+ $mod = getModinfo();
+ $installer = new sspmod_core_ModuleInstaller($mod);
+ $installer->upgrade(getBranch());
+ break;
+
+ case 'upgrade-all' :
+ $mdir = scandir($config->getBaseDir() . 'modules/');
+ foreach($mdir AS $md) {
+ if (!sspmod_core_ModuleDefinition::validId($md)) continue;
+ if (!sspmod_core_ModuleDefinition::isDefined($md)) continue;
+ $moduledef = sspmod_core_ModuleDefinition::load($md, 'remote');
+ $installer = new sspmod_core_ModuleInstaller($moduledef);
+
+ if ($moduledef->updateExists() || $moduledef->alwaysUpdate()) {
+ echo "Upgrading [" . $md . "]\n";
+ $installer->upgrade();
+ } else {
+ echo "No updates available for [" . $md . "]\n";
+ }
+ }
+ break;
+
+ default:
+ throw new Exception('Unknown action [' . $action . ']');
+}
+
+
+
+
diff --git a/modules/core/lib/ModuleDefinition.php b/modules/core/lib/ModuleDefinition.php
new file mode 100644
index 0000000..9499ed4
--- /dev/null
+++ b/modules/core/lib/ModuleDefinition.php
@@ -0,0 +1,122 @@
+<?php
+
+class sspmod_core_ModuleDefinition {
+
+ public $def;
+ private static $cache;
+
+ private function __construct($def) {
+ $this->def = $def;
+ $this->requireValidIdentifier();
+ }
+
+ public static function validId($id) {
+ return preg_match('|^[a-zA-Z_]+$|', $id);
+ }
+
+ public static function isDefined($id) {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+ $filename = $basedir . 'modules/' . $id . '/definition.json';
+ return (file_exists($filename));
+ }
+
+ public static function load($id, $force = NULL) {
+
+ if (isset($cache[$id])) return $cache[$id];
+
+ if (self::validId($id)) {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+ $filename = $basedir . 'modules/' . $id . '/definition.json';
+ if (!file_exists($filename))
+ throw new Exception('Could not read definition file for module [' . $id . '] : ' . $filename);
+ $defraw = file_get_contents($filename);
+ $def = json_decode($defraw, TRUE);
+
+ } elseif(preg_match('|^http(s)?://.*$|', $id)) {
+ $defraw = file_get_contents($id);
+ $def = json_decode($defraw, TRUE);
+ } else {
+ throw new Exception('Could not resolve [' . $id . '] as URL nor module identifier.');
+ }
+ $cache[$id] = new sspmod_core_ModuleDefinition($def);
+
+
+
+ if (isset($force)) {
+ if ($force === 'local') {
+ if(preg_match('|^http(s)?://.*$|', $id)) {
+ return self::load($def['id']);
+ }
+ } elseif($force === 'remote') {
+ if (self::validId($id)) {
+ if (!isset($def['definition']))
+ throw new Exception('Could not load remote definition file for module [' . $id . ']');
+ return self::load($def['definition']);
+ }
+ }
+ }
+
+ return $cache[$id];
+ }
+
+ private function requireValidIdentifier() {
+ if (!isset($this->def['id']))
+ throw new Exception('Missing [id] value in module definition');
+ if (!preg_match('|^[a-zA-Z_]+$|', $this->def['id']))
+ throw new Exception('Illegal characters in [id] in module definition');
+ }
+
+ public function getVersion($branch = NULL) {
+ if (!isset($this->def['access'])) throw new Exception('Missing [access] statement in module definition');
+ if (!isset($this->def['branch'])) throw new Exception('Missing [branch] statement in module definition');
+
+ if (is_null($branch)) $branch = $this->def['branch'];
+
+ if (!isset($this->def['access'][$branch])) throw new Exception('Missing [access] information for branch [' . var_export($branch, TRUE) . ']');
+ if (!isset($this->def['access'][$branch]['version'])) throw new Exception('Missing version information in [access] in branch [' . var_export($branch, TRUE) . ']');
+
+ return $this->def['access'][$branch]['version'];
+ }
+
+ public function alwaysUpdate($branch = NULL) {
+ $access = $this->getAccess($branch);
+ if ($access['type'] === 'svn') return TRUE;
+ return FALSE;
+ }
+
+ public function getBranch($branch = NULL) {
+ if (!isset($this->def['branch'])) throw new Exception('Missing [branch] statement in module definition');
+ if (is_null($branch)) $branch = $this->def['branch'];
+ return $branch;
+ }
+
+ public function updateExists($branch = NULL) {
+ $branch = $this->getBranch($branch);
+
+ $localDef = self::load($this->def['id'], 'local');
+ $thisVersion = $localDef->getVersion($branch);
+
+ $remoteDef = self::load($this->def['definition'], 'remote');
+ $remoteVersion = $remoteDef->getVersion($branch);
+
+ #echo ' Comparing versions localĀ [' . $thisVersion . '] and remote [' . $remoteVersion . ']' . "\n";
+
+ return version_compare($remoteVersion, $thisVersion, '>');
+ }
+
+
+ public function getAccess($branch = NULL) {
+ if (!isset($this->def['access'])) throw new Exception('Missing [access] statement in module definition');
+ if (!isset($this->def['branch'])) throw new Exception('Missing [branch] statement in module definition');
+
+ if (is_null($branch)) $branch = $this->def['branch'];
+
+ if (!isset($this->def['access'][$branch])) throw new Exception('Missing [access] information for branch [' . var_export($branch, TRUE) . ']');
+
+ return $this->def['access'][$branch];
+ }
+
+
+} \ No newline at end of file
diff --git a/modules/core/lib/ModuleInstaller.php b/modules/core/lib/ModuleInstaller.php
new file mode 100644
index 0000000..d18f59b
--- /dev/null
+++ b/modules/core/lib/ModuleInstaller.php
@@ -0,0 +1,188 @@
+<?php
+
+class sspmod_core_ModuleInstaller {
+
+ public $module;
+
+ public function __construct(sspmod_core_ModuleDefinition $module) {
+ $this->module = $module;
+
+ }
+
+ public function remove($branch = NULL) {
+ $access = $this->module->getAccess($branch);
+
+ switch($access['type']) {
+ // case 'svn' :
+ // $this->requireInstalled();
+ // $this->remove($access);
+ // break;
+
+ default:
+ $this->requireInstalled();
+ $this->removeModuleDir($access);
+ break;
+
+ }
+ }
+
+ public function install($branch = NULL) {
+
+ $access = $this->module->getAccess($branch);
+
+ switch($access['type']) {
+ case 'svn' :
+ $this->requireNotInstalled();
+ $this->svnCheckout($access);
+ $this->enable();
+ $this->prepareConfig();
+ break;
+
+ case 'zip' :
+ $this->requireNotInstalled();
+ $this->zipLoad($access);
+ $this->enable();
+ $this->prepareConfig();
+ break;
+
+
+ default:
+ throw new Exception('Unknown access method type. Not one of [zip,tgz,svn]');
+
+ }
+
+ }
+
+ public static function exec($cmd) {
+ echo ' $ ' . $cmd . "\n";
+ $output = shell_exec(escapeshellcmd($cmd));
+
+ if (empty($output)) return;
+
+ $oa = explode("\n", $output);
+
+ foreach($oa AS $ol) {
+ echo ' > ' . $ol . "\n";
+ }
+
+ }
+
+ public function upgrade($branch = NULL) {
+
+ $access = $this->module->getAccess($branch);
+
+ switch($access['type']) {
+ case 'svn' :
+ $this->requireInstalled();
+ $this->svnUpdate($access);
+ $this->enable();
+ $this->prepareConfig();
+ break;
+
+ case 'zip' :
+ $this->requireInstalled();
+ $this->zipLoad($access);
+ $this->enable();
+ $this->prepareConfig();
+ break;
+
+ default:
+ throw new Exception('Unknown access method type. Not one of [zip,tgz,svn]');
+
+ }
+
+ }
+
+ public function dirExists() {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+
+ $dir = $basedir . 'modules/' . $this->module->def['id'];
+
+ return (file_exists($dir) && is_dir($dir));
+ }
+
+ public function requireValidURL($url) {
+ if (!preg_match('|http(s)?://[a-zA-Z0-9_-/.]|', $url))
+ throw new Exception('Invalid URL [' . $url . ']');
+ }
+
+ public function requireNotInstalled() {
+ if ($this->dirExists())
+ throw new Exception('The module [' . $this->module->def['id'] . '] is already installed.');
+ }
+
+ public function requireInstalled() {
+ if (!$this->dirExists())
+ throw new Exception('The module [' . $this->module->def['id'] . '] is not installed.');
+ }
+
+ public function svnCheckout($access) {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+ $cmd = "svn co " . escapeshellarg($access['url']) . " " . $basedir . "modules/" . $this->module->def['id'];
+ self::exec($cmd);
+ }
+
+ public function svnUpdate($access) {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+ $cmd = "svn up " . $basedir . "modules/" . $this->module->def['id'];
+ self::exec($cmd);
+ }
+
+ public function removeModuleDir($access) {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+ $cmd = "rm -rf " . $basedir . "modules/" . $this->module->def['id'];
+ self::exec($cmd);
+ }
+
+ public function enable() {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+
+ $this->requireInstalled();
+
+ $cmd = "touch " . $basedir . "modules/" . $this->module->def['id'] . '/enable';
+ self::exec($cmd);
+ }
+
+ public function prepareConfig() {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+
+ $this->requireInstalled();
+
+ $dir = $basedir . "modules/" . $this->module->def['id'] . '/config-templates';
+ if (!file_exists($dir)) return;
+
+ $files = scandir($dir);
+ foreach($files AS $file) {
+ if(!preg_match('|^.*\.php|', $file)) continue;
+
+ if (file_exists($basedir . 'config/' . $file)) {
+ echo "Configuration file [" . $file . "] already exists. Will not overwrite existing file.\n";
+ continue;
+ }
+
+ $cmd = 'cp ' . $dir . '/' . $file . ' ' . $basedir . 'config/';
+ self::exec($cmd);
+ }
+ }
+
+ public function zipLoad($access) {
+ $config = SimpleSAML_Configuration::getConfig('config.php');
+ $basedir = $config->getBaseDir();
+
+ $zipfile = $access['url'];
+ $localfile = tempnam(sys_get_temp_dir(), 'ssp-module-');
+ $filecontents = file_get_contents($zipfile);
+ file_put_contents($localfile, $filecontents);
+
+ $cmd = "unzip -qo " . escapeshellarg($localfile) . " -d " . $basedir . "modules/";
+ self::exec($cmd);
+
+ }
+
+} \ No newline at end of file
diff --git a/modules/modinfo/dictionaries/modinfo.definition.json b/modules/modinfo/dictionaries/modinfo.definition.json
index 2391a52..1c20ab7 100644
--- a/modules/modinfo/dictionaries/modinfo.definition.json
+++ b/modules/modinfo/dictionaries/modinfo.definition.json
@@ -13,5 +13,15 @@
},
"modlist_disabled": {
"en": "Disabled"
+ },
+ "latest_version" : {
+ "en" : "Updated"
+ },
+ "update_exists" : {
+ "en" : "Update available"
+ },
+ "version" : {
+ "en" : "Version"
}
+
} \ No newline at end of file
diff --git a/modules/modinfo/templates/modlist.php b/modules/modinfo/templates/modlist.php
index d20df10..253adc7 100644
--- a/modules/modinfo/templates/modlist.php
+++ b/modules/modinfo/templates/modlist.php
@@ -10,15 +10,29 @@ $this->includeAtTemplateBase('includes/header.php');
<h2><?php echo($this->data['header']); ?></h2>
-<table>
+<table class="modules" style="width: 100%">
<tr>
-<th><?php echo($this->t('{modinfo:modinfo:modlist_name}')); ?></th>
-<th><?php echo($this->t('{modinfo:modinfo:modlist_status}')); ?></th>
+<th colspan="2"><?php echo($this->t('{modinfo:modinfo:modlist_name}')); ?></th>
+<th ><?php echo($this->t('{modinfo:modinfo:modlist_status}')); ?></th>
+<th colspan="2"><?php echo($this->t('{modinfo:modinfo:version}')); ?></th>
</tr>
<?php
+
+$i = 0;
foreach($this->data['modules'] as $id => $info) {
- echo('<tr>');
- echo('<td>' . htmlspecialchars($id) . '</td>');
+ echo('<tr class="' . ($i++ % 2 == 0 ? 'odd' : 'even') . '">');
+
+
+ if (isset($info['def'])) {
+ echo('<td><a href="http://simplesamlphp.org/modules/' . htmlspecialchars($id) . '">' . htmlspecialchars($info['def']->def['name']) . '</a></td>');
+ } else {
+ echo('<td> </td>');
+ }
+
+
+ echo('<td><tt>' . htmlspecialchars($id) . '</tt></td>');
+
+
if($info['enabled']) {
echo('<td><img src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/accept.png" alt="' .
htmlspecialchars($this->t('{modinfo:modinfo:modlist_enabled}')) . '" /></td>');
@@ -26,6 +40,22 @@ foreach($this->data['modules'] as $id => $info) {
echo('<td><img src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/delete.png" alt="' .
htmlspecialchars($this->t('{modinfo:modinfo:modlist_disabled}')) . '" /></td>');
}
+
+ if (isset($info['def'])) {
+ echo('<td>' . htmlspecialchars($info['def']->getVersion()) . ' (' .htmlspecialchars($info['def']->getBranch()) . ')</td>');
+ if ($info['def']->updateExists()) {
+ echo('<td><img style="display: inline" src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/delete.png" alt="' .
+ htmlspecialchars($this->t('{modinfo:modinfo:update_exists}')) . '" /> ' .
+ htmlspecialchars($this->t('{modinfo:modinfo:update_exists}')) . '</td>');
+ } else {
+ echo('<td><img style="display: inline" src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/accept.png" alt="' .
+ htmlspecialchars($this->t('{modinfo:modinfo:latest_version}')) . '" /> ' .
+ htmlspecialchars($this->t('{modinfo:modinfo:latest_version}')) . '</td>');
+ }
+ } else {
+ echo('<td colspan="2"> </td>');
+ }
+
echo('</tr>');
}
?>
diff --git a/modules/modinfo/www/index.php b/modules/modinfo/www/index.php
index 74897f0..d8b4547 100644
--- a/modules/modinfo/www/index.php
+++ b/modules/modinfo/www/index.php
@@ -9,7 +9,19 @@ foreach($modules as $m) {
$modinfo[$m] = array(
'enabled' => SimpleSAML_Module::isModuleEnabled($m),
);
+ if (sspmod_core_ModuleDefinition::isDefined($m)) {
+ $modinfo[$m]['def'] = sspmod_core_ModuleDefinition::load($m);
+ }
+
+}
+
+function cmpa($a, $b) {
+
+ if (isset($a['def']) && !isset($b['def'])) return -1;
+ if (isset($b['def']) && !isset($a['def'])) return 1;
+ return 0;
}
+uasort($modinfo, 'cmpa');
$config = SimpleSAML_Configuration::getInstance();
$t = new SimpleSAML_XHTML_Template($config, 'modinfo:modlist.php');
diff --git a/www/resources/default.css b/www/resources/default.css
index b3e66f6..984304f 100644
--- a/www/resources/default.css
+++ b/www/resources/default.css
@@ -276,6 +276,15 @@ div.preferredidp {
padding: 2px 2em 2px 2em;
}
+table.modules {
+ border-collapse: collapse;
+}
+table.modules tr td {
+ border-bottom: 1px solid #ddd;
+}
+table.modules tr.even td {
+ background: #f0f0f0;
+}
/* Attribute presentation in example page */
table.attributes {