Index: trunk/extensions/LdapAuthentication/LdapAutoAuthentication.php |
— | — | @@ -45,7 +45,6 @@ |
46 | 46 | $mungedUsername = $wgAuth->getCanonicalName( $autoauthname ); |
47 | 47 | |
48 | 48 | $wgAuth->printDebug( "User exists in LDAP; finding the user by name ($mungedUsername) in MediaWiki.", NONSENSITIVE ); |
49 | | - |
50 | 49 | $localId = User::idFromName( $mungedUsername ); |
51 | 50 | $wgAuth->printDebug( "Got id ($localId).", NONSENSITIVE ); |
52 | 51 | |
— | — | @@ -86,19 +85,15 @@ |
87 | 86 | } |
88 | 87 | |
89 | 88 | $wgAuth->printDebug( "User does not exist in local database; creating.", NONSENSITIVE ); |
90 | | - |
91 | 89 | // Checks passed, create the user |
92 | 90 | $user->loadDefaults( $mungedUsername ); |
93 | 91 | $user->addToDatabase(); |
94 | | - |
95 | 92 | $wgAuth->initUser( $user, true ); |
96 | 93 | $user->setCookies(); |
97 | 94 | wfSetupSession(); |
98 | | - |
99 | 95 | # Update user count |
100 | 96 | $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 ); |
101 | 97 | $ssUpdate->doUpdate(); |
102 | | - |
103 | 98 | # Notify hooks (e.g. Newuserlog) |
104 | 99 | wfRunHooks( 'AuthPluginAutoCreate', array( $user ) ); |
105 | 100 | |
— | — | @@ -116,10 +111,9 @@ |
117 | 112 | * @var $wgAuth LdapAuthenticationPlugin |
118 | 113 | */ |
119 | 114 | global $wgAuth; |
120 | | - $wgAuth->printDebug( "Entering NoLogout.", NONSENSITIVE ); |
121 | 115 | |
| 116 | + $wgAuth->printDebug( "Entering NoLogout.", NONSENSITIVE ); |
122 | 117 | unset( $personal_urls['logout'] ); |
123 | | - |
124 | 118 | return true; |
125 | 119 | } |
126 | 120 | |
Index: trunk/extensions/LdapAuthentication/LdapAuthentication.php |
— | — | @@ -17,17 +17,8 @@ |
18 | 18 | # http://www.gnu.org/copyleft/gpl.html |
19 | 19 | |
20 | 20 | /** |
21 | | - * LdapAuthentication plugin. |
| 21 | + * LdapAuthentication plugin. LDAP Authentication and authorization integration with MediaWiki. |
22 | 22 | * |
23 | | - * Password authentication, and Smartcard Authentication support are currently |
24 | | - * available. All forms of authentication, current and future, should support |
25 | | - * group, and attribute based restrictions; preference pulling; and group |
26 | | - * syncronization. All forms of authentication should have basic support for |
27 | | - * adding users, changing passwords, and updating preferences in LDAP. |
28 | | - * |
29 | | - * Password authentication has a number of configurations, including straight binds, |
30 | | - * proxy based authentication, and anonymous-search based authentication. |
31 | | - * |
32 | 23 | * @file |
33 | 24 | * @ingroup MediaWiki |
34 | 25 | */ |
— | — | @@ -496,36 +487,29 @@ |
497 | 488 | return true; |
498 | 489 | } |
499 | 490 | |
500 | | - $this->connect(); |
501 | | - if ( $this->ldapconn ) { |
502 | | - $this->printDebug( "PHP's LDAP connect method returned true (note, this does not imply it connected to the server).", NONSENSITIVE ); |
503 | | - |
| 491 | + $ret = false; |
| 492 | + if ( $this->connect() ) { |
504 | 493 | $searchstring = $this->getSearchString( $username ); |
505 | 494 | |
506 | 495 | // If we are using auto authentication, and we got |
507 | 496 | // anything back, then the user exists. |
508 | 497 | if ( $this->useAutoAuth() && $searchstring != '' ) { |
509 | | - // getSearchString is going to bind, but will not unbind |
510 | | - LdapAuthenticationPlugin::ldap_unbind( $this->ldapconn ); |
511 | | - return true; |
512 | | - } |
| 498 | + $ret = true; |
| 499 | + } else { |
| 500 | + // Search for the entry. |
| 501 | + $entry = LdapAuthenticationPlugin::ldap_read( $this->ldapconn, $searchstring, "objectclass=*" ); |
513 | 502 | |
514 | | - // Search for the entry. |
515 | | - $entry = LdapAuthenticationPlugin::ldap_read( $this->ldapconn, $searchstring, "objectclass=*" ); |
516 | | - |
| 503 | + if ( $entry ) { |
| 504 | + $this->printDebug( "Found a matching user in LDAP", NONSENSITIVE ); |
| 505 | + $ret = true; |
| 506 | + } else { |
| 507 | + $this->printDebug( "Did not find a matching user in LDAP", NONSENSITIVE ); |
| 508 | + } |
| 509 | + } |
517 | 510 | // getSearchString is going to bind, but will not unbind |
518 | 511 | LdapAuthenticationPlugin::ldap_unbind( $this->ldapconn ); |
519 | | - if ( !$entry ) { |
520 | | - $this->printDebug( "Did not find a matching user in LDAP", NONSENSITIVE ); |
521 | | - return false; |
522 | | - } else { |
523 | | - $this->printDebug( "Found a matching user in LDAP", NONSENSITIVE ); |
524 | | - return true; |
525 | | - } |
526 | | - } else { |
527 | | - $this->printDebug( "PHP's LDAP method returned false, this likely implies a misconfiguration of the plugin.", NONSENSITIVE ); |
528 | | - return false; |
529 | 512 | } |
| 513 | + return $ret; |
530 | 514 | } |
531 | 515 | |
532 | 516 | /** |
— | — | @@ -574,6 +558,10 @@ |
575 | 559 | |
576 | 560 | // Connect and set options |
577 | 561 | $this->ldapconn = LdapAuthenticationPlugin::ldap_connect( $servers ); |
| 562 | + if ( !$this->ldapconn ) { |
| 563 | + $this->printDebug( "PHP's LDAP connect method returned null, this likely implies a misconfiguration of the plugin.", NONSENSITIVE ); |
| 564 | + return false; |
| 565 | + } |
578 | 566 | ldap_set_option( $this->ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3 ); |
579 | 567 | ldap_set_option( $this->ldapconn, LDAP_OPT_REFERRALS, 0 ); |
580 | 568 | |
— | — | @@ -591,6 +579,8 @@ |
592 | 580 | return false; |
593 | 581 | } |
594 | 582 | } |
| 583 | + $this->printDebug( "PHP's LDAP connect method returned true (note, this does not imply it connected to the server).", NONSENSITIVE ); |
| 584 | + |
595 | 585 | return true; |
596 | 586 | } |
597 | 587 | |
— | — | @@ -625,15 +615,14 @@ |
626 | 616 | // We need to ensure that if we require a password, that it is |
627 | 617 | // not blank. We don't allow blank passwords, so we are being |
628 | 618 | // tricked if someone is supplying one when using password auth. |
629 | | - // Smartcard authentication uses a pin, and does not require |
630 | | - // a password to be given; a blank password here is wanted. |
| 619 | + // auto-authentication is handled by the webserver; a blank password |
| 620 | + // here is wanted. |
631 | 621 | if ( '' == $password && !$this->useAutoAuth() ) { |
632 | 622 | $this->printDebug( "User used a blank password", NONSENSITIVE ); |
633 | 623 | return false; |
634 | 624 | } |
635 | 625 | |
636 | | - $this->connect(); |
637 | | - if ( $this->ldapconn ) { |
| 626 | + if ( $this->connect() ) { |
638 | 627 | // Mediawiki munges the username before authenticate is called, |
639 | 628 | // this can mess with authentication, group pulling/restriction, |
640 | 629 | // preference pulling, etc. Let's allow the admin to use |
— | — | @@ -673,27 +662,23 @@ |
674 | 663 | // DOMAIN\\USER-NAME. |
675 | 664 | // Get the user's full DN so we can search for groups and such. |
676 | 665 | $this->userdn = $this->getUserDN( $username ); |
677 | | - $this->printDebug( "Pulled the user's DN: $this->userdn", NONSENSITIVE ); |
| 666 | + $this->printDebug( "Fetched UserDN: $this->userdn", NONSENSITIVE ); |
| 667 | + } else { |
| 668 | + // Now that we are bound, we can pull the user's info. |
| 669 | + $this->getUserInfo(); |
678 | 670 | } |
679 | 671 | } |
| 672 | + } |
680 | 673 | |
681 | | - $aa = $this->getConf( 'AuthAttribute' ); |
682 | | - if ( $aa ) { |
683 | | - |
684 | | - $this->printDebug( "Checking for auth attributes: $aa", NONSENSITIVE ); |
685 | | - |
686 | | - $filter = "(" . $aa . ")"; |
687 | | - $attributes = array( "dn" ); |
688 | | - |
689 | | - $entry = LdapAuthenticationPlugin::ldap_read( $this->ldapconn, $this->userdn, $filter, $attributes ); |
690 | | - $info = LdapAuthenticationPlugin::ldap_get_entries( $this->ldapconn, $entry ); |
691 | | - |
692 | | - if ( $info["count"] < 1 ) { |
693 | | - $this->printDebug( "Failed auth attribute check", NONSENSITIVE ); |
694 | | - LdapAuthenticationPlugin::ldap_unbind( $this->ldapconn ); |
695 | | - $this->markAuthFailed(); |
696 | | - return false; |
697 | | - } |
| 674 | + // Ensure the user's entry has the required auth attribute |
| 675 | + $aa = $this->getConf( 'AuthAttribute' ); |
| 676 | + if ( $aa ) { |
| 677 | + $this->printDebug( "Checking for auth attributes: $aa", NONSENSITIVE ); |
| 678 | + if ( !isset( $this->userInfo ) || !isset( $this->userInfo[0][$aa] ) ) { |
| 679 | + $this->printDebug( "Failed auth attribute check", NONSENSITIVE ); |
| 680 | + LdapAuthenticationPlugin::ldap_unbind( $this->ldapconn ); |
| 681 | + $this->markAuthFailed(); |
| 682 | + return false; |
698 | 683 | } |
699 | 684 | } |
700 | 685 | |
— | — | @@ -730,16 +715,10 @@ |
731 | 716 | */ |
732 | 717 | public function modifyUITemplate( &$template, &$type ) { |
733 | 718 | $this->printDebug( "Entering modifyUITemplate", NONSENSITIVE ); |
734 | | - |
735 | | - if ( !$this->getConf( 'AddLDAPUsers' ) ) { |
736 | | - $template->set( 'create', false ); |
737 | | - } |
738 | | - |
| 719 | + $template->set( 'create', $this->getConf( 'AddLDAPUsers' ) ); |
739 | 720 | $template->set( 'usedomain', true ); |
740 | 721 | $template->set( 'useemail', $this->getConf( 'MailPassword' ) ); |
741 | 722 | $template->set( 'canreset', $this->getConf( 'MailPassword' ) ); |
742 | | - |
743 | | - |
744 | 723 | $template->set( 'domainnames', $this->domainList() ); |
745 | 724 | wfRunHooks( 'LDAPModifyUITemplate', array( &$template ) ); |
746 | 725 | } |
— | — | @@ -758,7 +737,6 @@ |
759 | 738 | |
760 | 739 | if ( $this->getConf( 'AutoAuthDomain' ) ) { |
761 | 740 | $this->printDebug( "Allowing auto-authentication login, removing the domain from the list.", NONSENSITIVE ); |
762 | | - |
763 | 741 | // There is no reason for people to log in directly to the wiki if the are using an |
764 | 742 | // auto-authentication domain. If they try to, they are probably up to something fishy. |
765 | 743 | unset( $tempDomArr[array_search( $this->getConf( 'AutoAuthDomain' ), $tempDomArr )] ); |
— | — | @@ -801,7 +779,8 @@ |
802 | 780 | // We don't set local passwords, but we don't want the wiki |
803 | 781 | // to send the user a failure. |
804 | 782 | return true; |
805 | | - } else if ( !$this->getConf( 'UpdateLDAP' ) ) { |
| 783 | + } |
| 784 | + if ( !$this->getConf( 'UpdateLDAP' ) ) { |
806 | 785 | $this->printDebug( "Wiki is set to not allow updates", NONSENSITIVE ); |
807 | 786 | |
808 | 787 | // We aren't allowing the user to change his/her own password |
— | — | @@ -816,19 +795,15 @@ |
817 | 796 | // allowed to do it. |
818 | 797 | return false; |
819 | 798 | } |
820 | | - |
821 | 799 | $pass = $this->getPasswordHash( $password ); |
822 | 800 | |
823 | | - $this->connect(); |
824 | | - if ( $this->ldapconn ) { |
| 801 | + if ( $this->connect() ) { |
825 | 802 | $this->userdn = $this->getSearchString( $user->getName() ); |
826 | | - |
827 | 803 | $this->printDebug( "Binding as the writerDN", NONSENSITIVE ); |
828 | 804 | $bind = $this->bindAs( $writer, $this->getConf( 'WriterPassword' ) ); |
829 | 805 | if ( !$bind ) { |
830 | 806 | return false; |
831 | 807 | } |
832 | | - |
833 | 808 | $values["userpassword"] = $pass; |
834 | 809 | |
835 | 810 | // Blank out the password in the database. We don't want to save |
— | — | @@ -836,7 +811,6 @@ |
837 | 812 | $password = ''; |
838 | 813 | |
839 | 814 | $success = LdapAuthenticationPlugin::ldap_modify( $this->ldapconn, $this->userdn, $values ); |
840 | | - |
841 | 815 | LdapAuthenticationPlugin::ldap_unbind( $this->ldapconn ); |
842 | 816 | if ( $success ) { |
843 | 817 | $this->printDebug( "Successfully modified the user's password", NONSENSITIVE ); |
— | — | @@ -858,10 +832,8 @@ |
859 | 833 | global $wgMemc; |
860 | 834 | |
861 | 835 | $this->printDebug( "Entering updateExternalDB", NONSENSITIVE ); |
862 | | - |
863 | 836 | if ( !$this->getConf( 'UpdateLDAP' ) || $this->getSessionDomain() == 'local' ) { |
864 | 837 | $this->printDebug( "Either the user is using a local domain, or the wiki isn't allowing updates", NONSENSITIVE ); |
865 | | - |
866 | 838 | // We don't handle local preferences, but we don't want the |
867 | 839 | // wiki to return an error. |
868 | 840 | return true; |
— | — | @@ -870,7 +842,6 @@ |
871 | 843 | $writer = $this->getConf( 'WriterDN' ); |
872 | 844 | if ( !$writer ) { |
873 | 845 | $this->printDebug( "The wiki doesn't have wgLDAPWriterDN set", NONSENSITIVE ); |
874 | | - |
875 | 846 | // We can't modify LDAP preferences if we don't have a user |
876 | 847 | // capable of editing LDAP attributes. |
877 | 848 | return false; |
— | — | @@ -880,11 +851,8 @@ |
881 | 852 | $this->realname = $user->getRealName(); |
882 | 853 | $this->nickname = $user->getOption( 'nickname' ); |
883 | 854 | $this->lang = $user->getOption( 'language' ); |
884 | | - |
885 | | - $this->connect(); |
886 | | - if ( $this->ldapconn ) { |
| 855 | + if ( $this->connect() ) { |
887 | 856 | $this->userdn = $this->getSearchString( $user->getName() ); |
888 | | - |
889 | 857 | $this->printDebug( "Binding as the writerDN", NONSENSITIVE ); |
890 | 858 | $bind = $this->bindAs( $writer, $this->getConf( 'WriterPassword' ) ); |
891 | 859 | if ( !$bind ) { |
— | — | @@ -935,11 +903,9 @@ |
936 | 904 | if ( $this->getConf( 'UseLocal' ) && 'local' == $this->getSessionDomain() ) { |
937 | 905 | return true; |
938 | 906 | } |
939 | | - |
940 | 907 | if ( $this->getConf( 'UpdateLDAP' ) || $this->getConf( 'MailPassword' ) ) { |
941 | 908 | return true; |
942 | 909 | } |
943 | | - |
944 | 910 | return false; |
945 | 911 | } |
946 | 912 | |
— | — | @@ -962,7 +928,6 @@ |
963 | 929 | // Tell the wiki not to return an error. |
964 | 930 | return true; |
965 | 931 | } |
966 | | - |
967 | 932 | if ( $this->getConf( 'RequiredGroups' ) ) { |
968 | 933 | $this->printDebug( "The wiki is requiring users to be in specific groups, and cannot add users as this would be a security hole.", NONSENSITIVE ); |
969 | 934 | // It is possible that later we can add users into |
— | — | @@ -981,12 +946,12 @@ |
982 | 947 | |
983 | 948 | $this->email = $user->getEmail(); |
984 | 949 | $this->realname = $user->getRealName(); |
985 | | - $username = strtolower( $user->getName() ); |
986 | | - |
| 950 | + $username = $user->getName(); |
| 951 | + if ( $this->getConf( 'LowercaseUsernameScheme' ) ) { |
| 952 | + $username = strtolower( $username ); |
| 953 | + } |
987 | 954 | $pass = $this->getPasswordHash( $password ); |
988 | | - |
989 | | - $this->connect(); |
990 | | - if ( $this->ldapconn ) { |
| 955 | + if ( $this->connect() ) { |
991 | 956 | $writeloc = $this->getConf( 'WriteLocation' ); |
992 | 957 | $this->userdn = $this->getSearchString( $username ); |
993 | 958 | if ( '' == $this->userdn ) { |
— | — | @@ -1010,12 +975,13 @@ |
1011 | 976 | return false; |
1012 | 977 | } |
1013 | 978 | |
1014 | | - // Set up LDAP attributes |
| 979 | + // Set up LDAP objectclasses and attributes |
| 980 | + // TODO: make objectclasses and attributes configurable |
1015 | 981 | $values["uid"] = $username; |
1016 | 982 | // sn is required for objectclass inetorgperson |
1017 | 983 | $values["sn"] = $username; |
1018 | | - if ( '' != $this->email ) { $values["mail"] = $this->email; } |
1019 | | - if ( '' != $this->realname ) { $values["cn"] = $this->realname; } |
| 984 | + if ( $this->email ) { $values["mail"] = $this->email; } |
| 985 | + if ( $this->realname ) { $values["cn"] = $this->realname; } |
1020 | 986 | else { $values["cn"] = $username; } |
1021 | 987 | $values["userpassword"] = $pass; |
1022 | 988 | $values["objectclass"] = array( "inetorgperson" ); |
— | — | @@ -1023,7 +989,7 @@ |
1024 | 990 | $result = true; |
1025 | 991 | # Let other extensions modify the user object before creation |
1026 | 992 | wfRunHooks( 'LDAPSetCreationValues', array( $this, $username, &$values, $writeloc, &$this->userdn, &$result ) ); |
1027 | | - if ( ! $result ) { |
| 993 | + if ( !$result ) { |
1028 | 994 | $this->printDebug( "Failed to add user because LDAPSetCreationValues returned false", NONSENSITIVE ); |
1029 | 995 | LdapAuthenticationPlugin::ldap_unbind( $this->ldapconn ); |
1030 | 996 | return false; |
— | — | @@ -1067,14 +1033,12 @@ |
1068 | 1034 | global $wgLDAPDomainNames; |
1069 | 1035 | |
1070 | 1036 | $this->printDebug( "Entering validDomain", NONSENSITIVE ); |
1071 | | - |
1072 | 1037 | if ( in_array( $domain, $wgLDAPDomainNames ) || ( $this->getConf( 'UseLocal' ) && 'local' == $domain ) ) { |
1073 | 1038 | $this->printDebug( "User is using a valid domain ($domain).", NONSENSITIVE ); |
1074 | 1039 | return true; |
1075 | | - } else { |
1076 | | - $this->printDebug( "User is not using a valid domain ($domain).", NONSENSITIVE ); |
1077 | | - return false; |
1078 | 1040 | } |
| 1041 | + $this->printDebug( "User is not using a valid domain ($domain).", NONSENSITIVE ); |
| 1042 | + return false; |
1079 | 1043 | } |
1080 | 1044 | |
1081 | 1045 | /** |
— | — | @@ -1085,7 +1049,6 @@ |
1086 | 1050 | */ |
1087 | 1051 | public function updateUser( &$user ) { |
1088 | 1052 | $this->printDebug( "Entering updateUser", NONSENSITIVE ); |
1089 | | - |
1090 | 1053 | if ( $this->authFailed ) { |
1091 | 1054 | $this->printDebug( "User didn't successfully authenticate, exiting.", NONSENSITIVE ); |
1092 | 1055 | return; |
— | — | @@ -1093,36 +1056,31 @@ |
1094 | 1057 | |
1095 | 1058 | $saveSettings = false; |
1096 | 1059 | |
1097 | | - // If we aren't pulling preferences, we don't want to accidentally |
1098 | | - // overwrite anything. |
1099 | 1060 | if ( $this->getConf( 'Preferences' ) ) { |
1100 | 1061 | $this->printDebug( "Setting user preferences.", NONSENSITIVE ); |
1101 | | - |
1102 | | - if ( '' != $this->lang ) { |
| 1062 | + if ( $this->lang ) { |
1103 | 1063 | $this->printDebug( "Setting language.", NONSENSITIVE ); |
1104 | 1064 | $user->setOption( 'language', $this->lang ); |
1105 | 1065 | } |
1106 | | - if ( '' != $this->nickname ) { |
| 1066 | + if ( $this->nickname ) { |
1107 | 1067 | $this->printDebug( "Setting nickname.", NONSENSITIVE ); |
1108 | 1068 | $user->setOption( 'nickname', $this->nickname ); |
1109 | 1069 | } |
1110 | | - if ( '' != $this->realname ) { |
| 1070 | + if ( $this->realname ) { |
1111 | 1071 | $this->printDebug( "Setting realname.", NONSENSITIVE ); |
1112 | 1072 | $user->setRealName( $this->realname ); |
1113 | 1073 | } |
1114 | | - if ( '' != $this->email ) { |
| 1074 | + if ( $this->email ) { |
1115 | 1075 | $this->printDebug( "Setting email.", NONSENSITIVE ); |
1116 | 1076 | $user->setEmail( $this->email ); |
1117 | 1077 | $user->confirmEmail(); |
1118 | 1078 | } |
1119 | | - |
1120 | 1079 | $saveSettings = true; |
1121 | 1080 | } |
1122 | 1081 | |
1123 | 1082 | if ( $this->getConf( 'UseLDAPGroups' ) ) { |
1124 | 1083 | $this->printDebug( "Setting user groups.", NONSENSITIVE ); |
1125 | 1084 | $this->setGroups( $user ); |
1126 | | - |
1127 | 1085 | $saveSettings = true; |
1128 | 1086 | } |
1129 | 1087 | |
— | — | @@ -1147,12 +1105,11 @@ |
1148 | 1106 | |
1149 | 1107 | if ( $this->authFailed ) { |
1150 | 1108 | $this->printDebug( "User didn't successfully authenticate, exiting.", NONSENSITIVE ); |
1151 | | - return; |
| 1109 | + return null; |
1152 | 1110 | } |
1153 | | - |
1154 | 1111 | if ( 'local' == $this->getSessionDomain() ) { |
1155 | 1112 | $this->printDebug( "User is using a local domain", NONSENSITIVE ); |
1156 | | - return; |
| 1113 | + return null; |
1157 | 1114 | } |
1158 | 1115 | |
1159 | 1116 | // We are creating an LDAP user, it is very important that we do |
— | — | @@ -1163,7 +1120,7 @@ |
1164 | 1121 | // The update user function does everything else we need done. |
1165 | 1122 | $this->updateUser( $user ); |
1166 | 1123 | |
1167 | | - // updateUser() won't definately save the user's settings |
| 1124 | + // updateUser() won't necessarily save the user's settings |
1168 | 1125 | $user->saveSettings(); |
1169 | 1126 | } |
1170 | 1127 | |
— | — | @@ -1187,8 +1144,7 @@ |
1188 | 1145 | } |
1189 | 1146 | |
1190 | 1147 | /** |
1191 | | - * Munge the username to always have a form of uppercase for the first letter, |
1192 | | - * and lowercase for the rest of the letters. |
| 1148 | + * Munge the username based on a scheme (lowercase, by default) |
1193 | 1149 | * |
1194 | 1150 | * @param string $username |
1195 | 1151 | * @return string |
— | — | @@ -1197,17 +1153,14 @@ |
1198 | 1154 | global $wgMemc; |
1199 | 1155 | |
1200 | 1156 | $this->printDebug( "Entering getCanonicalName", NONSENSITIVE ); |
1201 | | - |
1202 | 1157 | $key = wfMemcKey( 'ldapauthentication', 'canonicalname', $username ); |
1203 | 1158 | $canonicalname = $username; |
1204 | 1159 | if ( $username != '' ) { |
1205 | 1160 | $this->printDebug( "Username isn't empty.", NONSENSITIVE ); |
1206 | | - |
1207 | 1161 | if ( $this->getConf( 'LowercaseUsernameScheme' ) ) { |
1208 | 1162 | $canonicalname = strtolower( $canonicalname ); |
1209 | 1163 | } else { |
1210 | 1164 | # Fetch username, so that we can possibly use it. |
1211 | | - # Only do it if we haven't already fetched it. |
1212 | 1165 | $userInfo = $wgMemc->get( $key ); |
1213 | 1166 | if ( is_array( $userInfo ) ) { |
1214 | 1167 | $this->printDebug( "Fetched userInfo from memcache.", NONSENSITIVE ); |
— | — | @@ -1216,10 +1169,18 @@ |
1217 | 1170 | return $userInfo["canonicalname"]; |
1218 | 1171 | } |
1219 | 1172 | } else { |
1220 | | - $this->connect(); |
1221 | | - if ( $this->ldapconn ) { |
1222 | | - $this->userdn = $this->getSearchString( $username ); |
1223 | | - wfRunHooks( 'SetUsernameAttributeFromLDAP', array( &$this->LDAPUsername, $this->userInfo ) ); |
| 1173 | + if ( $this->connect() ) { |
| 1174 | + // Try to pull the username from LDAP. In the case of straight binds, |
| 1175 | + // try to fetch the username by search before bind. |
| 1176 | + $this->userdn = $this->getUserDN( $username, true ); |
| 1177 | + $hookSetUsername = $this->LDAPUsername; |
| 1178 | + wfRunHooks( 'SetUsernameAttributeFromLDAP', array( &$hookSetUsername, $this->userInfo ) ); |
| 1179 | + if ( is_string( $hookSetUsername ) ) { |
| 1180 | + $this->printDebug( "Username munged by hook: $hookSetUsername", NONSENSITIVE ); |
| 1181 | + $this->LDAPUsername = $hookSetUsername; |
| 1182 | + } else { |
| 1183 | + $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 ); |
| 1184 | + } |
1224 | 1185 | } |
1225 | 1186 | } |
1226 | 1187 | |
— | — | @@ -1235,9 +1196,7 @@ |
1236 | 1197 | // uppercase the first letter |
1237 | 1198 | $canonicalname[0] = strtoupper( $canonicalname[0] ); |
1238 | 1199 | } |
1239 | | - |
1240 | 1200 | $this->printDebug( "Munged username: $canonicalname", NONSENSITIVE ); |
1241 | | - |
1242 | 1201 | $wgMemc->set( $key, array( "username" => $username, "canonicalname" => $canonicalname ), 3600 * 24 ); |
1243 | 1202 | return $canonicalname; |
1244 | 1203 | } |
— | — | @@ -1260,13 +1219,29 @@ |
1261 | 1220 | */ |
1262 | 1221 | function getSearchString( $username ) { |
1263 | 1222 | $this->printDebug( "Entering getSearchString", NONSENSITIVE ); |
1264 | | - |
1265 | 1223 | $ss = $this->getConf( 'SearchString' ); |
1266 | 1224 | if ( $ss ) { |
1267 | 1225 | // This is a straight bind |
1268 | 1226 | $this->printDebug( "Doing a straight bind", NONSENSITIVE ); |
1269 | 1227 | $userdn = str_replace( "USER-NAME", $username, $ss ); |
1270 | 1228 | } else { |
| 1229 | + $userdn = $this->getUserDN( $username, true ); |
| 1230 | + } |
| 1231 | + $this->printDebug( "userdn is: $userdn", SENSITIVE ); |
| 1232 | + return $userdn; |
| 1233 | + } |
| 1234 | + |
| 1235 | + /** |
| 1236 | + * Gets the DN of a user based upon settings for the domain. |
| 1237 | + * This function will set $this->LDAPUsername |
| 1238 | + * |
| 1239 | + * @param string $username |
| 1240 | + * @return string |
| 1241 | + * @access private |
| 1242 | + */ |
| 1243 | + function getUserDN( $username, $bind=false ) { |
| 1244 | + $this->printDebug( "Entering getUserDN", NONSENSITIVE ); |
| 1245 | + if ( $bind ) { |
1271 | 1246 | // This is a proxy bind, or an anonymous bind with a search |
1272 | 1247 | $proxyagent = $this->getConf( 'ProxyAgent'); |
1273 | 1248 | if ( $proxyagent ) { |
— | — | @@ -1278,56 +1253,26 @@ |
1279 | 1254 | $this->printDebug( "Doing an anonymous bind", NONSENSITIVE ); |
1280 | 1255 | $bind = $this->bindAs(); |
1281 | 1256 | } |
1282 | | - |
1283 | 1257 | if ( !$bind ) { |
1284 | 1258 | $this->printDebug( "Failed to bind", NONSENSITIVE ); |
1285 | 1259 | return ''; |
1286 | 1260 | } |
1287 | | - |
1288 | | - $userdn = $this->getUserDN( $username ); |
1289 | 1261 | } |
1290 | | - $this->printDebug( "userdn is: $userdn", SENSITIVE ); |
1291 | | - return $userdn; |
1292 | | - } |
1293 | 1262 | |
1294 | | - /** |
1295 | | - * Gets the DN of a user based upon settings for the domain. |
1296 | | - * This function will set $this->LDAPUsername |
1297 | | - * You must bind to the server before calling this. |
1298 | | - * |
1299 | | - * @param string $username |
1300 | | - * @return string |
1301 | | - * @access private |
1302 | | - */ |
1303 | | - function getUserDN( $username ) { |
1304 | | - $this->printDebug( "Entering getUserDN", NONSENSITIVE ); |
1305 | | - |
1306 | 1263 | // we need to do a subbase search for the entry |
1307 | | - // Auto auth needs to check LDAP for required attributes. |
1308 | | - $aa = $this->getConf( 'AuthAttribute' ); |
1309 | | - $searchattr = $this->getConf( 'SearchAttribute' ); |
1310 | | - if ( $aa && $this->useAutoAuth() ) { |
1311 | | - $auth_filter = "(" . $aa . ")"; |
1312 | | - $srch_filter = "(" . $searchattr . "=" . $this->getLdapEscapedString( $username ) . ")"; |
1313 | | - $filter = "(&" . $srch_filter . $auth_filter . ")"; |
1314 | | - $this->printDebug( "Created an auth attribute filter: $filter", SENSITIVE ); |
1315 | | - } else { |
1316 | | - $filter = "(" . $searchattr . "=" . $this->getLdapEscapedString( $username ) . ")"; |
1317 | | - $this->printDebug( "Created a regular filter: $filter", SENSITIVE ); |
1318 | | - } |
| 1264 | + $filter = "(" . $searchattr . "=" . $this->getLdapEscapedString( $username ) . ")"; |
| 1265 | + $this->printDebug( "Created a regular filter: $filter", SENSITIVE ); |
1319 | 1266 | |
| 1267 | + // We explicitly put memberof here because it's an operational attribute in some servers. |
1320 | 1268 | $attributes = array( "*", "memberof" ); |
1321 | 1269 | $base = $this->getBaseDN( USERDN ); |
1322 | | - |
1323 | 1270 | $this->printDebug( "Using base: $base", SENSITIVE ); |
1324 | | - |
1325 | 1271 | $entry = LdapAuthenticationPlugin::ldap_search( $this->ldapconn, $base, $filter, $attributes ); |
1326 | 1272 | if ( LdapAuthenticationPlugin::ldap_count_entries( $this->ldapconn, $entry ) == 0 ) { |
1327 | 1273 | $this->printDebug( "Couldn't find an entry", NONSENSITIVE ); |
1328 | 1274 | $this->fetchedUserInfo = false; |
1329 | 1275 | return ''; |
1330 | 1276 | } |
1331 | | - |
1332 | 1277 | $this->userInfo = LdapAuthenticationPlugin::ldap_get_entries( $this->ldapconn, $entry ); |
1333 | 1278 | $this->fetchedUserInfo = true; |
1334 | 1279 | if ( isset( $this->userInfo[0][$searchattr] ) ) { |
— | — | @@ -1335,36 +1280,28 @@ |
1336 | 1281 | $this->printDebug( "Setting the LDAPUsername based on fetched wgLDAPSearchAttributes: $username", NONSENSITIVE ); |
1337 | 1282 | $this->LDAPUsername = $username; |
1338 | 1283 | } |
1339 | | - |
1340 | | - // This is a pretty useful thing to have for auto authentication, |
1341 | | - // group checking, and pulling preferences. |
1342 | | - wfRunHooks( 'SetUsernameAttributeFromLDAP', array( &$this->LDAPUsername, $this->userInfo ) ); |
1343 | | - if ( !is_string( $this->LDAPUsername ) ) { |
1344 | | - $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 ); |
1345 | | - $this->LDAPUsername = ''; |
1346 | | - } |
1347 | | - |
1348 | 1284 | $userdn = $this->userInfo[0]["dn"]; |
1349 | 1285 | return $userdn; |
1350 | 1286 | } |
1351 | 1287 | |
1352 | 1288 | /** |
1353 | | - * @return array|null |
| 1289 | + * Load the current user's entry |
| 1290 | + * |
| 1291 | + * @return bool |
1354 | 1292 | */ |
1355 | 1293 | function getUserInfo() { |
1356 | 1294 | // Don't fetch the same data more than once |
1357 | 1295 | if ( $this->fetchedUserInfo ) { |
1358 | | - return $this->userInfo; |
| 1296 | + return true; |
1359 | 1297 | } |
1360 | | - |
1361 | 1298 | $userInfo = $this->getUserInfoStateless( $this->userdn ); |
1362 | 1299 | if ( is_null( $userInfo ) ) { |
1363 | 1300 | $this->fetchedUserInfo = false; |
1364 | | - return null; |
1365 | 1301 | } else { |
1366 | 1302 | $this->fetchedUserInfo = true; |
1367 | | - return $userInfo; |
| 1303 | + $this->userInfo = $userInfo; |
1368 | 1304 | } |
| 1305 | + return $this->fetchedUserInfo; |
1369 | 1306 | } |
1370 | 1307 | |
1371 | 1308 | /** |
— | — | @@ -1373,8 +1310,8 @@ |
1374 | 1311 | */ |
1375 | 1312 | function getUserInfoStateless( $userdn ) { |
1376 | 1313 | global $wgMemc; |
1377 | | - $key = wfMemcKey( 'ldapauthentication', 'userinfo', $userdn ); |
1378 | 1314 | |
| 1315 | + $key = wfMemcKey( 'ldapauthentication', 'userinfo', $userdn ); |
1379 | 1316 | $userInfo = $wgMemc->get( $key ); |
1380 | 1317 | if ( !is_array( $userInfo ) ) { |
1381 | 1318 | $entry = LdapAuthenticationPlugin::ldap_read( $this->ldapconn, $userdn, "objectclass=*", array( '*', 'memberof' ) ); |
— | — | @@ -1393,16 +1330,15 @@ |
1394 | 1331 | private function getPreferences() { |
1395 | 1332 | $this->printDebug( "Entering getPreferences", NONSENSITIVE ); |
1396 | 1333 | |
1397 | | - $this->userInfo = $this->getUserInfo(); |
1398 | | - if ( is_null( $this->userInfo ) ) { |
1399 | | - $this->printDebug( "Failed to get preferences", NONSENSITIVE ); |
1400 | | - } |
1401 | | - |
1402 | 1334 | // Retrieve preferences |
1403 | 1335 | $prefs = $this->getConf( 'Preferences' ); |
1404 | 1336 | if ( !$prefs ) { |
1405 | | - return; |
| 1337 | + return null; |
1406 | 1338 | } |
| 1339 | + if ( !$this->getUserInfo() ) { |
| 1340 | + $this->printDebug( "Failed to get preferences, the user's entry wasn't found.", NONSENSITIVE ); |
| 1341 | + return null; |
| 1342 | + } |
1407 | 1343 | $this->printDebug( "Retrieving preferences", NONSENSITIVE ); |
1408 | 1344 | foreach ( array_keys( $prefs ) as $key ) { |
1409 | 1345 | $attr = strtolower( $prefs[$key] ); |
— | — | @@ -1509,11 +1445,9 @@ |
1510 | 1446 | |
1511 | 1447 | if ( $this->getConf( 'GroupsUseMemberOf' ) ) { |
1512 | 1448 | $this->printDebug( "Using memberOf", NONSENSITIVE ); |
1513 | | - $this->userInfo = $this->getUserInfo(); |
1514 | | - if ( is_null( $this->userInfo ) ) { |
1515 | | - $this->printDebug( "Failed to get memberOf attribute", NONSENSITIVE ); |
1516 | | - } |
1517 | | - if ( isset( $this->userInfo[0]["memberof"] ) ) { |
| 1449 | + if ( !$this->getUserInfo() ) { |
| 1450 | + $this->printDebug( "Couldn't get the user's entry.", NONSENSITIVE ); |
| 1451 | + } else if ( isset( $this->userInfo[0]["memberof"] ) ) { |
1518 | 1452 | # The first entry is always a count |
1519 | 1453 | $memberOfMembers = $this->userInfo[0]["memberof"]; |
1520 | 1454 | array_shift( $memberOfMembers ); |
— | — | @@ -1540,13 +1474,11 @@ |
1541 | 1475 | } else { |
1542 | 1476 | $this->printDebug( "Searching for the groups", NONSENSITIVE ); |
1543 | 1477 | $this->userLDAPGroups = $this->searchGroups( $usertopass ); |
1544 | | - |
1545 | 1478 | if ( $this->getConf( 'GroupSearchNestedGroups' ) ) { |
1546 | 1479 | $this->userLDAPGroups = $this->searchNestedGroups( $this->userLDAPGroups ); |
1547 | 1480 | $this->printDebug( "Got the following nested groups:", SENSITIVE, $this->userLDAPGroups["dn"] ); |
1548 | 1481 | } |
1549 | 1482 | } |
1550 | | - |
1551 | 1483 | // Only find all groups if the user has any groups; otherwise, we are |
1552 | 1484 | // just wasting a search. |
1553 | 1485 | if ( $this->getConf( 'GroupsPrevail' ) && count( $this->userLDAPGroups ) != 0 ) { |
— | — | @@ -1575,7 +1507,6 @@ |
1576 | 1508 | } |
1577 | 1509 | |
1578 | 1510 | $this->printDebug( "Searching groups:", SENSITIVE, $groups["dn"] ); |
1579 | | - |
1580 | 1511 | $groupstosearch = array( "short" => array(), "dn" => array() ); |
1581 | 1512 | foreach ( $groups["dn"] as $group ) { |
1582 | 1513 | $returnedgroups = $this->searchGroups( $group ); |
— | — | @@ -1601,7 +1532,6 @@ |
1602 | 1533 | } |
1603 | 1534 | } |
1604 | 1535 | } |
1605 | | - |
1606 | 1536 | $searchedgroups = array_merge_recursive( $groups, $searchedgroups ); |
1607 | 1537 | |
1608 | 1538 | return $this->searchNestedGroups( $groupstosearch, $searchedgroups ); |
— | — | @@ -1617,7 +1547,6 @@ |
1618 | 1548 | $this->printDebug( "Entering searchGroups", NONSENSITIVE ); |
1619 | 1549 | |
1620 | 1550 | $base = $this->getBaseDN( GROUPDN ); |
1621 | | - |
1622 | 1551 | $objectclass = $this->getConf( 'GroupObjectclass' ); |
1623 | 1552 | $attribute = $this->getConf( 'GroupAttribute' ); |
1624 | 1553 | $nameattribute = $this->getConf( 'GroupNameAttribute' ); |
— | — | @@ -1687,24 +1616,19 @@ |
1688 | 1617 | } |
1689 | 1618 | |
1690 | 1619 | $filter = "(&($attribute=$value)(objectclass=$objectclass))"; |
1691 | | - |
1692 | 1620 | $this->printDebug( "Search string: $filter", SENSITIVE ); |
1693 | | - |
1694 | 1621 | $info = LdapAuthenticationPlugin::ldap_search( $this->ldapconn, $base, $filter ); |
1695 | 1622 | if ( !$info ) { |
1696 | 1623 | $this->printDebug( "No entries returned from search.", SENSITIVE ); |
1697 | | - |
1698 | 1624 | // Return an array so that other functions |
1699 | 1625 | // don't error out. |
1700 | 1626 | return array( "short" => array(), "dn" => array() ); |
1701 | 1627 | } |
1702 | 1628 | |
1703 | 1629 | $entries = LdapAuthenticationPlugin::ldap_get_entries( $this->ldapconn, $info ); |
1704 | | - |
1705 | 1630 | if ( $entries ){ |
1706 | 1631 | // We need to shift because the first entry will be a count |
1707 | 1632 | array_shift( $entries ); |
1708 | | - |
1709 | 1633 | // Let's get a list of both full dn groups and shortname groups |
1710 | 1634 | foreach ( $entries as $entry ) { |
1711 | 1635 | $shortMember = strtolower( $entry[$nameattribute][0] ); |
— | — | @@ -1715,7 +1639,6 @@ |
1716 | 1640 | } |
1717 | 1641 | |
1718 | 1642 | $this->printDebug( "Returned groups:", SENSITIVE, $groups["dn"] ); |
1719 | | - |
1720 | 1643 | return $groups; |
1721 | 1644 | } |
1722 | 1645 | |
— | — | @@ -1729,7 +1652,6 @@ |
1730 | 1653 | */ |
1731 | 1654 | function hasLDAPGroup( $group ) { |
1732 | 1655 | $this->printDebug( "Entering hasLDAPGroup", NONSENSITIVE ); |
1733 | | - |
1734 | 1656 | return in_array( strtolower( $group ), $this->userLDAPGroups["short"] ); |
1735 | 1657 | } |
1736 | 1658 | |
— | — | @@ -1742,7 +1664,6 @@ |
1743 | 1665 | */ |
1744 | 1666 | function isLDAPGroup( $group ) { |
1745 | 1667 | $this->printDebug( "Entering isLDAPGroup", NONSENSITIVE ); |
1746 | | - |
1747 | 1668 | return in_array( strtolower( $group ), $this->allLDAPGroups["short"] ); |
1748 | 1669 | } |
1749 | 1670 | |
— | — | @@ -1757,7 +1678,6 @@ |
1758 | 1679 | global $wgGroupPermissions; |
1759 | 1680 | |
1760 | 1681 | // TODO: this is *really* ugly code. clean it up! |
1761 | | - |
1762 | 1682 | $this->printDebug( "Entering setGroups.", NONSENSITIVE ); |
1763 | 1683 | |
1764 | 1684 | # Add ldap groups as local groups |
— | — | @@ -1773,9 +1693,7 @@ |
1774 | 1694 | # add groups permissions |
1775 | 1695 | $localAvailGrps = $user->getAllGroups(); |
1776 | 1696 | $localUserGrps = $user->getEffectiveGroups(); |
1777 | | - |
1778 | 1697 | $defaultLocallyManagedGrps = array( 'bot', 'sysop', 'bureaucrat' ); |
1779 | | - |
1780 | 1698 | $locallyManagedGrps = $this->getConf( 'LocallyManagedGroups' ); |
1781 | 1699 | if ( $locallyManagedGrps ) { |
1782 | 1700 | $locallyManagedGrps = array_unique( array_merge( $defaultLocallyManagedGrps, $locallyManagedGrps ) ); |
— | — | @@ -1787,7 +1705,6 @@ |
1788 | 1706 | |
1789 | 1707 | $this->printDebug( "Available groups are: ", NONSENSITIVE, $localAvailGrps ); |
1790 | 1708 | $this->printDebug( "Effective groups are: ", NONSENSITIVE, $localUserGrps ); |
1791 | | - |
1792 | 1709 | # note: $localUserGrps does not need to be updated with $cGroup added, |
1793 | 1710 | # as $localAvailGrps contains $cGroup only once. |
1794 | 1711 | foreach ( $localAvailGrps as $cGroup ) { |