* LDAP: implement optional group-filter

also some code cleanups and fixes
This commit is contained in:
ralf 2022-05-20 21:46:48 +02:00
parent f42a10deb7
commit ab427562b7
4 changed files with 119 additions and 71 deletions

View File

@ -32,14 +32,15 @@ use setup_cmd_ldap;
* *
* A user is recognised by eGW, if he's in the user_context tree AND has the posixAccount object class AND * A user is recognised by eGW, if he's in the user_context tree AND has the posixAccount object class AND
* matches the LDAP search filter specified in setup >> configuration. * matches the LDAP search filter specified in setup >> configuration.
* A group is recogniced by eGW, if it's in the group_context tree AND has the posixGroup object class. * A group is recognised by eGW, if it's in the group_context tree AND has the posixGroup object class AND
* - if specified - matches the LDAP group filter.
* The group members are stored as memberuid's. * The group members are stored as memberuid's.
* *
* The (positive) group-id's (gidnumber) of LDAP groups are mapped in this class to negative numeric * The (positive) group-id's (gidnumber) of LDAP groups are mapped in this class to negative numeric
* account_id's to not conflict with the user-id's, as both share in eGW internaly the same numberspace! * account_id's to not conflict with the user-id's, as both share in eGW internally the same numberspace!
* *
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> * @author Ralf Becker <rb@egroupware.org>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @license https://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @access internal only use the interface provided by the accounts class * @access internal only use the interface provided by the accounts class
*/ */
class Ldap class Ldap
@ -51,7 +52,7 @@ class Ldap
/** /**
* resource with connection to the ldap server * resource with connection to the ldap server
* *
* @var resource * @var resource|object
*/ */
var $ds; var $ds;
/** /**
@ -72,6 +73,12 @@ class Ldap
* @var string * @var string
*/ */
var $group_context; var $group_context;
/**
* Additional LDAP search filter for groups
*
* @var string
*/
var $group_filter;
/** /**
* total number of found entries from get_list method * total number of found entries from get_list method
* *
@ -79,10 +86,8 @@ class Ldap
*/ */
var $total; var $total;
var $ldapServerInfo;
/** /**
* required classe for user and groups * required object-classes for user and groups
* *
* @var array * @var array
*/ */
@ -140,7 +145,7 @@ class Ldap
const CHANGE_ACCOUNT_LID = true; const CHANGE_ACCOUNT_LID = true;
/** /**
* does backend requires password to be set, before allowing to enable an account * does backend require password to be set, before allowing to enable an account
*/ */
const REQUIRE_PASSWORD_FOR_ENABLE = false; const REQUIRE_PASSWORD_FOR_ENABLE = false;
@ -153,13 +158,34 @@ class Ldap
{ {
$this->frontend = $frontend; $this->frontend = $frontend;
$this->ldap = Api\Ldap::factory(false, $this->frontend->config['ldap_host'], $this->ds = $this->ldap_connection();
$this->frontend->config['ldap_root_dn'],$this->frontend->config['ldap_root_pw']);
$this->ds = $this->ldap->ds;
$this->user_context = $this->frontend->config['ldap_context']; $this->user_context = $this->frontend->config['ldap_context'];
$this->account_filter = $this->frontend->config['ldap_search_filter']; $this->account_filter = $this->frontend->config['ldap_search_filter'];
$this->group_context = $this->frontend->config['ldap_group_context'] ?: $this->frontend->config['ldap_context']; $this->group_context = $this->frontend->config['ldap_group_context'] ?: $this->frontend->config['ldap_context'];
$this->group_filter = $this->frontend->config['ldap_group_filter'];
if (!empty($this->group_filter) && !($this->group_filter[0] === '(' && substr($this->group_filter, -1) === ')'))
{
$this->group_filter = '('.$this->group_filter.')';
}
}
/**
* Get connection to ldap server and optionally reconnect
*
* @param boolean $reconnect =false true: reconnect even if already connected
* @return resource|object
* @throws Api\Exception\AssertionFailed
* @throws Api\Exception\NoPermission
*/
function ldap_connection(bool $reconnect = false)
{
$this->ldap = Api\Ldap::factory(false, $this->frontend->config['ldap_host'],
$this->frontend->config['ldap_root_dn'],$this->frontend->config['ldap_root_pw'], $reconnect);
$this->serverinfo = $this->ldap->getLDAPServerInfo();
return $this->ldap->ds;
} }
/** /**
@ -194,9 +220,9 @@ class Ldap
$data_utf8 = Api\Translation::convert($data,Api\Translation::charset(),'utf-8'); $data_utf8 = Api\Translation::convert($data,Api\Translation::charset(),'utf-8');
$members = $data['account_members']; $members = $data['account_members'];
if (!is_object($this->ldapServerInfo)) if (!is_object($this->serverinfo))
{ {
$this->ldapServerInfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']); $this->serverinfo = $this->ldap->getLDAPServerInfo();
} }
// common code for users and groups // common code for users and groups
// checks if account_lid (dn) has been changed or required objectclass'es are missing // checks if account_lid (dn) has been changed or required objectclass'es are missing
@ -264,7 +290,7 @@ class Ldap
$add = $additional; $add = $additional;
$additional = array_shift($add); $additional = array_shift($add);
} }
if ($this->ldapServerInfo->supportsObjectClass($additional)) if ($this->serverinfo->supportsObjectClass($additional))
{ {
$to_write['objectclass'][] = $additional; $to_write['objectclass'][] = $additional;
if ($add) $to_write += $add; if ($add) $to_write += $add;
@ -299,7 +325,7 @@ class Ldap
$keep_objectclass = false; $keep_objectclass = false;
if (is_array($forward)) list($forward,$extra_attr,$keep_objectclass) = $forward; if (is_array($forward)) list($forward,$extra_attr,$keep_objectclass) = $forward;
if ($this->ldapServerInfo->supportsObjectClass($objectclass) && if ($this->serverinfo->supportsObjectClass($objectclass) &&
($old && in_array($objectclass,$old['objectclass']) || $data_utf8['account_email'] || $old[static::MAIL_ATTR])) ($old && in_array($objectclass,$old['objectclass']) || $data_utf8['account_email'] || $old[static::MAIL_ATTR]))
{ {
if ($data_utf8['account_email']) // setting an email if ($data_utf8['account_email']) // setting an email
@ -431,13 +457,13 @@ class Ldap
protected function _read_group($account_id) protected function _read_group($account_id)
{ {
$group = array(); $group = array();
if (!is_object($this->ldapServerInfo)) if (!is_object($this->serverinfo))
{ {
$this->ldapServerInfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']); $this->serverinfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']);
} }
foreach(array_keys($this->group_mail_classes) as $objectclass) foreach(array_keys($this->group_mail_classes) as $objectclass)
{ {
if ($this->ldapServerInfo->supportsObjectClass($objectclass)) if ($this->serverinfo->supportsObjectClass($objectclass))
{ {
$group['mailAllowed'] = $objectclass; $group['mailAllowed'] = $objectclass;
break; break;
@ -703,17 +729,17 @@ class Ldap
$filter = "(&(objectclass=posixaccount)"; $filter = "(&(objectclass=posixaccount)";
if (!empty($query) && $query != '*') if (!empty($query) && $query != '*')
{ {
switch($param['query_type']) switch ($param['query_type'])
{ {
case 'all': case 'all':
default: default:
$query = '*'.$query; $query = '*' . $query;
// fall-through // fall-through
case 'start': case 'start':
$query .= '*'; $query .= '*';
// use now exact, as otherwise groups have "**pattern**", which dont match anything // use now exact, as otherwise groups have "**pattern**", which dont match anything
$param['query_type'] = 'exact'; $param['query_type'] = 'exact';
// fall-through // fall-through
case 'exact': case 'exact':
$filter .= "(|(uid=$query)(sn=$query)(cn=$query)(givenname=$query)(mail=$query))"; $filter .= "(|(uid=$query)(sn=$query)(cn=$query)(givenname=$query)(mail=$query))";
break; break;
@ -723,11 +749,11 @@ class Ldap
case 'email': case 'email':
$to_ldap = array( $to_ldap = array(
'firstname' => 'givenname', 'firstname' => 'givenname',
'lastname' => 'sn', 'lastname' => 'sn',
'lid' => 'uid', 'lid' => 'uid',
'email' => static::MAIL_ATTR, 'email' => static::MAIL_ATTR,
); );
$filter .= '('.$to_ldap[$param['query_type']].'=*'.$query.'*)'; $filter .= '(' . $to_ldap[$param['query_type']] . '=*' . $query . '*)';
break; break;
} }
} }
@ -755,14 +781,15 @@ class Ldap
$order = isset($propertyMap[$orders[0]]) ? $propertyMap[$orders[0]] : 'uid'; $order = isset($propertyMap[$orders[0]]) ? $propertyMap[$orders[0]] : 'uid';
$sri = ldap_search($this->ds, $this->user_context, $filter,array('uid', $order)); $sri = ldap_search($this->ds, $this->user_context, $filter,array('uid', $order));
$fullSet = array(); $fullSet = array();
foreach ((array)ldap_get_entries($this->ds, $sri) as $key => $entry) foreach (ldap_get_entries($this->ds, $sri) ?: [] as $key => $entry)
{ {
if ($key !== 'count') $fullSet[$entry['uid'][0]] = $entry[$order][0]; if ($key !== 'count') $fullSet[$entry['uid'][0]] = $entry[$order][0];
} }
if (is_numeric($param['type'])) // return only group-members if (is_numeric($param['type'])) // return only group-members
{ {
$sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=" . abs($param['type']) . "))",array('memberuid')); $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=" .
abs($param['type']) . "))",array('memberuid'));
$group = ldap_get_entries($this->ds, $sri); $group = ldap_get_entries($this->ds, $sri);
$fullSet = $group[0]['memberuid'] ? array_intersect_key($fullSet, array_flip($group[0]['memberuid'])) : array(); $fullSet = $group[0]['memberuid'] ? array_intersect_key($fullSet, array_flip($group[0]['memberuid'])) : array();
} }
@ -774,12 +801,12 @@ class Ldap
$filter = '(&(objectclass=posixaccount)(|(uid='.implode(')(uid=',$relevantAccounts).'))' . $this->account_filter.')'; $filter = '(&(objectclass=posixaccount)(|(uid='.implode(')(uid=',$relevantAccounts).'))' . $this->account_filter.')';
$filter = str_replace(array('%user','%domain'),array('*',$GLOBALS['egw_info']['user']['domain']),$filter); $filter = str_replace(array('%user','%domain'),array('*',$GLOBALS['egw_info']['user']['domain']),$filter);
} }
/** @noinspection SuspiciousAssignmentsInspection */
$sri = ldap_search($this->ds, $this->user_context, $filter,array('uid','uidNumber','givenname','sn',static::MAIL_ATTR,'shadowExpire','createtimestamp','modifytimestamp','objectclass','gidNumber')); $sri = ldap_search($this->ds, $this->user_context, $filter,array('uid','uidNumber','givenname','sn',static::MAIL_ATTR,'shadowExpire','createtimestamp','modifytimestamp','objectclass','gidNumber'));
$utc_diff = date('Z'); $utc_diff = date('Z');
foreach(ldap_get_entries($this->ds, $sri) as $allVals) foreach(ldap_get_entries($this->ds, $sri) ?: [] as $allVals)
{ {
settype($allVals,'array');
$test = @$allVals['uid'][0]; $test = @$allVals['uid'][0];
if (!$this->frontend->config['global_denied_users'][$test] && $allVals['uid'][0]) if (!$this->frontend->config['global_denied_users'][$test] && $allVals['uid'][0])
{ {
@ -816,9 +843,9 @@ class Ldap
} }
if ($param['type'] == 'groups' || $param['type'] == 'both') if ($param['type'] == 'groups' || $param['type'] == 'both')
{ {
if(empty($query) || $query == '*') if(empty($query) || $query === '*')
{ {
$filter = '(objectclass=posixgroup)'; $filter = "(&(objectclass=posixgroup)$this->group_filter)";
} }
else else
{ {
@ -834,12 +861,11 @@ class Ldap
case 'exact': case 'exact':
break; break;
} }
$filter = "(&(objectclass=posixgroup)(cn=$query))"; $filter = "(&(objectclass=posixgroup)(cn=$query)$this->group_filter)";
} }
$sri = ldap_search($this->ds, $this->group_context, $filter,array('cn','gidNumber')); $sri = ldap_search($this->ds, $this->group_context, $filter,array('cn','gidNumber'));
foreach((array)ldap_get_entries($this->ds, $sri) as $allVals) foreach(ldap_get_entries($this->ds, $sri) ?: [] as $allVals)
{ {
settype($allVals,'array');
$test = $allVals['cn'][0]; $test = $allVals['cn'][0];
if (!$this->frontend->config['global_denied_groups'][$test] && $allVals['cn'][0]) if (!$this->frontend->config['global_denied_groups'][$test] && $allVals['cn'][0])
{ {
@ -960,10 +986,10 @@ class Ldap
if (in_array($which, array('account_lid','account_email')) && $account_type !== 'u') // groups only support account_(lid|email) if (in_array($which, array('account_lid','account_email')) && $account_type !== 'u') // groups only support account_(lid|email)
{ {
$attr = $which == 'account_lid' ? 'cn' : static::MAIL_ATTR; $attr = $which == 'account_lid' ? 'cn' : static::MAIL_ATTR;
$sri = ldap_search($this->ds, $this->group_context, '(&('.$attr.'=' . $name . ')(objectclass=posixgroup))', array('gidNumber'));
$allValues = ldap_get_entries($this->ds, $sri);
if (@$allValues[0]['gidnumber'][0]) if (($sri = ldap_search($this->ds, $this->group_context, '(&('.$attr.'=' . $name . ")(objectclass=posixgroup)$this->group_filter)", array('gidNumber'))) &&
($allValues = ldap_get_entries($this->ds, $sri)) &&
!empty($allValues[0]['gidnumber'][0]))
{ {
return -$allValues[0]['gidnumber'][0]; return -$allValues[0]['gidnumber'][0];
} }
@ -978,11 +1004,9 @@ class Ldap
return False; return False;
} }
$sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber')); if (($sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber'))) &&
($allValues = ldap_get_entries($this->ds, $sri)) &&
$allValues = ldap_get_entries($this->ds, $sri); !empty($allValues[0]['uidnumber'][0]))
if (@$allValues[0]['uidnumber'][0])
{ {
return (int)$allValues[0]['uidnumber'][0]; return (int)$allValues[0]['uidnumber'][0];
} }
@ -1042,7 +1066,7 @@ class Ldap
* *
* @param int $_gid * @param int $_gid
* @return array|boolean array with uidnumber => uid pairs, * @return array|boolean array with uidnumber => uid pairs,
* false if $_git is not nummeric and can't be resolved to a nummeric gid * false if $_gid is not numeric and can't be resolved to a numeric gid
*/ */
function members($_gid) function members($_gid)
{ {
@ -1121,7 +1145,7 @@ class Ldap
if (!isset($objectclass)) if (!isset($objectclass))
{ {
$objectclass = $this->id2name($gid, 'objectclass'); $objectclass = $this->id2name($gid, 'objectclass');
// if we cant find objectclass, we might ge in the middle of a migration // if we can't find objectclass, we might ge in the middle of a migration
if (!isset($objectclass)) if (!isset($objectclass))
{ {
Api\Accounts::cache_invalidate($gid); Api\Accounts::cache_invalidate($gid);
@ -1141,7 +1165,7 @@ class Ldap
$member_dn = $this->id2name($member, 'account_dn'); $member_dn = $this->id2name($member, 'account_dn');
if (is_numeric($member)) $member = $this->id2name($member); if (is_numeric($member)) $member = $this->id2name($member);
// only add a member, if we have the neccessary info / he already exists in migration // only add a member, if we have the necessary info / he already exists in migration
if ($member && ($member_dn || !array_intersect(array('groupofnames','groupofuniquenames','univentiongroup'), $objectclass))) if ($member && ($member_dn || !array_intersect(array('groupofnames','groupofuniquenames','univentiongroup'), $objectclass)))
{ {
$to_write['memberuid'][] = $member; $to_write['memberuid'][] = $member;
@ -1281,7 +1305,7 @@ class Ldap
return -1; return -1;
} }
$id = (int)$GLOBALS['egw_info']['server'][$key='last_id_'.$location]; $id = (int)$GLOBALS['egw_info']['server']['last_id_'.$location];
if (!$id || $id < $min) if (!$id || $id < $min)
{ {
@ -1306,7 +1330,6 @@ class Ldap
{ {
$vars = get_object_vars($this); $vars = get_object_vars($this);
unset($vars['ds']); unset($vars['ds']);
unset($this->ds);
return array_keys($vars); return array_keys($vars);
} }
@ -1315,7 +1338,6 @@ class Ldap
*/ */
function __wakeup() function __wakeup()
{ {
$this->ds = Api\Ldap::factory(true, $this->frontend->config['ldap_host'], $this->ds = $this->ldap_connection();
$this->frontend->config['ldap_root_dn'],$this->frontend->config['ldap_root_pw']);
} }
} }

View File

@ -26,7 +26,6 @@ use EGroupware\Api\Ldap\ServerInfo;
*/ */
class Ldap class Ldap
{ {
const ALL = 0; const ALL = 0;
const ACCOUNTS = 1; const ACCOUNTS = 1;
const PERSONAL = 2; const PERSONAL = 2;
@ -372,7 +371,6 @@ class Ldap
{ {
$vars = get_object_vars($this); $vars = get_object_vars($this);
unset($vars['ds']); unset($vars['ds']);
unset($this->ds);
return array_keys($vars); return array_keys($vars);
} }
@ -472,7 +470,7 @@ class Ldap
* reads contact data * reads contact data
* *
* @param string|array $contact_id contact_id or array with values for id or account_id * @param string|array $contact_id contact_id or array with values for id or account_id
* @return array/boolean data if row could be retrived else False * @return array|false data if row could be retrived else False
*/ */
function read($contact_id) function read($contact_id)
{ {
@ -512,6 +510,7 @@ class Ldap
* *
* @param array $keys if given $keys are copied to data before saveing => allows a save as * @param array $keys if given $keys are copied to data before saveing => allows a save as
* @return int 0 on success and errno != 0 else * @return int 0 on success and errno != 0 else
* @noinspection UnsupportedStringOffsetOperationsInspection
*/ */
function save($keys=null) function save($keys=null)
{ {
@ -785,7 +784,7 @@ class Ldap
* @param string $join ='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or * @param string $join ='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or
* "LEFT JOIN table2 ON (x=y)", Note: there's no quoting done on $join! * "LEFT JOIN table2 ON (x=y)", Note: there's no quoting done on $join!
* @param boolean $need_full_no_count =false If true an unlimited query is run to determine the total number of rows, default false * @param boolean $need_full_no_count =false If true an unlimited query is run to determine the total number of rows, default false
* @return array of matching rows (the row is an array of the cols) or False * @return array|false of matching rows (the row is an array of the cols) or False
*/ */
function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='',$need_full_no_count=false) function &search($criteria,$only_keys=True,$order_by='',$extra_cols='',$wildcard='',$empty=False,$op='AND',$start=false,$filter=null,$join='',$need_full_no_count=false)
{ {
@ -1024,9 +1023,9 @@ class Ldap
elseif ($value) elseif ($value)
{ {
if (is_array($value)) $filters .= '(|'; if (is_array($value)) $filters .= '(|';
foreach((array)$value as $value) foreach((array)$value as $val)
{ {
$filters .= '(uidNumber='.(int)$value.')'; $filters .= '(uidNumber='.(int)$val.')';
} }
if (is_array($value)) $filters .= ')'; if (is_array($value)) $filters .= ')';
} }
@ -1192,7 +1191,7 @@ class Ldap
* @param int $_addressbooktype * @param int $_addressbooktype
* @param array $_skipPlugins =null schema-plugins to skip * @param array $_skipPlugins =null schema-plugins to skip
* @param string $order_by sql order string eg. "contact_email ASC" * @param string $order_by sql order string eg. "contact_email ASC"
* @param null|int|array $start [$start,$offset], on return null, if result sorted and limited by server * @param null|int|array $start [$start, $num_rows], on return null, if result sorted and limited by server
* @return array/boolean with eGW contacts or false on error * @return array/boolean with eGW contacts or false on error
*/ */
function _searchLDAP($_ldapContext, $_filter, $_attributes, $_addressbooktype, array $_skipPlugins=null, $order_by=null, &$start=null) function _searchLDAP($_ldapContext, $_filter, $_attributes, $_addressbooktype, array $_skipPlugins=null, $order_by=null, &$start=null)

View File

@ -20,7 +20,7 @@ namespace EGroupware\Api;
* Please note for SSL or TLS connections hostname has to be: * Please note for SSL or TLS connections hostname has to be:
* - SSL: "ldaps://host[:port]/" * - SSL: "ldaps://host[:port]/"
* - TLS: "tls://host[:port]/" * - TLS: "tls://host[:port]/"
* Both require certificats installed on the webserver, otherwise the connection will fail! * Both require certificates installed on the webserver, otherwise the connection will fail!
* *
* If multiple (space-separated) ldap hosts or urls are given, try them in order and * If multiple (space-separated) ldap hosts or urls are given, try them in order and
* move first successful one to first place in session, to try not working ones * move first successful one to first place in session, to try not working ones
@ -79,15 +79,16 @@ class Ldap
* @param string $host ='' ldap host, default $GLOBALS['egw_info']['server']['ldap_host'] * @param string $host ='' ldap host, default $GLOBALS['egw_info']['server']['ldap_host']
* @param string $dn ='' ldap dn, default $GLOBALS['egw_info']['server']['ldap_root_dn'] * @param string $dn ='' ldap dn, default $GLOBALS['egw_info']['server']['ldap_root_dn']
* @param string $passwd ='' ldap pw, default $GLOBALS['egw_info']['server']['ldap_root_pw'] * @param string $passwd ='' ldap pw, default $GLOBALS['egw_info']['server']['ldap_root_pw']
* @param bool $reconnect default false, true: reconnect, even if we have an existing connection
* @return object|resource|self|false resource/object from ldap_connect(), self or false on error * @return object|resource|self|false resource/object from ldap_connect(), self or false on error
* @throws Exception\AssertionFailed 'LDAP support unavailable!' (no ldap extension) * @throws Exception\AssertionFailed 'LDAP support unavailable!' (no ldap extension)
* @throws Exception\NoPermission if bind fails * @throws Exception\NoPermission if bind fails
*/ */
public static function factory($ressource=true, $host='', $dn='', $passwd='') public static function factory($ressource=true, $host='', $dn='', $passwd='', bool $reconnect=false)
{ {
$key = md5($host.':'.$dn.':'.$passwd); $key = md5($host.':'.$dn.':'.$passwd);
if (!isset(self::$connections[$key])) if (!isset(self::$connections[$key]) || $reconnect)
{ {
self::$connections[$key] = new Ldap(true); self::$connections[$key] = new Ldap(true);
@ -302,4 +303,27 @@ class Ldap
Cache::setSession(__CLASS__, 'ldapServerInfo', $this->ldapserverinfo); Cache::setSession(__CLASS__, 'ldapServerInfo', $this->ldapserverinfo);
} }
} }
/**
* Magic method called when object gets serialized
*
* We do NOT store ldapConnection, as we need to reconnect anyway.
* PHP 8.1 gives an error when trying to serialize LDAP\Connection object!
*
* @return array
*/
function __sleep()
{
$vars = get_object_vars($this);
unset($vars['ds']);
return array_keys($vars);
}
/**
* __wakeup function gets called by php while unserializing the object to reconnect with the ldap server
*/
function __wakeup()
{
}
} }

View File

@ -308,16 +308,21 @@
</tr> </tr>
<tr class="row_on"> <tr class="row_on">
<td>{lang_Additional_group_filter_(optional)}:</td>
<td><input name="newsettings[ldap_group_filter]" value="{value_ldap_group_filter}" size="40" /></td>
</tr>
<tr class="row_off">
<td>{lang_LDAP_rootdn} {lang_(searching_accounts_and_changing_passwords)}:</td> <td>{lang_LDAP_rootdn} {lang_(searching_accounts_and_changing_passwords)}:</td>
<td><input name="newsettings[ldap_root_dn]" value="{value_ldap_root_dn}" size="40" /></td> <td><input name="newsettings[ldap_root_dn]" value="{value_ldap_root_dn}" size="40" /></td>
</tr> </tr>
<tr class="row_off"> <tr class="row_on">
<td>{lang_LDAP_root_password}:</td> <td>{lang_LDAP_root_password}:</td>
<td><input name="newsettings[ldap_root_pw]" type="password" value="{value_ldap_root_pw}" /></td> <td><input name="newsettings[ldap_root_pw]" type="password" value="{value_ldap_root_pw}" /></td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>{lang_LDAP_encryption_type}:</td> <td>{lang_LDAP_encryption_type}:</td>
<td> <td>
<select name="newsettings[ldap_encryption_type]"> <select name="newsettings[ldap_encryption_type]">
@ -326,7 +331,7 @@
</td> </td>
</tr> </tr>
<tr class="row_off"> <tr class="row_on">
<td>{lang_Do_you_want_to_manage_homedirectory_and_loginshell_attributes?}:</td> <td>{lang_Do_you_want_to_manage_homedirectory_and_loginshell_attributes?}:</td>
<td> <td>
<select name="newsettings[ldap_extra_attributes]"> <select name="newsettings[ldap_extra_attributes]">
@ -336,17 +341,17 @@
</td> </td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>{lang_LDAP_Default_homedirectory_prefix_(e.g._/home_for_/home/username)}:</td> <td>{lang_LDAP_Default_homedirectory_prefix_(e.g._/home_for_/home/username)}:</td>
<td><input name="newsettings[ldap_account_home]" value="{value_ldap_account_home}" /></td> <td><input name="newsettings[ldap_account_home]" value="{value_ldap_account_home}" /></td>
</tr> </tr>
<tr class="row_off"> <tr class="row_on">
<td>{lang_LDAP_Default_shell_(e.g._/bin/bash)}:</td> <td>{lang_LDAP_Default_shell_(e.g._/bin/bash)}:</td>
<td><input name="newsettings[ldap_account_shell]" value="{value_ldap_account_shell}" /></td> <td><input name="newsettings[ldap_account_shell]" value="{value_ldap_account_shell}" /></td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>{lang_Allow_usernames_identical_to_system_users?}:</td> <td>{lang_Allow_usernames_identical_to_system_users?}:</td>
<td> <td>
<select name="newsettings[ldap_allow_systemusernames]"> <select name="newsettings[ldap_allow_systemusernames]">
@ -356,7 +361,7 @@
</td> </td>
</tr> </tr>
<tr class="row_off" valign="top"> <tr class="row_on" valign="top">
<td colspan="2"> <td colspan="2">
<a href="account_migration.php"><b>{lang_Migration_between_eGroupWare_account_repositories}:</b></a> <a href="account_migration.php"><b>{lang_Migration_between_eGroupWare_account_repositories}:</b></a>
</td> </td>
@ -710,5 +715,3 @@
</table> </table>
</form> </form>
<!-- END footer --> <!-- END footer -->