From ee5308382b15d6bb0a4bd32a94908f1f9db74c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20H=C3=A4rtl?= Date: Sun, 13 May 2018 19:14:05 +0200 Subject: [PATCH] Allow to configure multiple SAML providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Härtl --- appinfo/routes.php | 8 +++ css/admin.css | 21 +++++- js/admin.js | 99 ++++++++++++++++++++++++++- lib/Controller/SettingsController.php | 81 ++++++++++++++++++++++ lib/Settings/Admin.php | 8 +++ templates/admin.php | 35 ++++++---- 6 files changed, 238 insertions(+), 14 deletions(-) create mode 100644 lib/Controller/SettingsController.php diff --git a/appinfo/routes.php b/appinfo/routes.php index f1d3bfc..e3462f6 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -58,5 +58,13 @@ return [ 'url' => '/saml/selectUserBackEnd', 'verb' => 'GET', ], + [ + 'name' => 'Settings#getSamlProviderSettings', + 'url' => '/settings/providerSettings/{providerId}', + 'verb' => 'GET', + 'defaults' => [ + 'providerId' => '1' + ] + ], ], ]; diff --git a/css/admin.css b/css/admin.css index 85c62c0..1d17762 100644 --- a/css/admin.css +++ b/css/admin.css @@ -26,6 +26,25 @@ clear: both; padding: 7px 0; cursor: pointer; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; opacity: .5; } + +#user-saml .account-list { + margin: 10px 0 10px 0; + overflow:hidden; + padding: 10px 0 10px 0; +} +#user-saml .account-list li { + float: left; +} + +#user-saml .account-list li a:not(.button) { + padding: 7px; +} +#user-saml .account-list li a.button { + margin-left: 20px; +} +#user-saml .account-list li.active a { + border-bottom: 1px solid #333; + font-weight: bold; +} \ No newline at end of file diff --git a/js/admin.js b/js/admin.js index 26d091c..bfc19d8 100644 --- a/js/admin.js +++ b/js/admin.js @@ -5,6 +5,22 @@ * @namespace OCA.User_SAML.Admin */ OCA.User_SAML.Admin = { + currentConfig: '1', + providerIds: '1', + + _getAppConfig: function (key) { + return $.ajax({ + type: 'GET', + url: OC.linkToOCS('apps/provisioning_api/api/v1', 2) + 'config/apps' + '/user_saml/' + key + '?format=json' + }); + }, + init: function() { + this._getAppConfig('providerIds').done(function (data){ + if (data.ocs.data.data !== '') { + OCA.User_SAML.Admin.providerIds = data.ocs.data.data; + } + }); + }, chooseEnv: function() { if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.chooseEnv, this)); @@ -23,6 +39,32 @@ OCP.AppConfig.setValue('user_saml', 'type', 'saml', {success: function() {location.reload();}}); }, + getConfigIdentifier: function() { + if (this.currentConfig === '1') { + return ''; + } + return this.currentConfig + '-'; + }, + + /** + * Add a new provider + * @returns {number} id of the provider + */ + addProvider: function(callback) { + var providerIds = OCA.User_SAML.Admin.providerIds.split(','); + var nextId = 2; + while($.inArray('' + nextId, providerIds) >= 0) { + nextId++; + } + console.log(nextId); + OCP.AppConfig.setValue('user_saml', 'providerIds', OCA.User_SAML.Admin.providerIds + ',' + nextId, { + success: function () { + OCA.User_SAML.Admin.providerIds += ',' + nextId; + callback(nextId) + } + }); + }, + setSamlConfigValue: function(category, setting, value) { if (OC.PasswordConfirmation.requiresPasswordConfirmation()) { OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this.setSamlConfigValue, this, category, setting, value)); @@ -30,13 +72,14 @@ } OC.msg.startSaving('#user-saml-save-indicator'); - OC.AppConfig.setValue('user_saml', category+'-'+setting, value); + OCP.AppConfig.setValue('user_saml', this.getConfigIdentifier() + category + '-' + setting, value); OC.msg.finishedSaving('#user-saml-save-indicator', {status: 'success', data: {message: t('user_saml', 'Saved')}}); } } })(OCA); $(function() { + OCA.User_SAML.Admin.init(); // Hide depending on the setup state var type = $('#user-saml').data('type'); if(type !== '') { @@ -68,6 +111,53 @@ $(function() { } }); + var switchProvider = function(providerId) { + $('.account-list li').removeClass('active'); + $('.account-list li[data-id="' + providerId + '"]').addClass('active'); + OCA.User_SAML.Admin.currentConfig = providerId; + $.get(OC.generateUrl('/apps/user_saml/settings/providerSettings/' + providerId)).done(function(data) { + Object.keys(data).forEach(function(category, index){ + var entries = data[category]; + Object.keys(entries).forEach(function (configKey) { + var element = $('*[data-key="' + configKey + '"]'); + if ($('#user-saml-' + configKey).length) { + element = $('#user-saml-' + configKey); + } + if ($('[name="' + configKey + '"]').length) { + element = $('[name="' + configKey + '"]'); + } + if(element.is('input') && element.prop('type') === 'text') { + element.val(entries[configKey]) + } + else if(element.is('textarea')) { + element.val(entries[configKey]); + } + else if(element.prop('type') === 'checkbox') { + var value = entries[configKey] === '1' ? '1' : '0'; + element.val(value); + } else { + console.log('unable to find element for ' + configKey); + } + }); + }); + $('input:checkbox[value="1"]').attr('checked', true); + $('input:checkbox[value="0"]').attr('checked', false); + }); + }; + + $('.account-list').on('click', 'li:not(.add-provider)', function() { + var providerId = '' + $(this).data('id'); + switchProvider(providerId); + }); + + $('.account-list .add-provider').on('click', function() { + OCA.User_SAML.Admin.addProvider(function (nextId) { + $('
  • ' + t('user_saml', 'Provider') + ' ' + nextId + '
  • ').insertBefore('.account-list .add-provider'); + switchProvider(nextId); + }); + }); + + // Enable tabs $('input:checkbox[value="1"]').attr('checked', true); @@ -105,6 +195,13 @@ $(function() { var key = $(this).attr('name'); OCA.User_SAML.Admin.setSamlConfigValue('general', key, $(this).val()); } + if(el.data('key') === 'idp0_display_name') { + if ($(this).val() !== '') { + $('.account-list li[data-id=' + OCA.User_SAML.Admin.currentConfig + '] a').text($(this).val()) + } else { + $('.account-list li[data-id=' + OCA.User_SAML.Admin.currentConfig + '] a').text(t('user_saml', 'Provider') + ' ' + OCA.User_SAML.Admin.currentConfig); + } + } }); $('#user-saml-general input[type="checkbox"]').change(function(e) { diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php new file mode 100644 index 0000000..5f01764 --- /dev/null +++ b/lib/Controller/SettingsController.php @@ -0,0 +1,81 @@ + + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\User_SAML\Controller; + +use OCA\User_SAML\Settings\Admin; +use OCP\AppFramework\Controller; +use OCP\IConfig; +use OCP\IRequest; + +class SettingsController extends Controller { + + /** @var IConfig */ + private $config; + /** @var Admin */ + private $admin; + + public function __construct($appName, + IRequest $request, + IConfig $config, + Admin $admin) { + parent::__construct($appName, $request); + $this->config = $config; + $this->admin = $admin; + } + + /** + * @param $providerId + * @return array of categories containing entries for each config parameter with their value + */ + public function getSamlProviderSettings($providerId) { + /** + * This uses the list of available config parameters from the admin section + * and extends it with fields that are not coming from \OCA\User_SAML\Settings\Admin + */ + $params = $this->admin->getForm()->getParams(); + $params['idp'] = [ + 'singleLogoutService.url' => null, + 'singleSignOnService.url' => null, + 'idp-entityId' => null, + ]; + /* Fetch all config values for the given providerId */ + $settings = []; + foreach ($params as $category => $content) { + if (empty($content) || $category === 'providers') { + continue; + } + foreach ($content as $setting => $details) { + $prefix = $providerId === '1' ? '' : $providerId . '-'; + $key = $prefix . $category . '-' . $setting; + /* use security as category instead of security-* */ + if (strpos($category, 'security-') === 0) { + $category = 'security'; + } + $settings[$category][$setting] = $this->config->getAppValue('user_saml', $key, ''); + } + } + return $settings; + } + +} diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 919f4bd..56f8ce2 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -54,6 +54,13 @@ class Admin implements ISettings { * @return TemplateResponse */ public function getForm() { + $providerIds = explode(',', $this->config->getAppValue('user_saml', 'providerIds', '1')); + $providers = []; + foreach ($providerIds as $id) { + $prefix = $id === '1' ? '' : $id .'-'; + $name = $this->config->getAppValue('user_saml', $prefix . 'general-idp0_display_name', ''); + $providers[$id] = $name === '' ? $this->l10n->t('Provider ') . $id : $name; + } $serviceProviderFields = [ 'x509cert' => $this->l10n->t('X.509 certificate of the Service Provider'), 'privateKey' => $this->l10n->t('Private key of the Service Provider'), @@ -135,6 +142,7 @@ class Admin implements ISettings { 'general' => $generalSettings, 'attributeMappings' => $attributeMappingSettings, 'type' => $type, + 'providers' => $providers ]; return new TemplateResponse('user_saml', 'admin', $params); diff --git a/templates/admin.php b/templates/admin.php index 78a659d..fa8c18d 100644 --- a/templates/admin.php +++ b/templates/admin.php @@ -10,6 +10,29 @@ style('user_saml', 'admin'); title="t('Open documentation'));?>" href=""> + + + + +
    @@ -20,18 +43,6 @@ style('user_saml', 'admin');
    -

    t('General')) ?>