Changeset 1137

Show
Ignore:
Timestamp:
06/08/07 15:30:02 (1 year ago)
Author:
misja
Message:

LDAP authentication enhancements contributed by Victor Rajewski

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • devel/auth/ldap/lib.php

    r975 r1137  
    11<?php 
    22 
    3     // LDAP authentication module 
    4      
    5     /* 
    6      * Basic behaviour: 
    7      * 
    8      * Only if a user exists in LDAP authentication will be processed in this 
    9      * module. In all other cases it will fall back to the internal method. 
    10      * 
    11      * To enable, set $CFG->auth = 'ldap' in config.php 
    12      * 
    13      * Configuration parameters in config.php: 
    14      * 
    15      * // LDAP host 
    16      * $CFG->ldap_host = 'localhost'; 
    17      * // LDAP port 
    18      * $CFG->ldap_port = 389; 
    19      * // Base DN 
    20      * $CFG->ldap_basedn = 'dc=curverider,dc=co,dc=uk'; 
    21      * // Bind as 
    22      * $CFG->ldap_bind_dn = 'cn=admin,dc=curverider,dc=co,dc=uk'; 
    23      * // Password for non anonymous bind 
    24      * $CFG->ldap_bind_pwd = 'secret'; 
    25      * // Protocol version 
    26      * $CFG->ldap_protocol_version = 3; 
    27      * // Filter for username, common are cn or uid 
    28      * $CFG->ldap_filter_attr = 'uid'; 
    29      * // Search attibutes (can be bassed as array or comma seperated string) 
    30      * $CFG->ldap_search_attr = array('dn', 'ou', 'mail'); 
    31      * // Create user, relies on the givenname, sn, and email attributes for now 
    32      * $CFG->ldap_user_create = true; 
    33      * // Fallback option, try internal authentication if everything fails 
    34      * $CFG->ldap_internal_fallback = true 
    35      */ 
     3        // LDAP authentication module 
     4         
     5        /* 
     6         * Revised LDAP module by Victor Rajewski <askvictor@gmail.com> 
     7         * Based on LDAP module found in standard elgg distribution on 30/6/2007 
     8         * 
     9         * Basic behaviour: 
     10         * 
     11         * Only if a user exists in LDAP authentication will be processed in this 
     12         * module. In all other cases it will fall back to the internal method. 
     13         * 
     14         * To enable, set $CFG->auth = 'ldap' in config.php 
     15         * 
     16         * Configuration parameters in config.php: 
     17         * 
     18         * // LDAP host 
     19         * $CFG->ldap_host = 'localhost'; 
     20         * // LDAP port 
     21         * $CFG->ldap_port = 389; 
     22         * // Base DN - can be string or array of string for multiple DNs 
     23         * $CFG->ldap_basedn = 'dc=curverider,dc=co,dc=uk'; 
     24         * $CFG->ldap_basedn = array('dc=curverider,dc=co,dc=uk', 'dc=bucketrider,dc=co,dc=uk'); 
     25         * // Bind as 
     26         * $CFG->ldap_bind_dn = 'cn=admin,dc=curverider,dc=co,dc=uk'; 
     27         * // Password for non anonymous bind 
     28         * $CFG->ldap_bind_pwd = 'secret'; 
     29         * // Protocol version 
     30         * $CFG->ldap_protocol_version = 3; 
     31         * // Filter for username, common are cn, uid or sAMAccountName 
     32         * $CFG->ldap_filter_attr = 'uid'; 
     33         * // Search attibutes: associative array with the key being the attribute 
     34         *    description, and the value being the actual LDAP attribute. firstname 
     35         *    lastname and mail are used to create the elgg user profile. The 
     36         *    example below works for ActiveDirectory. 
     37         * $CFG->ldap_search_attr = array('firstname' => 'givenname', 
     38         *                                'lastname' => 'sn', 
     39         *                                'mail' => 'mail'); 
     40         * // Create user, relies on the givenname, sn, and email attributes for now 
     41         * $CFG->ldap_user_create = true; 
     42         * // Fallback option, try internal authentication if everything fails 
     43         * $CFG->ldap_internal_fallback = true 
     44         */ 
     45         
     46        /** 
     47         * Sets up the LDAP connection and returns the LDAP link resource, or 
     48         * null on failure 
     49         */ 
     50 
     51        function ldap_init_connection($host, $port, $protocol_ver, $bind_dn='', $bind_pwd='') { 
     52 
     53                global $messages; 
     54 
     55        // Setup the connection 
     56 
     57        $ds = @ldap_connect($host, $port); 
     58 
     59        @ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $version); 
     60 
     61        // Start the LDAP bind process 
     62 
     63        $ldapbind = null; 
     64 
     65        if ($ds) { 
     66            if ($bind_dn != '') { 
     67                $ldapbind = @ldap_bind($ds, $bind_dn, $bind_pwd); 
     68            } else { 
     69                // Anonymous bind 
     70                $ldapbind = @ldap_bind($ds); 
     71            } 
     72        } else { 
     73            // Unable to connect 
     74            $messages[] = 'Unable to connect to the LDAP server, please contact your system administrator. Error: '.ldap_error($ds); 
     75        } 
     76 
     77        if (! $ldapbind) { 
     78            $messages[] = 'Unable to bind to the LDAP server with your credentials, please contact your system administrator. LDAP error: '.ldap_error($ds); 
     79 
     80            ldap_close($ds); 
     81        } 
     82 
     83        return $ds; 
     84        } 
     85 
     86        /** 
     87         * Attempts to find the username and password in the provided DN, and if  
     88         * found tries to bind using the provided password to see if correct 
     89         */ 
     90 
     91        function ldap_do_auth($ds, $basedn, $username, $password, $filter_attr, $search_attr) { 
     92 
     93        $sr = @ldap_search($ds, $basedn, $filter_attr ."=". $username, array_values($search_attr)); 
     94 
     95        if(! $sr) { 
     96                $messages[] = 'Unable to perform LDAP search, please contact your system administrator. LDAP error: '.ldap_error($ds); 
     97 
     98                return false; 
     99                } 
     100 
     101        $entry = ldap_get_entries($ds, $sr); 
     102 
     103        if(! $entry or ! $entry[0]) { 
     104                return false; // didn't find username 
     105                } 
     106 
     107        // Username exists 
     108        // Perform a bind for testing credentials 
     109 
     110        if (@ldap_bind($ds, $entry[0]['dn'], $password) ) { 
     111 
     112            // We have a bind, valid login 
     113            //$messages[] = "Successful LDAP login for ".$entry[0]['dn']; 
     114 
     115            foreach (array_keys($search_attr) as $attr) { 
     116                        $ldap_user_info[$attr] = $entry[0][$search_attr[$attr]][0]; 
     117                        } 
     118 
     119            return $ldap_user_info; 
     120                } 
     121 
     122        // Wrong password 
     123        $messages[] = 'Wrong LDAP password. LDAP error: '.ldap_error($ds); 
     124 
     125        return false; 
     126        } 
     127 
     128        /** 
     129          * checks if the LDAP username is valid by elgg 
     130          * TODO - this should be a library function somewhere. 
     131          * IMPORTANT - Currently (1Jun07) this differs from the normal elgg  
     132          * username check as LDAP can have long usernames and non-alphanum 
     133          * characters. A clear policy needs to be decided on in this matter 
     134          */ 
     135 
     136        function elgg_valid_username($username){ 
     137        return preg_match("/^[A-Za-z0-9.\-]{3,20}$/",$username); 
     138        } 
     139 
     140        /** 
     141          * creates an entry in the elgg database for the given username and  
     142          * password and LDAP entry 
     143          */ 
     144 
     145        function ldap_create_elgg_user($username, $password, $user_info) { 
     146                if(! elgg_valid_username($username)) { 
     147            $messages[] = __gettext("Error! LDAP Username does not meet Elgg requirements"); 
     148        } else { 
     149            // Does the user already exist? 
     150            $username = strtolower($username); 
     151 
     152            if (record_exists('users','username',$username)) { 
     153                $messages[] = sprintf(__gettext("The username %s is already taken by another user. You will need to pick a different one."), $username); 
     154            } else { 
     155                // Everythink OK, create user 
     156                $user = new StdClass; 
     157                $user->email = $user_info["mail"]; 
     158                $user->name  = $user_info["firstname"]; 
     159                $user->name  = $user->name . " " . $user_info["lastname"]; 
     160                $user->username = $username; 
     161                $user->password = md5($password); 
     162                $user->user_type = 'person'; 
     163                $user->owner = -1; 
     164 
     165                $user_id = insert_record('users',$user); 
     166 
     167                if (!empty($user_id)) { 
     168                    $rssresult = run("weblogs:rss:publish", array($uid, false)); 
     169                    $rssresult = run("files:rss:publish", array($uid, false)); 
     170                    $rssresult = run("profile:rss:publish", array($uid, false)); 
     171                } else { 
     172                    // User creation failed 
     173                    $messages[] = sprintf(__gettext("User addition %d failed: Unknown reason, please contact you system administrator."), $username); 
     174                } 
     175            } 
     176        } 
     177        } 
     178 
     179        /**  
     180          * Sets up configuration variables and puts together the above functions 
     181          * to perform an authentication 
     182          */ 
    36183 
    37184    function ldap_authenticate_user_login($username, $password) { 
     
    42189            return false; 
    43190        } 
     191 
     192                /////////// Set up config ////////////// 
    44193 
    45194        // LDAP host 
     
    47196            // No host defined, switch to plain login 
    48197            require_once($CFG->dirroot . 'auth/internal/lib.php'); 
     198 
    49199            return internal_authenticate_user_login($username, $password); 
    50200        } 
     
    52202        // LDAP port 
    53203        if (!$CFG->ldap_port) { 
     204 
    54205            $CFG->ldap_port = 389; 
    55206        } 
     207 
     208        // Base DN setup 
     209        if(!$CFG->ldap_basedn) { 
     210                $CFG->ldap_basedn = array (); 
     211        } else { 
     212            if (!is_array($CFG->ldap_basedn)) { //single DN specified 
     213                                $CFG->ldap_basedn = array($CFG->ldap_basedn); 
     214                        } 
     215                } 
    56216 
    57217        // Which filter to apply for the username, e.g. cn or uid 
     
    62222        // Which search attributes to return 
    63223        if (!$CFG->ldap_search_attr) { 
    64             $CFG->ldap_search_attr = array('dn'); 
    65         } 
    66         else 
    67         { 
    68             if (!is_array($CFG->ldap_search_attr)) 
    69             { 
    70                 // Comma separated string 
    71                 $CFG->ldap_search_attr = explode(',', $CFG->ldap_search_attr); 
    72             } 
    73         } 
    74  
    75         // Setup the connection 
    76         $ds = @ldap_connect($CFG->ldap_host, $CFG->ldap_port); 
     224            $CFG->ldap_search_attr = array('dn' => 'dn'); 
     225        } 
    77226 
    78227        // Set protocol version, default is v3 
    79228        $version = 3; 
    80229 
    81         // LDAP protocol version 
     230        // Set up LDAP protocol version 
     231 
    82232        if ($CFG->ldap_protocol_version) { 
    83233            $version = $CFG->ldap_protocol_version; 
    84234        } 
    85                  
    86         @ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $version); 
    87  
    88         // Start the LDAP bind process 
    89         $ldapbind = null; 
    90      
    91         if ($ds) { 
    92             if ($CFG->ldap_bind_dn != '') { 
    93                 $ldapbind = @ldap_bind($ds, $CFG->ldap_bind_dn, $CFG->ldap_bind_pwd); 
    94             } else { 
    95                 // Anonymous bind 
    96                 $ldapbind = @ldap_bind($ds); 
    97             } 
    98         } else { 
    99             // Unable to connect 
    100             $messages[] = 'Unable to bind to the LDAP server, please contact your system administrator. Error: '.ldap_error($ds); 
    101         } 
    102  
    103         // Initial bind established 
    104         if ($ldapbind) { 
    105             // Perform LDAP search 
    106             $sr = @ldap_search($ds, $CFG->ldap_basedn, $CFG->ldap_filter_attr ."=". $username, $CFG->ldap_search_attr); 
    107  
    108             if ($sr) { 
    109                 $entry = ldap_get_entries($ds, $sr); 
    110  
    111                 // Username exists 
    112                 if ($entry) { 
    113                     if ($entry[0]) { 
    114                         // Perform a bind for testing credentials 
    115                         if (@ldap_bind($ds, $entry[0]['dn'], $password) ) { 
    116                             // We have a bind, valid login 
    117                             //$messages[] = "Succesfull LDAP login for ".$entry[0]['dn']; 
    118  
    119                             // If we need to create the user 
    120                             if ($CFG->ldap_user_create == true) { 
    121                                 // Valid Elgg username? 
    122                                 if (!preg_match("/^[A-Za-z0-9]{3,12}$/",$username)) { 
    123                                     $messages[] = __gettext("Error! Your username must contain letters and numbers only, cannot be blank, and must be between 3 and 12 characters in length."); 
    124                                 } else { 
    125                                     // Does the user already exist? 
    126                                     $username = strtolower($username); 
    127                                     if (record_exists('users','username',$username)) { 
    128                                         $messages[] = sprintf(__gettext("The username %s is already taken by another user. You will need to pick a different one."), $username); 
    129                                     } else { 
    130                                         // Everythink OK, create user 
    131                                         $user = new StdClass; 
    132                                         $user->email = $entry[0]["mail"][0]; 
    133                                         $user->name  = $entry[0]["givenname"][0]; 
    134                                         $user->name  = $user->name . " " . $entry[0]["sn"][0]; 
    135                                         $user->username = $username; 
    136                                         $user->password = md5($password); 
    137                                         $user->user_type = 'person'; 
    138                                         $user->owner = -1; 
    139  
    140                                         $user_id = insert_record('users',$user); 
    141  
    142                                         if (!empty($user_id)) { 
    143                                             $rssresult = run("weblogs:rss:publish", array($uid, false)); 
    144                                             $rssresult = run("files:rss:publish", array($uid, false)); 
    145                                             $rssresult = run("profile:rss:publish", array($uid, false)); 
    146  
    147                                         } else { 
    148                                             // User creation failed 
    149                                             $messages[] = sprintf(__gettext("User addition %d failed: Unknown reason, please contact you system administrator."), $username); 
    150                                         } 
    151                                     } 
    152                                 } 
    153                             } 
    154  
    155                             // Done with LDAP 
    156                             ldap_close($ds); 
    157  
    158                             // Return the user object 
    159                             return get_record_select('users',"username = ? AND active = ? AND user_type = ? ", 
    160                                                      array($username,'yes','person')); 
    161                         } else { 
    162                             // Invalid credentials 
    163                             $messages[] = 'Invalid credentials. LDAP error: '.ldap_error($ds); 
    164  
    165                             // Done with LDAP 
    166                             ldap_close($ds); 
    167  
    168                             return false; 
    169                         } 
    170                     } else { 
    171                             // Done with LDAP 
    172                             ldap_close($ds); 
    173  
    174                         // No such user in LDAP, fallback to internal authentication 
    175                         if ($CFG->ldap_internal_fallback && $CFG->ldap_internal_fallback == true) { 
    176                             require_once($CFG->dirroot . 'auth/internal/lib.php'); 
    177  
    178                             return internal_authenticate_user_login($username, $password); 
    179                         } 
    180                         else 
    181                         { 
    182                             return false; 
    183                         } 
    184                     } 
    185                 } 
    186             } else { 
    187                 $messages[] = 'Unable to setup an LDAP connection, please contact your system administrator. LDAP error: '.ldap_error($ds); 
    188  
    189                 // Done with LDAP 
    190                 ldap_close($ds); 
    191  
    192                 return false; 
    193             } 
    194         } else { 
    195             $messages[] = 'Unable to bind to the LDAP server with your credentials, please contact your system administrator. LDAP error: '.ldap_error($ds); 
    196  
    197             // Done with LDAP 
    198             ldap_close($ds); 
    199  
     235 
     236        ////////// Done setting up config ///////// 
     237 
     238        //connect and bind 
     239        $ds = ldap_init_connection($CFG->ldap_host, $CFG->ldap_port, 
     240                                                                   $CFG->ldap_protocol_version,  
     241                                                                   $CFG->ldap_bind_dn, 
     242                                                                   $CFG->ldap_bind_pwd); 
     243 
     244                if (! $ds) { 
     245                        return false; 
     246                } 
     247 
     248        // Perform LDAP search 
     249        foreach ($CFG->ldap_basedn as $this_ldap_basedn) { 
     250                $ldap_user_info = ldap_do_auth($ds, $this_ldap_basedn, $username, $password, $CFG->ldap_filter_attr, $CFG->ldap_search_attr); 
     251 
     252                if($ldap_user_info) { 
     253                        // LDAP login successful 
     254 
     255                // If we need to create the user 
     256 
     257                if ($CFG->ldap_user_create == true) { 
     258                        ldap_create_elgg_user($username, $password,$ldap_user_info); 
     259                                } 
     260 
     261                ldap_close($ds); 
     262 
     263                // Return the user object 
     264 
     265                    return get_record_select('users', "username = ? AND active = ? AND user_type = ? ", array($username,'yes','person')); 
     266                        } 
     267        } 
     268 
     269        // Done with LDAP 
     270        ldap_close($ds); 
     271 
     272        // No such user in LDAP, fallback to internal authentication 
     273 
     274        if ($CFG->ldap_internal_fallback == true) { 
     275            require_once($CFG->dirroot . 'auth/internal/lib.php'); 
     276 
     277            return internal_authenticate_user_login($username, $password); 
     278        } else { 
    200279            return false; 
    201280        } 
    202281    } 
    203282 
    204     function ldap_create_user($user) 
    205     { 
    206     } 
    207283?> 
     284