summaryrefslogtreecommitdiffstats
path: root/modules-available
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available')
-rw-r--r--modules-available/sysconfig/addmodule_shibauth.inc.php183
-rw-r--r--modules-available/sysconfig/hooks/cron.inc.php5
-rw-r--r--modules-available/sysconfig/inc/configmodule/shibauth.inc.php119
-rw-r--r--modules-available/sysconfig/inc/shib.inc.php150
-rw-r--r--modules-available/sysconfig/lang/de/config-module.json16
-rw-r--r--modules-available/sysconfig/lang/de/messages.json5
-rw-r--r--modules-available/sysconfig/lang/de/module.json1
-rw-r--r--modules-available/sysconfig/lang/de/template-tags.json6
-rw-r--r--modules-available/sysconfig/lang/en/config-module.json12
-rw-r--r--modules-available/sysconfig/lang/en/messages.json3
-rw-r--r--modules-available/sysconfig/lang/en/module.json1
-rw-r--r--modules-available/sysconfig/lang/en/template-tags.json6
-rw-r--r--modules-available/sysconfig/page.inc.php9
-rw-r--r--modules-available/sysconfig/templates/shibauth-orgs.html56
-rw-r--r--modules-available/sysconfig/templates/shibauth-start.html45
15 files changed, 601 insertions, 16 deletions
diff --git a/modules-available/sysconfig/addmodule_shibauth.inc.php b/modules-available/sysconfig/addmodule_shibauth.inc.php
new file mode 100644
index 00000000..ced9d9fd
--- /dev/null
+++ b/modules-available/sysconfig/addmodule_shibauth.inc.php
@@ -0,0 +1,183 @@
+<?php
+
+/*
+ * Wizard for configuring shibboleth login (lightdm greeter)
+ */
+
+const SHIB_SESSION_DATA = 'shibiddata';
+
+class ShibAuth_Start extends AddModule_Base
+{
+
+ protected function renderInternal()
+ {
+ /* Load or initialise session data */
+ if (Request::get('back', 'false', 'string') !== 'false') {
+ /* If coming via the back button, load the session data */
+ $session_data = Session::get(SHIB_SESSION_DATA);
+ if (!is_array($session_data)) {
+ $session_data = [];
+ }
+ } elseif ($this->edit !== null) {
+ $session_data = $this->edit->getData(null)
+ + ['title' => $this->edit->title()];
+ Session::set(SHIB_SESSION_DATA, $session_data);
+ } else {
+ $session_data = [];
+ Session::set(SHIB_SESSION_DATA, $session_data);
+ }
+ Render::addDialog(Dictionary::translateFile('config-module', 'shibauth_title'), false, 'shibauth-start', [
+ 'next' => 'ShibAuth_Orgs',
+ 'edit' => $this->edit !== null ? $this->edit->id() : 0,
+ ] + $session_data);
+ }
+
+ protected function ajaxInternal()
+ {
+ $ret = Shib::refreshIdpSuffixMap();
+ if (!$ret) {
+ Message::addError('shib-list-update-failed');
+ }
+ }
+
+}
+
+class Shibauth_Orgs extends AddModule_Base
+{
+
+ private $session_data;
+
+ protected function preprocessInternal()
+ {
+ $title = trim(Request::post('title', '', 'string'));
+ if (empty($title)) {
+ Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Start&back=true');
+ }
+ $this->session_data = Session::get(SHIB_SESSION_DATA);
+ if (!is_array($this->session_data)) {
+ $this->session_data = [];
+ }
+ $this->session_data['title'] = $title;
+ $this->session_data['browser'] = !empty(Request::post('browser', '', 'string'));
+ $this->session_data['qrcode'] = !empty(Request::post('qrcode', '', 'string'));
+ Session::set(SHIB_SESSION_DATA, $this->session_data);
+ }
+
+ protected function renderInternal()
+ {
+ $list = array_values(Shib::getListByRegistrar());
+ $selected = [];
+ if (is_array($this->session_data['regs'])) {
+ $selected += array_flip(Shib::explodeRegistrars($this->session_data['regs']));
+ }
+ if (is_array($this->session_data['idp'])) {
+ $selected += array_flip($this->session_data['idp']);
+ }
+ foreach ($list as &$reg) {
+ $reg['reghash'] = substr(md5($reg['registrar']), 0, 7);
+ foreach ($reg['list'] as &$idp) {
+ $idp['idphash'] = substr(md5($idp['id']), 0, 7);
+ if (isset($selected[$idp['id']])) {
+ $idp['checked'] = 'checked';
+ }
+ }
+ unset($idp);
+ ArrayUtil::sortByColumn($reg['list'], 'name');
+ }
+ Render::addDialog(Dictionary::translateFile('config-module', 'shibauth_title'), false, 'shibauth-orgs', [
+ 'next' => 'ShibAuth_Finish',
+ 'edit' => $this->edit !== null ? $this->edit->id() : 0,
+ 'list' => $list,
+ ]);
+ }
+
+}
+
+class ShibAuth_Finish extends AddModule_Base
+{
+
+ protected function preprocessInternal()
+ {
+ $userIdpList = Request::post('idp', [], 'array');
+ if (empty($userIdpList)) {
+ Message::addError('no-organization-selected');
+ Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Orgs');
+ }
+ $session_data = Session::get(SHIB_SESSION_DATA);
+ if (!is_array($session_data)) {
+ $session_data = ['idp' => $userIdpList];
+ Session::set(SHIB_SESSION_DATA, $session_data);
+ Message::addError('no-session-data');
+ Util::redirect('?do=sysconfig&action=addmodule&step=ShibAuth_Start&back=true');
+ }
+
+ /* Only create an instance if it's a new one */
+ if ($this->edit !== null) {
+ $module = $this->edit;
+ } else {
+ $module = ConfigModule::getInstance('ShibAuth');
+ }
+
+ // Now figure out which registrars were selected in its entirety.
+ // In those cases, add thr registrar instead.
+ $regLookup = Shib::getIdp2SuffixList();
+ $byReg = [];
+ $entireRegs = [];
+ // Collect by reg
+ foreach ($userIdpList as $idp) {
+ $reg = $regLookup[$idp]['regauth'] ?? 'none';
+ if (!isset($byReg[$reg])) {
+ $byReg[$reg] = [];
+ }
+ $byReg[$reg][] = $idp;
+ }
+ // Check which one matches all
+ $regList = Shib::getListByRegistrar();
+ foreach (array_keys($byReg) as $reg) {
+ if (!isset($regList[$reg]))
+ continue;
+ if (count ($regList[$reg]['list']) === count($byReg[$reg])) {
+ $entireRegs[] = $reg;
+ unset ($byReg[$reg]);
+ error_log("Entire registrar $reg");
+ }
+ }
+ // Build final IDP list by combining the remaining ones again
+ $userIdpList = [];
+ foreach ($byReg as $idps) {
+ $userIdpList = array_merge($userIdpList, $idps);
+ }
+
+ /* Set all the data to the module instance */
+ $module->setData('browser', $session_data['browser']);
+ $module->setData('qrcode', $session_data['qrcode']);
+ $module->setData('idp', $userIdpList);
+ $module->setData('regs', $entireRegs);
+
+ /* Insert or update database entries */
+ if ($this->edit !== null) {
+ $module->update($session_data['title']);
+ } else {
+ $module->insert($session_data['title']);
+ }
+
+ $task = $module->generate($this->edit === null);
+
+ // Yay
+ Session::set(SHIB_SESSION_DATA, false);
+ if ($task !== false && $this->edit !== null) {
+ Message::addSuccess('module-edited');
+ } elseif ($task !== false) {
+ Message::addSuccess('module-added');
+ AddModule_Base::setStep('AddModule_Assign', $module->id());
+ return;
+ }
+ Util::redirect('?do=SysConfig');
+ }
+
+ protected function renderInternal()
+ {
+
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/sysconfig/hooks/cron.inc.php b/modules-available/sysconfig/hooks/cron.inc.php
index b959060d..cedc0810 100644
--- a/modules-available/sysconfig/hooks/cron.inc.php
+++ b/modules-available/sysconfig/hooks/cron.inc.php
@@ -4,3 +4,8 @@
Database::exec("DELETE c FROM configtgz_location c
LEFT JOIN location l USING (locationid)
WHERE l.locationid IS NULL AND c.locationid <> 0");
+
+// Refresh every once in a while (should roughly be once a day)
+if (mt_rand(0, 300) === 0) {
+ Shib::refreshIdpSuffixMap();
+} \ No newline at end of file
diff --git a/modules-available/sysconfig/inc/configmodule/shibauth.inc.php b/modules-available/sysconfig/inc/configmodule/shibauth.inc.php
new file mode 100644
index 00000000..0627f02f
--- /dev/null
+++ b/modules-available/sysconfig/inc/configmodule/shibauth.inc.php
@@ -0,0 +1,119 @@
+<?php
+
+ConfigModule::registerModule(
+ ConfigModule_ShibAuth::MODID, // ID
+ Dictionary::translateFile('config-module', 'shibauth_title'), // Title
+ Dictionary::translateFile('config-module', 'shibauth_description'), // Description
+ Dictionary::translateFile('config-module', 'group_authentication'), // Group
+ false, // Only one per config?
+ 310 // Sort order
+);
+
+class ConfigModule_ShibAuth extends ConfigModule
+{
+ const MODID = 'ShibAuth';
+ const VERSION = 1;
+
+ protected function generateInternal(string $tgz, ?string $parent)
+ {
+ /* Validate if all data are available */
+ if (!$this->validateConfig())
+ return false;
+
+ return Taskmanager::submit('MakeTarball', array(
+ 'files' => $this->getFileArray(),
+ 'destination' => $tgz,
+ 'parentTask' => $parent,
+ ), false);
+ }
+
+ protected function moduleVersion(): int
+ {
+ return self::VERSION;
+ }
+
+ protected function validateConfig(): bool
+ {
+ return isset($this->moduleData['browser']) || isset($this->moduleData['qrcode'])
+ || !empty($this->moduleData['idp']);
+ }
+
+ public function setData(string $key, $value): bool
+ {
+ switch ($key) {
+ case 'browser':
+ case 'qrcode':
+ case 'idp':
+ case 'regs':
+ break;
+ default:
+ return false;
+ }
+ $this->moduleData[$key] = $value;
+ return true;
+ }
+
+ public function allowDownload(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Creates a map with filepath => file content
+ * @return array{"/opt/openslx/pam/shibboleth/whitelist/shib-$id.idp": string,
+ * "/etc/lightdm/qt-lightdm-greeter.conf.d/shib-$id.conf": string,
+ * "/opt/openslx/pam/shibboleth/whitelist/shib-$id.suffix": string}
+ */
+ private function getFileArray(): array
+ {
+ $id = $this->id();
+ $url = CONFIG_SHIB_CLIENT_URL;
+ $browser = '';
+ $qrcode = '';
+ if ($this->moduleData['browser'] ?? false) {
+ $browser = "shib-session-enabled = true";
+ }
+ if ($this->moduleData['qrcode'] ?? false) {
+ $qrcode = "qr-session-enabled = true";
+ }
+ return [
+ "/opt/openslx/pam/shibboleth/whitelist/shib-$id.idp" => implode("\n", $this->moduleData['idp']) . "\n",
+ "/etc/lightdm/qt-lightdm-greeter.conf.d/shib-$id.conf" => <<<EOF
+[General]
+shib-url = $url
+$browser
+$qrcode
+EOF,
+ "/opt/openslx/pam/shibboleth/whitelist/shib-$id.suffix" => $this->generateSuffixList(),
+ ];
+ }
+
+ /**
+ * Generate plain text file of suffixes belonging to all enabled entities.
+ * Used by pam-part on client to verify login.
+ */
+ private function generateSuffixList(): string
+ {
+ $idp2suffix = Shib::getIdp2SuffixList();
+ if ($idp2suffix === null)
+ return '';
+ // Explode registrar shortcuts
+ if (is_array($this->moduleData['regs'] ?? 0)) {
+ $idps = Shib::explodeRegistrars($this->moduleData['regs']);
+ } else {
+ $idps = [];
+ }
+ if (is_array($this->moduleData['idp'])) {
+ $idps = array_merge($idps, $this->moduleData['idp']);
+ }
+ // Build
+ $return = '';
+ foreach ($idps as $idp) {
+ if (empty($idp2suffix[$idp]))
+ continue;
+ $return .= implode("\n", $idp2suffix[$idp]['suffix']) . "\n";
+ }
+ return $return;
+ }
+
+}
diff --git a/modules-available/sysconfig/inc/shib.inc.php b/modules-available/sysconfig/inc/shib.inc.php
new file mode 100644
index 00000000..c3956572
--- /dev/null
+++ b/modules-available/sysconfig/inc/shib.inc.php
@@ -0,0 +1,150 @@
+<?php
+
+class Shib
+{
+ const SHIB_LOOKUP_JSON = '/var/cache/slx-admin/idp2suffix.json';
+
+ const SHIB_DISCO_JSON = '/var/cache/slx-admin/discofeed.json';
+
+ const RET_ERR = 0;
+ const RET_UPDATE = 1;
+ const RET_UNCHANGED = 2;
+
+ /**
+ * Updates the local copy of the JSON file for mapping IDP entityIDs to scope suffixes.
+ * If the list changed, all ShibAuth modules will be rebuilt.
+ *
+ * @return bool true on success, or if list didn't change. false otherwise.
+ */
+ public static function refreshIdpSuffixMap(): bool
+ {
+ $changed = false;
+ $ret = self::updateFromUrl(CONFIG_SHIB_LOOKUP_URL . '?what=id2suffix', self::SHIB_LOOKUP_JSON);
+ if ($ret === self::RET_ERR)
+ return false;
+ if ($ret === self::RET_UPDATE) {
+ $changed = true;
+ }
+ $ret = self::updateFromUrl(CONFIG_SHIB_DISCO_URL, self::SHIB_DISCO_JSON);
+ if ($ret === self::RET_ERR)
+ return false;
+ if ($ret === self::RET_UPDATE) {
+ $changed = true;
+ }
+ if ($changed) {
+ // Rebuild any modules in the background
+ $configs = [];
+ $list = ConfigModule::getAll('ShibAuth');
+ $parent = null;
+ foreach ($list as $mod) {
+ $r = $mod->generate(false, $parent);
+ if (is_string($r)) {
+ $parent = $r;
+ }
+ foreach (ConfigTgz::getAllForModule($mod->id()) as $tgz) {
+ $configs[$tgz->id()] = $tgz;
+ }
+ }
+ foreach ($configs as $tgz) {
+ $r = $tgz->generate(false, 0, $parent);
+ if (is_string($r)) {
+ $parent = $r;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ private static function updateFromUrl(string $url, string $file): int
+ {
+ if (!file_exists($file) || filemtime($file) + 86400 < time()) {
+ $code = null;
+ $ret = Download::toFile($file . '.tmp',
+ $url, 15, $code);
+ if (!$ret)
+ return self::RET_ERR;
+ if (!file_exists($file)
+ || filesize($file) !== filesize($file . '.tmp')
+ || sha1_file($file, true) !== sha1_file($file . '.tmp', true)
+ ) {
+ rename($file . '.tmp', $file);
+ if ($code >= 200 && $code < 300)
+ return self::RET_UPDATE;
+ return self::RET_ERR; // Not 2xx range, keep file but return error
+ }
+ unlink($file . '.tmp');
+ }
+ return self::RET_UNCHANGED;
+ }
+
+ /**
+ * @return ?array of registrars (key) containing array{"registrar": string, "list": string[]}
+ */
+ public static function getListByRegistrar(): ?array
+ {
+ $disco = json_decode(file_get_contents(self::SHIB_DISCO_JSON), true);
+ $lookup = json_decode(file_get_contents(self::SHIB_LOOKUP_JSON), true);
+ if (!is_array($disco) || !is_array($lookup)) {
+ return null;
+ }
+ foreach ($disco as $item) {
+ if (!isset($item['entityID'])) {
+ continue;
+ }
+ $id = $item['entityID'];
+ $name = null;
+ $fallback = null;
+ foreach ($item['DisplayNames'] ?? [] as $dn) {
+ if ($dn['lang'] === LANG) {
+ $name = $dn['value'];
+ } elseif ($name === null && $dn['lang'] === 'en') {
+ $name = $dn['value'];
+ } elseif ($fallback === null) {
+ $fallback = $dn['value'];
+ }
+ }
+ if ($name === null) {
+ $name = $fallback;
+ }
+ $registrar = $lookup[$id]['regauth'] ?? 'unknown';
+ if (!isset($return[$registrar])) {
+ $return[$registrar] = ['list' => [], 'registrar' => $registrar];
+ }
+ $return[$registrar]['list'][] = [
+ 'id' => $id,
+ 'name' => $name ?? $id,
+ ];
+ }
+ return $return;
+ }
+
+ public static function getIdp2SuffixList(): ?array
+ {
+ $lookup = json_decode(file_get_contents(self::SHIB_LOOKUP_JSON), true);
+ if (!is_array($lookup))
+ return null;
+ return $lookup;
+ }
+
+ /**
+ * Map given list of registrar IDs back to all the IdP entityIDs they cover.
+ * @param string[] $regs
+ * @return string[]
+ */
+ public static function explodeRegistrars(array $regs): array
+ {
+ if (empty($regs))
+ return [];
+ $idps = [];
+ $reg2idps = Shib::getListByRegistrar();
+ foreach ($regs as $reg) {
+ if (!isset($reg2idps[$reg]['list']))
+ continue;
+ foreach ($reg2idps[$reg]['list'] as $elem) {
+ $idps[] = $elem['id'];
+ }
+ }
+ return $idps;
+ }
+
+} \ No newline at end of file
diff --git a/modules-available/sysconfig/lang/de/config-module.json b/modules-available/sysconfig/lang/de/config-module.json
index 33c743a5..6d38565b 100644
--- a/modules-available/sysconfig/lang/de/config-module.json
+++ b/modules-available/sysconfig/lang/de/config-module.json
@@ -9,14 +9,16 @@
"group_branding": "Einrichtungsspezifisches Logo",
"group_generic": "Generisch",
"group_screensaver": "Bildschirmschoner Styling",
- "group_sshconfig": "SSH-Dämon",
+ "group_sshconfig": "SSH-D\u00e4mon",
"group_sshkey": "SSH-Key",
"ldapAuth_description": "Mit diesem Modul l\u00e4sst sich eine generische LDAP-Authentifizierung einrichten.",
- "ldapAuth_title": "LDAP Authentifizierung",
- "screensaver_title": "Bildschirmschoner Anpassungen",
- "screensaver_description": "Mit diesem Modul können sie den Style (QSS) und die Texte des Bildschirmschoners anpassen.",
+ "ldapAuth_title": "LDAP-Authentifizierung",
+ "screensaver_description": "Mit diesem Modul k\u00f6nnen sie den Style (QSS) und die Texte des Bildschirmschoners anpassen.",
+ "screensaver_title": "Design\/Texte Bildschirmschoner",
+ "shibauth_description": "Mit diesem Modul l\u00e4sst sich die Authentifizierung mittels Shibboleth\/SSO am Client einrichten.",
+ "shibauth_title": "Shibboleth-Authentifizierung",
"sshconfig_description": "Mit diesem Modul l\u00e4sst sich steuern, ob und wie der sshd auf den gebooteten Clients startet, und welche Funktionen er zur Verf\u00fcgung stellt. Wenn Sie keinen sshd auf den Clients nutzen wollen, brauchen Sie kein solches Modul zu erstellen.",
"sshconfig_title": "SSH-D\u00e4mon",
- "sshkey_title": "SSH-Key",
- "sshkey_description": "Einen öffentlichen SSH-Schlüssel zu den authorized_keys des root-Benutzers hinzufügen. Mit dem zugehörigen privaten Schlüssel kann dann via SSH auf die gebooteten Clients zugegriffen werden, sofern root-Login im zugehörigen SSH-Dämon-Modul aktiviert wurde."
-}
+ "sshkey_description": "Einen \u00f6ffentlichen SSH-Schl\u00fcssel zu den authorized_keys des root-Benutzers hinzuf\u00fcgen. Mit dem zugeh\u00f6rigen privaten Schl\u00fcssel kann dann via SSH auf die gebooteten Clients zugegriffen werden, sofern root-Login im zugeh\u00f6rigen SSH-D\u00e4mon-Modul aktiviert wurde.",
+ "sshkey_title": "SSH-Key"
+} \ No newline at end of file
diff --git a/modules-available/sysconfig/lang/de/messages.json b/modules-available/sysconfig/lang/de/messages.json
index 6bc2f2e5..5e8423df 100644
--- a/modules-available/sysconfig/lang/de/messages.json
+++ b/modules-available/sysconfig/lang/de/messages.json
@@ -13,10 +13,13 @@
"module-rebuilding": "Modul wird neu generiert",
"module-rebuilt": "Modul wurde neu generiert",
"no-image": "Unter der angegebenen URL konnte kein SVG-Bild gefunden werden",
- "no-noconfig-active": "Es wurde noch keine Systemkonfiguration ausgew\u00e4hlt.",
+ "no-noconfig-active": "Es wurde noch keine Systemkonfiguration ausgew\u00e4hlt",
+ "no-organization-selected": "Keine Organisation ausgew\u00e4hlt",
+ "no-session-data": "Keine Sitzungsdaten zum Editieren. Bitte erneut beginnen.",
"remote-timeout": "Konnte Ressource {{0}} nicht herunterladen ({{1}})",
"replacing-config": "Ersetzen von Konfiguration {{0}}",
"replacing-module": "Ersetzen von Modul {{0}}",
+ "shib-list-update-failed": "Aktualisierung der IdP-\/Organisationsliste fehlgeschlagen",
"unsuccessful-action": "Nicht erfolgreich",
"upload-failed": "Upload schlug fehl: {{0}}"
} \ No newline at end of file
diff --git a/modules-available/sysconfig/lang/de/module.json b/modules-available/sysconfig/lang/de/module.json
index 4bc1642f..e17e7cb4 100644
--- a/modules-available/sysconfig/lang/de/module.json
+++ b/modules-available/sysconfig/lang/de/module.json
@@ -1,5 +1,4 @@
{
- "config-module": "Konfigurationsmodul",
"lang_configurationCompilation": "Konfiguration zusammenstellen",
"lang_contentOf": "Inhalt von",
"lang_moduleAdd": "Modul hinzuf\u00fcgen",
diff --git a/modules-available/sysconfig/lang/de/template-tags.json b/modules-available/sysconfig/lang/de/template-tags.json
index 16b2c672..8d3e5d25 100644
--- a/modules-available/sysconfig/lang/de/template-tags.json
+++ b/modules-available/sysconfig/lang/de/template-tags.json
@@ -117,6 +117,7 @@
"lang_screenTextInherit": "Werte Erben",
"lang_screenUnlocked": "Bildschirmschoner",
"lang_searchBase": "Suchbasis",
+ "lang_selectDeselectAll": "Alle an-\/abw\u00e4hlen",
"lang_selectFile": "Bitte w\u00e4hlen Sie ein Archiv",
"lang_selectHomeAttribute": "Home-Attribut",
"lang_selfSignedNote": "Das Zertifikat des Servers scheint selbst signiert zu sein. Wenn Sie fortfahren wird versucht, die Zertifikatskette vom Server abzufragen. Dies ist in den meisten F\u00e4llen erfolgreich, sollte aber nur getan werden wenn Sie wissen, dass das Zertifikat des Servers von einer unbekannten CA signiert wurde. Falls die Authentifizierung anschlie\u00dfend nicht funktioniert, \u00fcberpr\u00fcfen Sie die LDAP-Proxy Logs auf der Serverstatus-Seite.",
@@ -132,6 +133,11 @@
"lang_shareModeNote": "\"Nativer Modus mit Fallback auf VMware\" ist experimentell und kann dazu f\u00fchren, dass die VM in regelm\u00e4\u00dfigen Abst\u00e4nden H\u00e4nger hat.",
"lang_shareOther": "Andere (Saved Games, Kontakte, Favoriten, ...)",
"lang_shareRemapMode": "Einbindemodus",
+ "lang_shibEnabledMethods": "Aktivierte Anmeldemethoden",
+ "lang_shibIntroText": "Hier k\u00f6nnen Sie die Anmeldung am Client mittels Single-Sign-on (SSO, z.B. Shibboleth) aktivieren. Dies erm\u00f6glicht einen Login via Browser, oder QR-Code. Dadurch wird z.B. die Nutzung von 2FA m\u00f6glich, sofern dies durch den genutzten IdP umgesetzt wird.\r\nBitte beachten Sie, dass es zur Zeit nicht m\u00f6glich ist, automatisiert ein Home-Verzeichnis f\u00fcr den Nutzer einzubinden, da die erforderlichen Daten beim Anmeldevorgang nicht \u00fcbertragen werden.",
+ "lang_shibSelectOrgs": "Organisationen ausw\u00e4hlen, denen die Anmeldung gew\u00e4hrt wird.",
+ "lang_shibUseBrowser": "Anmeldung durch Browser im Login-Screen erlauben",
+ "lang_shibUseQrCode": "Anmeldung durch das Scannen eines QR-Codes erlauben",
"lang_show": "Ansehen",
"lang_showLong": "Inhalt des Moduls anzeigen.",
"lang_skip": "Weiter",
diff --git a/modules-available/sysconfig/lang/en/config-module.json b/modules-available/sysconfig/lang/en/config-module.json
index d4e1a8cc..bd7f1803 100644
--- a/modules-available/sysconfig/lang/en/config-module.json
+++ b/modules-available/sysconfig/lang/en/config-module.json
@@ -12,11 +12,13 @@
"group_sshconfig": "SSH config",
"group_sshkey": "SSH key",
"ldapAuth_description": "This module enables you to create a simple LDAP authentication module.",
- "ldapAuth_title": "LDAP Authentication",
- "screensaver_title": "Screensaver customization",
+ "ldapAuth_title": "LDAP authentication",
"screensaver_description": "With this module you can customize the style (QSS) and texts of the screensaver.",
+ "screensaver_title": "Screensaver customization",
+ "shibauth_description": "Using this module you can enable Shibboleth\/SSO login on the client.",
+ "shibauth_title": "Shibboleth authentication",
"sshconfig_description": "Here you can set whether the sshd on the clients will start, and what options it will use.",
"sshconfig_title": "SSH daemon",
- "sshkey_title": "SSH key",
- "sshkey_description": "Add a public key to the authorized_keys file of the root user. You can then use the according private key to log in on a running client as root via SSH. root login needs to be enabled in the according SSH daemon module."
-}
+ "sshkey_description": "Add a public key to the authorized_keys file of the root user. You can then use the according private key to log in on a running client as root via SSH. root login needs to be enabled in the according SSH daemon module.",
+ "sshkey_title": "SSH key"
+} \ No newline at end of file
diff --git a/modules-available/sysconfig/lang/en/messages.json b/modules-available/sysconfig/lang/en/messages.json
index d34e4bfa..e74a0e61 100644
--- a/modules-available/sysconfig/lang/en/messages.json
+++ b/modules-available/sysconfig/lang/en/messages.json
@@ -14,9 +14,12 @@
"module-rebuilt": "Module was rebuilt",
"no-image": "Could not find an SVG-image at the given URL",
"no-noconfig-active": "No system configuration created\/selected yet.",
+ "no-organization-selected": "No irganization selected",
+ "no-session-data": "No session data, please start over",
"remote-timeout": "Could not download resource {{0}} ({{1}})",
"replacing-config": "Replace config {{0}}",
"replacing-module": "Replace module {{0}}",
+ "shib-list-update-failed": "Updating IdP\/organization list failed",
"unsuccessful-action": "Not successful",
"upload-failed": "Upload failed: {{0}}"
} \ No newline at end of file
diff --git a/modules-available/sysconfig/lang/en/module.json b/modules-available/sysconfig/lang/en/module.json
index dbfacdb8..6daf799b 100644
--- a/modules-available/sysconfig/lang/en/module.json
+++ b/modules-available/sysconfig/lang/en/module.json
@@ -1,5 +1,4 @@
{
- "config-module": "Config module",
"lang_configurationCompilation": "Compile configuration",
"lang_contentOf": "Content of",
"lang_moduleAdd": "Add Module",
diff --git a/modules-available/sysconfig/lang/en/template-tags.json b/modules-available/sysconfig/lang/en/template-tags.json
index eddd03d4..cf9d8873 100644
--- a/modules-available/sysconfig/lang/en/template-tags.json
+++ b/modules-available/sysconfig/lang/en/template-tags.json
@@ -117,6 +117,7 @@
"lang_screenTextInherit": "Inherit Values",
"lang_screenUnlocked": "Screensaver",
"lang_searchBase": "Search Base",
+ "lang_selectDeselectAll": "(De)select all",
"lang_selectFile": "Please select an archive",
"lang_selectHomeAttribute": "Home attribute",
"lang_selfSignedNote": "The certificate of this server cannot be verified using the builtin trust store. If you know that the server's certificate was signed by an unknown CA, you can try to proceed. The chain will then be extracted from the server, which should be successful in most cases. If the authentication module does not work afterwards, check the LDAP-proxy logs on the server status page.",
@@ -132,6 +133,11 @@
"lang_shareModeNote": "\"Native mode with fallback\" is experimental and known to cause temporary freezes with some VMs. Use with care.",
"lang_shareOther": "Other (Saved Games, Contacts, Favorites, ...)",
"lang_shareRemapMode": "Mapping mode",
+ "lang_shibEnabledMethods": "Enabled login methods",
+ "lang_shibIntroText": "Here you can activate client login using single sign-on (SSO, e.g. Shibboleth). This enables login via browser or QR code. This makes it possible to use 2FA, for example, provided that this is implemented by the IdP being used. Please note that this makes it impossible to automatically mount the user's home directory, as the according meta-data is not provided.",
+ "lang_shibSelectOrgs": "Select organizations that should be allowed to login.",
+ "lang_shibUseBrowser": "Allow logging in via browser embedded in login screen",
+ "lang_shibUseQrCode": "Allow logging in by scanning a QR code",
"lang_show": "Show",
"lang_showLong": "Show content of module.",
"lang_skip": "Next",
diff --git a/modules-available/sysconfig/page.inc.php b/modules-available/sysconfig/page.inc.php
index b11f399e..9f816e30 100644
--- a/modules-available/sysconfig/page.inc.php
+++ b/modules-available/sysconfig/page.inc.php
@@ -461,7 +461,8 @@ class Page_SysConfig extends Page
*/
protected function doAjax()
{
- if (Request::post('action') === 'status') {
+ $action = Request::any('action', '', 'string');
+ if ($action === 'status') {
$mods = Request::post('mods');
$confs = Request::post('confs');
$mods = explode(',', $mods);
@@ -475,6 +476,12 @@ class Page_SysConfig extends Page
Header('Content-Type: application/json');
die(json_encode(array('mods' => $outMods, 'confs' => $outConfs)));
}
+ if ($action === 'addmodule') {
+ User::load();
+ User::assertPermission('module.edit');
+ $this->initAddModule();
+ AddModule_Base::ajax();
+ }
}
}
diff --git a/modules-available/sysconfig/templates/shibauth-orgs.html b/modules-available/sysconfig/templates/shibauth-orgs.html
new file mode 100644
index 00000000..98ebe5d1
--- /dev/null
+++ b/modules-available/sysconfig/templates/shibauth-orgs.html
@@ -0,0 +1,56 @@
+<form role="form" method="post" action="?do=SysConfig&amp;action=addmodule&amp;step={{next}}">
+<input type="hidden" name="token" value="{{token}}">
+<input type="hidden" name="edit" value="{{edit}}">
+
+<div>{{lang_shibSelectOrgs}}</div>
+<div class="slx-space"></div>
+
+{{#list}}
+
+ <div id="frame_{{reghash}}" class="panel panel-default">
+ <div class="panel-heading">
+ <div class="panel-title">
+ <a class="collapsed" data-toggle="collapse" data-parent="#accordion" href="#panel_{{reghash}}">
+ &downarrow; {{registrar}}
+ </a>
+ </div>
+ </div>
+ <div id="panel_{{reghash}}" class="panel-collapse collapse">
+ <div class="panel-body">
+ <div>
+ <button type="button" class="btn btn-sm btn-default reg-group-toggle">
+ <span class="glyphicon glyphicon-check"></span>
+ {{lang_selectDeselectAll}}
+ </button>
+ </div>
+ <div class="reg-group">
+ {{#list}}
+ <div class="checkbox">
+ <input type="checkbox" name="idp[]" value="{{id}}" id="ch_{{idphash}}" {{checked}}>
+ <label for="ch_{{idphash}}">{{name}}</label>
+ </div>
+ {{/list}}
+ </div>
+ </div>
+ </div>
+ </div>
+
+{{/list}}
+
+<div class="pull-right">
+ <button type="submit" class="btn btn-primary">{{lang_next}} &raquo;</button>
+</div>
+<div class="clearfix"></div>
+</form>
+
+<script>
+ document.addEventListener('DOMContentLoaded', function() {
+ $('.reg-group-toggle').click(function() {
+ var $c = $(this);
+ var $checks = $c.closest('.panel-body').find('.reg-group .checkbox input');
+ var cc = $checks.filter('input:checked').length;
+ console.log($checks.length, cc);
+ $checks.prop('checked', cc < ($checks.length - cc));
+ });
+ });
+</script> \ No newline at end of file
diff --git a/modules-available/sysconfig/templates/shibauth-start.html b/modules-available/sysconfig/templates/shibauth-start.html
new file mode 100644
index 00000000..c98f694e
--- /dev/null
+++ b/modules-available/sysconfig/templates/shibauth-start.html
@@ -0,0 +1,45 @@
+<form role="form" method="post" action="?do=SysConfig&amp;action=addmodule&amp;step={{next}}">
+<input type="hidden" name="token" value="{{token}}">
+<input type="hidden" name="edit" value="{{edit}}">
+
+<div>{{lang_shibIntroText}}</div>
+<div class="slx-space"></div>
+
+<div class="input-group">
+ <span class="input-group-addon">{{lang_moduleTitle}}</span>
+ <input tabindex="1" name="title" value="{{title}}" type="text" class="form-control" autofocus required>
+</div>
+<div class="slx-space"></div>
+{{lang_shibEnabledMethods}}
+<div class="input-group">
+ <div class="checkbox">
+ <input type="checkbox" id="cbbrowser" name="browser" {{#browser}}checked{{/browser}}>
+ <label for="cbbrowser">
+ {{lang_shibUseBrowser}}
+ </label>
+ </div>
+</div>
+<div class="input-group">
+ <div class="checkbox">
+ <input type="checkbox" id="cbqrcode" name="qrcode" {{#qrcode}}checked{{/qrcode}}>
+ <label for="cbqrcode">
+ {{lang_shibUseQrCode}}
+ </label>
+ </div>
+</div>
+
+<div class="slx-space"></div>
+
+<div id="idp-update"></div>
+
+<div class="pull-right">
+ <button type="submit" class="btn btn-primary">{{lang_next}} &raquo;</button>
+</div>
+<div class="clearfix"></div>
+</form>
+
+<script>
+ document.addEventListener('DOMContentLoaded', function() {
+ $('#idp-update').load('?do=SysConfig&action=addmodule&step=ShibAuth_Start');
+ });
+</script> \ No newline at end of file