Thomas
2013-10-17 739a1d93a9bf7ca612ed0464cca146765781c9b7
Integrate patch with more configuarion possibilities to LDAP (#1488753)
2 files modified
118 ■■■■■ changed files
config/main.inc.php.dist 23 ●●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_ldap.php 95 ●●●●● patch | view | raw | blame | history
config/main.inc.php.dist
@@ -601,6 +601,8 @@
  // 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
@@ -682,14 +684,19 @@
  // if the groups base_dn is empty, the contact base_dn is used for the groups as well
  // -> in this case, assure that groups and contacts are separated due to the concernig filters! 
  'groups'  => 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
    'name_attr'      => 'cn',       // Attribute to be used as group name
    'member_filter'  => '(objectclass=*)',  // Optional filter to use when querying for group members
    'vlv'            => false,      // Use VLV controls to list groups
    'base_dn'           => '',
    'scope'             => 'sub',       // Search mode: sub|base|list
    'filter'            => '(objectClass=groupOfNames)',
    '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.
program/lib/Roundcube/rcube_ldap.php
@@ -81,6 +81,16 @@
                $this->prop['groups']['name_attr'] = 'cn';
            if (empty($this->prop['groups']['scope']))
                $this->prop['groups']['scope'] = 'sub';
            // set default group objectclass => member attribute mapping
            if (empty($this->prop['groups']['class_member_attr'])) {
                $this->prop['groups']['class_member_attr'] = array(
                    'group'                   => 'member',
                    'groupofnames'            => 'member',
                    'kolabgroupofnames'       => 'member',
                    'groupofuniquenames'      => 'uniquemember',
                    'kolabgroupofuniquenames' => 'uniquemember',
                );
            }
            // add group name attrib to the list of attributes to be fetched
            $fetch_attributes[] = $this->prop['groups']['name_attr'];
@@ -279,6 +289,14 @@
                            continue;  // bind failed, try neyt host
                    }
                    $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 for the dn to use to authenticate
                    $this->prop['search_base_dn'] = strtr($this->prop['search_base_dn'], $replaces);
                    $this->prop['search_filter'] = strtr($this->prop['search_filter'], $replaces);
@@ -286,7 +304,7 @@
                    $this->_debug("S: searching with base {$this->prop['search_base_dn']} for {$this->prop['search_filter']}");
                    // TODO: use $this->ldap->search() here
                    $res = @ldap_search($this->ldap->conn, $this->prop['search_base_dn'], $this->prop['search_filter'], array('uid'));
                    $res = @ldap_search($this->ldap->conn, $this->prop['search_base_dn'], $this->prop['search_filter'], $search_attribs);
                    if ($res) {
                        if (($entry = ldap_first_entry($this->ldap->conn, $res))
                            && ($bind_dn = ldap_get_dn($this->ldap->conn, $entry))
@@ -294,6 +312,15 @@
                            $this->_debug("S: search returned dn: $bind_dn");
                            $dn = ldap_explode_dn($bind_dn, 1);
                            $replaces['%dn'] = $dn[0];
                            // add more replacements from 'search_bind_attrib' config
                            if ($search_bind_attrib) {
                                $res = ldap_get_attributes($this->ldap->conn, $entry);
                                foreach ($search_bind_attrib as $r => $attr) {
                                    $sa_value = $res[$attr][0];
                                    $replaces[$r] = $sa_value;
                                }
                            }
                        }
                    }
                    else {
@@ -318,6 +345,23 @@
                $bind_dn              = strtr($bind_dn, $replaces);
                $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;
@@ -522,9 +566,10 @@
    /**
     * Get all members of the given group
     *
     * @param string Group DN
     * @param array  Group entries (if called recursively)
     * @return array Accumulated group members
     * @param string  Group DN
     * @param boolean Count only
     * @param array   Group entries (if called recursively)
     * @return array  Accumulated group members
     */
    function list_group_members($dn, $count = false, $entries = null)
    {
@@ -533,11 +578,13 @@
        // fetch group object
        if (empty($entries)) {
            $this->_debug("C: Read Group [dn: $dn]");
            $entries = $this->ldap->read_entries($dn, '(objectClass=*)', array('dn','objectClass','member','uniqueMember','memberURL'));
            $entries = $this->ldap->read_entries($dn, '(objectClass=*)', array_merge(array('dn','objectClass','memberURL'), array_values($this->prop['groups']['class_member_attr'])));
            if ($entries === false) {
                return $group_members;
            }
        }
        $class_member_map = $this->prop['groups']['class_member_attr'];
        for ($i=0; $i < $entries['count']; $i++) {
            $entry = $entries[$i];
@@ -546,19 +593,11 @@
                continue;
            foreach ((array)$entry['objectclass'] as $objectclass) {
                switch (strtolower($objectclass)) {
                    case "group":
                    case "groupofnames":
                    case "kolabgroupofnames":
                        $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, 'member', $count));
                        break;
                    case "groupofuniquenames":
                    case "kolabgroupofuniquenames":
                        $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, 'uniquemember', $count));
                        break;
                    case "groupofurls":
                        $group_members = array_merge($group_members, $this->_list_group_memberurl($dn, $entry, $count));
                        break;
                if ($member_attr = $class_member_map[strtolower($objectclass)]) {
                    $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, $member_attr, $count));
                }
                else if (!empty($entry['memberurl'])) {
                    $group_members = array_merge($group_members, $this->_list_group_memberurl($dn, $entry, $count));
                }
            }
@@ -575,6 +614,7 @@
     * @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)
@@ -587,8 +627,7 @@
        // 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->prop['groups']['class_member_attr']));
        $attrib[] = 'memberURL';
        $filter = $this->prop['groups']['member_filter'] ? $this->prop['groups']['member_filter'] : '(objectclass=*)';
@@ -1869,18 +1908,12 @@
            $object_classes = $this->prop['groups']['object_classes'];
        }
        if (!empty($object_classes)) {
            $class_member_map = $this->prop['groups']['class_member_attr'];
            foreach ((array)$object_classes as $oc) {
                switch (strtolower($oc)) {
                    case 'group':
                    case 'groupofnames':
                    case 'kolabgroupofnames':
                        $member_attr = 'member';
                        break;
                    case 'groupofuniquenames':
                    case 'kolabgroupofuniquenames':
                        $member_attr = 'uniqueMember';
                        break;
                $oc = strtolower($oc);
                if (!empty($class_member_map[$oc])) {
                    $member_attr = $class_member_map[$oc];
                    break;
                }
            }
        }