Allow to configure multiple SAML providers

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl 2018-05-13 19:14:05 +02:00 committed by Bjoern Schiessle
parent 9cdcae4bd5
commit ee5308382b
No known key found for this signature in database
GPG key ID: 2378A753E2BF04F6
6 changed files with 238 additions and 14 deletions

View file

@ -58,5 +58,13 @@ return [
'url' => '/saml/selectUserBackEnd',
'verb' => 'GET',
],
[
'name' => 'Settings#getSamlProviderSettings',
'url' => '/settings/providerSettings/{providerId}',
'verb' => 'GET',
'defaults' => [
'providerId' => '1'
]
],
],
];

View file

@ -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;
}

View file

@ -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) {
$('<li data-id="' + nextId + '"><a>' + t('user_saml', 'Provider') + ' ' + nextId + '</a></li>').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) {

View file

@ -0,0 +1,81 @@
<?php
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}

View file

@ -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);

View file

@ -10,6 +10,29 @@ style('user_saml', 'admin');
title="<?php p($l->t('Open documentation'));?>"
href="<?php p(link_to_docs('admin-sso')); ?>"></a>
<div class="warning hidden" id="user-saml-warning-admin-user">
<?php p(
$l->t(
'Make sure to configure an administrative user that can access the instance via SSO. Logging-in with your regular %s account won\'t be possible anymore, unless you enabled "%s"',
[
$theme->getEntity(),
$_['general']['allow_multiple_user_back_ends']['text']
]
)
)
?>
</div>
<ul class="account-list">
<?php foreach ($_['providers'] as $id => $name) { ?>
<li data-id="<?php p($id); ?>" class="<?php if ((string)$id === '1') { p('active'); } ?>">
<a href="#"><?php p($name); ?></a>
</li>
<?php } ?>
<li class="add-provider"><a href="#" class="button"><span class="icon-add"></span> Add another account</a></li>
</ul>
<div id="user-saml-save-indicator" class="msg success inlineblock" style="display: none;">Saved</div>
<div id="user-saml-settings">
@ -20,18 +43,6 @@ style('user_saml', 'admin');
<button id="user-saml-choose-env"><?php p($l->t('Use environment variable')) ?></button>
</div>
<div class="warning hidden" id="user-saml-warning-admin-user">
<?php p(
$l->t(
'Make sure to configure an administrative user that can access the instance via SSO. Logging-in with your regular %s account won\'t be possible anymore, unless you enabled "%s"',
[
$theme->getEntity(),
$_['general']['allow_multiple_user_back_ends']['text']
]
)
)
?>
</div>
<div id="user-saml-general">
<h3><?php p($l->t('General')) ?></h3>