~ubuntu-branches/ubuntu/saucy/mediawiki-extensions/saucy

« back to all changes in this revision

Viewing changes to dist/mediawiki-extensions-ldapauth/usr/share/mediawiki-extensions/ldapauth/LdapAuthentication.php

  • Committer: Bazaar Package Importer
  • Author(s): Romain Beauxis
  • Date: 2010-05-04 15:13:35 UTC
  • mfrom: (0.1.1 experimental)
  • Revision ID: james.westby@ubuntu.com-20100504151335-54qeucg3ec108q28
Tags: 2.2
* Added Replaces:/Conflicts: to allow a proper upgrade.
Closes: #580066
* Fixed package descriptions.
Closes: #579667
* Patched mediawiki-extensions-fckeditor to make it work with
  php 5.3. The fix may not be perfect but at least it work.
  Not closing the bug (#579822) for now..

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
# Copyright (C) 2004 Ryan Lane <http://www.mediawiki.org/wiki/User:Ryan_lane>
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License along
 
15
# with this program; if not, write to the Free Software Foundation, Inc.,
 
16
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
# http://www.gnu.org/copyleft/gpl.html
 
18
 
 
19
/**
 
20
 * LdapAuthentication plugin.
 
21
 *
 
22
 * Password authentication, and Smartcard Authentication support are currently
 
23
 * available. All forms of authentication, current and future, should support
 
24
 * group, and attribute based restrictions; preference pulling; and group
 
25
 * syncronization. All forms of authentication should have basic support for 
 
26
 * adding users, changing passwords, and updating preferences in LDAP.
 
27
 *
 
28
 * Password authentication has a number of configurations, including straight binds,
 
29
 * proxy based authentication, and anonymous-search based authentication.
 
30
 *
 
31
 * @package MediaWiki
 
32
 */
 
33
 
 
34
#
 
35
# LdapAuthentication.php
 
36
#
 
37
# Info available at http://www.mediawiki.org/wiki/Extension:LDAP_Authentication
 
38
# and at http://www.mediawiki.org/wiki/Extension:LDAP_Authentication/Configuration_Examples
 
39
# and at http://www.mediawiki.org/wiki/Extension:LDAP_Authentication/Smartcard_Configuration_Examples
 
40
#
 
41
# Support is available at http://www.mediawiki.org/wiki/Extension_talk:LDAP_Authentication 
 
42
#
 
43
 
 
44
/**
 
45
 * Add extension information to Special:Version
 
46
 */
 
47
$wgExtensionCredits['other'][] = array(
 
48
        'name' => 'LDAP Authentication Plugin',
 
49
        'version' => '1.2b (alpha)',
 
50
        'author' => 'Ryan Lane',
 
51
        'description' => 'LDAP Authentication plugin with support for multiple LDAP authentication methods',
 
52
        'url' => 'http://www.mediawiki.org/wiki/Extension:LDAP_Authentication',
 
53
        );
 
54
 
 
55
//constants for search base
 
56
define("GROUPDN", 0);
 
57
define("USERDN", 1);
 
58
define("DEFAULTDN", 2);
 
59
 
 
60
//constants for error reporting
 
61
define("NONSENSITIVE", 1);
 
62
define("SENSITIVE", 2);
 
63
define("HIGHLYSENSITIVE", 3);
 
64
 
 
65
class LdapAuthenticationPlugin extends AuthPlugin {
 
66
 
 
67
        //ldap connection resource
 
68
        var $ldapconn;
 
69
 
 
70
        //preferences
 
71
        var $email, $lang, $realname, $nickname, $externalid;
 
72
 
 
73
        //username pulled from ldap
 
74
        var $LDAPUsername;
 
75
 
 
76
        //userdn pulled from ldap
 
77
        var $userdn;
 
78
 
 
79
        //groups pulled from ldap
 
80
        var $userLDAPGroups;
 
81
        var $allLDAPGroups;
 
82
 
 
83
        //boolean to test for failed auth
 
84
        var $authFailed;
 
85
 
 
86
        //boolean to test for fetched user info
 
87
        var $fetchedUserInfo;
 
88
 
 
89
        //the user's entry and all attributes
 
90
        var $userInfo;
 
91
 
 
92
        function LdapAuthenticationPlugin() {
 
93
        }
 
94
 
 
95
        /**
 
96
         * Check whether there exists a user account with the given name.
 
97
         * The name will be normalized to MediaWiki's requirements, so
 
98
         * you might need to munge it (for instance, for lowercase initial
 
99
         * letters).
 
100
         *
 
101
         * @param string $username
 
102
         * @return bool
 
103
         * @access public
 
104
         */
 
105
        function userExists( $username ) {
 
106
                global $wgLDAPAddLDAPUsers;
 
107
 
 
108
                $this->printDebug( "Entering userExists", NONSENSITIVE );
 
109
 
 
110
                //If we can't add LDAP users, we don't really need to check
 
111
                //if the user exists, the authenticate method will do this for
 
112
                //us. This will decrease hits to the LDAP server.
 
113
                //We do however, need to use this if we are using auto authentication.
 
114
                if ( ( !isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) || !$wgLDAPAddLDAPUsers[$_SESSION['wsDomain']]) && !$this->useAutoAuth() ) {
 
115
                        return true;
 
116
                }
 
117
 
 
118
                $this->ldapconn = $this->connect();
 
119
                if ( $this->ldapconn ) {
 
120
                        $this->printDebug( "Successfully connected", NONSENSITIVE );
 
121
 
 
122
                        $searchstring = $this->getSearchString( $this->ldapconn, $username );
 
123
 
 
124
                        //If we are using auto authentication, and we got
 
125
                        //anything back, then the user exists.
 
126
                        if ( $this->useAutoAuth() && $searchstring != '' ) {
 
127
                                //getSearchString is going to bind, but will not unbind
 
128
                                //Let's clean up
 
129
                                @ldap_unbind();
 
130
                                return true;
 
131
                        }
 
132
 
 
133
                        //Search for the entry.
 
134
                        $entry = @ldap_read( $this->ldapconn, $searchstring, "objectclass=*" );
 
135
 
 
136
                        //getSearchString is going to bind, but will not unbind
 
137
                        //Let's clean up
 
138
                        @ldap_unbind();
 
139
                        if ( !$entry ) {
 
140
                                $this->printDebug( "Did not find a matching user in LDAP", NONSENSITIVE );
 
141
                                return false;
 
142
                        } else {
 
143
                                $this->printDebug( "Found a matching user in LDAP", NONSENSITIVE );
 
144
                                return true;
 
145
                        }
 
146
                } else {
 
147
                        $this->printDebug( "Failed to connect", NONSENSITIVE );
 
148
                        return false;
 
149
                }
 
150
                
 
151
        }
 
152
 
 
153
        /**
 
154
         * Connect to LDAP
 
155
         *
 
156
         * @access private
 
157
         */
 
158
        function connect() {
 
159
                global $wgLDAPServerNames;
 
160
                global $wgLDAPEncryptionType;
 
161
                global $wgLDAPOptions;
 
162
 
 
163
                $this->printDebug( "Entering Connect", NONSENSITIVE );
 
164
 
 
165
                if ( !function_exists( 'ldap_connect' ) ) {
 
166
                        $this->printDebug( "It looks like you are issing LDAP support; please ensure you have either compiled LDAP support in, or have enabled the module. If the authentication is working for you, the plugin isn't properly detecting the LDAP module, and you can safely ignore this message.", NONSENSITIVE );
 
167
                        return false;
 
168
                }
 
169
 
 
170
                //If the admin didn't set an encryption type, we default to tls
 
171
                if ( isset( $wgLDAPEncryptionType[$_SESSION['wsDomain']] ) ) {
 
172
                        $encryptionType = $wgLDAPEncryptionType[$_SESSION['wsDomain']];
 
173
                } else {
 
174
                        $encryptionType = "tls";
 
175
                }
 
176
 
 
177
                //Set the server string depending on whether we use ssl or not
 
178
                switch( $encryptionType ) {
 
179
                        case "ldapi":
 
180
                                # this is a really dirty place to put this,
 
181
                                # but it is easy and avoids another config option.
 
182
                                $this->printDebug( "Using ldapi", SENSITIVE );
 
183
                                $serverpre = "ldapi://";
 
184
                                break;
 
185
                        case "ssl":
 
186
                                $this->printDebug( "Using SSL", SENSITIVE );
 
187
                                $serverpre = "ldaps://";
 
188
                                break;
 
189
                        default:
 
190
                                $this->printDebug( "Using TLS or not using encryption.", SENSITIVE );
 
191
                                $serverpre = "ldap://";
 
192
                }
 
193
 
 
194
                //Make a space separated list of server strings with the ldap:// or ldaps://
 
195
                //string added.
 
196
                $servers = "";
 
197
                $tmpservers = $wgLDAPServerNames[$_SESSION['wsDomain']];
 
198
                $tok = strtok( $tmpservers, " " );
 
199
                while ( $tok ) {
 
200
                        $servers = $servers . " " . $serverpre . $tok;
 
201
                        $tok = strtok( " " );
 
202
                }
 
203
                $servers = rtrim($servers);
 
204
 
 
205
                $this->printDebug( "Using servers: $servers", SENSITIVE );
 
206
 
 
207
                //Connect and set options
 
208
                $this->ldapconn = @ldap_connect( $servers );
 
209
                ldap_set_option( $this->ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
 
210
                ldap_set_option( $this->ldapconn, LDAP_OPT_REFERRALS, 0);
 
211
 
 
212
                if ( isset( $wgLDAPOptions[$_SESSION['wsDomain']] ) ) {
 
213
                        $options = $wgLDAPOptions[$_SESSION['wsDomain']];
 
214
                        foreach ( $options as $key => $value ) {
 
215
                                if ( !ldap_set_option( $this->ldapconn, constant( $key ), $value ) ) {
 
216
                                        $this->printDebug( "Can't set option to LDAP! Option code and value: " . $key . "=" . $value, 1 );
 
217
                                }
 
218
                        }
 
219
                }
 
220
 
 
221
                //TLS needs to be started after the connection is made
 
222
                if ( $encryptionType == "tls" ) {
 
223
                        $this->printDebug( "Using TLS", SENSITIVE );
 
224
                        if ( !ldap_start_tls( $this->ldapconn ) ) {
 
225
                                $this->printDebug( "Failed to start TLS.", SENSITIVE );
 
226
                                return;
 
227
                        }
 
228
                }
 
229
        }
 
230
 
 
231
        /**
 
232
         * Check if a username+password pair is a valid login, or if the username
 
233
         * is allowed access to the wiki.
 
234
         * The name will be normalized to MediaWiki's requirements, so
 
235
         * you might need to munge it (for instance, for lowercase initial
 
236
         * letters).
 
237
         *
 
238
         * @param string $username
 
239
         * @param string $password
 
240
         * @return bool
 
241
         * @access public
 
242
         */
 
243
        function authenticate( $username, $password='' ) {
 
244
                global $wgLDAPAuthAttribute;
 
245
                global $wgLDAPAutoAuthUsername;
 
246
                global $wgLDAPLowerCaseUsername;
 
247
                global $wgLDAPSearchStrings;
 
248
 
 
249
                $this->printDebug( "Entering authenticate", NONSENSITIVE );
 
250
 
 
251
                //We don't handle local authentication
 
252
                if ( 'local' == $_SESSION['wsDomain'] ) {
 
253
                        $this->printDebug( "User is using a local domain", SENSITIVE );
 
254
                        return false;
 
255
                }
 
256
 
 
257
                //If the user is using auto authentication, we need to ensure
 
258
                //that he/she isn't trying to fool us by sending a username other
 
259
                //than the one the web server got from the auto-authentication method.
 
260
                if ( $this->useAutoAuth() && $wgLDAPAutoAuthUsername != $username ) {
 
261
                        $this->printDebug( "The username provided ($username) doesn't match the username provided by the webserver ($wgLDAPAutoAuthUsername). The user is probably trying to log in to the auto-authentication domain with password authentication via the wiki. Denying access.", SENSITIVE );
 
262
                        return false;
 
263
                }
 
264
 
 
265
                //We need to ensure that if we require a password, that it is
 
266
                //not blank. We don't allow blank passwords, so we are being
 
267
                //tricked if someone is supplying one when using password auth.
 
268
                //Smartcard authentication uses a pin, and does not require
 
269
                //a password to be given; a blank password here is wanted.
 
270
                if ( '' == $password && !$this->useAutoAuth() ) {
 
271
                        $this->printDebug( "User used a blank password", NONSENSITIVE );
 
272
                        return false;
 
273
                }
 
274
 
 
275
                $this->connect();
 
276
                if ( $this->ldapconn ) {
 
277
                        $this->printDebug( "Connected successfully", NONSENSITIVE );
 
278
 
 
279
                        //Mediawiki munges the username before authenticate is called,
 
280
                        //this can mess with authentication, group pulling/restriction,
 
281
                        //preference pulling, etc. Let's allow the admin to use
 
282
                        //a lowercased username if needed.
 
283
                        if ( isset( $wgLDAPLowerCaseUsername[$_SESSION['wsDomain']] ) && $wgLDAPLowerCaseUsername[$_SESSION['wsDomain']] ) {
 
284
                                $this->printDebug( "Lowercasing the username: $username", NONSENSITIVE );
 
285
                                $username = strtolower( $username );
 
286
                        }
 
287
 
 
288
                        $this->userdn = $this->getSearchString( $username );
 
289
 
 
290
                        //It is possible that getSearchString will return an
 
291
                        //empty string; if this happens, the bind will ALWAYS
 
292
                        //return true, and will let anyone in!
 
293
                        if ( '' == $this->userdn ) {
 
294
                                $this->printDebug( "User DN is blank", NONSENSITIVE );
 
295
                                @ldap_unbind();
 
296
                                $this->markAuthFailed();
 
297
                                return false;
 
298
                        }
 
299
 
 
300
                        //If we are using password authentication, we need to bind as the
 
301
                        //user to make sure the password is correct.
 
302
                        if ( !$this->useAutoAuth() ) {
 
303
                                $this->printDebug( "Binding as the user", NONSENSITIVE );
 
304
 
 
305
                                //Let's see if the user can authenticate.
 
306
                                $bind = $this->bindAs( $this->userdn, $password );
 
307
                                if ( !$bind ) {
 
308
                                        $this->markAuthFailed();
 
309
                                        return false;
 
310
                                }
 
311
 
 
312
                                $this->printDebug( "Bound successfully", NONSENSITIVE );
 
313
 
 
314
                                if ( isset( $wgLDAPSearchStrings[$_SESSION['wsDomain']] ) ) { 
 
315
                                        $ss = $wgLDAPSearchStrings[$_SESSION['wsDomain']];
 
316
                                        if ( strstr( $ss, "@" ) || strstr( $ss, '\\' ) ) {
 
317
                                                //We are most likely configured using USER-NAME@DOMAIN, or
 
318
                                                //DOMAIN\\USER-NAME.
 
319
                                                //Get the user's full DN so we can search for groups and such.
 
320
                                                $this->userdn = $this->getUserDN( $username );
 
321
                                                $this->printDebug( "Pulled the user's DN: $this->userdn", NONSENSITIVE );
 
322
                                        }
 
323
                                }
 
324
 
 
325
                                if ( isset( $wgLDAPAuthAttribute[$_SESSION['wsDomain']] ) ) {
 
326
 
 
327
                                        $this->printDebug( "Checking for auth attributes", NONSENSITIVE );
 
328
 
 
329
                                        $filter = "(" . $wgLDAPAuthAttribute[$_SESSION['wsDomain']] . ")";
 
330
                                        $attributes = array( "dn" );
 
331
 
 
332
                                        $entry = ldap_read( $this->ldapconn, $this->userdn, $filter, $attributes );
 
333
                                        $info = ldap_get_entries( $this->ldapconn, $entry );
 
334
 
 
335
                                        if ( $info["count"] < 1 ) {
 
336
                                                $this->printDebug( "Failed auth attribute check", NONSENSITIVE );
 
337
                                                @ldap_unbind();
 
338
                                                $this->markAuthFailed();
 
339
                                                return false;
 
340
                                        }
 
341
                                }
 
342
                        }
 
343
 
 
344
                        $this->getGroups( $username );
 
345
 
 
346
                        if ( !$this->checkGroups( $username ) ) {
 
347
                                @ldap_unbind();
 
348
                                $this->markAuthFailed();
 
349
                                return false;
 
350
                        }
 
351
 
 
352
                        $this->getPreferences();
 
353
 
 
354
                        if ( !$this->synchUsername( $username ) ) {
 
355
                                @ldap_unbind();
 
356
                                $this->markAuthFailed();
 
357
                                return false;
 
358
                        }
 
359
 
 
360
                        @ldap_unbind();
 
361
                } else {
 
362
                        $this->printDebug( "Failed to connect", NONSENSITIVE );
 
363
                        $this->markAuthFailed();
 
364
                        return false;
 
365
                }
 
366
                $this->printDebug( "Authentication passed", NONSENSITIVE );
 
367
 
 
368
                //We made it this far; the user authenticated and didn't fail any checks, so he/she gets in.
 
369
                return true;
 
370
        }
 
371
 
 
372
        function markAuthFailed() {
 
373
                $this->authFailed = true;
 
374
        }
 
375
 
 
376
        /**
 
377
         * Modify options in the login template.
 
378
         *
 
379
         * @param UserLoginTemplate $template
 
380
         * @access public
 
381
         */
 
382
        function modifyUITemplate( &$template ) {
 
383
                global $wgLDAPDomainNames, $wgLDAPUseLocal;
 
384
                global $wgLDAPAddLDAPUsers;
 
385
                global $wgLDAPAutoAuthDomain;
 
386
 
 
387
                $this->printDebug( "Entering modifyUITemplate", NONSENSITIVE );
 
388
 
 
389
                if ( !isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) || !$wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) {
 
390
                        $template->set( 'create', false );
 
391
                }
 
392
 
 
393
                $template->set( 'usedomain', true );
 
394
                $template->set( 'useemail', false );
 
395
 
 
396
                $tempDomArr = $wgLDAPDomainNames;
 
397
                if ( $wgLDAPUseLocal ) {
 
398
                        $this->printDebug( "Allowing the local domain, adding it to the list.", NONSENSITIVE );
 
399
                        array_push( $tempDomArr, 'local' );
 
400
                }
 
401
 
 
402
                if ( isset( $wgLDAPAutoAuthDomain ) ) {
 
403
                        $this->printDebug( "Allowing auto-authentication login, removing the domain from the list.", NONSENSITIVE );
 
404
 
 
405
                        //There is no reason for people to log in directly to the wiki if the are using an
 
406
                        //auto-authentication domain. If they try to, they are probably up to something fishy.
 
407
                        unset( $tempDomArr[array_search( $wgLDAPAutoAuthDomain, $tempDomArr )] );
 
408
                }
 
409
 
 
410
                $template->set( 'domainnames', $tempDomArr );
 
411
        }
 
412
 
 
413
        /**
 
414
         * Return true if the wiki should create a new local account automatically
 
415
         * when asked to login a user who doesn't exist locally but does in the
 
416
         * external auth database.
 
417
         *
 
418
         * This is just a question, and shouldn't perform any actions.
 
419
         *
 
420
         * @return bool
 
421
         * @access public
 
422
         */
 
423
        function autoCreate() {
 
424
                global $wgLDAPDisableAutoCreate;
 
425
 
 
426
                if ( isset( $wgLDAPDisableAutoCreate[$_SESSION['wsDomain']] ) && $wgLDAPDisableAutoCreate[$_SESSION['wsDomain']] ) {
 
427
                        return false;
 
428
                } else {
 
429
                        return true;
 
430
                }
 
431
        }
 
432
 
 
433
        /**
 
434
         * Set the given password in LDAP.
 
435
         * Return true if successful.
 
436
         *
 
437
         * @param User $user
 
438
         * @param string $password
 
439
         * @return bool
 
440
         * @access public
 
441
         */
 
442
        function setPassword( $user, &$password ) {
 
443
                global $wgLDAPUpdateLDAP, $wgLDAPWriterDN, $wgLDAPWriterPassword;
 
444
 
 
445
                $this->printDebug( "Entering setPassword", NONSENSITIVE );
 
446
 
 
447
                if ( $_SESSION['wsDomain'] == 'local' ) {
 
448
                        $this->printDebug( "User is using a local domain", NONSENSITIVE );
 
449
 
 
450
                        //We don't set local passwords, but we don't want the wiki
 
451
                        //to send the user a failure.           
 
452
                        return true;
 
453
                } else if ( !isset( $wgLDAPUpdateLDAP[$_SESSION['wsDomain']] ) || !$wgLDAPUpdateLDAP[$_SESSION['wsDomain']] ) {
 
454
                        $this->printDebug( "Wiki is set to not allow updates", NONSENSITIVE );
 
455
 
 
456
                        //We aren't allowing the user to change his/her own password
 
457
                        return false;
 
458
                }
 
459
 
 
460
                if ( !isset( $wgLDAPWriterDN[$_SESSION['wsDomain']] ) ) {
 
461
                        $this->printDebug( "Wiki doesn't have wgLDAPWriterDN set", NONSENSITIVE );
 
462
 
 
463
                        //We can't change a user's password without an account that is
 
464
                        //allowed to do it.
 
465
                        return false;
 
466
                }
 
467
 
 
468
                $pass = $this->getPasswordHash( $password );
 
469
 
 
470
                $this->connect();
 
471
                if ( $this->ldapconn ) {
 
472
                        $this->printDebug( "Connected successfully", NONSENSITIVE );
 
473
                        $this->userdn = $this->getSearchString( $user->getName() );
 
474
 
 
475
                        $this->printDebug( "Binding as the writerDN", NONSENSITIVE );
 
476
                        $bind = $this->bindAs( $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
 
477
                        if ( !$bind ) {
 
478
                                return false;
 
479
                        }
 
480
 
 
481
                        $values["userpassword"] = $pass;
 
482
 
 
483
                        //Blank out the password in the database. We don't want to save
 
484
                        //domain credentials for security reasons.
 
485
                        $password = '';
 
486
 
 
487
                        $success = @ldap_modify( $this->ldapconn, $this->userdn, $values );
 
488
 
 
489
                        //Let's clean up
 
490
                        @ldap_unbind();
 
491
                        if ( $success ) {
 
492
                                $this->printDebug( "Successfully modified the user's password", NONSENSITIVE );
 
493
                                return true;
 
494
                        } else {
 
495
                                $this->printDebug( "Failed to modify the user's password", NONSENSITIVE );
 
496
                                return false;
 
497
                        }
 
498
                } else {
 
499
                        $this->printDebug( "Failed to connect", NONSENSITIVE );
 
500
                        return false;
 
501
                }
 
502
        }
 
503
 
 
504
        /**
 
505
         * Update user information in LDAP
 
506
         * Return true if successful.
 
507
         *
 
508
         * @param User $user
 
509
         * @return bool
 
510
         * @access public
 
511
         */     
 
512
        function updateExternalDB( $user ) {
 
513
                global $wgLDAPUpdateLDAP;
 
514
                global $wgLDAPWriterDN, $wgLDAPWriterPassword;
 
515
 
 
516
                $this->printDebug( "Entering updateExternalDB", NONSENSITIVE );
 
517
 
 
518
                if ( ( !isset( $wgLDAPUpdateLDAP[$_SESSION['wsDomain']] ) || !$wgLDAPUpdateLDAP[$_SESSION['wsDomain']] ) ||
 
519
                        $_SESSION['wsDomain'] == 'local' ) {
 
520
                        $this->printDebug( "Either the user is using a local domain, or the wiki isn't allowing updates", NONSENSITIVE );
 
521
 
 
522
                        //We don't handle local preferences, but we don't want the
 
523
                        //wiki to return an error.
 
524
                        return true;
 
525
                }
 
526
 
 
527
                if ( !isset( $wgLDAPWriterDN[$_SESSION['wsDomain']] ) ) {
 
528
                        $this->printDebug( "The wiki doesn't have wgLDAPWriterDN set", NONSENSITIVE );
 
529
 
 
530
                        //We can't modify LDAP preferences if we don't have a user
 
531
                        //capable of editing LDAP attributes.
 
532
                        return false;
 
533
                }
 
534
 
 
535
                $this->email = $user->getEmail();
 
536
                $this->realname = $user->getRealName();
 
537
                $this->nickname = $user->getOption( 'nickname' );
 
538
                $this->language = $user->getOption( 'language' );
 
539
 
 
540
                $this->connect();
 
541
                if ( $this->ldapconn ) {
 
542
                        $this->printDebug( "Connected successfully", NONSENSITIVE );
 
543
                        $this->userdn = $this->getSearchString( $user->getName() );
 
544
 
 
545
                        $this->printDebug( "Binding as the writerDN", NONSENSITIVE );
 
546
                        $bind = $this->bindAs( $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
 
547
                        if ( !$bind ) {
 
548
                                return false;
 
549
                        }
 
550
 
 
551
                        if ( '' != $this->email ) { $values["mail"] = $this->email; }
 
552
                        if ( '' != $this->nickname ) { $values["displayname"] = $this->nickname; }
 
553
                        if ( '' != $this->realname ) { $values["cn"] = $this->realname; }
 
554
                        if ( '' != $this->language ) { $values["preferredlanguage"] = $this->language; }
 
555
 
 
556
                        if ( 0 != sizeof( $values ) && @ldap_modify( $this->ldapconn, $this->userdn, $values ) ) {
 
557
                                $this->printDebug( "Successfully modified the user's attributes", NONSENSITIVE );
 
558
                                @ldap_unbind();
 
559
                                return true;
 
560
                        } else {
 
561
                                $this->printDebug( "Failed to modify the user's attributes", NONSENSITIVE );
 
562
                                @ldap_unbind();
 
563
                                return false;
 
564
                        }
 
565
                } else {
 
566
                        $this->printDebug( "Failed to Connect", NONSENSITIVE );
 
567
                        return false;
 
568
                }
 
569
        }
 
570
 
 
571
        /**
 
572
         * Can the wiki create accounts in LDAP?
 
573
         * Return true if yes.
 
574
         *
 
575
         * @return bool
 
576
         * @access public
 
577
         */     
 
578
        function canCreateAccounts() {
 
579
                global $wgLDAPAddLDAPUsers;
 
580
 
 
581
                if ( isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) && $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) {
 
582
                        return true;
 
583
                } else {
 
584
                        return false;
 
585
                }
 
586
        }
 
587
 
 
588
        /**
 
589
         * Can the wiki change passwords in LDAP, or can the user
 
590
         * change passwords locally?
 
591
         * Return true if yes.
 
592
         *
 
593
         * @return bool
 
594
         * @access public
 
595
         */     
 
596
        function allowPasswordChange() {
 
597
                global $wgLDAPUpdateLDAP, $wgLDAPMailPassword;
 
598
                global $wgLDAPUseLocal;
 
599
 
 
600
                $this->printDebug( "Entering allowPasswordChange", NONSENSITIVE );
 
601
 
 
602
                $retval = false;
 
603
 
 
604
                // Local domains need to be able to change passwords
 
605
                if ( (isset($wgLDAPUseLocal) && $wgLDAPUseLocal) && 'local' == $_SESSION['wsDomain'] ) {
 
606
                        $retval = true;
 
607
                }
 
608
 
 
609
                if ( isset( $wgLDAPUpdateLDAP[$_SESSION['wsDomain']] ) && $wgLDAPUpdateLDAP[$_SESSION['wsDomain']] ) {
 
610
                        $retval = true;
 
611
                }
 
612
 
 
613
                if ( isset( $wgLDAPMailPassword[$_SESSION['wsDomain']] ) && $wgLDAPMailPassword[$_SESSION['wsDomain']] ) {
 
614
                        $retval = true;
 
615
                }
 
616
 
 
617
                return $retval;
 
618
        }
 
619
 
 
620
        /**
 
621
         * Add a user to LDAP.
 
622
         * Return true if successful.
 
623
         *
 
624
         * @param User $user
 
625
         * @param string $password
 
626
         * @return bool
 
627
         * @access public
 
628
         */
 
629
        function addUser( $user, $password ) {
 
630
                global $wgLDAPAddLDAPUsers, $wgLDAPWriterDN, $wgLDAPWriterPassword;
 
631
                global $wgLDAPSearchAttributes;
 
632
                global $wgLDAPWriteLocation;
 
633
                global $wgLDAPRequiredGroups, $wgLDAPGroupDN;
 
634
                global $wgLDAPAuthAttribute;
 
635
 
 
636
                $this->printDebug( "Entering addUser", NONSENSITIVE );
 
637
 
 
638
                if ( ( !isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) || !$wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) ||
 
639
                        'local' == $_SESSION['wsDomain'] ) {
 
640
                        $this->printDebug( "Either the user is using a local domain, or the wiki isn't allowing users to be added to LDAP", NONSENSITIVE );
 
641
 
 
642
                        //Tell the wiki not to return an error.
 
643
                        return true;
 
644
                }
 
645
 
 
646
                if ( $wgLDAPRequiredGroups || $wgLDAPGroupDN ) {
 
647
                        $this->printDebug( "The wiki is requiring users to be in specific groups, and cannot add users as this would be a security hole.", NONSENSITIVE );
 
648
                        //It is possible that later we can add users into
 
649
                        //groups, but since we don't support it, we don't want
 
650
                        //to open holes!
 
651
                        return false;
 
652
                }
 
653
 
 
654
                if ( !isset( $wgLDAPWriterDN[$_SESSION['wsDomain']] ) ) {
 
655
                        $this->printDebug( "The wiki doesn't have wgLDAPWriterDN set", NONSENSITIVE );
 
656
 
 
657
                        //We can't add users without an LDAP account capable of doing so.
 
658
                        return false;
 
659
                }
 
660
 
 
661
                $this->email = $user->getEmail();
 
662
                $this->realname = $user->getRealName();
 
663
                $username = $user->getName();
 
664
 
 
665
                $pass = $this->getPasswordHash( $password );
 
666
 
 
667
                $this->connect();
 
668
                if ( $this->ldapconn ) {
 
669
                        $this->printDebug( "Successfully connected", NONSENSITIVE );
 
670
 
 
671
                        $this->userdn = $this->getSearchString( $username );
 
672
                        if ( '' == $this->userdn ) {
 
673
                                $this->printDebug( "$this->userdn is blank, attempting to use wgLDAPWriteLocation", NONSENSITIVE );
 
674
                                if ( isset( $wgLDAPWriteLocation[$_SESSION['wsDomain']] ) ) {
 
675
                                        $this->printDebug( "wgLDAPWriteLocation is set, using that", NONSENSITIVE );
 
676
                                        $this->userdn = $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=" .
 
677
                                                $username . "," . $wgLDAPWriteLocation[$_SESSION['wsDomain']];
 
678
                                } else {
 
679
                                        $this->printDebug( "wgLDAPWriteLocation is not set, failing", NONSENSITIVE );
 
680
                                        //getSearchString will bind, but will not unbind
 
681
                                        @ldap_unbind();
 
682
                                        return false;
 
683
                                }
 
684
                        }
 
685
 
 
686
                        $this->printDebug( "Binding as the writerDN", NONSENSITIVE );
 
687
 
 
688
                        $bind = $this->bindAs( $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
 
689
                        if ( !$bind ) {
 
690
                                $this->printDebug( "Failed to bind as the writerDN; add failed", NONSENSITIVE );
 
691
                                return false;
 
692
                        }
 
693
 
 
694
                        //Set up LDAP attributes
 
695
                        $values["uid"] = $username;
 
696
                        //sn is required for objectclass inetorgperson
 
697
                        $values["sn"] = $username;
 
698
                        if ( '' != $this->email ) { $values["mail"] = $this->email; }
 
699
                        if ( '' != $this->realname ) {$values["cn"] = $this->realname; }
 
700
                                else { $values["cn"] = $username; }
 
701
                        $values["userpassword"] = $pass;
 
702
                        $values["objectclass"] = "inetorgperson";
 
703
 
 
704
                        if ( isset ( $wgLDAPAuthAttribute[$_SESSION['wsDomain']] ) ) {
 
705
                                $values[$wgLDAPAuthAttribute[$_SESSION['wsDomain']]] = "true";
 
706
                        }
 
707
 
 
708
                        $this->printDebug( "Adding user", NONSENSITIVE );
 
709
                        if ( @ldap_add( $this->ldapconn, $this->userdn, $values ) ) {
 
710
                                $this->printDebug( "Successfully added user", NONSENSITIVE );
 
711
                                @ldap_unbind();
 
712
                                return true;
 
713
                        } else {
 
714
                                $this->printDebug( "Failed to add user", NONSENSITIVE );
 
715
                                @ldap_unbind();
 
716
                                return false;
 
717
                        }
 
718
                } else {
 
719
                        $this->printDebug( "Failed to connect; add failed", NONSENSITIVE );
 
720
                        return false;
 
721
                }
 
722
        }
 
723
 
 
724
        /**
 
725
         * Set the domain this plugin is supposed to use when authenticating.
 
726
         *
 
727
         * @param string $domain
 
728
         * @access public       
 
729
         */
 
730
        function setDomain( $domain ) {
 
731
                $this->printDebug( "Setting domain as: $domain", NONSENSITIVE );
 
732
                $_SESSION['wsDomain'] = $domain;
 
733
        }
 
734
 
 
735
        /**
 
736
         * Check to see if the specific domain is a valid domain.
 
737
         * Return true if the domain is valid.
 
738
         *
 
739
         * @param string $domain
 
740
         * @return bool
 
741
         * @access public
 
742
         */
 
743
        function validDomain( $domain ) {
 
744
                global $wgLDAPDomainNames, $wgLDAPUseLocal;
 
745
 
 
746
                $this->printDebug( "Entering validDomain", NONSENSITIVE );
 
747
 
 
748
                if ( in_array( $domain, $wgLDAPDomainNames ) || ( $wgLDAPUseLocal && 'local' == $domain ) ) {
 
749
                        $this->printDebug( "User is using a valid domain.", NONSENSITIVE );
 
750
                        return true;
 
751
                } else {
 
752
                        $this->printDebug( "User is not using a valid domain.", NONSENSITIVE );
 
753
                        return false;
 
754
                }
 
755
        }
 
756
 
 
757
        /**
 
758
         * When a user logs in, update user with information from LDAP.
 
759
         *
 
760
         * @param User $user
 
761
         * @access public
 
762
         * TODO: fix the setExternalID stuff
 
763
         */
 
764
        function updateUser( &$user ) {
 
765
                global $wgLDAPRetrievePrefs, $wgLDAPPreferences;
 
766
                global $wgLDAPUseLDAPGroups;
 
767
                global $wgLDAPUniqueBlockLogin, $wgLDAPUniqueRenameUser;
 
768
 
 
769
                $this->printDebug( "Entering updateUser", NONSENSITIVE );
 
770
 
 
771
                if ($this->authFailed) {
 
772
                        $this->printDebug( "User didn't successfully authenticate, exiting.", NONSENSITIVE );
 
773
                        return;
 
774
                }
 
775
 
 
776
                $saveSettings = false;
 
777
 
 
778
                //If we aren't pulling preferences, we don't want to accidentally
 
779
                //overwrite anything.
 
780
                if ( ( isset( $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) && $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] )
 
781
                        || isset( $wgLDAPPreferences[$_SESSION['wsDomain']] ) ) {
 
782
                        $this->printDebug( "Setting user preferences.", NONSENSITIVE );
 
783
 
 
784
                        if ( '' != $this->lang ) {
 
785
                                $this->printDebug( "Setting language.", NONSENSITIVE );
 
786
                                $user->setOption( 'language', $this->lang );
 
787
                        }
 
788
                        if ( '' != $this->nickname ) {
 
789
                                $this->printDebug( "Setting nickname.", NONSENSITIVE );
 
790
                                $user->setOption( 'nickname', $this->nickname );
 
791
                        }
 
792
                        if ( '' != $this->realname ) {
 
793
                                $this->printDebug( "Setting realname.", NONSENSITIVE );
 
794
                                $user->setRealName( $this->realname );
 
795
                        }
 
796
                        if ( '' != $this->email ) {
 
797
                                $this->printDebug( "Setting email.", NONSENSITIVE );
 
798
                                $user->setEmail( $this->email );
 
799
                                $user->confirmEmail();
 
800
                        }
 
801
                        if ( ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
 
802
                                || ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) ) {
 
803
 
 
804
                                if ( '' != $this->externalid ) {
 
805
                                        $user->setExternalID( $this->externalid );
 
806
                                }
 
807
                        }
 
808
 
 
809
                        $saveSettings = true;
 
810
                }
 
811
 
 
812
                if ( isset( $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) && $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) {
 
813
                        $this->printDebug( "Setting user groups.", NONSENSITIVE );
 
814
                        $this->setGroups( $user );
 
815
 
 
816
                        $saveSettings = true;
 
817
                }
 
818
 
 
819
                if ( $saveSettings ) {
 
820
                        $this->printDebug( "Saving user settings.", NONSENSITIVE );
 
821
                        $user->saveSettings();
 
822
                }
 
823
        }
 
824
 
 
825
        /**
 
826
         * When creating a user account, initialize user with information from LDAP.
 
827
         *
 
828
         * @param User $user
 
829
         * @access public
 
830
         * TODO: fix setExternalID stuff
 
831
         */
 
832
        function initUser( &$user ) {
 
833
                global $wgLDAPUseLDAPGroups;
 
834
 
 
835
                $this->printDebug( "Entering initUser", NONSENSITIVE );
 
836
 
 
837
                if ($this->authFailed) {
 
838
                        $this->printDebug( "User didn't successfully authenticate, exiting.", NONSENSITIVE );
 
839
                        return;
 
840
                }
 
841
 
 
842
                if ( 'local' == $_SESSION['wsDomain'] ) {
 
843
                        $this->printDebug( "User is using a local domain", NONSENSITIVE );
 
844
                        return;
 
845
                }
 
846
 
 
847
                //We are creating an LDAP user, it is very important that we do
 
848
                //NOT set a local password because it could compromise the
 
849
                //security of our domain.
 
850
                $user->mPassword = '';
 
851
 
 
852
                //The update user function does everything else we need done.
 
853
                $this->updateUser($user);
 
854
 
 
855
                //updateUser() won't definately save the user's settings
 
856
                $user->saveSettings();
 
857
        }
 
858
 
 
859
        /**
 
860
         * Return true to prevent logins that don't authenticate here from being
 
861
         * checked against the local database's password fields.
 
862
         *
 
863
         * This is just a question, and shouldn't perform any actions.
 
864
         *
 
865
         * @return bool
 
866
         * @access public
 
867
         */
 
868
        function strict() {
 
869
                global $wgLDAPUseLocal, $wgLDAPMailPassword;
 
870
 
 
871
                $this->printDebug( "Entering strict.", NONSENSITIVE );
 
872
 
 
873
                if ( $wgLDAPUseLocal || $wgLDAPMailPassword ) {
 
874
                        $this->printDebug( "Returning false in strict().", NONSENSITIVE );
 
875
                        return false;
 
876
                } else {
 
877
                        $this->printDebug( "Returning true in strict().", NONSENSITIVE );
 
878
                        return true;
 
879
                }
 
880
        }
 
881
 
 
882
        /**
 
883
         * Munge the username to always have a form of uppercase for the first letter,
 
884
         * and lowercase for the rest of the letters.
 
885
         *
 
886
         * @param string $username
 
887
         * @return string
 
888
         * @access public
 
889
         */
 
890
        function getCanonicalName( $username ) {
 
891
                global $wgLDAPUseLocal;
 
892
                $this->printDebug( "Entering getCanonicalName", NONSENSITIVE );
 
893
 
 
894
                if ( $username != '' ) {
 
895
                        $this->printDebug( "Username isn't empty.", NONSENSITIVE );
 
896
 
 
897
                        //We want to use the username returned by LDAP
 
898
                        //if it exists
 
899
                        if ( $this->LDAPUsername != '' ) {
 
900
                                $this->printDebug( "Using LDAPUsername.", NONSENSITIVE );
 
901
                                $username = $this->LDAPUsername;
 
902
                        }
 
903
 
 
904
                        if ( isset($_SESSION['wsDomain']) && 'local' != $_SESSION['wsDomain']) {
 
905
                                //Change username to lowercase so that multiple user accounts
 
906
                                //won't be created for the same user.
 
907
                                //But don't do it for the local domain!
 
908
                                $username = strtolower( $username );
 
909
                        }
 
910
 
 
911
                        //The wiki considers an all lowercase name to be invalid; need to
 
912
                        //uppercase the first letter
 
913
                        $username[0] = strtoupper( $username[0] );
 
914
                }
 
915
 
 
916
                $this->printDebug( "Munged username: $username", NONSENSITIVE );
 
917
 
 
918
                return $username;
 
919
        }
 
920
 
 
921
        /**
 
922
         * Configures the authentication plugin for use with auto-authentication
 
923
         * plugins.
 
924
         *
 
925
         * @access public
 
926
         */
 
927
        function autoAuthSetup() {
 
928
                global $wgLDAPAutoAuthDomain;
 
929
 
 
930
                $this->setDomain( $wgLDAPAutoAuthDomain );
 
931
        }
 
932
 
 
933
        /**
 
934
         * Gets the searchstring for a user based upon settings for the domain.
 
935
         * Returns a full DN for a user.
 
936
         *
 
937
         * @param string $username
 
938
         * @return string
 
939
         * @access private
 
940
         */
 
941
        function getSearchString( $username ) {
 
942
                global $wgLDAPSearchStrings;
 
943
                global $wgLDAPProxyAgent, $wgLDAPProxyAgentPassword;
 
944
 
 
945
                $this->printDebug( "Entering getSearchString", NONSENSITIVE );
 
946
 
 
947
                if ( isset( $wgLDAPSearchStrings[$_SESSION['wsDomain']] ) ) {
 
948
                        //This is a straight bind
 
949
                        $this->printDebug( "Doing a straight bind", NONSENSITIVE );
 
950
 
 
951
                        $tmpuserdn = $wgLDAPSearchStrings[$_SESSION['wsDomain']];
 
952
                        $userdn = str_replace( "USER-NAME", $username, $tmpuserdn );
 
953
                } else {
 
954
                        //This is a proxy bind, or an anonymous bind with a search
 
955
                        if ( isset( $wgLDAPProxyAgent[$_SESSION['wsDomain']] ) ) {
 
956
                                //This is a proxy bind
 
957
                                $this->printDebug( "Doing a proxy bind", NONSENSITIVE );
 
958
                                $bind = $this->bindAs( $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
 
959
                        } else {
 
960
                                //This is an anonymous bind
 
961
                                $this->printDebug( "Doing an anonymous bind", NONSENSITIVE );
 
962
                                $bind = $this->bindAs();
 
963
                        }
 
964
        
 
965
                        if ( !$bind ) {
 
966
                                $this->printDebug( "Failed to bind", NONSENSITIVE );
 
967
                                return '';
 
968
                        }
 
969
 
 
970
                        $userdn = $this->getUserDN( $username );
 
971
                }
 
972
                $this->printDebug( "userdn is: $userdn", SENSITIVE );
 
973
                return $userdn;
 
974
        }
 
975
 
 
976
        /**
 
977
         * Gets the DN of a user based upon settings for the domain.
 
978
         * This function will set $this->LDAPUsername
 
979
         * You must bind to the server before calling this.
 
980
         *
 
981
         * @param string $username
 
982
         * @return string
 
983
         * @access private
 
984
         */
 
985
        function getUserDN( $username ) {
 
986
                global $wgLDAPSearchAttributes;
 
987
                global $wgLDAPAuthAttribute;
 
988
 
 
989
                $this->printDebug("Entering getUserDN", NONSENSITIVE);
 
990
 
 
991
                //we need to do a subbase search for the entry
 
992
 
 
993
                //Auto auth needs to check LDAP for required attributes.
 
994
                if ( ( isset( $wgLDAPAuthAttribute[$_SESSION['wsDomain']] ) )
 
995
                        && $this->useAutoAuth() ) {
 
996
                        $auth_filter = "(" . $wgLDAPAuthAttribute[$_SESSION['wsDomain']] . ")";
 
997
                        $srch_filter = "(" . $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=" . $this->getLdapEscapedString( $username ) . ")";
 
998
                        $filter = "(&" . $srch_filter . $auth_filter . ")";
 
999
                        $this->printDebug( "Created an auth attribute filter: $filter", SENSITIVE );
 
1000
                } else {
 
1001
                        $filter = "(" . $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=" . $this->getLdapEscapedString( $username ) . ")";
 
1002
                        $this->printDebug( "Created a regular filter: $filter", SENSITIVE );
 
1003
                }
 
1004
 
 
1005
                $attributes = array( "*" );
 
1006
                $base = $this->getBaseDN( USERDN );
 
1007
 
 
1008
                $this->printDebug( "Using base: $base", SENSITIVE );
 
1009
 
 
1010
                $entry = @ldap_search( $this->ldapconn, $base, $filter, $attributes );
 
1011
                if ( !$entry ) {
 
1012
                        $this->printDebug( "Couldn't find an entry", NONSENSITIVE );
 
1013
                        $this->fetchedUserInfo = false;
 
1014
                        return '';
 
1015
                }
 
1016
 
 
1017
                $this->userInfo = @ldap_get_entries( $this->ldapconn, $entry );
 
1018
                $this->fetchedUserInfo = true;
 
1019
 
 
1020
                //This is a pretty useful thing to have for auto authentication,
 
1021
                //group checking, and pulling preferences.
 
1022
                wfRunHooks( 'SetUsernameAttributeFromLDAP', array( &$this->LDAPUsername, $this->userInfo ) );
 
1023
                if ( !is_string( $this->LDAPUsername ) ) {
 
1024
                        $this->printDebug( "Fetched username is not a string (check your hook code...). This message can be safely ignored if you do not have the SetUsernameAttributeFromLDAP hook defined.", NONSENSITIVE );
 
1025
                        $this->LDAPUsername = '';
 
1026
                }
 
1027
 
 
1028
                $userdn = $this->userInfo[0]["dn"];
 
1029
                return $userdn;
 
1030
        }
 
1031
 
 
1032
        function getUserInfo() {
 
1033
                //Don't fetch the same data more than once
 
1034
                if ( $this->fetchedUserInfo ) {
 
1035
                        return $this->userInfo;
 
1036
                }
 
1037
 
 
1038
                $entry = @ldap_read( $this->ldapconn, $this->userdn, "objectclass=*" );
 
1039
                $userInfo = @ldap_get_entries( $this->ldapconn, $entry );
 
1040
                if ( $userInfo["count"] < 1 ) {
 
1041
                        $this->fetchedUserInfo = false;
 
1042
                        return;
 
1043
                } else {
 
1044
                        $this->fetchedUserInfo = true;
 
1045
                        return $userInfo;
 
1046
                }
 
1047
        }
 
1048
 
 
1049
        /**
 
1050
         * Retrieve user preferences from LDAP
 
1051
         *
 
1052
         * @param string $userDN
 
1053
         * @access private
 
1054
         */
 
1055
        function getPreferences() {
 
1056
                global $wgLDAPPreferences;
 
1057
                global $wgLDAPRetrievePrefs;
 
1058
 
 
1059
                $this->printDebug("Entering getPreferences", NONSENSITIVE);
 
1060
 
 
1061
                $this->userInfo = $this->getUserInfo();
 
1062
                if ( is_null( $this->userInfo ) ) {
 
1063
                        $this->printDebug("Failed to get preferences", NONSENSITIVE);
 
1064
                }
 
1065
 
 
1066
                //Retrieve preferences
 
1067
                if ( isset( $wgLDAPPreferences[$_SESSION['wsDomain']] ) ) {
 
1068
                        $this->printDebug( "Retrieving preferences", NONSENSITIVE );
 
1069
                        $prefs = $wgLDAPPreferences[$_SESSION['wsDomain']];
 
1070
                        foreach ( array_keys( $prefs ) as $key ) {
 
1071
                                switch ( $key ) {
 
1072
                                        case "email":
 
1073
                                                if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 
1074
                                                        $this->email = $this->userInfo[0]["$prefs[$key]"][0];
 
1075
                                                        $this->printDebug( "Retrieved email ($this->email) using attribute ($prefs[$key])", NONSENSITIVE );
 
1076
                                                }
 
1077
                                                break;
 
1078
                                        case "language":
 
1079
                                                if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 
1080
                                                        $this->lang = $this->userInfo[0][$prefs[$key]][0];
 
1081
                                                        $this->printDebug( "Retrieved language ($this->lang) using attribute ($prefs[$key])", NONSENSITIVE );
 
1082
                                                }
 
1083
                                                break;
 
1084
                                        case "nickname":
 
1085
                                                if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 
1086
                                                        $this->nickname = $this->userInfo[0]["$prefs[$key]"][0];
 
1087
                                                        $this->printDebug( "Retrieved nickname ($this->nickname) using attribute ($prefs[$key])", NONSENSITIVE );
 
1088
                                                }
 
1089
                                                break;
 
1090
                                        case "realname":
 
1091
                                                if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 
1092
                                                        $this->realname = $this->userInfo[0]["$prefs[$key]"][0];
 
1093
                                                        $this->printDebug( "Retrieved realname ($this->realname) using attribute ($prefs[$key])", NONSENSITIVE );
 
1094
                                                }
 
1095
                                                break;
 
1096
                                }
 
1097
                        }
 
1098
                } else if ( isset( $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) && $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) {
 
1099
                        //DEPRECATED. Kept for backwards compatibility.
 
1100
                        $this->printDebug( "Retrieving preferences", NONSENSITIVE );
 
1101
                        $this->printDebug( '$wgLDAPRetrievePrefs is a DEPRECATED option, please use $wgLDAPPreferences.', NONSENSITIVE );
 
1102
 
 
1103
                        if (isset($this->userInfo[0]["mail"])) {
 
1104
                                $this->email = $this->userInfo[0]["mail"][0];
 
1105
                        }
 
1106
                        if (isset($this->userInfo[0]["preferredlanguage"])) {
 
1107
                                $this->lang = $this->userInfo[0]["preferredlanguage"][0];
 
1108
                        }
 
1109
                        if (isset($this->userInfo[0]["displayname"])) {
 
1110
                                $this->nickname = $this->userInfo[0]["displayname"][0];
 
1111
                        }
 
1112
                        if (isset($this->userInfo[0]["cn"])) {
 
1113
                                $this->realname = $this->userInfo[0]["cn"][0];
 
1114
                        }
 
1115
 
 
1116
                        $this->printDebug( "Retrieved: $this->email, $this->lang, $this->nickname, $this->realname", SENSITIVE );
 
1117
                }
 
1118
        }
 
1119
 
 
1120
        function synchUsername( $username ) {
 
1121
                global $wgLDAPUniqueBlockLogin, $wgLDAPUniqueRenameUser;
 
1122
                global $wgLDAPUniqueAttribute;
 
1123
 
 
1124
                $this->printDebug("Entering synchUsername", NONSENSITIVE);
 
1125
 
 
1126
                $this->userInfo = $this->getUserInfo();
 
1127
                if ( is_null( $this->userInfo ) ) {
 
1128
                        $this->printDebug("Failed to get preferences", NONSENSITIVE);
 
1129
                }
 
1130
 
 
1131
                // Are we blocking login/renaming users on unique external ID mismatches?
 
1132
                //     *** WARNING ***
 
1133
                //     This needs to be fixed before use! This probably does not work correctly
 
1134
                //     with all options. It is probably a good idea to refactor the username stuff
 
1135
                //     in general (as it is currently somewhat of a kludge). Also, MediaWiki does
 
1136
                //     not currently have support for this.
 
1137
                //     *** WARNING ***
 
1138
                if ( ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
 
1139
                        || ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) ) {
 
1140
 
 
1141
                        $this->printDebug( "Checking for username change in LDAP.", SENSITIVE );
 
1142
 
 
1143
                        //Get the user's unique attribute from LDAP
 
1144
                        if ( isset( $wgLDAPUniqueAttribute[$_SESSION['wsDomain']] ) ) {
 
1145
                                $ldapuniqueattr = $wgLDAPUniqueAttribute[$_SESSION['wsDomain']];
 
1146
                                $this->externalid = $this->info[0][$ldapuniqueattr][0];
 
1147
                        } else {
 
1148
                                return false;
 
1149
                        }
 
1150
 
 
1151
                        $this->printDebug( "Retrieved external id: $this->externalid", SENSITIVE );
 
1152
 
 
1153
                        $retrievedusername = User::whoIsExternalID( "$this->externalid" );
 
1154
 
 
1155
                        $this->printDebug( "Username (in MediaWiki database) of fetched external id: $retrievedusername", SENSITIVE );
 
1156
 
 
1157
                        // See if the username returned from the database matches the username given
 
1158
                        if ( $retrievedusername != '' && ( $username != $retrievedusername ) ) {
 
1159
                                if ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
 
1160
                                        && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) {
 
1161
 
 
1162
                                        $this->printDebug( "Usernames do not match, blocking login.", SENSITIVE );
 
1163
                                        return false;
 
1164
                                } else if ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] )
 
1165
                                        && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) {
 
1166
 
 
1167
                                        $this->printDebug( "Usernames do not match, renaming user in database.", SENSITIVE );
 
1168
 
 
1169
                                        global $wgVersion;
 
1170
                                        if ( version_compare( $wgVersion, '1.7.0', '<' ) ) {
 
1171
                                                $this->printDebug( "Renaming users is only supported in MediaWiki 1.7+, please upgrade.", SENSITIVE );
 
1172
                                                $this->markAuthFailed();
 
1173
                                                return false;
 
1174
                                        }
 
1175
 
 
1176
                                        $olduser = User::newFromName( $retrievedusername );
 
1177
                                        $uid = $olduser->idForName();
 
1178
 
 
1179
                                        // Ensure we don't require the same class twice
 
1180
                                        if ( !class_exists( 'RenameuserSQL' ) ) {
 
1181
                                                require( 'Renameuser/SpecialRenameuser_body.php' );
 
1182
                                        }
 
1183
 
 
1184
                                        // Make a new rename user object with: from, to, uid of from    
 
1185
                                        $rename = new RenameuserSQL( $retrievedusername, $username, $uid );
 
1186
                                        $rename->rename();
 
1187
 
 
1188
                                        // For the time being we can't just allow the user to log in
 
1189
                                        // as MediaWiki will try to create the user account after we
 
1190
                                        // do a rename. If we don't return false, the user will get
 
1191
                                        // a database error
 
1192
                                        return false;
 
1193
                                }
 
1194
                        }
 
1195
 
 
1196
                        $this->printDebug( "Usernames matched or the user doesn't exist in the database yet.", SENSITIVE );
 
1197
                }
 
1198
 
 
1199
                return true;
 
1200
        }
 
1201
 
 
1202
        /**
 
1203
         * Checks to see whether a user is in a required group.
 
1204
         *
 
1205
         * @param string $username
 
1206
         * @return bool
 
1207
         * @access private
 
1208
         */
 
1209
        function checkGroups( $username ) {
 
1210
                global $wgLDAPGroupDN;
 
1211
                global $wgLDAPRequiredGroups, $wgLDAPExcludedGroups;
 
1212
 
 
1213
                $this->printDebug("Entering checkGroups", NONSENSITIVE);
 
1214
 
 
1215
                //Old style groups, non-nestable and fairly limited on group type (full DN
 
1216
                //versus username). DEPRECATED
 
1217
                if ( $wgLDAPGroupDN ) {
 
1218
                        $this->printDebug( "Checking for (old style) group membership", NONSENSITIVE );
 
1219
                        //we need to do a subbase search for the entry
 
1220
                        $filter = "(member=" . $this->getLdapEscapedString( $this->userdn ) . ")";
 
1221
                        $info = @ldap_get_entries( $this->ldapconn, @ldap_search( $this->ldapconn, $wgLDAPGroupDN, $filter ) );
 
1222
 
 
1223
                        return ( $info["count"] >= 1 );
 
1224
                }
 
1225
 
 
1226
                if ( isset( $wgLDAPExcludedGroups[$_SESSION['wsDomain']] ) ) {
 
1227
                        $this->printDebug( "Checking for excluded group membership", NONSENSITIVE );
 
1228
                        $excgroups = $wgLDAPExcludedGroups[$_SESSION['wsDomain']];
 
1229
                        for ( $i = 0; $i < count( $excgroups ); $i++ ) {
 
1230
                                $excgroups[$i] = strtolower( $excgroups[$i] );
 
1231
                        }
 
1232
 
 
1233
                        $this->printDebug( "Excluded groups:", NONSENSITIVE, $excgroups );
 
1234
 
 
1235
                        foreach ( $this->userLDAPGroups["dn"] as $group ) {
 
1236
                                $this->printDebug( "Checking against: $group", NONSENSITIVE );
 
1237
                                if ( in_array( $group, $excgroups ) ) {
 
1238
                                        $this->printDebug( "Found user in an excluded group.", NONSENSITIVE );
 
1239
                                        return false;
 
1240
                                }
 
1241
                        }
 
1242
                }
 
1243
 
 
1244
                //New style group checking
 
1245
                if ( isset( $wgLDAPRequiredGroups[$_SESSION['wsDomain']] ) ) {
 
1246
                        $this->printDebug( "Checking for (new style) group membership", NONSENSITIVE );
 
1247
                        $reqgroups = $wgLDAPRequiredGroups[$_SESSION['wsDomain']];
 
1248
                        for ( $i = 0; $i < count( $reqgroups ); $i++ ) {
 
1249
                                $reqgroups[$i] = strtolower( $reqgroups[$i] );
 
1250
                        }
 
1251
 
 
1252
                        $this->printDebug( "Required groups:", NONSENSITIVE, $reqgroups );
 
1253
 
 
1254
                        foreach ( $this->userLDAPGroups["dn"] as $group ) {
 
1255
                                $this->printDebug( "Checking against: $group", NONSENSITIVE );
 
1256
                                if ( in_array( $group, $reqgroups ) ) {
 
1257
                                        $this->printDebug( "Found user in a group.", NONSENSITIVE );
 
1258
                                        return true;
 
1259
                                }
 
1260
                        }
 
1261
 
 
1262
                        $this->printDebug("Couldn't find the user in any groups.", NONSENSITIVE );
 
1263
                        return false;
 
1264
                }
 
1265
 
 
1266
                // Ensure we return true if we aren't checking groups.
 
1267
                return true;
 
1268
        }
 
1269
 
 
1270
        /**
 
1271
         * Function to get the user's groups.
 
1272
         *
 
1273
         * @access private
 
1274
         */
 
1275
        function getGroups( $username ) {
 
1276
                global $wgLDAPRequiredGroups, $wgLDAPUseLDAPGroups;
 
1277
                global $wgLDAPGroupUseFullDN, $wgLDAPGroupUseRetrievedUsername;
 
1278
                global $wgLDAPGroupSearchNestedGroups;
 
1279
                global $wgLDAPGroupsPrevail;
 
1280
                global $wgLDAPGroupsUseMemberOf;
 
1281
 
 
1282
                $this->printDebug("Entering getGroups", NONSENSITIVE);
 
1283
 
 
1284
                //Find groups
 
1285
                if ( isset( $wgLDAPRequiredGroups[$_SESSION['wsDomain']] ) || ( isset( $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) && $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) ) {
 
1286
                        $this->printDebug( "Retrieving LDAP group membership", NONSENSITIVE );
 
1287
 
 
1288
                        //Let's figure out what we should be searching for
 
1289
                        if ( isset( $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) {
 
1290
                                $usertopass = $this->userdn;
 
1291
                        } else {
 
1292
                                if ( ( isset( $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] )
 
1293
                                        && $this->LDAPUsername != '' ) {
 
1294
 
 
1295
                                        $usertopass = $this->LDAPUsername;
 
1296
                                } else {
 
1297
                                        $usertopass = $username;
 
1298
                                }
 
1299
                        }
 
1300
 
 
1301
                        if ( isset( $wgLDAPGroupsUseMemberOf[$_SESSION['wsDomain']] ) && $wgLDAPGroupsUseMemberOf[$_SESSION['wsDomain']] ) {
 
1302
                                $this->printDebug( "Using memberOf", NONSENSITIVE );
 
1303
                                $this->userInfo = $this->getUserInfo();
 
1304
                                if ( is_null( $this->userInfo ) ) {
 
1305
                                        $this->printDebug("Failed to get memberOf attribute", NONSENSITIVE);
 
1306
                                }
 
1307
                                if ( isset( $this->userInfo[0]["memberof"] ) ) {
 
1308
                                        # The first entry is always a count
 
1309
                                        $memberOfMembers = $this->userInfo[0]["memberof"];
 
1310
                                        array_shift( $memberOfMembers );
 
1311
                                        $groups = array( "dn"=> array(), "short"=>array() );
 
1312
                                        foreach( $memberOfMembers as $mem ) {
 
1313
                                                array_push( $groups["dn"], strtolower( $mem ) );
 
1314
                                        }
 
1315
                                        $this->userLDAPGroups = $groups;
 
1316
                                }
 
1317
                        } else {
 
1318
                                $this->printDebug( "Searching for the groups", NONSENSITIVE );
 
1319
                                $this->userLDAPGroups = $this->searchGroups( $usertopass );
 
1320
 
 
1321
                                if ( isset( $wgLDAPGroupSearchNestedGroups[$_SESSION['wsDomain']] ) && $wgLDAPGroupSearchNestedGroups[$_SESSION['wsDomain']] ) {
 
1322
                                        $this->userLDAPGroups = $this->searchNestedGroups( $this->userLDAPGroups );
 
1323
                                        $this->printDebug( "Got the following nested groups:", SENSITIVE, $this->userLDAPGroups["dn"] );
 
1324
                                }
 
1325
                        }
 
1326
 
 
1327
                        //Only find all groups if the user has any groups; otherwise, we are
 
1328
                        //just wasting a search.
 
1329
                        if ( ( isset( $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && count( $this->userLDAPGroups ) != 0 ) {
 
1330
                                $this->allLDAPGroups = $this->searchGroups( '*' );
 
1331
                        }
 
1332
                }
 
1333
        }
 
1334
 
 
1335
        /**
 
1336
         * Function to return an array of nested groups when given a group or list of groups.
 
1337
         * $searchedgroups is used for tail recursion and shouldn't be provided
 
1338
         * when called externally.
 
1339
         *
 
1340
         * @param string $userDN
 
1341
         * @param array $searchedgroups
 
1342
         * @return bool
 
1343
         * @access private
 
1344
         */
 
1345
        function searchNestedGroups( $groups, $searchedgroups = array( "dn" => Array(), "short" => Array() ) ) {
 
1346
                $this->printDebug( "Entering searchNestedGroups", NONSENSITIVE );
 
1347
 
 
1348
                //base case, no more groups left to check
 
1349
                if ( count( $groups["dn"] ) == 0 ) {
 
1350
                        $this->printDebug( "No more groups to search.", NONSENSITIVE );
 
1351
                        return $searchedgroups;
 
1352
                }
 
1353
 
 
1354
                $this->printDebug( "Searching groups:", SENSITIVE, $groups["dn"] );
 
1355
 
 
1356
                $groupstosearch = array( "short"=>array(), "dn"=>array() );
 
1357
                foreach ( $groups["dn"] as $group ) {
 
1358
                        $returnedgroups = $this->searchGroups( $group );
 
1359
                        $this->printDebug( "Group $group is in the following groups:", SENSITIVE, $returnedgroups["dn"] );
 
1360
                        foreach ( $returnedgroups["dn"] as $searchme ) {
 
1361
                                if ( in_array( $searchme, $searchedgroups["dn"] ) ) {
 
1362
                                        //We already searched this, move on
 
1363
                                        continue;
 
1364
                                } else {
 
1365
                                        //We'll need to search this group's members now
 
1366
                                        $this->printDebug( "Adding $searchme to the list of groups (1)", SENSITIVE );
 
1367
                                        $groupstosearch["dn"][] = $searchme;
 
1368
                                }
 
1369
                        }
 
1370
                        foreach ( $returnedgroups["short"] as $searchme ) {
 
1371
                                if ( in_array( $searchme, $searchedgroups["short"] ) ) {
 
1372
                                        //We already searched this, move on
 
1373
                                        continue;
 
1374
                                } else {
 
1375
                                        $this->printDebug( "Adding $searchme to the list of groups (2)", SENSITIVE );
 
1376
                                        //We'll need to search this group's members now
 
1377
                                        $groupstosearch["short"][] = $searchme;
 
1378
                                }
 
1379
                        }
 
1380
                }
 
1381
 
 
1382
                $searchedgroups = array_merge_recursive( $groups, $searchedgroups );
 
1383
 
 
1384
                //Mmmmmm. Tail recursion. Tasty.
 
1385
                return $this->searchNestedGroups( $groupstosearch, $searchedgroups ); 
 
1386
        }
 
1387
 
 
1388
        /**
 
1389
         * Search groups for the supplied DN
 
1390
         *
 
1391
         * @param string $dn
 
1392
         * @return array
 
1393
         * @access private
 
1394
         */
 
1395
        function searchGroups( $dn ) {
 
1396
                global $wgLDAPGroupObjectclass, $wgLDAPGroupAttribute, $wgLDAPGroupNameAttribute;
 
1397
                global $wgLDAPProxyAgent, $wgLDAPProxyAgentPassword;
 
1398
                global $wgUser;
 
1399
 
 
1400
                $this->printDebug( "Entering searchGroups", NONSENSITIVE );
 
1401
 
 
1402
                $base = $this->getBaseDN( GROUPDN );
 
1403
 
 
1404
                $objectclass = $wgLDAPGroupObjectclass[$_SESSION['wsDomain']];
 
1405
                $attribute = $wgLDAPGroupAttribute[$_SESSION['wsDomain']];
 
1406
                $nameattribute = $wgLDAPGroupNameAttribute[$_SESSION['wsDomain']];
 
1407
 
 
1408
                // We actually want to search for * not \2a
 
1409
                $value = $dn;
 
1410
                if ( $value != "*" )
 
1411
                        $value = $this->getLdapEscapedString( $value );
 
1412
 
 
1413
                $filter = "(&($attribute=$value)(objectclass=$objectclass))";
 
1414
 
 
1415
                $this->printDebug( "Search string: $filter", SENSITIVE );
 
1416
 
 
1417
                if ( isset( $wgLDAPProxyAgent[$_SESSION['wsDomain']] ) ) {
 
1418
                        //We'll try to bind as the proxyagent as the proxyagent should normally have more
 
1419
                        //rights than the user. If the proxyagent fails to bind, we will still be able
 
1420
                        //to search as the normal user (which is why we don't return on fail).
 
1421
                        $this->printDebug( "Binding as the proxyagent", NONSENSITIVE );
 
1422
                        $bind = $this->bindAs( $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
 
1423
                }
 
1424
 
 
1425
                $info = @ldap_search( $this->ldapconn, $base, $filter );
 
1426
                #if ( $info["count"] < 1 ) {
 
1427
                if ( !$info ) {
 
1428
                        $this->printDebug( "No entries returned from search.", SENSITIVE );
 
1429
 
 
1430
                        //Return an array so that other functions
 
1431
                        //don't error out.
 
1432
                        return array( "short"=>array(), "dn"=>array() );
 
1433
                }
 
1434
 
 
1435
                $entries = @ldap_get_entries( $this->ldapconn, $info );
 
1436
 
 
1437
                //We need to shift because the first entry will be a count
 
1438
                array_shift( $entries );
 
1439
 
 
1440
                //Let's get a list of both full dn groups and shortname groups
 
1441
                $groups = array( "short"=>array(), "dn"=>array() );
 
1442
                foreach ( $entries as $entry ) {
 
1443
                        $shortMember = strtolower( $entry[$nameattribute][0] );
 
1444
                        $dnMember = strtolower( $entry['dn'] );
 
1445
                        $groups["short"][] = $shortMember;
 
1446
                        $groups["dn"][] = $dnMember;
 
1447
                }
 
1448
 
 
1449
                $this->printDebug( "Returned groups:", SENSITIVE, $groups["dn"] );
 
1450
 
 
1451
                return $groups;
 
1452
        }
 
1453
 
 
1454
        /**
 
1455
         * Returns true if this group is in the list of the currently authenticated
 
1456
         * user's groups, else false.
 
1457
         *
 
1458
         * @param string $group
 
1459
         * @return bool
 
1460
         * @access private
 
1461
         */
 
1462
        function hasLDAPGroup( $group ) {
 
1463
                $this->printDebug( "Entering hasLDAPGroup", NONSENSITIVE );
 
1464
 
 
1465
                return in_array( strtolower( $group ), $this->userLDAPGroups["short"] );
 
1466
        }
 
1467
 
 
1468
        /**
 
1469
         * Returns true if an LDAP group with this name exists, else false.
 
1470
         *
 
1471
         * @param string $group
 
1472
         * @return bool
 
1473
         * @access private
 
1474
         */
 
1475
        function isLDAPGroup( $group ) {
 
1476
                $this->printDebug( "Entering isLDAPGroup", NONSENSITIVE );
 
1477
 
 
1478
                return in_array( strtolower( $group ), $this->allLDAPGroups["short"] );
 
1479
        }
 
1480
 
 
1481
        /**
 
1482
         * Helper function for updateUser() and initUser(). Adds users into MediaWiki security groups
 
1483
         * based upon groups retreived from LDAP.
 
1484
         *
 
1485
         * @param User $user
 
1486
         * @access private
 
1487
         */
 
1488
        function setGroups( &$user ) {
 
1489
                global $wgLDAPGroupsPrevail, $wgGroupPermissions;
 
1490
                global $wgLDAPLocallyManagedGroups;
 
1491
 
 
1492
                //TODO: this is *really* ugly code. clean it up!
 
1493
 
 
1494
                $this->printDebug( "Entering setGroups.", NONSENSITIVE );
 
1495
 
 
1496
                # add groups permissions
 
1497
                $localAvailGrps = $user->getAllGroups();
 
1498
                $localUserGrps = $user->getEffectiveGroups();
 
1499
                
 
1500
                $defaultLocallyManagedGrps = array( 'bot', 'sysop', 'bureaucrat' );
 
1501
 
 
1502
                if ( isset( $wgLDAPLocallyManagedGroups[$_SESSION['wsDomain']] ) ) {
 
1503
                        $locallyManagedGrps = $wgLDAPLocallyManagedGroups[$_SESSION['wsDomain']];
 
1504
                        $locallyManagedGrps = array_unique( array_merge( $defaultLocallyManagedGrps, $locallyManagedGrps ) );           
 
1505
                        $this->printDebug( "Locally managed groups: ", SENSITIVE, $locallyManagedGrps );
 
1506
                } else {
 
1507
                        $locallyManagedGrps = $defaultLocallyManagedGrps;
 
1508
                        $this->printDebug( "Locally managed groups is unset, using defaults: ", SENSITIVE, $locallyManagedGrps );
 
1509
                }
 
1510
                        
 
1511
 
 
1512
                # Add ldap groups as local groups
 
1513
                if ( isset( $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) {
 
1514
                        $this->printDebug( "Adding all groups to wgGroupPermissions: ", SENSITIVE, $this->allLDAPGroups );
 
1515
                        foreach ( $this->allLDAPGroups["short"] as $ldapgroup )
 
1516
                                if ( !array_key_exists( $ldapgroup, $wgGroupPermissions ) )
 
1517
                                        $wgGroupPermissions[$ldapgroup] = array();
 
1518
                }
 
1519
 
 
1520
                $this->printDebug( "Available groups are: ", NONSENSITIVE, $localAvailGrps );
 
1521
                $this->printDebug( "Effective groups are: ", NONSENSITIVE, $localUserGrps );
 
1522
 
 
1523
                # note: $localUserGrps does not need to be updated with $cGroup added,
 
1524
                #       as $localAvailGrps contains $cGroup only once.
 
1525
                foreach ( $localAvailGrps as $cGroup ) {
 
1526
                        # did we once add the user to the group?
 
1527
                        if ( in_array( $cGroup,$localUserGrps ) ) {
 
1528
                                $this->printDebug( "Checking to see if we need to remove user from: $cGroup", NONSENSITIVE );
 
1529
                                if ( ( !$this->hasLDAPGroup( $cGroup ) ) && ( !in_array( $cGroup, $locallyManagedGrps ) ) ) {
 
1530
                                        $this->printDebug( "Removing user from: $cGroup", NONSENSITIVE );
 
1531
                                        # the ldap group overrides the local group
 
1532
                                        # so as the user is currently not a member of the ldap group, he shall be removed from the local group
 
1533
                                        $user->removeGroup( $cGroup );
 
1534
                                }
 
1535
                        } else { # no, but maybe the user has recently been added to the ldap group?
 
1536
                                $this->printDebug( "Checking to see if user is in: $cGroup", NONSENSITIVE );
 
1537
                                if ( $this->hasLDAPGroup( $cGroup ) ) {
 
1538
                                        $this->printDebug( "Adding user to: $cGroup", NONSENSITIVE );
 
1539
                                        $user->addGroup( $cGroup );
 
1540
                                }
 
1541
                        }
 
1542
                }
 
1543
        }
 
1544
 
 
1545
        /**
 
1546
         * Returns a password that is created via the configured hash settings.
 
1547
         *
 
1548
         * @param string $password
 
1549
         * @return string
 
1550
         * @access private
 
1551
         */
 
1552
        function getPasswordHash( $password ) {
 
1553
                global $wgLDAPPasswordHash;
 
1554
 
 
1555
                $this->printDebug( "Entering getPasswordHash", NONSENSITIVE );
 
1556
 
 
1557
                if ( isset( $wgLDAPPasswordHash[$_SESSION['wsDomain']] ) ) {
 
1558
                        $hashtouse = $wgLDAPPasswordHash[$_SESSION['wsDomain']];
 
1559
                } else {
 
1560
                        $hashtouse = '';
 
1561
                }
 
1562
 
 
1563
                //Set the password hashing based upon admin preference
 
1564
                switch ( $hashtouse ) {
 
1565
                        case 'crypt':
 
1566
                                $pass = '{CRYPT}' . crypt( $password );
 
1567
                                break;
 
1568
                        case 'clear':
 
1569
                                $pass = $password;
 
1570
                                break;
 
1571
                        default:
 
1572
                                $pwd_sha = base64_encode( pack( 'H*',sha1( $password ) ) );
 
1573
                                $pass = "{SHA}".$pwd_sha;
 
1574
                                break;
 
1575
                }
 
1576
 
 
1577
                $this->printDebug( "Password is $pass", HIGHLYSENSITIVE );
 
1578
                return $pass;
 
1579
        }
 
1580
 
 
1581
        /**
 
1582
         * Prints debugging information. $debugText is what you want to print, $debugVal
 
1583
         * is the level at which you want to print the information.
 
1584
         *
 
1585
         * @param string $debugText
 
1586
         * @param string $debugVal
 
1587
         * @access private
 
1588
         */
 
1589
        function printDebug( $debugText, $debugVal, $debugArr = null ) {
 
1590
                global $wgLDAPDebug;
 
1591
 
 
1592
                if ( isset( $debugArr ) ) {
 
1593
                        if ( $wgLDAPDebug > $debugVal ) {
 
1594
                                $text = $debugText . " " . implode( "::", $debugArr );
 
1595
                                wfDebugLog( 'ldap', $text, false );
 
1596
                        }
 
1597
                } else {
 
1598
                        if ( $wgLDAPDebug > $debugVal ) {
 
1599
                                wfDebugLog( 'ldap', $debugText, false );
 
1600
                        }
 
1601
                }
 
1602
        }
 
1603
 
 
1604
        /**
 
1605
         * Binds as $userdn with $password. This can be called with only the ldap
 
1606
         * connection resource for an anonymous bind.
 
1607
         *
 
1608
         * @param string $userdn
 
1609
         * @param string $password
 
1610
         * @return bool
 
1611
         * @access private
 
1612
         */
 
1613
        function bindAs( $userdn=null, $password=null ) {
 
1614
                //Let's see if the user can authenticate.
 
1615
                if ( $userdn == null || $password == null ) {
 
1616
                        $bind = @ldap_bind( $this->ldapconn );
 
1617
                } else {
 
1618
                        $bind = @ldap_bind( $this->ldapconn, $userdn, $password );
 
1619
                }
 
1620
                if ( !$bind ) {
 
1621
                        $this->printDebug( "Failed to bind as $userdn", NONSENSITIVE );
 
1622
                        $this->printDebug( "with password: $password", HIGHLYSENSITIVE );
 
1623
                        return false;
 
1624
                }
 
1625
                return true;
 
1626
        }
 
1627
 
 
1628
        /**
 
1629
         * Returns true if auto-authentication is allowed, and the user is
 
1630
         * authenticating using the auto-authentication domain.
 
1631
         *
 
1632
         * @return bool
 
1633
         * @access private
 
1634
         */
 
1635
        function useAutoAuth() {
 
1636
                global $wgLDAPAutoAuthDomain;
 
1637
 
 
1638
                $this->printDebug( "", NONSENSITIVE );
 
1639
                return isset( $wgLDAPAutoAuthDomain ) && $_SESSION['wsDomain'] == $wgLDAPAutoAuthDomain;
 
1640
        }
 
1641
 
 
1642
        /**
 
1643
         * Returns a string which has the chars *, (, ), \ & NUL escaped to LDAP compliant
 
1644
         * syntax as per RFC 2254
 
1645
         * Thanks and credit to Iain Colledge for the research and function.
 
1646
         * 
 
1647
         * @param string $string
 
1648
         * @return string
 
1649
         * @access private
 
1650
         */
 
1651
        function getLdapEscapedString ( $string ) {
 
1652
                // Make the string LDAP compliant by escaping *, (, ) , \ & NUL
 
1653
                return str_replace(
 
1654
                        array( "*", "(", ")", "\\", "\x00" ), //replace this
 
1655
                        array( "\\2a", "\\28", "\\29", "\\5c", "\\00" ), //with this
 
1656
                        $string //in this
 
1657
                        );
 
1658
        }
 
1659
 
 
1660
        /**
 
1661
         * Returns a basedn by the type of entry we are searching for.
 
1662
         * 
 
1663
         * @param int $type
 
1664
         * @return string
 
1665
         * @access private
 
1666
         */
 
1667
        function getBaseDN ( $type ) {
 
1668
                global $wgLDAPBaseDNs, $wgLDAPGroupBaseDNs, $wgLDAPUserBaseDNs;
 
1669
 
 
1670
                $this->printDebug( "Entering getBaseDN", NONSENSITIVE );
 
1671
 
 
1672
                $ret = '';
 
1673
                switch( $type ) {
 
1674
                        case USERDN:
 
1675
                                if ( isset( $wgLDAPUserBaseDNs[$_SESSION['wsDomain']] ) ) {
 
1676
                                        $ret = $wgLDAPUserBaseDNs[$_SESSION['wsDomain']];
 
1677
                                }
 
1678
                                break;
 
1679
                        case GROUPDN:
 
1680
                                if ( isset( $wgLDAPGroupBaseDNs[$_SESSION['wsDomain']] ) ) {
 
1681
                                        $ret =  $wgLDAPGroupBaseDNs[$_SESSION['wsDomain']];
 
1682
                                }
 
1683
                                break;
 
1684
                        case DEFAULTDN:
 
1685
                                if ( isset( $wgLDAPBaseDNs[$_SESSION['wsDomain']] ) ) {
 
1686
                                        $ret = $wgLDAPBaseDNs[$_SESSION['wsDomain']];
 
1687
                                        $this->printDebug( "basedn is $ret", NONSENSITIVE );
 
1688
                                        return $ret;
 
1689
                                } else {
 
1690
                                        $this->printDebug( "basedn is not set.", NONSENSITIVE );
 
1691
                                        return '';
 
1692
                                }
 
1693
                                break;
 
1694
                }
 
1695
 
 
1696
                if ( $ret == '' ) {
 
1697
                        $this->printDebug( "basedn is not set for this type of entry, trying to get the default basedn.", NONSENSITIVE );
 
1698
                        // We will never reach here if $type is self::DEFAULTDN, so to avoid code
 
1699
                        // code duplication, we'll get the default by re-calling the function.
 
1700
                        return $this->getBaseDN( DEFAULTDN );
 
1701
                } else {
 
1702
                        $this->printDebug( "basedn is $ret", NONSENSITIVE );
 
1703
                        return $ret;
 
1704
                }
 
1705
        }
 
1706
 
 
1707
}
 
1708
 
 
1709
// The following was derived from the SSL Authentication plugin
 
1710
// http://www.mediawiki.org/wiki/SSL_authentication
 
1711
 
 
1712
/**
 
1713
 * Sets up the SSL authentication piece of the LDAP plugin.
 
1714
 *
 
1715
 * @access public
 
1716
 */
 
1717
function AutoAuthSetup() {
 
1718
        global $wgLDAPAutoAuthUsername;
 
1719
        global $wgLDAPSSLUsername;
 
1720
        global $wgLDAPAutoAuthDomain;
 
1721
        global $wgLDAPSmartcardDomain;
 
1722
        global $wgHooks;
 
1723
        global $wgAuth;
 
1724
 
 
1725
        $wgAuth = new LdapAuthenticationPlugin();
 
1726
 
 
1727
        $wgAuth->printDebug( "Entering AutoAuthSetup.", NONSENSITIVE );
 
1728
 
 
1729
        //Set configuration options for backwards compatibility
 
1730
        if ( isset( $wgLDAPSSLUsername ) ) {
 
1731
                $wgAuth->printDebug( 'Setting $wgLDAPAutoAuthUsername to $wgLDAPSSLUsername; please change your configuration to fix this deprecated configuration variable.', NONSENSITIVE );
 
1732
                $wgLDAPAutoAuthUsername = $wgLDAPSSLUsername;
 
1733
        }
 
1734
        if ( isset( $wgLDAPSmartcardDomain ) ) {
 
1735
                $wgAuth->printDebug( 'Setting $wgLDAPAutoAuthDomain to $wgLDAPSmartcardDomain; please change your configuration to fix this deprecated configuration variable.', NONSENSITIVE );
 
1736
                $wgLDAPAutoAuthDomain = $wgLDAPSmartcardDomain;
 
1737
        }
 
1738
 
 
1739
        if( $wgLDAPAutoAuthUsername != null ) {
 
1740
                $wgAuth->printDebug( "wgLDAPAutoAuthUsername is not null, adding hooks.", NONSENSITIVE );
 
1741
                if ( version_compare( $wgVersion, '1.14.0', '<' ) ) {
 
1742
                        $wgHooks['UserLoadFromSession'][] = 'LdapAutoAuthentication::Authenticate';
 
1743
                } else {
 
1744
                        $wgHooks['UserLoadAfterLoadFromSession'][] = 'LdapAutoAuthentication::Authenticate';
 
1745
                }
 
1746
                $wgHooks['PersonalUrls'][] = 'LdapAutoAuthentication::NoLogout'; /* Disallow logout link */
 
1747
        }
 
1748
}