mirror of
https://github.com/nextcloud/server.git
synced 2026-02-19 02:38:40 -05:00
Merge pull request #48065 from nextcloud/artonge/chore/ldap_wizard_rewrite
refactor(user_ldap): Rewrite setup wizard
This commit is contained in:
commit
0c6478add3
119 changed files with 2027 additions and 5506 deletions
|
|
@ -32,6 +32,8 @@ module.exports = {
|
|||
ignores: ['/^[a-z]+(?:-[a-z]+)*:[a-z]+(?:-[a-z]+)*$/u'],
|
||||
}],
|
||||
'vue/html-self-closing': 'error',
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'jsdoc/require-param-description': 'off',
|
||||
},
|
||||
settings: {
|
||||
jsdoc: {
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@
|
|||
|
||||
<NcAppSettingsSection id="shortcuts"
|
||||
:name="t('files', 'Keyboard shortcuts')">
|
||||
|
||||
<h3>{{ t('files', 'Actions') }}</h3>
|
||||
<dl>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -23,12 +23,6 @@ $this->create('user_ldap_ajax_wizard', 'apps/user_ldap/ajax/wizard.php')
|
|||
->actionInclude('user_ldap/ajax/wizard.php');
|
||||
|
||||
return [
|
||||
'ocs' => [
|
||||
['name' => 'ConfigAPI#create', 'url' => '/api/v1/config', 'verb' => 'POST'],
|
||||
['name' => 'ConfigAPI#show', 'url' => '/api/v1/config/{configID}', 'verb' => 'GET'],
|
||||
['name' => 'ConfigAPI#modify', 'url' => '/api/v1/config/{configID}', 'verb' => 'PUT'],
|
||||
['name' => 'ConfigAPI#delete', 'url' => '/api/v1/config/{configID}', 'verb' => 'DELETE'],
|
||||
],
|
||||
'routes' => [
|
||||
['name' => 'renewPassword#tryRenewPassword', 'url' => '/renewpassword', 'verb' => 'POST'],
|
||||
['name' => 'renewPassword#showRenewPasswordForm', 'url' => '/renewpassword/{user}', 'verb' => 'GET'],
|
||||
|
|
|
|||
|
|
@ -1,226 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2012-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
.table {
|
||||
display: table;
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.inlinetable {
|
||||
display: inline-table;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.tablerow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.tablerow input, .tablerow textarea {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.tablerow textarea {
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
#ldap .tablerow label {
|
||||
margin-inline-start: 3px;
|
||||
}
|
||||
|
||||
.ldapIconCopy {
|
||||
background-image: url('../img/copy.svg');
|
||||
}
|
||||
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.forceHidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ldapSettingsTabs {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.ldapWizardControls {
|
||||
width: 60%;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.ldapWizardInfo {
|
||||
width: 100% !important;
|
||||
height: 50px;
|
||||
background-color: lightyellow;
|
||||
border-radius: 8px;
|
||||
padding: 10px 8px 6px !important;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#ldapWizard1 .hostPortCombinator {
|
||||
width: 60%;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#ldapWizard1 .hostPortCombinatorSpan {
|
||||
width: 14.5%;
|
||||
display: inline-block;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
#ldapWizard1 .host {
|
||||
width: 100%;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
.tableCellInput {
|
||||
margin-inline-start: -40%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tableCellLabel {
|
||||
text-align: end;
|
||||
padding-inline-end: 25%;
|
||||
}
|
||||
|
||||
.ldapIndent {
|
||||
margin-inline-start: 50px;
|
||||
}
|
||||
|
||||
.ldapwarning {
|
||||
margin-inline-start: 22px;
|
||||
color: #FF3B3B;
|
||||
}
|
||||
|
||||
.ldapSpinner {
|
||||
height: 15px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.ldap_count {
|
||||
line-height: 45px;
|
||||
}
|
||||
|
||||
.ldapSettingControls {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
#ldap fieldset p label {
|
||||
width: 20%;
|
||||
max-width: 200px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
text-align: end;
|
||||
padding-top: 9px;
|
||||
padding-inline-end: 5px;
|
||||
}
|
||||
|
||||
#ldap fieldset input[type=submit] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.ldapManyGroupsSupport .buttonSpan {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.ldapManyGroupsSupport .buttonSpan button {
|
||||
margin-top: 35px;
|
||||
}
|
||||
|
||||
.ldapManyGroupsSearch {
|
||||
width: 425px !important;
|
||||
}
|
||||
|
||||
.ldapGroupList {
|
||||
height: 150px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#ldap fieldset input, #ldap fieldset textarea {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
#ldap fieldset textarea ~ button {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
input.ldapVerifyInput {
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
.ldapInputColElement {
|
||||
width: 35%;
|
||||
display: inline-block;
|
||||
padding-inline-start: 10px;
|
||||
}
|
||||
|
||||
.ldapToggle {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
span.ldapInputColElement {
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
#ldap fieldset p input[type=checkbox] {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
#ldap input[type=checkbox] {
|
||||
width: 15px !important;
|
||||
}
|
||||
|
||||
select[multiple=multiple] + button {
|
||||
height: 28px;
|
||||
padding-top: 6px !important;
|
||||
min-width: 40%;
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
.save-cursor {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
#ldap .ldap_saving {
|
||||
margin-inline-end: 15px;
|
||||
color: orange;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#ldap .ldap_saving img { height: 15px; }
|
||||
|
||||
.ldap_config_state_indicator_sign {
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.ldap_config_state_indicator_sign.success {
|
||||
background: #37ce02;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.ldap_config_state_indicator_sign.error {
|
||||
background: #ce3702;
|
||||
}
|
||||
|
||||
.ldap_grey {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
#ldapSettings {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.ui-multiselect-checkboxes label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -1,606 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc this class represents a server configuration. It communicates
|
||||
* with the Nextcloud server to ensure to always have the up to date LDAP
|
||||
* configuration. It sends various events that views can listen to and
|
||||
* provides methods so they can modify the configuration based upon user
|
||||
* input. This model is also extended by so-called "detectors" who let the
|
||||
* Nextcloud server try to auto-detect settings and manipulate the
|
||||
* configuration as well.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var ConfigModel = function() {};
|
||||
|
||||
ConfigModel.prototype = {
|
||||
/** @constant {number} */
|
||||
FILTER_MODE_ASSISTED: 0,
|
||||
/** @constant {number} */
|
||||
FILTER_MODE_RAW: 1,
|
||||
|
||||
/**
|
||||
* initializes the instance. Always call it after creating the instance.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.WizardDetectorQueue} detectorQueue
|
||||
*/
|
||||
init: function (detectorQueue) {
|
||||
/** @type {object} holds the configuration in key-value-pairs */
|
||||
this.configuration = {};
|
||||
/** @type {object} holds the subscribers that listen to the events */
|
||||
this.subscribers = {};
|
||||
/** @type {Array} holds registered detectors */
|
||||
this.detectors = [];
|
||||
/** @type {boolean} whether a configuration is currently loading */
|
||||
this.loadingConfig = false;
|
||||
|
||||
if(detectorQueue instanceof OCA.LDAP.Wizard.WizardDetectorQueue) {
|
||||
/** @type {OCA.LDAP.Wizard.WizardDetectorQueue} */
|
||||
this.detectorQueue = detectorQueue;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* loads a specified configuration
|
||||
*
|
||||
* @param {string} [configID] - the configuration id (or prefix)
|
||||
*/
|
||||
load: function (configID) {
|
||||
if(this.loadingConfig) {
|
||||
return;
|
||||
}
|
||||
this._resetDetectorQueue();
|
||||
|
||||
this.configID = configID;
|
||||
var url = OC.generateUrl('apps/user_ldap/ajax/getConfiguration.php');
|
||||
var params = OC.buildQueryString({ldap_serverconfig_chooser: configID});
|
||||
this.loadingConfig = true;
|
||||
var model = this;
|
||||
$.post(url, params, function (result) { model._processLoadConfig(model, result) });
|
||||
},
|
||||
|
||||
/**
|
||||
* creates a new LDAP configuration
|
||||
*
|
||||
* @param {boolean} [copyCurrent] - if true, the current configuration
|
||||
* is copied, otherwise a blank one is created.
|
||||
*/
|
||||
newConfig: function(copyCurrent) {
|
||||
this._resetDetectorQueue();
|
||||
|
||||
var url = OC.generateUrl('apps/user_ldap/ajax/getNewServerConfigPrefix.php');
|
||||
var params = {};
|
||||
if(copyCurrent === true) {
|
||||
params['copyConfig'] = this.configID;
|
||||
}
|
||||
params = OC.buildQueryString(params);
|
||||
var model = this;
|
||||
copyCurrent = _.isUndefined(copyCurrent) ? false : copyCurrent;
|
||||
$.post(url, params, function (result) { model._processNewConfigPrefix(model, result, copyCurrent) });
|
||||
},
|
||||
|
||||
/**
|
||||
* deletes the current configuration. This method will not ask for
|
||||
* confirmation, if desired it needs to be ensured by the caller.
|
||||
*
|
||||
* @param {string} [configID] - the configuration id (or prefix)
|
||||
*/
|
||||
deleteConfig: function(configID) {
|
||||
var url = OC.generateUrl('apps/user_ldap/ajax/deleteConfiguration.php');
|
||||
var params = OC.buildQueryString({ldap_serverconfig_chooser: configID});
|
||||
var model = this;
|
||||
$.post(url, params, function (result) { model._processDeleteConfig(model, result, configID) });
|
||||
},
|
||||
|
||||
/**
|
||||
* @callback wizardCallBack
|
||||
* @param {ConfigModel} [model]
|
||||
* @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
|
||||
* @param {object} [result] - response from the ajax request
|
||||
*/
|
||||
|
||||
/**
|
||||
* calls an AJAX endpoint at Nextcloud. This method should be called by
|
||||
* detectors only!
|
||||
*
|
||||
* @param {string} [params] - as return by OC.buildQueryString
|
||||
* @param {wizardCallBack} [callback]
|
||||
* @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
|
||||
* @returns {jqXHR}
|
||||
*/
|
||||
callWizard: function(params, callback, detector) {
|
||||
return this.callAjax('wizard.php', params, callback, detector);
|
||||
},
|
||||
|
||||
/**
|
||||
* calls an AJAX endpoint at Nextcloud. This method should be called by
|
||||
* detectors only!
|
||||
*
|
||||
* @param {string} destination - the desired end point
|
||||
* @param {string} [params] - as return by OC.buildQueryString
|
||||
* @param {wizardCallBack} [callback]
|
||||
* @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
|
||||
* @returns {jqXHR}
|
||||
*/
|
||||
callAjax: function(destination, params, callback, detector) {
|
||||
var url = OC.generateUrl('apps/user_ldap/ajax/' + destination);
|
||||
var model = this;
|
||||
return $.post(url, params, function (result) {
|
||||
callback(model, detector,result);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* setRequested Event
|
||||
*
|
||||
* @event ConfigModel#setRequested
|
||||
* @type{object} - empty
|
||||
*/
|
||||
|
||||
/**
|
||||
* modifies a configuration key. If a provided configuration key does
|
||||
* not exist or the provided value equals the current setting, false is
|
||||
* returned. Otherwise Nextcloud server will be called to save the new
|
||||
* value, an event will notify when this is done. True is returned when
|
||||
* the request is sent, however it does not mean whether saving was
|
||||
* successful or not.
|
||||
*
|
||||
* This method is supposed to be called by views, after the user did a
|
||||
* change which needs to be saved.
|
||||
*
|
||||
* @param {string} [key]
|
||||
* @param {string|number} [value]
|
||||
* @returns {boolean}
|
||||
* @fires {ConfigModel#setRequested}
|
||||
*/
|
||||
set: function(key, value) {
|
||||
if(_.isUndefined(this.configuration[key])) {
|
||||
console.warn('will not save undefined key: ' + key);
|
||||
return false;
|
||||
}
|
||||
if(this.configuration[key] === value) {
|
||||
return false;
|
||||
}
|
||||
this._broadcast('setRequested', {});
|
||||
var url = OC.generateUrl('apps/user_ldap/ajax/wizard.php');
|
||||
var objParams = {
|
||||
ldap_serverconfig_chooser: this.configID,
|
||||
action: 'save',
|
||||
cfgkey: key,
|
||||
cfgval: value
|
||||
};
|
||||
var strParams = OC.buildQueryString(objParams);
|
||||
var model = this;
|
||||
$.post(url, strParams, function(result) { model._processSetResult(model, result, objParams) });
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* configUpdated Event
|
||||
*
|
||||
* object property is a key-value-pair of the configuration key as index
|
||||
* and its value.
|
||||
*
|
||||
* @event ConfigModel#configUpdated
|
||||
* @type{object}
|
||||
*/
|
||||
|
||||
/**
|
||||
* updates the model's configuration data. This should be called only,
|
||||
* when a new configuration value was received from the Nextcloud server.
|
||||
* This is typically done by detectors, but never by views.
|
||||
*
|
||||
* Cancels with false if old and new values already match.
|
||||
*
|
||||
* @param {string} [key]
|
||||
* @param {string} [value]
|
||||
* @returns {boolean}
|
||||
* @fires ConfigModel#configUpdated
|
||||
*/
|
||||
update: function(key, value) {
|
||||
if(this.configuration[key] === value) {
|
||||
return false;
|
||||
}
|
||||
if(!_.isUndefined(this.configuration[key])) {
|
||||
// don't write e.g. count values to the configuration
|
||||
// they don't go as feature, yet
|
||||
this.configuration[key] = value;
|
||||
}
|
||||
var configPart = {};
|
||||
configPart[key] = value;
|
||||
this._broadcast('configUpdated', configPart);
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} FeaturePayload
|
||||
* @property {string} feature
|
||||
* @property {Array} data
|
||||
*/
|
||||
|
||||
/**
|
||||
* informs about a detected LDAP "feature" (wider sense). For examples,
|
||||
* the detected object classes for users or groups
|
||||
*
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
inform: function(payload) {
|
||||
this._broadcast('receivedLdapFeature', payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} ErrorPayload
|
||||
* @property {string} message
|
||||
* @property {string} relatedKey
|
||||
*/
|
||||
|
||||
/**
|
||||
* broadcasts an error message, if a wizard reply ended up in an error.
|
||||
* To be called by detectors.
|
||||
*
|
||||
* @param {ErrorPayload} payload
|
||||
*/
|
||||
gotServerError: function(payload) {
|
||||
this._broadcast('serverError', payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* detectionStarted Event
|
||||
*
|
||||
* @event ConfigModel#detectionStarted
|
||||
* @type{string} - the target configuration key that is being
|
||||
* auto-detected
|
||||
*/
|
||||
|
||||
/**
|
||||
* lets the model broadcast the info that a detector starts to run
|
||||
*
|
||||
* supposed to be called by detectors only
|
||||
*
|
||||
* @param {string} [key]
|
||||
* @fires ConfigModel#detectionStarted
|
||||
*/
|
||||
notifyAboutDetectionStart: function(key) {
|
||||
this._broadcast('detectionStarted', key);
|
||||
},
|
||||
|
||||
/**
|
||||
* detectionCompleted Event
|
||||
*
|
||||
* @event ConfigModel#detectionCompleted
|
||||
* @type{string} - the target configuration key that was
|
||||
* auto-detected
|
||||
*/
|
||||
|
||||
/**
|
||||
* lets the model broadcast the info that a detector run was completed
|
||||
*
|
||||
* supposed to be called by detectors only
|
||||
*
|
||||
* @param {string} [key]
|
||||
* @fires ConfigModel#detectionCompleted
|
||||
*/
|
||||
notifyAboutDetectionCompletion: function(key) {
|
||||
this._broadcast('detectionCompleted', key);
|
||||
},
|
||||
|
||||
/**
|
||||
* @callback listenerCallback
|
||||
* @param {OCA.LDAP.Wizard.WizardTabGeneric|OCA.LDAP.Wizard.WizardView} [view]
|
||||
* @param {object} [params]
|
||||
*/
|
||||
|
||||
/**
|
||||
* registers a listener to an event
|
||||
*
|
||||
* the idea is that only views listen.
|
||||
*
|
||||
* @param {string} [name] - the event name
|
||||
* @param {listenerCallback} [fn]
|
||||
* @param {OCA.LDAP.Wizard.WizardTabGeneric|OCA.LDAP.Wizard.WizardView} [context]
|
||||
*/
|
||||
on: function(name, fn, context) {
|
||||
if(_.isUndefined(this.subscribers[name])) {
|
||||
this.subscribers[name] = [];
|
||||
}
|
||||
this.subscribers[name].push({fn: fn, context: context});
|
||||
},
|
||||
|
||||
/**
|
||||
* starts a configuration test on the Nextcloud server
|
||||
*/
|
||||
requestConfigurationTest: function() {
|
||||
var url = OC.generateUrl('apps/user_ldap/ajax/testConfiguration.php');
|
||||
var params = OC.buildQueryString({ldap_serverconfig_chooser: this.configID});
|
||||
var model = this;
|
||||
$.post(url, params, function(result) { model._processTestResult(model, result) });
|
||||
//TODO: make sure only one test is running at a time
|
||||
},
|
||||
|
||||
/**
|
||||
* the view may request a call to the wizard, for instance to fetch
|
||||
* object classes or groups
|
||||
*
|
||||
* @param {string} featureKey
|
||||
* @param {Object} [additionalParams]
|
||||
*/
|
||||
requestWizard: function(featureKey, additionalParams) {
|
||||
var model = this;
|
||||
var detectorCount = this.detectors.length;
|
||||
var found = false;
|
||||
for(var i = 0; i < detectorCount; i++) {
|
||||
if(this.detectors[i].runsOnFeatureRequest(featureKey)) {
|
||||
found = true;
|
||||
(function (detector) {
|
||||
model.detectorQueue.add(function() {
|
||||
return detector.run(model, model.configID, additionalParams);
|
||||
});
|
||||
})(model.detectors[i]);
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
console.warn('No detector found for feature ' + featureKey);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* resets the detector queue
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_resetDetectorQueue: function() {
|
||||
if(!_.isUndefined(this.detectorQueue)) {
|
||||
this.detectorQueue.reset();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* detectors can be registered herewith
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
|
||||
*/
|
||||
registerDetector: function(detector) {
|
||||
if(detector instanceof OCA.LDAP.Wizard.WizardDetectorGeneric) {
|
||||
this.detectors.push(detector);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* emits an event
|
||||
*
|
||||
* @param {string} [name] - the event name
|
||||
* @param {*} [params]
|
||||
* @private
|
||||
*/
|
||||
_broadcast: function(name, params) {
|
||||
if(_.isUndefined(this.subscribers[name])) {
|
||||
return;
|
||||
}
|
||||
var subscribers = this.subscribers[name];
|
||||
var subscriberCount = subscribers.length;
|
||||
for(var i = 0; i < subscriberCount; i++) {
|
||||
if(_.isUndefined(subscribers[i]['fn'])) {
|
||||
console.warn('callback method is not defined. Event ' + name);
|
||||
continue;
|
||||
}
|
||||
subscribers[i]['fn'](subscribers[i]['context'], params);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ConfigModel#configLoaded Event
|
||||
*
|
||||
* @event ConfigModel#configLoaded
|
||||
* @type {object} - LDAP configuration as key-value-pairs
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ConfigLoadResponse
|
||||
* @property {string} [status]
|
||||
* @property {object} [configuration] - only present if status equals 'success'
|
||||
*/
|
||||
|
||||
/**
|
||||
* processes the ajax response of a configuration load request
|
||||
*
|
||||
* @param {ConfigModel} [model]
|
||||
* @param {ConfigLoadResponse} [result]
|
||||
* @fires ConfigModel#configLoaded
|
||||
* @private
|
||||
*/
|
||||
_processLoadConfig: function(model, result) {
|
||||
model.configuration = {};
|
||||
if(result['status'] === 'success') {
|
||||
$.each(result['configuration'], function(key, value) {
|
||||
model.configuration[key] = value;
|
||||
});
|
||||
}
|
||||
model.loadingConfig = false;
|
||||
model._broadcast('configLoaded', model.configuration);
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} ConfigSetPayload
|
||||
* @property {boolean} [isSuccess]
|
||||
* @property {string} [key]
|
||||
* @property {string} [value]
|
||||
* @property {string} [errorMessage]
|
||||
*/
|
||||
|
||||
/**
|
||||
* ConfigModel#setCompleted Event
|
||||
*
|
||||
* @event ConfigModel#setCompleted
|
||||
* @type {ConfigSetPayload}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ConfigSetResponse
|
||||
* @property {string} [status]
|
||||
* @property {object} [message] - might be present only in error cases
|
||||
*/
|
||||
|
||||
/**
|
||||
* processes the ajax response of a configuration key set request
|
||||
*
|
||||
* @param {ConfigModel} [model]
|
||||
* @param {ConfigSetResponse} [result]
|
||||
* @param {object} [params] - the original changeSet
|
||||
* @fires ConfigModel#configLoaded
|
||||
* @private
|
||||
*/
|
||||
_processSetResult: function(model, result, params) {
|
||||
var isSuccess = (result['status'] === 'success');
|
||||
if(isSuccess) {
|
||||
model.configuration[params.cfgkey] = params.cfgval;
|
||||
}
|
||||
var payload = {
|
||||
isSuccess: isSuccess,
|
||||
key: params.cfgkey,
|
||||
value: model.configuration[params.cfgkey],
|
||||
errorMessage: _.isUndefined(result['message']) ? '' : result['message']
|
||||
};
|
||||
model._broadcast('setCompleted', payload);
|
||||
|
||||
// let detectors run
|
||||
// NOTE: detector's changes will not result in new _processSetResult
|
||||
// calls, … in case they interfere it is because of this ;)
|
||||
if(_.isUndefined(model.detectorQueue)) {
|
||||
console.warn("DetectorQueue was not set, detectors will not be fired");
|
||||
return;
|
||||
}
|
||||
var detectorCount = model.detectors.length;
|
||||
for(var i = 0; i < detectorCount; i++) {
|
||||
if(model.detectors[i].triggersOn(params.cfgkey)) {
|
||||
(function (detector) {
|
||||
model.detectorQueue.add(function() {
|
||||
return detector.run(model, model.configID);
|
||||
});
|
||||
})(model.detectors[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} ConfigTestPayload
|
||||
* @property {boolean} [isSuccess]
|
||||
*/
|
||||
|
||||
/**
|
||||
* ConfigModel#configurationTested Event
|
||||
*
|
||||
* @event ConfigModel#configurationTested
|
||||
* @type {ConfigTestPayload}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} StatusResponse
|
||||
* @property {string} [status]
|
||||
*/
|
||||
|
||||
/**
|
||||
* processes the ajax response of a configuration test request
|
||||
*
|
||||
* @param {ConfigModel} [model]
|
||||
* @param {StatusResponse} [result]
|
||||
* @fires ConfigModel#configurationTested
|
||||
* @private
|
||||
*/
|
||||
_processTestResult: function(model, result) {
|
||||
var payload = {
|
||||
isSuccess: (result['status'] === 'success')
|
||||
};
|
||||
model._broadcast('configurationTested', payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} BasicConfigPayload
|
||||
* @property {boolean} [isSuccess]
|
||||
* @property {string} [configPrefix] - the new config ID
|
||||
* @property {string} [errorMessage]
|
||||
*/
|
||||
|
||||
/**
|
||||
* ConfigModel#newConfiguration Event
|
||||
*
|
||||
* @event ConfigModel#newConfiguration
|
||||
* @type {BasicConfigPayload}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} NewConfigResponse
|
||||
* @property {string} [status]
|
||||
* @property {string} [configPrefix]
|
||||
* @property {object} [defaults] - default configuration values
|
||||
* @property {string} [message] - might only appear with status being
|
||||
* not 'success'
|
||||
*/
|
||||
|
||||
/**
|
||||
* processes the ajax response of a new configuration request
|
||||
*
|
||||
* @param {ConfigModel} [model]
|
||||
* @param {NewConfigResponse} [result]
|
||||
* @param {boolean} [copyCurrent]
|
||||
* @fires ConfigModel#newConfiguration
|
||||
* @fires ConfigModel#configLoaded
|
||||
* @private
|
||||
*/
|
||||
_processNewConfigPrefix: function(model, result, copyCurrent) {
|
||||
var isSuccess = (result['status'] === 'success');
|
||||
var payload = {
|
||||
isSuccess: isSuccess,
|
||||
configPrefix: result['configPrefix'],
|
||||
errorMessage: _.isUndefined(result['message']) ? '' : result['message']
|
||||
};
|
||||
model._broadcast('newConfiguration', payload);
|
||||
|
||||
if(isSuccess) {
|
||||
this.configID = result['configPrefix'];
|
||||
if(!copyCurrent) {
|
||||
model.configuration = {};
|
||||
$.each(result['defaults'], function(key, value) {
|
||||
model.configuration[key] = value;
|
||||
});
|
||||
// view / tabs need to update with new blank config
|
||||
model._broadcast('configLoaded', model.configuration);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ConfigModel#deleteConfiguration Event
|
||||
*
|
||||
* @event ConfigModel#deleteConfiguration
|
||||
* @type {BasicConfigPayload}
|
||||
*/
|
||||
|
||||
/**
|
||||
* processes the ajax response of a delete configuration request
|
||||
*
|
||||
* @param {ConfigModel} [model]
|
||||
* @param {StatusResponse} [result]
|
||||
* @param {string} [configID]
|
||||
* @fires ConfigModel#deleteConfiguration
|
||||
* @private
|
||||
*/
|
||||
_processDeleteConfig: function(model, result, configID) {
|
||||
var isSuccess = (result['status'] === 'success');
|
||||
var payload = {
|
||||
isSuccess: isSuccess,
|
||||
configPrefix: configID,
|
||||
errorMessage: _.isUndefined(result['message']) ? '' : result['message']
|
||||
};
|
||||
model._broadcast('deleteConfiguration', payload);
|
||||
}
|
||||
};
|
||||
|
||||
OCA.LDAP.Wizard.ConfigModel = ConfigModel;
|
||||
})();
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
OCA.LDAP = {};
|
||||
OCA.LDAP.Wizard = {};
|
||||
|
||||
(function(){
|
||||
|
||||
/**
|
||||
* @classdesc minimalistic controller that basically makes the view render
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardController = function() {};
|
||||
|
||||
WizardController.prototype = {
|
||||
/**
|
||||
* initializes the instance. Always call it after creating the instance.
|
||||
*/
|
||||
init: function() {
|
||||
this.view = false;
|
||||
this.configModel = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the model instance
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} [model]
|
||||
*/
|
||||
setModel: function(model) {
|
||||
this.configModel = model;
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the view instance
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.WizardView} [view]
|
||||
*/
|
||||
setView: function(view) {
|
||||
this.view = view;
|
||||
},
|
||||
|
||||
/**
|
||||
* makes the view render i.e. ready to be used
|
||||
*/
|
||||
run: function() {
|
||||
this.view.render();
|
||||
}
|
||||
};
|
||||
|
||||
OCA.LDAP.Wizard.Controller = WizardController;
|
||||
})();
|
||||
|
|
@ -1,470 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc main view class. It takes care of tab-unrelated control
|
||||
* elements (status bar, control buttons) and does or requests configuration
|
||||
* checks. It also manages the separate tab views.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardView = function() {};
|
||||
|
||||
WizardView.prototype = {
|
||||
/** @constant {number} */
|
||||
STATUS_ERROR: 0,
|
||||
/** @constant {number} */
|
||||
STATUS_INCOMPLETE: 1,
|
||||
/** @constant {number} */
|
||||
STATUS_SUCCESS: 2,
|
||||
/** @constant {number} */
|
||||
STATUS_UNTESTED: 3,
|
||||
|
||||
/**
|
||||
* initializes the instance. Always call it after creating the instance.
|
||||
*/
|
||||
init: function () {
|
||||
this.tabs = {};
|
||||
this.tabs.server = new OCA.LDAP.Wizard.WizardTabElementary();
|
||||
this.$settings = $('#ldapSettings');
|
||||
this.$saveSpinners = $('.ldap_saving');
|
||||
this.saveProcesses = 0;
|
||||
_.bindAll(this, 'onTabChange', 'onTestButtonClick');
|
||||
},
|
||||
|
||||
/**
|
||||
* applies click events to the forward and backward buttons
|
||||
*/
|
||||
initControls: function() {
|
||||
var view = this;
|
||||
$('.ldap_action_continue').click(function(event) {
|
||||
event.preventDefault();
|
||||
view._controlContinue(view);
|
||||
});
|
||||
|
||||
$('.ldap_action_back').click(function(event) {
|
||||
event.preventDefault();
|
||||
view._controlBack(view);
|
||||
});
|
||||
|
||||
$('.ldap_action_test_connection').click(this.onTestButtonClick);
|
||||
},
|
||||
|
||||
/**
|
||||
* registers a tab
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.WizardTabGeneric} tabView
|
||||
* @param {string} index
|
||||
* @returns {boolean}
|
||||
*/
|
||||
registerTab: function(tabView, index) {
|
||||
if( _.isUndefined(this.tabs[index])
|
||||
&& tabView instanceof OCA.LDAP.Wizard.WizardTabGeneric
|
||||
) {
|
||||
this.tabs[index] = tabView;
|
||||
this.tabs[index].setModel(this.configModel);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* checks certain config values for completeness and depending on them
|
||||
* enables or disables non-elementary tabs.
|
||||
*/
|
||||
basicStatusCheck: function(view) {
|
||||
var host = view.configModel.configuration.ldap_host;
|
||||
var port = view.configModel.configuration.ldap_port;
|
||||
var base = view.configModel.configuration.ldap_base;
|
||||
var agent = view.configModel.configuration.ldap_dn;
|
||||
var pwd = view.configModel.configuration.ldap_agent_password;
|
||||
|
||||
if(((host && port && base) || (host && base && host.indexOf('ldapi://') > -1 ))
|
||||
&& ((!agent && !pwd) || (agent && pwd))) {
|
||||
view.enableTabs();
|
||||
} else {
|
||||
view.disableTabs();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* if the configuration is sufficient the model is being request to
|
||||
* perform a configuration test. Otherwise, the status indicator is
|
||||
* being updated with the status "incomplete"
|
||||
*/
|
||||
functionalityCheck: function() {
|
||||
// this method should be called only if necessary, because it may
|
||||
// cause an LDAP request!
|
||||
var host = this.configModel.configuration.ldap_host;
|
||||
var port = this.configModel.configuration.ldap_port;
|
||||
var base = this.configModel.configuration.ldap_base;
|
||||
var userFilter = this.configModel.configuration.ldap_userlist_filter;
|
||||
var loginFilter = this.configModel.configuration.ldap_login_filter;
|
||||
|
||||
if((host && port && base && userFilter && loginFilter) ||
|
||||
(host && base && host.indexOf('ldapi://') > -1 && userFilter && loginFilter)) {
|
||||
this.configModel.requestConfigurationTest();
|
||||
} else {
|
||||
this._updateStatusIndicator(this.STATUS_INCOMPLETE);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* will request a functionality check if one of the related configuration
|
||||
* settings was changed.
|
||||
*
|
||||
* @param {ConfigSetPayload|Object} [changeSet]
|
||||
*/
|
||||
considerFunctionalityCheck: function(changeSet) {
|
||||
var testTriggers = [
|
||||
'ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password',
|
||||
'ldap_base', 'ldap_userlist_filter', 'ldap_login_filter'
|
||||
];
|
||||
for(var key in changeSet) {
|
||||
if($.inArray(key, testTriggers) >= 0) {
|
||||
this.functionalityCheck();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* keeps number of running save processes and shows a spinner if
|
||||
* necessary
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @listens ConfigModel#setRequested
|
||||
*/
|
||||
onSetRequested: function(view) {
|
||||
view.saveProcesses += 1;
|
||||
if(view.saveProcesses === 1) {
|
||||
view.showSaveSpinner();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* keeps number of running save processes and hides the spinner if
|
||||
* necessary. Also triggers checks, to adjust tabs state and status bar.
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @param {ConfigSetPayload} [result]
|
||||
* @listens ConfigModel#setCompleted
|
||||
*/
|
||||
onSetRequestDone: function(view, result) {
|
||||
if(view.saveProcesses > 0) {
|
||||
view.saveProcesses -= 1;
|
||||
if(view.saveProcesses === 0) {
|
||||
view.hideSaveSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
view.basicStatusCheck(view);
|
||||
var param = {};
|
||||
param[result.key] = 1;
|
||||
view.considerFunctionalityCheck(param);
|
||||
},
|
||||
|
||||
/**
|
||||
* Base DN test results will arrive here
|
||||
*
|
||||
* @param {WizardTabElementary} view
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
onDetectionTestCompleted: function(view, payload) {
|
||||
if(payload.feature === 'TestBaseDN') {
|
||||
if(payload.data.status === 'success') {
|
||||
var objectsFound = parseInt(payload.data.changes.ldap_test_base, 10);
|
||||
if(objectsFound > 0) {
|
||||
view._updateStatusIndicator(view.STATUS_SUCCESS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
view._updateStatusIndicator(view.STATUS_ERROR);
|
||||
OC.Notification.showTemporary(t('user_ldap', 'The Base DN appears to be wrong'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the status indicator based on the configuration test result
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @param {ConfigTestPayload} [result]
|
||||
* @listens ConfigModel#configurationTested
|
||||
*/
|
||||
onTestCompleted: function(view, result) {
|
||||
if(result.isSuccess) {
|
||||
view.configModel.requestWizard('ldap_test_base');
|
||||
} else {
|
||||
view._updateStatusIndicator(view.STATUS_ERROR);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* triggers initial checks upon configuration loading to update status
|
||||
* controls
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @listens ConfigModel#configLoaded
|
||||
*/
|
||||
onConfigLoaded: function(view) {
|
||||
view._updateStatusIndicator(view.STATUS_UNTESTED);
|
||||
view.basicStatusCheck(view);
|
||||
view.functionalityCheck();
|
||||
},
|
||||
|
||||
/**
|
||||
* reacts on attempts to switch to a different tab
|
||||
*
|
||||
* @param {object} event
|
||||
* @param {object} ui
|
||||
* @returns {boolean}
|
||||
*/
|
||||
onTabChange: function(event, ui) {
|
||||
if(this.saveProcesses > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var newTabID = ui.newTab[0].id;
|
||||
if(newTabID === '#ldapWizard1') {
|
||||
newTabID = 'server';
|
||||
}
|
||||
var oldTabID = ui.oldTab[0].id;
|
||||
if(oldTabID === '#ldapWizard1') {
|
||||
oldTabID = 'server';
|
||||
}
|
||||
if(!_.isUndefined(this.tabs[newTabID])) {
|
||||
this.tabs[newTabID].isActive = true;
|
||||
this.tabs[newTabID].onActivate();
|
||||
} else {
|
||||
console.warn('Unreferenced activated tab ' + newTabID);
|
||||
}
|
||||
if(!_.isUndefined(this.tabs[oldTabID])) {
|
||||
this.tabs[oldTabID].isActive = false;
|
||||
} else {
|
||||
console.warn('Unreferenced left tab ' + oldTabID);
|
||||
}
|
||||
|
||||
if(!_.isUndefined(this.tabs[newTabID])) {
|
||||
this._controlUpdate(this.tabs[newTabID].tabIndex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* triggers checks upon configuration updates to keep status controls
|
||||
* up to date
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @param {object} [changeSet]
|
||||
* @listens ConfigModel#configUpdated
|
||||
*/
|
||||
onConfigUpdated: function(view, changeSet) {
|
||||
view.basicStatusCheck(view);
|
||||
view.considerFunctionalityCheck(changeSet);
|
||||
},
|
||||
|
||||
/**
|
||||
* requests a configuration test
|
||||
*/
|
||||
onTestButtonClick: function() {
|
||||
this.configModel.requestWizard('ldap_action_test_connection', {ldap_serverconfig_chooser: this.configModel.configID});
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the model instance and registers event listeners
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} [configModel]
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
/** @type {OCA.LDAP.Wizard.ConfigModel} */
|
||||
this.configModel = configModel;
|
||||
for(var i in this.tabs) {
|
||||
this.tabs[i].setModel(configModel);
|
||||
}
|
||||
|
||||
// make sure this is definitely run after tabs did their work, order is important here
|
||||
// for now this works, because tabs are supposed to register their listeners in their
|
||||
// setModel() method.
|
||||
// alternative: make Elementary Tab a Publisher as well.
|
||||
this.configModel.on('configLoaded', this.onConfigLoaded, this);
|
||||
this.configModel.on('configUpdated', this.onConfigUpdated, this);
|
||||
this.configModel.on('setRequested', this.onSetRequested, this);
|
||||
this.configModel.on('setCompleted', this.onSetRequestDone, this);
|
||||
this.configModel.on('configurationTested', this.onTestCompleted, this);
|
||||
this.configModel.on('receivedLdapFeature', this.onDetectionTestCompleted, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* enables tab and navigation buttons
|
||||
*/
|
||||
enableTabs: function() {
|
||||
//do not use this function directly, use basicStatusCheck instead.
|
||||
if(this.saveProcesses === 0) {
|
||||
$('.ldap_action_continue').removeAttr('disabled');
|
||||
$('.ldap_action_back').removeAttr('disabled');
|
||||
this.$settings.tabs('option', 'disabled', []);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* disables tab and navigation buttons
|
||||
*/
|
||||
disableTabs: function() {
|
||||
$('.ldap_action_continue').attr('disabled', 'disabled');
|
||||
$('.ldap_action_back').attr('disabled', 'disabled');
|
||||
this.$settings.tabs('option', 'disabled', [1, 2, 3, 4, 5]);
|
||||
},
|
||||
|
||||
/**
|
||||
* shows a save spinner
|
||||
*/
|
||||
showSaveSpinner: function() {
|
||||
this.$saveSpinners.removeClass('hidden');
|
||||
$('#ldap *').addClass('save-cursor');
|
||||
},
|
||||
|
||||
/**
|
||||
* hides the save spinner
|
||||
*/
|
||||
hideSaveSpinner: function() {
|
||||
this.$saveSpinners.addClass('hidden');
|
||||
$('#ldap *').removeClass('save-cursor');
|
||||
},
|
||||
|
||||
/**
|
||||
* performs a config load request to the model
|
||||
*
|
||||
* @param {string} [configID]
|
||||
* @private
|
||||
*/
|
||||
_requestConfig: function(configID) {
|
||||
this.configModel.load(configID);
|
||||
},
|
||||
|
||||
/**
|
||||
* bootstraps the visual appearance and event listeners, as well as the
|
||||
* first config
|
||||
*/
|
||||
render: function () {
|
||||
$('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'});
|
||||
this.$settings.tabs({});
|
||||
$('#ldapSettings button:not(.icon-default-style):not(.ui-multiselect)').button();
|
||||
$('#ldapSettings').tabs({ beforeActivate: this.onTabChange });
|
||||
|
||||
this.initControls();
|
||||
this.disableTabs();
|
||||
|
||||
this._requestConfig(this.tabs.server.getConfigID());
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the status indicator / bar
|
||||
*
|
||||
* @param {number} [state]
|
||||
* @private
|
||||
*/
|
||||
_updateStatusIndicator: function(state) {
|
||||
var $indicator = $('.ldap_config_state_indicator');
|
||||
var $indicatorLight = $('.ldap_config_state_indicator_sign');
|
||||
|
||||
switch(state) {
|
||||
case this.STATUS_UNTESTED:
|
||||
$indicator.text(t('user_ldap',
|
||||
'Testing configuration…'
|
||||
));
|
||||
$indicator.addClass('ldap_grey');
|
||||
$indicatorLight.removeClass('error');
|
||||
$indicatorLight.removeClass('success');
|
||||
break;
|
||||
case this.STATUS_ERROR:
|
||||
$indicator.text(t('user_ldap',
|
||||
'Configuration incorrect'
|
||||
));
|
||||
$indicator.removeClass('ldap_grey');
|
||||
$indicatorLight.addClass('error');
|
||||
$indicatorLight.removeClass('success');
|
||||
break;
|
||||
case this.STATUS_INCOMPLETE:
|
||||
$indicator.text(t('user_ldap',
|
||||
'Configuration incomplete'
|
||||
));
|
||||
$indicator.removeClass('ldap_grey');
|
||||
$indicatorLight.removeClass('error');
|
||||
$indicatorLight.removeClass('success');
|
||||
break;
|
||||
case this.STATUS_SUCCESS:
|
||||
$indicator.text(t('user_ldap', 'Configuration OK'));
|
||||
$indicator.addClass('ldap_grey');
|
||||
$indicatorLight.removeClass('error');
|
||||
$indicatorLight.addClass('success');
|
||||
if(!this.tabs.server.isActive) {
|
||||
this.configModel.set('ldap_configuration_active', 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* handles a click on the Back button
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @private
|
||||
*/
|
||||
_controlBack: function(view) {
|
||||
var curTabIndex = view.$settings.tabs('option', 'active');
|
||||
if(curTabIndex == 0) {
|
||||
return;
|
||||
}
|
||||
view.$settings.tabs('option', 'active', curTabIndex - 1);
|
||||
view._controlUpdate(curTabIndex - 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* handles a click on the Continue button
|
||||
*
|
||||
* @param {WizardView} [view]
|
||||
* @private
|
||||
*/
|
||||
_controlContinue: function(view) {
|
||||
var curTabIndex = view.$settings.tabs('option', 'active');
|
||||
if(curTabIndex == 3) {
|
||||
return;
|
||||
}
|
||||
view.$settings.tabs('option', 'active', 1 + curTabIndex);
|
||||
view._controlUpdate(curTabIndex + 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the controls (navigation buttons)
|
||||
*
|
||||
* @param {number} [nextTabIndex] - index of the tab being switched to
|
||||
* @private
|
||||
*/
|
||||
_controlUpdate: function(nextTabIndex) {
|
||||
if(nextTabIndex == 0) {
|
||||
$('.ldap_action_back').addClass('invisible');
|
||||
$('.ldap_action_continue').removeClass('invisible');
|
||||
} else
|
||||
if(nextTabIndex == 1) {
|
||||
$('.ldap_action_back').removeClass('invisible');
|
||||
$('.ldap_action_continue').removeClass('invisible');
|
||||
} else
|
||||
if(nextTabIndex == 2) {
|
||||
$('.ldap_action_continue').removeClass('invisible');
|
||||
$('.ldap_action_back').removeClass('invisible');
|
||||
} else
|
||||
if(nextTabIndex == 3) {
|
||||
$('.ldap_action_back').removeClass('invisible');
|
||||
$('.ldap_action_continue').addClass('invisible');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OCA.LDAP.Wizard.WizardView = WizardView;
|
||||
})();
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* initializes the wizard and related components and kicks it off.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var Wizard = function() {
|
||||
var detectorQueue = new OCA.LDAP.Wizard.WizardDetectorQueue();
|
||||
detectorQueue.init();
|
||||
|
||||
var detectors = [];
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorPort());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorBaseDN());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorEmailAttribute());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserDisplayNameAttribute());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserGroupAssociation());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserObjectClasses());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupObjectClasses());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupsForUsers());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupsForGroups());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorFilterUser());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorFilterLogin());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorFilterGroup());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserCount());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupCount());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorAvailableAttributes());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorTestLoginName());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorTestBaseDN());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorTestConfiguration());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorClearUserMappings());
|
||||
detectors.push(new OCA.LDAP.Wizard.WizardDetectorClearGroupMappings());
|
||||
|
||||
var model = new OCA.LDAP.Wizard.ConfigModel();
|
||||
model.init(detectorQueue);
|
||||
// NOTE: order of detectors may play a role
|
||||
// for example, BaseDN detector needs the port. The port is typically found
|
||||
// by the Port Detector. If BaseDN detector was run first, it will not have
|
||||
// all necessary information. Only after Port Detector was executed…
|
||||
for (var i = 0; i < detectors.length; i++) {
|
||||
model.registerDetector(detectors[i]);
|
||||
}
|
||||
|
||||
var filterOnTypeFactory = new OCA.LDAP.Wizard.FilterOnTypeFactory();
|
||||
|
||||
var tabs = [];
|
||||
tabs.push(new OCA.LDAP.Wizard.WizardTabUserFilter(filterOnTypeFactory, 1));
|
||||
tabs.push(new OCA.LDAP.Wizard.WizardTabLoginFilter(2));
|
||||
tabs.push(new OCA.LDAP.Wizard.WizardTabGroupFilter(filterOnTypeFactory, 3));
|
||||
tabs.push(new OCA.LDAP.Wizard.WizardTabAdvanced());
|
||||
tabs.push(new OCA.LDAP.Wizard.WizardTabExpert());
|
||||
|
||||
var view = new OCA.LDAP.Wizard.WizardView(model);
|
||||
view.init();
|
||||
view.setModel(model);
|
||||
for (var j = 0; j < tabs.length; j++) {
|
||||
view.registerTab(tabs[j], '#ldapWizard' + (j + 2));
|
||||
}
|
||||
|
||||
var controller = new OCA.LDAP.Wizard.Controller();
|
||||
controller.init();
|
||||
controller.setView(view);
|
||||
controller.setModel(model);
|
||||
controller.run();
|
||||
};
|
||||
|
||||
OCA.LDAP.Wizard.Wizard = Wizard;
|
||||
})();
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
new OCA.LDAP.Wizard.Wizard();
|
||||
});
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc an Attributes Detector. It executes the auto-detection of
|
||||
* available attributes by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorAvailableAttributes = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_loginfilter_attributes');
|
||||
this.runsOnRequest = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* runs the detector, if port is not set.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
model.notifyAboutDetectionStart(this.getTargetKey());
|
||||
var params = OC.buildQueryString({
|
||||
action: 'determineAttributes',
|
||||
ldap_serverconfig_chooser: configID
|
||||
});
|
||||
return model.callWizard(params, this.processResult, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
processResult: function(model, detector, result) {
|
||||
if(result.status === 'success') {
|
||||
var payload = {
|
||||
feature: 'AvailableAttributes',
|
||||
data: result.options[detector.getTargetKey()]
|
||||
};
|
||||
model.inform(payload);
|
||||
}
|
||||
this._super(model, detector, result);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorAvailableAttributes = WizardDetectorAvailableAttributes;
|
||||
})();
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Base DN Detector. It executes the auto-detection of the base
|
||||
* DN by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorBaseDN = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_base');
|
||||
this.runsOnRequest = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* runs the detector, if specified configuration settings are set and
|
||||
* base DN is not set.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
if( !model.configuration['ldap_host']
|
||||
|| !model.configuration['ldap_port']
|
||||
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
model.notifyAboutDetectionStart(this.getTargetKey());
|
||||
var params = OC.buildQueryString({
|
||||
action: 'guessBaseDN',
|
||||
ldap_serverconfig_chooser: configID
|
||||
});
|
||||
return model.callWizard(params, this.processResult, this);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorBaseDN = WizardDetectorBaseDN;
|
||||
})();
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc requests clearing of user mappings
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorClearGroupMappings = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_action_clear_group_mappings');
|
||||
this.testName = 'ClearMappings';
|
||||
this.isLegacy = true;
|
||||
this.legacyDestination = 'clearMappings.php';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorClearGroupMappings = WizardDetectorClearGroupMappings;
|
||||
})();
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc requests clearing of user mappings
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorClearUserMappings = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_action_clear_user_mappings');
|
||||
this.testName = 'ClearMappings';
|
||||
this.isLegacy = true;
|
||||
this.legacyDestination = 'clearMappings.php';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorClearUserMappings = WizardDetectorClearUserMappings;
|
||||
})();
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc let's the wizard backend count the available users
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorEmailAttribute = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_user_count');
|
||||
this.wizardMethod = 'detectEmailAttribute';
|
||||
this.runsOnRequest = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
if(model.configuration.ldap_email_attr) {
|
||||
// a value is already set. Don't overwrite and don't ask LDAP
|
||||
// without reason.
|
||||
return false;
|
||||
}
|
||||
this._super(model, configID);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorEmailAttribute = WizardDetectorEmailAttribute;
|
||||
})();
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc abstract detector for detecting groups and object classes
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorFeatureAbstract = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
|
||||
/**
|
||||
* runs the detector, if port is not set.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
model.notifyAboutDetectionStart(this.getTargetKey());
|
||||
var params = OC.buildQueryString({
|
||||
action: this.wizardMethod,
|
||||
ldap_serverconfig_chooser: configID
|
||||
});
|
||||
return model.callWizard(params, this.processResult, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
processResult: function(model, detector, result) {
|
||||
if(result.status === 'success') {
|
||||
var payload = {
|
||||
feature: detector.featureName,
|
||||
data: result.options[detector.getTargetKey()]
|
||||
};
|
||||
model.inform(payload);
|
||||
}
|
||||
|
||||
this._super(model, detector, result);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorFeatureAbstract = WizardDetectorFeatureAbstract;
|
||||
})();
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorFilterGroup = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTrigger([
|
||||
'ldap_groupfilter_groups',
|
||||
'ldap_groupfilter_objectclass'
|
||||
]);
|
||||
this.setTargetKey('ldap_group_filter');
|
||||
|
||||
this.wizardMethod = 'getGroupFilter';
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorFilterGroup = WizardDetectorFilterGroup;
|
||||
})();
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorFilterLogin = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTrigger([
|
||||
'ldap_loginfilter_username',
|
||||
'ldap_loginfilter_email',
|
||||
'ldap_loginfilter_attributes'
|
||||
]);
|
||||
this.setTargetKey('ldap_login_filter');
|
||||
this.runsOnRequest = true;
|
||||
|
||||
this.wizardMethod = 'getUserLoginFilter';
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorFilterLogin = WizardDetectorFilterLogin;
|
||||
})();
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorFilterUser = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTrigger([
|
||||
'ldap_userfilter_groups',
|
||||
'ldap_userfilter_objectclass'
|
||||
]);
|
||||
this.setTargetKey('ldap_userlist_filter');
|
||||
this.runsOnRequest = true;
|
||||
|
||||
this.wizardMethod = 'getUserListFilter';
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorFilterUser = WizardDetectorFilterUser;
|
||||
})();
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @classdesc a generic (abstract) Detector template. A Detector's task is
|
||||
* to kick off server side detection of certain LDAP features. It is invoked
|
||||
* when changes to specified configuration keys happen.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorGeneric = OCA.LDAP.Wizard.WizardObject.subClass({
|
||||
/**
|
||||
* initializes the instance. Always call it after creating the instance.
|
||||
*/
|
||||
init: function() {
|
||||
this.setTrigger([]);
|
||||
this.targetKey = '';
|
||||
this.runsOnRequest = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the configuration keys the detector is listening on
|
||||
*
|
||||
* @param {string[]} triggers
|
||||
*/
|
||||
setTrigger: function(triggers) {
|
||||
this.triggers = triggers;
|
||||
},
|
||||
|
||||
/**
|
||||
* tests whether the detector is triggered by the provided key
|
||||
*
|
||||
* @param {string} key
|
||||
* @returns {boolean}
|
||||
*/
|
||||
triggersOn: function(key) {
|
||||
return ($.inArray(key, this.triggers) >= 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* whether the detector runs on explicit request
|
||||
*
|
||||
* @param {string} key
|
||||
* @returns {boolean}
|
||||
*/
|
||||
runsOnFeatureRequest: function(key) {
|
||||
return !!(this.runsOnRequest && this.targetKey === key);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the configuration key the detector is attempting to auto-detect
|
||||
*
|
||||
* @param {string} key
|
||||
*/
|
||||
setTargetKey: function(key) {
|
||||
this.targetKey = key;
|
||||
},
|
||||
|
||||
/**
|
||||
* returns the configuration key the detector is attempting to
|
||||
* auto-detect
|
||||
*/
|
||||
getTargetKey: function() {
|
||||
return this.targetKey;
|
||||
},
|
||||
|
||||
/**
|
||||
* runs the detector. This method is supposed to be implemented by the
|
||||
* concrete detector.
|
||||
*
|
||||
* Must return false if the detector decides not to run.
|
||||
* Must return a jqXHR object otherwise, which is provided by the
|
||||
* model's callWizard()
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
// to be implemented by subClass
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* processes the result of the Nextcloud server
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {WizardDetectorGeneric} detector
|
||||
* @param {object} result
|
||||
*/
|
||||
processResult: function(model, detector, result) {
|
||||
model['notifyAboutDetectionCompletion'](detector.getTargetKey());
|
||||
if(result.status === 'success') {
|
||||
for (var id in result.changes) {
|
||||
// update and not set method, as values are already stored
|
||||
model['update'](id, result.changes[id]);
|
||||
}
|
||||
} else {
|
||||
var payload = { relatedKey: detector.targetKey };
|
||||
if(!_.isUndefined(result.message)) {
|
||||
payload.message = result.message;
|
||||
}
|
||||
model.gotServerError(payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorGeneric = WizardDetectorGeneric;
|
||||
})();
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorGroupCount = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_group_count');
|
||||
this.wizardMethod = 'countGroups';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorGroupCount = WizardDetectorGroupCount;
|
||||
})();
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc discovers object classes for the groups tab
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorGroupObjectClasses = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_groupfilter_objectclass');
|
||||
this.wizardMethod = 'determineGroupObjectClasses';
|
||||
this.featureName = 'GroupObjectClasses';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorGroupObjectClasses = WizardDetectorGroupObjectClasses;
|
||||
})();
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc detects groups for the groups tab
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorGroupsForGroups = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_groupfilter_groups');
|
||||
this.wizardMethod = 'determineGroupsForGroups';
|
||||
this.featureName = 'GroupsForGroups';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorGroupsForGroups = WizardDetectorGroupsForGroups;
|
||||
})();
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc detects groups for the users tab
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorGroupsForUsers = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_userfilter_groups');
|
||||
this.wizardMethod = 'determineGroupsForUsers';
|
||||
this.featureName = 'GroupsForUsers';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorGroupsForUsers = WizardDetectorGroupsForUsers;
|
||||
})();
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorPort = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_port');
|
||||
this.runsOnRequest = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* runs the detector, if port is not set.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
model.notifyAboutDetectionStart('ldap_port');
|
||||
var params = OC.buildQueryString({
|
||||
action: 'guessPortAndTLS',
|
||||
ldap_serverconfig_chooser: configID
|
||||
});
|
||||
return model.callWizard(params, this.processResult, this);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorPort = WizardDetectorPort;
|
||||
})();
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
/**
|
||||
* @classdesc only run detector is allowed to run at a time. Basically
|
||||
* because we cannot have parallel LDAP connections per session. This
|
||||
* queue is takes care of running all the detectors one after the other.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorQueue = OCA.LDAP.Wizard.WizardObject.subClass({
|
||||
/**
|
||||
* initializes the instance. Always call it after creating the instance.
|
||||
*/
|
||||
init: function() {
|
||||
this.queue = [];
|
||||
this.isRunning = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* empties the queue and cancels a possibly running request
|
||||
*/
|
||||
reset: function() {
|
||||
this.queue = [];
|
||||
if(!_.isUndefined(this.runningRequest)) {
|
||||
this.runningRequest.abort();
|
||||
delete this.runningRequest;
|
||||
}
|
||||
this.isRunning = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* a parameter-free callback that eventually executes the run method of
|
||||
* the detector.
|
||||
*
|
||||
* @callback detectorCallBack
|
||||
* @see OCA.LDAP.Wizard.ConfigModel._processSetResult
|
||||
*/
|
||||
|
||||
/**
|
||||
* adds a detector to the queue and attempts to trigger to run the
|
||||
* next job, because it might be the first.
|
||||
*
|
||||
* @param {detectorCallBack} callback
|
||||
*/
|
||||
add: function(callback) {
|
||||
this.queue.push(callback);
|
||||
this.next();
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes the next detector if none is running. This method is also
|
||||
* automatically invoked after a detector finished.
|
||||
*/
|
||||
next: function() {
|
||||
if(this.isRunning === true || this.queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRunning = true;
|
||||
var callback = this.queue.shift();
|
||||
var request = callback();
|
||||
|
||||
// we receive either false or a jqXHR object
|
||||
// false in case the detector decided against executing
|
||||
if(request === false) {
|
||||
this.isRunning = false;
|
||||
this.next();
|
||||
return;
|
||||
}
|
||||
this.runningRequest = request;
|
||||
|
||||
var detectorQueue = this;
|
||||
$.when(request).then(function() {
|
||||
detectorQueue.isRunning = false;
|
||||
detectorQueue.next();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorQueue = WizardDetectorQueue;
|
||||
})();
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorFilterSimpleRequestAbstract = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
|
||||
runsOnRequest: true,
|
||||
|
||||
/**
|
||||
* runs the detector, if port is not set.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
if(_.isUndefined(this.wizardMethod)) {
|
||||
console.warn('wizardMethod not set! ' + this.constructor);
|
||||
return false;
|
||||
}
|
||||
model.notifyAboutDetectionStart(this.targetKey);
|
||||
var params = OC.buildQueryString({
|
||||
action: this.wizardMethod,
|
||||
ldap_serverconfig_chooser: configID
|
||||
});
|
||||
return model.callWizard(params, this.processResult, this);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract = WizardDetectorFilterSimpleRequestAbstract;
|
||||
})();
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorTestAbstract = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
|
||||
isLegacy: false,
|
||||
|
||||
/**
|
||||
* runs the test
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} model
|
||||
* @param {string} configID - the configuration prefix
|
||||
* @param {Object} params - additional parameters needed to send to the
|
||||
* wizard
|
||||
* @returns {boolean|jqXHR}
|
||||
* @abstract
|
||||
*/
|
||||
run: function(model, configID, params) {
|
||||
if(_.isUndefined(this.wizardMethod) && !this.isLegacy) {
|
||||
console.warn('wizardMethod not set! ' + this.constructor);
|
||||
return false;
|
||||
}
|
||||
model.notifyAboutDetectionStart(this.getTargetKey());
|
||||
params = params || {};
|
||||
params = OC.buildQueryString($.extend({
|
||||
action: this.wizardMethod,
|
||||
ldap_serverconfig_chooser: configID
|
||||
}, params));
|
||||
if(!this.isLegacy) {
|
||||
return model.callWizard(params, this.processResult, this);
|
||||
} else {
|
||||
return model.callAjax(this.legacyDestination, params, this.processResult, this);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
processResult: function(model, detector, result) {
|
||||
model['notifyAboutDetectionCompletion'](detector.getTargetKey());
|
||||
var payload = {
|
||||
feature: detector.testName,
|
||||
data: result
|
||||
};
|
||||
model.inform(payload);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorTestAbstract = WizardDetectorTestAbstract;
|
||||
})();
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc Tests, how many objects reside in the given base DN(s)
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorTestBaseDN = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_test_base');
|
||||
this.testName = 'TestBaseDN';
|
||||
this.wizardMethod = 'countInBaseDN';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorTestBaseDN = WizardDetectorTestBaseDN;
|
||||
})();
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc a Port Detector. It executes the auto-detection of the port
|
||||
* by the Nextcloud server, if requirements are met.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorTestConfiguration = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_action_test_connection');
|
||||
this.testName = 'TestConfiguration';
|
||||
this.isLegacy = true;
|
||||
this.legacyDestination = 'testConfiguration.php';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorTestConfiguration = WizardDetectorTestConfiguration;
|
||||
})();
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc checks whether the provided log in name can be resolved into
|
||||
* a DN using the current login filter
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorTestLoginName = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_test_loginname');
|
||||
this.testName = 'TestLoginName';
|
||||
this.wizardMethod = 'testLoginName';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorTestLoginName = WizardDetectorTestLoginName;
|
||||
})();
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc let's the wizard backend count the available users
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorUserCount = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_user_count');
|
||||
this.wizardMethod = 'countUsers';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorUserCount = WizardDetectorUserCount;
|
||||
})();
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc let's the wizard backend count the available users
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorUserDisplayNameAttribute = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_user_count');
|
||||
this.wizardMethod = 'detectUserDisplayNameAttribute';
|
||||
this.runsOnRequest = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
// default value has capital N. Detected values are always lowercase
|
||||
if(model.configuration.ldap_display_name && model.configuration.ldap_display_name !== 'displayName') {
|
||||
// a value is already set. Don't overwrite and don't ask LDAP
|
||||
// without reason.
|
||||
return false;
|
||||
}
|
||||
this._super(model, configID);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorUserDisplayNameAttribute = WizardDetectorUserDisplayNameAttribute;
|
||||
})();
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc let's the wizard backend count the available users
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorUserGroupAssociation = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
|
||||
init: function() {
|
||||
this.setTargetKey('ldap_group_count');
|
||||
this.wizardMethod = 'determineGroupMemberAssoc';
|
||||
this.runsOnRequest = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
run: function(model, configID) {
|
||||
// TODO: might be better with configuration marker as uniqueMember
|
||||
// is a valid value (although probably less common then member and memberUid).
|
||||
if(model.configuration.ldap_group_member_assoc_attribute && model.configuration.ldap_group_member_assoc_attribute !== '') {
|
||||
// a value is already set. Don't overwrite and don't ask LDAP
|
||||
// without reason.
|
||||
return false;
|
||||
}
|
||||
this._super(model, configID);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorUserGroupAssociation = WizardDetectorUserGroupAssociation;
|
||||
})();
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc discovers object classes for the users tab
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var WizardDetectorUserObjectClasses = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
|
||||
/** @inheritdoc */
|
||||
init: function() {
|
||||
// given, it is not a configuration key
|
||||
this.setTargetKey('ldap_userfilter_objectclass');
|
||||
this.wizardMethod = 'determineUserObjectClasses';
|
||||
this.featureName = 'UserObjectClasses';
|
||||
this.runsOnRequest = true;
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardDetectorUserObjectClasses = WizardDetectorUserObjectClasses;
|
||||
})();
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc filters a select box when a text element is typed in
|
||||
*/
|
||||
var FilterOnType = OCA.LDAP.Wizard.WizardObject.subClass({
|
||||
/**
|
||||
* initializes a type filter on a text input for a select element
|
||||
*
|
||||
* @param {jQuery} $select
|
||||
* @param {jQuery} $textInput
|
||||
*/
|
||||
init: function($select, $textInput) {
|
||||
this.$select = $select;
|
||||
this.$textInput = $textInput;
|
||||
this.lastSearch = '';
|
||||
|
||||
var fity = this;
|
||||
$textInput.bind('change keyup', function () {
|
||||
if(fity.runID) {
|
||||
window.clearTimeout(fity.runID);
|
||||
}
|
||||
fity.runID = window.setTimeout(function() {
|
||||
fity.filter(fity);
|
||||
}, 250);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* the actual search or filter method
|
||||
*
|
||||
* @param {FilterOnType} fity
|
||||
*/
|
||||
filter: function(fity) {
|
||||
var filterVal = fity.$textInput.val().toLowerCase();
|
||||
if(filterVal === fity.lastSearch) {
|
||||
return;
|
||||
}
|
||||
fity.lastSearch = filterVal;
|
||||
|
||||
fity.$select.find('option').each(function() {
|
||||
if(!filterVal || $(this).val().toLowerCase().indexOf(filterVal) > -1) {
|
||||
$(this).removeAttr('hidden')
|
||||
} else {
|
||||
$(this).attr('hidden', 'hidden');
|
||||
}
|
||||
});
|
||||
delete(fity.runID);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.FilterOnType = FilterOnType;
|
||||
})();
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc creates instances of OCA.LDAP.Wizard.FilterOnType upon request
|
||||
*/
|
||||
var FilterOnTypeFactory = OCA.LDAP.Wizard.WizardObject.subClass({
|
||||
/**
|
||||
* initializes a type filter on a text input for a select element
|
||||
*
|
||||
* @param {jQuery} $select
|
||||
* @param {jQuery} $textInput
|
||||
*/
|
||||
get: function($select, $textInput) {
|
||||
return new OCA.LDAP.Wizard.FilterOnType($select, $textInput);
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.FilterOnTypeFactory = FilterOnTypeFactory;
|
||||
})();
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
var initializing = false;
|
||||
var superPattern = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;
|
||||
|
||||
/**
|
||||
* @classdesc a base class that allows inheritance
|
||||
*
|
||||
* @abstrcact
|
||||
* @constructor
|
||||
*/
|
||||
var WizardObject = function(){};
|
||||
WizardObject.subClass = function(properties) {
|
||||
var _super = this.prototype;
|
||||
|
||||
initializing = true;
|
||||
var proto = new this();
|
||||
initializing = false;
|
||||
|
||||
for (var name in properties) {
|
||||
proto[name] =
|
||||
typeof properties[name] === "function" &&
|
||||
typeof _super[name] === 'function' &&
|
||||
superPattern.test(properties[name]) ?
|
||||
(function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
this._super = _super[name];
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
return ret;
|
||||
};
|
||||
})(name, properties[name]) :
|
||||
properties[name];
|
||||
};
|
||||
|
||||
function Class() {
|
||||
if(!initializing && this.init) {
|
||||
this.init.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
Class.prototype = proto;
|
||||
Class.constructor = Class;
|
||||
Class.subClass = arguments.callee;
|
||||
return Class;
|
||||
};
|
||||
|
||||
WizardObject.constructor = WizardObject;
|
||||
|
||||
OCA.LDAP.Wizard.WizardObject = WizardObject;
|
||||
})();
|
||||
|
|
@ -1,384 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the server tab
|
||||
* in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabAbstractFilter = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
|
||||
/**
|
||||
* @property {number} number that needs to exceeded to use complex group
|
||||
* selection element
|
||||
*/
|
||||
_groupElementSwitchThreshold: 40,
|
||||
|
||||
/**
|
||||
* @property {boolean} - tells whether multiselect or complex element is
|
||||
* used for selecting groups
|
||||
*/
|
||||
isComplexGroupChooser: false,
|
||||
|
||||
/** @property {string} */
|
||||
tabID: '',
|
||||
|
||||
/**
|
||||
* initializes the instance. Always call it after initialization.
|
||||
* concrete view must set managed items first, and then call the parent
|
||||
* init.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.FilterOnTypeFactory} fotf
|
||||
* @param {number} [tabIndex]
|
||||
* @param {string} [tabID]
|
||||
*/
|
||||
init: function (fotf, tabIndex, tabID) {
|
||||
this._super(tabIndex, tabID);
|
||||
|
||||
/** @type {OCA.LDAP.Wizard.FilterOnTypeFactory} */
|
||||
this.foTFactory = fotf;
|
||||
this._initMultiSelect(
|
||||
this.getGroupsItem().$element,
|
||||
t('user_ldap', 'Select groups')
|
||||
);
|
||||
this._initMultiSelect(
|
||||
this.getObjectClassItem().$element,
|
||||
t('user_ldap', 'Select object classes')
|
||||
);
|
||||
this.filterName = this.getFilterItem().keyName;
|
||||
this._initFilterModeSwitcher(
|
||||
this.getToggleItem().$element,
|
||||
this.getRawFilterContainerItem().$element,
|
||||
[ this.getObjectClassItem().$element ],
|
||||
this.getFilterModeKey(),
|
||||
{
|
||||
status: 'disabled',
|
||||
$element: this.getGroupsItem().$element
|
||||
}
|
||||
);
|
||||
_.bindAll(this, 'onCountButtonClick', 'onSelectGroup', 'onDeselectGroup');
|
||||
this.getCountItem().$relatedElements.click(this.onCountButtonClick);
|
||||
if(this.manyGroupsSupport) {
|
||||
var $selectBtn = $(this.tabID).find('.ldapGroupListSelect');
|
||||
$selectBtn.click(this.onSelectGroup);
|
||||
var $deselectBtn = $(this.tabID).find('.ldapGroupListDeselect');
|
||||
$deselectBtn.click(this.onDeselectGroup);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* returns managed item for the object class chooser. must be
|
||||
* implemented by concrete view
|
||||
*/
|
||||
getObjectClassItem: function () {},
|
||||
|
||||
/**
|
||||
* returns managed item for the group chooser. must be
|
||||
* implemented by concrete view
|
||||
*/
|
||||
getGroupsItem: function () {},
|
||||
|
||||
/**
|
||||
* returns managed item for the effective filter. must be
|
||||
* implemented by concrete view
|
||||
*/
|
||||
getFilterItem: function () {},
|
||||
|
||||
/**
|
||||
* returns managed item for the toggle element. must be
|
||||
* implemented by concrete view
|
||||
*/
|
||||
getToggleItem: function () {},
|
||||
|
||||
/**
|
||||
* returns managed item for the raw filter container. must be
|
||||
* implemented by concrete view
|
||||
*/
|
||||
getRawFilterContainerItem: function () {},
|
||||
|
||||
/**
|
||||
* returns managed item for the count control. must be
|
||||
* implemented by concrete view
|
||||
*/
|
||||
getCountItem: function () {},
|
||||
|
||||
/**
|
||||
* returns name of the filter mode key. must be implemented by concrete
|
||||
* view
|
||||
*/
|
||||
getFilterModeKey: function () {},
|
||||
|
||||
/**
|
||||
* Sets the config model for this view and subscribes to some events.
|
||||
* Also binds the config chooser to the model
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} configModel
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
this._super(configModel);
|
||||
this.configModel.on('configLoaded', this.onConfigSwitch, this);
|
||||
this.configModel.on('receivedLdapFeature', this.onFeatureReceived, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
_setFilterModeAssisted: function () {
|
||||
this._super();
|
||||
if(this.isComplexGroupChooser) {
|
||||
this.enableElement(this.getGroupsItem().$relatedElements);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
_setFilterModeRaw: function () {
|
||||
this._super();
|
||||
if(this.manyGroupsSupport) {
|
||||
this.disableElement(this.getGroupsItem().$relatedElements);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the selected user object classes
|
||||
*
|
||||
* @param {Array} classes
|
||||
*/
|
||||
setObjectClass: function(classes) {
|
||||
this.setElementValue(this.getObjectClassItem().$element, classes);
|
||||
this.getObjectClassItem().$element.multiselect('refresh');
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the selected groups
|
||||
*
|
||||
* @param {string} groups
|
||||
*/
|
||||
setGroups: function(groups) {
|
||||
if(typeof groups === 'string') {
|
||||
groups = groups.split("\n");
|
||||
}
|
||||
if(!this.isComplexGroupChooser) {
|
||||
this.setElementValue(this.getGroupsItem().$element, groups);
|
||||
this.getGroupsItem().$element.multiselect('refresh');
|
||||
} else {
|
||||
var $element = $(this.tabID).find('.ldapGroupListSelected');
|
||||
this.equipMultiSelect($element, groups);
|
||||
this.updateFilterOnType();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the filter
|
||||
*
|
||||
* @param {string} filter
|
||||
*/
|
||||
setFilter: function(filter) {
|
||||
this.setElementValue(this.getFilterItem().$element, filter);
|
||||
this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the user count string
|
||||
*
|
||||
* @param {string} countInfo
|
||||
*/
|
||||
setCount: function(countInfo) {
|
||||
this.setElementValue(this.getCountItem().$element, countInfo);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
considerFeatureRequests: function() {
|
||||
if(!this.isActive) {
|
||||
return;
|
||||
}
|
||||
if(this.getObjectClassItem().$element.find('option').length === 0) {
|
||||
this.disableElement(this.getObjectClassItem().$element);
|
||||
this.disableElement(this.getGroupsItem().$element);
|
||||
if(this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED) {
|
||||
this.configModel.requestWizard(this.getObjectClassItem().keyName);
|
||||
this.configModel.requestWizard(this.getGroupsItem().keyName);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates (creates, if necessary) filterOnType instances
|
||||
*/
|
||||
updateFilterOnType: function() {
|
||||
if(_.isUndefined(this.filterOnType)) {
|
||||
this.filterOnType = [];
|
||||
|
||||
var $availableGroups = $(this.tabID).find('.ldapGroupListAvailable');
|
||||
this.filterOnType.push(this.foTFactory.get(
|
||||
$availableGroups, $(this.tabID).find('.ldapManyGroupsSearch')
|
||||
));
|
||||
var $selectedGroups = $(this.tabID).find('.ldapGroupListSelected');
|
||||
this.filterOnType.push(this.foTFactory.get(
|
||||
$selectedGroups, $(this.tabID).find('.ldapManyGroupsSearch')
|
||||
));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
onActivate: function() {
|
||||
this._super();
|
||||
this.considerFeatureRequests();
|
||||
},
|
||||
|
||||
/**
|
||||
* resets the view when a configuration switch happened.
|
||||
*
|
||||
* @param {WizardTabAbstractFilter} view
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
onConfigSwitch: function(view, configuration) {
|
||||
view.getObjectClassItem().$element.find('option').remove();
|
||||
view.getGroupsItem().$element.find('option').remove();
|
||||
view.getCountItem().$element.text('');
|
||||
$(view.tabID).find('.ldapGroupListAvailable').empty();
|
||||
$(view.tabID).find('.ldapGroupListSelected').empty();
|
||||
view.updateFilterOnType();
|
||||
$(view.tabID).find('.ldapManyGroupsSearch').val('');
|
||||
|
||||
if(view.isComplexGroupChooser) {
|
||||
view.isComplexGroupChooser = false;
|
||||
view.getGroupsItem().$element.multiselect({classes: view.multiSelectPluginClass});
|
||||
$(view.tabID).find(".ldapManyGroupsSupport").addClass('hidden');
|
||||
}
|
||||
|
||||
view.onConfigLoaded(view, configuration);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
onConfigLoaded: function(view, configuration) {
|
||||
for(var key in view.managedItems){
|
||||
if(!_.isUndefined(configuration[key])) {
|
||||
var value = configuration[key];
|
||||
var methodName = view.managedItems[key].setMethod;
|
||||
if(!_.isUndefined(view[methodName])) {
|
||||
view[methodName](value);
|
||||
// we reimplement it here to update the filter index
|
||||
// for groups. Maybe we can isolate it?
|
||||
if(methodName === 'setGroups') {
|
||||
view.updateFilterOnType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* if UserObjectClasses are found, the corresponding element will be
|
||||
* updated
|
||||
*
|
||||
* @param {WizardTabAbstractFilter} view
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
onFeatureReceived: function(view, payload) {
|
||||
if(payload.feature === view.getObjectClassItem().featureName) {
|
||||
view.equipMultiSelect(view.getObjectClassItem().$element, payload.data);
|
||||
if( !view.getFilterItem().$element.val()
|
||||
&& view.parsedFilterMode === view.configModel.FILTER_MODE_ASSISTED
|
||||
) {
|
||||
view.configModel.requestWizard(view.getFilterItem().keyName);
|
||||
}
|
||||
} else if (payload.feature === view.getGroupsItem().featureName) {
|
||||
if(view.manyGroupsSupport && payload.data.length > view._groupElementSwitchThreshold) {
|
||||
// we need to fill the left list box, excluding the values
|
||||
// that are already selected
|
||||
var $element = $(view.tabID).find('.ldapGroupListAvailable');
|
||||
var selected = view.configModel.configuration[view.getGroupsItem().keyName];
|
||||
var available = $(payload.data).not(selected).get();
|
||||
view.equipMultiSelect($element, available);
|
||||
$(view.tabID).find(".ldapManyGroupsSupport").removeClass('hidden');
|
||||
view.getGroupsItem().$element.multiselect({classes: view.multiSelectPluginClass + ' forceHidden'});
|
||||
view.isComplexGroupChooser = true;
|
||||
} else {
|
||||
view.isComplexGroupChooser = false;
|
||||
view.equipMultiSelect(view.getGroupsItem().$element, payload.data);
|
||||
view.getGroupsItem().$element.multiselect({classes: view.multiSelectPluginClass});
|
||||
$(view.tabID).find(".ldapManyGroupsSupport").addClass('hidden');
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* request to count the users with the current filter
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
onCountButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
// let's clear the field
|
||||
this.getCountItem().$element.text('');
|
||||
this.configModel.requestWizard(this.getCountItem().keyName);
|
||||
},
|
||||
|
||||
/**
|
||||
* saves groups when using the complex UI
|
||||
*
|
||||
* @param {Array} groups
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
_saveGroups: function(groups) {
|
||||
var toSave = '';
|
||||
$(groups).each(function() { toSave = toSave + "\n" + this; } );
|
||||
this.configModel.set(this.getGroupsItem().keyName, $.trim(toSave));
|
||||
},
|
||||
|
||||
/**
|
||||
* acts on adding groups to the filter
|
||||
*/
|
||||
onSelectGroup: function() {
|
||||
var $available = $(this.tabID).find('.ldapGroupListAvailable');
|
||||
if(!$available.val()) {
|
||||
return; // no selection – nothing to do
|
||||
}
|
||||
|
||||
var $selected = $(this.tabID).find('.ldapGroupListSelected');
|
||||
var selected = $.map($selected.find('option'), function(e) { return e.value; });
|
||||
|
||||
let selectedGroups = [];
|
||||
$available.find('option:selected:visible').each(function() {
|
||||
selectedGroups.push($(this).val());
|
||||
});
|
||||
|
||||
this._saveGroups(selected.concat(selectedGroups));
|
||||
$available.find('option:selected:visible').prependTo($selected);
|
||||
this.updateFilterOnType(); // selected groups are not updated yet
|
||||
$available.find('option:selected').prop("selected", false);
|
||||
},
|
||||
|
||||
/**
|
||||
* acts on removing groups to the filter
|
||||
*/
|
||||
onDeselectGroup: function() {
|
||||
var $available = $(this.tabID).find('.ldapGroupListAvailable');
|
||||
var $selected = $(this.tabID).find('.ldapGroupListSelected');
|
||||
var selected = $.map($selected.find('option:not(:selected:visible)'), function(e) { return e.value; });
|
||||
|
||||
this._saveGroups(selected);
|
||||
$selected.find('option:selected:visible').appendTo($available);
|
||||
this.updateFilterOnType(); // selected groups are not updated yet
|
||||
$selected.find('option:selected').prop("selected", false);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabAbstractFilter = WizardTabAbstractFilter;
|
||||
})();
|
||||
|
|
@ -1,527 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the advanced tab
|
||||
* in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabAdvanced = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
|
||||
/**
|
||||
* initializes the instance. Always call it after initialization.
|
||||
*
|
||||
* @param {any} tabIndex -
|
||||
* @param {any} tabID -
|
||||
*/
|
||||
init: function (tabIndex, tabID) {
|
||||
this._super(tabIndex, tabID);
|
||||
|
||||
var items = {
|
||||
// Connection settings
|
||||
ldap_configuration_active: {
|
||||
$element: $('#ldap_configuration_active'),
|
||||
setMethod: 'setConfigurationState'
|
||||
},
|
||||
ldap_backup_host: {
|
||||
$element: $('#ldap_backup_host'),
|
||||
setMethod: 'setBackupHost'
|
||||
},
|
||||
ldap_backup_port: {
|
||||
$element: $('#ldap_backup_port'),
|
||||
setMethod: 'setBackupPort'
|
||||
},
|
||||
ldap_override_main_server: {
|
||||
$element: $('#ldap_override_main_server'),
|
||||
setMethod: 'setOverrideMainServerState'
|
||||
},
|
||||
ldap_turn_off_cert_check: {
|
||||
$element: $('#ldap_turn_off_cert_check'),
|
||||
setMethod: 'setCertCheckDisabled'
|
||||
},
|
||||
ldap_cache_ttl: {
|
||||
$element: $('#ldap_cache_ttl'),
|
||||
setMethod: 'setCacheTTL'
|
||||
},
|
||||
|
||||
//Directory Settings
|
||||
ldap_display_name: {
|
||||
$element: $('#ldap_display_name'),
|
||||
setMethod: 'setUserDisplayName'
|
||||
},
|
||||
ldap_user_display_name_2: {
|
||||
$element: $('#ldap_user_display_name_2'),
|
||||
setMethod: 'setUserDisplayName2'
|
||||
},
|
||||
ldap_base_users: {
|
||||
$element: $('#ldap_base_users'),
|
||||
setMethod: 'setBaseDNUsers'
|
||||
},
|
||||
ldap_attributes_for_user_search: {
|
||||
$element: $('#ldap_attributes_for_user_search'),
|
||||
setMethod: 'setSearchAttributesUsers'
|
||||
},
|
||||
ldap_mark_remnants_as_disabled: {
|
||||
$element: $('#ldap_mark_remnants_as_disabled'),
|
||||
setMethod: 'setMarkRemnantsAsDisabled'
|
||||
},
|
||||
ldap_group_display_name: {
|
||||
$element: $('#ldap_group_display_name'),
|
||||
setMethod: 'setGroupDisplayName'
|
||||
},
|
||||
ldap_base_groups: {
|
||||
$element: $('#ldap_base_groups'),
|
||||
setMethod: 'setBaseDNGroups'
|
||||
},
|
||||
ldap_attributes_for_group_search: {
|
||||
$element: $('#ldap_attributes_for_group_search'),
|
||||
setMethod: 'setSearchAttributesGroups'
|
||||
},
|
||||
ldap_group_member_assoc_attribute: {
|
||||
$element: $('#ldap_group_member_assoc_attribute'),
|
||||
setMethod: 'setGroupMemberAssociationAttribute'
|
||||
},
|
||||
ldap_dynamic_group_member_url: {
|
||||
$element: $('#ldap_dynamic_group_member_url'),
|
||||
setMethod: 'setDynamicGroupMemberURL'
|
||||
},
|
||||
ldap_nested_groups: {
|
||||
$element: $('#ldap_nested_groups'),
|
||||
setMethod: 'setUseNestedGroups'
|
||||
},
|
||||
ldap_paging_size: {
|
||||
$element: $('#ldap_paging_size'),
|
||||
setMethod: 'setPagingSize'
|
||||
},
|
||||
ldap_turn_on_pwd_change: {
|
||||
$element: $('#ldap_turn_on_pwd_change'),
|
||||
setMethod: 'setPasswordChangeEnabled'
|
||||
},
|
||||
ldap_default_ppolicy_dn: {
|
||||
$element: $('#ldap_default_ppolicy_dn'),
|
||||
setMethod: 'setDefaultPPolicyDN'
|
||||
},
|
||||
|
||||
//Special Attributes
|
||||
ldap_quota_attr: {
|
||||
$element: $('#ldap_quota_attr'),
|
||||
setMethod: 'setQuotaAttribute'
|
||||
},
|
||||
ldap_quota_def: {
|
||||
$element: $('#ldap_quota_def'),
|
||||
setMethod: 'setQuotaDefault'
|
||||
},
|
||||
ldap_email_attr: {
|
||||
$element: $('#ldap_email_attr'),
|
||||
setMethod: 'setEmailAttribute'
|
||||
},
|
||||
home_folder_naming_rule: {
|
||||
$element: $('#home_folder_naming_rule'),
|
||||
setMethod: 'setHomeFolderAttribute'
|
||||
},
|
||||
ldap_ext_storage_home_attribute: {
|
||||
$element: $('#ldap_ext_storage_home_attribute'),
|
||||
setMethod: 'setExternalStorageHomeAttribute'
|
||||
},
|
||||
|
||||
//User Profile Attributes
|
||||
ldap_attr_phone: {
|
||||
$element: $('#ldap_attr_phone'),
|
||||
setMethod: 'setPhoneAttribute'
|
||||
},
|
||||
ldap_attr_website: {
|
||||
$element: $('#ldap_attr_website'),
|
||||
setMethod: 'setWebsiteAttribute'
|
||||
},
|
||||
ldap_attr_address: {
|
||||
$element: $('#ldap_attr_address'),
|
||||
setMethod: 'setAddressAttribute'
|
||||
},
|
||||
ldap_attr_twitter: {
|
||||
$element: $('#ldap_attr_twitter'),
|
||||
setMethod: 'setTwitterAttribute'
|
||||
},
|
||||
ldap_attr_fediverse: {
|
||||
$element: $('#ldap_attr_fediverse'),
|
||||
setMethod: 'setFediverseAttribute'
|
||||
},
|
||||
ldap_attr_organisation: {
|
||||
$element: $('#ldap_attr_organisation'),
|
||||
setMethod: 'setOrganisationAttribute'
|
||||
},
|
||||
ldap_attr_role: {
|
||||
$element: $('#ldap_attr_role'),
|
||||
setMethod: 'setRoleAttribute'
|
||||
},
|
||||
ldap_attr_headline: {
|
||||
$element: $('#ldap_attr_headline'),
|
||||
setMethod: 'setHeadlineAttribute'
|
||||
},
|
||||
ldap_attr_biography: {
|
||||
$element: $('#ldap_attr_biography'),
|
||||
setMethod: 'setBiographyAttribute'
|
||||
},
|
||||
ldap_attr_birthdate: {
|
||||
$element: $('#ldap_attr_birthdate'),
|
||||
setMethod: 'setBirthdateAttribute'
|
||||
},
|
||||
};
|
||||
this.setManagedItems(items);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the config model for this view and subscribes to some events.
|
||||
* Also binds the config chooser to the model
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} configModel
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
this._super(configModel);
|
||||
this.configModel.on('configLoaded', this.onConfigLoaded, this);
|
||||
this.configModel.on('receivedLdapFeature', this.onResultReceived, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the experienced admin check box
|
||||
*
|
||||
* @param {string} isConfigActive contains an int
|
||||
*/
|
||||
setConfigurationState: function(isConfigActive) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_configuration_active.$element, isConfigActive
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the backup host configuration text field
|
||||
*
|
||||
* @param {string} host
|
||||
*/
|
||||
setBackupHost: function(host) {
|
||||
this.setElementValue(this.managedItems.ldap_backup_host.$element, host);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the backup port configuration text field
|
||||
*
|
||||
* @param {string} port
|
||||
*/
|
||||
setBackupPort: function(port) {
|
||||
this.setElementValue(this.managedItems.ldap_backup_port.$element, port);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets whether the main server should be overridden or not
|
||||
*
|
||||
* @param {string} doOverride contains an int
|
||||
*/
|
||||
setOverrideMainServerState: function(doOverride) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_override_main_server.$element, doOverride
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets whether the SSL/TLS certification check shout be disabled
|
||||
*
|
||||
* @param {string} doCertCheck contains an int
|
||||
*/
|
||||
setCertCheckDisabled: function(doCertCheck) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_turn_off_cert_check.$element, doCertCheck
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the time-to-live of the LDAP cache (in seconds)
|
||||
*
|
||||
* @param {string} cacheTTL contains an int
|
||||
*/
|
||||
setCacheTTL: function(cacheTTL) {
|
||||
this.setElementValue(this.managedItems.ldap_cache_ttl.$element, cacheTTL);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the user display name attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setUserDisplayName: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_display_name.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the additional user display name attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setUserDisplayName2: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_user_display_name_2.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the Base DN for users
|
||||
*
|
||||
* @param {string} base
|
||||
*/
|
||||
setBaseDNUsers: function(base) {
|
||||
this.setElementValue(this.managedItems.ldap_base_users.$element, base);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attributes for user searches
|
||||
*
|
||||
* @param {string} attributes
|
||||
*/
|
||||
setSearchAttributesUsers: function(attributes) {
|
||||
this.setElementValue(this.managedItems.ldap_attributes_for_user_search.$element, attributes);
|
||||
},
|
||||
|
||||
/**
|
||||
* enables or disables marking remnants as disabled
|
||||
*
|
||||
* @param {string} markRemnantsAsDisabled contains an int
|
||||
*/
|
||||
setMarkRemnantsAsDisabled: function(markRemnantsAsDisabled) {
|
||||
this.setElementValue(this.managedItems.ldap_mark_remnants_as_disabled.$element, markRemnantsAsDisabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the display name attribute for groups
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setGroupDisplayName: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_group_display_name.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the Base DN for groups
|
||||
*
|
||||
* @param {string} base
|
||||
*/
|
||||
setBaseDNGroups: function(base) {
|
||||
this.setElementValue(this.managedItems.ldap_base_groups.$element, base);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attributes for group search
|
||||
*
|
||||
* @param {string} attributes
|
||||
*/
|
||||
setSearchAttributesGroups: function(attributes) {
|
||||
this.setElementValue(this.managedItems.ldap_attributes_for_group_search.$element, attributes);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the association of users and groups
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setGroupMemberAssociationAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_group_member_assoc_attribute.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the dynamic group member url attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setDynamicGroupMemberURL: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_dynamic_group_member_url.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* enabled or disables the use of nested groups (groups in groups in
|
||||
* groups…)
|
||||
*
|
||||
* @param {string} useNestedGroups contains an int
|
||||
*/
|
||||
setUseNestedGroups: function(useNestedGroups) {
|
||||
this.setElementValue(this.managedItems.ldap_nested_groups.$element, useNestedGroups);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the size of pages for paged search
|
||||
*
|
||||
* @param {string} size contains an int
|
||||
*/
|
||||
setPagingSize: function(size) {
|
||||
this.setElementValue(this.managedItems.ldap_paging_size.$element, size);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets whether the password changes per user should be enabled
|
||||
*
|
||||
* @param {string} doPasswordChange contains an int
|
||||
*/
|
||||
setPasswordChangeEnabled: function(doPasswordChange) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_turn_on_pwd_change.$element, doPasswordChange
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the default ppolicy attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setDefaultPPolicyDN: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_default_ppolicy_dn.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the email attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setEmailAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_email_attr.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the external storage home attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setExternalStorageHomeAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_ext_storage_home_attribute.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the quota attribute
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setQuotaAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_quota_attr.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the default quota for LDAP users
|
||||
*
|
||||
* @param {string} quota contains an int
|
||||
*/
|
||||
setQuotaDefault: function(quota) {
|
||||
this.setElementValue(this.managedItems.ldap_quota_def.$element, quota);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user specific home folder location
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setHomeFolderAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.home_folder_naming_rule.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile phone Number
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setPhoneAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_phone.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile website
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setWebsiteAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_website.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile postal address
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setAddressAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_address.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile twitter
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setTwitterAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_twitter.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile fediverse
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setFediverseAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_fediverse.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile organisation
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setOrganisationAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_organisation.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile role
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setRoleAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_role.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile headline
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setHeadlineAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_headline.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile biography
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setBiographyAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_biography.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute for the Nextcloud user profile birthday
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setBirthdateAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_attr_birthdate.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* deals with the result of the Test Connection test
|
||||
*
|
||||
* @param {WizardTabAdvanced} view
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
onResultReceived: function(view, payload) {
|
||||
if(payload.feature === 'TestConfiguration') {
|
||||
OC.Notification.showTemporary(payload.data.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabAdvanced = WizardTabAdvanced;
|
||||
})();
|
||||
|
|
@ -1,390 +0,0 @@
|
|||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the server tab
|
||||
* in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabElementary = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
|
||||
/** @property {number} */
|
||||
_configChooserNextServerNumber: 1,
|
||||
|
||||
baseDNTestTriggered: false,
|
||||
|
||||
/**
|
||||
* initializes the instance. Always call it after initialization.
|
||||
*
|
||||
* @param {any} tabIndex -
|
||||
* @param {any} tabID -
|
||||
*/
|
||||
init: function (tabIndex, tabID) {
|
||||
tabIndex = 0;
|
||||
this._super(tabIndex, tabID);
|
||||
this.isActive = true;
|
||||
this.$configChooser = $('#ldap_serverconfig_chooser');
|
||||
|
||||
var items = {
|
||||
ldap_host: {
|
||||
$element: $('#ldap_host'),
|
||||
setMethod: 'setHost'
|
||||
},
|
||||
ldap_port: {
|
||||
$element: $('#ldap_port'),
|
||||
setMethod: 'setPort',
|
||||
$relatedElements: $('.ldapDetectPort')
|
||||
},
|
||||
ldap_dn: {
|
||||
$element: $('#ldap_dn'),
|
||||
setMethod: 'setAgentDN',
|
||||
preventAutoSave: true,
|
||||
$saveButton: $('.ldapSaveAgentCredentials')
|
||||
},
|
||||
ldap_agent_password: {
|
||||
$element: $('#ldap_agent_password'),
|
||||
setMethod: 'setAgentPwd',
|
||||
preventAutoSave: true,
|
||||
$saveButton: $('.ldapSaveAgentCredentials')
|
||||
},
|
||||
ldap_base: {
|
||||
$element: $('#ldap_base'),
|
||||
setMethod: 'setBase',
|
||||
$relatedElements: $('.ldapDetectBase, .ldapTestBase'),
|
||||
$detectButton: $('.ldapDetectBase'),
|
||||
$testButton: $('.ldapTestBase')
|
||||
},
|
||||
ldap_base_test: {
|
||||
$element: $('#ldap_base')
|
||||
},
|
||||
ldap_experienced_admin: {
|
||||
$element: $('#ldap_experienced_admin'),
|
||||
setMethod: 'setExperiencedAdmin'
|
||||
}
|
||||
};
|
||||
this.setManagedItems(items);
|
||||
_.bindAll(this,
|
||||
'onPortButtonClick',
|
||||
'onBaseDNButtonClick',
|
||||
'onBaseDNTestButtonClick'
|
||||
);
|
||||
this.managedItems.ldap_port.$relatedElements.click(this.onPortButtonClick);
|
||||
this.managedItems.ldap_base.$detectButton.click(this.onBaseDNButtonClick);
|
||||
this.managedItems.ldap_base.$testButton.click(this.onBaseDNTestButtonClick);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the config model for this view and subscribes to some events.
|
||||
* Also binds the config chooser to the model
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} configModel
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
this._super(configModel);
|
||||
this.configModel.on('configLoaded', this.onConfigSwitch, this);
|
||||
this.configModel.on('newConfiguration', this.onNewConfiguration, this);
|
||||
this.configModel.on('deleteConfiguration', this.onDeleteConfiguration, this);
|
||||
this.configModel.on('receivedLdapFeature', this.onTestResultReceived, this);
|
||||
this._enableConfigChooser();
|
||||
this._enableConfigButtons();
|
||||
},
|
||||
|
||||
/**
|
||||
* returns the currently selected configuration ID
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getConfigID: function() {
|
||||
return this.$configChooser.val();
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the host configuration text field
|
||||
*
|
||||
* @param {string} host
|
||||
*/
|
||||
setHost: function(host) {
|
||||
this.setElementValue(this.managedItems.ldap_host.$element, host);
|
||||
if(host) {
|
||||
this.enableElement(this.managedItems.ldap_port.$relatedElements);
|
||||
} else {
|
||||
this.disableElement(this.managedItems.ldap_port.$relatedElements);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the port configuration text field
|
||||
*
|
||||
* @param {string} port
|
||||
*/
|
||||
setPort: function(port) {
|
||||
this.setElementValue(this.managedItems.ldap_port.$element, port);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the user (agent) DN text field
|
||||
*
|
||||
* @param {string} agentDN
|
||||
*/
|
||||
setAgentDN: function(agentDN) {
|
||||
this.setElementValue(this.managedItems.ldap_dn.$element, agentDN);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the user (agent) password field
|
||||
*
|
||||
* @param {string} agentPwd
|
||||
*/
|
||||
setAgentPwd: function(agentPwd) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_agent_password.$element, agentPwd
|
||||
);
|
||||
if (agentPwd && $('html').hasClass('lte9')) {
|
||||
// make it a password field again (IE fix, placeholders bug)
|
||||
this.managedItems.ldap_agent_password.$element.attr('type', 'password');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* updates the base DN text area
|
||||
*
|
||||
* @param {string} bases
|
||||
*/
|
||||
setBase: function(bases) {
|
||||
this.setElementValue(this.managedItems.ldap_base.$element, bases);
|
||||
if(!bases) {
|
||||
this.disableElement(this.managedItems.ldap_base.$testButton);
|
||||
} else {
|
||||
this.enableElement(this.managedItems.ldap_base.$testButton);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the experienced admin check box
|
||||
*
|
||||
* @param {string} xpAdminMode contains an int
|
||||
*/
|
||||
setExperiencedAdmin: function(xpAdminMode) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_experienced_admin.$element, xpAdminMode
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
overrideErrorMessage: function(message, key) {
|
||||
var original = message;
|
||||
message = this._super(message, key);
|
||||
if(original !== message) {
|
||||
// we pass the parents change
|
||||
return message;
|
||||
}
|
||||
switch(key) {
|
||||
case 'ldap_port':
|
||||
if (message === 'Invalid credentials') {
|
||||
return t('user_ldap', 'Please check the credentials, they seem to be wrong.');
|
||||
} else {
|
||||
return t('user_ldap', 'Please specify the port, it could not be auto-detected.');
|
||||
}
|
||||
break;
|
||||
case 'ldap_base':
|
||||
if( message === 'Server is unwilling to perform'
|
||||
|| message === 'Could not connect to LDAP'
|
||||
) {
|
||||
return t('user_ldap', 'Base DN could not be auto-detected, please revise credentials, host and port.');
|
||||
}
|
||||
return t('user_ldap', 'Could not detect Base DN, please enter it manually.');
|
||||
break;
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
/**
|
||||
* resets the view when a configuration switch happened.
|
||||
*
|
||||
* @param {WizardTabElementary} view
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
onConfigSwitch: function(view, configuration) {
|
||||
this.baseDNTestTriggered = false;
|
||||
view.disableElement(view.managedItems.ldap_port.$relatedElements);
|
||||
view.managedItems.ldap_dn.$saveButton.removeClass('primary');
|
||||
view.onConfigLoaded(view, configuration);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the configuration chooser when a new configuration was added
|
||||
* which also means it is being switched to. The configuration fields
|
||||
* are updated on a different step.
|
||||
*
|
||||
* @param {WizardTabElementary} view
|
||||
* @param {Object} result
|
||||
*/
|
||||
onNewConfiguration: function(view, result) {
|
||||
if(result.isSuccess === true) {
|
||||
var nthServer = view._configChooserNextServerNumber;
|
||||
view.$configChooser.find('option:selected').removeAttr('selected');
|
||||
var html = '<option value="'+result.configPrefix+'" selected="selected">'+t('user_ldap','{nthServer}. Server', {nthServer: nthServer})+'</option>';
|
||||
if(view.$configChooser.find('option:last').length > 0) {
|
||||
view.$configChooser.find('option:last').after(html);
|
||||
} else {
|
||||
view.$configChooser.html(html);
|
||||
}
|
||||
|
||||
view._configChooserNextServerNumber++;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the configuration chooser upon the deletion of a
|
||||
* configuration and, if necessary, loads an existing one.
|
||||
*
|
||||
* @param {any} view -
|
||||
* @param {any} result -
|
||||
*/
|
||||
onDeleteConfiguration: function(view, result) {
|
||||
if(result.isSuccess === true) {
|
||||
if(view.getConfigID() === result.configPrefix) {
|
||||
// if the deleted value is still the selected one (99% of
|
||||
// the cases), remove it from the list and load the topmost
|
||||
view.$configChooser.find('option:selected').remove();
|
||||
view.$configChooser.find('option:first').select();
|
||||
if(view.$configChooser.find(' option').length < 1) {
|
||||
view.configModel.newConfig(false);
|
||||
} else {
|
||||
view.configModel.load(view.getConfigID());
|
||||
}
|
||||
} else {
|
||||
// otherwise just remove the entry
|
||||
view.$configChooser.find('option[value=' + result.configPrefix + ']').remove();
|
||||
}
|
||||
} else {
|
||||
OC.Notification.showTemporary(result.errorMessage);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Base DN test results will arrive here
|
||||
*
|
||||
* @param {WizardTabElementary} view
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
onTestResultReceived: function(view, payload) {
|
||||
if(view.baseDNTestTriggered && payload.feature === 'TestBaseDN') {
|
||||
view.enableElement(view.managedItems.ldap_base.$testButton);
|
||||
var message;
|
||||
if(payload.data.status === 'success') {
|
||||
var objectsFound = parseInt(payload.data.changes.ldap_test_base, 10);
|
||||
if(objectsFound < 1) {
|
||||
message = t('user_ldap', 'No object found in the given Base DN. Please revise.');
|
||||
} else if(objectsFound > 1000) {
|
||||
message = t('user_ldap', 'More than 1,000 directory entries available.');
|
||||
} else {
|
||||
message = n(
|
||||
'user_ldap',
|
||||
'{objectsFound} entry available within the provided Base DN',
|
||||
'{objectsFound} entries available within the provided Base DN',
|
||||
objectsFound,
|
||||
{
|
||||
objectsFound: objectsFound
|
||||
});
|
||||
}
|
||||
} else {
|
||||
message = view.overrideErrorMessage(payload.data.message);
|
||||
message = message || t('user_ldap', 'An error occurred. Please check the Base DN, as well as connection settings and credentials.');
|
||||
if(payload.data.message) {
|
||||
console.warn(payload.data.message);
|
||||
}
|
||||
}
|
||||
OC.Notification.showTemporary(message);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* request to count the users with the current filter
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
onPortButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
this.configModel.requestWizard('ldap_port');
|
||||
},
|
||||
|
||||
/**
|
||||
* request to count the users with the current filter
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
onBaseDNButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
this.configModel.requestWizard('ldap_base');
|
||||
},
|
||||
|
||||
/**
|
||||
* request to count the users with the current filter
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
onBaseDNTestButtonClick: function(event) {
|
||||
event.preventDefault();
|
||||
this.baseDNTestTriggered = true;
|
||||
this.configModel.requestWizard('ldap_test_base');
|
||||
this.disableElement(this.managedItems.ldap_base.$testButton);
|
||||
},
|
||||
|
||||
/**
|
||||
* registers the change event on the configuration chooser and makes
|
||||
* the model load a newly selected configuration
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_enableConfigChooser: function() {
|
||||
this._configChooserNextServerNumber = this.$configChooser.find(' option').length + 1;
|
||||
var view = this;
|
||||
this.$configChooser.change(function(){
|
||||
var value = view.$configChooser.find(' option:selected:first').attr('value');
|
||||
view.configModel.load(value);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* adds actions to the action buttons for configuration management
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_enableConfigButtons: function() {
|
||||
var view = this;
|
||||
$('#ldap_action_delete_configuration').click(function(event) {
|
||||
event.preventDefault();
|
||||
OC.dialogs.confirm(
|
||||
t('user_ldap', 'Do you really want to delete the current Server Configuration?'),
|
||||
t('user_ldap', 'Confirm Deletion'),
|
||||
function(doDelete) {
|
||||
if(doDelete) {
|
||||
view.configModel.deleteConfig(view.getConfigID());
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
$('#ldap_action_add_configuration').click(function(event) {
|
||||
event.preventDefault();
|
||||
view.configModel.newConfig(false);
|
||||
});
|
||||
|
||||
$('#ldap_action_copy_configuration').click(function(event) {
|
||||
event.preventDefault();
|
||||
view.configModel.newConfig(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabElementary = WizardTabElementary;
|
||||
})();
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the expert tab
|
||||
* in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabExpert = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
|
||||
/**
|
||||
* initializes the instance. Always call it after initialization.
|
||||
*
|
||||
* @param {any} tabIndex -
|
||||
* @param {any} tabID -
|
||||
*/
|
||||
init: function (tabIndex, tabID) {
|
||||
this._super(tabIndex, tabID);
|
||||
|
||||
var items = {
|
||||
ldap_expert_username_attr: {
|
||||
$element: $('#ldap_expert_username_attr'),
|
||||
setMethod: 'setUsernameAttribute'
|
||||
},
|
||||
ldap_expert_uuid_user_attr: {
|
||||
$element: $('#ldap_expert_uuid_user_attr'),
|
||||
setMethod: 'setUserUUIDAttribute'
|
||||
},
|
||||
ldap_expert_uuid_group_attr: {
|
||||
$element: $('#ldap_expert_uuid_group_attr'),
|
||||
setMethod: 'setGroupUUIDAttribute'
|
||||
},
|
||||
|
||||
//Buttons
|
||||
ldap_action_clear_user_mappings: {
|
||||
$element: $('#ldap_action_clear_user_mappings')
|
||||
},
|
||||
ldap_action_clear_group_mappings: {
|
||||
$element: $('#ldap_action_clear_group_mappings')
|
||||
}
|
||||
|
||||
};
|
||||
this.setManagedItems(items);
|
||||
_.bindAll(this, 'onClearUserMappingsClick', 'onClearGroupMappingsClick');
|
||||
this.managedItems.ldap_action_clear_user_mappings.$element.click(this.onClearUserMappingsClick);
|
||||
this.managedItems.ldap_action_clear_group_mappings.$element.click(this.onClearGroupMappingsClick);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the config model for this view and subscribes to some events.
|
||||
* Also binds the config chooser to the model
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} configModel
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
this._super(configModel);
|
||||
this.configModel.on('configLoaded', this.onConfigLoaded, this);
|
||||
this.configModel.on('receivedLdapFeature', this.onResultReceived, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute to be used to create an Nextcloud ID (username)
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setUsernameAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_expert_username_attr.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute that provides an unique identifier per LDAP user
|
||||
* entry
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setUserUUIDAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_expert_uuid_user_attr.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the attribute that provides an unique identifier per LDAP group
|
||||
* entry
|
||||
*
|
||||
* @param {string} attribute
|
||||
*/
|
||||
setGroupUUIDAttribute: function(attribute) {
|
||||
this.setElementValue(this.managedItems.ldap_expert_uuid_group_attr.$element, attribute);
|
||||
},
|
||||
|
||||
/**
|
||||
* requests clearing of all user mappings
|
||||
*/
|
||||
onClearUserMappingsClick: function() {
|
||||
this.configModel.requestWizard('ldap_action_clear_user_mappings', {ldap_clear_mapping: 'user'});
|
||||
},
|
||||
|
||||
/**
|
||||
* requests clearing of all group mappings
|
||||
*/
|
||||
onClearGroupMappingsClick: function() {
|
||||
this.configModel.requestWizard('ldap_action_clear_group_mappings', {ldap_clear_mapping: 'group'});
|
||||
},
|
||||
|
||||
/**
|
||||
* deals with the result of the Test Connection test
|
||||
*
|
||||
* @param {WizardTabAdvanced} view
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
onResultReceived: function(view, payload) {
|
||||
if(payload.feature === 'ClearMappings') {
|
||||
var message;
|
||||
if(payload.data.status === 'success') {
|
||||
message = t('user_ldap', 'Mappings cleared successfully!');
|
||||
} else {
|
||||
message = t('user_ldap', 'Error while clearing the mappings.');
|
||||
}
|
||||
OC.Notification.showTemporary(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabExpert = WizardTabExpert;
|
||||
})();
|
||||
|
|
@ -1,642 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc An abstract tab view
|
||||
* @abstract
|
||||
*/
|
||||
var WizardTabGeneric = OCA.LDAP.Wizard.WizardObject.subClass({
|
||||
isActive: false,
|
||||
|
||||
/**
|
||||
* @property {string} - class that identifies a multiselect-plugin
|
||||
* control.
|
||||
*/
|
||||
multiSelectPluginClass: 'multiSelectPlugin',
|
||||
|
||||
/**
|
||||
* @property {string} - class that identifies a multiselect-plugin
|
||||
* control.
|
||||
*/
|
||||
bjQuiButtonClass: 'ui-button',
|
||||
|
||||
/**
|
||||
* @property {boolean} - indicates whether a filter mode toggle operation
|
||||
* is still in progress
|
||||
*/
|
||||
isToggling: false,
|
||||
|
||||
/** @inheritdoc */
|
||||
init: function(tabIndex, tabID) {
|
||||
this.tabIndex = tabIndex;
|
||||
this.tabID = tabID;
|
||||
this.spinner = $('.ldapSpinner').first().clone().removeClass('hidden');
|
||||
_.bindAll(this, '_toggleRawFilterMode', '_toggleRawFilterModeConfirmation');
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the configuration items that are managed by that view.
|
||||
*
|
||||
* The parameter contains key-value pairs the key being the
|
||||
* configuration keys and the value being its setter method.
|
||||
*
|
||||
* @param {object} managedItems
|
||||
*/
|
||||
setManagedItems: function(managedItems) {
|
||||
this.managedItems = managedItems;
|
||||
this._enableAutoSave();
|
||||
this._enableSaveButton();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the config model. The concrete view likely wants to subscribe
|
||||
* to events as well.
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} configModel
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
this.configModel = configModel;
|
||||
this.parsedFilterMode = this.configModel.FILTER_MODE_ASSISTED;
|
||||
this.configModel.on('detectionStarted', this.onDetectionStarted, this);
|
||||
this.configModel.on('detectionCompleted', this.onDetectionCompleted, this);
|
||||
this.configModel.on('serverError', this.onServerError, this);
|
||||
this.configModel.on('setCompleted', this.onItemSaved, this);
|
||||
this.configModel.on('configUpdated', this.onConfigLoaded, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* the method can be used to display a different error/information
|
||||
* message than provided by the Nextcloud server response. The concrete
|
||||
* Tab View may optionally implement it. Returning an empty string will
|
||||
* avoid any notification.
|
||||
*
|
||||
* @param {string} message
|
||||
* @param {string} key
|
||||
* @returns {string}
|
||||
*/
|
||||
overrideErrorMessage: function(message, key) {
|
||||
if(message === 'LDAP authentication method rejected'
|
||||
&& !this.configModel.configuration.ldap_dn)
|
||||
{
|
||||
message = t('user_ldap', 'Anonymous bind is not allowed. Please provide a User DN and Password.');
|
||||
} else if (message === 'LDAP Operations error'
|
||||
&& !this.configModel.configuration.ldap_dn
|
||||
&& !this.configModel.configuration.ldap_agent_password)
|
||||
{
|
||||
message = t('user_ldap', 'LDAP Operations error. Anonymous bind might not be allowed.');
|
||||
}
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
/**
|
||||
* this is called by the main view, if the tab is being switched to.
|
||||
*/
|
||||
onActivate: function() {
|
||||
if(!_.isUndefined(this.filterModeKey)
|
||||
&& this.configModel.configuration.ldap_experienced_admin === '1') {
|
||||
this.setFilterMode(this.configModel.FILTER_MODE_RAW);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the tab when the model loaded a configuration and notified
|
||||
* this view.
|
||||
*
|
||||
* @param {WizardTabGeneric} view - this instance
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
onConfigLoaded: function(view, configuration) {
|
||||
for(var key in view.managedItems){
|
||||
if(!_.isUndefined(configuration[key])) {
|
||||
var value = configuration[key];
|
||||
var methodName = view.managedItems[key].setMethod;
|
||||
if(!_.isUndefined(view[methodName])) {
|
||||
view[methodName](value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* reacts on a set action on the model and updates the tab with the
|
||||
* valid value.
|
||||
*
|
||||
* @param {WizardTabGeneric} view
|
||||
* @param {Object} result
|
||||
*/
|
||||
onItemSaved: function(view, result) {
|
||||
if(!_.isUndefined(view.managedItems[result.key])) {
|
||||
var methodName = view.managedItems[result.key].setMethod;
|
||||
view[methodName](result.value);
|
||||
if(!result.isSuccess) {
|
||||
OC.Notification.showTemporary(t('user_ldap', 'Saving failed. Please make sure the database is in Operation. Reload before continuing.'));
|
||||
console.warn(result.errorMessage);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* displays server error messages.
|
||||
*
|
||||
* @param {any} view -
|
||||
* @param {any} payload -
|
||||
*/
|
||||
onServerError: function(view, payload) {
|
||||
if ( !_.isUndefined(view.managedItems[payload.relatedKey])) {
|
||||
var message = view.overrideErrorMessage(payload.message, payload.relatedKey);
|
||||
if(message) {
|
||||
OC.Notification.showTemporary(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* disables affected, managed fields if a detector is running against them
|
||||
*
|
||||
* @param {WizardTabGeneric} view
|
||||
* @param {string} key
|
||||
*/
|
||||
onDetectionStarted: function(view, key) {
|
||||
if(!_.isUndefined(view.managedItems[key])) {
|
||||
view.disableElement(view.managedItems[key].$element);
|
||||
if(!_.isUndefined(view.managedItems[key].$relatedElements)){
|
||||
view.disableElement(view.managedItems[key].$relatedElements);
|
||||
}
|
||||
view.attachSpinner(view.managedItems[key].$element.attr('id'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* enables affected, managed fields after a detector was run against them
|
||||
*
|
||||
* @param {WizardTabGeneric} view
|
||||
* @param {string} key
|
||||
*/
|
||||
onDetectionCompleted: function(view, key) {
|
||||
if(!_.isUndefined(view.managedItems[key])) {
|
||||
view.enableElement(view.managedItems[key].$element);
|
||||
if(!_.isUndefined(view.managedItems[key].$relatedElements)){
|
||||
view.enableElement(view.managedItems[key].$relatedElements);
|
||||
}
|
||||
view.removeSpinner(view.managedItems[key].$element.attr('id'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the value to an HTML element. Checkboxes, text areas and (text)
|
||||
* input fields are supported.
|
||||
*
|
||||
* @param {jQuery} $element - the target element
|
||||
* @param {string|number|Array} value
|
||||
*/
|
||||
setElementValue: function($element, value) {
|
||||
// deal with check box
|
||||
if ($element.is('input[type=checkbox]')) {
|
||||
this._setCheckBox($element, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// special cases: deal with text area and multiselect
|
||||
if ($element.is('textarea') && $.isArray(value)) {
|
||||
value = value.join("\n");
|
||||
} else if($element.hasClass(this.multiSelectPluginClass)) {
|
||||
if(!_.isArray(value)) {
|
||||
value = value.split("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($element.is('span')) {
|
||||
$element.text(value);
|
||||
} else {
|
||||
$element.val(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* replaces options on a multiselect element
|
||||
*
|
||||
* @param {jQuery} $element - the multiselect element
|
||||
* @param {Array} options
|
||||
*/
|
||||
equipMultiSelect: function($element, options) {
|
||||
if($element.find('option').length === 0) {
|
||||
$element.empty();
|
||||
for (var i in options) {
|
||||
var name = options[i];
|
||||
$element.append($('<option>').val(name).text(name).attr('title', name));
|
||||
}
|
||||
}
|
||||
if(!$element.hasClass('ldapGroupList')) {
|
||||
$element.multiselect('refresh');
|
||||
this.enableElement($element);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* enables the specified HTML element
|
||||
*
|
||||
* @param {jQuery} $element
|
||||
*/
|
||||
enableElement: function($element) {
|
||||
var isMS = $element.is('select[multiple]');
|
||||
var hasOptions = isMS ? ($element.find('option').length > 0) : false;
|
||||
|
||||
if($element.hasClass(this.multiSelectPluginClass) && hasOptions) {
|
||||
$element.multiselect("enable");
|
||||
} else if ($element.hasClass(this.bjQuiButtonClass)) {
|
||||
$element.button("enable");
|
||||
}
|
||||
else if(!isMS || (isMS && hasOptions)) {
|
||||
$element.prop('disabled', false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* disables the specified HTML element
|
||||
*
|
||||
* @param {jQuery} $element
|
||||
*/
|
||||
disableElement: function($element) {
|
||||
if($element.hasClass(this.multiSelectPluginClass)) {
|
||||
$element.multiselect("disable");
|
||||
} else if ($element.hasClass(this.bjQuiButtonClass)) {
|
||||
$element.button("disable");
|
||||
} else {
|
||||
$element.prop('disabled', 'disabled');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* attaches a spinner icon to the HTML element specified by ID
|
||||
*
|
||||
* @param {string} elementID
|
||||
*/
|
||||
attachSpinner: function(elementID) {
|
||||
if($('#' + elementID + ' + .ldapSpinner').length == 0) {
|
||||
var spinner = this.spinner.clone();
|
||||
var $element = $('#' + elementID);
|
||||
$(spinner).insertAfter($element);
|
||||
// and special treatment for multiselects:
|
||||
if ($element.is('select[multiple]')) {
|
||||
$('#' + elementID + " + img + button").css('display', 'none');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* removes the spinner icon from the HTML element specified by ID
|
||||
*
|
||||
* @param {string} elementID
|
||||
*/
|
||||
removeSpinner: function(elementID) {
|
||||
$('#' + elementID+' + .ldapSpinner').remove();
|
||||
// and special treatment for multiselects:
|
||||
$('#' + elementID + " + button").css('display', 'inline');
|
||||
},
|
||||
|
||||
/**
|
||||
* whether the wizard works in experienced admin mode
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isExperiencedMode: function() {
|
||||
return parseInt(this.configModel.configuration.ldap_experienced_admin, 10) === 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* sets up auto-save functionality to the managed items
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_enableAutoSave: function() {
|
||||
var view = this;
|
||||
|
||||
for(var id in this.managedItems) {
|
||||
if(_.isUndefined(this.managedItems[id].$element)
|
||||
|| _.isUndefined(this.managedItems[id].setMethod)
|
||||
|| (!_.isUndefined(this.managedItems[id].preventAutoSave)
|
||||
&& this.managedItems[id].preventAutoSave === true)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
var $element = this.managedItems[id].$element;
|
||||
if (!$element.is('select[multiple]')) {
|
||||
$element.change(function() {
|
||||
view._requestSave($(this));
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* set's up save-button behavior (essentially used for agent dn and pwd)
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_enableSaveButton: function() {
|
||||
var view = this;
|
||||
|
||||
// TODO: this is not nice, because it fires one request per change
|
||||
// in the scenario this happens twice, causes detectors to run
|
||||
// duplicated etc. To have this work properly, the wizard endpoint
|
||||
// must accept setting multiple changes. Instead of messing around
|
||||
// with old ajax/wizard.php use this opportunity and create a
|
||||
// Controller
|
||||
for(var id in this.managedItems) {
|
||||
if(_.isUndefined(this.managedItems[id].$element)
|
||||
|| _.isUndefined(this.managedItems[id].$saveButton)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
(function (item) {
|
||||
item.$saveButton.click(function(event) {
|
||||
event.preventDefault();
|
||||
view._requestSave(item.$element);
|
||||
item.$saveButton.removeClass('primary');
|
||||
});
|
||||
item.$element.change(function () {
|
||||
item.$saveButton.addClass('primary');
|
||||
});
|
||||
})(this.managedItems[id]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* initializes a multiSelect element
|
||||
*
|
||||
* @param {jQuery} $element
|
||||
* @param {string} caption
|
||||
* @private
|
||||
*/
|
||||
_initMultiSelect: function($element, caption) {
|
||||
var view = this;
|
||||
$element.multiselect({
|
||||
header: false,
|
||||
selectedList: 9,
|
||||
noneSelectedText: caption,
|
||||
classes: this.multiSelectPluginClass,
|
||||
close: function() {
|
||||
view._requestSave($element);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} viewSaveInfo
|
||||
* @property {Function} val
|
||||
* @property {Function} attr
|
||||
* @property {Function} is
|
||||
*/
|
||||
|
||||
/**
|
||||
* requests a save operation from the model for a given value
|
||||
* represented by a HTML element and its ID.
|
||||
*
|
||||
* @param {jQuery|viewSaveInfo} $element
|
||||
* @private
|
||||
*/
|
||||
_requestSave: function($element) {
|
||||
var value = '';
|
||||
if($element.is('input[type=checkbox]')
|
||||
&& !$element.is(':checked')) {
|
||||
value = 0;
|
||||
} else if ($element.is('select[multiple]')) {
|
||||
var entries = $element.multiselect("getChecked");
|
||||
for(var i = 0; i < entries.length; i++) {
|
||||
value = value + "\n" + entries[i].value;
|
||||
}
|
||||
value = $.trim(value);
|
||||
} else {
|
||||
value = $element.val();
|
||||
}
|
||||
this.configModel.set($element.attr('id'), value);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates a checkbox element according to the provided value
|
||||
*
|
||||
* @param {jQuery} $element
|
||||
* @param {string|number} value
|
||||
* @private
|
||||
*/
|
||||
_setCheckBox: function($element, value) {
|
||||
if(parseInt(value, 10) === 1) {
|
||||
$element.prop('checked', 'checked');
|
||||
} else {
|
||||
$element.removeAttr('checked');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* this is called when the filter mode is switched to assisted. The
|
||||
* concrete tab view should implement this, to load LDAP features
|
||||
* (e.g. object classes, groups, attributes…), if necessary.
|
||||
*/
|
||||
considerFeatureRequests: function() {},
|
||||
|
||||
/**
|
||||
* this is called when the filter mode is switched to Assisted. The
|
||||
* concrete tab view should request the compilation of the respective
|
||||
* filter.
|
||||
*/
|
||||
requestCompileFilter: function() {
|
||||
this.configModel.requestWizard(this.filterName);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the filter mode initially and resets the "isToggling" marker.
|
||||
* This method is called after a save operation against the mode key.
|
||||
*
|
||||
* @param {any} mode -
|
||||
*/
|
||||
setFilterModeOnce: function(mode) {
|
||||
this.isToggling = false;
|
||||
if(!this.filterModeInitialized) {
|
||||
this.filterModeInitialized = true;
|
||||
this.setFilterMode(mode);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the filter mode according to the provided configuration value
|
||||
*
|
||||
* @param {string} mode
|
||||
*/
|
||||
setFilterMode: function(mode) {
|
||||
if(parseInt(mode, 10) === this.configModel.FILTER_MODE_ASSISTED) {
|
||||
this.parsedFilterMode = this.configModel.FILTER_MODE_ASSISTED;
|
||||
this.considerFeatureRequests();
|
||||
this._setFilterModeAssisted();
|
||||
if(this.isActive) {
|
||||
// filter compilation should happen only, if the mode was
|
||||
// switched manually, but not when initiating the view
|
||||
this.requestCompileFilter();
|
||||
}
|
||||
} else {
|
||||
this._setFilterModeRaw();
|
||||
this.parsedFilterMode = this.configModel.FILTER_MODE_RAW;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the UI so that it represents the assisted mode setting
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_setFilterModeAssisted: function() {
|
||||
var view = this;
|
||||
this.$filterModeRawContainer.addClass('invisible');
|
||||
var filter = this.$filterModeRawContainer.find('.ldapFilterInputElement').val();
|
||||
this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
|
||||
this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').removeClass('hidden');
|
||||
$.each(this.filterModeDisableableElements, function(i, $element) {
|
||||
view.enableElement($element);
|
||||
});
|
||||
if(!_.isUndefined(this.filterModeStateElement)) {
|
||||
if (this.filterModeStateElement.status === 'enabled') {
|
||||
this.enableElement(this.filterModeStateElement.$element);
|
||||
} else {
|
||||
this.filterModeStateElement.status = 'disabled';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the UI so that it represents the raw mode setting
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_setFilterModeRaw: function() {
|
||||
var view = this;
|
||||
this.$filterModeRawContainer.removeClass('invisible');
|
||||
this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').addClass('hidden');
|
||||
$.each(this.filterModeDisableableElements, function (i, $element) {
|
||||
view.disableElement($element);
|
||||
});
|
||||
|
||||
if(!_.isUndefined(this.filterModeStateElement)) {
|
||||
if(this.filterModeStateElement.$element.multiselect().attr('disabled') === 'disabled') {
|
||||
this.filterModeStateElement.status = 'disabled';
|
||||
} else {
|
||||
this.filterModeStateElement.status = 'enabled';
|
||||
}
|
||||
}
|
||||
if(!_.isUndefined(this.filterModeStateElement)) {
|
||||
this.disableElement(this.filterModeStateElement.$element);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @callback toggleConfirmCallback
|
||||
* @param {boolean} isConfirmed
|
||||
*/
|
||||
|
||||
/**
|
||||
* shows a confirmation dialogue before switching from raw to assisted
|
||||
* mode if experienced mode is enabled.
|
||||
*
|
||||
* @param {toggleConfirmCallback} toggleFnc
|
||||
* @private
|
||||
*/
|
||||
_toggleRawFilterModeConfirmation: function(toggleFnc) {
|
||||
if( !this.isExperiencedMode()
|
||||
|| this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED
|
||||
) {
|
||||
toggleFnc(true);
|
||||
} else {
|
||||
OC.dialogs.confirm(
|
||||
t('user_ldap', 'Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?'),
|
||||
t('user_ldap', 'Mode switch'),
|
||||
toggleFnc
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* toggles the visibility of a raw filter container and so also the
|
||||
* state of the multi-select controls. The model is requested to save
|
||||
* the state.
|
||||
*/
|
||||
_toggleRawFilterMode: function() {
|
||||
var view = this;
|
||||
this._toggleRawFilterModeConfirmation(function(isConfirmed) {
|
||||
if(!isConfirmed) {
|
||||
return;
|
||||
}
|
||||
/** var {number} */
|
||||
var mode;
|
||||
if (view.parsedFilterMode === view.configModel.FILTER_MODE_ASSISTED) {
|
||||
mode = view.configModel.FILTER_MODE_RAW;
|
||||
} else {
|
||||
mode = view.configModel.FILTER_MODE_ASSISTED;
|
||||
}
|
||||
view.setFilterMode(mode);
|
||||
/** @var {viewSaveInfo} */
|
||||
var saveInfo = {
|
||||
val: function () {
|
||||
return mode;
|
||||
},
|
||||
attr: function () {
|
||||
return view.filterModeKey;
|
||||
},
|
||||
is: function () {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
view._requestSave(saveInfo);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @typedef {object} filterModeStateElementObj
|
||||
* @property {string} status - either "enabled" or "disabled"
|
||||
* @property {jQuery} $element
|
||||
*/
|
||||
|
||||
/**
|
||||
* initializes a raw filter mode switcher
|
||||
*
|
||||
* @param {jQuery} $switcher - the element receiving the click
|
||||
* @param {jQuery} $filterModeRawContainer - contains the raw filter
|
||||
* input elements
|
||||
* @param {jQuery[]} filterModeDisableableElements - an array of elements
|
||||
* not belonging to the raw filter part that shall be en/disabled.
|
||||
* @param {string} filterModeKey - the setting key that save the state
|
||||
* of the mode
|
||||
* @param {filterModeStateElementObj} [filterModeStateElement] - one element
|
||||
* which status (enabled or not) is tracked by a setting
|
||||
* @private
|
||||
*/
|
||||
_initFilterModeSwitcher: function(
|
||||
$switcher,
|
||||
$filterModeRawContainer,
|
||||
filterModeDisableableElements,
|
||||
filterModeKey,
|
||||
filterModeStateElement
|
||||
) {
|
||||
this.$filterModeRawContainer = $filterModeRawContainer;
|
||||
this.filterModeDisableableElements = filterModeDisableableElements;
|
||||
this.filterModeStateElement = filterModeStateElement;
|
||||
this.filterModeKey = filterModeKey;
|
||||
var view = this;
|
||||
$switcher.click(function() {
|
||||
if(view.isToggling) {
|
||||
return;
|
||||
}
|
||||
view.isToggling = true;
|
||||
view._toggleRawFilterMode();
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabGeneric = WizardTabGeneric;
|
||||
})();
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the server tab
|
||||
* in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabGroupFilter = OCA.LDAP.Wizard.WizardTabAbstractFilter.subClass({
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
init: function (fotf, tabIndex, tabID) {
|
||||
tabID = '#ldapWizard4';
|
||||
var items = {
|
||||
ldap_groupfilter_objectclass: {
|
||||
$element: $('#ldap_groupfilter_objectclass'),
|
||||
setMethod: 'setObjectClass',
|
||||
keyName: 'ldap_groupfilter_objectclass',
|
||||
featureName: 'GroupObjectClasses'
|
||||
},
|
||||
ldap_group_filter_mode: {
|
||||
setMethod: 'setFilterModeOnce'
|
||||
},
|
||||
ldap_groupfilter_groups: {
|
||||
$element: $('#ldap_groupfilter_groups'),
|
||||
setMethod: 'setGroups',
|
||||
keyName: 'ldap_groupfilter_groups',
|
||||
featureName: 'GroupsForGroups',
|
||||
$relatedElements: $(
|
||||
tabID + ' .ldapGroupListAvailable,' +
|
||||
tabID + ' .ldapGroupListSelected,' +
|
||||
tabID + ' .ldapManyGroupsSearch'
|
||||
)
|
||||
},
|
||||
ldap_group_filter: {
|
||||
$element: $('#ldap_group_filter'),
|
||||
setMethod: 'setFilter',
|
||||
keyName: 'ldap_group_filter'
|
||||
},
|
||||
groupFilterRawToggle: {
|
||||
$element: $('#toggleRawGroupFilter')
|
||||
},
|
||||
groupFilterRawContainer: {
|
||||
$element: $('#rawGroupFilterContainer')
|
||||
},
|
||||
ldap_group_count: {
|
||||
$element: $('#ldap_group_count'),
|
||||
$relatedElements: $('.ldapGetGroupCount'),
|
||||
setMethod: 'setCount',
|
||||
keyName: 'ldap_group_count'
|
||||
}
|
||||
};
|
||||
this.setManagedItems(items);
|
||||
this.manyGroupsSupport = true;
|
||||
this._super(fotf, tabIndex, tabID);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getObjectClassItem: function () {
|
||||
return this.managedItems.ldap_groupfilter_objectclass;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getGroupsItem: function () {
|
||||
return this.managedItems.ldap_groupfilter_groups;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getFilterItem: function () {
|
||||
return this.managedItems.ldap_group_filter;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getToggleItem: function () {
|
||||
return this.managedItems.groupFilterRawToggle;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getRawFilterContainerItem: function () {
|
||||
return this.managedItems.groupFilterRawContainer;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getCountItem: function () {
|
||||
return this.managedItems.ldap_group_count;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {string}
|
||||
*/
|
||||
getFilterModeKey: function () {
|
||||
return 'ldap_group_filter_mode';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabGroupFilter = WizardTabGroupFilter;
|
||||
})();
|
||||
|
|
@ -1,271 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the login filter
|
||||
* tab in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabLoginFilter = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
|
||||
/**
|
||||
* initializes the instance. Always call it after initialization.
|
||||
*
|
||||
* @param {any} tabIndex -
|
||||
* @param {any} tabID -
|
||||
*/
|
||||
init: function (tabIndex, tabID) {
|
||||
this._super(tabIndex, tabID);
|
||||
|
||||
var items = {
|
||||
ldap_loginfilter_username: {
|
||||
$element: $('#ldap_loginfilter_username'),
|
||||
setMethod: 'setLoginAttributeUsername'
|
||||
},
|
||||
ldap_loginfilter_email: {
|
||||
$element: $('#ldap_loginfilter_email'),
|
||||
setMethod: 'setLoginAttributeEmail'
|
||||
},
|
||||
ldap_login_filter_mode: {
|
||||
setMethod: 'setFilterModeOnce'
|
||||
},
|
||||
ldap_loginfilter_attributes: {
|
||||
$element: $('#ldap_loginfilter_attributes'),
|
||||
setMethod: 'setLoginAttributesOther'
|
||||
},
|
||||
ldap_login_filter: {
|
||||
$element: $('#ldap_login_filter'),
|
||||
setMethod: 'setLoginFilter'
|
||||
},
|
||||
loginFilterRawToggle: {
|
||||
$element: $('#toggleRawLoginFilter')
|
||||
},
|
||||
loginFilterRawContainer: {
|
||||
$element: $('#rawLoginFilterContainer')
|
||||
},
|
||||
ldap_test_loginname: {
|
||||
$element: $('#ldap_test_loginname'),
|
||||
$relatedElements: $('.ldapVerifyLoginName')
|
||||
}
|
||||
};
|
||||
this.setManagedItems(items);
|
||||
|
||||
this.filterModeKey = 'ldapLoginFilterMode';
|
||||
this._initMultiSelect(
|
||||
this.managedItems.ldap_loginfilter_attributes.$element,
|
||||
t('user_ldap', 'Select attributes')
|
||||
);
|
||||
this.filterName = 'ldap_login_filter';
|
||||
this._initFilterModeSwitcher(
|
||||
this.managedItems.loginFilterRawToggle.$element,
|
||||
this.managedItems.loginFilterRawContainer.$element,
|
||||
[
|
||||
this.managedItems.ldap_loginfilter_username.$element,
|
||||
this.managedItems.ldap_loginfilter_email.$element,
|
||||
this.managedItems.ldap_loginfilter_attributes.$element
|
||||
],
|
||||
'ldap_login_filter_mode'
|
||||
);
|
||||
_.bindAll(this, 'onVerifyClick', 'onTestLoginnameChange');
|
||||
this.managedItems.ldap_test_loginname.$element.keyup(this.onTestLoginnameChange);
|
||||
this.managedItems.ldap_test_loginname.$relatedElements.click(this.onVerifyClick);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the config model for this view and subscribes to some events.
|
||||
* Also binds the config chooser to the model
|
||||
*
|
||||
* @param {OCA.LDAP.Wizard.ConfigModel} configModel
|
||||
*/
|
||||
setModel: function(configModel) {
|
||||
this._super(configModel);
|
||||
this.configModel.on('configLoaded', this.onConfigSwitch, this);
|
||||
this.configModel.on('configUpdated', this.onConfigUpdated, this);
|
||||
this.configModel.on('receivedLdapFeature', this.onFeatureReceived, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the selected attributes
|
||||
*
|
||||
* @param {Array} attributes
|
||||
*/
|
||||
setLoginAttributesOther: function(attributes) {
|
||||
this.setElementValue(this.managedItems.ldap_loginfilter_attributes.$element, attributes);
|
||||
this.managedItems.ldap_loginfilter_attributes.$element.multiselect('refresh');
|
||||
},
|
||||
|
||||
/**
|
||||
* sets the login list filter
|
||||
*
|
||||
* @param {string} filter
|
||||
*/
|
||||
setLoginFilter: function(filter) {
|
||||
this.setElementValue(this.managedItems.ldap_login_filter.$element, filter);
|
||||
this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the username attribute check box
|
||||
*
|
||||
* @param {string} useUsername contains an int
|
||||
*/
|
||||
setLoginAttributeUsername: function(useUsername) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_loginfilter_username.$element, useUsername
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* updates the email attribute check box
|
||||
*
|
||||
* @param {string} useEmail contains an int
|
||||
*/
|
||||
setLoginAttributeEmail: function(useEmail) {
|
||||
this.setElementValue(
|
||||
this.managedItems.ldap_loginfilter_email.$element, useEmail
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* presents the result of the login name test
|
||||
*
|
||||
* @param {any} result -
|
||||
*/
|
||||
handleLoginTestResult: function(result) {
|
||||
var message;
|
||||
var isHtml = false;
|
||||
if(result.status === 'success') {
|
||||
var usersFound = parseInt(result.changes.ldap_test_loginname, 10);
|
||||
if(usersFound < 1) {
|
||||
var filter = $('<p>').text(result.changes.ldap_test_effective_filter).html();
|
||||
message = t('user_ldap', 'User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command-line validation): <br/>' + filter);
|
||||
console.warn(filter);
|
||||
isHtml = true;
|
||||
} else if(usersFound === 1) {
|
||||
message = t('user_ldap', 'User found and settings verified.');
|
||||
} else if(usersFound > 1) {
|
||||
message = t('user_ldap', 'Consider narrowing your search, as it encompassed many users, only the first one of whom will be able to log in.');
|
||||
}
|
||||
} else {
|
||||
message = t('user_ldap', 'An unspecified error occurred. Please check log and settings.');
|
||||
if(!_.isUndefined(result.message) && result.message) {
|
||||
message = result.message;
|
||||
}
|
||||
if(message === 'Bad search filter') {
|
||||
message = t('user_ldap', 'The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise.');
|
||||
} else if(message === 'connection error') {
|
||||
message = t('user_ldap', 'A connection error to LDAP/AD occurred. Please check host, port and credentials.');
|
||||
} else if(message === 'missing placeholder') {
|
||||
message = t('user_ldap', 'The "%uid" placeholder is missing. It will be replaced with the login name when querying LDAP/AD.');
|
||||
}
|
||||
}
|
||||
OC.Notification.showTemporary(message, {isHTML: isHtml});
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
considerFeatureRequests: function() {
|
||||
if(!this.isActive) {
|
||||
return;
|
||||
}
|
||||
if(this.managedItems.ldap_loginfilter_attributes.$element.find('option').length === 0) {
|
||||
this.disableElement(this.managedItems.ldap_loginfilter_attributes.$element);
|
||||
if(this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED) {
|
||||
this.configModel.requestWizard('ldap_loginfilter_attributes');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
onActivate: function() {
|
||||
this._super();
|
||||
this.considerFeatureRequests();
|
||||
if(!this.managedItems.ldap_login_filter.$element.val()) {
|
||||
this.configModel.requestWizard('ldap_login_filter');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* resets the view when a configuration switch happened.
|
||||
*
|
||||
* @param {WizardTabLoginFilter} view
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
onConfigSwitch: function(view, configuration) {
|
||||
view.managedItems.ldap_loginfilter_attributes.$element.find('option').remove();
|
||||
|
||||
view.onConfigLoaded(view, configuration);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {WizardTabLoginFilter} view
|
||||
* @param {Object} configuration
|
||||
*/
|
||||
onConfigUpdated: function(view, configuration) {
|
||||
// When the user list filter is updated in assisted mode, also
|
||||
// update the login filter automatically.
|
||||
if(
|
||||
!_.isUndefined(configuration.ldap_userlist_filter)
|
||||
&& view.parsedFilterMode === view.configModel.FILTER_MODE_ASSISTED
|
||||
&& _.toArray(configuration).length === 1
|
||||
) {
|
||||
view.configModel.requestWizard('ldap_login_filter');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* if UserObjectClasses are found, the corresponding element will be
|
||||
* updated
|
||||
*
|
||||
* @param {WizardTabLoginFilter} view
|
||||
* @param {FeaturePayload} payload
|
||||
*/
|
||||
onFeatureReceived: function(view, payload) {
|
||||
if(payload.feature === 'AvailableAttributes') {
|
||||
view.equipMultiSelect(view.managedItems.ldap_loginfilter_attributes.$element, payload.data);
|
||||
} else if(payload.feature === 'TestLoginName') {
|
||||
view.handleLoginTestResult(payload.data);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* request to test the provided login name
|
||||
*
|
||||
* @param {Event} event
|
||||
*/
|
||||
onVerifyClick: function(event) {
|
||||
event.preventDefault();
|
||||
var testLogin = this.managedItems.ldap_test_loginname.$element.val();
|
||||
if(!testLogin) {
|
||||
OC.Notification.showTemporary(t('user_ldap', 'Please provide a login name to test against'), 3);
|
||||
} else {
|
||||
this.configModel.requestWizard('ldap_test_loginname', {ldap_test_loginname: testLogin});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* enables/disables the "Verify Settings" button, depending whether
|
||||
* the corresponding text input has a value or not
|
||||
*/
|
||||
onTestLoginnameChange: function() {
|
||||
var loginName = this.managedItems.ldap_test_loginname.$element.val();
|
||||
var beDisabled = !_.isString(loginName) || !loginName.trim();
|
||||
if(beDisabled) {
|
||||
this.disableElement(this.managedItems.ldap_test_loginname.$relatedElements);
|
||||
} else {
|
||||
this.enableElement(this.managedItems.ldap_test_loginname.$relatedElements);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabLoginFilter = WizardTabLoginFilter;
|
||||
})();
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
OCA = OCA || {};
|
||||
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* @classdesc This class represents the view belonging to the server tab
|
||||
* in the LDAP wizard.
|
||||
*/
|
||||
var WizardTabUserFilter = OCA.LDAP.Wizard.WizardTabAbstractFilter.subClass({
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
init: function (fotf, tabIndex, tabID) {
|
||||
tabID = '#ldapWizard2';
|
||||
var items = {
|
||||
ldap_userfilter_objectclass: {
|
||||
$element: $('#ldap_userfilter_objectclass'),
|
||||
setMethod: 'setObjectClass',
|
||||
keyName: 'ldap_userfilter_objectclass',
|
||||
featureName: 'UserObjectClasses'
|
||||
},
|
||||
ldap_user_filter_mode: {
|
||||
setMethod: 'setFilterModeOnce'
|
||||
},
|
||||
ldap_userfilter_groups: {
|
||||
$element: $('#ldap_userfilter_groups'),
|
||||
setMethod: 'setGroups',
|
||||
keyName: 'ldap_userfilter_groups',
|
||||
featureName: 'GroupsForUsers',
|
||||
$relatedElements: $(
|
||||
tabID + ' .ldapGroupListAvailable,' +
|
||||
tabID + ' .ldapGroupListSelected,' +
|
||||
tabID + ' .ldapManyGroupsSearch'
|
||||
)
|
||||
},
|
||||
ldap_userlist_filter: {
|
||||
$element: $('#ldap_userlist_filter'),
|
||||
setMethod: 'setFilter',
|
||||
keyName: 'ldap_userlist_filter'
|
||||
},
|
||||
userFilterRawToggle: {
|
||||
$element: $('#toggleRawUserFilter')
|
||||
},
|
||||
userFilterRawContainer: {
|
||||
$element: $('#rawUserFilterContainer')
|
||||
},
|
||||
ldap_user_count: {
|
||||
$element: $('#ldap_user_count'),
|
||||
$relatedElements: $('.ldapGetUserCount'),
|
||||
setMethod: 'setCount',
|
||||
keyName: 'ldap_user_count'
|
||||
}
|
||||
};
|
||||
this.setManagedItems(items);
|
||||
this.manyGroupsSupport = true;
|
||||
this._super(fotf, tabIndex, tabID);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getObjectClassItem: function () {
|
||||
return this.managedItems.ldap_userfilter_objectclass;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getGroupsItem: function () {
|
||||
return this.managedItems.ldap_userfilter_groups;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getFilterItem: function () {
|
||||
return this.managedItems.ldap_userlist_filter;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getToggleItem: function () {
|
||||
return this.managedItems.userFilterRawToggle;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getRawFilterContainerItem: function () {
|
||||
return this.managedItems.userFilterRawContainer;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Object}
|
||||
*/
|
||||
getCountItem: function () {
|
||||
return this.managedItems.ldap_user_count;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {string}
|
||||
*/
|
||||
getFilterModeKey: function () {
|
||||
return 'ldap_user_filter_mode';
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
overrideErrorMessage: function(message, key) {
|
||||
var original = message;
|
||||
message = this._super(message, key);
|
||||
if(original !== message) {
|
||||
// we pass the parents change
|
||||
return message;
|
||||
}
|
||||
if( key === 'ldap_userfilter_groups'
|
||||
&& message === 'memberOf is not supported by the server'
|
||||
) {
|
||||
message = t('user_ldap', 'The group box was disabled, because the LDAP/AD server does not support memberOf.');
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
OCA.LDAP.Wizard.WizardTabUserFilter = WizardTabUserFilter;
|
||||
})();
|
||||
|
|
@ -14,6 +14,7 @@ use OCA\User_LDAP\ConnectionFactory;
|
|||
use OCA\User_LDAP\Helper;
|
||||
use OCA\User_LDAP\Settings\Admin;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\ApiRoute;
|
||||
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||
|
|
@ -58,6 +59,7 @@ class ConfigAPIController extends OCSController {
|
|||
* 200: Config created successfully
|
||||
*/
|
||||
#[AuthorizedAdminSetting(settings: Admin::class)]
|
||||
#[ApiRoute(verb: 'POST', url: '/api/v1/config')]
|
||||
public function create() {
|
||||
try {
|
||||
$configPrefix = $this->ldapHelper->getNextServerConfigurationPrefix();
|
||||
|
|
@ -82,6 +84,7 @@ class ConfigAPIController extends OCSController {
|
|||
* 200: Config deleted successfully
|
||||
*/
|
||||
#[AuthorizedAdminSetting(settings: Admin::class)]
|
||||
#[ApiRoute(verb: 'DELETE', url: '/api/v1/config/{configID}')]
|
||||
public function delete($configID) {
|
||||
try {
|
||||
$this->ensureConfigIDExists($configID);
|
||||
|
|
@ -103,7 +106,7 @@ class ConfigAPIController extends OCSController {
|
|||
*
|
||||
* @param string $configID ID of the config
|
||||
* @param array<string, mixed> $configData New config
|
||||
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, array<string, mixed>, array{}>
|
||||
* @throws OCSException
|
||||
* @throws OCSBadRequestException Modifying config is not possible
|
||||
* @throws OCSNotFoundException Config not found
|
||||
|
|
@ -111,6 +114,7 @@ class ConfigAPIController extends OCSController {
|
|||
* 200: Config returned
|
||||
*/
|
||||
#[AuthorizedAdminSetting(settings: Admin::class)]
|
||||
#[ApiRoute(verb: 'PUT', url: '/api/v1/config/{configID}')]
|
||||
public function modify($configID, $configData) {
|
||||
try {
|
||||
$this->ensureConfigIDExists($configID);
|
||||
|
|
@ -137,7 +141,7 @@ class ConfigAPIController extends OCSController {
|
|||
throw new OCSException('An issue occurred when modifying the config.');
|
||||
}
|
||||
|
||||
return new DataResponse();
|
||||
return $this->show($configID, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -215,6 +219,7 @@ class ConfigAPIController extends OCSController {
|
|||
* 200: Config returned
|
||||
*/
|
||||
#[AuthorizedAdminSetting(settings: Admin::class)]
|
||||
#[ApiRoute(verb: 'GET', url: '/api/v1/config/{configID}')]
|
||||
public function show($configID, $showPassword = false) {
|
||||
try {
|
||||
$this->ensureConfigIDExists($configID);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ namespace OCA\User_LDAP\Settings;
|
|||
use OCA\User_LDAP\Configuration;
|
||||
use OCA\User_LDAP\Helper;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
use OCP\IL10N;
|
||||
use OCP\Server;
|
||||
use OCP\Settings\IDelegatedSettings;
|
||||
|
|
@ -18,13 +19,11 @@ class Admin implements IDelegatedSettings {
|
|||
public function __construct(
|
||||
private IL10N $l,
|
||||
private ITemplateManager $templateManager,
|
||||
private IInitialState $initialState,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function getForm() {
|
||||
public function getForm(): TemplateResponse {
|
||||
$helper = Server::get(Helper::class);
|
||||
$prefixes = $helper->getServerConfigurationPrefixes();
|
||||
if (count($prefixes) === 0) {
|
||||
|
|
@ -35,19 +34,6 @@ class Admin implements IDelegatedSettings {
|
|||
$prefixes[] = $newPrefix;
|
||||
}
|
||||
|
||||
$hosts = $helper->getServerConfigurationHosts();
|
||||
|
||||
$wControls = $this->templateManager->getTemplate('user_ldap', 'part.wizardcontrols');
|
||||
$wControls = $wControls->fetchPage();
|
||||
$sControls = $this->templateManager->getTemplate('user_ldap', 'part.settingcontrols');
|
||||
$sControls = $sControls->fetchPage();
|
||||
|
||||
$parameters = [];
|
||||
$parameters['serverConfigurationPrefixes'] = $prefixes;
|
||||
$parameters['serverConfigurationHosts'] = $hosts;
|
||||
$parameters['settingControls'] = $sControls;
|
||||
$parameters['wizardControls'] = $wControls;
|
||||
|
||||
// assign default values
|
||||
if (!isset($config)) {
|
||||
$config = new Configuration('', false);
|
||||
|
|
@ -57,13 +43,26 @@ class Admin implements IDelegatedSettings {
|
|||
$parameters[$key . '_default'] = $default;
|
||||
}
|
||||
|
||||
$ldapConfigs = [];
|
||||
foreach ($prefixes as $prefix) {
|
||||
$ldapConfig = new Configuration($prefix);
|
||||
$rawLdapConfig = $ldapConfig->getConfiguration();
|
||||
foreach ($rawLdapConfig as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$rawLdapConfig[$key] = implode(';', $value);
|
||||
}
|
||||
}
|
||||
|
||||
$ldapConfigs[$prefix] = $rawLdapConfig;
|
||||
}
|
||||
|
||||
$this->initialState->provideInitialState('ldapConfigs', $ldapConfigs);
|
||||
$this->initialState->provideInitialState('ldapModuleInstalled', function_exists('ldap_connect'));
|
||||
|
||||
return new TemplateResponse('user_ldap', 'settings', $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the section ID, e.g. 'sharing'
|
||||
*/
|
||||
public function getSection() {
|
||||
public function getSection(): string {
|
||||
return 'ldap';
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +73,7 @@ class Admin implements IDelegatedSettings {
|
|||
*
|
||||
* E.g.: 70
|
||||
*/
|
||||
public function getPriority() {
|
||||
public function getPriority(): int {
|
||||
return 5;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -174,10 +174,10 @@
|
|||
}
|
||||
},
|
||||
"/ocs/v2.php/apps/user_ldap/api/v1/config/{configID}": {
|
||||
"get": {
|
||||
"operationId": "configapi-show",
|
||||
"summary": "Get a configuration",
|
||||
"description": "Output can look like this: <?xml version=\"1.0\"?> <ocs> <meta> <status>ok</status> <statuscode>200</statuscode> <message>OK</message> </meta> <data> <ldapHost>ldaps://my.ldap.server</ldapHost> <ldapPort>7770</ldapPort> <ldapBackupHost></ldapBackupHost> <ldapBackupPort></ldapBackupPort> <ldapBase>ou=small,dc=my,dc=ldap,dc=server</ldapBase> <ldapBaseUsers>ou=users,ou=small,dc=my,dc=ldap,dc=server</ldapBaseUsers> <ldapBaseGroups>ou=small,dc=my,dc=ldap,dc=server</ldapBaseGroups> <ldapAgentName>cn=root,dc=my,dc=ldap,dc=server</ldapAgentName> <ldapAgentPassword>clearTextWithShowPassword=1</ldapAgentPassword> <ldapTLS>1</ldapTLS> <turnOffCertCheck>0</turnOffCertCheck> <ldapIgnoreNamingRules/> <ldapUserDisplayName>displayname</ldapUserDisplayName> <ldapUserDisplayName2>uid</ldapUserDisplayName2> <ldapUserFilterObjectclass>inetOrgPerson</ldapUserFilterObjectclass> <ldapUserFilterGroups></ldapUserFilterGroups> <ldapUserFilter>(&(objectclass=nextcloudUser)(nextcloudEnabled=TRUE))</ldapUserFilter> <ldapUserFilterMode>1</ldapUserFilterMode> <ldapGroupFilter>(&(|(objectclass=nextcloudGroup)))</ldapGroupFilter> <ldapGroupFilterMode>0</ldapGroupFilterMode> <ldapGroupFilterObjectclass>nextcloudGroup</ldapGroupFilterObjectclass> <ldapGroupFilterGroups></ldapGroupFilterGroups> <ldapGroupDisplayName>cn</ldapGroupDisplayName> <ldapGroupMemberAssocAttr>memberUid</ldapGroupMemberAssocAttr> <ldapLoginFilter>(&(|(objectclass=inetOrgPerson))(uid=%uid))</ldapLoginFilter> <ldapLoginFilterMode>0</ldapLoginFilterMode> <ldapLoginFilterEmail>0</ldapLoginFilterEmail> <ldapLoginFilterUsername>1</ldapLoginFilterUsername> <ldapLoginFilterAttributes></ldapLoginFilterAttributes> <ldapQuotaAttribute></ldapQuotaAttribute> <ldapQuotaDefault></ldapQuotaDefault> <ldapEmailAttribute>mail</ldapEmailAttribute> <ldapCacheTTL>20</ldapCacheTTL> <ldapUuidUserAttribute>auto</ldapUuidUserAttribute> <ldapUuidGroupAttribute>auto</ldapUuidGroupAttribute> <ldapOverrideMainServer></ldapOverrideMainServer> <ldapConfigurationActive>1</ldapConfigurationActive> <ldapAttributesForUserSearch>uid;sn;givenname</ldapAttributesForUserSearch> <ldapAttributesForGroupSearch></ldapAttributesForGroupSearch> <ldapExperiencedAdmin>0</ldapExperiencedAdmin> <homeFolderNamingRule></homeFolderNamingRule> <hasMemberOfFilterSupport></hasMemberOfFilterSupport> <useMemberOfToDetectMembership>1</useMemberOfToDetectMembership> <ldapExpertUsernameAttr>uid</ldapExpertUsernameAttr> <ldapExpertUUIDUserAttr>uid</ldapExpertUUIDUserAttr> <ldapExpertUUIDGroupAttr></ldapExpertUUIDGroupAttr> <lastJpegPhotoLookup>0</lastJpegPhotoLookup> <ldapNestedGroups>0</ldapNestedGroups> <ldapPagingSize>500</ldapPagingSize> <turnOnPasswordChange>1</turnOnPasswordChange> <ldapDynamicGroupMemberURL></ldapDynamicGroupMemberURL> </data> </ocs>\nThis endpoint requires admin access",
|
||||
"delete": {
|
||||
"operationId": "configapi-delete",
|
||||
"summary": "Delete a LDAP configuration",
|
||||
"description": "This endpoint requires admin access",
|
||||
"tags": [
|
||||
"configapi"
|
||||
],
|
||||
|
|
@ -199,15 +199,6 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "showPassword",
|
||||
"in": "query",
|
||||
"description": "Whether to show the password",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
|
|
@ -221,7 +212,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Config returned",
|
||||
"description": "Config deleted successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -240,12 +231,7 @@
|
|||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -418,7 +404,12 @@
|
|||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -540,10 +531,10 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"operationId": "configapi-delete",
|
||||
"summary": "Delete a LDAP configuration",
|
||||
"description": "This endpoint requires admin access",
|
||||
"get": {
|
||||
"operationId": "configapi-show",
|
||||
"summary": "Get a configuration",
|
||||
"description": "Output can look like this: <?xml version=\"1.0\"?> <ocs> <meta> <status>ok</status> <statuscode>200</statuscode> <message>OK</message> </meta> <data> <ldapHost>ldaps://my.ldap.server</ldapHost> <ldapPort>7770</ldapPort> <ldapBackupHost></ldapBackupHost> <ldapBackupPort></ldapBackupPort> <ldapBase>ou=small,dc=my,dc=ldap,dc=server</ldapBase> <ldapBaseUsers>ou=users,ou=small,dc=my,dc=ldap,dc=server</ldapBaseUsers> <ldapBaseGroups>ou=small,dc=my,dc=ldap,dc=server</ldapBaseGroups> <ldapAgentName>cn=root,dc=my,dc=ldap,dc=server</ldapAgentName> <ldapAgentPassword>clearTextWithShowPassword=1</ldapAgentPassword> <ldapTLS>1</ldapTLS> <turnOffCertCheck>0</turnOffCertCheck> <ldapIgnoreNamingRules/> <ldapUserDisplayName>displayname</ldapUserDisplayName> <ldapUserDisplayName2>uid</ldapUserDisplayName2> <ldapUserFilterObjectclass>inetOrgPerson</ldapUserFilterObjectclass> <ldapUserFilterGroups></ldapUserFilterGroups> <ldapUserFilter>(&(objectclass=nextcloudUser)(nextcloudEnabled=TRUE))</ldapUserFilter> <ldapUserFilterMode>1</ldapUserFilterMode> <ldapGroupFilter>(&(|(objectclass=nextcloudGroup)))</ldapGroupFilter> <ldapGroupFilterMode>0</ldapGroupFilterMode> <ldapGroupFilterObjectclass>nextcloudGroup</ldapGroupFilterObjectclass> <ldapGroupFilterGroups></ldapGroupFilterGroups> <ldapGroupDisplayName>cn</ldapGroupDisplayName> <ldapGroupMemberAssocAttr>memberUid</ldapGroupMemberAssocAttr> <ldapLoginFilter>(&(|(objectclass=inetOrgPerson))(uid=%uid))</ldapLoginFilter> <ldapLoginFilterMode>0</ldapLoginFilterMode> <ldapLoginFilterEmail>0</ldapLoginFilterEmail> <ldapLoginFilterUsername>1</ldapLoginFilterUsername> <ldapLoginFilterAttributes></ldapLoginFilterAttributes> <ldapQuotaAttribute></ldapQuotaAttribute> <ldapQuotaDefault></ldapQuotaDefault> <ldapEmailAttribute>mail</ldapEmailAttribute> <ldapCacheTTL>20</ldapCacheTTL> <ldapUuidUserAttribute>auto</ldapUuidUserAttribute> <ldapUuidGroupAttribute>auto</ldapUuidGroupAttribute> <ldapOverrideMainServer></ldapOverrideMainServer> <ldapConfigurationActive>1</ldapConfigurationActive> <ldapAttributesForUserSearch>uid;sn;givenname</ldapAttributesForUserSearch> <ldapAttributesForGroupSearch></ldapAttributesForGroupSearch> <ldapExperiencedAdmin>0</ldapExperiencedAdmin> <homeFolderNamingRule></homeFolderNamingRule> <hasMemberOfFilterSupport></hasMemberOfFilterSupport> <useMemberOfToDetectMembership>1</useMemberOfToDetectMembership> <ldapExpertUsernameAttr>uid</ldapExpertUsernameAttr> <ldapExpertUUIDUserAttr>uid</ldapExpertUUIDUserAttr> <ldapExpertUUIDGroupAttr></ldapExpertUUIDGroupAttr> <lastJpegPhotoLookup>0</lastJpegPhotoLookup> <ldapNestedGroups>0</ldapNestedGroups> <ldapPagingSize>500</ldapPagingSize> <turnOnPasswordChange>1</turnOnPasswordChange> <ldapDynamicGroupMemberURL></ldapDynamicGroupMemberURL> </data> </ocs>\nThis endpoint requires admin access",
|
||||
"tags": [
|
||||
"configapi"
|
||||
],
|
||||
|
|
@ -565,6 +556,15 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "showPassword",
|
||||
"in": "query",
|
||||
"description": "Whether to show the password",
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
|
|
@ -578,7 +578,7 @@
|
|||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Config deleted successfully",
|
||||
"description": "Config returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
|
|
@ -597,7 +597,12 @@
|
|||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
apps/user_ldap/src/LDAPSettingsApp.vue
Normal file
11
apps/user_ldap/src/LDAPSettingsApp.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<Settings />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Settings from './views/Settings.vue'
|
||||
</script>
|
||||
294
apps/user_ldap/src/components/SettingsTabs/AdvancedTab.vue
Normal file
294
apps/user_ldap/src/components/SettingsTabs/AdvancedTab.vue
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<fieldset class="ldap-wizard__advanced">
|
||||
<details open name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
|
||||
<summary><h3>{{ t('user_ldap', 'Connection Settings') }}</h3></summary>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label=" t('user_ldap', 'Backup (Replica) Host')"
|
||||
:value="ldapConfigProxy.ldapBackupHost"
|
||||
:helper-text="t('user_ldap', 'Give an optional backup host. It must be a replica of the main LDAP/AD server.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapBackupHost = event.target.value" />
|
||||
|
||||
<NcTextField type="number"
|
||||
:value="ldapConfigProxy.ldapBackupPort"
|
||||
:label="t('user_ldap', 'Backup (Replica) Port') "
|
||||
@change.native="(event) => ldapConfigProxy.ldapBackupPort = event.target.value" />
|
||||
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.ldapOverrideMainServer === '1'"
|
||||
type="switch"
|
||||
:aria-label="t('user_ldap', 'Only connect to the replica server.')"
|
||||
@update:checked="ldapConfigProxy.ldapOverrideMainServer = $event ? '1' : '0'">
|
||||
{{ t('user_ldap', 'Disable Main Server') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.turnOffCertCheck === '1'"
|
||||
:aria-label="t('user_ldap', 'Not recommended, use it for testing only! If connection only works with this option, import the LDAP server\'s SSL certificate in your {instanceName} server.', { instanceName })"
|
||||
@update:checked="ldapConfigProxy.turnOffCertCheck = $event ? '1' : '0'">
|
||||
{{ t('user_ldap', 'Turn off SSL certificate validation.') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcTextField type="number"
|
||||
:label="t('user_ldap', 'Cache Time-To-Live')"
|
||||
:value="ldapConfigProxy.ldapCacheTTL"
|
||||
:helper-text="t('user_ldap', 'in seconds. A change empties the cache.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapCacheTTL = event.target.value" />
|
||||
</details>
|
||||
|
||||
<details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
|
||||
<summary><h3>{{ t('user_ldap', 'Directory Settings') }}</h3></summary>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:value="ldapConfigProxy.ldapUserDisplayName"
|
||||
:label="t('user_ldap', 'User Display Name Field')"
|
||||
:helper-text="t('user_ldap', 'The LDAP attribute to use to generate the user\'s display name.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapUserDisplayName = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:value="ldapConfigProxy.ldapUserDisplayName2"
|
||||
:label="t('user_ldap', '2nd User Display Name Field')"
|
||||
:helper-text="t('user_ldap', 'Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapUserDisplayName2 = event.target.value" />
|
||||
|
||||
<NcTextArea :value="ldapConfigProxy.ldapBaseUsers"
|
||||
:placeholder="t('user_ldap', 'One User Base DN per line')"
|
||||
:label="t('user_ldap', 'Base User Tree')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapBaseUsers = event.target.value" />
|
||||
|
||||
<NcTextArea :value="ldapConfigProxy.ldapAttributesForUserSearch"
|
||||
:placeholder="t('user_ldap', 'Optional; one attribute per line')"
|
||||
:label="t('user_ldap', 'User Search Attributes')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributesForUserSearch = event.target.value" />
|
||||
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.markRemnantsAsDisabled === '1'"
|
||||
:aria-label="t('user_ldap', 'When switched on, users imported from LDAP which are then missing will be disabled')"
|
||||
@update:checked="ldapConfigProxy.markRemnantsAsDisabled = $event ? '1' : '0'">
|
||||
{{ t('user_ldap', 'Disable users missing from LDAP') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:value="ldapConfigProxy.ldapGroupDisplayName"
|
||||
:label="t('user_ldap', 'Group Display Name Field')"
|
||||
:title="t('user_ldap', 'The LDAP attribute to use to generate the groups\'s display name.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapGroupDisplayName = event.target.value" />
|
||||
|
||||
<NcTextArea :value="ldapConfigProxy.ldapBaseGroups"
|
||||
:placeholder="t('user_ldap', 'One Group Base DN per line')"
|
||||
:label="t('user_ldap', 'Base Group Tree')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapBaseGroups = event.target.value" />
|
||||
|
||||
<NcTextArea :value="ldapConfigProxy.ldapAttributesForGroupSearch"
|
||||
:placeholder="t('user_ldap', 'Optional; one attribute per line')"
|
||||
:label="t('user_ldap', 'Group Search Attributes')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributesForGroupSearch = event.target.value" />
|
||||
|
||||
<NcSelect v-model="ldapConfigProxy.ldapGroupMemberAssocAttr"
|
||||
:options="Object.keys(groupMemberAssociation)"
|
||||
:input-label="t('user_ldap', 'Group-Member association')">
|
||||
<template #option="{label: configId}">
|
||||
{{ groupMemberAssociation[configId] }}
|
||||
</template>
|
||||
<template #selected-option="{label: configId}">
|
||||
{{ groupMemberAssociation[configId] }}
|
||||
</template>
|
||||
</NcSelect>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Dynamic Group Member URL')"
|
||||
:value="ldapConfigProxy.ldapDynamicGroupMemberURL"
|
||||
:helper-text="t('user_ldap', 'The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapDynamicGroupMemberURL = event.target.value" />
|
||||
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.ldapNestedGroups === '1'"
|
||||
:aria-label="t('user_ldap', 'When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)')"
|
||||
@update:checked="ldapConfigProxy.ldapNestedGroups = $event ? '1' : '0'">
|
||||
{{ t('user_ldap', 'Nested Groups') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcTextField type="number"
|
||||
:label="t('user_ldap', 'Paging chunksize')"
|
||||
:value="ldapConfigProxy.ldapPagingSize"
|
||||
:helper-text="t('user_ldap', 'Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapPagingSize = event.target.value" />
|
||||
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.turnOnPasswordChange === '1'"
|
||||
:aria-label="t('user_ldap', 'Allow LDAP users to change their password and allow Super Administrators and Group Administrators to change the password of their LDAP users. Only works when access control policies are configured accordingly on the LDAP server. As passwords are sent in plaintext to the LDAP server, transport encryption must be used and password hashing should be configured on the LDAP server.')"
|
||||
@update:checked="ldapConfigProxy.turnOnPasswordChange = $event ? '1' : '0'">
|
||||
{{ t('user_ldap', 'Enable LDAP password changes per user') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
<span class="tablecell">
|
||||
{{ t('user_ldap', '(New password is sent as plain text to LDAP)') }}
|
||||
</span>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Default password policy DN')"
|
||||
:value="ldapConfigProxy.ldapDefaultPPolicyDN"
|
||||
:helper-text="t('user_ldap', 'The DN of a default password policy that will be used for password expiry handling. Works only when LDAP password changes per user are enabled and is only supported by OpenLDAP. Leave empty to disable password expiry handling.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapDefaultPPolicyDN = event.target.value" />
|
||||
</details>
|
||||
|
||||
<details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
|
||||
<summary><h3>{{ t('user_ldap', 'Special Attributes') }}</h3></summary>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:value="ldapConfigProxy.ldapQuotaAttribute"
|
||||
:label="t('user_ldap', 'Quota Field')"
|
||||
:helper-text="t('user_ldap', 'Leave empty for user\'s default quota. Otherwise, specify an LDAP/AD attribute.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapQuotaAttribute = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:value="ldapConfigProxy.ldapQuotaDefault"
|
||||
:label="t('user_ldap', 'Quota Default')"
|
||||
:helper-text="t('user_ldap', 'Override default quota for LDAP users who do not have a quota set in the Quota Field.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapQuotaDefault = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:value="ldapConfigProxy.ldapEmailAttribute"
|
||||
:label="t('user_ldap', 'Email Field')"
|
||||
:helper-text="t('user_ldap', 'Set the user\'s email from their LDAP attribute. Leave it empty for default behaviour.')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapEmailAttribute = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'User Home Folder Naming Rule')"
|
||||
:value="ldapConfigProxy.homeFolderNamingRule"
|
||||
:helper-text="t('user_ldap', 'Leave empty for username (default). Otherwise, specify an LDAP/AD attribute.')"
|
||||
@change.native="(event) => ldapConfigProxy.homeFolderNamingRule = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', '`$home` Placeholder Field')"
|
||||
:value="ldapConfigProxy.ldapExtStorageHomeAttribute"
|
||||
:helper-text="t('user_ldap', '$home in an external storage configuration will be replaced with the value of the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapExtStorageHomeAttribute = event.target.value" />
|
||||
</details>
|
||||
|
||||
<details name="ldap-wizard__advanced__section" class="ldap-wizard__advanced__section">
|
||||
<summary><h3>{{ t('user_ldap', 'User Profile Attributes') }}</h3></summary>
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Phone Field')"
|
||||
:value="ldapConfigProxy.ldapAttributePhone"
|
||||
:helper-text="t('user_ldap', 'User profile Phone will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributePhone = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Website Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeWebsite"
|
||||
:helper-text="t('user_ldap', 'User profile Website will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeWebsite = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Address Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeAddress"
|
||||
:helper-text="t('user_ldap', 'User profile Address will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeAddress = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Twitter Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeTwitter"
|
||||
:helper-text="t('user_ldap', 'User profile Twitter will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeTwitter = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Fediverse Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeFediverse"
|
||||
:helper-text="t('user_ldap', 'User profile Fediverse will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeFediverse = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Organisation Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeOrganisation"
|
||||
:helper-text="t('user_ldap', 'User profile Organisation will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeOrganisation = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Role Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeRole"
|
||||
:helper-text="t('user_ldap', 'User profile Role will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeRole = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Headline Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeHeadline"
|
||||
:helper-text="t('user_ldap', 'User profile Headline will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeHeadline = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Biography Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeBiography"
|
||||
:helper-text="t('user_ldap', 'User profile Biography will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeBiography = event.target.value" />
|
||||
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'Birthdate Field')"
|
||||
:value="ldapConfigProxy.ldapAttributeBirthDate"
|
||||
:helper-text="t('user_ldap', 'User profile Date of birth will be set from the specified attribute')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapAttributeBirthDate = event.target.value" />
|
||||
</details>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcTextField, NcTextArea, NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
|
||||
import { useLDAPConfigsStore } from '../../store/configs'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const ldapConfigProxy = computed(() => ldapConfigsStore.getConfigProxy(props.configId))
|
||||
|
||||
const instanceName = (getCapabilities() as { theming: { name:string } }).theming.name
|
||||
|
||||
const groupMemberAssociation = {
|
||||
uniqueMember: t('user_ldap', 'uniqueMember'),
|
||||
memberUid: t('user_ldap', 'memberUid'),
|
||||
member: t('user_ldap', 'member (AD)'),
|
||||
gidNumber: t('user_ldap', 'gidNumber'),
|
||||
zimbraMailForwardingAddress: t('user_ldap', 'zimbraMailForwardingAddress'),
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__advanced {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&__section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--color-text-lighter);
|
||||
border-radius: var(--border-radius);
|
||||
padding: 8px;
|
||||
|
||||
& > * {
|
||||
margin-top: 12px !important;
|
||||
}
|
||||
|
||||
summary {
|
||||
margin-top: 0 !important;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-lighter);
|
||||
font-size: 16px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &[open] {
|
||||
h3 {
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
64
apps/user_ldap/src/components/SettingsTabs/ExpertTab.vue
Normal file
64
apps/user_ldap/src/components/SettingsTabs/ExpertTab.vue
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<fieldset class="ldap-wizard__expert">
|
||||
<div class="ldap-wizard__expert__line">
|
||||
<strong>{{ t('user_ldap', 'Internal Username') }}</strong>
|
||||
<p id="ldap_expert_username_attr">
|
||||
{{ t('user_ldap', 'By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [a-zA-Z0-9_.@-]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all DAV services. With this setting, the default behavior can be overridden. Changes will have effect only on newly mapped (added) LDAP users. Leave it empty for default behavior.') }}
|
||||
</p>
|
||||
<NcTextField aria-describedby="ldap_expert_username_attr"
|
||||
autocomplete="off"
|
||||
:label="t('user_ldap', 'Internal Username Attribute:')"
|
||||
:value="ldapConfigProxy.ldapExpertUsernameAttr"
|
||||
:label-outside="true"
|
||||
@change.native="(event) => ldapConfigProxy.ldapExpertUsernameAttr = event.target.value" />
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__expert__line">
|
||||
<strong>{{ t('user_ldap', 'Override UUID detection') }}</strong>
|
||||
<p id="ldap_expert_uuid_user_attr">
|
||||
{{ t('user_ldap', 'By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups.') }}
|
||||
</p>
|
||||
<NcTextField aria-describedby="ldap_expert_uuid_user_attr"
|
||||
autocomplete="off"
|
||||
:label="t('user_ldap', 'UUID Attribute for Users')"
|
||||
:value="ldapConfigProxy.ldapExpertUUIDUserAttr"
|
||||
@change.native="(event) => ldapConfigProxy.ldapExpertUUIDUserAttr = event.target.value" />
|
||||
<NcTextField autocomplete="off"
|
||||
:label="t('user_ldap', 'UUID Attribute for Groups')"
|
||||
:value="ldapConfigProxy.ldapExpertUUIDGroupAttr"
|
||||
@change.native="(event) => ldapConfigProxy.ldapExpertUUIDGroupAttr = event.target.value" />
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcTextField } from '@nextcloud/vue'
|
||||
|
||||
import { useLDAPConfigsStore } from '../../store/configs'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const ldapConfigProxy = computed(() => ldapConfigsStore.getConfigProxy(props.configId))
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__expert {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&__line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-inline-start: 32px;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
158
apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue
Normal file
158
apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<fieldset class="ldap-wizard__groups">
|
||||
<legend>
|
||||
{{ t('user_ldap', 'Groups meeting these criteria are available in {instanceName}:', {instanceName}) }}
|
||||
</legend>
|
||||
|
||||
<div class="ldap-wizard__groups__line ldap-wizard__groups__filter-selection">
|
||||
<NcSelect v-model="ldapGroupFilterObjectclass"
|
||||
class="ldap-wizard__groups__group-filter-groups__select"
|
||||
:options="groupObjectClasses"
|
||||
:disabled="ldapConfigProxy.ldapGroupFilterMode === '1'"
|
||||
:input-label="t('user_ldap', 'Only these object classes:')"
|
||||
:multiple="true" />
|
||||
|
||||
<NcSelect v-model="ldapGroupFilterGroups"
|
||||
class="ldap-wizard__groups__group-filter-groups__select"
|
||||
:options="groupGroups"
|
||||
:disabled="ldapConfigProxy.ldapGroupFilterMode === '1'"
|
||||
:input-label="t('user_ldap', 'Only from these groups:')"
|
||||
:multiple="true" />
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__groups__line ldap-wizard__groups__groups-filter">
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.ldapGroupFilterMode === '1'"
|
||||
@update:checked="toggleFilterMode">
|
||||
{{ t('user_ldap', 'Edit LDAP Query') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<div v-if="ldapConfigProxy.ldapGroupFilterMode === '1'">
|
||||
<NcTextArea :value.sync="ldapConfigProxy.ldapGroupFilter"
|
||||
:placeholder="t('user_ldap', 'Edit LDAP Query')"
|
||||
:helper-text="t('user_ldap', 'The filter specifies which LDAP groups shall have access to the {instanceName} instance.', {instanceName})" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<span>{{ t('user_ldap', 'LDAP Filter:') }}</span>
|
||||
<code>{{ ldapConfigProxy.ldapGroupFilter }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__groups__line ldap-wizard__groups__groups-count-check">
|
||||
<NcButton :disabled="loadingGroupCount" @click="countGroups">
|
||||
{{ t('user_ldap', 'Verify settings and count the groups') }}
|
||||
</NcButton>
|
||||
|
||||
<NcLoadingIcon v-if="loadingGroupCount" :size="20" />
|
||||
<span v-if="groupsCountLabel !== undefined && !loadingGroupCount">{{ groupsCountLabel }}</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcButton, NcTextArea, NcCheckboxRadioSwitch, NcSelect, NcLoadingIcon } from '@nextcloud/vue'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
|
||||
import { useLDAPConfigsStore } from '../../store/configs'
|
||||
import { callWizard, showEnableAutomaticFilterInfo } from '../../services/ldapConfigService'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const { ldapConfigs } = storeToRefs(ldapConfigsStore)
|
||||
const ldapConfigProxy = computed(() => ldapConfigsStore.getConfigProxy(props.configId, {
|
||||
ldapGroupFilterObjectclass: getGroupFilter,
|
||||
ldapGroupFilterGroups: getGroupFilter,
|
||||
}))
|
||||
|
||||
const instanceName = (getCapabilities() as { theming: { name:string } }).theming.name
|
||||
|
||||
const groupsCountLabel = ref<number|undefined>(undefined)
|
||||
|
||||
const groupObjectClasses = ref([] as string[])
|
||||
const groupGroups = ref([] as string[])
|
||||
const loadingGroupCount = ref(false)
|
||||
|
||||
const ldapGroupFilterObjectclass = computed({
|
||||
get() { return ldapConfigProxy.value.ldapGroupFilterObjectclass.split(';').filter((item) => item !== '') },
|
||||
set(value) { ldapConfigProxy.value.ldapGroupFilterObjectclass = value.join(';') },
|
||||
})
|
||||
const ldapGroupFilterGroups = computed({
|
||||
get() { return ldapConfigProxy.value.ldapGroupFilterGroups.split(';').filter((item) => item !== '') },
|
||||
set(value) { ldapConfigProxy.value.ldapGroupFilterGroups = value.join(';') },
|
||||
})
|
||||
|
||||
async function init() {
|
||||
const response1 = await callWizard('determineGroupObjectClasses', props.configId)
|
||||
groupObjectClasses.value = response1.options!.ldap_groupfilter_objectclass
|
||||
|
||||
const response2 = await callWizard('determineGroupsForGroups', props.configId)
|
||||
groupGroups.value = response2.options!.ldap_groupfilter_groups
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
async function getGroupFilter() {
|
||||
const response = await callWizard('getGroupFilter', props.configId)
|
||||
// Not using ldapConfig to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapGroupFilter = response.changes!.ldap_group_filter as string
|
||||
}
|
||||
|
||||
async function countGroups() {
|
||||
try {
|
||||
loadingGroupCount.value = true
|
||||
const response = await callWizard('countGroups', props.configId)
|
||||
groupsCountLabel.value = response.changes!.ldap_group_count as number
|
||||
} finally {
|
||||
loadingGroupCount.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleFilterMode(value: boolean) {
|
||||
if (value) {
|
||||
ldapConfigProxy.value.ldapGroupFilterMode = '1'
|
||||
} else {
|
||||
ldapConfigProxy.value.ldapGroupFilterMode = await showEnableAutomaticFilterInfo() ? '0' : '1'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&__line {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
&__filter-selection {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__groups-filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
code {
|
||||
background-color: var(--color-background-dark);
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__groups-count-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
171
apps/user_ldap/src/components/SettingsTabs/LoginTab.vue
Normal file
171
apps/user_ldap/src/components/SettingsTabs/LoginTab.vue
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<fieldset class="ldap-wizard__login">
|
||||
<legend>
|
||||
{{ t('user_ldap', 'When logging in, {instanceName} will find the user based on the following attributes:', { instanceName }) }}
|
||||
</legend>
|
||||
|
||||
<div class="ldap-wizard__login__line ldap-wizard__login__login-attributes">
|
||||
<NcSelect v-model="ldapLoginFilterAttributes"
|
||||
:close-on-select="false"
|
||||
:disabled="ldapLoginFilterMode"
|
||||
:options="filteredLoginFilterOptions"
|
||||
:input-label="t('user_ldap', 'Other Attributes:')"
|
||||
:multiple="true" />
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__login__line ldap-wizard__login__user-login-filter">
|
||||
<NcCheckboxRadioSwitch :model-value="ldapLoginFilterMode"
|
||||
@update:checked="toggleFilterMode">
|
||||
{{ t('user_ldap', 'Edit LDAP Query') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcTextArea v-if="ldapLoginFilterMode"
|
||||
:value="ldapConfigProxy.ldapLoginFilter"
|
||||
:placeholder="t('user_ldap', 'Edit LDAP Query')"
|
||||
:helper-text="t('user_ldap', 'Defines the filter to apply, when login is attempted. `%%uid` replaces the username in the login action. Example: `uid=%%uid`')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapLoginFilter = event.target.value" />
|
||||
<div v-else>
|
||||
<span>{{ t('user_ldap', 'LDAP Filter:') }}</span>
|
||||
<code>{{ ldapConfigProxy.ldapLoginFilter }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__login__line">
|
||||
<NcTextField v-model="testUsername"
|
||||
:helper-text="t('user_ldap', 'Attempts to receive a DN for the given login name and the current login filter')"
|
||||
:placeholder="t('user_ldap', 'Test Login name')"
|
||||
autocomplete="off" />
|
||||
|
||||
<NcButton :disabled="testUsername.length === 0"
|
||||
@click="verifyLoginName">
|
||||
{{ t('user_ldap', 'Verify settings') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcButton, NcTextField, NcTextArea, NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
import { showError, showSuccess, showWarning } from '@nextcloud/dialogs'
|
||||
|
||||
import { useLDAPConfigsStore } from '../../store/configs'
|
||||
import { callWizard, showEnableAutomaticFilterInfo } from '../../services/ldapConfigService'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const { ldapConfigs } = storeToRefs(ldapConfigsStore)
|
||||
const ldapConfigProxy = computed(() => ldapConfigsStore.getConfigProxy(props.configId, {
|
||||
ldapLoginFilterAttributes: getUserLoginFilter,
|
||||
ldapLoginFilterUsername: getUserLoginFilter,
|
||||
ldapLoginFilterEmail: getUserLoginFilter,
|
||||
}))
|
||||
|
||||
const instanceName = (getCapabilities() as { theming: { name:string } }).theming.name
|
||||
const testUsername = ref('')
|
||||
const loginFilterOptions = ref<string[]>([])
|
||||
|
||||
const ldapLoginFilterAttributes = computed({
|
||||
get() { return ldapConfigProxy.value.ldapLoginFilterAttributes.split(';').filter((item) => item !== '') },
|
||||
set(value) { ldapConfigProxy.value.ldapLoginFilterAttributes = value.join(';') },
|
||||
})
|
||||
|
||||
const ldapLoginFilterMode = computed(() => ldapConfigProxy.value.ldapLoginFilterMode === '1')
|
||||
const filteredLoginFilterOptions = computed(() => loginFilterOptions.value.filter((option) => !ldapLoginFilterAttributes.value.includes(option)))
|
||||
|
||||
async function init() {
|
||||
const response = await callWizard('determineAttributes', props.configId)
|
||||
loginFilterOptions.value = response.options!.ldap_loginfilter_attributes
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
async function getUserLoginFilter() {
|
||||
if (ldapConfigProxy.value.ldapLoginFilterMode === '0') {
|
||||
const response = await callWizard('getUserLoginFilter', props.configId)
|
||||
// Not using ldapConfig to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapLoginFilter = response.changes!.ldap_login_filter as string
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyLoginName() {
|
||||
try {
|
||||
const response = await callWizard('testLoginName', props.configId, { ldap_test_loginname: testUsername.value })
|
||||
|
||||
const testLoginName = response.changes!.ldap_test_loginname as number
|
||||
const testEffectiveFilter = response.changes!.ldap_test_effective_filter as string
|
||||
|
||||
if (testLoginName < 1) {
|
||||
showError(t('user_ldap', 'User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command-line validation): {filter}', { filter: testEffectiveFilter }))
|
||||
} else if (testLoginName === 1) {
|
||||
showSuccess(t('user_ldap', 'User found and settings verified.'))
|
||||
} else if (testLoginName > 1) {
|
||||
showWarning(t('user_ldap', 'Consider narrowing your search, as it encompassed many users, only the first one of whom will be able to log in.'))
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error ?? t('user_ldap', 'An unspecified error occurred. Please check log and settings.')
|
||||
|
||||
switch (message) {
|
||||
case 'Bad search filter':
|
||||
showError(t('user_ldap', 'The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise.'))
|
||||
break
|
||||
case 'connection error':
|
||||
showError(t('user_ldap', 'A connection error to LDAP/AD occurred. Please check host, port and credentials.'))
|
||||
break
|
||||
case 'missing placeholder':
|
||||
showError(t('user_ldap', 'The "%uid" placeholder is missing. It will be replaced with the login name when querying LDAP/AD.'))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleFilterMode(value: boolean) {
|
||||
if (value) {
|
||||
ldapConfigProxy.value.ldapLoginFilterMode = '1'
|
||||
} else {
|
||||
ldapConfigProxy.value.ldapLoginFilterMode = await showEnableAutomaticFilterInfo() ? '0' : '1'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__login {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__line {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
&__login-attributes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__user-login-filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
code {
|
||||
background-color: var(--color-background-dark);
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
192
apps/user_ldap/src/components/SettingsTabs/ServerTab.vue
Normal file
192
apps/user_ldap/src/components/SettingsTabs/ServerTab.vue
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<fieldset class="ldap-wizard__server">
|
||||
<div class="ldap-wizard__server__line">
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.ldapConfigurationActive === '1'"
|
||||
type="switch"
|
||||
:aria-label="t('user_ldap', 'When unchecked, this configuration will be skipped.')"
|
||||
@update:checked="ldapConfigProxy.ldapConfigurationActive = $event ? '1' : '0'">
|
||||
{{ t('user_ldap', 'Configuration Active') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<NcButton :title="t('user_ldap', 'Copy current configuration into new directory binding')"
|
||||
@click="ldapConfigsStore.copyConfig(configId)">
|
||||
<template #icon>
|
||||
<ContentCopy :size="20" />
|
||||
</template>
|
||||
{{ t('user_ldap', 'Copy configuration') }}
|
||||
</NcButton>
|
||||
<NcButton variant="error"
|
||||
@click="ldapConfigsStore.removeConfig(configId)">
|
||||
<template #icon>
|
||||
<Delete :size="20" />
|
||||
</template>
|
||||
{{ t('user_ldap', 'Delete configuration') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__server__line">
|
||||
<NcTextField :value="ldapConfigProxy.ldapHost"
|
||||
:helper-text="t('user_ldap', 'You can omit the protocol, unless you require SSL. If so, start with ldaps://')"
|
||||
:placeholder="t('user_ldap', 'Host')"
|
||||
autocomplete="off"
|
||||
@change.native="(event) => ldapConfigProxy.ldapHost = event.target.value" />
|
||||
<div class="ldap-wizard__server__host__port">
|
||||
<NcTextField :value="ldapConfigProxy.ldapPort"
|
||||
:placeholder="t('user_ldap', 'Port')"
|
||||
type="number"
|
||||
autocomplete="off"
|
||||
@change.native="(event) => ldapConfigProxy.ldapPort = event.target.value" />
|
||||
<NcButton :disabled="loadingGuessPortAndTLS" @click="guessPortAndTLS">
|
||||
{{ t('user_ldap', 'Detect Port') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__server__line">
|
||||
<NcTextField v-model="localLdapAgentName"
|
||||
:helper-text="t('user_ldap', 'The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty.')"
|
||||
:placeholder="t('user_ldap', 'User DN')"
|
||||
autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__server__line">
|
||||
<NcTextField v-model="localLdapAgentPassword"
|
||||
type="password"
|
||||
:helper-text="t('user_ldap', 'For anonymous access, leave DN and Password empty.')"
|
||||
:placeholder="t('user_ldap', 'Password')"
|
||||
autocomplete="off" />
|
||||
|
||||
<NcButton :disabled="!needsToSaveCredentials" @click="updateCredentials">
|
||||
{{ t('user_ldap', 'Save Credentials') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__server__line">
|
||||
<NcTextArea :label="t('user_ldap', 'Base DN')"
|
||||
:value="ldapConfigProxy.ldapBase"
|
||||
:placeholder="t('user_ldap', 'One Base DN per line')"
|
||||
:helper-text="t('user_ldap', 'You can specify Base DN for users and groups in the Advanced tab')"
|
||||
@change.native="(event) => ldapConfigProxy.ldapBase = event.target.value" />
|
||||
|
||||
<NcButton :disabled="loadingGuessBaseDN" @click="guessBaseDN">
|
||||
{{ t('user_ldap', 'Detect Base DN') }}
|
||||
</NcButton>
|
||||
<NcButton :disabled="loadingCountInBaseDN" @click="countInBaseDN">
|
||||
{{ t('user_ldap', 'Test Base DN') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
|
||||
import Delete from 'vue-material-design-icons/Delete.vue'
|
||||
|
||||
import { n, t } from '@nextcloud/l10n'
|
||||
import { NcButton, NcTextField, NcTextArea, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
||||
import { showInfo } from '@nextcloud/dialogs'
|
||||
|
||||
import { useLDAPConfigsStore } from '../../store/configs'
|
||||
import { callWizard } from '../../services/ldapConfigService'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const { ldapConfigs } = storeToRefs(ldapConfigsStore)
|
||||
const ldapConfigProxy = computed(() => ldapConfigsStore.getConfigProxy(props.configId))
|
||||
|
||||
const loadingGuessPortAndTLS = ref(false)
|
||||
const loadingCountInBaseDN = ref(false)
|
||||
const loadingGuessBaseDN = ref(false)
|
||||
|
||||
const localLdapAgentName = ref(ldapConfigProxy.value.ldapAgentName)
|
||||
const localLdapAgentPassword = ref(ldapConfigProxy.value.ldapAgentPassword)
|
||||
const needsToSaveCredentials = computed(() => {
|
||||
return ldapConfigProxy.value.ldapAgentName !== localLdapAgentName.value || ldapConfigProxy.value.ldapAgentPassword !== localLdapAgentPassword.value
|
||||
})
|
||||
|
||||
function updateCredentials() {
|
||||
ldapConfigProxy.value.ldapAgentName = localLdapAgentName.value
|
||||
ldapConfigProxy.value.ldapAgentPassword = localLdapAgentPassword.value
|
||||
}
|
||||
|
||||
async function guessPortAndTLS() {
|
||||
try {
|
||||
loadingGuessPortAndTLS.value = true
|
||||
const { changes } = await callWizard('guessPortAndTLS', props.configId)
|
||||
// Not using ldapConfigProxy to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapPort = (changes!.ldap_port as string) ?? ''
|
||||
} finally {
|
||||
loadingGuessPortAndTLS.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function guessBaseDN() {
|
||||
try {
|
||||
loadingGuessBaseDN.value = true
|
||||
const { changes } = await callWizard('guessBaseDN', props.configId)
|
||||
// Not using ldapConfigProxy to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapBase = (changes!.ldap_base as string) ?? ''
|
||||
} finally {
|
||||
loadingGuessBaseDN.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function countInBaseDN() {
|
||||
try {
|
||||
loadingCountInBaseDN.value = true
|
||||
const { changes } = await callWizard('countInBaseDN', props.configId)
|
||||
const ldapTestBase = changes!.ldap_test_base as number
|
||||
|
||||
if (ldapTestBase < 1) {
|
||||
showInfo(t('user_ldap', 'No object found in the given Base DN. Please revise.'))
|
||||
} else if (ldapTestBase > 1000) {
|
||||
showInfo(t('user_ldap', 'More than 1,000 directory entries available.'))
|
||||
} else {
|
||||
showInfo(
|
||||
n(
|
||||
'user_ldap',
|
||||
'{ldapTestBase} entry available within the provided Base DN',
|
||||
'{ldapTestBase} entries available within the provided Base DN',
|
||||
ldapTestBase,
|
||||
{ ldapTestBase },
|
||||
),
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
loadingCountInBaseDN.value = false
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__server {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
button {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__line {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__host__port {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
180
apps/user_ldap/src/components/SettingsTabs/UsersTab.vue
Normal file
180
apps/user_ldap/src/components/SettingsTabs/UsersTab.vue
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<fieldset class="ldap-wizard__users">
|
||||
{{ t('user_ldap', 'Listing and searching for users is constrained by these criteria:') }}
|
||||
|
||||
<div class="ldap-wizard__users__line ldap-wizard__users__user-filter-object-class">
|
||||
<NcSelect v-model="ldapUserFilterObjectclass"
|
||||
:disabled="ldapConfigProxy.ldapUserFilterMode === '1'"
|
||||
class="ldap-wizard__users__user-filter-object-class__select"
|
||||
:options="userObjectClasses"
|
||||
:input-label="t('user_ldap', 'Only these object classes:')"
|
||||
:multiple="true" />
|
||||
{{ t('user_ldap', 'The most common object classes for users are organizationalPerson, person, user, and inetOrgPerson. If you are not sure which object class to select, please consult your directory admin.') }}
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__users__line ldap-wizard__users__user-filter-groups">
|
||||
<NcSelect v-model="ldapUserFilterGroups"
|
||||
class="ldap-wizard__users__user-filter-groups__select"
|
||||
:disabled="ldapConfigProxy.ldapUserFilterMode === '1'"
|
||||
:options="userGroups"
|
||||
:input-label="t('user_ldap', 'Only from these groups:')"
|
||||
:multiple="true" />
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__users__line ldap-wizard__users__user-filter">
|
||||
<NcCheckboxRadioSwitch :checked="ldapConfigProxy.ldapUserFilterMode === '1'"
|
||||
@update:checked="toggleFilterMode">
|
||||
{{ t('user_ldap', 'Edit LDAP Query') }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
|
||||
<div v-if="ldapConfigProxy.ldapUserFilterMode === '1'">
|
||||
<NcTextArea :value.sync="ldapConfigProxy.ldapUserFilter"
|
||||
:placeholder="t('user_ldap', 'Edit LDAP Query')"
|
||||
:helper-text="t('user_ldap', 'The filter specifies which LDAP users shall have access to the {instanceName} instance.', { instanceName })" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<label>{{ t('user_ldap', 'LDAP Filter:') }}</label>
|
||||
<code>{{ ldapConfigProxy.ldapUserFilter }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__users__line ldap-wizard__users__user-count-check">
|
||||
<NcButton :disabled="loadingUserCount" @click="countUsers">
|
||||
{{ t('user_ldap', 'Verify settings and count users') }}
|
||||
</NcButton>
|
||||
|
||||
<NcLoadingIcon v-if="loadingUserCount" :size="16" />
|
||||
<span v-if="usersCount !== undefined && !loadingUserCount">{{ t('user_ldap', 'User count: {usersCount}', { usersCount }, { escape: false }) }}</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcButton, NcTextArea, NcCheckboxRadioSwitch, NcSelect, NcLoadingIcon } from '@nextcloud/vue'
|
||||
import { getCapabilities } from '@nextcloud/capabilities'
|
||||
|
||||
import { useLDAPConfigsStore } from '../../store/configs'
|
||||
import { callWizard, showEnableAutomaticFilterInfo } from '../../services/ldapConfigService'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const { ldapConfigs } = storeToRefs(ldapConfigsStore)
|
||||
const ldapConfigProxy = computed(() => ldapConfigsStore.getConfigProxy(props.configId, {
|
||||
ldapUserFilterObjectclass: reloadFilters,
|
||||
ldapUserFilterGroups: reloadFilters,
|
||||
}))
|
||||
|
||||
const usersCount = ref<number|undefined>(undefined)
|
||||
const loadingUserCount = ref(false)
|
||||
|
||||
const instanceName = (getCapabilities() as { theming: { name:string } }).theming.name
|
||||
|
||||
const userObjectClasses = ref([] as string[])
|
||||
const userGroups = ref([] as string[])
|
||||
|
||||
const ldapUserFilterObjectclass = computed({
|
||||
get() { return ldapConfigProxy.value.ldapUserFilterObjectclass?.split(';').filter((item) => item !== '') ?? [] },
|
||||
set(value) { ldapConfigProxy.value.ldapUserFilterObjectclass = value.join(';') },
|
||||
})
|
||||
const ldapUserFilterGroups = computed({
|
||||
get() { return ldapConfigProxy.value.ldapUserFilterGroups.split(';').filter((item) => item !== '') },
|
||||
set(value) { ldapConfigProxy.value.ldapUserFilterGroups = value.join(';') },
|
||||
})
|
||||
|
||||
async function init() {
|
||||
const response1 = await callWizard('determineUserObjectClasses', props.configId)
|
||||
userObjectClasses.value = response1.options!.ldap_userfilter_objectclass
|
||||
// Not using ldapConfig to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapUserFilterObjectclass = response1.changes!.ldap_userfilter_objectclass?.join(';') ?? ''
|
||||
|
||||
const response2 = await callWizard('determineGroupsForUsers', props.configId)
|
||||
userGroups.value = response2.options!.ldap_userfilter_groups
|
||||
// Not using ldapConfig to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapUserFilterGroups = response2.changes!.ldap_userfilter_groups?.join(';') ?? ''
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
async function reloadFilters() {
|
||||
if (ldapConfigProxy.value.ldapUserFilterMode === '0') {
|
||||
const response1 = await callWizard('getUserListFilter', props.configId)
|
||||
// Not using ldapConfig to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapUserFilter = response1.changes!.ldap_userlist_filter as string
|
||||
|
||||
const response2 = await callWizard('getUserLoginFilter', props.configId)
|
||||
// Not using ldapConfig to avoid triggering the save logic.
|
||||
ldapConfigs.value[props.configId].ldapLoginFilter = response2.changes!.ldap_userlogin_filter as string
|
||||
}
|
||||
}
|
||||
|
||||
async function countUsers() {
|
||||
try {
|
||||
loadingUserCount.value = true
|
||||
const response = await callWizard('countUsers', props.configId)
|
||||
usersCount.value = response.changes!.ldap_user_count as number
|
||||
} finally {
|
||||
loadingUserCount.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleFilterMode(value: boolean) {
|
||||
if (value) {
|
||||
ldapConfigProxy.value.ldapUserFilterMode = '1'
|
||||
} else {
|
||||
ldapConfigProxy.value.ldapUserFilterMode = await showEnableAutomaticFilterInfo() ? '0' : '1'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__users {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&__line {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
&__user-filter-object-class {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
||||
&__select {
|
||||
min-width: 50%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__user-filter-groups {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__user-filter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
code {
|
||||
background-color: var(--color-background-dark);
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__user-count-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
94
apps/user_ldap/src/components/WizardControls.vue
Normal file
94
apps/user_ldap/src/components/WizardControls.vue
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<div class="ldap-wizard__controls">
|
||||
<NcButton type="primary" :disabled="loading" @click="testSelectedConfig">
|
||||
{{ t('user_ldap', 'Test Configuration') }}
|
||||
</NcButton>
|
||||
|
||||
<NcButton variant="tertiary"
|
||||
href="https://docs.nextcloud.com/server/stable/go.php?to=admin-ldap"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener">
|
||||
<template #icon>
|
||||
<Information :size="20" />
|
||||
</template>
|
||||
<span>{{ t('user_ldap', 'Help') }}</span>
|
||||
</NcButton>
|
||||
|
||||
<template v-if="result !== null && !loading">
|
||||
<span class="ldap-wizard__controls__state_indicator"
|
||||
:class="{'ldap-wizard__controls__state_indicator--valid': isValide}" />
|
||||
|
||||
<span class="ldap-wizard__controls__state_message">
|
||||
{{ result.message }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<NcLoadingIcon v-if="loading" :size="16" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import Information from 'vue-material-design-icons/ContentCopy.vue'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { NcButton, NcLoadingIcon } from '@nextcloud/vue'
|
||||
|
||||
import { testConfiguration } from '../services/ldapConfigService'
|
||||
import { useLDAPConfigsStore } from '../store/configs'
|
||||
|
||||
const props = defineProps<{configId: string}>()
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const { updatingConfig } = storeToRefs(ldapConfigsStore)
|
||||
|
||||
const loading = ref(false)
|
||||
const result = ref<{message: string, status: 'error'|'success'}|null>(null)
|
||||
const isValide = computed(() => result.value?.status === 'success')
|
||||
|
||||
watch(updatingConfig, () => {
|
||||
result.value = null
|
||||
})
|
||||
|
||||
async function testSelectedConfig() {
|
||||
try {
|
||||
loading.value = true
|
||||
result.value = await testConfiguration(props.configId)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard__controls {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
min-height: 45px; // Prevents jumping when the message length need two lines.
|
||||
|
||||
& > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__state_message {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
&__state_indicator {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 100%;
|
||||
background-color: var(--color-element-error);
|
||||
|
||||
&--valid {
|
||||
background-color: var(--color-element-success);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
21
apps/user_ldap/src/main.ts
Normal file
21
apps/user_ldap/src/main.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import Vue from 'vue'
|
||||
import { PiniaVuePlugin } from 'pinia'
|
||||
import { getCSPNonce } from '@nextcloud/auth'
|
||||
|
||||
import { pinia } from './store/index'
|
||||
import LDAPSettingsApp from './LDAPSettingsApp.vue'
|
||||
|
||||
__webpack_nonce__ = getCSPNonce()
|
||||
|
||||
// Init Pinia store
|
||||
Vue.use(PiniaVuePlugin)
|
||||
|
||||
const LDAPSettingsAppVue = Vue.extend(LDAPSettingsApp)
|
||||
new LDAPSettingsAppVue({
|
||||
name: 'LDAPSettingsApp',
|
||||
pinia,
|
||||
}).$mount('#content-ldap-settings')
|
||||
71
apps/user_ldap/src/models/index.ts
Normal file
71
apps/user_ldap/src/models/index.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export type LDAPConfig = {
|
||||
ldapHost: string // Example: ldaps://my.ldap.server</ldapHost>
|
||||
ldapPort: string // Example: 7770
|
||||
ldapBackupHost: string
|
||||
ldapBackupPort: string
|
||||
ldapBase: string // Example: ou=small,dc=my,dc=ldap,dc=server
|
||||
ldapBaseUsers: string // Example: ou=users,ou=small,dc=my,dc=ldap,dc=server
|
||||
ldapBaseGroups: string // Example: ou=small,dc=my,dc=ldap,dc=server
|
||||
ldapAgentName: string // Example: cn=root,dc=my,dc=ldap,dc=server
|
||||
ldapAgentPassword: string // Example: clearTextWithShowPassword=1
|
||||
ldapTLS: '0'|'1' // Example: 1
|
||||
turnOffCertCheck: '0'|'1' // Example: 0
|
||||
ldapIgnoreNamingRules: string // Example: >
|
||||
ldapUserDisplayName: string // Example: displayname
|
||||
ldapUserDisplayName2: string // Example: uid
|
||||
ldapUserFilterObjectclass?: string // Example: inetOrgPerson
|
||||
ldapUserFilterGroups: string
|
||||
ldapUserFilter: string // Example: (&(objectclass=nextcloudUser)(nextcloudEnabled=TRUE))
|
||||
ldapUserFilterMode: '0'|'1' // Example: 1
|
||||
ldapGroupFilter: string // Example: (&(|(objectclass=nextcloudGroup)))
|
||||
ldapGroupFilterMode: '0'|'1' // Example: 0
|
||||
ldapGroupFilterObjectclass: string // Example: nextcloudGroup
|
||||
ldapGroupFilterGroups: string
|
||||
ldapGroupDisplayName: string // Example: cn
|
||||
ldapGroupMemberAssocAttr: string // Example: memberUid
|
||||
ldapLoginFilter: string // Example: (&(|(objectclass=inetOrgPerson))(uid=%uid))
|
||||
ldapLoginFilterMode: '0'|'1' // Example: 0
|
||||
ldapLoginFilterEmail: '0'|'1' // Example: 0
|
||||
ldapLoginFilterUsername: '0'|'1' // Example: 1
|
||||
ldapLoginFilterAttributes: string
|
||||
ldapQuotaAttribute: string
|
||||
ldapQuotaDefault: string
|
||||
ldapEmailAttribute: string // Example: mail
|
||||
ldapCacheTTL: string // Example: 20
|
||||
ldapUuidUserAttribute: string // Example: auto
|
||||
ldapUuidGroupAttribute: string // Example: auto
|
||||
ldapOverrideMainServer: string
|
||||
ldapConfigurationActive: '0'|'1' // Example: 1
|
||||
ldapAttributesForUserSearch: string // Example: uid;sn;givenname
|
||||
ldapAttributesForGroupSearch: string
|
||||
ldapExperiencedAdmin: '0'|'1' // Example: 0
|
||||
homeFolderNamingRule: string
|
||||
hasMemberOfFilterSupport: string
|
||||
useMemberOfToDetectMembership: '0'|'1' // Example: 1
|
||||
ldapExpertUsernameAttr: string // Example: uid
|
||||
ldapExpertUUIDUserAttr: string // Example: uid
|
||||
ldapExpertUUIDGroupAttr: string
|
||||
lastJpegPhotoLookup: '0'|'1' // Example: 0
|
||||
ldapNestedGroups: '0'|'1' // Example: 0
|
||||
ldapPagingSize: string // Example: 500
|
||||
turnOnPasswordChange: '0'|'1' // Example: 1
|
||||
ldapDynamicGroupMemberURL: string
|
||||
markRemnantsAsDisabled: '0'|'1' // Example: 1
|
||||
ldapDefaultPPolicyDN: string
|
||||
ldapExtStorageHomeAttribute: string
|
||||
ldapAttributePhone: string
|
||||
ldapAttributeWebsite: string
|
||||
ldapAttributeAddress: string
|
||||
ldapAttributeTwitter: string
|
||||
ldapAttributeFediverse: string
|
||||
ldapAttributeOrganisation: string
|
||||
ldapAttributeRole: string
|
||||
ldapAttributeHeadline: string
|
||||
ldapAttributeBiography: string
|
||||
ldapAttributeBirthDate: string
|
||||
}
|
||||
189
apps/user_ldap/src/services/ldapConfigService.ts
Normal file
189
apps/user_ldap/src/services/ldapConfigService.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import path from 'path'
|
||||
|
||||
import { DialogSeverity, getDialogBuilder, showError, showSuccess } from '@nextcloud/dialogs'
|
||||
import axios, { AxiosError, type AxiosResponse } from '@nextcloud/axios'
|
||||
import { getAppRootUrl, generateOcsUrl } from '@nextcloud/router'
|
||||
import type { OCSResponse } from '@nextcloud/typings/ocs'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
|
||||
import type { LDAPConfig } from '../models'
|
||||
import logger from './logger'
|
||||
|
||||
const AJAX_ENDPOINT = path.join(getAppRootUrl('user_ldap'), '/ajax')
|
||||
|
||||
export type WizardAction =
|
||||
'guessPortAndTLS' |
|
||||
'guessBaseDN' |
|
||||
'detectEmailAttribute' |
|
||||
'detectUserDisplayNameAttribute' |
|
||||
'determineGroupMemberAssoc' |
|
||||
'determineUserObjectClasses' |
|
||||
'determineGroupObjectClasses' |
|
||||
'determineGroupsForUsers' |
|
||||
'determineGroupsForGroups' |
|
||||
'determineAttributes' |
|
||||
'getUserListFilter' |
|
||||
'getUserLoginFilter' |
|
||||
'getGroupFilter' |
|
||||
'countUsers' |
|
||||
'countGroups' |
|
||||
'countInBaseDN' |
|
||||
'testLoginName' |
|
||||
'save'
|
||||
|
||||
export async function createConfig() {
|
||||
const response = await axios.post(generateOcsUrl('apps/user_ldap/api/v1/config')) as AxiosResponse<OCSResponse<{configID: string}>>
|
||||
logger.debug('Created configuration', { configId: response.data.ocs.data.configID })
|
||||
return response.data.ocs.data.configID
|
||||
}
|
||||
|
||||
export async function copyConfig(configId: string) {
|
||||
const params = new FormData()
|
||||
params.set('copyConfig', configId)
|
||||
|
||||
const response = await axios.post(
|
||||
path.join(AJAX_ENDPOINT, 'getNewServerConfigPrefix.php'),
|
||||
params,
|
||||
) as AxiosResponse<{status: 'error'|'success', configPrefix: string}>
|
||||
|
||||
logger.debug('Created configuration', { configId: response.data.configPrefix })
|
||||
return response.data.configPrefix
|
||||
}
|
||||
|
||||
export async function getConfig(configId: string): Promise<LDAPConfig> {
|
||||
const response = await axios.get(generateOcsUrl('apps/user_ldap/api/v1/config/{configId}', { configId })) as AxiosResponse<OCSResponse<LDAPConfig>>
|
||||
logger.debug('Fetched configuration', { configId, config: response.data.ocs.data })
|
||||
return response.data.ocs.data
|
||||
}
|
||||
|
||||
export async function updateConfig(configId: string, config: LDAPConfig): Promise<LDAPConfig> {
|
||||
const response = await axios.put(
|
||||
generateOcsUrl('apps/user_ldap/api/v1/config/{configId}', { configId }),
|
||||
{ configData: config },
|
||||
) as AxiosResponse<OCSResponse<LDAPConfig>>
|
||||
|
||||
logger.debug('Updated configuration', { configId, config })
|
||||
|
||||
return response.data.ocs.data
|
||||
}
|
||||
|
||||
export async function deleteConfig(configId: string): Promise<boolean> {
|
||||
try {
|
||||
const isConfirmed = await confirmOperation(
|
||||
t('user_ldap', 'Confirm action'),
|
||||
t('user_ldap', 'Are you sure you want to permanently delete this LDAP configuration? This cannot be undone.'),
|
||||
)
|
||||
if (!isConfirmed) {
|
||||
return false
|
||||
}
|
||||
|
||||
await axios.delete(generateOcsUrl('apps/user_ldap/api/v1/config/{configId}', { configId }))
|
||||
logger.debug('Deleted configuration', { configId })
|
||||
} catch (error) {
|
||||
const errorResponse = (error as AxiosError<OCSResponse>).response
|
||||
showError(errorResponse?.data.ocs.meta.message || t('user_ldap', 'Fail to delete config'))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export async function testConfiguration(configId: string) {
|
||||
const params = new FormData()
|
||||
params.set('ldap_serverconfig_chooser', configId)
|
||||
|
||||
const response = await axios.post(
|
||||
path.join(AJAX_ENDPOINT, 'testConfiguration.php'),
|
||||
params,
|
||||
) as AxiosResponse<{message: string, status: 'error'|'success'}>
|
||||
|
||||
logger.debug(`Configuration is ${response.data.status === 'success' ? 'valide' : 'invalide'}`, { configId, params, response })
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function clearMapping(subject: 'user' | 'group') {
|
||||
const isConfirmed = await confirmOperation(
|
||||
t('user_ldap', 'Confirm action'),
|
||||
t('user_ldap', 'Are you sure you want to permanently clear the LDAP mapping? This cannot be undone.'),
|
||||
)
|
||||
if (!isConfirmed) {
|
||||
return false
|
||||
}
|
||||
|
||||
const params = new FormData()
|
||||
params.set('ldap_clear_mapping', subject)
|
||||
|
||||
const response = await axios.post(
|
||||
path.join(AJAX_ENDPOINT, 'clearMappings.php'),
|
||||
params,
|
||||
)
|
||||
|
||||
if (response.data.status === 'success') {
|
||||
logger.debug('Cleared mapping', { subject, params, response })
|
||||
showSuccess(t('user_ldap', 'Mapping cleared'))
|
||||
} else {
|
||||
showError(t('user_ldap', 'Failed to clear mapping'))
|
||||
}
|
||||
}
|
||||
|
||||
export async function callWizard(action: WizardAction, configId: string, extraParams: Record<string, string> = {}) {
|
||||
const params = new FormData()
|
||||
params.set('action', action)
|
||||
params.set('ldap_serverconfig_chooser', configId)
|
||||
|
||||
Object.entries(extraParams).forEach(([key, value]) => {
|
||||
params.set(key, value)
|
||||
})
|
||||
|
||||
const response = await axios.post(
|
||||
path.join(AJAX_ENDPOINT, 'wizard.php'),
|
||||
params,
|
||||
) as AxiosResponse<{ status: 'error', message?: string} | {status: 'success', changes?: Record<string, unknown>, options?: Record<string, []>}>
|
||||
|
||||
logger.debug(`Called wizard action: ${action}`, { configId, params, response })
|
||||
|
||||
if (response.data.status === 'error') {
|
||||
const message = response.data.message ?? t('user_ldap', 'An error occurred')
|
||||
showError(message)
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
export async function showEnableAutomaticFilterInfo() {
|
||||
return await confirmOperation(
|
||||
t('user_ldap', 'Mode switch'),
|
||||
t('user_ldap', 'Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?'),
|
||||
)
|
||||
}
|
||||
|
||||
export async function confirmOperation(name: string, text: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const dialog = getDialogBuilder(name)
|
||||
.setText(text)
|
||||
.setSeverity(DialogSeverity.Warning)
|
||||
.addButton({
|
||||
label: t('user_ldap', 'Cancel'),
|
||||
callback() {
|
||||
dialog.hide()
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
.addButton({
|
||||
label: t('user_ldap', 'Confirm'),
|
||||
variant: 'error',
|
||||
callback() {
|
||||
resolve(true)
|
||||
},
|
||||
})
|
||||
.build()
|
||||
|
||||
dialog.show()
|
||||
})
|
||||
}
|
||||
11
apps/user_ldap/src/services/logger.ts
Normal file
11
apps/user_ldap/src/services/logger.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { getLoggerBuilder } from '@nextcloud/logger'
|
||||
|
||||
export default getLoggerBuilder()
|
||||
.setApp('LDAP')
|
||||
.detectUser()
|
||||
.build()
|
||||
75
apps/user_ldap/src/store/configs.ts
Normal file
75
apps/user_ldap/src/store/configs.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import Vue, { computed, ref } from 'vue'
|
||||
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
||||
import { callWizard, copyConfig, createConfig, deleteConfig, getConfig } from '../services/ldapConfigService'
|
||||
import type { LDAPConfig } from '../models'
|
||||
|
||||
export const useLDAPConfigsStore = defineStore('ldap-configs', () => {
|
||||
const ldapConfigs = ref(loadState('user_ldap', 'ldapConfigs') as Record<string, LDAPConfig>)
|
||||
const selectedConfigId = ref<string>(Object.keys(ldapConfigs.value)[0])
|
||||
const selectedConfig = computed(() => ldapConfigs.value[selectedConfigId.value])
|
||||
const updatingConfig = ref(0)
|
||||
|
||||
function getConfigProxy<J>(configId: string, postSetHooks: Partial<Record<keyof LDAPConfig, (value: J) => void >> = {}) {
|
||||
return new Proxy(ldapConfigs.value[configId], {
|
||||
get(target, property) {
|
||||
return target[property]
|
||||
},
|
||||
set(target, property: string, newValue) {
|
||||
target[property] = newValue
|
||||
|
||||
;(async () => {
|
||||
updatingConfig.value++
|
||||
await callWizard('save', configId, { cfgkey: property, cfgval: newValue })
|
||||
updatingConfig.value--
|
||||
|
||||
if (postSetHooks[property] !== undefined) {
|
||||
postSetHooks[property](target[property])
|
||||
}
|
||||
})()
|
||||
|
||||
return true
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function create() {
|
||||
const configId = await createConfig()
|
||||
Vue.set(ldapConfigs.value, configId, await getConfig(configId))
|
||||
selectedConfigId.value = configId
|
||||
return configId
|
||||
}
|
||||
|
||||
async function _copyConfig(fromConfigId: string) {
|
||||
const configId = await copyConfig(fromConfigId)
|
||||
Vue.set(ldapConfigs.value, configId, { ...ldapConfigs.value[fromConfigId] })
|
||||
selectedConfigId.value = configId
|
||||
return configId
|
||||
}
|
||||
|
||||
async function removeConfig(configId: string) {
|
||||
const result = await deleteConfig(configId)
|
||||
if (result === true) {
|
||||
Vue.delete(ldapConfigs.value, configId)
|
||||
}
|
||||
|
||||
selectedConfigId.value = Object.keys(ldapConfigs.value)[0] ?? await create()
|
||||
}
|
||||
|
||||
return {
|
||||
ldapConfigs,
|
||||
selectedConfigId,
|
||||
selectedConfig,
|
||||
updatingConfig,
|
||||
getConfigProxy,
|
||||
create,
|
||||
copyConfig: _copyConfig,
|
||||
removeConfig,
|
||||
}
|
||||
})
|
||||
8
apps/user_ldap/src/store/index.ts
Normal file
8
apps/user_ldap/src/store/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export const pinia = createPinia()
|
||||
186
apps/user_ldap/src/views/Settings.vue
Normal file
186
apps/user_ldap/src/views/Settings.vue
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
<template>
|
||||
<form class="ldap-wizard">
|
||||
<h2>{{ t('user_ldap', 'LDAP/AD integration') }}</h2>
|
||||
|
||||
<NcNoteCard v-if="!ldapModuleInstalled"
|
||||
type="warning"
|
||||
:text="t('user_ldap', 'The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.')" />
|
||||
|
||||
<template v-if="ldapModuleInstalled">
|
||||
<div class="ldap-wizard__config-selection">
|
||||
<NcSelect v-if="selectedConfigId !== undefined"
|
||||
v-model="selectedConfigId"
|
||||
:options="Object.keys(ldapConfigs)"
|
||||
:input-label="t('user_ldap', 'Select LDAP Config')">
|
||||
<template #option="{label: configId}">
|
||||
{{ `${configId}: ${ldapConfigs[configId].ldapHost}` }}
|
||||
</template>
|
||||
<template #selected-option="{label: configId}">
|
||||
{{ `${configId}: ${ldapConfigs[configId].ldapHost}` }}
|
||||
</template>
|
||||
</NcSelect>
|
||||
<NcButton :label="t('user_ldap','Create New Config')"
|
||||
class="ldap-wizard__config-selection__create-button"
|
||||
@click="ldapConfigsStore.create">
|
||||
<template #icon>
|
||||
<Plus :size="20" />
|
||||
</template>
|
||||
{{ t('user_ldap', 'Create configuration') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedConfigId !== undefined" class="ldap-wizard__tab-container">
|
||||
<div class="ldap-wizard__tab-selection-container">
|
||||
<div class="ldap-wizard__tab-selection">
|
||||
<NcCheckboxRadioSwitch v-for="(tabLabel, tabId) in tabs"
|
||||
:key="tabId"
|
||||
:button-variant="true"
|
||||
:checked.sync="selectedTab"
|
||||
:value="tabId"
|
||||
type="radio"
|
||||
:disabled="tabId !== 'server' && !selectedConfigHasServerInfo"
|
||||
button-variant-grouped="horizontal">
|
||||
{{ tabLabel }}
|
||||
</NcCheckboxRadioSwitch>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ServerTab v-if="selectedTab === 'server'" :config-id="selectedConfigId" />
|
||||
<UsersTab v-else-if="selectedTab === 'users'" :config-id="selectedConfigId" />
|
||||
<LoginTab v-else-if="selectedTab === 'login'" :config-id="selectedConfigId" />
|
||||
<GroupsTab v-else-if="selectedTab === 'groups'" :config-id="selectedConfigId" />
|
||||
<ExpertTab v-else-if="selectedTab === 'expert'" :config-id="selectedConfigId" />
|
||||
<AdvancedTab v-else-if="selectedTab === 'advanced'" :config-id="selectedConfigId" />
|
||||
|
||||
<WizardControls class="ldap-wizard__controls" :config-id="selectedConfigId" />
|
||||
</div>
|
||||
|
||||
<div class="ldap-wizard__clear-mapping">
|
||||
<strong>{{ t('user_ldap', 'Username-LDAP User Mapping') }}</strong>
|
||||
{{ t('user_ldap', 'Usernames are used to store and assign metadata. In order to precisely identify and recognize users, each LDAP user will have an internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage.') }}
|
||||
|
||||
<div class="ldap-wizard__clear-mapping__buttons">
|
||||
<NcButton variant="error"
|
||||
:disabled="clearMappingLoading"
|
||||
@click="requestClearMapping('user')">
|
||||
{{ t('user_ldap', 'Clear Username-LDAP User Mapping') }}
|
||||
</NcButton>
|
||||
<NcButton variant="error"
|
||||
:disabled="clearMappingLoading"
|
||||
@click="requestClearMapping('group')">
|
||||
{{ t('user_ldap', 'Clear Groupname-LDAP Group Mapping') }}
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import Plus from 'vue-material-design-icons/Plus.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
import { NcCheckboxRadioSwitch, NcSelect, NcButton, NcNoteCard } from '@nextcloud/vue'
|
||||
|
||||
import ServerTab from '../components/SettingsTabs/ServerTab.vue'
|
||||
import UsersTab from '../components/SettingsTabs/UsersTab.vue'
|
||||
import LoginTab from '../components/SettingsTabs/LoginTab.vue'
|
||||
import GroupsTab from '../components/SettingsTabs/GroupsTab.vue'
|
||||
import ExpertTab from '../components/SettingsTabs/ExpertTab.vue'
|
||||
import AdvancedTab from '../components/SettingsTabs/AdvancedTab.vue'
|
||||
import WizardControls from '../components/WizardControls.vue'
|
||||
import { useLDAPConfigsStore } from '../store/configs'
|
||||
import { clearMapping } from '../services/ldapConfigService'
|
||||
|
||||
const ldapModuleInstalled = loadState('user_ldap', 'ldapModuleInstalled')
|
||||
|
||||
const tabs = {
|
||||
server: t('user_ldap', 'Server'),
|
||||
users: t('user_ldap', 'Users'),
|
||||
login: t('user_ldap', 'Login Attributes'),
|
||||
groups: t('user_ldap', 'Groups'),
|
||||
advanced: t('user_ldap', 'Advanced'),
|
||||
expert: t('user_ldap', 'Expert'),
|
||||
}
|
||||
|
||||
const ldapConfigsStore = useLDAPConfigsStore()
|
||||
const { ldapConfigs, selectedConfigId, selectedConfig } = storeToRefs(ldapConfigsStore)
|
||||
|
||||
const selectedTab = ref('server')
|
||||
const clearMappingLoading = ref(false)
|
||||
|
||||
const selectedConfigHasServerInfo = computed(() => {
|
||||
return selectedConfig.value.ldapHost !== ''
|
||||
&& selectedConfig.value.ldapPort !== ''
|
||||
&& selectedConfig.value.ldapBase !== ''
|
||||
&& selectedConfig.value.ldapAgentName !== ''
|
||||
&& selectedConfig.value.ldapAgentPassword !== ''
|
||||
})
|
||||
|
||||
async function requestClearMapping(subject: 'user'|'group') {
|
||||
try {
|
||||
clearMappingLoading.value = true
|
||||
await clearMapping(subject)
|
||||
} finally {
|
||||
clearMappingLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ldap-wizard {
|
||||
padding: 16px;
|
||||
max-width: 1000px;
|
||||
|
||||
&__config-selection {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
margin-bottom: 8px;
|
||||
gap: 16px;
|
||||
|
||||
&__create-button {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__tab-selection-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__tab-selection {
|
||||
display: flex;
|
||||
margin-inline-start: -16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-inline-end: -16px;
|
||||
}
|
||||
}
|
||||
|
||||
&__tab-container {
|
||||
border-bottom: 1px solid var(--color-text-light);
|
||||
padding: 0 16px 16px 16px;
|
||||
}
|
||||
|
||||
&__controls {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__clear-mapping {
|
||||
padding: 16px;
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
margin-top: 8px;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,158 +6,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
style('user_ldap', 'vendor/ui-multiselect/jquery.multiselect');
|
||||
|
||||
script('user_ldap', [
|
||||
'vendor/ui-multiselect/src/jquery.multiselect',
|
||||
'wizard/controller',
|
||||
'wizard/configModel',
|
||||
'wizard/view',
|
||||
'wizard/wizardObject',
|
||||
'wizard/wizardTabGeneric',
|
||||
'wizard/wizardTabElementary',
|
||||
'wizard/wizardTabAbstractFilter',
|
||||
'wizard/wizardTabUserFilter',
|
||||
'wizard/wizardTabLoginFilter',
|
||||
'wizard/wizardTabGroupFilter',
|
||||
'wizard/wizardTabAdvanced',
|
||||
'wizard/wizardTabExpert',
|
||||
'wizard/wizardDetectorQueue',
|
||||
'wizard/wizardDetectorGeneric',
|
||||
'wizard/wizardDetectorPort',
|
||||
'wizard/wizardDetectorBaseDN',
|
||||
'wizard/wizardDetectorFeatureAbstract',
|
||||
'wizard/wizardDetectorUserObjectClasses',
|
||||
'wizard/wizardDetectorGroupObjectClasses',
|
||||
'wizard/wizardDetectorGroupsForUsers',
|
||||
'wizard/wizardDetectorGroupsForGroups',
|
||||
'wizard/wizardDetectorSimpleRequestAbstract',
|
||||
'wizard/wizardDetectorFilterUser',
|
||||
'wizard/wizardDetectorFilterLogin',
|
||||
'wizard/wizardDetectorFilterGroup',
|
||||
'wizard/wizardDetectorUserCount',
|
||||
'wizard/wizardDetectorGroupCount',
|
||||
'wizard/wizardDetectorEmailAttribute',
|
||||
'wizard/wizardDetectorUserDisplayNameAttribute',
|
||||
'wizard/wizardDetectorUserGroupAssociation',
|
||||
'wizard/wizardDetectorAvailableAttributes',
|
||||
'wizard/wizardDetectorTestAbstract',
|
||||
'wizard/wizardDetectorTestLoginName',
|
||||
'wizard/wizardDetectorTestBaseDN',
|
||||
'wizard/wizardDetectorTestConfiguration',
|
||||
'wizard/wizardDetectorClearUserMappings',
|
||||
'wizard/wizardDetectorClearGroupMappings',
|
||||
'wizard/wizardFilterOnType',
|
||||
'wizard/wizardFilterOnTypeFactory',
|
||||
'wizard/wizard'
|
||||
]);
|
||||
|
||||
style('user_ldap', 'settings');
|
||||
|
||||
/** @var \OCP\IL10N $l */
|
||||
/** @var array $_ */
|
||||
|
||||
\OCP\Util::addScript('user_ldap', 'main', 'core');
|
||||
?>
|
||||
|
||||
<form id="ldap" class="section" action="#" method="post">
|
||||
<h2><?php p($l->t('LDAP/AD integration')); ?></h2>
|
||||
|
||||
<div id="ldapSettings">
|
||||
<ul>
|
||||
<li id="#ldapWizard1"><a href="#ldapWizard1"><?php p($l->t('Server'));?></a></li>
|
||||
<li id="#ldapWizard2"><a href="#ldapWizard2"><?php p($l->t('Users'));?></a></li>
|
||||
<li id="#ldapWizard3"><a href="#ldapWizard3"><?php p($l->t('Login Attributes'));?></a></li>
|
||||
<li id="#ldapWizard4"><a href="#ldapWizard4"><?php p($l->t('Groups'));?></a></li>
|
||||
<li class="ldapSettingsTabs"><a href="#ldapSettings-2"><?php p($l->t('Expert'));?></a></li>
|
||||
<li class="ldapSettingsTabs"><a href="#ldapSettings-1"><?php p($l->t('Advanced'));?></a></li>
|
||||
</ul>
|
||||
<?php
|
||||
if (!function_exists('ldap_connect')) {
|
||||
print_unescaped('<p class="ldapwarning">' . $l->t('<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.') . '</p>');
|
||||
}
|
||||
?>
|
||||
<?php require_once __DIR__ . '/part.wizard-server.php'; ?>
|
||||
<?php require_once __DIR__ . '/part.wizard-userfilter.php'; ?>
|
||||
<?php require_once __DIR__ . '/part.wizard-loginfilter.php'; ?>
|
||||
<?php require_once __DIR__ . '/part.wizard-groupfilter.php'; ?>
|
||||
<fieldset id="ldapSettings-1">
|
||||
<div id="ldapAdvancedAccordion">
|
||||
<h3><?php p($l->t('Connection Settings'));?></h3>
|
||||
<div>
|
||||
<p><label for="ldap_configuration_active"><?php p($l->t('Configuration Active'));?></label><input type="checkbox" id="ldap_configuration_active" name="ldap_configuration_active" value="1" data-default="<?php p($_['ldap_configuration_active_default']); ?>" aria-describedby="ldap_configuration_active_instructions" title="<?php p($l->t('When unchecked, this configuration will be skipped.'));?>" /><p class="hidden-visually" id="ldap_configuration_active_instructions"><?php p($l->t('When unchecked, this configuration will be skipped.'));?></p></p>
|
||||
<p><label for="ldap_backup_host"><?php p($l->t('Backup (Replica) Host'));?></label><input type="text" id="ldap_backup_host" name="ldap_backup_host" data-default="<?php p($_['ldap_backup_host_default']); ?>" aria-describedby="ldap_backup_host_instructions" title="<?php p($l->t('Give an optional backup host. It must be a replica of the main LDAP/AD server.'));?>"><p class="hidden-visually" id="ldap_backup_host_instructions"><?php p($l->t('Give an optional backup host. It must be a replica of the main LDAP/AD server.'));?></p></p>
|
||||
<p><label for="ldap_backup_port"><?php p($l->t('Backup (Replica) Port'));?></label><input type="number" id="ldap_backup_port" name="ldap_backup_port" data-default="<?php p($_['ldap_backup_port_default']); ?>" /></p>
|
||||
<p><label for="ldap_override_main_server"><?php p($l->t('Disable Main Server'));?></label><input type="checkbox" id="ldap_override_main_server" name="ldap_override_main_server" value="1" data-default="<?php p($_['ldap_override_main_server_default']); ?>" aria-describedby="ldap_override_main_server_instructions" title="<?php p($l->t('Only connect to the replica server.'));?>" /><p class="hidden-visually" id="ldap_override_main_server_instructions"><?php p($l->t('Only connect to the replica server.'));?></p></p>
|
||||
<p><label for="ldap_turn_off_cert_check"><?php p($l->t('Turn off SSL certificate validation.'));?></label><input type="checkbox" id="ldap_turn_off_cert_check" name="ldap_turn_off_cert_check" aria-describedby="ldap_turn_off_cert_check_instructions" title="<?php p($l->t('Not recommended, use it for testing only! If connection only works with this option, import the LDAP server\'s SSL certificate in your %s server.', [$theme->getName()]));?>" data-default="<?php p($_['ldap_turn_off_cert_check_default']); ?>" value="1"><p class="hidden-visually" id="ldap_turn_off_cert_check_instructions"><?php p($l->t('Not recommended, use it for testing only! If connection only works with this option, import the LDAP server\'s SSL certificate in your %s server.', [$theme->getName()]));?></p><br/></p>
|
||||
<p><label for="ldap_cache_ttl"><?php p($l->t('Cache Time-To-Live'));?></label><input type="number" id="ldap_cache_ttl" name="ldap_cache_ttl" aria-describedby="ldap_cache_ttl_instructions" title="<?php p($l->t('in seconds. A change empties the cache.'));?>" data-default="<?php p($_['ldap_cache_ttl_default']); ?>" /><p class="hidden-visually" id="ldap_cache_ttl_instructions"><?php p($l->t('in seconds. A change empties the cache.'));?></p></p>
|
||||
</div>
|
||||
<h3><?php p($l->t('Directory Settings'));?></h3>
|
||||
<div>
|
||||
<p><label for="ldap_display_name"><?php p($l->t('User Display Name Field'));?></label><input type="text" id="ldap_display_name" name="ldap_display_name" data-default="<?php p($_['ldap_display_name_default']); ?>" aria-describedby="ldap_display_name_instructions" title="<?php p($l->t('The LDAP attribute to use to generate the user\'s display name.'));?>" /><p class="hidden-visually" id="ldap_display_name_instructions"><?php p($l->t('The LDAP attribute to use to generate the user\'s display name.'));?></p></p>
|
||||
<p><label for="ldap_user_display_name_2"><?php p($l->t('2nd User Display Name Field'));?></label><input type="text" id="ldap_user_display_name_2" name="ldap_user_display_name_2" data-default="<?php p($_['ldap_user_display_name_2_default']); ?>" aria-describedby="ldap_user_display_name_2_instructions" title="<?php p($l->t('Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«.'));?>" /><p class="hidden-visually" id="ldap_user_display_name_2_instructions"><?php p($l->t('Optional. An LDAP attribute to be added to the display name in brackets. Results in e.g. »John Doe (john.doe@example.org)«.'));?></p></p>
|
||||
<p><label for="ldap_base_users"><?php p($l->t('Base User Tree'));?></label><textarea id="ldap_base_users" name="ldap_base_users" placeholder="<?php p($l->t('One User Base DN per line'));?>" data-default="<?php p($_['ldap_base_users_default']); ?>" aria-describedby="ldap_base_users_instructions" title="<?php p($l->t('Base User Tree'));?>"></textarea><p class="hidden-visually" id="ldap_base_users_instructions"><?php p($l->t('Base User Tree'));?></p></p>
|
||||
<p><label for="ldap_attributes_for_user_search"><?php p($l->t('User Search Attributes'));?></label><textarea id="ldap_attributes_for_user_search" name="ldap_attributes_for_user_search" placeholder="<?php p($l->t('Optional; one attribute per line'));?>" data-default="<?php p($_['ldap_attributes_for_user_search_default']); ?>" aria-describedby="ldap_attributes_for_user_search_instructions" title="<?php p($l->t('User Search Attributes'));?>"></textarea><p class="hidden-visually" id="ldap_attributes_for_user_search_instructions"><?php p($l->t('User Search Attributes'));?></p></p>
|
||||
<p><label for="ldap_mark_remnants_as_disabled"><?php p($l->t('Disable users missing from LDAP'));?></label><input type="checkbox" id="ldap_mark_remnants_as_disabled" name="ldap_mark_remnants_as_disabled" value="1" data-default="<?php p($_['ldap_mark_remnants_as_disabled_default']); ?>" aria-describedby="ldap_mark_remnants_as_disabled_instructions" title="<?php p($l->t('When switched on, users imported from LDAP which are then missing will be disabled'));?>" /><p class="hidden-visually" id="ldap_mark_remnants_as_disabled_instructions"><?php p($l->t('When switched on, users imported from LDAP which are then missing will be disabled'));?></p></p>
|
||||
<p><label for="ldap_group_display_name"><?php p($l->t('Group Display Name Field'));?></label><input type="text" id="ldap_group_display_name" name="ldap_group_display_name" data-default="<?php p($_['ldap_group_display_name_default']); ?>" aria-describedby="ldap_group_display_name_instructions" title="<?php p($l->t('The LDAP attribute to use to generate the groups\'s display name.'));?>" /><p class="hidden-visually" id="ldap_group_display_name_instructions"><?php p($l->t('The LDAP attribute to use to generate the groups\'s display name.'));?></p></p>
|
||||
<p><label for="ldap_base_groups"><?php p($l->t('Base Group Tree'));?></label><textarea id="ldap_base_groups" name="ldap_base_groups" placeholder="<?php p($l->t('One Group Base DN per line'));?>" data-default="<?php p($_['ldap_base_groups_default']); ?>" aria-describedby="ldap_base_groups_instructions" title="<?php p($l->t('Base Group Tree'));?>"></textarea><p class="hidden-visually" id="ldap_base_groups_instructions"><?php p($l->t('Base Group Tree'));?></p></p>
|
||||
<p><label for="ldap_attributes_for_group_search"><?php p($l->t('Group Search Attributes'));?></label><textarea id="ldap_attributes_for_group_search" name="ldap_attributes_for_group_search" placeholder="<?php p($l->t('Optional; one attribute per line'));?>" data-default="<?php p($_['ldap_attributes_for_group_search_default']); ?>" aria-describedby="ldap_attributes_for_group_search_instructions" title="<?php p($l->t('Group Search Attributes'));?>"></textarea><p class="hidden-visually" id="ldap_attributes_for_group_search_instructions"><?php p($l->t('Group Search Attributes'));?></p></p>
|
||||
<p><label for="ldap_group_member_assoc_attribute"><?php p($l->t('Group-Member association'));?></label><select id="ldap_group_member_assoc_attribute" name="ldap_group_member_assoc_attribute" data-default="<?php p($_['ldap_group_member_assoc_attribute_default']); ?>" ><option value="uniqueMember"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'uniqueMember')) {
|
||||
p(' selected');
|
||||
} ?>>uniqueMember</option><option value="memberUid"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'memberUid')) {
|
||||
p(' selected');
|
||||
} ?>>memberUid</option><option value="member"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'member')) {
|
||||
p(' selected');
|
||||
} ?>>member (AD)</option><option value="gidNumber"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'gidNumber')) {
|
||||
p(' selected');
|
||||
} ?>>gidNumber</option><option value="zimbraMailForwardingAddress"<?php if (isset($_['ldap_group_member_assoc_attribute']) && ($_['ldap_group_member_assoc_attribute'] === 'zimbraMailForwardingAddress')) {
|
||||
p(' selected');
|
||||
} ?>>zimbraMailForwardingAddress</option></select></p>
|
||||
<p><label for="ldap_dynamic_group_member_url"><?php p($l->t('Dynamic Group Member URL'));?></label><input type="text" id="ldap_dynamic_group_member_url" name="ldap_dynamic_group_member_url" aria-describedby="ldap_dynamic_group_member_url_instructions" title="<?php p($l->t('The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)'));?>" data-default="<?php p($_['ldap_dynamic_group_member_url_default']); ?>" /><p class="hidden-visually" id="ldap_dynamic_group_member_url_instructions"><?php p($l->t('The LDAP attribute that on group objects contains an LDAP search URL that determines what objects belong to the group. (An empty setting disables dynamic group membership functionality.)'));?></p></p>
|
||||
<p><label for="ldap_nested_groups"><?php p($l->t('Nested Groups'));?></label><input type="checkbox" id="ldap_nested_groups" name="ldap_nested_groups" value="1" data-default="<?php p($_['ldap_nested_groups_default']); ?>" aria-describedby="ldap_nested_groups_instructions" title="<?php p($l->t('When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)'));?>" /><p class="hidden-visually" id="ldap_nested_groups_instructions"><?php p($l->t('When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)'));?></p></p>
|
||||
<p><label for="ldap_paging_size"><?php p($l->t('Paging chunksize'));?></label><input type="number" id="ldap_paging_size" name="ldap_paging_size" aria-describedby="ldap_paging_size_instructions" title="<?php p($l->t('Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)'));?>" data-default="<?php p($_['ldap_paging_size_default']); ?>" /><p class="hidden-visually" id="ldap_paging_size_instructions"><?php p($l->t('Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)'));?></p></p>
|
||||
<p><label for="ldap_turn_on_pwd_change"><?php p($l->t('Enable LDAP password changes per user'));?></label><span class="inlinetable"><span class="tablerow left"><input type="checkbox" id="ldap_turn_on_pwd_change" name="ldap_turn_on_pwd_change" value="1" data-default="<?php p($_['ldap_turn_on_pwd_change_default']); ?>" aria-describedby="ldap_turn_on_pwd_change_instructions" title="<?php p($l->t('Allow LDAP users to change their password and allow Super Administrators and Group Administrators to change the password of their LDAP users. Only works when access control policies are configured accordingly on the LDAP server. As passwords are sent in plaintext to the LDAP server, transport encryption must be used and password hashing should be configured on the LDAP server.'));?>" /><p class="hidden-visually" id="ldap_turn_on_pwd_change_instructions"><?php p($l->t('Allow LDAP users to change their password and allow Super Administrators and Group Administrators to change the password of their LDAP users. Only works when access control policies are configured accordingly on the LDAP server. As passwords are sent in plaintext to the LDAP server, transport encryption must be used and password hashing should be configured on the LDAP server.'));?></p><span class="tablecell"><?php p($l->t('(New password is sent as plain text to LDAP)'));?></span></span>
|
||||
</span><br/></p>
|
||||
<p><label for="ldap_default_ppolicy_dn"><?php p($l->t('Default password policy DN'));?></label><input type="text" id="ldap_default_ppolicy_dn" name="ldap_default_ppolicy_dn" aria-describedby="ldap_default_ppolicy_dn_instructions" title="<?php p($l->t('The DN of a default password policy that will be used for password expiry handling. Works only when LDAP password changes per user are enabled and is only supported by OpenLDAP. Leave empty to disable password expiry handling.'));?>" data-default="<?php p($_['ldap_default_ppolicy_dn_default']); ?>" /><p class="hidden-visually" id="ldap_default_ppolicy_dn_instructions"><?php p($l->t('The DN of a default password policy that will be used for password expiry handling. Works only when LDAP password changes per user are enabled and is only supported by OpenLDAP. Leave empty to disable password expiry handling.'));?></p></p>
|
||||
</div>
|
||||
<h3><?php p($l->t('Special Attributes'));?></h3>
|
||||
<div>
|
||||
<p><label for="ldap_quota_attr"><?php p($l->t('Quota Field'));?></label><input type="text" id="ldap_quota_attr" name="ldap_quota_attr" data-default="<?php p($_['ldap_quota_attr_default']); ?>" aria-describedby="ldap_quota_attr_instructions" title="<?php p($l->t('Leave empty for user\'s default quota. Otherwise, specify an LDAP/AD attribute.'));?>" /><p class="hidden-visually" id="ldap_quota_attr_instructions"><?php p($l->t('Leave empty for user\'s default quota. Otherwise, specify an LDAP/AD attribute.'));?></p></p>
|
||||
<p><label for="ldap_quota_def"><?php p($l->t('Quota Default'));?></label><input type="text" id="ldap_quota_def" name="ldap_quota_def" data-default="<?php p($_['ldap_quota_def_default']); ?>" aria-describedby="ldap_quota_def_instructions" title="<?php p($l->t('Override default quota for LDAP users who do not have a quota set in the Quota Field.'));?>" /><p class="hidden-visually" id="ldap_quota_def_instructions"><?php p($l->t('Override default quota for LDAP users who do not have a quota set in the Quota Field.'));?></p></p>
|
||||
<p><label for="ldap_email_attr"><?php p($l->t('Email Field'));?></label><input type="text" id="ldap_email_attr" name="ldap_email_attr" data-default="<?php p($_['ldap_email_attr_default']); ?>" aria-describedby="ldap_email_attr_instructions" title="<?php p($l->t('Set the user\'s email from their LDAP attribute. Leave it empty for default behaviour.'));?>" /><p class="hidden-visually" id="ldap_email_attr_instructions"><?php p($l->t('Set the user\'s email from their LDAP attribute. Leave it empty for default behaviour.'));?></p></p>
|
||||
<p><label for="home_folder_naming_rule"><?php p($l->t('User Home Folder Naming Rule'));?></label><input type="text" id="home_folder_naming_rule" name="home_folder_naming_rule" aria-describedby="home_folder_naming_rule_instructions" title="<?php p($l->t('Leave empty for username (default). Otherwise, specify an LDAP/AD attribute.'));?>" data-default="<?php p($_['home_folder_naming_rule_default']); ?>" /><p class="hidden-visually" id="home_folder_naming_rule_instructions"><?php p($l->t('Leave empty for username (default). Otherwise, specify an LDAP/AD attribute.'));?></p></p>
|
||||
<p><label for="ldap_ext_storage_home_attribute"> <?php p($l->t('"$home" Placeholder Field')); ?></label><input type="text" id="ldap_ext_storage_home_attribute" name="ldap_ext_storage_home_attribute" aria-describedby="ldap_ext_storage_home_attribute_instructions" title="<?php p($l->t('$home in an external storage configuration will be replaced with the value of the specified attribute')); ?>" data-default="<?php p($_['ldap_ext_storage_home_attribute_default']); ?>"><p class="hidden-visually" id="ldap_ext_storage_home_attribute_instructions"><?php p($l->t('$home in an external storage configuration will be replaced with the value of the specified attribute')); ?></p></p>
|
||||
</div>
|
||||
<h3><?php p($l->t('User Profile Attributes'));?></h3>
|
||||
<div>
|
||||
<p><label for="ldap_attr_phone"> <?php p($l->t('Phone Field')); ?></label><input type="text" id="ldap_attr_phone" name="ldap_attr_phone" title="<?php p($l->t('User profile Phone will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_phone_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_website"> <?php p($l->t('Website Field')); ?></label><input type="text" id="ldap_attr_website" name="ldap_attr_website" title="<?php p($l->t('User profile Website will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_website_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_address"> <?php p($l->t('Address Field')); ?></label><input type="text" id="ldap_attr_address" name="ldap_attr_address" title="<?php p($l->t('User profile Address will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_address_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_twitter"> <?php p($l->t('Twitter Field')); ?></label><input type="text" id="ldap_attr_twitter" name="ldap_attr_twitter" title="<?php p($l->t('User profile Twitter will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_twitter_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_fediverse"> <?php p($l->t('Fediverse Field')); ?></label><input type="text" id="ldap_attr_fediverse" name="ldap_attr_fediverse" title="<?php p($l->t('User profile Fediverse will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_fediverse_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_organisation"> <?php p($l->t('Organisation Field')); ?></label><input type="text" id="ldap_attr_organisation" name="ldap_attr_organisation" title="<?php p($l->t('User profile Organisation will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_organisation_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_role"> <?php p($l->t('Role Field')); ?></label><input type="text" id="ldap_attr_role" name="ldap_attr_role" title="<?php p($l->t('User profile Role will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_role_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_headline"> <?php p($l->t('Headline Field')); ?></label><input type="text" id="ldap_attr_headline" name="ldap_attr_headline" title="<?php p($l->t('User profile Headline will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_headline_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_biography"> <?php p($l->t('Biography Field')); ?></label><input type="text" id="ldap_attr_biography" name="ldap_attr_biography" title="<?php p($l->t('User profile Biography will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_biography_default']); ?>"></p>
|
||||
<p><label for="ldap_attr_birthdate"> <?php p($l->t('Birthdate Field')); ?></label><input type="text" id="ldap_attr_birthdate" name="ldap_attr_birthdate" title="<?php p($l->t('User profile Date of birth will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_birthdate_default']); ?>"></p>
|
||||
<p></p><label for="ldap_attr_pronouns"> <?php p($l->t('Pronouns Field')); ?></label><input type="text" id="ldap_attr_pronouns" name="ldap_attr_pronouns" title="<?php p($l->t('User profile Pronouns will be set from the specified attribute')); ?>" data-default="<?php p($_['ldap_attr_pronouns_default']); ?>"></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php print_unescaped($_['settingControls']); ?>
|
||||
</fieldset>
|
||||
<fieldset id="ldapSettings-2">
|
||||
<p><strong><?php p($l->t('Internal Username'));?></strong></p>
|
||||
<p class="ldapIndent"><?php p($l->t('By default the internal username will be created from the UUID attribute. It makes sure that the username is unique and characters do not need to be converted. The internal username has the restriction that only these characters are allowed: [a-zA-Z0-9_.@-]. Other characters are replaced with their ASCII correspondence or simply omitted. On collisions a number will be added/increased. The internal username is used to identify a user internally. It is also the default name for the user home folder. It is also a part of remote URLs, for instance for all DAV services. With this setting, the default behavior can be overridden. Changes will have effect only on newly mapped (added) LDAP users. Leave it empty for default behavior.'));?></p>
|
||||
<p class="ldapIndent"><label for="ldap_expert_username_attr"><?php p($l->t('Internal Username Attribute:'));?></label><input type="text" id="ldap_expert_username_attr" name="ldap_expert_username_attr" data-default="<?php p($_['ldap_expert_username_attr_default']); ?>" /></p>
|
||||
<p><strong><?php p($l->t('Override UUID detection'));?></strong></p>
|
||||
<p class="ldapIndent"><?php p($l->t('By default, the UUID attribute is automatically detected. The UUID attribute is used to doubtlessly identify LDAP users and groups. Also, the internal username will be created based on the UUID, if not specified otherwise above. You can override the setting and pass an attribute of your choice. You must make sure that the attribute of your choice can be fetched for both users and groups and it is unique. Leave it empty for default behavior. Changes will have effect only on newly mapped (added) LDAP users and groups.'));?></p>
|
||||
<p class="ldapIndent"><label for="ldap_expert_uuid_user_attr"><?php p($l->t('UUID Attribute for Users:'));?></label><input type="text" id="ldap_expert_uuid_user_attr" name="ldap_expert_uuid_user_attr" data-default="<?php p($_['ldap_expert_uuid_user_attr_default']); ?>" /></p>
|
||||
<p class="ldapIndent"><label for="ldap_expert_uuid_group_attr"><?php p($l->t('UUID Attribute for Groups:'));?></label><input type="text" id="ldap_expert_uuid_group_attr" name="ldap_expert_uuid_group_attr" data-default="<?php p($_['ldap_expert_uuid_group_attr_default']); ?>" /></p>
|
||||
<p><strong><?php p($l->t('Username-LDAP User Mapping'));?></strong></p>
|
||||
<p class="ldapIndent"><?php p($l->t('Usernames are used to store and assign metadata. In order to precisely identify and recognize users, each LDAP user will have an internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage.'));?></p>
|
||||
<p class="ldapIndent"><button type="button" id="ldap_action_clear_user_mappings" name="ldap_action_clear_user_mappings"><?php p($l->t('Clear Username-LDAP User Mapping'));?></button><br/><button type="button" id="ldap_action_clear_group_mappings" name="ldap_action_clear_group_mappings"><?php p($l->t('Clear Groupname-LDAP Group Mapping'));?></button></p>
|
||||
<?php print_unescaped($_['settingControls']); ?>
|
||||
</fieldset>
|
||||
</div>
|
||||
<!-- Spinner Template -->
|
||||
<img class="ldapSpinner hidden" src="<?php p(image_path('core', 'loading.gif')); ?>">
|
||||
</form>
|
||||
<div id="content-ldap-settings"></div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace OCA\User_LDAP\Tests\Settings;
|
|||
use OCA\User_LDAP\Configuration;
|
||||
use OCA\User_LDAP\Settings\Admin;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
use OCP\IL10N;
|
||||
use OCP\Server;
|
||||
use OCP\Template\ITemplateManager;
|
||||
|
|
@ -23,16 +24,19 @@ use Test\TestCase;
|
|||
class AdminTest extends TestCase {
|
||||
private IL10N&MockObject $l10n;
|
||||
private ITemplateManager $templateManager;
|
||||
private IInitialState&MockObject $initialState;
|
||||
private Admin $admin;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->templateManager = Server::get(ITemplateManager::class);
|
||||
$this->initialState = $this->createMock(IInitialState::class);
|
||||
|
||||
$this->admin = new Admin(
|
||||
$this->l10n,
|
||||
$this->templateManager,
|
||||
$this->initialState,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -46,10 +50,6 @@ class AdminTest extends TestCase {
|
|||
$sControls = $sControls->fetchPage();
|
||||
|
||||
$parameters = [];
|
||||
$parameters['serverConfigurationPrefixes'] = $prefixes;
|
||||
$parameters['serverConfigurationHosts'] = $hosts;
|
||||
$parameters['settingControls'] = $sControls;
|
||||
$parameters['wizardControls'] = $wControls;
|
||||
|
||||
// assign default values
|
||||
$config = new Configuration('', false);
|
||||
|
|
|
|||
2
dist/4508-4508.js
vendored
2
dist/4508-4508.js
vendored
|
|
@ -1 +1 @@
|
|||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[4508],{64508:(e,r,i)=>{i.r(r),i.d(r,{NcCustomPickerRenderResult:()=>s.N,NcReferenceList:()=>c.a,NcReferencePicker:()=>t.N,NcReferencePickerModal:()=>t.e,NcReferenceWidget:()=>t.f,NcRichText:()=>c.N,NcSearch:()=>t.h,anyLinkProviderId:()=>t.a,default:()=>c.N,getLinkWithPicker:()=>t.g,getProvider:()=>t.b,getProviders:()=>t.c,isCustomPickerElementRegistered:()=>s.c,isWidgetRegistered:()=>s.i,registerCustomPickerElement:()=>s.e,registerWidget:()=>s.r,renderCustomPickerElement:()=>s.f,renderWidget:()=>s.a,searchProvider:()=>t.s,sortProviders:()=>t.d});var c=i(7838),t=i(86432),s=i(52781)}}]);
|
||||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[4508],{64508:(e,r,i)=>{i.r(r),i.d(r,{NcCustomPickerRenderResult:()=>s.N,NcReferenceList:()=>c.a,NcReferencePicker:()=>t.N,NcReferencePickerModal:()=>t.e,NcReferenceWidget:()=>t.f,NcRichText:()=>c.N,NcSearch:()=>t.h,anyLinkProviderId:()=>t.a,default:()=>c.N,getLinkWithPicker:()=>t.g,getProvider:()=>t.b,getProviders:()=>t.c,isCustomPickerElementRegistered:()=>s.c,isWidgetRegistered:()=>s.i,registerCustomPickerElement:()=>s.e,registerWidget:()=>s.r,renderCustomPickerElement:()=>s.f,renderWidget:()=>s.a,searchProvider:()=>t.s,sortProviders:()=>t.d});var c=i(88961),t=i(90690),s=i(52781)}}]);
|
||||
6
dist/4508-4508.js.license
vendored
6
dist/4508-4508.js.license
vendored
|
|
@ -117,6 +117,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
|
|
@ -174,6 +177,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-buffer
|
||||
- version: 1.1.6
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- md5
|
||||
- version: 2.3.0
|
||||
- license: BSD-3-Clause
|
||||
|
|
|
|||
2
dist/5258-5258.js
vendored
2
dist/5258-5258.js
vendored
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[5258],{16343:(e,t,n)=>{n.d(t,{a:()=>i,e:()=>l});var c=n(59097),u=n(43850),r=n(24715);const a=(0,c.c0)("nextcloud-vue").persist(!0).build();let o;function l(e,t=10){o||(o=new u.EmojiIndex(r));const n=function(){const e=Number.parseInt(a.getItem("NcEmojiPicker::currentSkinTone")??"1");return Math.min(Math.max(e,1),6)}();let c;return e?(c=o.search(`:${e}`,t),c.length<t&&(c=c.concat(o.search(e,t-c.length)))):c=u.frequently.get(t).map(e=>o.emoji(e))||[],c.map(e=>e.getSkin(n))}function i(e){u.frequently.add(e)}},95528:(e,t,n)=>{n.r(t),n.d(t,{NcAutoCompleteResult:()=>u.N,NcMentionBubble:()=>c.N,default:()=>u.a});var c=n(36079),u=n(68768)}}]);
|
||||
//# sourceMappingURL=5258-5258.js.map?v=80a9b9a289e4eb695373
|
||||
1
dist/5258-5258.js.map
vendored
1
dist/5258-5258.js.map
vendored
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"5258-5258.js?v=80a9b9a289e4eb695373","mappings":"qKAGA,MAAMA,GAAU,QAAW,iBAAiBC,SAAQ,GAAMC,QAC1D,IAAIC,EAUJ,SAASC,EAAYC,EAAOC,EAAa,IAClCH,IACHA,EAAa,IAAI,EAAAI,WAAW,IAE9B,MAAMC,EAeR,WACE,MAAMC,EAAWC,OAAOC,SAASX,EAAQY,QAAQ,mCAAqC,KACtF,OAAOC,KAAKC,IACVD,KAAKE,IACHN,EACA,GAGF,EAGJ,CA1B0BO,GACxB,IAAIC,EASJ,OARIZ,GACFY,EAAUd,EAAWe,OAAO,IAAIb,IAASC,GACrCW,EAAQE,OAASb,IACnBW,EAAUA,EAAQG,OAAOjB,EAAWe,OAAOb,EAAOC,EAAaW,EAAQE,WAGzEF,EAAU,EAAAI,WAAWC,IAAIhB,GAAYiB,IAAKC,GAAOrB,EAAWsB,MAAMD,KAAQ,GAErEP,EAAQM,IAAKE,GAAUA,EAAMC,QAAQlB,GAC9C,CACA,SAASmB,EAAeC,GACtB,EAAAP,WAAWQ,IAAID,EACjB,C","sources":["webpack:///nextcloud/node_modules/@nextcloud/vue/dist/chunks/emoji-BY_D0V5K.mjs"],"sourcesContent":["import { getBuilder } from \"@nextcloud/browser-storage\";\nimport { EmojiIndex, frequently } from \"emoji-mart-vue-fast\";\nimport data from \"emoji-mart-vue-fast/data/all.json\";\nconst storage = getBuilder(\"nextcloud-vue\").persist(true).build();\nlet emojiIndex;\nvar EmojiSkinTone = /* @__PURE__ */ ((EmojiSkinTone2) => {\n EmojiSkinTone2[EmojiSkinTone2[\"Neutral\"] = 1] = \"Neutral\";\n EmojiSkinTone2[EmojiSkinTone2[\"Light\"] = 2] = \"Light\";\n EmojiSkinTone2[EmojiSkinTone2[\"MediumLight\"] = 3] = \"MediumLight\";\n EmojiSkinTone2[EmojiSkinTone2[\"Medium\"] = 4] = \"Medium\";\n EmojiSkinTone2[EmojiSkinTone2[\"MediumDark\"] = 5] = \"MediumDark\";\n EmojiSkinTone2[EmojiSkinTone2[\"Dark\"] = 6] = \"Dark\";\n return EmojiSkinTone2;\n})(EmojiSkinTone || {});\nfunction emojiSearch(query, maxResults = 10) {\n if (!emojiIndex) {\n emojiIndex = new EmojiIndex(data);\n }\n const currentSkinTone = getCurrentSkinTone();\n let results;\n if (query) {\n results = emojiIndex.search(`:${query}`, maxResults);\n if (results.length < maxResults) {\n results = results.concat(emojiIndex.search(query, maxResults - results.length));\n }\n } else {\n results = frequently.get(maxResults).map((id) => emojiIndex.emoji(id)) || [];\n }\n return results.map((emoji) => emoji.getSkin(currentSkinTone));\n}\nfunction emojiAddRecent(emojiData) {\n frequently.add(emojiData);\n}\nfunction getCurrentSkinTone() {\n const skinTone = Number.parseInt(storage.getItem(\"NcEmojiPicker::currentSkinTone\") ?? \"1\");\n return Math.min(\n Math.max(\n skinTone,\n 1\n /* Neutral */\n ),\n 6\n /* Dark */\n );\n}\nfunction setCurrentSkinTone(skinTone) {\n skinTone = Math.min(\n Math.max(\n skinTone,\n 1\n /* Neutral */\n ),\n 6\n /* Dark */\n );\n storage.setItem(\"NcEmojiPicker::currentSkinTone\", skinTone.toString());\n}\nexport {\n EmojiSkinTone as E,\n emojiAddRecent as a,\n emojiSearch as e,\n getCurrentSkinTone as g,\n setCurrentSkinTone as s\n};\n//# sourceMappingURL=emoji-BY_D0V5K.mjs.map\n"],"names":["storage","persist","build","emojiIndex","emojiSearch","query","maxResults","EmojiIndex","currentSkinTone","skinTone","Number","parseInt","getItem","Math","min","max","getCurrentSkinTone","results","search","length","concat","frequently","get","map","id","emoji","getSkin","emojiAddRecent","emojiData","add"],"sourceRoot":""}
|
||||
1
dist/5258-5258.js.map.license
vendored
1
dist/5258-5258.js.map.license
vendored
|
|
@ -1 +0,0 @@
|
|||
5258-5258.js.license
|
||||
1
dist/5528-5528.js
vendored
Normal file
1
dist/5528-5528.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
"use strict";(self.webpackChunknextcloud=self.webpackChunknextcloud||[]).push([[5528],{95528:(e,u,t)=>{t.r(u),t.d(u,{NcAutoCompleteResult:()=>c.N,NcMentionBubble:()=>l.N,default:()=>c.a});var l=t(36079),c=t(68768)}}]);
|
||||
|
|
@ -121,6 +121,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
|
|
@ -187,6 +190,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-buffer
|
||||
- version: 1.1.6
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- jquery
|
||||
- version: 3.7.1
|
||||
- license: MIT
|
||||
4
dist/7462-7462.js
vendored
4
dist/7462-7462.js
vendored
File diff suppressed because one or more lines are too long
2
dist/7462-7462.js.map
vendored
2
dist/7462-7462.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/7838-7838.js
vendored
2
dist/7838-7838.js
vendored
File diff suppressed because one or more lines are too long
1
dist/7838-7838.js.map
vendored
1
dist/7838-7838.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/7838-7838.js.map.license
vendored
1
dist/7838-7838.js.map.license
vendored
|
|
@ -1 +0,0 @@
|
|||
7838-7838.js.license
|
||||
4
dist/comments-comments-app.js
vendored
4
dist/comments-comments-app.js
vendored
File diff suppressed because one or more lines are too long
2
dist/comments-comments-app.js.map
vendored
2
dist/comments-comments-app.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/comments-comments-tab.js
vendored
4
dist/comments-comments-tab.js
vendored
File diff suppressed because one or more lines are too long
2
dist/comments-comments-tab.js.map
vendored
2
dist/comments-comments-tab.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/core-common.js
vendored
4
dist/core-common.js
vendored
File diff suppressed because one or more lines are too long
6
dist/core-common.js.license
vendored
6
dist/core-common.js.license
vendored
|
|
@ -214,6 +214,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- balanced-match
|
||||
- version: 1.0.2
|
||||
- license: MIT
|
||||
|
|
@ -388,6 +391,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-generator-function
|
||||
- version: 1.1.0
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- is-regex
|
||||
- version: 1.2.1
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/core-common.js.map
vendored
2
dist/core-common.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/dav-settings-example-content.js
vendored
4
dist/dav-settings-example-content.js
vendored
File diff suppressed because one or more lines are too long
6
dist/dav-settings-example-content.js.license
vendored
6
dist/dav-settings-example-content.js.license
vendored
|
|
@ -147,6 +147,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
|
|
@ -222,6 +225,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-buffer
|
||||
- version: 1.1.6
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- jquery
|
||||
- version: 3.7.1
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/dav-settings-example-content.js.map
vendored
2
dist/dav-settings-example-content.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-main.js
vendored
4
dist/files-main.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-main.js.map
vendored
2
dist/files-main.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-reference-files.js
vendored
4
dist/files-reference-files.js
vendored
File diff suppressed because one or more lines are too long
6
dist/files-reference-files.js.license
vendored
6
dist/files-reference-files.js.license
vendored
|
|
@ -137,6 +137,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
|
|
@ -200,6 +203,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-buffer
|
||||
- version: 1.1.6
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- md5
|
||||
- version: 2.3.0
|
||||
- license: BSD-3-Clause
|
||||
|
|
|
|||
2
dist/files-reference-files.js.map
vendored
2
dist/files-reference-files.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/files-settings-personal.js
vendored
4
dist/files-settings-personal.js
vendored
File diff suppressed because one or more lines are too long
2
dist/files-settings-personal.js.map
vendored
2
dist/files-settings-personal.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/profile-main.js
vendored
4
dist/profile-main.js
vendored
File diff suppressed because one or more lines are too long
6
dist/profile-main.js.license
vendored
6
dist/profile-main.js.license
vendored
|
|
@ -131,6 +131,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
|
|
@ -188,6 +191,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-buffer
|
||||
- version: 1.1.6
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- md5
|
||||
- version: 2.3.0
|
||||
- license: BSD-3-Clause
|
||||
|
|
|
|||
2
dist/profile-main.js.map
vendored
2
dist/profile-main.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/settings-users-3239.js
vendored
4
dist/settings-users-3239.js
vendored
File diff suppressed because one or more lines are too long
6
dist/settings-users-3239.js.license
vendored
6
dist/settings-users-3239.js.license
vendored
|
|
@ -170,6 +170,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- axios
|
||||
- version: 1.12.2
|
||||
- license: MIT
|
||||
- bail
|
||||
- version: 2.0.2
|
||||
- license: MIT
|
||||
- base64-js
|
||||
- version: 1.5.1
|
||||
- license: MIT
|
||||
|
|
@ -257,6 +260,9 @@ This file is generated from multiple sources. Included packages:
|
|||
- is-buffer
|
||||
- version: 1.1.6
|
||||
- license: MIT
|
||||
- is-plain-obj
|
||||
- version: 4.1.0
|
||||
- license: MIT
|
||||
- jquery
|
||||
- version: 3.7.1
|
||||
- license: MIT
|
||||
|
|
|
|||
2
dist/settings-users-3239.js.map
vendored
2
dist/settings-users-3239.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue