LDAP Wizard Overhaul

wizard refactor

reimplement save spinners and cursor

implement Port detector

introduced detector queue, added base dn detector

disable input fields when detectors are running

introduce spinners for fields that are being updated by detector

cache jq element objects

consolidate processing of detector results in generic / abstract base class

display notification if a detector discovered a problem

don't run base dn detector if a base is configured

reset detector queue on configuration switch

implement functionality check and update of status indicator

document ConfigModel

jsdoc for controller and main view

more documentation

implement the user filter tab view

so far the multiselects get initialized (not filled yet) and the mode can be switched.

mode is also restored.

reintroduce filter switch confirmation in admin XP mode

new detector for user object classes. so we also load user object classes if necessary and are able to save and show the setting.

multiselect trigger save actions now on close only

show spinners automatically, when a detector is running

20k limit for object classes preselection test

adjust wordings, fix grammar

add group (for users tab) detector

also includes wording fixes

error presentation moved from detectors to view, where it belongs

add info label to users page

missing wording changes

show effective LDAP filter in Assisted Mode

add user filter detector

implement count button for users and limit all count actions to 1001 for performance reasons

make port field a bit bigger. not perfect though.

do not detect port automatically

implement login filter tab view

only load features in assisted mode and don't enable assisted fields while in raw mode

add tooltips on login filter checkbox options for better understanding

permanently show filter on login tab

and also compile login filter in assisted mode

test/verify button on login attributes tab, with backend changes.

only run wizard requests if your an active tab. also run compile filter requests when switching to assisted mode

underline toggle filter links to stress that they are clickable

unity user and group tab functionality in common abstract class, add group filter tab view. only detectors and template adjustments left to have group tab implementation complete

add object class and group detector for groups as well as filter composer

show ldap filter permanently on groups tab

introduce input element that can deal better with many groups, will be used with > 40

fix disabling complex group chooser while detection is running

hide complex group chooser on config switch

fix few more issues with complex chooser

make complex group chooser available on Users tab as well

detect base dn improvements/changes:

- do not look for Base DN automatically, offer a button instead
- fix for alternative way to detect a base dn (if agent dn is not given)
- do not trigger filter composers on config switch

Changes with configuration chooser controls

- "New" was removed out of the configuration list
- and split into buttons "add" and "copy"
- delete button is also now an icon

add test button for Base DN

reimplement advanced tab. The save button is gone.

reimplement expert tab

remove unused methods

implement mail attribute detector

implement user display name attribute detection

implement member group association detector

replace text input with textarea for raw filter input

finish functionality check

auto-enable good configurations, as it was before

cleanup

move save confirmation handling to base class, reduces code duplication

enable tabs only if no running save processes are left.

move onConfigLoaded to base class, avoids code duplication

simplify, save LOCs

Test Configuration button to be dealt with in main view as it is a cross-tab element

require detectorQueue in constructor

cleanup

put bootstrap into a function and thus make it testable

get rid of old stuff
This commit is contained in:
Arthur Schiwon 2015-03-03 11:56:03 +01:00
parent 56f1ffe820
commit 5355c285fc
61 changed files with 5110 additions and 1620 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,433 @@
/**
* 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)) {
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);
}
},
/**
* 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));
tabs.push(new OCA.LDAP.Wizard.WizardTabLoginFilter());
tabs.push(new OCA.LDAP.Wizard.WizardTabGroupFilter(filterOnTypeFactory));
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,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 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.wizardMethod = 'getUserLoginFilter';
}
});
OCA.LDAP.Wizard.WizardDetectorFilterLogin = WizardDetectorFilterLogin;
})();

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 WizardDetectorFilterUser = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
init: function() {
this.setTrigger([
'ldap_userfilter_groups',
'ldap_userfilter_objectclass'
]);
this.setTargetKey('ldap_userlist_filter');
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 a Port Detector. It executes the auto-detection of the port
* by the ownCloud server, if requirements are met.
*
* @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,373 @@
/**
* 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);
} 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,346 @@
/**
* 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) {
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})+'</option>';
$(view.configChooserID + ' option:last').before(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,235 @@
/**
* 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 = result.changes.ldap_test_effective_filter;
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();
},
/**
* 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;
}

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,25 @@ 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');
$this->result->addChange('ldap_test_base', $total);
return $this->result;
}
/**
* counts users with a specified attribute
* @param string $attr
@ -281,45 +313,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 +460,9 @@ class Wizard extends LDAPUtility {
return false;
}
$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
$this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
//so it will be saved on destruct
$this->result->markChange();
//$this->result->markChange();
return $this->result;
}
@ -603,6 +597,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,11 +703,14 @@ class Wizard extends LDAPUtility {
}
$dparts = explode('.', $domain);
$base2 = implode('dc=', $dparts);
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 +752,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 +762,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 +1146,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,52 @@
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');
\OCP\Util::addScript('user_ldap', 'wizard/controller');
\OCP\Util::addScript('user_ldap', 'wizard/configModel');
\OCP\Util::addScript('user_ldap', 'wizard/view');
\OCP\Util::addScript('user_ldap', 'wizard/wizardObject');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabGeneric');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabElementary');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabAbstractFilter');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabUserFilter');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabLoginFilter');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabGroupFilter');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabAdvanced');
\OCP\Util::addScript('user_ldap', 'wizard/wizardTabExpert');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorQueue');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorGeneric');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorPort');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorBaseDN');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorFeatureAbstract');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorUserObjectClasses');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorGroupObjectClasses');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorGroupsForUsers');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorGroupsForGroups');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorSimpleRequestAbstract');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorFilterUser');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorFilterLogin');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorFilterGroup');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorUserCount');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorGroupCount');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorEmailAttribute');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorUserDisplayNameAttribute');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorUserGroupAssociation');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorAvailableAttributes');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorTestAbstract');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorTestLoginName');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorTestBaseDN');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorTestConfiguration');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorClearUserMappings');
\OCP\Util::addScript('user_ldap', 'wizard/wizardDetectorClearGroupMappings');
\OCP\Util::addScript('user_ldap', 'wizard/wizardFilterOnType');
\OCP\Util::addScript('user_ldap', 'wizard/wizardFilterOnTypeFactory');
\OCP\Util::addScript('user_ldap', 'wizard/wizard');
OCP\Util::addStyle('user_ldap', 'settings');
// fill template
$tmpl = new OCP\Template('user_ldap', 'settings');
@ -55,9 +94,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

@ -65,5 +65,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

@ -0,0 +1,3 @@
<form id="ldapWizard" class="section" action="#" method="post">
<h2><?php p($l->t('LDAP2')); ?></h2>
</form>

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