source: ldap-authentication/trunk/ldap-authentication.php

Last change on this file was 447, checked in by SamBauers, 7 years ago

ldap-authentication: Added workaround for bbPress bug 670, tagged version 2.0.2

File size: 17.4 KB
Line 
1<?php
2/*
3Plugin Name: LDAP authentication
4Plugin URI: http://bbpress.org/plugins/topic/26
5Description: Allows users to authenticate against an LDAP service
6Author: Sam Bauers
7Version: 2.0.2
8Author URI:
9
10Version History:
111.0     : Initial Release
121.0.1   : Small non-critical fixes to ldap_remove_password_capability()
131.0.2   : Cookie hacking vulnerability fixed
14                  Disabled password reseting function for LDAP users
15                  Added option to disable automatic registration of LDAP users
161.0.3   : Added option to retrieve LDAP users email address on registration
171.0.4   : Added support for new admin menu structure introduced in build 740
182.0             : Moved most functions into a class
19                  Amalgamated options into new serialized options
20                  Fixed issues with enabling disabling features when using permalinks
21                  Added support for bb_admin_add_submenu()
222.0.1   : Made PHP4 compatible
232.0.2   : Workaround for bbPress bug 670 - http://trac.bbpress.org/ticket/670
24*/
25
26
27/**
28 * LDAP authentication for bbPress version 2.0.2
29 *
30 * ----------------------------------------------------------------------------------
31 *
32 * Copyright (C) 2007 Sam Bauers (sam@viveka.net.au)
33 *
34 * ----------------------------------------------------------------------------------
35 *
36 * LICENSE:
37 *
38 * This program is free software; you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation; either version 2 of the License, or
41 * (at your option) any later version.
42 *
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
46 * GNU General Public License for more details.
47 *
48 * You should have received a copy of the GNU General Public License
49 * along with this program; if not, write to the Free Software
50 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
51 *
52 * ----------------------------------------------------------------------------------
53 *
54 * PHP version 4 and 5
55 *
56 * ----------------------------------------------------------------------------------
57 *
58 * @author    Sam Bauers <sam@viveka.net.au>
59 * @copyright 2007 Sam Bauers
60 * @license   http://www.gnu.org/licenses/gpl.txt GNU General Public License v2
61 * @version   2.0.2
62 **/
63
64
65/**
66 * Container class for LDAP authentication
67 *
68 * @author  Sam Bauers
69 * @version 1.0.1
70 **/
71class LDAP_Authentication
72{
73        /**
74         * The current version of the plugin
75         *
76         * @var string
77         **/
78        var $version = '2.0.1';
79       
80       
81        /**
82         * Whether the plugin is enabled
83         *
84         * @var boolean
85         **/
86        var $enabled;
87       
88       
89        /**
90         * An array of settings for the LDAP server connection
91         *
92         * @var array
93         **/
94        var $server;
95       
96       
97        /**
98         * Whether the plugin has enough settings to work
99         *
100         * @var boolean
101         **/
102        var $active = false;
103       
104       
105        /**
106         * Additional plugin options in an array
107         *
108         * @var array
109         **/
110        var $options;
111       
112       
113        /**
114         * Pulls out database settings
115         *
116         * @return void
117         * @author Sam Bauers
118         **/
119        function LDAP_Authentication()
120        {
121                // An integer set to 1 for enabled or 0 for disabled
122                $this->enabled = bb_get_option('ldap_authentication_enabled');
123               
124                $this->server = bb_get_option('ldap_authentication_server');
125               
126                if (isset($this->server['host']) && isset($this->server['domain'])) {
127                        $this->active = true;
128                }
129               
130                $this->options = bb_get_option('ldap_authentication_options');
131        }
132       
133       
134        /**
135         * Returns whether the plugin is active or not
136         *
137         * @return boolean
138         * @author Sam Bauers
139         **/
140        function isActive()
141        {
142                if ($this->enabled && $this->active) {
143                        return true;
144                } else {
145                        return false;
146                }
147        }
148       
149       
150        /**
151         * Determines whether we are viewing the given page
152         *
153         * Mostly adapted from bb_get_location();
154         *
155         * @return boolean
156         * @author Sam Bauers
157         **/
158        function locationIs($page)
159        {
160                $names = array(
161                        $_SERVER['PHP_SELF'],
162                        $_SERVER['SCRIPT_FILENAME'],
163                        $_SERVER['SCRIPT_NAME']
164                );
165               
166                foreach ($names as $name) {
167                        if (false !== strpos($name, '.php')) {
168                                $file = $name;
169                        }
170                }
171               
172                if (bb_find_filename($file) == $page) {
173                        return true;
174                } else {
175                        return false;
176                }
177        }
178       
179       
180        /**
181         * Disables standard registration
182         *
183         * @return void
184         * @author Sam Bauers
185         **/
186        function disableRegistration()
187        {
188                if ($this->isActive() && $this->options['disable_registration'] && $this->locationIs('register.php')) {
189                        bb_die(__('Registration is disabled for this forum, please login using your LDAP username and password.'));
190                }
191        }
192       
193       
194        /**
195         * Disables password recovery for users who have LDAP passwords
196         *
197         * @return void
198         * @author Sam Bauers
199         **/
200        function disableLDAPpasswordRecovery()
201        {
202                if ($this->isActive() && $this->locationIs('bb-reset-password.php')) {
203                        $user_login = user_sanitize($_POST['user_login']);
204                        if (!empty($user_login)) {
205                                $user = bb_get_user_by_name($user_login);
206                                if (substr($user->user_pass, 0, 5) == '^LDAP') {
207                                        bb_die(__('Password recovery is not possible for this account because it uses an LDAP username and password to login. To change your LDAP password, please contact your system administrator.'));
208                                }
209                        }
210                }
211        }
212       
213       
214        /**
215         * Disables password editing for users who have LDAP passwords
216         *
217         * @return void
218         * @author Sam Bauers
219         **/
220        function disableLDAPpasswordEditing()
221        {
222                global $bb_current_user;
223               
224                if ($this->isActive() && ($this->locationIs('profile.php') || $this->locationIs('profile-edit.php'))) {
225                        if (substr($bb_current_user->data->user_pass, 0, 5) == '^LDAP') {
226                                add_filter('bb_user_has_cap', array($this, 'removePasswordCapability'), 10, 2);
227                        }
228                }
229        }
230       
231       
232        /**
233         * Removes the change password capability for the current user
234         *
235         * @return array
236         * @author Sam Bauers
237         **/
238        function removePasswordCapability($allcaps, $caps)
239        {
240                if ($caps[0] == 'change_password') {
241                        unset($allcaps['change_password']);
242                }
243               
244                return $allcaps;
245        }
246       
247       
248        /**
249         * Replacement for bb_check_login
250         *
251         * @return object
252         * @author Sam Bauers
253         **/
254        function checkLogin($user, $pass, $already_md5 = false)
255        {
256                global $bbdb;
257               
258                $user = user_sanitize($user);
259               
260                if (!$already_md5) {
261                        $user_exists = bb_user_exists($user);
262                        if (!$user_exists) {
263                                // Check using LDAP
264                                if (!$this->options['disable_automatic_ldap_registration'] && $mail = $this->connectUser($user, $pass, true)) {
265                                        // Create the new user in the local database
266                                        if ($user_id = $this->newUser($user, $pass, $mail)) {
267                                                return $bbdb->get_row("SELECT * FROM $bbdb->users WHERE `ID` = $user_id");
268                                        } else {
269                                                bb_die(__('Failed to add new LDAP user to local database.'));
270                                        }
271                                } else {
272                                        return false;
273                                }
274                        } else {
275                                if (substr($user_exists->user_pass, 0, 5) == '^LDAP') {
276                                        if ($this->connectUser($user, $pass)) {
277                                                // Update their MD5 hash in case their password has changed
278                                                $bbdb->query("UPDATE $bbdb->users SET user_pass = '^LDAP-" . md5($pass) . "' WHERE user_login = '$user'");
279                                               
280                                                // Get their record from the local database
281                                                return $bbdb->get_row("SELECT * FROM $bbdb->users WHERE user_login = '$user' AND SUBSTRING(SUBSTRING_INDEX(user_pass, '---', 1), 1, 5) = '^LDAP'");
282                                        } else {
283                                                return false;
284                                        }
285                                } else {
286                                        $pass = user_sanitize(md5($pass));
287                                        return $bbdb->get_row("SELECT * FROM $bbdb->users WHERE user_login = '$user' AND SUBSTRING_INDEX(user_pass, '---', 1) = '$pass'");
288                                }
289                        }
290                } else {
291                        return $bbdb->get_row("SELECT * FROM $bbdb->users WHERE user_login = '$user' AND MD5( user_pass ) = '$pass'");
292                }
293        }
294       
295       
296        /**
297         * Connects the user to the LDAP server
298         *
299         * @return mixed
300         * @author Sam Bauers
301         **/
302        function connectUser($user, $pass, $register = false)
303        {
304                if ($this->server['port']) {
305                        $connection = ldap_connect($this->server['host'], $this->server['port']);
306                } else {
307                        $connection = ldap_connect($this->server['host']);
308                }
309               
310                if ($this->server['options']) {
311                        $options = explode('|', $this->server['options']);
312                        foreach ($options as $option) {
313                                $optionParts = explode(':', $option);
314                                ldap_set_option($connection, $optionParts[0], $optionParts[1]);
315                        }
316                }
317               
318                if ($this->server['tls']) {
319                        // TLS requires ldap protocol version 3
320                        ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
321                        $tlsStart = ldap_start_tls($connection);
322                } else {
323                        $tlsStart = true;
324                }
325               
326                if ($connection && $tlsStart) {
327                        $uid = 'uid=' . stripslashes(addslashes($user));
328                        $bindString = $uid . ',' . $this->server['domain'];
329                       
330                        $result = ldap_bind($connection, $bindString, stripslashes(addslashes($pass)));
331                       
332                        if ($result) {
333                                $mail = true;
334                               
335                                if ($register && $this->options['enable_email_retrieval_from_ldap']) {
336                                        if ($search = ldap_search($connection, $this->server['domain'], '(' . $uid . ')', array('mail'))) {
337                                                if ($entry = ldap_first_entry($connection, $search)) {
338                                                        $attributes = ldap_get_attributes($connection, $entry);
339                                                        $mail = $attributes['mail'][0];
340                                                }
341                                        }
342                                }
343                               
344                                ldap_unbind($result);
345                                ldap_close($connection);
346                               
347                                return $mail;
348                        } else {
349                                ldap_close($connection);
350                                return FALSE;
351                        }
352                } else {
353                        bb_die(__('Could not connect to LDAP authentication service.'));
354                }
355        }
356       
357       
358        /**
359         * Creates a new user with an LDAP password
360         *
361         * @return integer
362         * @author Sam Bauers
363         **/
364        function newUser($user_login, $user_pass, $user_email)
365        {
366                global $bbdb;
367                global $bb_table_prefix;
368               
369                $now = bb_current_time('mysql');
370                $password = '^LDAP-' . md5($user_pass);
371               
372                if ($user_email !== true) {
373                        $email = $user_email;
374                }
375               
376                $bbdb->query("INSERT INTO $bbdb->users (user_login, user_pass, user_email, user_registered) VALUES ('$user_login', '$password', '$email', '$now')");
377                $user_id = $bbdb->insert_id;
378               
379                bb_update_usermeta($user_id, $bb_table_prefix . 'capabilities', array('member' => true));
380               
381                do_action('ldap_authentication_new_user', $user_id);
382               
383                return $user_id;
384        }
385} // END class LDAP_Authentication
386
387
388// Initialise the class
389$ldap_authentication = new LDAP_Authentication();
390
391
392// If active, then add filters via API
393if ($ldap_authentication->isActive()) {
394        add_action('bb_init', array($ldap_authentication, 'disableRegistration'));
395        add_action('bb_init', array($ldap_authentication, 'disableLDAPpasswordRecovery'));
396        add_action('bb_init', array($ldap_authentication, 'disableLDAPpasswordEditing'));
397       
398       
399        /**
400         * Alias that hooks into LDAP_Authentication class checkLogin function
401         *
402         * This is a pluggable function, so it must sit outside the class like this
403         *
404         * @return object
405         * @author Sam Bauers
406         **/
407        if (!function_exists('bb_check_login')) {
408                function bb_check_login($user, $pass, $already_md5 = false)
409                {
410                        global $ldap_authentication;
411                        return $ldap_authentication->checkLogin($user, $pass, $already_md5);
412                }
413        }
414}
415
416
417/**
418 * The admin pages below are handled outside of the class due to constraints
419 * in the architecture of the admin menu generation routine in bbPress
420 */
421
422
423// Add filters for the admin area
424add_action('bb_admin_menu_generator', 'ldap_authentication_admin_page_add');
425add_action('bb_admin-header.php','ldap_authentication_admin_page_process');
426
427
428/**
429 * Adds in an item to the $bb_admin_submenu array
430 *
431 * @return void
432 * @author Sam Bauers
433 **/
434function ldap_authentication_admin_page_add() {
435        if (function_exists('bb_admin_add_submenu')) { // Build 794+
436                bb_admin_add_submenu(__('LDAP authentication'), 'use_keys', 'ldap_authentication_admin_page');
437        } else {
438                global $bb_submenu;
439                $submenu = array(__('LDAP authentication'), 'use_keys', 'ldap_authentication_admin_page');
440                if (isset($bb_submenu['plugins.php'])) { // Build 740-793
441                        $bb_submenu['plugins.php'][] = $submenu;
442                } else { // Build 277-739
443                        $bb_submenu['site.php'][] = $submenu;
444                }
445        }
446}
447
448
449/**
450 * Writes an admin page for the plugin
451 *
452 * @return string
453 * @author Sam Bauers
454 **/
455function ldap_authentication_admin_page() {
456        $enabled = bb_get_option('ldap_authentication_enabled');
457        $options = bb_get_option('ldap_authentication_options');
458        $server = bb_get_option('ldap_authentication_server');
459       
460        if ($enabled) {
461                $enabled_checked = ' checked="checked"';
462        }
463        if ($options['enable_email_retrieval_from_ldap']) {
464                $enable_email_retrieval_from_ldap_checked = ' checked="checked"';
465        }
466        if ($options['disable_automatic_ldap_registration']) {
467                $disable_automatic_ldap_registration_checked = ' checked="checked"';
468        }
469        if ($options['disable_registration']) {
470                $disable_registration_checked = ' checked="checked"';
471        }
472        if ($server['tls']) {
473                $tls_checked = ' checked="checked"';
474        }
475?>
476        <h2>LDAP authentication</h2>
477        <h3>Enable</h3>
478        <form method="post">
479        <p>
480                Enable LDAP authentication here:
481        </p>
482        <p>
483                <input type="checkbox" name="ldap_authentication_enabled" value="1" tabindex="10"<?php echo $enabled_checked; ?> /> Enable LDAP authentication<br />
484                &nbsp;
485        </p>
486        <h3>Retrieve email address when LDAP users register</h3>
487        <p>
488                If checked, the LDAP registration process will attempt to retrieve the LDAP
489                users email address from the LDAP repository:
490        </p>
491        <p>
492                <input type="checkbox" name="ldap_authentication_enable_email_retrieval_from_ldap" value="1" tabindex="20"<?php echo $enable_email_retrieval_from_ldap_checked; ?> /> Retrieve email address when LDAP users register<br />
493                &nbsp;
494        </p>
495        <h3>Disable automatic registration of LDAP users</h3>
496        <p>
497                In normal use, LDAP users are registered in bbPress on their first successful
498                login. If automatic registration is disabled here, then LDAP users will have
499                to be added manually in the database, they cannot be created through the normal
500                registration process:
501        </p>
502        <p>
503                <input type="checkbox" name="ldap_authentication_disable_automatic_ldap_registration" value="1" tabindex="20"<?php echo $disable_automatic_ldap_registration_checked; ?> /> Disable automatic registration of LDAP users<br />
504                &nbsp;
505        </p>
506        <h3>Disable normal registration</h3>
507        <p>
508                This will disable the normal registration page. Non-LDAP users are still
509                allowed to login normally with this option activated:
510        </p>
511        <p>
512                <input type="checkbox" name="ldap_authentication_disable_registration" value="1" tabindex="30"<?php echo $disable_registration_checked; ?> /> Disable normal registration<br />
513                &nbsp;
514        </p>
515        <h3>Server settings</h3>
516        <p>
517                Specify LDAP server settings here:
518        </p>
519        <table>
520                <tr>
521                        <th scope="row">Host:</th>
522                        <td><input type="text" name="ldap_authentication_host" tabindex="40" value="<?php echo $server['host']; ?>" /> required</td>
523                </tr>
524                <tr>
525                        <th scope="row">Port:</th>
526                        <td><input type="text" name="ldap_authentication_port" tabindex="50" value="<?php echo $server['port']; ?>" /> defaults to 389</td>
527                </tr>
528                <tr>
529                        <th scope="row">Domain:</th>
530                        <td><input type="text" name="ldap_authentication_domain" tabindex="60" value="<?php echo $server['domain']; ?>" /> required</td>
531                </tr>
532                <tr>
533                        <th scope="row">TLS:</th>
534                        <td><input type="checkbox" name="ldap_authentication_tls" value="1" tabindex="70"<?php echo $tls_checked; ?> /> use TLS encryption to connect (sets LDAP protocol to version 3)</td>
535                </tr>
536                <tr>
537                        <th scope="row">Options:</th>
538                        <td><input type="text" name="ldap_authentication_options" tabindex="80" value="<?php echo $server['options']; ?>" /> e.g.: option1:value1|option2:value2|...</td>
539                </tr>
540        </table>
541        <p class="submit alignleft">
542                <input name="submit" type="submit" value="<?php _e('Update'); ?>" tabindex="90" />
543                <input type="hidden" name="action" value="ldap_authentication_update" />
544        </p>
545        </form>
546<?php
547}
548
549
550/**
551 * Processes the admin page form
552 *
553 * @return void
554 * @author Sam Bauers
555 **/
556function ldap_authentication_admin_page_process() {
557        if (isset($_POST['submit'])) {
558                if ('ldap_authentication_update' == $_POST['action']) {
559                        // Enable LDAP
560                        if ($_POST['ldap_authentication_enabled']) {
561                                bb_update_option('ldap_authentication_enabled', $_POST['ldap_authentication_enabled']);
562                        } else {
563                                bb_delete_option('ldap_authentication_enabled');
564                        }
565                       
566                        // Set an empty options array
567                        $options = array();
568                       
569                        // Enable email retrieval from LDAP
570                        if ($_POST['ldap_authentication_enable_email_retrieval_from_ldap']) {
571                                $options['enable_email_retrieval_from_ldap'] = $_POST['ldap_authentication_enable_email_retrieval_from_ldap'];
572                        }
573                       
574                        // Disable automatic LDAP registration
575                        if ($_POST['ldap_authentication_disable_automatic_ldap_registration']) {
576                                $options['disable_automatic_ldap_registration'] = $_POST['ldap_authentication_disable_automatic_ldap_registration'];
577                        }
578                       
579                        // Disable normal registration
580                        if ($_POST['ldap_authentication_disable_registration']) {
581                                $options['disable_registration'] = $_POST['ldap_authentication_disable_registration'];
582                        }
583                       
584                        // Save or delete the options
585                        if (count($options)) {
586                                bb_update_option('ldap_authentication_options', $options);
587                        } else {
588                                bb_delete_option('ldap_authentication_options');
589                        }
590                       
591                        // Set an empty server array
592                        $server = array();
593                       
594                        // Host
595                        if ($_POST['ldap_authentication_host']) {
596                                $server['host'] = $_POST['ldap_authentication_host'];
597                        }
598                       
599                        // Port
600                        if ($_POST['ldap_authentication_port']) {
601                                $server['port'] = $_POST['ldap_authentication_port'];
602                        }
603                       
604                        // Domain
605                        if ($_POST['ldap_authentication_domain']) {
606                                $server['domain'] = $_POST['ldap_authentication_domain'];
607                        }
608                       
609                        // TLS
610                        if ($_POST['ldap_authentication_tls']) {
611                                $server['tls'] = $_POST['ldap_authentication_tls'];
612                        }
613                       
614                        // Host
615                        if ($_POST['ldap_authentication_host']) {
616                                $server['options'] = $_POST['ldap_authentication_options'];
617                        }
618                       
619                        // Save or delete the server
620                        if (count($server)) {
621                                bb_update_option('ldap_authentication_server', $server);
622                        } else {
623                                bb_delete_option('ldap_authentication_server');
624                        }
625                }
626        }
627}
628?>
Note: See TracBrowser for help on using the repository browser.