Applied the (modified) patch to extend configuration possibilities of LDAP address books as suggested in #1488753:

- Add option to specify arbitrary replacements of config options with attributes from the bound user
- Allow mapping of group object class => member attribute used in these objects
- Describe the 'member_filter' property for groups config
pull/146/head
Thomas Bruederli 11 years ago
parent 98b7b548a2
commit 3ce7c56826

@ -687,6 +687,8 @@ $config['ldap_public']['Verisign'] = array(
// DN and password to bind as before searching for bind DN, if anonymous search is not allowed
'search_bind_dn' => '',
'search_bind_pw' => '',
// Optional map of replacement strings => attributes used when binding for an individual address book
'search_bind_attrib' => array(), // e.g. array('%udc' => 'ou')
// Default for %dn variable if search doesn't return DN value
'search_dn_default' => '',
// Optional authentication identifier to be used as SASL authorization proxy
@ -771,11 +773,16 @@ $config['ldap_public']['Verisign'] = array(
'base_dn' => '',
'scope' => 'sub', // Search mode: sub|base|list
'filter' => '(objectClass=groupOfNames)',
'object_classes' => array("top", "groupOfNames"),
'member_attr' => 'member', // Name of the member attribute, e.g. uniqueMember
'object_classes' => array('top', 'groupOfNames'), // Object classes to be assigned to new groups
'member_attr' => 'member', // Name of the default member attribute, e.g. uniqueMember
'name_attr' => 'cn', // Attribute to be used as group name
'email_attr' => 'mail', // Group email address attribute (e.g. for mailing lists)
'member_filter' => '(objectclass=*)', // Optional filter to use when querying for group members
'vlv' => false, // Use VLV controls to list groups
'class_member_attr' => array( // Mapping of group object class to member attribute used in these objects
'groupofnames' => 'member',
'groupofuniquenames' => 'uniquemember'
),
),
// this configuration replaces the regular groups listing in the directory tree with
// a hard-coded list of groups, each listing entries with the configured base DN and filter.

@ -52,7 +52,7 @@ class rcube_ldap extends rcube_addressbook
*
* @var array
*/
private static $group_types = array(
private $group_types = array(
'group' => 'member',
'groupofnames' => 'member',
'kolabgroupofnames' => 'member',
@ -94,6 +94,9 @@ class rcube_ldap extends rcube_addressbook
$this->prop['groups']['name_attr'] = 'cn';
if (empty($this->prop['groups']['scope']))
$this->prop['groups']['scope'] = 'sub';
// extend group objectclass => member attribute mapping
if (!empty($this->prop['groups']['class_member_attr']))
$this->group_types = array_merge($this->group_types, $this->prop['groups']['class_member_attr']);
// add group name attrib to the list of attributes to be fetched
$fetch_attributes[] = $this->prop['groups']['name_attr'];
@ -292,6 +295,14 @@ class rcube_ldap extends rcube_addressbook
if ($this->prop['search_base_dn'] && $this->prop['search_filter']
&& (strstr($bind_dn, '%dn') || strstr($this->base_dn, '%dn') || strstr($this->groups_base_dn, '%dn'))
) {
$search_attribs = array('uid');
if ($search_bind_attrib = (array)$this->prop['search_bind_attrib']) {
foreach ($search_bind_attrib as $r => $attr) {
$search_attribs[] = $attr;
$replaces[$r] = '';
}
}
$search_bind_dn = strtr($this->prop['search_bind_dn'], $replaces);
$search_base_dn = strtr($this->prop['search_base_dn'], $replaces);
$search_filter = strtr($this->prop['search_filter'], $replaces);
@ -321,10 +332,18 @@ class rcube_ldap extends rcube_addressbook
}
}
$res = $ldap->search($search_base_dn, $search_filter, 'sub', array('uid'));
$res = $ldap->search($search_base_dn, $search_filter, 'sub', $search_attribs);
if ($res) {
$res->rewind();
$replaces['%dn'] = $res->get_dn();
// add more replacements from 'search_bind_attrib' config
if ($search_bind_attrib) {
$res = $res->current();
foreach ($search_bind_attrib as $r => $attr) {
$replaces[$r] = $res[$attr][0];
}
}
}
if ($ldap != $this->ldap) {
@ -355,6 +374,23 @@ class rcube_ldap extends rcube_addressbook
$this->base_dn = strtr($this->base_dn, $replaces);
$this->groups_base_dn = strtr($this->groups_base_dn, $replaces);
// replace placeholders in filter settings
if (!empty($this->prop['filter']))
$this->prop['filter'] = strtr($this->prop['filter'], $replaces);
if (!empty($this->prop['groups']['filter']))
$this->prop['groups']['filter'] = strtr($this->prop['groups']['filter'], $replaces);
if (!empty($this->prop['groups']['member_filter']))
$this->prop['groups']['member_filter'] = strtr($this->prop['groups']['member_filter'], $replaces);
if (!empty($this->prop['group_filters'])) {
foreach ($this->prop['group_filters'] as $i => $gf) {
if (!empty($gf['base_dn']))
$this->prop['group_filters'][$i]['base_dn'] = strtr($gf['base_dn'], $replaces);
if (!empty($gf['filter']))
$this->prop['group_filters'][$i]['filter'] = strtr($gf['filter'], $replaces);
}
}
if (empty($bind_user)) {
$bind_user = $u;
}
@ -560,6 +596,7 @@ class rcube_ldap extends rcube_addressbook
* Get all members of the given group
*
* @param string Group DN
* @param boolean Count only
* @param array Group entries (if called recursively)
* @return array Accumulated group members
*/
@ -569,7 +606,7 @@ class rcube_ldap extends rcube_addressbook
// fetch group object
if (empty($entries)) {
$attribs = array('dn','objectClass','member','uniqueMember','memberURL');
$attribs = array_merge(array('dn','objectClass','memberURL'), array_values($this->group_types));
$entries = $this->ldap->read_entries($dn, '(objectClass=*)', $attribs);
if ($entries === false) {
return $group_members;
@ -581,17 +618,17 @@ class rcube_ldap extends rcube_addressbook
$attrs = array();
foreach ((array)$entry['objectclass'] as $objectclass) {
if (strtolower($objectclass) == 'groupofurls') {
$members = $this->_list_group_memberurl($dn, $entry, $count);
$group_members = array_merge($group_members, $members);
}
else if (($member_attr = $this->get_group_member_attr(array($objectclass), ''))
if (($member_attr = $this->get_group_member_attr(array($objectclass), ''))
&& ($member_attr = strtolower($member_attr)) && !in_array($member_attr, $attrs)
) {
$members = $this->_list_group_members($dn, $entry, $member_attr, $count);
$group_members = array_merge($group_members, $members);
$attrs[] = $member_attr;
}
else if (!empty($entry['memberurl'])) {
$members = $this->_list_group_memberurl($dn, $entry, $count);
$group_members = array_merge($group_members, $members);
}
if ($this->prop['sizelimit'] && count($group_members) > $this->prop['sizelimit']) {
break 2;
@ -608,6 +645,7 @@ class rcube_ldap extends rcube_addressbook
* @param string Group DN
* @param array Group entry
* @param string Member attribute to use
* @param boolean Count only
* @return array Accumulated group members
*/
private function _list_group_members($dn, $entry, $attr, $count)
@ -621,8 +659,7 @@ class rcube_ldap extends rcube_addressbook
// read these attributes for all members
$attrib = $count ? array('dn','objectClass') : $this->prop['list_attributes'];
$attrib[] = 'member';
$attrib[] = 'uniqueMember';
$attrib = array_merge($attrib, array_values($this->group_types));
$attrib[] = 'memberURL';
$filter = $this->prop['groups']['member_filter'] ? $this->prop['groups']['member_filter'] : '(objectclass=*)';
@ -1483,7 +1520,7 @@ class rcube_ldap extends rcube_addressbook
{
$classes = array_map('strtolower', (array)$entry['objectclass']);
return count(array_intersect(array_keys(self::$group_types), $classes)) > 0;
return count(array_intersect(array_keys($this->group_types), $classes)) > 0;
}
/**
@ -1914,7 +1951,7 @@ class rcube_ldap extends rcube_addressbook
if (!empty($object_classes)) {
foreach ((array)$object_classes as $oc) {
if ($attr = self::$group_types[strtolower($oc)]) {
if ($attr = $this->group_types[strtolower($oc)]) {
return $attr;
}
}

@ -1260,6 +1260,7 @@ function rcmail_settings_tabs($attrib)
array('command' => 'preferences', 'type' => 'link', 'label' => 'preferences', 'title' => 'editpreferences'),
array('command' => 'folders', 'type' => 'link', 'label' => 'folders', 'title' => 'managefolders'),
array('command' => 'identities', 'type' => 'link', 'label' => 'identities', 'title' => 'manageidentities'),
array('command' => 'responses', 'type' => 'link', 'label' => 'responses', 'title' => 'editresponses'),
);
// get all identites from DB and define list of cols to be displayed

Loading…
Cancel
Save