aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@owncloud.com>2013-10-04 16:33:37 +0200
committerArthur Schiwon <blizzz@owncloud.com>2013-10-17 19:13:27 +0200
commit3b1822cf91407f243f18311b8abc273ad2eb1b11 (patch)
treedce41cedcf3916bd9ef4951b74fec029fb9636c7 /apps
parent8290929aa6fcb1e62e79d7acf8bf310c8d6f94d7 (diff)
downloadnextcloud-server-3b1822cf91407f243f18311b8abc273ad2eb1b11.tar.gz
nextcloud-server-3b1822cf91407f243f18311b8abc273ad2eb1b11.zip
LDAP Wizard: add detection, load and save of LDAP objectClasses for filter purposes
Diffstat (limited to 'apps')
-rw-r--r--apps/user_ldap/ajax/wizard.php1
-rw-r--r--apps/user_ldap/css/settings.css20
-rw-r--r--apps/user_ldap/js/settings.js81
-rw-r--r--apps/user_ldap/lib/configuration.php20
-rw-r--r--apps/user_ldap/lib/ildapwrapper.php16
-rw-r--r--apps/user_ldap/lib/ldap.php8
-rw-r--r--apps/user_ldap/lib/wizard.php150
-rw-r--r--apps/user_ldap/lib/wizardresult.php11
-rw-r--r--apps/user_ldap/settings.php12
-rw-r--r--apps/user_ldap/templates/part.wizard-server.php4
-rw-r--r--apps/user_ldap/templates/part.wizard-userfilter.php51
-rw-r--r--apps/user_ldap/templates/settings.php2
12 files changed, 343 insertions, 33 deletions
diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php
index 7df922f17af..807f04ca696 100644
--- a/apps/user_ldap/ajax/wizard.php
+++ b/apps/user_ldap/ajax/wizard.php
@@ -46,6 +46,7 @@ $wizard = new \OCA\user_ldap\lib\Wizard($configuration, $ldapWrapper);
switch($action) {
case 'guessPortAndTLS':
case 'guessBaseDN':
+ case 'determineObjectClasses':
try {
$result = $wizard->$action();
if($result !== false) {
diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css
index aa6c4687cf9..f6c9f756339 100644
--- a/apps/user_ldap/css/settings.css
+++ b/apps/user_ldap/css/settings.css
@@ -1,5 +1,6 @@
.table {
display: table;
+ width: 60%;
}
.tablecell {
@@ -18,7 +19,7 @@
height: 15px;
}
-.hidden {
+.hidden, .invisible {
visibility: hidden;
}
@@ -55,6 +56,16 @@
width: 96.5% !important;
}
+.tableCellInput {
+ margin-left: -40%;
+ width: 100%;
+}
+
+.tableCellLabel {
+ text-align: right;
+ padding-right: 25%;
+}
+
.ldapIndent {
margin-left: 50px;
}
@@ -81,4 +92,11 @@
#ldap fieldset p input[type=checkbox] {
vertical-align: bottom;
+}
+
+select[multiple=multiple] + button {
+ height: 28px;
+ padding-top: 6px !important;
+ min-width: 40%;
+ max-width: 40%;
} \ No newline at end of file
diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js
index 45b1a9239f5..88f63e25ca2 100644
--- a/apps/user_ldap/js/settings.js
+++ b/apps/user_ldap/js/settings.js
@@ -144,7 +144,10 @@ var LdapWizard = {
applyChanges: function (result) {
for (id in result.changes) {
- LdapWizard.saveBlacklist[id] = true;
+ if(!$.isArray(result.changes[id])) {
+ //no need to blacklist multiselect
+ LdapWizard.saveBlacklist[id] = true;
+ }
$('#'+id).val(result.changes[id]);
}
},
@@ -162,12 +165,12 @@ var LdapWizard = {
function(result) {
LdapWizard.applyChanges(result);
if($('#ldap_base').val()) {
- $('#ldap_base').removeClass('hidden');
+ $('#ldap_base').removeClass('invisible');
LdapWizard.hideInfoBox();
}
},
function (result) {
- $('#ldap_base').removeClass('hidden');
+ $('#ldap_base').removeClass('invisible');
LdapWizard.showInfoBox('Please specify a port');
}
);
@@ -187,28 +190,59 @@ var LdapWizard = {
function(result) {
LdapWizard.applyChanges(result);
if($('#ldap_port').val()) {
- $('#ldap_port').removeClass('hidden');
+ $('#ldap_port').removeClass('invisible');
LdapWizard.hideInfoBox();
}
},
function (result) {
- $('#ldap_port').removeClass('hidden');
+ $('#ldap_port').removeClass('invisible');
LdapWizard.showInfoBox('Please specify the BaseDN');
}
);
}
},
+ findObjectClasses: function() {
+ param = 'action=determineObjectClasses'+
+ '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val();
+
+ LdapWizard.ajax(param,
+ function(result) {
+ $('#ldap_userfilter_objectclass').find('option').remove();
+ for (i in result.options['ldap_userfilter_objectclass']) {
+ //FIXME: move HTML into template
+ objc = result.options['ldap_userfilter_objectclass'][i];
+ $('#ldap_userfilter_objectclass').append("<option value='"+objc+"'>"+objc+"</option>");
+ }
+ LdapWizard.applyChanges(result);
+ $('#ldap_userfilter_objectclass').multiselect('refresh');
+ },
+ function (result) {
+ //TODO: error handling
+ }
+ );
+ },
+
hideInfoBox: function() {
if(LdapWizard.checkInfoShown) {
- $('#ldapWizard1 .ldapWizardInfo').addClass('hidden');
+ $('#ldapWizard1 .ldapWizardInfo').addClass('invisible');
LdapWizard.checkInfoShown = false;
}
},
init: function() {
if($('#ldap_port').val()) {
- $('#ldap_port').removeClass('hidden');
+ $('#ldap_port').removeClass('invisible');
+ }
+ },
+
+ initUserFilter: function() {
+ LdapWizard.findObjectClasses();
+ },
+
+ onTabChange: function(event, ui) {
+ if(ui.newTab[0].id === '#ldapWizard2') {
+ LdapWizard.initUserFilter();
}
},
@@ -227,8 +261,20 @@ var LdapWizard = {
delete LdapWizard.saveBlacklist[inputObj.id];
return;
}
- param = 'cfgkey='+inputObj.id+
- '&cfgval='+$(inputObj).val()+
+ LdapWizard._save(inputObj, $(inputObj).val());
+ },
+
+ saveMultiSelect: function(originalObj, resultObj) {
+ values = '';
+ for(i = 0; i < resultObj.length; i++) {
+ values = values + "\n" + resultObj[i].value;
+ }
+ LdapWizard._save($('#'+originalObj)[0], $.trim(values));
+ },
+
+ _save: function(object, value) {
+ param = 'cfgkey='+object.id+
+ '&cfgval='+value+
'&action=save'+
'&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val();
@@ -237,7 +283,7 @@ var LdapWizard = {
param,
function(result) {
if(result.status == 'success') {
- LdapWizard.processChanges(inputObj);
+ LdapWizard.processChanges(object);
} else {
// alert('Oooooooooooh :(');
}
@@ -247,17 +293,28 @@ var LdapWizard = {
showInfoBox: function(text) {
$('#ldapWizard1 .ldapWizardInfo').text(t('user_ldap', text));
- $('#ldapWizard1 .ldapWizardInfo').removeClass('hidden');
+ $('#ldapWizard1 .ldapWizardInfo').removeClass('invisible');
LdapWizard.checkInfoShown = true;
}
};
$(document).ready(function() {
$('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'});
- $('#ldapSettings').tabs();
+ $('#ldapSettings').tabs({ beforeActivate: LdapWizard.onTabChange });
$('#ldap_submit').button();
$('#ldap_action_test_connection').button();
$('#ldap_action_delete_configuration').button();
+ $('#ldap_userfilter_groups').multiselect();
+ $('#ldap_userfilter_objectclass').multiselect({
+ header: false,
+ selectedList: 9,
+ noneSelectedText: t('user_ldap', 'Select object classes'),
+ click: function(event, ui) {
+ LdapWizard.saveMultiSelect('ldap_userfilter_objectclass',
+ $('#ldap_userfilter_objectclass').multiselect("getChecked")
+ );
+ }
+ });
$('.lwautosave').change(function() { LdapWizard.save(this); });
LdapConfiguration.refreshConfig();
$('#ldap_action_test_connection').click(function(event){
diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php
index ed6f384da88..e67e0d8d00b 100644
--- a/apps/user_ldap/lib/configuration.php
+++ b/apps/user_ldap/lib/configuration.php
@@ -44,6 +44,7 @@ class Configuration {
'turnOffCertCheck' => null,
'ldapIgnoreNamingRules' => null,
'ldapUserDisplayName' => null,
+ 'ldapUserFilterObjectclass' => null,
'ldapUserFilter' => null,
'ldapGroupFilter' => null,
'ldapGroupDisplayName' => null,
@@ -121,6 +122,7 @@ class Configuration {
case 'ldapBaseGroups':
case 'ldapAttributesForUserSearch':
case 'ldapAttributesForGroupSearch':
+ case 'ldapUserFilterObjectclass':
$setMethod = 'setMultiLine';
default:
$this->$setMethod($key, $val);
@@ -136,19 +138,18 @@ class Configuration {
if(!$this->configRead && !is_null($this->configPrefix)) {
$cta = array_flip($this->getConfigTranslationArray());
foreach($this->config as $key => $val) {
-// if($this->configPrefix == 's04') var_dump($key);
if(!isset($cta[$key])) {
//some are determined
continue;
}
$dbkey = $cta[$key];
-// if($this->configPrefix == 's04') var_dump($dbkey);
switch($key) {
case 'ldapBase':
case 'ldapBaseUsers':
case 'ldapBaseGroups':
case 'ldapAttributesForUserSearch':
case 'ldapAttributesForGroupSearch':
+ case 'ldapUserFilterObjectclass':
$readMethod = 'getMultiLine';
break;
case 'ldapIgnoreNamingRules':
@@ -166,16 +167,10 @@ class Configuration {
$readMethod = 'getValue';
break;
}
-// if($this->configPrefix == 's04') var_dump($readMethod);
$this->config[$key] = $this->$readMethod($dbkey);
}
$this->configRead = true;
}
- if($this->configPrefix == 's03') {
-// var_dump($this->config);
-
-// die;
- }
}
/**
@@ -193,6 +188,7 @@ class Configuration {
case 'ldapBaseGroups':
case 'ldapAttributesForUserSearch':
case 'ldapAttributesForGroupSearch':
+ case 'ldapUserFilterObjectclass':
if(is_array($value)) {
$value = implode("\n", $value);
}
@@ -250,12 +246,6 @@ class Configuration {
if(is_null($defaults)) {
$defaults = $this->getDefaults();
}
-// if($this->configPrefix == 's04') var_dump($this->configPrefix.$varname);
-// if(0 == $this->configKeyToDBKey($varname)) {
-// var_dump($varname);
-// print("<pre>");
-// debug_print_backtrace(); die;
-// }
return \OCP\Config::getAppValue('user_ldap',
$this->configPrefix.$varname,
$defaults[$varname]);
@@ -288,6 +278,7 @@ class Configuration {
'ldap_base_users' => '',
'ldap_base_groups' => '',
'ldap_userlist_filter' => 'objectClass=person',
+ 'ldap_userfilter_objectclass' => '',
'ldap_login_filter' => 'uid=%uid',
'ldap_group_filter' => 'objectClass=posixGroup',
'ldap_display_name' => 'cn',
@@ -327,6 +318,7 @@ class Configuration {
'ldap_base' => 'ldapBase',
'ldap_base_users' => 'ldapBaseUsers',
'ldap_base_groups' => 'ldapBaseGroups',
+ 'ldap_userfilter_objectclass' => 'ldapUserFilterObjectclass',
'ldap_userlist_filter' => 'ldapUserFilter',
'ldap_login_filter' => 'ldapLoginFilter',
'ldap_group_filter' => 'ldapGroupFilter',
diff --git a/apps/user_ldap/lib/ildapwrapper.php b/apps/user_ldap/lib/ildapwrapper.php
index 5e12c7c63b9..20587cba7db 100644
--- a/apps/user_ldap/lib/ildapwrapper.php
+++ b/apps/user_ldap/lib/ildapwrapper.php
@@ -106,6 +106,14 @@ interface ILDAPWrapper {
public function getAttributes($link, $result);
/**
+ * @brief Get the DN of a result entry
+ * @param $link LDAP link resource
+ * @param $result LDAP result resource
+ * @return string containing the DN, false on error
+ */
+ public function getDN($link, $result);
+
+ /**
* @brief Get all result entries
* @param $link LDAP link resource
* @param $result LDAP result resource
@@ -114,6 +122,14 @@ interface ILDAPWrapper {
public function getEntries($link, $result);
/**
+ * @brief Return next result id
+ * @param $link LDAP link resource
+ * @param $result LDAP entry result resource
+ * @return an LDAP search result resource
+ * */
+ public function nextEntry($link, $result);
+
+ /**
* @brief Read an entry
* @param $link LDAP link resource
* @param $baseDN The DN of the entry to read from
diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php
index 13314462b8c..bc963191722 100644
--- a/apps/user_ldap/lib/ldap.php
+++ b/apps/user_ldap/lib/ldap.php
@@ -69,10 +69,18 @@ class LDAP implements ILDAPWrapper {
return $this->invokeLDAPMethod('get_attributes', $link, $result);
}
+ public function getDN($link, $result) {
+ return $this->invokeLDAPMethod('get_dn', $link, $result);
+ }
+
public function getEntries($link, $result) {
return $this->invokeLDAPMethod('get_entries', $link, $result);
}
+ public function nextEntry($link, $result) {
+ return $this->invokeLDAPMethod('next_entry', $link, $result);
+ }
+
public function read($link, $baseDN, $filter, $attr) {
return $this->invokeLDAPMethod('read', $link, $baseDN, $filter, $attr);
}
diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php
index ad71fd10f63..170af44e118 100644
--- a/apps/user_ldap/lib/wizard.php
+++ b/apps/user_ldap/lib/wizard.php
@@ -28,6 +28,10 @@ class Wizard extends LDAPUtility {
protected $configuration;
protected $result;
+ const LRESULT_PROCESSED_OK = 0;
+ const LRESULT_PROCESSED_INVALID = 1;
+ const LRESULT_PROCESSED_SKIP = 2;
+
/**
* @brief Constructor
* @param $configuration an instance of Configuration
@@ -48,6 +52,51 @@ class Wizard extends LDAPUtility {
}
}
+ public function determineObjectClasses() {
+ if(!$this->checkRequirements(array('ldapHost',
+ 'ldapPort',
+ 'ldapAgentName',
+ 'ldapAgentPassword',
+ 'ldapBase',
+ ))) {
+ return false;
+ }
+ $cr = $this->getConnection();
+ if(!$cr) {
+ throw new \Excpetion('Could not connect to LDAP');
+ }
+
+ $p = 'objectclass=';
+ $obclasses = array($p.'inetOrgPerson', $p.'person',
+ $p.'organizationalPerson', $p.'user',
+ $p.'posixAccount', $p.'*');
+
+ $maxEntryObjC = '';
+ $availableObjectClasses =
+ $this->cumulativeSearchOnAttribute($obclasses, 'objectclass',
+ true, $maxEntryObjC);
+ if(is_array($availableObjectClasses)
+ && count($availableObjectClasses) > 0) {
+ $this->result->addOptions('ldap_userfilter_objectclass',
+ $availableObjectClasses);
+ } else {
+ throw new \Exception(self::$l->t('Could not find any objectClass'));
+ }
+ $setOCs = $this->configuration->ldapUserFilterObjectclass;
+ file_put_contents('/tmp/set', print_r($setOCs, true));
+ if(is_array($setOCs) && !empty($setOCs)) {
+ //something is already configured? pre-select it.
+ $this->result->addChange('ldap_userfilter_objectclass', $setOCs);
+ } else if(!empty($maxEntryObjC)) {
+ //new? pre-select something hopefully sane
+ $maxEntryObjC = str_replace($p, '', $maxEntryObjC);
+ $this->result->addChange('ldap_userfilter_objectclass',
+ $maxEntryObjC);
+ }
+
+ return $this->result;
+ }
+
/**
* Tries to determine the port, requires given Host, User DN and Password
* @returns mixed WizardResult on success, false otherwise
@@ -55,7 +104,8 @@ class Wizard extends LDAPUtility {
public function guessPortAndTLS() {
if(!$this->checkRequirements(array('ldapHost',
'ldapAgentName',
- 'ldapAgentPassword'))) {
+ 'ldapAgentPassword'
+ ))) {
return false;
}
$this->checkHost();
@@ -266,6 +316,104 @@ class Wizard extends LDAPUtility {
return true;
}
+ /**
+ * @brief does a cumulativeSearch on LDAP to get different values of a
+ * specified attribute
+ * @param $filters array, the filters that shall be used in the search
+ * @param $attr the attribute of which a list of values shall be returned
+ * @param $lfw bool, whether the last filter is a wildcard which shall not
+ * be processed if there were already findings, defaults to true
+ * @param $maxF string. if not null, this variable will have the filter that
+ * yields most result entries
+ * @return mixed, an array with the values on success, false otherwise
+ *
+ */
+ private function cumulativeSearchOnAttribute($filters, $attr, $lfw = true, &$maxF = null) {
+ $dnRead = array();
+ $foundItems = array();
+ $maxEntries = 0;
+ if(!is_array($this->configuration->ldapBase) || !isset($this->configuration->ldapBase[0])) {
+ return false;
+ }
+ $base = $this->configuration->ldapBase[0];
+ $cr = $this->getConnection();
+ if(!is_resource($cr)) {
+ return false;
+ }
+ foreach($filters as $filter) {
+ if($lfw && count($foundItems) > 0) {
+ continue;
+ }
+ $rr = $this->ldap->search($cr, $base, $filter, array($attr));
+ if(!$this->ldap->isResource($rr)) {
+ \OCP\Util::writeLog('user_ldap', 'Search failed, Base '.$base, \OCP\Util::DEBUG);
+ continue;
+ }
+ $entries = $this->ldap->countEntries($cr, $rr);
+ $getEntryFunc = 'firstEntry';
+ if(($entries !== false) && ($entries > 0)) {
+ if(!is_null($maxF) && $entries > $maxEntries) {
+ $maxEntries = $entries;
+ $maxF = $filter;
+ }
+ do {
+ $entry = $this->ldap->$getEntryFunc($cr, $rr);
+ if(!$this->ldap->isResource($entry)) {
+ continue 2;
+ }
+ $attributes = $this->ldap->getAttributes($cr, $entry);
+ $dn = $this->ldap->getDN($cr, $entry);
+ if($dn === false || in_array($dn, $dnRead)) {
+ continue;
+ }
+ $state = $this->getAttributeValuesFromEntry($attributes,
+ $attr,
+ $foundItems);
+ $dnRead[] = $dn;
+ $getEntryFunc = 'nextEntry';
+ $rr = $entry; //will be expected by nextEntry next round
+ } while($state === self::LRESULT_PROCESSED_SKIP
+ || $this->ldap->isResource($entry));
+ }
+ }
+
+ return $foundItems;
+ }
+
+ /**
+ * @brief appends a list of values fr
+ * @param $result resource, the return value from ldap_get_attributes
+ * @param $attribute string, the attribute values to look for
+ * @param &$known array, new values will be appended here
+ * @return int, state on of the class constants LRESULT_PROCESSED_OK,
+ * LRESULT_PROCESSED_INVALID or LRESULT_PROCESSED_SKIP
+ */
+ private function getAttributeValuesFromEntry($result, $attribute, &$known) {
+ if(!is_array($result)
+ || !isset($result['count'])
+ || !$result['count'] > 0) {
+ return self::LRESULT_PROCESSED_INVALID;
+ }
+
+ //strtolower on all keys for proper comparison
+ $result = \OCP\Util::mb_array_change_key_case($result);
+ $attribute = strtolower($attribute);
+ if(isset($result[$attribute])) {
+ foreach($result[$attribute] as $key => $val) {
+ if($key === 'count') {
+ continue;
+ }
+ if(!in_array($val, $known)) {
+ \OCP\Util::writeLog('user_ldap', 'Found objclass '.$val, \OCP\Util::DEBUG);
+ $known[] = $val;
+ }
+ }
+ return self::LRESULT_PROCESSED_OK;
+ } else {
+ return self::LRESULT_PROCESSED_SKIP;
+ }
+ }
+
private function getConnection() {
$cr = $this->ldap->connect(
$this->configuration->ldapHost.':'.$this->configuration->ldapPort,
diff --git a/apps/user_ldap/lib/wizardresult.php b/apps/user_ldap/lib/wizardresult.php
index 2140f654fd0..4c3b563c0c2 100644
--- a/apps/user_ldap/lib/wizardresult.php
+++ b/apps/user_ldap/lib/wizardresult.php
@@ -25,11 +25,19 @@ namespace OCA\user_ldap\lib;
class WizardResult {
protected $changes = array();
+ protected $options = array();
public function addChange($key, $value) {
$this->changes[$key] = $value;
}
+ public function addOptions($key, $values) {
+ if(!is_array($values)) {
+ $values = array($values);
+ }
+ $this->options[$key] = $values;
+ }
+
public function hasChanges() {
return count($this->changes) > 0;
}
@@ -37,6 +45,9 @@ class WizardResult {
public function getResultArray() {
$result = array();
$result['changes'] = $this->changes;
+ if(count($this->options) > 0) {
+ $result['options'] = $this->options;
+ }
return $result;
}
} \ No newline at end of file
diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php
index 8a418a65005..ebba1dbd3a6 100644
--- a/apps/user_ldap/settings.php
+++ b/apps/user_ldap/settings.php
@@ -25,8 +25,11 @@
OC_Util::checkAdminUser();
-OCP\Util::addscript('user_ldap', 'settings');
-OCP\Util::addstyle('user_ldap', 'settings');
+OCP\Util::addScript('user_ldap', 'settings');
+OCP\Util::addScript('core', 'jquery.multiselect');
+OCP\Util::addStyle('user_ldap', 'settings');
+OCP\Util::addStyle('core', 'jquery.multiselect');
+OCP\Util::addStyle('core', 'jquery-ui-1.10.0.custom');
// fill template
$tmpl = new OCP\Template('user_ldap', 'settings');
@@ -49,6 +52,11 @@ $wizard1->assign('wizardControls', $wControls);
$wizardHtml .= $wizard1->fetchPage();
$toc['#ldapWizard1'] = 'Server';
+$wizard2 = new OCP\Template('user_ldap', 'part.wizard-userfilter');
+$wizard2->assign('wizardControls', $wControls);
+$wizardHtml .= $wizard2->fetchPage();
+$toc['#ldapWizard2'] = 'User Filter';
+
$tmpl->assign('tabs', $wizardHtml);
$tmpl->assign('toc', $toc);
diff --git a/apps/user_ldap/templates/part.wizard-server.php b/apps/user_ldap/templates/part.wizard-server.php
index ae0a7e650c9..c6900fd24e8 100644
--- a/apps/user_ldap/templates/part.wizard-server.php
+++ b/apps/user_ldap/templates/part.wizard-server.php
@@ -33,7 +33,7 @@
/>
<span>
<input type="number" id="ldap_port" name="ldap_port"
- class="hidden lwautosave"
+ class="invisible lwautosave"
data-default="<?php p($_['ldap_port_default']); ?>"
placeholder="<?php p($l->t('Port'));?>" />
</span>
@@ -68,7 +68,7 @@
</div>
<div class="tablerow">
- <div class="tablecell ldapWizardInfo hidden">&nbsp;
+ <div class="tablecell ldapWizardInfo invisible">&nbsp;
</div>
</div>
</div>
diff --git a/apps/user_ldap/templates/part.wizard-userfilter.php b/apps/user_ldap/templates/part.wizard-userfilter.php
new file mode 100644
index 00000000000..b58784b680f
--- /dev/null
+++ b/apps/user_ldap/templates/part.wizard-userfilter.php
@@ -0,0 +1,51 @@
+<fieldset id="ldapWizard2">
+
+ <div>
+ <p>
+ <?php p($l->t('Limit the access to ownCloud to users meetignthis criteria:'));?>
+ </p>
+
+ <p>
+ <label for="ldap_userfilter_objectclass">
+ <?php p($l->t('only those object classes:'));?>
+ </label>
+
+ <select id="ldap_userfilter_objectclass" multiple="multiple"
+ name="ldap_userfilter_objectclass"
+ data-default="<?php p($_['ldap_userfilter_objectclass_default']); ?>">
+<!-- <option><?php p($l->t('Any'));?></option> -->
+ </select>
+ </p>
+
+ <p>
+ <label for="ldap_userfilter_groups">
+ <?php p($l->t('only from those groups:'));?>
+ </label>
+
+ <select id="ldap_userfilter_groups" multiple="multiple"
+ name="ldap_userfilter_groups" class="lwautosave"
+ data-default="<?php p($_['ldap_userfilter_groups_default']); ?>">
+<!-- <option value="TODOfillIn">TODO: fill in object classes via Ajax</option> -->
+<!-- <option value="TODOfillIn2">22222</option> -->
+ </select>
+ </p>
+
+ <p>
+ <label><a>↓ <?php p($l->t('Edit raw filter instead'));?></a></label>
+ </p>
+
+ <p class="invisible">
+ <input type="text" id="ldap_userlistfilter_raw" name="ldap_userlistfilter_raw"
+ class="lwautosave"
+ data-default="<?php p($_['ldap_userlistfilter_raw_default']); ?>"
+ placeholder="<?php p($l->t('Raw LDAP filter'));?>"
+ title="<?php p($l->t('The filter specifies which LDAP users shall have access to the ownCloud instance.'));?>"
+ />
+ </p>
+
+ <p>
+ <div class="ldapWizardInfo invisible">&nbsp;</div>
+ </p>
+ <?php print_unescaped($_['wizardControls']); ?>
+ </div>
+</fieldset> \ No newline at end of file
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
index 9ca9673ada2..f63a4bdef1f 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -2,7 +2,7 @@
<div id="ldapSettings" class="personalblock">
<ul>
<?php foreach($_['toc'] as $id => $title) { ?>
- <li><a href="<?php p($id); ?>"><?php p($title); ?></a></li>
+ <li id="<?php p($id); ?>"><a href="<?php p($id); ?>"><?php p($title); ?></a></li>
<?php } ?>
<li class="ldapSettingsTabs"><a href="#ldapSettings-3">Expert</a></li>
<li class="ldapSettingsTabs"><a href="#ldapSettings-2">Advanced</a></li>