Merge pull request #15492 from owncloud/ldap-wizard-overhaul

LDAP Wizard Overhaul
This commit is contained in:
blizzz 2015-04-09 23:52:53 +02:00
commit c6151053ba
61 changed files with 5143 additions and 1630 deletions

View File

@ -32,4 +32,17 @@ sort($serverConnections);
$lk = array_pop($serverConnections);
$ln = intval(str_replace('s', '', $lk));
$nk = 's'.str_pad($ln+1, 2, '0', STR_PAD_LEFT);
OCP\JSON::success(array('configPrefix' => $nk));
$resultData = array('configPrefix' => $nk);
if(isset($_POST['copyConfig'])) {
$originalConfig = new \OCA\user_ldap\lib\Configuration($_POST['copyConfig']);
$newConfig = new \OCA\user_ldap\lib\Configuration($nk, false);
$newConfig->setConfiguration($originalConfig->getConfiguration());
$newConfig->saveConfiguration();
} else {
$configuration = new \OCA\user_ldap\lib\Configuration($nk, false);
$resultData['defaults'] = $configuration->getDefaults();
}
OCP\JSON::success($resultData);

View File

@ -72,13 +72,11 @@ switch($action) {
case 'determineGroupsForGroups':
case 'determineAttributes':
case 'getUserListFilter':
case 'getLoginFilterMode':
case 'getUserLoginFilter':
case 'getUserFilterMode':
case 'getGroupFilter':
case 'getGroupFilterMode':
case 'countUsers':
case 'countGroups':
case 'countInBaseDN':
try {
$result = $wizard->$action();
if($result !== false) {
@ -93,6 +91,23 @@ switch($action) {
exit;
break;
case 'testLoginName': {
try {
$loginName = $_POST['ldap_test_loginname'];
$result = $wizard->$action($loginName);
if($result !== false) {
OCP\JSON::success($result->getResultArray());
exit;
}
} catch (\Exception $e) {
\OCP\JSON::error(array('message' => $e->getMessage()));
exit;
}
\OCP\JSON::error();
exit;
break;
}
case 'save':
$key = isset($_POST['cfgkey']) ? $_POST['cfgkey'] : false;
$val = isset($_POST['cfgval']) ? $_POST['cfgval'] : null;
@ -115,6 +130,6 @@ switch($action) {
OCP\JSON::success();
break;
default:
//TODO: return 4xx error
\OCP\JSON::error(array('message' => $l->t('Action does not exist')));
break;
}

View File

@ -1 +1 @@
0.5.0
0.6.0

View File

@ -1,6 +1,6 @@
.table {
display: table;
width: 60%;
width: 85%;
}
.tablerow {
@ -21,10 +21,18 @@
margin-left: 3px;
}
.ldapIconCopy {
background-image: url('../img/copy.svg');
}
.invisible {
visibility: hidden;
}
.forceHidden {
display: none !important;
}
.ldapSettingsTabs {
float: right !important;
}
@ -49,13 +57,16 @@
}
#ldapWizard1 .hostPortCombinator div span {
width: 7%;
display: table-cell;
width: 14.5%;
display: inline-block;
text-align: right;
}
#ldapWizard1 .host {
width: 96.5% !important;
width: 100%;
margin-left: 0;
margin-right: 0;
border: 0;
}
.tableCellInput {
@ -77,7 +88,7 @@
color: #FF3B3B;
}
.wizSpinner {
.ldapSpinner {
height: 15px;
margin: 5px;
}
@ -104,10 +115,51 @@
width: auto;
}
.ldapManyGroupsSupport span {
display: inline-block;
vertical-align: top;
height: 150px;
}
.ldapManyGroupsSupport span 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-left: 10px;
}
.ldapToggle {
text-decoration: underline;
}
span.ldapInputColElement {
margin-top: 9px;
}
#ldap fieldset p input[type=checkbox] {
vertical-align: bottom;
}

BIN
apps/user_ldap/img/copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

120
apps/user_ldap/img/copy.svg Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:ns1="http://sozi.baierouge.fr"
xmlns:cc="http://web.resource.org/cc/"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
id="svg1"
sodipodi:docname="copy.svg"
viewBox="0 0 60 60"
sodipodi:version="0.32"
_SVGFile__filename="oldscale/actions/copy.svg"
version="1.0"
y="0"
x="0"
inkscape:version="0.40"
sodipodi:docbase="/home/danny/work/flat/SVG/mono/scalable/actions"
>
<sodipodi:namedview
id="base"
bordercolor="#666666"
inkscape:pageshadow="2"
inkscape:window-y="0"
pagecolor="#ffffff"
inkscape:window-height="699"
inkscape:zoom="4.9119411"
inkscape:window-x="0"
borderopacity="1.0"
inkscape:current-layer="svg1"
inkscape:cx="60.290892"
inkscape:cy="24.030855"
inkscape:window-width="1024"
inkscape:pageopacity="0.0"
/>
<path
id="path1716"
style="stroke-linejoin:round;stroke:#ffffff;stroke-width:8.3605;fill:none"
transform="matrix(.97183 0 0 .97183 .87037 .23733)"
d="m7.513 4.5767c-1.3709 0-2.4745 1.1036-2.4745 2.4745v31.564c0 1.371 1.1036 2.474 2.4745 2.474h29.783c1.371 0 2.474-1.103 2.474-2.474v-31.564c0-1.3707-1.103-2.4743-2.474-2.4743h-29.783z"
/>
<path
id="rect1101"
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-width:3.2156;fill:#ffffff"
transform="matrix(.97183 0 0 .97183 .87037 .23733)"
d="m7.513 4.5767c-1.3709 0-2.4745 1.1036-2.4745 2.4745v31.564c0 1.371 1.1036 2.474 2.4745 2.474h29.783c1.371 0 2.474-1.103 2.474-2.474v-31.564c0-1.3707-1.103-2.4743-2.474-2.4743h-29.783z"
/>
<path
id="path1094"
style="stroke-linejoin:round;stroke:#ffffff;stroke-width:8.3605;fill:none"
transform="matrix(.97183 0 0 .97183 .87037 .23733)"
d="m22.652 20.161c-1.37 0-2.474 1.104-2.474 2.475v31.564c0 1.371 1.104 2.474 2.474 2.474h29.783c1.371 0 2.475-1.103 2.475-2.474v-31.564c0-1.371-1.104-2.475-2.475-2.475h-29.783z"
/>
<path
id="rect1111"
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-width:3.2156;fill:#ffffff"
transform="matrix(.97183 0 0 .97183 .87037 .23733)"
d="m22.652 20.161c-1.37 0-2.474 1.104-2.474 2.475v31.564c0 1.371 1.104 2.474 2.474 2.474h29.783c1.371 0 2.475-1.103 2.475-2.474v-31.564c0-1.371-1.104-2.475-2.475-2.475h-29.783z"
/>
<path
id="path1718"
style="stroke-linejoin:round;stroke:#ffffff;stroke-linecap:round;stroke-width:8.125;fill:none"
d="m12.457 21.599c2.325 20.529 20.15 19.15 21.296 19.043v6.139l8.981-8.889-8.981-8.87v6.066c-1.348 0.159-13.941 1.422-21.296-13.489z"
/>
<path
id="path1574"
style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:3.125;fill:#000000"
d="m12.457 21.599c2.325 20.529 20.15 19.15 21.296 19.043v6.139l8.981-8.889-8.981-8.87v6.066c-1.348 0.159-13.941 1.422-21.296-13.489z"
/>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,92 +0,0 @@
/**
* Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
/* global LdapWizard */
/**
* controls behaviour depend on whether the admin is experienced in LDAP or not.
*
* @class
* @param {object} wizard the LDAP Wizard object
* @param {boolean} initialState whether the admin is experienced or not
*/
function ExperiencedAdmin(wizard, initialState) {
this.wizard = wizard;
this._isExperienced = initialState;
if(this._isExperienced) {
this.hideEntryCounters();
}
}
/**
* toggles whether the admin is an experienced one or not
*
* @param {boolean} isExperienced whether the admin is experienced or not
*/
ExperiencedAdmin.prototype.setExperienced = function(isExperienced) {
this._isExperienced = isExperienced;
if(this._isExperienced) {
this.enableRawMode();
this.hideEntryCounters();
} else {
this.showEntryCounters();
}
};
/**
* answers whether the admin is an experienced one or not
*
* @return {boolean} whether the admin is experienced or not
*/
ExperiencedAdmin.prototype.isExperienced = function() {
return this._isExperienced;
};
/**
* switches all LDAP filters from Assisted to Raw mode.
*/
ExperiencedAdmin.prototype.enableRawMode = function() {
LdapWizard._save({id: 'ldapGroupFilterMode'}, LdapWizard.filterModeRaw);
LdapWizard._save({id: 'ldapUserFilterMode' }, LdapWizard.filterModeRaw);
LdapWizard._save({id: 'ldapLoginFilterMode'}, LdapWizard.filterModeRaw);
};
ExperiencedAdmin.prototype.updateUserTab = function(mode) {
this._updateTab(mode, $('#ldap_user_count'));
};
ExperiencedAdmin.prototype.updateGroupTab = function(mode) {
this._updateTab(mode, $('#ldap_group_count'));
};
ExperiencedAdmin.prototype._updateTab = function(mode, $countEl) {
if(mode === LdapWizard.filterModeAssisted) {
$countEl.removeClass('hidden');
} else if(!this._isExperienced) {
$countEl.removeClass('hidden');
} else {
$countEl.addClass('hidden');
}
};
/**
* hide user and group counters, they will be displayed on demand only
*/
ExperiencedAdmin.prototype.hideEntryCounters = function() {
$('#ldap_user_count').addClass('hidden');
$('#ldap_group_count').addClass('hidden');
$('.ldapGetEntryCount').removeClass('hidden');
};
/**
* shows user and group counters, they will be displayed on demand only
*/
ExperiencedAdmin.prototype.showEntryCounters = function() {
$('#ldap_user_count').removeClass('hidden');
$('#ldap_group_count').removeClass('hidden');
$('.ldapGetEntryCount').addClass('hidden');
};

View File

@ -1,193 +0,0 @@
/* global LdapWizard */
function LdapFilter(target, determineModeCallback) {
this.locked = true;
this.target = false;
this.mode = LdapWizard.filterModeAssisted;
this.lazyRunCompose = false;
this.determineModeCallback = determineModeCallback;
this.foundFeatures = false;
this.activated = false;
this.countPending = false;
if( target === 'User' ||
target === 'Login' ||
target === 'Group') {
this.target = target;
}
}
LdapFilter.prototype.activate = function() {
if(this.activated) {
// might be necessary, if configuration changes happened.
this.findFeatures();
return;
}
this.activated = true;
this.determineMode();
};
LdapFilter.prototype.compose = function(updateCount) {
var action;
if(updateCount === true) {
this.countPending = updateCount;
}
if(this.locked) {
this.lazyRunCompose = true;
return false;
}
if(this.mode === LdapWizard.filterModeRaw) {
//Raw filter editing, i.e. user defined filter, don't compose
return;
}
if(this.target === 'User') {
action = 'getUserListFilter';
} else if(this.target === 'Login') {
action = 'getUserLoginFilter';
} else if(this.target === 'Group') {
action = 'getGroupFilter';
}
var param = 'action='+action+
'&ldap_serverconfig_chooser='+
encodeURIComponent($('#ldap_serverconfig_chooser').val());
var filter = this;
LdapWizard.ajax(param,
function(result) {
filter.afterComposeSuccess(result);
},
function () {
filter.countPending = false;
console.log('LDAP Wizard: could not compose filter. '+
'Please check owncloud.log');
}
);
};
/**
* this function is triggered after LDAP filters have been composed successfully
* @param {object} result returned by the ajax call
*/
LdapFilter.prototype.afterComposeSuccess = function(result) {
LdapWizard.applyChanges(result);
if(this.countPending) {
this.countPending = false;
this.updateCount();
}
};
LdapFilter.prototype.determineMode = function() {
var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+
'&ldap_serverconfig_chooser='+
encodeURIComponent($('#ldap_serverconfig_chooser').val());
var filter = this;
LdapWizard.ajax(param,
function(result) {
var property = 'ldap' + filter.target + 'FilterMode';
filter.mode = parseInt(result.changes[property], 10);
var rawContainerIsInvisible =
$('#raw'+filter.target+'FilterContainer').hasClass('invisible');
if ( filter.mode === LdapWizard.filterModeRaw
&& rawContainerIsInvisible
) {
LdapWizard['toggleRaw'+filter.target+'Filter']();
} else if ( filter.mode === LdapWizard.filterModeAssisted
&& !rawContainerIsInvisible
) {
LdapWizard['toggleRaw'+filter.target+'Filter']();
} else {
console.log('LDAP Wizard determineMode: returned mode was »' +
filter.mode + '« of type ' + typeof filter.mode);
}
filter.unlock();
filter.determineModeCallback(filter.mode);
},
function () {
//on error case get back to default i.e. Assisted
if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) {
LdapWizard['toggleRaw'+filter.target+'Filter']();
filter.mode = LdapWizard.filterModeAssisted;
}
filter.unlock();
filter.determineModeCallback(filter.mode);
}
);
};
LdapFilter.prototype.setMode = function(mode) {
if(mode === LdapWizard.filterModeAssisted || mode === LdapWizard.filterModeRaw) {
this.mode = mode;
}
};
LdapFilter.prototype.getMode = function() {
return this.mode;
};
LdapFilter.prototype.unlock = function() {
this.locked = false;
if(this.lazyRunCompose) {
this.lazyRunCompose = false;
this.compose();
}
};
/**
* resets this.foundFeatures so that LDAP queries can be fired again to retrieve
* objectClasses, groups, etc.
*/
LdapFilter.prototype.reAllowFeatureLookup = function () {
this.foundFeatures = false;
};
LdapFilter.prototype.findFeatures = function() {
if(!this.foundFeatures && !this.locked && this.mode === LdapWizard.filterModeAssisted) {
this.foundFeatures = true;
var objcEl, avgrEl;
if(this.target === 'User') {
objcEl = 'ldap_userfilter_objectclass';
avgrEl = 'ldap_userfilter_groups';
} else if (this.target === 'Group') {
objcEl = 'ldap_groupfilter_objectclass';
avgrEl = 'ldap_groupfilter_groups';
} else if (this.target === 'Login') {
LdapWizard.findAttributes();
return;
} else {
return false;
}
LdapWizard.findObjectClasses(objcEl, this.target);
LdapWizard.findAvailableGroups(avgrEl, this.target + "s");
}
};
/**
* this function is triggered before user and group counts are executed
* resolving the passed status variable will fire up counting
*/
LdapFilter.prototype.beforeUpdateCount = function() {
var status = $.Deferred();
LdapWizard.runDetectors(this.target, function() {
status.resolve();
});
return status;
};
LdapFilter.prototype.updateCount = function(doneCallback) {
var filter = this;
$.when(this.beforeUpdateCount()).done(function() {
if(filter.target === 'User') {
LdapWizard.countUsers(doneCallback);
} else if (filter.target === 'Group') {
LdapWizard.countGroups(doneCallback);
}
});
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,606 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc this class represents a server configuration. It communicates
* with the ownCloud 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
* ownCloud 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 ownCloud. 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 ownCloud. 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 ownCloud 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 ownCloud 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 ownCloud server
*/
requestConfigurationTest: function() {
var url = OC.generateUrl('apps/user_ldap/ajax/testConfiguration.php');
var params = OC.buildQueryString(this.configuration);
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;
})();

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,437 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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,
/**
* 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 backword 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) && ((!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) {
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);
},
/**
* 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._updateStatusIndicator(view.STATUS_SUCCESS);
} 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.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', this.configModel.configuration);
},
/**
* 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);
},
/**
* 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({});
$('.ldap_submit').button();
$('.ldap_action_test_connection').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_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;
})();

View File

@ -0,0 +1,80 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();
$(document).ready(function() {
new OCA.LDAP.Wizard.Wizard();
});

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc an Attributes Detector. It executes the auto-detection of
* available attributes by the ownCloud 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;
})();

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Base DN Detector. It executes the auto-detection of the base
* DN by the ownCloud 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;
})();

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,38 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,117 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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 ownCloud 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;
})();

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,89 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,63 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
OCA = OCA || {};
(function() {
/**
* @classdesc a Port Detector. It executes the auto-detection of the port
* by the ownCloud 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;
})();

View File

@ -0,0 +1,30 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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 !== 'uniqueMember') {
// 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;
})();

View File

@ -0,0 +1,29 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,76 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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.updateOptions();
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 options will be read in again. Should be called after a
* configuration switch.
*/
updateOptions: function() {
var options = [];
this.$select.find('option').each(function() {
options.push({
value: $(this).val(),
normalized: $(this).val().toLowerCase()
}
);
});
this._options = options;
},
/**
* 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.empty();
$.each(fity._options, function() {
if(!filterVal || this.normalized.indexOf(filterVal) > -1) {
fity.$select.append($('<option>').val(this.value).text(this.value));
}
});
delete(fity.runID);
}
});
OCA.LDAP.Wizard.FilterOnType = FilterOnType;
})();

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,60 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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;
})();

View File

@ -0,0 +1,378 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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 {Array} groups
*/
setGroups: function(groups) {
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);
}
},
/**
* 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
*
* @param {string} [only] - if only one search index should be updated
*/
updateFilterOnType: function(only) {
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')
));
} else {
if(_.isUndefined || only.toLowerCase() === 'available') {
this.filterOnType[0].updateOptions();
}
if(_.isUndefined || only.toLowerCase() === 'selected') {
this.filterOnType[1].updateOptions();
}
}
},
/**
* @inheritdoc
*/
onActivate: function() {
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('selected');
}
}
}
}
},
/**
* 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.updateFilterOnType('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');
var $selected = $(this.tabID).find('.ldapGroupListSelected');
var selected = $.map($selected.find('option'), function(e) { return e.value; });
this._saveGroups(selected.concat($available.val()));
$available.find('option:selected').prependTo($selected);
this.updateFilterOnType();
},
/**
* 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)'), function(e) { return e.value; });
this._saveGroups(selected);
$selected.find('option:selected').appendTo($available);
this.updateFilterOnType();
}
});
OCA.LDAP.Wizard.WizardTabAbstractFilter = WizardTabAbstractFilter;
})();

View File

@ -0,0 +1,330 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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 tabIndex
* @param 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_nocase: {
$element: $('#ldap_nocase'),
setMethod: 'setNoCase'
},
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_base_users: {
$element: $('#ldap_base_users'),
setMethod: 'setBaseDNUsers'
},
ldap_attributes_for_user_search: {
$element: $('#ldap_attributes_for_user_search'),
setMethod: 'setSearchAttributesUsers'
},
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_nested_groups: {
$element: $('#ldap_nested_groups'),
setMethod: 'setUseNestedGroups'
},
ldap_paging_size: {
$element: $('#ldap_paging_size'),
setMethod: 'setPagingSize'
},
//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'
}
};
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
);
},
/**
* whether the server is case insensitive. This setting does not play
* a role anymore (probably never had).
*
* @param {string} noCase contains an int
*/
setNoCase: function(noCase) {
this.setElementValue(this.managedItems.ldap_nocase.$element, noCase);
},
/**
* 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 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);
},
/**
* 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);
},
/**
* 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 the email attribute
*
* @param {string} attribute
*/
setEmailAttribute: function(attribute) {
this.setElementValue(this.managedItems.ldap_email_attr.$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 ownCloud user specific home folder location
*
* @param {string} attribute
*/
setHomeFolderAttribute: function(attribute) {
this.setElementValue(this.managedItems.home_folder_naming_rule.$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;
})();

View File

@ -0,0 +1,347 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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({
/**
* initializes the instance. Always call it after initialization.
*
* @param tabIndex
* @param tabID
*/
init: function (tabIndex, tabID) {
tabIndex = 0;
this._super(tabIndex, tabID);
this.isActive = true;
this.configChooserID = '#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'
},
ldap_agent_password: {
$element: $('#ldap_agent_password'),
setMethod: 'setAgentPwd'
},
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.configChooserID).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
);
},
/**
* 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) {
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) {
view.disableElement(view.managedItems.ldap_port.$relatedElements);
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) {
$(view.configChooserID + ' option:selected').removeAttr('selected');
var html = '<option value="'+result.configPrefix+'" selected="selected">'+t('user_ldap','{nthServer}. Server', {nthServer: $(view.configChooserID + ' option').length + 1})+'</option>';
$(view.configChooserID + ' option:last').after(html);
}
},
/**
* updates the configuration chooser upon the deletion of a
* configuration and, if necessary, loads an existing one.
*
* @param view
* @param 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.configChooserID + ' option:selected').remove();
$(view.configChooserID + ' option:first').select();
if($(view.configChooserID + ' option').length < 2) {
view.configModel.newConfig(false);
} else {
view.configModel.load(view.getConfigID());
}
} else {
// otherwise just remove the entry
$(view.configChooserID + ' 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(payload.feature === 'TestBaseDN') {
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 then 1.000 directory entries available.');
} else {
message = t('user_ldap', objectsFound + ' entries available within the provided Base DN');
}
} else {
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.configModel.requestWizard('ldap_test_base');
},
/**
* registers the change event on the configuration chooser and makes
* the model load a newly selected configuration
*
* @private
*/
_enableConfigChooser: function() {
var view = this;
$(this.configChooserID).change(function(){
var value = $(view.configChooserID + ' 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;
})();

View File

@ -0,0 +1,130 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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 tabIndex
* @param 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 ownCloud 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;
})();

View File

@ -0,0 +1,547 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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',
/** @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();
},
/**
* 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 ownCloud 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) {
return message;
},
/**
* this is called by the main view, if the tab is being switched to.
* The concrete tab view can implement this if necessary.
*/
onActivate: function() { },
/**
* 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 view
* @param 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;
}
// deal with text area
if ($element.is('textarea') && $.isArray(value)) {
value = value.join("\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) {
$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(!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 {
$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)) {
continue;
}
var $element = this.managedItems[id].$element;
if (!$element.is('select[multiple]')) {
$element.change(function() {
view._requestSave($(this));
});
}
}
},
/**
* 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.attr('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 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 {
OCdialogs.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;
$switcher.click(this._toggleRawFilterMode);
}
});
OCA.LDAP.Wizard.WizardTabGeneric = WizardTabGeneric;
})();

View File

@ -0,0 +1,124 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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: 'setFilterMode'
},
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;
})();

View File

@ -0,0 +1,238 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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 tabIndex
* @param 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: 'setFilterMode'
},
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');
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('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 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', 'Settings verified, but one user found. Only the first will be able to login. Consider a more narrow filter.');
}
} else {
message = t('user_ldap', 'An unspecified error occurred. Please check the settings and the log.');
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.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);
},
/**
* 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});
}
}
});
OCA.LDAP.Wizard.WizardTabLoginFilter = WizardTabLoginFilter;
})();

View File

@ -0,0 +1,136 @@
/**
* Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or later.
* See the COPYING-README file.
*/
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: 'setFilterMode'
},
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) {
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;
})();

View File

@ -595,6 +595,22 @@ class Access extends LDAPUtility implements user\IUserTools {
return $altName;
}
/**
* fetches a list of users according to a provided loginName and utilizing
* the login filter.
*
* @param string $loginName
* @param array $attributes optional, list of attributes to read
* @return array
*/
public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
$loginName = $this->escapeFilterPart($loginName);
$filter = \OCP\Util::mb_str_replace(
'%uid', $loginName, $this->connection->ldapLoginFilter, 'UTF-8');
$users = $this->fetchListOfUsers($filter, $attributes);
return $users;
}
/**
* @param string $filter
* @param string|string[] $attr
@ -686,6 +702,17 @@ class Access extends LDAPUtility implements user\IUserTools {
return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
}
/**
* returns the number of available objects on the base DN
*
* @param int|null $limit
* @param int|null $offset
* @return int|bool
*/
public function countObjects($limit = null, $offset = null) {
return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
}
/**
* retrieved. Results will according to the order in the array.
* @param int $limit optional, maximum results to be counted

View File

@ -201,11 +201,14 @@ class Configuration {
case 'ldapAgentPassword':
$readMethod = 'getPwd';
break;
case 'ldapUserDisplayName':
case 'ldapGroupDisplayName':
$readMethod = 'getLcValue';
break;
case 'ldapUserDisplayName':
default:
// user display name does not lower case because
// we rely on an upper case N as indicator whether to
// auto-detect it or not. FIXME
$readMethod = 'getValue';
break;
}
@ -374,7 +377,7 @@ class Configuration {
'ldap_groupfilter_groups' => '',
'ldap_display_name' => 'displayName',
'ldap_group_display_name' => 'cn',
'ldap_tls' => 1,
'ldap_tls' => 0,
'ldap_nocase' => 0,
'ldap_quota_def' => '',
'ldap_quota_attr' => '',

View File

@ -75,9 +75,11 @@ class Wizard extends LDAPUtility {
/**
* counts entries in the LDAP directory
*
* @param string $filter the LDAP search filter
* @param string $type a string being either 'users' or 'groups';
* @return int|bool
* @return bool|int
* @throws \Exception
*/
public function countEntries($filter, $type) {
$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
@ -88,17 +90,36 @@ class Wizard extends LDAPUtility {
throw new \Exception('Requirements not met', 400);
}
$attr = array('dn'); // default
$limit = 1001;
if($type === 'groups') {
$result = $this->access->countGroups($filter);
$result = $this->access->countGroups($filter, $attr, $limit);
} else if($type === 'users') {
$result = $this->access->countUsers($filter);
$result = $this->access->countUsers($filter, $attr, $limit);
} else if ($type === 'objects') {
$result = $this->access->countObjects($limit);
} else {
throw new \Exception('internal error: invald object type', 500);
throw new \Exception('internal error: invalid object type', 500);
}
return $result;
}
/**
* formats the return value of a count operation to the string to be
* inserted.
*
* @param bool|int $count
* @return int|string
*/
private function formatCountResult($count) {
$formatted = ($count !== false) ? $count : 0;
if($formatted > 1000) {
$formatted = '> 1000';
}
return $formatted;
}
public function countGroups() {
$filter = $this->configuration->ldapGroupFilter;
@ -109,7 +130,7 @@ class Wizard extends LDAPUtility {
}
try {
$groupsTotal = $this->countEntries($filter, 'groups');
$groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
} catch (\Exception $e) {
//400 can be ignored, 500 is forwarded
if($e->getCode() === 500) {
@ -117,7 +138,6 @@ class Wizard extends LDAPUtility {
}
return false;
}
$groupsTotal = ($groupsTotal !== false) ? $groupsTotal : 0;
$output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
$this->result->addChange('ldap_group_count', $output);
return $this->result;
@ -130,13 +150,28 @@ class Wizard extends LDAPUtility {
public function countUsers() {
$filter = $this->access->getFilterForUserCount();
$usersTotal = $this->countEntries($filter, 'users');
$usersTotal = ($usersTotal !== false) ? $usersTotal : 0;
$usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
$output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
$this->result->addChange('ldap_user_count', $output);
return $this->result;
}
/**
* counts any objects in the currently set base dn
*
* @return WizardResult
* @throws \Exception
*/
public function countInBaseDN() {
// we don't need to provide a filter in this case
$total = $this->countEntries(null, 'objects');
if($total === false) {
throw new \Exception('invalid results received');
}
$this->result->addChange('ldap_test_base', $total);
return $this->result;
}
/**
* counts users with a specified attribute
* @param string $attr
@ -281,45 +316,6 @@ class Wizard extends LDAPUtility {
return $this->result;
}
/**
* return the state of the Group Filter Mode
* @return WizardResult
*/
public function getGroupFilterMode() {
$this->getFilterMode('ldapGroupFilterMode');
return $this->result;
}
/**
* return the state of the Login Filter Mode
* @return WizardResult
*/
public function getLoginFilterMode() {
$this->getFilterMode('ldapLoginFilterMode');
return $this->result;
}
/**
* return the state of the User Filter Mode
* @return WizardResult
*/
public function getUserFilterMode() {
$this->getFilterMode('ldapUserFilterMode');
return $this->result;
}
/**
* return the state of the mode of the specified filter
* @param string $confKey contains the access key of the Configuration
*/
private function getFilterMode($confKey) {
$mode = $this->configuration->$confKey;
if(is_null($mode)) {
$mode = $this->LFILTER_MODE_ASSISTED;
}
$this->result->addChange($confKey, $mode);
}
/**
* detects the available LDAP attributes
* @return array|false The instance's WizardResult instance
@ -467,8 +463,7 @@ class Wizard extends LDAPUtility {
return false;
}
$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
//so it will be saved on destruct
$this->result->markChange();
$this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
return $this->result;
}
@ -603,6 +598,41 @@ class Wizard extends LDAPUtility {
return $this->result;
}
/**
* @return bool|WizardResult
* @param string $loginName
* @throws \Exception
*/
public function testLoginName($loginName) {
if(!$this->checkRequirements(array('ldapHost',
'ldapPort',
'ldapBase',
'ldapLoginFilter',
))) {
return false;
}
$cr = $this->access->connection->getConnectionResource();
if(!$this->ldap->isResource($cr)) {
throw new \Exception('connection error');
}
if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
=== false) {
throw new \Exception('missing placeholder');
}
$users = $this->access->fetchUsersByLoginName($loginName);
if($this->ldap->errno($cr) !== 0) {
throw new \Exception($this->ldap->error($cr));
}
$filter = \OCP\Util::mb_str_replace(
'%uid', $loginName, $this->access->connection->ldapLoginFilter, 'UTF-8');
$this->result->addChange('ldap_test_loginname', count($users));
$this->result->addChange('ldap_test_effective_filter', $filter);
return $this->result;
}
/**
* Tries to determine the port, requires given Host, User DN and Password
* @return WizardResult|false WizardResult on success, false otherwise
@ -674,10 +704,13 @@ class Wizard extends LDAPUtility {
}
$dparts = explode('.', $domain);
$base2 = implode('dc=', $dparts);
if($base !== $base2 && $this->testBaseDN($base2)) {
$this->applyFind('ldap_base', $base2);
return $this->result;
while(count($dparts) > 0) {
$base2 = 'dc=' . implode(',dc=', $dparts);
if ($base !== $base2 && $this->testBaseDN($base2)) {
$this->applyFind('ldap_base', $base2);
return $this->result;
}
array_shift($dparts);
}
return false;
@ -720,7 +753,7 @@ class Wizard extends LDAPUtility {
* @throws \Exception
*/
private function detectGroupMemberAssoc() {
$possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'unfugasdfasdfdfa');
$possibleAttrs = array('uniqueMember', 'memberUid', 'member');
$filter = $this->configuration->ldapGroupFilter;
if(empty($filter)) {
return false;
@ -730,7 +763,7 @@ class Wizard extends LDAPUtility {
throw new \Exception('Could not connect to LDAP');
}
$base = $this->configuration->ldapBase[0];
$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs);
$rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
if(!$this->ldap->isResource($rr)) {
return false;
}
@ -1114,7 +1147,8 @@ class Wizard extends LDAPUtility {
//skip when the filter is a wildcard and results were found
continue;
}
$rr = $this->ldap->search($cr, $base, $filter, array($attr));
// 20k limit for performance and reason
$rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
if(!$this->ldap->isResource($rr)) {
continue;
}

View File

@ -29,13 +29,6 @@
OC_Util::checkAdminUser();
OCP\Util::addScript('user_ldap', 'ldapFilter');
OCP\Util::addScript('user_ldap', 'experiencedAdmin');
OCP\Util::addScript('user_ldap', 'settings');
\OC_Util::addVendorScript('user_ldap', 'ui-multiselect/src/jquery.multiselect');
OCP\Util::addStyle('user_ldap', 'settings');
\OC_Util::addVendorStyle('user_ldap', 'ui-multiselect/jquery.multiselect');
// fill template
$tmpl = new OCP\Template('user_ldap', 'settings');
@ -55,9 +48,9 @@ $l = \OC::$server->getL10N('user_ldap');
$wizTabs = array();
$wizTabs[] = array('tpl' => 'part.wizard-server', 'cap' => $l->t('Server'));
$wizTabs[] = array('tpl' => 'part.wizard-userfilter', 'cap' => $l->t('User Filter'));
$wizTabs[] = array('tpl' => 'part.wizard-loginfilter', 'cap' => $l->t('Login Filter'));
$wizTabs[] = array('tpl' => 'part.wizard-groupfilter', 'cap' => $l->t('Group Filter'));
$wizTabs[] = array('tpl' => 'part.wizard-userfilter', 'cap' => $l->t('Users'));
$wizTabs[] = array('tpl' => 'part.wizard-loginfilter', 'cap' => $l->t('Login Attributes'));
$wizTabs[] = array('tpl' => 'part.wizard-groupfilter', 'cap' => $l->t('Groups'));
$wizTabsCount = count($wizTabs);
for($i = 0; $i < $wizTabsCount; $i++) {
$tab = new OCP\Template('user_ldap', $wizTabs[$i]['tpl']);

View File

@ -1,5 +1,4 @@
<div class="ldapSettingControls">
<input class="ldap_submit" value="<?php p($l->t('Save'));?>" type="submit">
<button type="button" class="ldap_action_test_connection" name="ldap_action_test_connection">
<?php p($l->t('Test Configuration'));?>
</button>

View File

@ -5,31 +5,48 @@
</p>
<p>
<label for="ldap_groupfilter_objectclass">
<?php p($l->t('only those object classes:'));?>
<?php p($l->t('Only these object classes:'));?>
</label>
<select id="ldap_groupfilter_objectclass" multiple="multiple"
name="ldap_groupfilter_objectclass">
name="ldap_groupfilter_objectclass" class="multiSelectPlugin">
</select>
</p>
<p>
<label for="ldap_groupfilter_groups">
<?php p($l->t('only from those groups:'));?>
<?php p($l->t('Only from these groups:'));?>
</label>
<input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="<?php p($l->t('Search groups'));?>" />
<select id="ldap_groupfilter_groups" multiple="multiple"
name="ldap_groupfilter_groups">
name="ldap_groupfilter_groups" class="multiSelectPlugin">
</select>
</p>
<p class="ldapManyGroupsSupport hidden">
<label></label>
<select class="ldapGroupList ldapGroupListAvailable" multiple="multiple"
title="<?php p($l->t('Available groups'));?>"></select>
<span>
<button class="ldapGroupListSelect" type="button">&gt;</button><br/>
<button class="ldapGroupListDeselect" type="button">&lt;</button>
</span>
<select class="ldapGroupList ldapGroupListSelected" multiple="multiple"
title="<?php p($l->t('Selected groups'));?>"></select>
</p>
<p>
<label><a id='toggleRawGroupFilter'> <?php p($l->t('Edit raw filter instead'));?></a></label>
<label><a id='toggleRawGroupFilter' class='ldapToggle'> <?php p($l->t('Edit LDAP Query'));?></a></label>
</p>
<p id="ldapReadOnlyGroupFilterContainer" class="hidden ldapReadOnlyFilterContainer">
<label><?php p($l->t('LDAP Filter:'));?></label>
<span class="ldapFilterReadOnlyElement ldapInputColElement"></span>
</p>
<p id="rawGroupFilterContainer" class="invisible">
<input type="text" id="ldap_group_filter" name="ldap_group_filter"
class="lwautosave"
placeholder="<?php p($l->t('Raw LDAP filter'));?>"
title="<?php p($l->t('The filter specifies which LDAP groups shall have access to the %s instance.', $theme->getName()));?>"
/>
<textarea type="text" id="ldap_group_filter" name="ldap_group_filter"
placeholder="<?php p($l->t('Edit LDAP Query'));?>"
title="<?php p($l->t('The filter specifies which LDAP groups shall have access to the %s instance.', $theme->getName()));?>">
</textarea>
<button class="ldapGetEntryCount hidden" name="ldapGetEntryCount" type="button">
<?php p($l->t('Test Filter'));?>
</button>
@ -38,7 +55,10 @@
<div class="ldapWizardInfo invisible">&nbsp;</div>
</p>
<p class="ldap_count">
<span id="ldap_group_count">0 <?php p($l->t('groups found'));?></span>
<button class="ldapGetEntryCount ldapGetGroupCount" name="ldapGetEntryCount" type="button">
<?php p($l->t('Verify settings and count groups'));?>
</button>
<span id="ldap_group_count"></span>
</p>
<?php print_unescaped($_['wizardControls']); ?>
</div>

View File

@ -1,23 +1,25 @@
<fieldset id="ldapWizard3">
<div>
<p>
<?php p($l->t('Users login with this attribute:'));?>
<?php p($l->t('When logging in, %s will find the user based on the following attributes:', $theme->getName()));?>
</p>
<p>
<label for="ldap_loginfilter_username">
<?php p($l->t('LDAP Username:'));?>
<?php p($l->t('LDAP / AD Username:'));?>
</label>
<input type="checkbox" id="ldap_loginfilter_username"
name="ldap_loginfilter_username" value="1" class="lwautosave" />
title="<?php p($l->t('Allows login against the LDAP / AD username, which is either uid or samaccountname and will be detected.'));?>"
name="ldap_loginfilter_username" value="1" />
</p>
<p>
<label for="ldap_loginfilter_email">
<?php p($l->t('LDAP Email Address:'));?>
<?php p($l->t('LDAP / AD Email Address:'));?>
</label>
<input type="checkbox" id="ldap_loginfilter_email"
name="ldap_loginfilter_email" value="1" class="lwautosave" />
title="<?php p($l->t('Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed.'));?>"
name="ldap_loginfilter_email" value="1" />
</p>
<p>
<label for="ldap_loginfilter_attributes">
@ -25,23 +27,35 @@
</label>
<select id="ldap_loginfilter_attributes" multiple="multiple"
name="ldap_loginfilter_attributes">
name="ldap_loginfilter_attributes" class="multiSelectPlugin">
</select>
</p>
<p>
<label><a id='toggleRawLoginFilter'> <?php p($l->t('Edit raw filter instead'));?></a></label>
<label><a id='toggleRawLoginFilter' class='ldapToggle'> <?php p($l->t('Edit LDAP Query'));?></a></label>
</p>
<p id="ldapReadOnlyLoginFilterContainer" class="hidden ldapReadOnlyFilterContainer">
<label><?php p($l->t('LDAP Filter:'));?></label>
<span class="ldapFilterReadOnlyElement ldapInputColElement"></span>
</p>
<p id="rawLoginFilterContainer" class="invisible">
<input type="text" id="ldap_login_filter" name="ldap_login_filter"
class="lwautosave"
placeholder="<?php p($l->t('Raw LDAP filter'));?>"
title="<?php p($l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: "uid=%%uid"'));?>"
/>
<textarea type="text" id="ldap_login_filter" name="ldap_login_filter"
class="ldapFilterInputElement"
placeholder="<?php p($l->t('Edit LDAP Query'));?>"
title="<?php p($l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: "uid=%%uid"'));?>">
</textarea>
</p>
<p>
<div class="ldapWizardInfo invisible">&nbsp;</div>
</p>
<p class="ldap_verify">
<input type="text" id="ldap_test_loginname" name="ldap_test_loginname"
placeholder="<?php p($l->t('Test Loginname'));?>"
class="ldapVerifyInput"
title="Attempts to receive a DN for the given loginname and the current login filter"/>
<button class="ldapVerifyLoginName" name="ldapTestLoginSettings" type="button">
<?php p($l->t('Verify settings'));?>
</button>
</p>
<?php print_unescaped($_['wizardControls']); ?>
</div>
</fieldset>

View File

@ -22,32 +22,41 @@
}
}
?>
<option value="NEW"><?php p($l->t('Add Server Configuration'));?></option>
</select>
<button type="button" id="ldap_action_add_configuration"
name="ldap_action_add_configuration" class="icon-add"
title="Adds a new and blank configuration">&nbsp;</button>
<button type="button" id="ldap_action_copy_configuration"
name="ldap_action_copy_configuration"
class="ldapIconCopy icon-default-style"
title="Copy current configuration into new directory binding">&nbsp;</button>
<button type="button" id="ldap_action_delete_configuration"
name="ldap_action_delete_configuration"><?php p($l->t('Delete Configuration'));?></button>
name="ldap_action_delete_configuration" class="icon-delete"
title="Delete the current configuration">&nbsp;</button>
</p>
<div class="hostPortCombinator">
<div class="tablerow">
<div class="tablecell">
<div class="table">
<input type="text" class="host tablecell lwautosave" id="ldap_host"
<input type="text" class="host" id="ldap_host"
name="ldap_host"
placeholder="<?php p($l->t('Host'));?>"
title="<?php p($l->t('You can omit the protocol, except you require SSL. Then start with ldaps://'));?>"
/>
<span>
<input type="number" id="ldap_port" name="ldap_port"
class="lwautosave"
placeholder="<?php p($l->t('Port'));?>" />
<button class="ldapDetectPort" name="ldapDetectPort" type="button">
<?php p($l->t('Detect Port'));?>
</button>
</span>
</div>
</div>
</div>
<div class="tablerow">
<input type="text" id="ldap_dn" name="ldap_dn"
class="tablecell lwautosave"
class="tablecell"
placeholder="<?php p($l->t('User DN'));?>" autocomplete="off"
title="<?php p($l->t('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.'));?>"
/>
@ -55,7 +64,7 @@
<div class="tablerow">
<input type="password" id="ldap_agent_password"
class="tablecell lwautosave" name="ldap_agent_password"
class="tablecell" name="ldap_agent_password"
placeholder="<?php p($l->t('Password'));?>" autocomplete="off"
title="<?php p($l->t('For anonymous access, leave DN and Password empty.'));?>"
/>
@ -63,15 +72,21 @@
<div class="tablerow">
<textarea id="ldap_base" name="ldap_base"
class="tablecell lwautosave"
class="tablecell"
placeholder="<?php p($l->t('One Base DN per line'));?>"
title="<?php p($l->t('You can specify Base DN for users and groups in the Advanced tab'));?>">
</textarea>
<button class="ldapDetectBase" name="ldapDetectBase" type="button">
<?php p($l->t('Detect Base DN'));?>
</button>
<button class="ldapTestBase" name="ldapTestBase" type="button">
<?php p($l->t('Test Base DN'));?>
</button>
</div>
<div class="tablerow left">
<input type="checkbox" id="ldap_experienced_admin" value="1"
name="ldap_experienced_admin" class="tablecell lwautosave"
name="ldap_experienced_admin" class="tablecell"
title="<?php p($l->t('Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.'));?>"
/>
<label for="ldap_experienced_admin" class="tablecell">

View File

@ -5,40 +5,61 @@
</p>
<p>
<label for="ldap_userfilter_objectclass">
<?php p($l->t('only those object classes:'));?>
<?php p($l->t('Only these object classes:'));?>
</label>
<select id="ldap_userfilter_objectclass" multiple="multiple"
name="ldap_userfilter_objectclass">
name="ldap_userfilter_objectclass" class="multiSelectPlugin">
</select>
</p>
<p>
<label></label>
<span class="ldapInputColElement"><?php p($l->t('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.'));?></span>
</p>
<p>
<label for="ldap_userfilter_groups">
<?php p($l->t('only from those groups:'));?>
<?php p($l->t('Only from these groups:'));?>
</label>
<input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="<?php p($l->t('Search groups'));?>" />
<select id="ldap_userfilter_groups" multiple="multiple"
name="ldap_userfilter_groups">
name="ldap_userfilter_groups" class="multiSelectPlugin">
</select>
</p>
<p>
<label><a id='toggleRawUserFilter'> <?php p($l->t('Edit raw filter instead'));?></a></label>
<p class="ldapManyGroupsSupport hidden">
<label></label>
<select class="ldapGroupList ldapGroupListAvailable" multiple="multiple"
title="<?php p($l->t('Available groups'));?>"></select>
<span>
<button class="ldapGroupListSelect" type="button">&gt;</button><br/>
<button class="ldapGroupListDeselect" type="button">&lt;</button>
</span>
<select class="ldapGroupList ldapGroupListSelected" multiple="multiple"
title="<?php p($l->t('Selected groups'));?>"></select>
</p>
<p id="rawUserFilterContainer" class="invisible">
<input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter"
class="lwautosave"
placeholder="<?php p($l->t('Raw LDAP filter'));?>"
title="<?php p($l->t('The filter specifies which LDAP users shall have access to the %s instance.', $theme->getName()));?>"
/>
<button class="ldapGetEntryCount hidden" name="ldapGetEntryCount" type="button">
<?php p($l->t('Test Filter'));?>
</button>
<p>
<label><a id='toggleRawUserFilter' class='ldapToggle'> <?php p($l->t('Edit LDAP Query'));?></a></label>
</p>
<p id="ldapReadOnlyUserFilterContainer" class="hidden ldapReadOnlyFilterContainer">
<label><?php p($l->t('LDAP Filter:'));?></label>
<span class="ldapFilterReadOnlyElement ldapInputColElement"></span>
</p>
<p id="rawUserFilterContainer">
<textarea type="text" id="ldap_userlist_filter" name="ldap_userlist_filter"
class="ldapFilterInputElement"
placeholder="<?php p($l->t('Edit LDAP Query'));?>"
title="<?php p($l->t('The filter specifies which LDAP users shall have access to the %s instance.', $theme->getName()));?>">
</textarea>
</p>
<p>
<div class="ldapWizardInfo invisible">&nbsp;</div>
</p>
<p class="ldap_count">
<span id="ldap_user_count">0 <?php p($l->t('users found'));?></span>
<button class="ldapGetEntryCount ldapGetUserCount" name="ldapGetEntryCount" type="button">
<?php p($l->t('Verify settings and count users'));?>
</button>
<span id="ldap_user_count"></span>
</p>
<?php print_unescaped($_['wizardControls']); ?>
</div>

View File

@ -1,3 +1,56 @@
<?php
vendor_script('user_ldap', 'ui-multiselect/src/jquery.multiselect');
vendor_style('user_ldap', 'ui-multiselect/jquery.multiselect');
script('user_ldap', [
'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');
?>
<form id="ldap" class="section" action="#" method="post">
<h2><?php p($l->t('LDAP')); ?></h2>
@ -65,5 +118,6 @@
<?php print_unescaped($_['settingControls']); ?>
</fieldset>
</div>
<!-- Spinner Template -->
<img class="ldapSpinner hidden" src="<?php p(\OCP\Util::imagePath('core', 'loading.gif')); ?>">
</form>

View File

@ -108,12 +108,6 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
* @return void
*/
private function prepareAccessForCheckPassword(&$access, $noDisplayName = false) {
$access->expects($this->once())
->method('escapeFilterPart')
->will($this->returnCallback(function($uid) {
return $uid;
}));
$access->connection->expects($this->any())
->method('__get')
->will($this->returnCallback(function($name) {
@ -132,6 +126,15 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
return array();
}));
$access->expects($this->any())
->method('fetchUsersByLoginName')
->will($this->returnCallback(function($uid) {
if($uid === 'roland') {
return array(array('dn' => 'dnOfRoland,dc=test'));
}
return array();
}));
$retVal = 'gunslinger';
if($noDisplayName === true) {
$retVal = false;

View File

@ -79,14 +79,10 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
* Check if the password is correct without logging in the user
*/
public function checkPassword($uid, $password) {
$uid = $this->access->escapeFilterPart($uid);
//find out dn of the user name
$attrs = array($this->access->connection->ldapUserDisplayName, 'dn',
'uid', 'samaccountname');
$filter = \OCP\Util::mb_str_replace(
'%uid', $uid, $this->access->connection->ldapLoginFilter, 'UTF-8');
$users = $this->access->fetchListOfUsers($filter, $attrs);
$users = $this->access->fetchUsersByLoginName($uid, $attrs);
if(count($users) < 1) {
return false;
}