r44299 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r44298‎ | r44299 | r44300 >
Date:03:32, 8 December 2008
Author:laner
Status:deferred
Tags:
Comment:
* Rewrote most of the group code. Should be slightly more efficient at some tasks, and slightly less efficient at others.
** Added nested group support for group synchronization
** Added memberOf support
*** There is a minor issue with this: active directory is somewhat stupid, and it is extremely difficult to find a user's primary group. memberOf doesn't list primary groups, only secondary groups. As such, I'm not going to support primary groups in memberOf. You can ask me to support it, but my answer will be: I'll take a patch. Seriously, I think everyone should go see what you have to do to find a user's primary group, it is rediculous.
*** The variable for this is $wgLDAPGroupsUseMemberOf
* Changed the way debug logs print out; it will print to a log specified via: $wgDebugLogGroups["ldap"] = '/path/to/file.log'; It will not print anywhere else.
* Upped the version to 1.2a (alpha)
* Lots of code refactoring

This is a somewhat alpha release. Please don't use this in a production environment; wait for the real release. I definitely welcome feedback, since this is a fairly large change.
Modified paths:
  • /trunk/extensions/LdapAuthentication/LdapAuthentication.php (modified) (history)

Diff [purge]

Index: trunk/extensions/LdapAuthentication/LdapAuthentication.php
@@ -46,7 +46,7 @@
4747 */
4848 $wgExtensionCredits['other'][] = array(
4949 'name' => 'LDAP Authentication Plugin',
50 - 'version' => '1.2a (beta)',
 50+ 'version' => '1.2b (alpha)',
5151 'author' => 'Ryan Lane',
5252 'description' => 'LDAP Authentication plugin with support for multiple LDAP authentication methods',
5353 'url' => 'http://www.mediawiki.org/wiki/Extension:LDAP_Authentication',
@@ -64,19 +64,31 @@
6565
6666 class LdapAuthenticationPlugin extends AuthPlugin {
6767
 68+ //ldap connection resource
 69+ var $ldapconn;
 70+
6871 //preferences
6972 var $email, $lang, $realname, $nickname, $externalid;
7073
7174 //username pulled from ldap
7275 var $LDAPUsername;
7376
 77+ //userdn pulled from ldap
 78+ var $userdn;
 79+
7480 //groups pulled from ldap
75 - var $userLDAPGroups, $foundUserLDAPGroups;
 81+ var $userLDAPGroups;
7682 var $allLDAPGroups;
7783
7884 //boolean to test for failed auth
7985 var $authFailed;
8086
 87+ //boolean to test for fetched user info
 88+ var $fetchedUserInfo;
 89+
 90+ //the user's entry and all attributes
 91+ var $userInfo;
 92+
8193 function LdapAuthenticationPlugin() {
8294 }
8395
@@ -103,11 +115,11 @@
104116 return true;
105117 }
106118
107 - $ldapconn = $this->connect();
108 - if ( $ldapconn ) {
 119+ $this->ldapconn = $this->connect();
 120+ if ( $this->ldapconn ) {
109121 $this->printDebug( "Successfully connected", NONSENSITIVE );
110122
111 - $searchstring = $this->getSearchString( $ldapconn, $username );
 123+ $searchstring = $this->getSearchString( $this->ldapconn, $username );
112124
113125 //If we are using auto authentication, and we got
114126 //anything back, then the user exists.
@@ -119,7 +131,7 @@
120132 }
121133
122134 //Search for the entry.
123 - $entry = @ldap_read( $ldapconn, $searchstring, "objectclass=*" );
 135+ $entry = @ldap_read( $this->ldapconn, $searchstring, "objectclass=*" );
124136
125137 //getSearchString is going to bind, but will not unbind
126138 //Let's clean up
@@ -141,7 +153,6 @@
142154 /**
143155 * Connect to LDAP
144156 *
145 - * @return resource
146157 * @access private
147158 */
148159 function connect() {
@@ -194,14 +205,14 @@
195206 $this->printDebug( "Using servers: $servers", SENSITIVE );
196207
197208 //Connect and set options
198 - $ldapconn = @ldap_connect( $servers );
199 - ldap_set_option( $ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
200 - ldap_set_option( $ldapconn, LDAP_OPT_REFERRALS, 0);
 209+ $this->ldapconn = @ldap_connect( $servers );
 210+ ldap_set_option( $this->ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
 211+ ldap_set_option( $this->ldapconn, LDAP_OPT_REFERRALS, 0);
201212
202213 if ( isset( $wgLDAPOptions[$_SESSION['wsDomain']] ) ) {
203214 $options = $wgLDAPOptions[$_SESSION['wsDomain']];
204215 foreach ( $options as $key => $value ) {
205 - if ( !ldap_set_option( $ldapconn, constant( $key ), $value ) ) {
 216+ if ( !ldap_set_option( $this->ldapconn, constant( $key ), $value ) ) {
206217 $this->printDebug( "Can't set option to LDAP! Option code and value: " . $key . "=" . $value, 1 );
207218 }
208219 }
@@ -210,13 +221,11 @@
211222 //TLS needs to be started after the connection is made
212223 if ( $encryptionType == "tls" ) {
213224 $this->printDebug( "Using TLS", SENSITIVE );
214 - if ( !ldap_start_tls( $ldapconn ) ) {
 225+ if ( !ldap_start_tls( $this->ldapconn ) ) {
215226 $this->printDebug( "Failed to start TLS.", SENSITIVE );
216227 return;
217228 }
218229 }
219 -
220 - return $ldapconn;
221230 }
222231
223232 /**
@@ -232,23 +241,16 @@
233242 * @access public
234243 */
235244 function authenticate( $username, $password='' ) {
236 - global $wgLDAPRetrievePrefs, $wgLDAPPreferences;
237 - global $wgLDAPGroupDN, $wgLDAPRequiredGroups;
238 - global $wgLDAPGroupUseFullDN, $wgLDAPGroupUseRetrievedUsername;
239 - global $wgLDAPUseLDAPGroups;
240 - global $wgLDAPRequireAuthAttribute, $wgLDAPAuthAttribute;
 245+ global $wgLDAPAuthAttribute;
241246 global $wgLDAPAutoAuthUsername;
242247 global $wgLDAPLowerCaseUsername;
243248 global $wgLDAPSearchStrings;
244 - global $wgLDAPUniqueAttribute, $wgLDAPUniqueBlockLogin, $wgLDAPUniqueRenameUser;
245 - global $wgLDAPGroupsPrevail;
246249
247250 $this->printDebug( "Entering authenticate", NONSENSITIVE );
248251
249252 //We don't handle local authentication
250253 if ( 'local' == $_SESSION['wsDomain'] ) {
251254 $this->printDebug( "User is using a local domain", SENSITIVE );
252 - $this->cleanupFailedAuth();
253255 return false;
254256 }
255257
@@ -257,7 +259,6 @@
258260 //than the one the web server got from the auto-authentication method.
259261 if ( $this->useAutoAuth() && $wgLDAPAutoAuthUsername != $username ) {
260262 $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 );
261 - $this->cleanupFailedAuth();
262263 return false;
263264 }
264265
@@ -268,13 +269,11 @@
269270 //a password to be given; a blank password here is wanted.
270271 if ( '' == $password && !$this->useAutoAuth() ) {
271272 $this->printDebug( "User used a blank password", NONSENSITIVE );
272 - $this->cleanupFailedAuth();
273273 return false;
274274 }
275275
276 - $ldapconn = $this->connect();
277 - //This seems really expensive.
278 - if ( $ldapconn ) {
 276+ $this->connect();
 277+ if ( $this->ldapconn ) {
279278 $this->printDebug( "Connected successfully", NONSENSITIVE );
280279
281280 //Mediawiki munges the username before authenticate is called,
@@ -286,16 +285,15 @@
287286 $username = strtolower( $username );
288287 }
289288
290 - $userdn = $this->getSearchString( $ldapconn, $username );
 289+ $this->userdn = $this->getSearchString( $username );
291290
292291 //It is possible that getSearchString will return an
293292 //empty string; if this happens, the bind will ALWAYS
294293 //return true, and will let anyone in!
295 - if ( '' == $userdn ) {
 294+ if ( '' == $this->userdn ) {
296295 $this->printDebug( "User DN is blank", NONSENSITIVE );
297 - // Lets clean up.
298296 @ldap_unbind();
299 - $this->cleanupFailedAuth();
 297+ $this->markAuthFailed();
300298 return false;
301299 }
302300
@@ -305,13 +303,12 @@
306304 $this->printDebug( "Binding as the user", NONSENSITIVE );
307305
308306 //Let's see if the user can authenticate.
309 - $bind = $this->bindAs( $ldapconn, $userdn, $password );
 307+ $bind = $this->bindAs( $this->userdn, $password );
310308 if ( !$bind ) {
311 - // Lets clean up.
312 - @ldap_unbind();
313 - $this->cleanupFailedAuth();
 309+ $this->markAuthFailed();
314310 return false;
315311 }
 312+
316313 $this->printDebug( "Bound successfully", NONSENSITIVE );
317314
318315 if ( isset( $wgLDAPSearchStrings[$_SESSION['wsDomain']] ) ) {
@@ -320,227 +317,50 @@
321318 //We are most likely configured using USER-NAME@DOMAIN, or
322319 //DOMAIN\\USER-NAME.
323320 //Get the user's full DN so we can search for groups and such.
324 - $userdn = $this->getUserDN( $ldapconn, $username );
325 - $this->printDebug( "Pulled the user's DN: $userdn", NONSENSITIVE );
 321+ $this->userdn = $this->getUserDN( $username );
 322+ $this->printDebug( "Pulled the user's DN: $this->userdn", NONSENSITIVE );
326323 }
327324 }
328325
329 - if ( ( isset( $wgLDAPRequireAuthAttribute[$_SESSION['wsDomain']] )
330 - && $wgLDAPRequireAuthAttribute[$_SESSION['wsDomain']] ) ) {
 326+ if ( isset( $wgLDAPAuthAttribute[$_SESSION['wsDomain']] ) ) {
331327
332328 $this->printDebug( "Checking for auth attributes", NONSENSITIVE );
333329
334330 $filter = "(" . $wgLDAPAuthAttribute[$_SESSION['wsDomain']] . ")";
335331 $attributes = array( "dn" );
336332
337 - $entry = ldap_read( $ldapconn, $userdn, $filter, $attributes );
338 - $info = ldap_get_entries( $ldapconn, $entry );
 333+ $entry = ldap_read( $this->ldapconn, $this->userdn, $filter, $attributes );
 334+ $info = ldap_get_entries( $this->ldapconn, $entry );
339335
340336 if ( $info["count"] < 1 ) {
341337 $this->printDebug( "Failed auth attribute check", NONSENSITIVE );
342 - // Lets clean up.
343338 @ldap_unbind();
344 - $this->cleanupFailedAuth();
 339+ $this->markAuthFailed();
345340 return false;
346341 }
347342 }
348343 }
349344
350 - //Old style groups, non-nestable and fairly limited on group type (full DN
351 - //versus username). DEPRECATED
352 - if ( $wgLDAPGroupDN ) {
353 - $this->printDebug( "Checking for (old style) group membership", NONSENSITIVE );
354 - if ( !$this->isMemberOfLdapGroup( $ldapconn, $userdn, $wgLDAPGroupDN ) ) {
355 - $this->printDebug( "Failed (old style) group membership check", NONSENSITIVE );
 345+ $this->getGroups( $username );
356346
357 - //No point in going on if the user isn't in the required group
358 - // Lets clean up.
359 - @ldap_unbind();
360 - $this->cleanupFailedAuth();
361 - return false;
362 - }
 347+ if ( !$this->checkGroups( $username ) ) {
 348+ @ldap_unbind();
 349+ $this->markAuthFailed();
 350+ return false;
363351 }
364352
365 - //New style group checking
366 - if ( isset( $wgLDAPRequiredGroups[$_SESSION['wsDomain']] ) ) {
367 - $this->printDebug( "Checking for (new style) group membership", NONSENSITIVE );
 353+ $this->getPreferences();
368354
369 - if ( isset( $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) {
370 - $inGroup = $this->isMemberOfRequiredLdapGroup( $ldapconn, $userdn );
371 - } else {
372 - if ( ( isset( $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] )
373 - && $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] )
374 - && $this->LDAPUsername != '' ) {
375 -
376 - $this->printDebug( "Using the username retrieved from the user's entry.", NONSENSITIVE );
377 - $inGroup = $this->isMemberOfRequiredLdapGroup( $ldapconn, $this->LDAPUsername );
378 - } else {
379 - $inGroup = $this->isMemberOfRequiredLdapGroup( $ldapconn, $username );
380 - }
381 - }
382 -
383 - if ( !$inGroup ) {
384 - // Lets clean up.
385 - @ldap_unbind();
386 - $this->cleanupFailedAuth();
387 - return false;
388 - }
389 -
 355+ if ( !$this->synchUsername( $username ) ) {
 356+ @ldap_unbind();
 357+ $this->markAuthFailed();
 358+ return false;
390359 }
391360
392 - //Synch LDAP groups with MediaWiki groups
393 - if ( isset( $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) && $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) {
394 - $this->printDebug( "Retrieving LDAP group membership", NONSENSITIVE );
395 -
396 - //Let's get the user's LDAP groups
397 - if ( isset( $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) {
398 - $this->userLDAPGroups = $this->getUserGroups( $ldapconn, $userdn, true );
399 - } else {
400 - if ( ( isset( $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] )
401 - && $this->LDAPUsername != '' ) {
402 -
403 - $this->userLDAPGroups = $this->getUserGroups( $ldapconn, $this->LDAPUsername, true );
404 - } else {
405 - $this->userLDAPGroups = $this->getUserGroups( $ldapconn, $username, true );
406 - }
407 - }
408 -
409 - //Only find all groups if the user has any groups; otherwise, we are
410 - //just wasting a search.
411 - if ( $this->foundUserLDAPGroups && ( isset( $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) ) {
412 - $this->allLDAPGroups = $this->getAllGroups( $ldapconn, true );
413 - }
414 - }
415 -
416 - //Retrieve preferences
417 - if ( isset( $wgLDAPPreferences[$_SESSION['wsDomain']] ) ) {
418 - $this->printDebug( "Retrieving preferences", NONSENSITIVE );
419 - $entry = @ldap_read( $ldapconn, $userdn, "objectclass=*" );
420 - $info = @ldap_get_entries( $ldapconn, $entry );
421 - $prefs = $wgLDAPPreferences[$_SESSION['wsDomain']];
422 - foreach ( array_keys( $prefs ) as $key ) {
423 - switch ( $key ) {
424 - case "email":
425 - if ( isset( $info[0]["$prefs[$key]"] ) ) {
426 - $this->email = $info[0]["$prefs[$key]"][0];
427 - $this->printDebug( "Retrieved email ($this->email) using attribute ($prefs[$key])", NONSENSITIVE );
428 - }
429 - break;
430 - case "language":
431 - if ( isset( $info[0]["$prefs[$key]"] ) ) {
432 - $this->lang = $info[0][$prefs[$key]][0];
433 - $this->printDebug( "Retrieved language ($this->lang) using attribute ($prefs[$key])", NONSENSITIVE );
434 - }
435 - break;
436 - case "nickname":
437 - if ( isset( $info[0]["$prefs[$key]"] ) ) {
438 - $this->nickname = $info[0]["$prefs[$key]"][0];
439 - $this->printDebug( "Retrieved nickname ($this->nickname) using attribute ($prefs[$key])", NONSENSITIVE );
440 - }
441 - break;
442 - case "realname":
443 - if ( isset( $info[0]["$prefs[$key]"] ) ) {
444 - $this->realname = $info[0]["$prefs[$key]"][0];
445 - $this->printDebug( "Retrieved realname ($this->realname) using attribute ($prefs[$key])", NONSENSITIVE );
446 - }
447 - break;
448 - }
449 - }
450 - } else if ( isset( $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) && $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) {
451 - //DEPRECATED. Kept for backwards compatibility.
452 - $this->printDebug( "Retrieving preferences", NONSENSITIVE );
453 - $this->printDebug( '$wgLDAPRetrievePrefs is a DEPRECATED option, please use $wgLDAPPreferences.', NONSENSITIVE );
454 -
455 - $entry = @ldap_read( $ldapconn, $userdn, "objectclass=*" );
456 - $info = @ldap_get_entries( $ldapconn, $entry );
457 - if (isset($info[0]["mail"])) {
458 - $this->email = $info[0]["mail"][0];
459 - }
460 - if (isset($info[0]["preferredlanguage"])) {
461 - $this->lang = $info[0]["preferredlanguage"][0];
462 - }
463 - if (isset($info[0]["displayname"])) {
464 - $this->nickname = $info[0]["displayname"][0];
465 - }
466 - if (isset($info[0]["cn"])) {
467 - $this->realname = $info[0]["cn"][0];
468 - }
469 -
470 - $this->printDebug( "Retrieved: $this->email, $this->lang, $this->nickname, $this->realname", SENSITIVE );
471 - }
472 -
473 - // Are we blocking login/renaming users on unique external ID mismatches?
474 - // *** WARNING ***
475 - // This needs to be fixed before use! This probably does not work correctly
476 - // with all options. It is probably a good idea to refactor the username stuff
477 - // in general (as it is currently somewhat of a kludge). Also, MediaWiki does
478 - // not currently have support for this.
479 - // *** WARNING ***
480 - if ( ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
481 - || ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) ) {
482 -
483 - $this->printDebug( "Checking for username change in LDAP.", SENSITIVE );
484 -
485 - //Get the user's unique attribute from LDAP
486 - if ( isset( $wgLDAPUniqueAttribute[$_SESSION['wsDomain']] ) ) {
487 - $ldapuniqueattr = $wgLDAPUniqueAttribute[$_SESSION['wsDomain']];
488 - $this->externalid = $info[0][$ldapuniqueattr][0];
489 - }
490 -
491 - $this->printDebug( "Retrieved external id: $this->externalid", SENSITIVE );
492 -
493 - $retrievedusername = User::whoIsExternalID( "$this->externalid" );
494 -
495 - $this->printDebug( "Username (in MediaWiki database) of fetched external id: $retrievedusername", SENSITIVE );
496 -
497 - // See if the username returned from the database matches the username given
498 - if ( $retrievedusername != '' && ( $username != $retrievedusername ) ) {
499 - if ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
500 - && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) {
501 -
502 - $this->printDebug( "Usernames do not match, blocking login.", SENSITIVE );
503 - return false;
504 - } else if ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] )
505 - && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) {
506 -
507 - $this->printDebug( "Usernames do not match, renaming user in database.", SENSITIVE );
508 -
509 - global $wgVersion;
510 - if ( version_compare( $wgVersion, '1.7.0', '<' ) ) {
511 - $this->printDebug( "Renaming users is only supported in MediaWiki 1.7+, please upgrade.", SENSITIVE );
512 - $this->cleanupFailedAuth();
513 - return false;
514 - }
515 -
516 - $olduser = User::newFromName( $retrievedusername );
517 - $uid = $olduser->idForName();
518 -
519 - // Ensure we don't require the same class twice
520 - if ( !class_exists( 'RenameuserSQL' ) ) {
521 - require( 'Renameuser/SpecialRenameuser_body.php' );
522 - }
523 -
524 - // Make a new rename user object with: from, to, uid of from
525 - $rename = new RenameuserSQL( $retrievedusername, $username, $uid );
526 - $rename->rename();
527 -
528 - // For the time being we can't just allow the user to log in
529 - // as MediaWiki will try to create the user account after we
530 - // do a rename. If we don't return false, the user will get
531 - // a database error
532 - $this->cleanupFailedAuth();
533 - return false;
534 - }
535 - }
536 -
537 - $this->printDebug( "Usernames matched or the user doesn't exist in the database yet.", SENSITIVE );
538 - }
539 -
540 - // Lets clean up.
541361 @ldap_unbind();
542362 } else {
543363 $this->printDebug( "Failed to connect", NONSENSITIVE );
544 - $this->cleanupFailedAuth();
 364+ $this->markAuthFailed();
545365 return false;
546366 }
547367 $this->printDebug( "Authentication passed", NONSENSITIVE );
@@ -549,7 +369,7 @@
550370 return true;
551371 }
552372
553 - function cleanupFailedAuth() {
 373+ function markAuthFailed() {
554374 $this->authFailed = true;
555375 }
556376
@@ -647,13 +467,13 @@
648468
649469 $pass = $this->getPasswordHash( $password );
650470
651 - $ldapconn = $this->connect();
652 - if ( $ldapconn ) {
 471+ $this->connect();
 472+ if ( $this->ldapconn ) {
653473 $this->printDebug( "Connected successfully", NONSENSITIVE );
654 - $userdn = $this->getSearchString( $ldapconn, $user->getName() );
 474+ $this->userdn = $this->getSearchString( $user->getName() );
655475
656476 $this->printDebug( "Binding as the writerDN", NONSENSITIVE );
657 - $bind = $this->bindAs( $ldapconn, $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
 477+ $bind = $this->bindAs( $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
658478 if ( !$bind ) {
659479 return false;
660480 }
@@ -664,7 +484,7 @@
665485 //domain credentials for security reasons.
666486 $password = '';
667487
668 - $success = ldap_modify( $ldapconn, $userdn, $values );
 488+ $success = @ldap_modify( $this->ldapconn, $this->userdn, $values );
669489
670490 //Let's clean up
671491 @ldap_unbind();
@@ -717,13 +537,13 @@
718538 $this->nickname = $user->getOption( 'nickname' );
719539 $this->language = $user->getOption( 'language' );
720540
721 - $ldapconn = $this->connect();
722 - if ( $ldapconn ) {
 541+ $this->connect();
 542+ if ( $this->ldapconn ) {
723543 $this->printDebug( "Connected successfully", NONSENSITIVE );
724 - $userdn = $this->getSearchString( $ldapconn, $user->getName() );
 544+ $this->userdn = $this->getSearchString( $user->getName() );
725545
726546 $this->printDebug( "Binding as the writerDN", NONSENSITIVE );
727 - $bind = $this->bindAs( $ldapconn, $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
 547+ $bind = $this->bindAs( $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
728548 if ( !$bind ) {
729549 return false;
730550 }
@@ -733,7 +553,7 @@
734554 if ( '' != $this->realname ) { $values["cn"] = $this->realname; }
735555 if ( '' != $this->language ) { $values["preferredlanguage"] = $this->language; }
736556
737 - if ( 0 != sizeof( $values ) && ldap_modify( $ldapconn, $userdn, $values ) ) {
 557+ if ( 0 != sizeof( $values ) && @ldap_modify( $this->ldapconn, $this->userdn, $values ) ) {
738558 $this->printDebug( "Successfully modified the user's attributes", NONSENSITIVE );
739559 @ldap_unbind();
740560 return true;
@@ -811,7 +631,7 @@
812632 global $wgLDAPSearchAttributes;
813633 global $wgLDAPWriteLocation;
814634 global $wgLDAPRequiredGroups, $wgLDAPGroupDN;
815 - global $wgLDAPRequireAuthAttribute, $wgLDAPAuthAttribute;
 635+ global $wgLDAPAuthAttribute;
816636
817637 $this->printDebug( "Entering addUser", NONSENSITIVE );
818638
@@ -844,16 +664,16 @@
845665
846666 $pass = $this->getPasswordHash( $password );
847667
848 - $ldapconn = $this->connect();
849 - if ( $ldapconn ) {
 668+ $this->connect();
 669+ if ( $this->ldapconn ) {
850670 $this->printDebug( "Successfully connected", NONSENSITIVE );
851671
852 - $userdn = $this->getSearchString( $ldapconn, $username );
853 - if ( '' == $userdn ) {
854 - $this->printDebug( "userdn is blank, attempting to use wgLDAPWriteLocation", NONSENSITIVE );
 672+ $this->userdn = $this->getSearchString( $username );
 673+ if ( '' == $this->userdn ) {
 674+ $this->printDebug( "$this->userdn is blank, attempting to use wgLDAPWriteLocation", NONSENSITIVE );
855675 if ( isset( $wgLDAPWriteLocation[$_SESSION['wsDomain']] ) ) {
856676 $this->printDebug( "wgLDAPWriteLocation is set, using that", NONSENSITIVE );
857 - $userdn = $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=" .
 677+ $this->userdn = $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=" .
858678 $username . "," . $wgLDAPWriteLocation[$_SESSION['wsDomain']];
859679 } else {
860680 $this->printDebug( "wgLDAPWriteLocation is not set, failing", NONSENSITIVE );
@@ -865,7 +685,7 @@
866686
867687 $this->printDebug( "Binding as the writerDN", NONSENSITIVE );
868688
869 - $bind = $this->bindAs( $ldapconn, $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
 689+ $bind = $this->bindAs( $wgLDAPWriterDN[$_SESSION['wsDomain']], $wgLDAPWriterPassword[$_SESSION['wsDomain']] );
870690 if ( !$bind ) {
871691 $this->printDebug( "Failed to bind as the writerDN; add failed", NONSENSITIVE );
872692 return false;
@@ -881,12 +701,12 @@
882702 $values["userpassword"] = $pass;
883703 $values["objectclass"] = "inetorgperson";
884704
885 - if ( isset ( $wgLDAPRequireAuthAttribute ) && $wgLDAPRequireAuthAttribute[$_SESSION['wsDomain']] ) {
 705+ if ( isset ( $wgLDAPAuthAttribute[$_SESSION['wsDomain']] ) ) {
886706 $values[$wgLDAPAuthAttribute[$_SESSION['wsDomain']]] = "true";
887707 }
888708
889709 $this->printDebug( "Adding user", NONSENSITIVE );
890 - if ( @ldap_add( $ldapconn, $userdn, $values ) ) {
 710+ if ( @ldap_add( $this->ldapconn, $this->userdn, $values ) ) {
891711 $this->printDebug( "Successfully added user", NONSENSITIVE );
892712 @ldap_unbind();
893713 return true;
@@ -1114,12 +934,11 @@
1115935 * Gets the searchstring for a user based upon settings for the domain.
1116936 * Returns a full DN for a user.
1117937 *
1118 - * @param resource $ldapconn
1119938 * @param string $username
1120939 * @return string
1121940 * @access private
1122941 */
1123 - function getSearchString( $ldapconn, $username ) {
 942+ function getSearchString( $username ) {
1124943 global $wgLDAPSearchStrings;
1125944 global $wgLDAPProxyAgent, $wgLDAPProxyAgentPassword;
1126945
@@ -1136,11 +955,11 @@
1137956 if ( isset( $wgLDAPProxyAgent[$_SESSION['wsDomain']] ) ) {
1138957 //This is a proxy bind
1139958 $this->printDebug( "Doing a proxy bind", NONSENSITIVE );
1140 - $bind = $this->bindAs( $ldapconn, $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
 959+ $bind = $this->bindAs( $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
1141960 } else {
1142961 //This is an anonymous bind
1143962 $this->printDebug( "Doing an anonymous bind", NONSENSITIVE );
1144 - $bind = $this->bindAs( $ldapconn );
 963+ $bind = $this->bindAs();
1145964 }
1146965
1147966 if ( !$bind ) {
@@ -1148,7 +967,7 @@
1149968 return '';
1150969 }
1151970
1152 - $userdn = $this->getUserDN( $ldapconn, $username );
 971+ $userdn = $this->getUserDN( $username );
1153972 }
1154973 $this->printDebug( "userdn is: $userdn", SENSITIVE );
1155974 return $userdn;
@@ -1159,21 +978,20 @@
1160979 * This function will set $this->LDAPUsername
1161980 * You must bind to the server before calling this.
1162981 *
1163 - * @param resource $ldapconn
1164982 * @param string $username
1165983 * @return string
1166984 * @access private
1167985 */
1168 - function getUserDN( $ldapconn, $username ) {
 986+ function getUserDN( $username ) {
1169987 global $wgLDAPSearchAttributes;
1170 - global $wgLDAPRequireAuthAttribute, $wgLDAPAuthAttribute;
 988+ global $wgLDAPAuthAttribute;
1171989
1172990 $this->printDebug("Entering getUserDN", NONSENSITIVE);
1173991
1174992 //we need to do a subbase search for the entry
1175993
1176 - //Smartcard auth needs to check LDAP for required attributes.
1177 - if ( ( isset( $wgLDAPRequireAuthAttribute[$_SESSION['wsDomain']] ) && $wgLDAPRequireAuthAttribute[$_SESSION['wsDomain']] )
 994+ //Auto auth needs to check LDAP for required attributes.
 995+ if ( ( isset( $wgLDAPAuthAttribute[$_SESSION['wsDomain']] ) )
1178996 && $this->useAutoAuth() ) {
1179997 $auth_filter = "(" . $wgLDAPAuthAttribute[$_SESSION['wsDomain']] . ")";
1180998 $srch_filter = "(" . $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=" . $this->getLdapEscapedString( $username ) . ")";
@@ -1189,269 +1007,388 @@
11901008
11911009 $this->printDebug( "Using base: $base", SENSITIVE );
11921010
1193 - $entry = @ldap_search( $ldapconn, $base, $filter, $attributes );
 1011+ $entry = @ldap_search( $this->ldapconn, $base, $filter, $attributes );
11941012 if ( !$entry ) {
11951013 $this->printDebug( "Couldn't find an entry", NONSENSITIVE );
 1014+ $this->fetchedUserInfo = false;
11961015 return '';
11971016 }
11981017
1199 - $info = @ldap_get_entries( $ldapconn, $entry );
 1018+ $this->userInfo = @ldap_get_entries( $this->ldapconn, $entry );
 1019+ $this->fetchedUserInfo = true;
12001020
12011021 //This is a pretty useful thing to have for auto authentication,
12021022 //group checking, and pulling preferences.
1203 - wfRunHooks( 'SetUsernameAttributeFromLDAP', array( &$this->LDAPUsername, $info ) );
 1023+ wfRunHooks( 'SetUsernameAttributeFromLDAP', array( &$this->LDAPUsername, $this->userInfo ) );
12041024 if ( !is_string( $this->LDAPUsername ) ) {
12051025 $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 );
12061026 $this->LDAPUsername = '';
12071027 }
12081028
1209 - $userdn = $info[0]["dn"];
 1029+ $userdn = $this->userInfo[0]["dn"];
12101030 return $userdn;
12111031 }
12121032
1213 - //DEPRECATED
1214 - function isMemberOfLdapGroup( $ldapconn, $userDN, $groupDN ) {
1215 - $this->printDebug( "Entering isMemberOfLdapGroup (DEPRECATED)", NONSENSITIVE );
 1033+ function getUserInfo() {
 1034+ //Don't fetch the same data more than once
 1035+ if ( $this->fetchedUserInfo ) {
 1036+ return $this->userInfo;
 1037+ }
12161038
1217 - //we need to do a subbase search for the entry
1218 - $filter = "(member=" . $this->getLdapEscapedString( $userDN ) . ")";
1219 - $info = ldap_get_entries( $ldapconn, @ldap_search( $ldapconn, $groupDN, $filter ) );
1220 -
1221 - return ( $info["count"] >= 1 );
 1039+ $entry = @ldap_read( $this->ldapconn, $this->userdn, "objectclass=*" );
 1040+ $userInfo = @ldap_get_entries( $this->ldapconn, $entry );
 1041+ if ( $userInfo["count"] < 1 ) {
 1042+ $this->fetchedUserInfo = false;
 1043+ return;
 1044+ } else {
 1045+ $this->fetchedUserInfo = true;
 1046+ return $userInfo;
 1047+ }
12221048 }
12231049
12241050 /**
1225 - * Determines whether a user is a member of a group, or a nested group.
 1051+ * Retrieve user preferences from LDAP
12261052 *
1227 - * @param resource $ldapconn
12281053 * @param string $userDN
1229 - * @return bool
12301054 * @access private
12311055 */
1232 - function isMemberOfRequiredLdapGroup( $ldapconn, $userDN ) {
1233 - global $wgLDAPRequiredGroups;
1234 - global $wgLDAPGroupSearchNestedGroups;
 1056+ function getPreferences() {
 1057+ global $wgLDAPPreferences;
 1058+ global $wgLDAPRetrievePrefs;
12351059
1236 - $this->printDebug( "Entering isMemberOfRequiredLdapGroup", NONSENSITIVE );
 1060+ $this->printDebug("Entering getPreferences", NONSENSITIVE);
12371061
1238 - $reqgroups = $wgLDAPRequiredGroups[$_SESSION['wsDomain']];
1239 - for ( $i = 0; $i < count( $reqgroups ); $i++ ) {
1240 - $reqgroups[$i] = strtolower( $reqgroups[$i] );
 1062+ $this->userInfo = $this->getUserInfo();
 1063+ if ( is_null( $this->userInfo ) ) {
 1064+ $this->printDebug("Failed to get preferences", NONSENSITIVE);
12411065 }
12421066
1243 - $searchnested = $wgLDAPGroupSearchNestedGroups[$_SESSION['wsDomain']];
 1067+ //Retrieve preferences
 1068+ if ( isset( $wgLDAPPreferences[$_SESSION['wsDomain']] ) ) {
 1069+ $this->printDebug( "Retrieving preferences", NONSENSITIVE );
 1070+ $prefs = $wgLDAPPreferences[$_SESSION['wsDomain']];
 1071+ foreach ( array_keys( $prefs ) as $key ) {
 1072+ switch ( $key ) {
 1073+ case "email":
 1074+ if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 1075+ $this->email = $this->userInfo[0]["$prefs[$key]"][0];
 1076+ $this->printDebug( "Retrieved email ($this->email) using attribute ($prefs[$key])", NONSENSITIVE );
 1077+ }
 1078+ break;
 1079+ case "language":
 1080+ if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 1081+ $this->lang = $this->userInfo[0][$prefs[$key]][0];
 1082+ $this->printDebug( "Retrieved language ($this->lang) using attribute ($prefs[$key])", NONSENSITIVE );
 1083+ }
 1084+ break;
 1085+ case "nickname":
 1086+ if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 1087+ $this->nickname = $this->userInfo[0]["$prefs[$key]"][0];
 1088+ $this->printDebug( "Retrieved nickname ($this->nickname) using attribute ($prefs[$key])", NONSENSITIVE );
 1089+ }
 1090+ break;
 1091+ case "realname":
 1092+ if ( isset( $this->userInfo[0]["$prefs[$key]"] ) ) {
 1093+ $this->realname = $this->userInfo[0]["$prefs[$key]"][0];
 1094+ $this->printDebug( "Retrieved realname ($this->realname) using attribute ($prefs[$key])", NONSENSITIVE );
 1095+ }
 1096+ break;
 1097+ }
 1098+ }
 1099+ } else if ( isset( $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) && $wgLDAPRetrievePrefs[$_SESSION['wsDomain']] ) {
 1100+ //DEPRECATED. Kept for backwards compatibility.
 1101+ $this->printDebug( "Retrieving preferences", NONSENSITIVE );
 1102+ $this->printDebug( '$wgLDAPRetrievePrefs is a DEPRECATED option, please use $wgLDAPPreferences.', NONSENSITIVE );
12441103
1245 - $this->printDebug( "Required groups:", NONSENSITIVE, $reqgroups );
 1104+ if (isset($this->userInfo[0]["mail"])) {
 1105+ $this->email = $this->userInfo[0]["mail"][0];
 1106+ }
 1107+ if (isset($this->userInfo[0]["preferredlanguage"])) {
 1108+ $this->lang = $this->userInfo[0]["preferredlanguage"][0];
 1109+ }
 1110+ if (isset($this->userInfo[0]["displayname"])) {
 1111+ $this->nickname = $this->userInfo[0]["displayname"][0];
 1112+ }
 1113+ if (isset($this->userInfo[0]["cn"])) {
 1114+ $this->realname = $this->userInfo[0]["cn"][0];
 1115+ }
12461116
1247 - $groups = $this->getUserGroups( $ldapconn, $userDN );
 1117+ $this->printDebug( "Retrieved: $this->email, $this->lang, $this->nickname, $this->realname", SENSITIVE );
 1118+ }
 1119+ }
12481120
1249 - //TODO: using variables for this kind of thing is dirty, let's think of a new way
1250 - // to handle this need.
1251 - if ( !$this->foundUserLDAPGroups ) {
1252 - $this->printDebug( "Couldn't find the user in any groups (1).", NONSENSITIVE );
 1121+ function synchUsername( $username ) {
 1122+ global $wgLDAPUniqueBlockLogin, $wgLDAPUniqueRenameUser;
 1123+ global $wgLDAPUniqueAttribute;
12531124
1254 - //User isn't in any groups, so he/she obviously can't be in
1255 - //a required one
1256 - return false;
1257 - } else {
1258 - //User is in groups, let's see if a required group is one of them
1259 - foreach ( $groups as $group ) {
1260 - if ( in_array( $group, $reqgroups ) ) {
1261 - $this->printDebug( "Found user in a group.", NONSENSITIVE );
1262 - return true;
1263 - }
 1125+ $this->printDebug("Entering synchUsername", NONSENSITIVE);
 1126+
 1127+ $this->userInfo = $this->getUserInfo();
 1128+ if ( is_null( $this->userInfo ) ) {
 1129+ $this->printDebug("Failed to get preferences", NONSENSITIVE);
 1130+ }
 1131+
 1132+ // Are we blocking login/renaming users on unique external ID mismatches?
 1133+ // *** WARNING ***
 1134+ // This needs to be fixed before use! This probably does not work correctly
 1135+ // with all options. It is probably a good idea to refactor the username stuff
 1136+ // in general (as it is currently somewhat of a kludge). Also, MediaWiki does
 1137+ // not currently have support for this.
 1138+ // *** WARNING ***
 1139+ if ( ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
 1140+ || ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) ) {
 1141+
 1142+ $this->printDebug( "Checking for username change in LDAP.", SENSITIVE );
 1143+
 1144+ //Get the user's unique attribute from LDAP
 1145+ if ( isset( $wgLDAPUniqueAttribute[$_SESSION['wsDomain']] ) ) {
 1146+ $ldapuniqueattr = $wgLDAPUniqueAttribute[$_SESSION['wsDomain']];
 1147+ $this->externalid = $this->info[0][$ldapuniqueattr][0];
 1148+ } else {
 1149+ return false;
12641150 }
12651151
1266 - //We didn't find the user in the group, lets check nested groups
1267 - if ( $searchnested ) {
1268 - if ( $this->searchNestedGroups( $ldapconn, $groups ) ) {
1269 - return true;
 1152+ $this->printDebug( "Retrieved external id: $this->externalid", SENSITIVE );
 1153+
 1154+ $retrievedusername = User::whoIsExternalID( "$this->externalid" );
 1155+
 1156+ $this->printDebug( "Username (in MediaWiki database) of fetched external id: $retrievedusername", SENSITIVE );
 1157+
 1158+ // See if the username returned from the database matches the username given
 1159+ if ( $retrievedusername != '' && ( $username != $retrievedusername ) ) {
 1160+ if ( isset( $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] )
 1161+ && $wgLDAPUniqueBlockLogin[$_SESSION['wsDomain']] ) {
 1162+
 1163+ $this->printDebug( "Usernames do not match, blocking login.", SENSITIVE );
 1164+ return false;
 1165+ } else if ( isset( $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] )
 1166+ && $wgLDAPUniqueRenameUser[$_SESSION['wsDomain']] ) {
 1167+
 1168+ $this->printDebug( "Usernames do not match, renaming user in database.", SENSITIVE );
 1169+
 1170+ global $wgVersion;
 1171+ if ( version_compare( $wgVersion, '1.7.0', '<' ) ) {
 1172+ $this->printDebug( "Renaming users is only supported in MediaWiki 1.7+, please upgrade.", SENSITIVE );
 1173+ $this->markAuthFailed();
 1174+ return false;
 1175+ }
 1176+
 1177+ $olduser = User::newFromName( $retrievedusername );
 1178+ $uid = $olduser->idForName();
 1179+
 1180+ // Ensure we don't require the same class twice
 1181+ if ( !class_exists( 'RenameuserSQL' ) ) {
 1182+ require( 'Renameuser/SpecialRenameuser_body.php' );
 1183+ }
 1184+
 1185+ // Make a new rename user object with: from, to, uid of from
 1186+ $rename = new RenameuserSQL( $retrievedusername, $username, $uid );
 1187+ $rename->rename();
 1188+
 1189+ // For the time being we can't just allow the user to log in
 1190+ // as MediaWiki will try to create the user account after we
 1191+ // do a rename. If we don't return false, the user will get
 1192+ // a database error
 1193+ return false;
12701194 }
12711195 }
12721196
1273 - $this->printDebug("Couldn't find the user in any groups (2).", NONSENSITIVE );
1274 -
1275 - return false;
 1197+ $this->printDebug( "Usernames matched or the user doesn't exist in the database yet.", SENSITIVE );
12761198 }
 1199+
 1200+ return true;
12771201 }
12781202
12791203 /**
1280 - * Helper function for isMemberOfRequiredLdapGroup.
1281 - * $checkedgroups is used for tail recursion and shouldn't be provided
1282 - * when called externally.
 1204+ * Checks to see whether a user is in a required group.
12831205 *
1284 - * @param resource $ldapconn
1285 - * @param string $userDN
1286 - * @param array $checkedgroups
 1206+ * @param string $username
12871207 * @return bool
12881208 * @access private
12891209 */
1290 - function searchNestedGroups( $ldapconn, $groups, $checkedgroups = array() ) {
 1210+ function checkGroups( $username ) {
 1211+ global $wgLDAPGroupDN;
12911212 global $wgLDAPRequiredGroups;
12921213
1293 - $this->printDebug( "Entering searchNestedGroups", NONSENSITIVE );
 1214+ $this->printDebug("Entering checkGroups", NONSENSITIVE);
12941215
1295 - //base case, no more groups left to check
1296 - if ( !$groups ) {
1297 - $this->printDebug( "Couldn't find user in any nested groups.", NONSENSITIVE );
1298 - return false;
 1216+ //Old style groups, non-nestable and fairly limited on group type (full DN
 1217+ //versus username). DEPRECATED
 1218+ if ( $wgLDAPGroupDN ) {
 1219+ $this->printDebug( "Checking for (old style) group membership", NONSENSITIVE );
 1220+ //we need to do a subbase search for the entry
 1221+ $filter = "(member=" . $this->getLdapEscapedString( $this->userdn ) . ")";
 1222+ $info = @ldap_get_entries( $this->ldapconn, @ldap_search( $this->ldapconn, $wgLDAPGroupDN, $filter ) );
 1223+
 1224+ return ( $info["count"] >= 1 );
12991225 }
13001226
1301 - $this->printDebug( "Checking groups:", SENSITIVE, $groups );
 1227+ //New style group checking
 1228+ if ( isset( $wgLDAPRequiredGroups[$_SESSION['wsDomain']] ) ) {
 1229+ $this->printDebug( "Checking for (new style) group membership", NONSENSITIVE );
 1230+ $reqgroups = $wgLDAPRequiredGroups[$_SESSION['wsDomain']];
 1231+ for ( $i = 0; $i < count( $reqgroups ); $i++ ) {
 1232+ $reqgroups[$i] = strtolower( $reqgroups[$i] );
 1233+ }
13021234
1303 - $reqgroups = $wgLDAPRequiredGroups[$_SESSION['wsDomain']];
1304 - for ( $i = 0; $i < count( $reqgroups ); $i++ ) {
1305 - $reqgroups[$i] = strtolower( $reqgroups[$i] );
1306 - }
 1235+ $this->printDebug( "Required groups:", NONSENSITIVE, $reqgroups );
13071236
1308 - $groupstocheck = array();
1309 - foreach ( $groups as $group ) {
1310 - $returnedgroups = $this->getUserGroups( $ldapconn, $group, false, false );
1311 - $this->printDebug( "Group $group is in the following groups:", SENSITIVE, $returnedgroups );
1312 - foreach ( $returnedgroups as $checkme ) {
1313 - if ( in_array( $checkme, $checkedgroups ) ) {
1314 - //We already checked this, move on
1315 - continue;
 1237+ if ( count( $this->userLDAPGroups ) == 0 ) {
 1238+ $this->printDebug( "Couldn't find the user in any groups (1).", NONSENSITIVE );
 1239+
 1240+ //User isn't in any groups, so he/she obviously can't be in
 1241+ //a required one
 1242+ return false;
 1243+ } else {
 1244+ //User is in groups, let's see if a required group is one of them
 1245+ foreach ( $this->userLDAPGroups["dn"] as $group ) {
 1246+ $this->printDebug( "Checking against: $group", NONSENSITIVE );
 1247+ if ( in_array( $group, $reqgroups ) ) {
 1248+ $this->printDebug( "Found user in a group.", NONSENSITIVE );
 1249+ return true;
 1250+ }
13161251 }
1317 - $this->printDebug( "Checking membership for: $checkme", SENSITIVE );
1318 - if ( in_array( $checkme, $reqgroups ) ) {
1319 - $this->printDebug( "Found user in a nested group.", NONSENSITIVE );
1320 - //Woohoo
1321 - return true;
1322 - } else {
1323 - //We'll need to check this group's members now
1324 - array_push( $groupstocheck, $checkme );
1325 - }
 1252+
 1253+ $this->printDebug("Couldn't find the user in any groups (2).", NONSENSITIVE );
 1254+ return false;
13261255 }
13271256 }
13281257
1329 - $checkedgroups = array_unique( array_merge( $groups, $checkedgroups ) );
1330 -
1331 - //Mmmmmm. Tail recursion. Tasty.
1332 - return $this->searchNestedGroups( $ldapconn, $groupstocheck, $checkedgroups );
 1258+ // Ensure we return true if we aren't checking groups.
 1259+ return true;
13331260 }
13341261
13351262 /**
1336 - * Helper function for isMemberOfRequiredLdapGroup and searchNestedGroups. Returns
1337 - * a list of groups the user is in, all munged to lowercase.
1338 - * Sets $this->foundUserLDAPGroups
 1263+ * Function to get the user's groups.
13391264 *
1340 - * @param resource $ldapconn
1341 - * @param string $dn
1342 - * @return array
13431265 * @access private
13441266 */
1345 - function getUserGroups( $ldapconn, $dn, $getShortnames = false, $returncache = true ) {
1346 - $this->printDebug( "Entering getUserGroups", NONSENSITIVE );
 1267+ function getGroups( $username ) {
 1268+ global $wgLDAPRequiredGroups, $wgLDAPUseLDAPGroups;
 1269+ global $wgLDAPGroupUseFullDN, $wgLDAPGroupUseRetrievedUsername;
 1270+ global $wgLDAPGroupSearchNestedGroups;
 1271+ global $wgLDAPGroupsPrevail;
 1272+ global $wgLDAPGroupsUseMemberOf;
13471273
1348 - //Let's return the saved groups if they are available
1349 - if ( $getShortnames ) {
1350 - if ( $returncache && isset( $this->userLDAPShortnameGroupCache ) ) {
1351 - $this->printDebug( "Returning short name group cache.", NONSENSITIVE );
1352 - return $this->userLDAPShortnameGroupCache;
 1274+ $this->printDebug("Entering getGroups", NONSENSITIVE);
 1275+
 1276+ //Find groups
 1277+ if ( isset( $wgLDAPRequiredGroups[$_SESSION['wsDomain']] ) || ( isset( $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) && $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) ) {
 1278+ $this->printDebug( "Retrieving LDAP group membership", NONSENSITIVE );
 1279+
 1280+ //Let's figure out what we should be searching for
 1281+ if ( isset( $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) {
 1282+ $usertopass = $this->userdn;
 1283+ } else {
 1284+ if ( ( isset( $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] )
 1285+ && $this->LDAPUsername != '' ) {
 1286+
 1287+ $usertopass = $this->LDAPUsername;
 1288+ } else {
 1289+ $usertopass = $username;
 1290+ }
13531291 }
1354 - } else {
1355 - if ( $returncache && isset( $this->userLDAPGroupCache ) ) {
1356 - $this->printDebug( "Returning long name group cache.", NONSENSITIVE );
1357 - return $this->userLDAPGroupCache;
1358 - }
1359 - }
13601292
1361 - //We haven't done a search yet, lets do it now
1362 - list( $groups, $shortnamegroups ) = $this->getGroups( $ldapconn, $dn );
 1293+ if ( isset( $wgLDAPGroupsUseMemberOf[$_SESSION['wsDomain']] ) && $wgLDAPGroupsUseMemberOf[$_SESSION['wsDomain']] ) {
 1294+ $this->printDebug( "Using memberOf", NONSENSITIVE );
 1295+ $this->userInfo = $this->getUserInfo();
 1296+ if ( is_null( $this->userInfo ) ) {
 1297+ $this->printDebug("Failed to get memberOf attribute", NONSENSITIVE);
 1298+ }
 1299+ if ( isset( $this->userInfo[0]["memberof"] ) ) {
 1300+ # The first entry is always a count
 1301+ $memberOfMembers = $this->userInfo[0]["memberof"];
 1302+ array_shift( $memberOfMembers );
 1303+ $groups = array( "dn"=> array(), "short"=>array() );
 1304+ foreach( $memberOfMembers as $mem ) {
 1305+ array_push( $groups["dn"], strtolower( $mem ) );
 1306+ }
 1307+ $this->userLDAPGroups = $groups;
 1308+ }
 1309+ } else {
 1310+ $this->printDebug( "Searching for the groups", NONSENSITIVE );
 1311+ $this->userLDAPGroups = $this->searchGroups( $usertopass );
13631312
1364 - //Save the groups for next time we are called
1365 - //Merge groups if a set of groups already exist for
1366 - //nested group searching (half assed) support
1367 - if ( isset( $this->userLDAPGroupCache ) ) {
1368 - array_unique( array_merge( $groups, $this->userLDAPGroupCache ) );
1369 - } else {
1370 - $this->userLDAPGroupCache = $groups;
1371 - }
1372 - if ( isset( $this->userLDAPShortnameGroupCache ) ) {
1373 - array_unique( array_merge( $shortnamegroups, $this->userLDAPShortnameGroupCache ) );
1374 - } else {
1375 - $this->userLDAPShortnameGroupCache = $shortnamegroups;
1376 - }
 1313+ if ( isset( $wgLDAPGroupSearchNestedGroups[$_SESSION['wsDomain']] ) && $wgLDAPGroupSearchNestedGroups[$_SESSION['wsDomain']] ) {
 1314+ $this->userLDAPGroups = $this->searchNestedGroups( $this->userLDAPGroups );
 1315+ $this->printDebug( "Got the following nested groups:", SENSITIVE, $this->userLDAPGroups["dn"] );
 1316+ }
 1317+ }
13771318
1378 - //We only need to check one of the two arrays, as they should be
1379 - //identical from a member standpoint.
1380 - if ( count( $groups ) == 0 ) {
1381 - $this->foundUserLDAPGroups = false;
1382 - } else {
1383 - $this->foundUserLDAPGroups = true;
 1319+ //Only find all groups if the user has any groups; otherwise, we are
 1320+ //just wasting a search.
 1321+ if ( ( isset( $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && count( $this->userLDAPGroups ) != 0 ) {
 1322+ $this->allLDAPGroups = $this->searchGroups( '*' );
 1323+ }
13841324 }
1385 -
1386 - if ( $getShortnames ) {
1387 - return $shortnamegroups;
1388 - } else {
1389 - return $groups;
1390 - }
13911325 }
13921326
13931327 /**
1394 - * Helper function for retrieving all LDAP groups. Returns
1395 - * a list of all groups in the LDAP server, that match available groups
1396 - * the user is already joined to in MediaWiki, under the appropriate
1397 - * basedn, all munged to lowercase.
1398 - * Sets $this->foundAllLDAPGroups
 1328+ * Function to return an array of nested groups when given a group or list of groups.
 1329+ * $searchedgroups is used for tail recursion and shouldn't be provided
 1330+ * when called externally.
13991331 *
1400 - * @param resource $ldapconn
1401 - * @param string $dn
1402 - * @return array
 1332+ * @param string $userDN
 1333+ * @param array $searchedgroups
 1334+ * @return bool
14031335 * @access private
14041336 */
1405 - function getAllGroups( $ldapconn, $getShortnames = false ) {
1406 - $this->printDebug( "Entering getAllGroups", NONSENSITIVE );
 1337+ function searchNestedGroups( $groups, $searchedgroups = array( "dn" => Array(), "short" => Array() ) ) {
 1338+ $this->printDebug( "Entering searchNestedGroups", NONSENSITIVE );
14071339
1408 - //Let's return the saved groups if they are available
1409 - if ( $getShortnames ) {
1410 - if ( isset( $this->allLDAPShortnameGroupCache ) ) {
1411 - return $this->allLDAPShortnameGroupCache;
1412 - }
1413 - } else {
1414 - if ( isset( $this->allLDAPGroupCache ) ) {
1415 - return $this->allLDAPGroupCache;
1416 - }
 1340+ //base case, no more groups left to check
 1341+ if ( count( $groups["dn"] ) == 0 ) {
 1342+ $this->printDebug( "No more groups to search.", NONSENSITIVE );
 1343+ return $searchedgroups;
14171344 }
14181345
1419 - //We haven't done a search yet, lets do it now
1420 - list( $groups, $shortnamegroups ) = $this->getGroups( $ldapconn, '*' );
 1346+ $this->printDebug( "Searching groups:", SENSITIVE, $groups["dn"] );
14211347
1422 - //Save the groups for next time we are called
1423 - $this->allLDAPGroupCache = $groups;
1424 - $this->allLDAPShortnameGroupCache = $shortnamegroups;
1425 -
1426 - //We only need to check one of the two arrays, as they should be
1427 - //identical from a member standpoint.
1428 - if ( count( $groups ) == 0 ) {
1429 - $this->foundAllLDAPGroups = false;
1430 - } else {
1431 - $this->foundAllLDAPGroups = true;
 1348+ $groupstosearch = array( "short"=>array(), "dn"=>array() );
 1349+ foreach ( $groups["dn"] as $group ) {
 1350+ $returnedgroups = $this->searchGroups( $group );
 1351+ $this->printDebug( "Group $group is in the following groups:", SENSITIVE, $returnedgroups["dn"] );
 1352+ foreach ( $returnedgroups["dn"] as $searchme ) {
 1353+ if ( in_array( $searchme, $searchedgroups["dn"] ) ) {
 1354+ //We already searched this, move on
 1355+ continue;
 1356+ } else {
 1357+ //We'll need to search this group's members now
 1358+ $this->printDebug( "Adding $searchme to the list of groups (1)", SENSITIVE );
 1359+ $groupstosearch["dn"][] = $searchme;
 1360+ }
 1361+ }
 1362+ foreach ( $returnedgroups["short"] as $searchme ) {
 1363+ if ( in_array( $searchme, $searchedgroups["short"] ) ) {
 1364+ //We already searched this, move on
 1365+ continue;
 1366+ } else {
 1367+ $this->printDebug( "Adding $searchme to the list of groups (2)", SENSITIVE );
 1368+ //We'll need to search this group's members now
 1369+ $groupstosearch["short"][] = $searchme;
 1370+ }
 1371+ }
14321372 }
14331373
1434 - if ( $getShortnames ) {
1435 - return $shortnamegroups;
1436 - } else {
1437 - return $groups;
1438 - }
 1374+ $searchedgroups = array_merge_recursive( $groups, $searchedgroups );
 1375+
 1376+ //Mmmmmm. Tail recursion. Tasty.
 1377+ return $this->searchNestedGroups( $groupstosearch, $searchedgroups );
14391378 }
14401379
14411380 /**
1442 - * Helper function for getUserGroups and getAllGroups. You shouldn't
1443 - * call this directly.
 1381+ * Search groups for the supplied DN
14441382 *
1445 - * @param resource $ldapconn
14461383 * @param string $dn
14471384 * @return array
14481385 * @access private
14491386 */
1450 - function getGroups( $ldapconn, $dn ) {
 1387+ function searchGroups( $dn ) {
14511388 global $wgLDAPGroupObjectclass, $wgLDAPGroupAttribute, $wgLDAPGroupNameAttribute;
14521389 global $wgLDAPProxyAgent, $wgLDAPProxyAgentPassword;
14531390 global $wgUser;
14541391
1455 - $this->printDebug( "Entering getGroups", NONSENSITIVE );
 1392+ $this->printDebug( "Entering searchGroups", NONSENSITIVE );
14561393
14571394 $base = $this->getBaseDN( GROUPDN );
14581395
@@ -1472,44 +1409,37 @@
14731410 //We'll try to bind as the proxyagent as the proxyagent should normally have more
14741411 //rights than the user. If the proxyagent fails to bind, we will still be able
14751412 //to search as the normal user (which is why we don't return on fail).
1476 - $this->printDebug( "Binding as the proxyagentDN", NONSENSITIVE );
1477 - $bind = $this->bindAs( $ldapconn, $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
 1413+ $this->printDebug( "Binding as the proxyagent", NONSENSITIVE );
 1414+ $bind = $this->bindAs( $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
14781415 }
14791416
1480 - $info = @ldap_search( $ldapconn, $base, $filter );
1481 - //TODO: Active Directory always returns something, we need to take this into account
 1417+ $info = @ldap_search( $this->ldapconn, $base, $filter );
 1418+ #if ( $info["count"] < 1 ) {
14821419 if ( !$info ) {
14831420 $this->printDebug( "No entries returned from search.", SENSITIVE );
14841421
1485 - //Return an array with two empty arrays so that other functions
 1422+ //Return an array so that other functions
14861423 //don't error out.
1487 - return array( array(), array() );
 1424+ return array( "short"=>array(), "dn"=>array() );
14881425 }
14891426
1490 - $entries = @ldap_get_entries( $ldapconn, $info );
 1427+ $entries = @ldap_get_entries( $this->ldapconn, $info );
14911428
14921429 //We need to shift because the first entry will be a count
14931430 array_shift( $entries );
14941431
14951432 //Let's get a list of both full dn groups and shortname groups
1496 - $groups = array();
1497 - $shortnamegroups = array();
 1433+ $groups = array( "short"=>array(), "dn"=>array() );
14981434 foreach ( $entries as $entry ) {
1499 - $mem = strtolower( $entry['dn'] );
1500 - $shortnamemem = strtolower( $entry[$nameattribute][0] );
1501 -
1502 - array_push( $groups, $mem );
1503 - array_push( $shortnamegroups, $shortnamemem );
 1435+ $shortMember = strtolower( $entry[$nameattribute][0] );
 1436+ $dnMember = strtolower( $entry['dn'] );
 1437+ $groups["short"][] = $shortMember;
 1438+ $groups["dn"][] = $dnMember;
15041439 }
15051440
1506 - $both_groups = array();
1507 - array_push( $both_groups, $groups );
1508 - array_push( $both_groups, $shortnamegroups );
 1441+ $this->printDebug( "Returned groups:", SENSITIVE, $groups["dn"] );
15091442
1510 - $this->printDebug( "Returned groups:", SENSITIVE, $groups );
1511 - $this->printDebug( "Returned groups:", SENSITIVE, $shortnamegroups );
1512 -
1513 - return $both_groups;
 1443+ return $groups;
15141444 }
15151445
15161446 /**
@@ -1523,7 +1453,7 @@
15241454 function hasLDAPGroup( $group ) {
15251455 $this->printDebug( "Entering hasLDAPGroup", NONSENSITIVE );
15261456
1527 - return in_array( strtolower( $group ), $this->userLDAPGroups );
 1457+ return in_array( strtolower( $group ), $this->userLDAPGroups["short"] );
15281458 }
15291459
15301460 /**
@@ -1536,7 +1466,7 @@
15371467 function isLDAPGroup( $group ) {
15381468 $this->printDebug( "Entering isLDAPGroup", NONSENSITIVE );
15391469
1540 - return in_array( strtolower( $group ), $this->allLDAPGroups );
 1470+ return in_array( strtolower( $group ), $this->allLDAPGroups["short"] );
15411471 }
15421472
15431473 /**
@@ -1573,7 +1503,7 @@
15741504 # Add ldap groups as local groups
15751505 if ( isset( $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) && $wgLDAPGroupsPrevail[$_SESSION['wsDomain']] ) {
15761506 $this->printDebug( "Adding all groups to wgGroupPermissions: ", SENSITIVE, $this->allLDAPGroups );
1577 - foreach ( $this->allLDAPGroups as $ldapgroup )
 1507+ foreach ( $this->allLDAPGroups["short"] as $ldapgroup )
15781508 if ( !array_key_exists( $ldapgroup, $wgGroupPermissions ) )
15791509 $wgGroupPermissions[$ldapgroup] = array();
15801510 }
@@ -1647,16 +1577,17 @@
16481578 * @param string $debugVal
16491579 * @access private
16501580 */
1651 - function printDebug( $debugText, $debugVal, $debugArr = Null ) {
 1581+ function printDebug( $debugText, $debugVal, $debugArr = null ) {
16521582 global $wgLDAPDebug;
16531583
16541584 if ( isset( $debugArr ) ) {
16551585 if ( $wgLDAPDebug > $debugVal ) {
1656 - echo $debugText . implode( ",", $debugArr ) . "<br />";
 1586+ $text = $debugText . " " . implode( "::", $debugArr );
 1587+ wfDebugLog( 'ldap', $text, false );
16571588 }
16581589 } else {
16591590 if ( $wgLDAPDebug > $debugVal ) {
1660 - echo $debugText . "<br />";
 1591+ wfDebugLog( 'ldap', $debugText, false );
16611592 }
16621593 }
16631594 }
@@ -1665,18 +1596,17 @@
16661597 * Binds as $userdn with $password. This can be called with only the ldap
16671598 * connection resource for an anonymous bind.
16681599 *
1669 - * @param resourse $ldapconn
16701600 * @param string $userdn
16711601 * @param string $password
16721602 * @return bool
16731603 * @access private
16741604 */
1675 - function bindAs( $ldapconn, $userdn=null, $password=null ) {
 1605+ function bindAs( $userdn=null, $password=null ) {
16761606 //Let's see if the user can authenticate.
16771607 if ( $userdn == null || $password == null ) {
1678 - $bind = @ldap_bind( $ldapconn );
 1608+ $bind = @ldap_bind( $this->ldapconn );
16791609 } else {
1680 - $bind = @ldap_bind( $ldapconn, $userdn, $password );
 1610+ $bind = @ldap_bind( $this->ldapconn, $userdn, $password );
16811611 }
16821612 if ( !$bind ) {
16831613 $this->printDebug( "Failed to bind as $userdn", NONSENSITIVE );

Status & tagging log