lib/Auth

Jeroen van Meeuwen vanmeeuwen at kolabsys.com
Sat Apr 7 19:06:35 CEST 2012


 lib/Auth/LDAP.php |  449 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 274 insertions(+), 175 deletions(-)

New commits:
commit f6b429200c03b878285b35b220e8a5d40289867c
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date:   Sat Apr 7 19:05:48 2012 +0200

    Make effective_rights() work with legacy group membership based authorization mechanisms
    Sort private functions alphabetically (for the most part)

diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index cb8aaf8..f8f0d24 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -139,7 +139,7 @@ class LDAP
         // 'uid=admin'.
         $subject = $this->entry_dn($username);
 
-        console($subject);
+        //console($subject);
 
         if (!$subject) {
             list($this->userid, $this->domain) = $this->_qualify_id($username);
@@ -253,6 +253,8 @@ class LDAP
 
     public function allowed_attributes($objectclasses = Array())
     {
+        //console("Listing allowed_attributes for objectclasses", $objectclasses);
+
         $_schema = $this->init_schema();
 
         if (!is_array($objectclasses)) {
@@ -303,7 +305,20 @@ class LDAP
 
     public function effective_rights($subject)
     {
-        $attributes = array();
+        $effective_rights_control_oid = "1.3.6.1.4.1.42.2.27.9.5.2";
+
+        $supported_controls = $this->supported_controls();
+
+        if (!in_array($effective_rights_control_oid, $supported_controls)) {
+            error_log("No getEffectiveRights control in supportedControls");
+            return $this->legacy_rights($subject);
+        }
+
+        $attributes = array(
+                'attributeLevelRights' => array(),
+                'entryLevelRights' => array(),
+            );
+
         $output = array();
 
         $conf = Conf::get_instance();
@@ -347,9 +362,9 @@ class LDAP
 
         //console("Executing command " . implode(' ', $command));
 
-        exec(implode(' ', $command), $output);
+        exec(implode(' ', $command), $output, $return_code);
 
-        //console("Output", $output);
+        //console("Output", $output, "Return code: " . $return_code);
 
         $lines = array();
         foreach ($output as $line_num => $line) {
@@ -760,42 +775,6 @@ class LDAP
         return $domain_rootdn;
     }
 
-    private function init_schema()
-    {
-        $conf = Conf::get_instance();
-
-        $this->_ldap_uri    = $this->conf->get('ldap_uri');
-        $this->_ldap_server = parse_url($this->_ldap_uri, PHP_URL_HOST);
-        $this->_ldap_port   = parse_url($this->_ldap_uri, PHP_URL_PORT);
-        $this->_ldap_scheme = parse_url($this->_ldap_uri, PHP_URL_SCHEME);
-
-        require_once("Net/LDAP2.php");
-
-        $_ldap_cfg = Array(
-                'host' => $this->_ldap_server,
-                'port' => $this->_ldap_port,
-                'tls' => false,
-                'version' => 3,
-                'binddn' => $conf->get('bind_dn'),
-                'bindpw' => $conf->get('bind_pw')
-            );
-
-        $_ldap_schema_cache_cfg = Array(
-                'path' => "/tmp/Net_LDAP2_Schema.cache",
-                'max_age' => 86400,
-            );
-
-        $_ldap_schema_cache = new Net_LDAP2_SimpleFileSchemaCache($_ldap_schema_cache_cfg);
-
-        $_ldap = Net_LDAP2::connect($_ldap_cfg);
-
-        $result = $_ldap->registerSchemaCache($_ldap_schema_cache);
-
-        $_schema = $_ldap->schema('cn=schema');
-
-        return $_schema;
-    }
-
     public function search($base_dn, $search_filter = '(objectClass=*)', $attributes = array('*'))
     {
         //console("Auth::LDAP::search", $base_dn);
@@ -840,47 +819,54 @@ class LDAP
         return $this->_search($base_dn, $filter);
     }
 
-    private function users_list($attributes = array(), $search = array())
+    private function entry_dn($subject)
     {
-        $conf = Conf::get_instance();
+        //console("entry_dn on subject $subject");
+        $is_dn = ldap_explode_dn($subject, 1);
+        //console($is_dn);
 
-        $base_dn = $conf->get('user_base_dn');
+        if (is_array($is_dn) && array_key_exists("count", $is_dn) && $is_dn["count"] > 0) {
+            return $subject;
+        }
 
-        if (!$base_dn)
-            $base_dn = $conf->get('base_dn');
+        $unique_attr = $this->unique_attribute();
+        $subject     = $this->entry_find_by_attribute(array($unique_attr => $subject));
 
-        $filter  = $conf->get('user_filter');
+        if (!empty($subject)) {
+            return key($subject);
+        }
+    }
 
-        if (empty($attributes) || !is_array($attributes)) {
-            $attributes = array('*');
+    private function entry_find_by_attribute($attribute)
+    {
+        if (empty($attribute) || !is_array($attribute) || count($attribute) > 1) {
+            return false;
         }
 
-        if ($s_filter = $this->_search_filter($search)) {
-            // join search filter with objectClass filter
-            $filter = '(&' . $filter . $s_filter . ')';
+        if (empty($attribute[key($attribute)])) {
+            return false;
         }
 
-        return $this->_search($base_dn, $filter, $attributes);
-    }
+        $filter = "(&";
 
-    private function roles_list($attributes = array(), $search = array())
-    {
-        $conf = Conf::get_instance();
+        foreach ($attribute as $key => $value) {
+            $filter .= "(" . $key . "=" . $value . ")";
+        }
 
-        $base_dn = $conf->get('base_dn');
-        // TODO: From config
-        $filter  = "(&(objectclass=ldapsubentry)(objectclass=nsroledefinition))";
+        $filter .= ")";
 
-        if (empty($attributes) || !is_array($attributes)) {
-            $attributes = array('*');
-        }
+        $base_dn = $this->domain_root_dn($this->domain);
 
-        if ($s_filter = $this->_search_filter($search)) {
-            // join search filter with objectClass filter
-            $filter = '(&' . $filter . $s_filter . ')';
-        }
+        $result = self::normalize_result($this->_search($base_dn, $filter, array_keys($attribute)));
 
-        return $this->_search($base_dn, $filter, $attributes);
+        if (count($result) > 0) {
+            error_log("Results found: " . implode(', ', array_keys($result)));
+            return $result;
+        }
+        else {
+            error_log("No result");
+            return false;
+        }
     }
 
     private function groups_list($attributes = array(), $search = array())
@@ -906,145 +892,111 @@ class LDAP
         return $this->_search($base_dn, $filter, $attributes);
     }
 
-    public static function normalize_result($__result)
+    private function init_schema()
     {
-        if (!is_array($__result)) {
-            return array();
-        }
-
         $conf = Conf::get_instance();
 
-        $dn_attr = $conf->get($conf->get('kolab', 'auth_mechanism'), 'domain_name_attribute');
-        $result  = array();
+        $this->_ldap_uri    = $this->conf->get('ldap_uri');
+        $this->_ldap_server = parse_url($this->_ldap_uri, PHP_URL_HOST);
+        $this->_ldap_port   = parse_url($this->_ldap_uri, PHP_URL_PORT);
+        $this->_ldap_scheme = parse_url($this->_ldap_uri, PHP_URL_SCHEME);
 
-        for ($x = 0; $x < $__result["count"]; $x++) {
-            $dn = $__result[$x]['dn'];
-            $result[$dn] = array();
-            for ($y = 0; $y < $__result[$x]["count"]; $y++) {
-                $attr = $__result[$x][$y];
-                if ($__result[$x][$attr]["count"] == 1) {
-                    switch ($attr) {
-                        case "objectclass":
-                            $result[$dn][$attr] = strtolower($__result[$x][$attr][0]);
-                            break;
-                        default:
-                            $result[$dn][$attr] = $__result[$x][$attr][0];
-                            break;
-                    }
-                }
-                else {
-                    $result[$dn][$attr] = array();
-                    for ($z = 0; $z < $__result[$x][$attr]["count"]; $z++) {
-                        // The first result in the array is the primary domain.
-                        if ($z == 0 && $attr == $dn_attr) {
-                            $result[$dn]['primary_domain'] = $__result[$x][$attr][$z];
-                        }
+        require_once("Net/LDAP2.php");
 
-                        switch ($attr) {
-                            case "objectclass":
-                                $result[$dn][$attr][] = strtolower($__result[$x][$attr][$z]);
-                                break;
-                            default:
-                                $result[$dn][$attr][] = $__result[$x][$attr][$z];
-                                break;
-                        }
-                    }
-                }
-            }
-        }
+        $_ldap_cfg = Array(
+                'host' => $this->_ldap_server,
+                'port' => $this->_ldap_port,
+                'tls' => false,
+                'version' => 3,
+                'binddn' => $conf->get('bind_dn'),
+                'bindpw' => $conf->get('bind_pw')
+            );
 
-        return $result;
-    }
+        $_ldap_schema_cache_cfg = Array(
+                'path' => "/tmp/" . $this->_ldap_server . ":" . ($this->_ldap_port ? $this->_ldap_port : '389') . "-Net_LDAP2_Schema.cache",
+                'max_age' => 86400,
+            );
 
-    private function entry_find_by_attribute($attribute)
-    {
-        if (empty($attribute) || !is_array($attribute) || count($attribute) > 1) {
-            return false;
-        }
+        $_ldap_schema_cache = new Net_LDAP2_SimpleFileSchemaCache($_ldap_schema_cache_cfg);
 
-        if (empty($attribute[key($attribute)])) {
-            return false;
-        }
+        $_ldap = Net_LDAP2::connect($_ldap_cfg);
 
-        $filter = "(&";
+        $result = $_ldap->registerSchemaCache($_ldap_schema_cache);
 
-        foreach ($attribute as $key => $value) {
-            $filter .= "(" . $key . "=" . $value . ")";
+        // TODO: We should learn what LDAP tech. we're running against.
+        // Perhaps with a scope base objectclass recognize rootdse entry
+        $schema_root_dn = $conf->get('schema_root_dn');
+        if (!$schema_root_dn) {
+            $_schema = $_ldap->schema();
         }
 
-        $filter .= ")";
-
-        $base_dn = $this->domain_root_dn($this->domain);
-
-        $result = self::normalize_result($this->_search($base_dn, $filter, array_keys($attribute)));
-
-        if (count($result) > 0) {
-            error_log("Results found: " . implode(', ', array_keys($result)));
-            return $result;
-        }
-        else {
-            error_log("No result");
-            return false;
-        }
+        return $_schema;
     }
 
-    private function entry_dn($subject)
+    private function legacy_rights($subject)
     {
-        console("entry_dn on subject $subject");
-        $is_dn = ldap_explode_dn($subject, 1);
-        console($is_dn);
-
-        if (is_array($is_dn) && array_key_exists("count", $is_dn) && $is_dn["count"] > 0) {
-            return $subject;
-        }
+        //console($subject);
 
-        $unique_attr = $this->unique_attribute();
-        $subject     = $this->entry_find_by_attribute(array($unique_attr => $subject));
-
-        if (!empty($subject)) {
-            return key($subject);
-        }
-    }
+        $subject = $this->user_info($subject);
 
-    private function parse_attribute_level_rights($attribute_value)
-    {
-        $attribute_value = str_replace(", ", ",", $attribute_value);
-        $attribute_values = explode(",", $attribute_value);
+        //console($subject);
 
-        $attribute_value = array();
+        $subject_dn = key($subject);
 
-        foreach ($attribute_values as $access_right) {
-            $access_right_components = explode(":", $access_right);
-            $access_attribute = strtolower(array_shift($access_right_components));
-            $access_value = array_shift($access_right_components);
+        $user_is_admin = false;
+        $user_is_self = false;
 
-            $attribute_value[$access_attribute] = array();
+        // List group memberships
+        $user_groups = $this->find_user_groups($_SESSION['user']->user_bind_dn);
+        //console("User's groups", $user_groups);
 
-            for ($i = 0; $i < strlen($access_value); $i++) {
-                $method = $this->attribute_level_rights_map[substr($access_value, $i, 1)];
+        foreach ($user_groups as $user_group_dn) {
+            if ($user_is_admin)
+                continue;
 
-                if (!in_array($method, $attribute_value[$access_attribute])) {
-                    $attribute_value[$access_attribute][] = $method;
+            $user_group_dn_components = ldap_explode_dn($user_group_dn, 1);
+            unset($user_group_dn_components["count"]);
+            $user_group_cn = array_shift($user_group_dn_components);
+            //console("Resolved user_group_dn $user_group_dn to cn $user_group_cn");
+            if (in_array($user_group_cn, array('admin', 'maintainer', 'domain-maintainer'))) {
+                // All rights default to write.
+                //console("User is an admin");
+                $user_is_admin = true;
+            } else {
+                //console("User is a user");
+                // The user is a regular user, see if the subject is the same has the
+                // user session's bind_dn.
+                if ($subject == $_SESSION['user']->user_bind_dn) {
+                    //console("the subject $subject is the same as the user's bind_dn");
+                    $user_is_self = true;
                 }
             }
         }
 
-        return $attribute_value;
-    }
+        if ($user_is_admin) {
+            $standard_rights = array("add", "delete", "read", "write");
+        } elseif ($user_is_self) {
+            $standard_rights = array("read");
+        } else {
+            $standard_rights = array("read");
+        }
 
-    private function parse_entry_level_rights($attribute_value)
-    {
-        $_attribute_value = array();
+        $rights = array(
+                'entryLevelRights' => $standard_rights,
+                'attributeLevelRights' => array(),
+            );
 
-        for ($i = 0; $i < strlen($attribute_value); $i++) {
-            $method = $this->entry_level_rights_map[substr($attribute_value, $i, 1)];
+        $attributes = $this->allowed_attributes($subject[$subject_dn]['objectclass']);
+        //console($attributes);
 
-            if (!in_array($method, $_attribute_value)) {
-                $_attribute_value[] = $method;
-            }
+        $attributes = array_merge($attributes['may'], $attributes['must']);
+
+        foreach ($attributes as $attribute) {
+            $rights['attributeLevelRights'][$attribute] = $standard_rights;
         }
 
-        return $_attribute_value;
+        return $rights;
+
     }
 
     private function modify_entry($subject_dn, $old_attrs, $new_attrs)
@@ -1233,6 +1185,153 @@ class LDAP
         }
     }
 
+    private function parse_attribute_level_rights($attribute_value)
+    {
+        $attribute_value = str_replace(", ", ",", $attribute_value);
+        $attribute_values = explode(",", $attribute_value);
+
+        $attribute_value = array();
+
+        foreach ($attribute_values as $access_right) {
+            $access_right_components = explode(":", $access_right);
+            $access_attribute = strtolower(array_shift($access_right_components));
+            $access_value = array_shift($access_right_components);
+
+            $attribute_value[$access_attribute] = array();
+
+            for ($i = 0; $i < strlen($access_value); $i++) {
+                $method = $this->attribute_level_rights_map[substr($access_value, $i, 1)];
+
+                if (!in_array($method, $attribute_value[$access_attribute])) {
+                    $attribute_value[$access_attribute][] = $method;
+                }
+            }
+        }
+
+        return $attribute_value;
+    }
+
+    private function parse_entry_level_rights($attribute_value)
+    {
+        $_attribute_value = array();
+
+        for ($i = 0; $i < strlen($attribute_value); $i++) {
+            $method = $this->entry_level_rights_map[substr($attribute_value, $i, 1)];
+
+            if (!in_array($method, $_attribute_value)) {
+                $_attribute_value[] = $method;
+            }
+        }
+
+        return $_attribute_value;
+    }
+
+    private function roles_list($attributes = array(), $search = array())
+    {
+        $conf = Conf::get_instance();
+
+        $base_dn = $conf->get('base_dn');
+        // TODO: From config
+        $filter  = "(&(objectclass=ldapsubentry)(objectclass=nsroledefinition))";
+
+        if (empty($attributes) || !is_array($attributes)) {
+            $attributes = array('*');
+        }
+
+        if ($s_filter = $this->_search_filter($search)) {
+            // join search filter with objectClass filter
+            $filter = '(&' . $filter . $s_filter . ')';
+        }
+
+        return $this->_search($base_dn, $filter, $attributes);
+    }
+
+    private function supported_controls()
+    {
+        $conf = Conf::get_instance();
+
+        $this->_bind($conf->get('bind_dn'), $conf->get('bind_pw'));
+
+        $result = ldap_read($this->conn, "", "(objectclass=*)", array("supportedControl"));
+        $result = ldap_get_entries($this->conn, $result);
+        $result = self::normalize_result($result);
+
+        return $result['']['supportedcontrol'];
+    }
+
+    private function users_list($attributes = array(), $search = array())
+    {
+        $conf = Conf::get_instance();
+
+        $base_dn = $conf->get('user_base_dn');
+
+        if (!$base_dn)
+            $base_dn = $conf->get('base_dn');
+
+        $filter  = $conf->get('user_filter');
+
+        if (empty($attributes) || !is_array($attributes)) {
+            $attributes = array('*');
+        }
+
+        if ($s_filter = $this->_search_filter($search)) {
+            // join search filter with objectClass filter
+            $filter = '(&' . $filter . $s_filter . ')';
+        }
+
+        return $this->_search($base_dn, $filter, $attributes);
+    }
+
+    public static function normalize_result($__result)
+    {
+        if (!is_array($__result)) {
+            return array();
+        }
+
+        $conf = Conf::get_instance();
+
+        $dn_attr = $conf->get($conf->get('kolab', 'auth_mechanism'), 'domain_name_attribute');
+        $result  = array();
+
+        for ($x = 0; $x < $__result["count"]; $x++) {
+            $dn = $__result[$x]['dn'];
+            $result[$dn] = array();
+            for ($y = 0; $y < $__result[$x]["count"]; $y++) {
+                $attr = $__result[$x][$y];
+                if ($__result[$x][$attr]["count"] == 1) {
+                    switch ($attr) {
+                        case "objectclass":
+                            $result[$dn][$attr] = strtolower($__result[$x][$attr][0]);
+                            break;
+                        default:
+                            $result[$dn][$attr] = $__result[$x][$attr][0];
+                            break;
+                    }
+                }
+                else {
+                    $result[$dn][$attr] = array();
+                    for ($z = 0; $z < $__result[$x][$attr]["count"]; $z++) {
+                        // The first result in the array is the primary domain.
+                        if ($z == 0 && $attr == $dn_attr) {
+                            $result[$dn]['primary_domain'] = $__result[$x][$attr][$z];
+                        }
+
+                        switch ($attr) {
+                            case "objectclass":
+                                $result[$dn][$attr][] = strtolower($__result[$x][$attr][$z]);
+                                break;
+                            default:
+                                $result[$dn][$attr][] = $__result[$x][$attr][$z];
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+
+        return $result;
+    }
+
     /**
      * Result sorting callback for uasort()
      */





More information about the commits mailing list