Go to content Go to navigation Go to search
setting up mediawiki with active directory

The problem is this: How do you get MediaWiki which runs on top of XAMPP to be secure using Active Directory?

We wanted to secure our MediaWiki site so that users can log in using their Active Directory account. In addition we wanted only some users to be able to read the Wiki pages and a smaller subset of those users to be able to edit pages.

MediaWiki does have an LDAP Authentication extension but it is not obvious from the extension web page how to set it up. I came across a lot of web pages to get this final solution, but this this one was especially helpful. So without further adieu..

how to set up the extension

1. First, you need to enable the LDAP module in PHP (recent versions of PHP have this built in). To do this, you need to find the php.ini file that your installation is using. I found mine in xampp\apache\bin\php.ini. If you’re not sure which one to change you can do the following:

    Create a file called phpinfo.php and put this code in it:

    <?php  phpinfo();  ?>

    Run it from your browser (http://server/directory/phpinfo.php) and the page that comes up will show you the php.ini path.

2. Edit the php.ini file and remove the semicolon at the head of this line:

;extension=php_ldap.dll

3. Next, copy the LdapAuthentication.php file to your wiki includes directory. In my case this directory is xampp\htdocs\wiki\includes.

4. Add the following lines to your LocalSettings.php, replacing the bold items with actual values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
   #### LDAP settings 
   require_once( '$IP/includesLdapAuthentication.php' );
   $wgAuth = new LdapAuthenticationPlugin();
   #### Uncomment this line to see debug messages:
   #$wgLDAPDebug = 10;
   $wgLDAPDomainNames = array('<strong>DOMAINNAME</strong>',);
   $wgLDAPServerNames = array('<strong>DOMAINNAME</strong>' => '<strong>DOMAINCONTROLLERNAME</strong>',);
   $wgLDAPEncryptionType = array('<strong>DOMAINNAME</strong>' => 'clear',);
   $wgLDAPBaseDNs = array('<strong>DOMAINNAME</strong>' => '<strong>BASEDN</strong>');
   $wgLDAPSearchAttributes = array('<strong>DOMAINNAME</strong>' => 'sAMAccountName');
   $wgLDAPProxyAgent = array("<strong>DOMAINNAME</strong>"=>"<strong>PROXYUSERDN</strong>");
   $wgLDAPProxyAgentPassword = array("<strong>DOMAINNAME</strong>"=>"<strong>PROXYUSERPASS</strong>");
   $wgLDAPUpdateLDAP = array("<strong>DOMAINNAME</strong>"=>false);
   $wgLDAPAddLDAPUsers = array("<strong>DOMAINNAME</strong>"=>false);
 
   $wgLDAPGroupUseFullDN = array("<strong>DOMAINNAME</strong>"=>true);
   $wgLDAPGroupObjectclass = array( "<strong>DOMAINNAME</strong>"=&gt;"group" );
   $wgLDAPGroupAttribute = array( "<strong>DOMAINNAME</strong>"=&gt;"member" );
   $wgLDAPGroupMemberOfAttribute = array( "<strong>DOMAINNAME</strong>"=>"memberof" );
   $wgLDAPGroupSearchNestedGroups = array( "<strong>DOMAINNAME</strong>"=>true );
   $wgLDAPGroupNameAttribute = array( "<strong>DOMAINNAME</strong>"=>"cn" );
   $wgLDAPUseLDAPGroups = array( "<strong>DOMAINNAME</strong>"=>true);
 
   #### restrict to logged in users only$wgGroupPermissions['*']['edit'] = false;
   $wgGroupPermissions['user']['edit'] = false;
   $wgGroupPermissions['<strong>EDITGROUPNAME</strong>']['edit'] = true;
   $wgGroupPermissions['*']['read'] = false;
   $wgGroupPermissions['user']['read'] = false;
   $wgGroupPermissions['<strong>READONLYGROUPNAME</strong>']['read'] = true;
   $wgGroupPermissions['<strong>EDITGROUPNAME</strong>']['read'] = true;
   $wgGroupPermissions['*']['createaccount'] = false;
 
   #### make sure login and logout pages are visible
   $wgWhitelistRead = array( "Special:Userlogout", "Special:Userlogin" );

nested groups

The above will set up MediaWiki to connect to your Active Directory. However, the default implementation of the extension does not support nested groups when it comes to determining the groups that a user is in. If you want your user to be a member of a group which is a member of another group, and use the parent group in your wiki settings, then you need to make this modification. In order to do this I had to modify the LDAP Authentication extension. This requires changing the LdapAuthentication.php file:

1. Add the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
function mygetGroups( $ldapconn, $dn ) {
        global $wgLDAPGroupObjectclass, $wgLDAPGroupAttribute, $wgLDAPGroupNameAttribute, $wgLDAPGroupMemberOfAttribute;
        global $wgLDAPProxyAgent, $wgLDAPProxyAgentPassword;
        global $wgUser;
 
        //$this-&gt;printDebug( "Entering getGroups", NONSENSITIVE );
 
        $nameattribute = $wgLDAPGroupNameAttribute[$_SESSION['wsDomain']];
        $memberofattr = $wgLDAPGroupMemberOfAttribute[$_SESSION['wsDomain']];
 
        $groups = array();
        $shortnamegroups = array();
        $entry = @ldap_read( $ldapconn, $dn, "objectclass=*" );
        $infos = @ldap_get_entries( $ldapconn, $entry );
        if (isset($infos[0][$memberofattr])) {
            //We need to shift because the first entry will be a count
            array_shift( $infos[0][$memberofattr] );
            foreach ($infos[0][$memberofattr] as $memberof)
            {
                $entryName = @ldap_read( $ldapconn, $memberof, "objectclass=*" );
                $infosName = @ldap_get_entries( $ldapconn, $entryName );
                if (isset($infosName[0][$nameattribute])) {
                            $this->printDebug( "memberof=" .  strtolower($infosName[0][$nameattribute][0]), SENSITIVE);
                    array_push( $groups, $memberof );
                    array_push( $shortnamegroups, strtolower($infosName[0][$nameattribute][0]) );
                }
            }
        }
        $both_groups = array();
        array_push( $both_groups, $groups );
        array_push( $both_groups, $shortnamegroups );
 
        //$this->printDebug( "Returned groups:" . implode( ",", $groups ) . "", SENSITIVE );
        $this->printDebug( "Returned groups:" . implode( ", ", $shortnamegroups ) . "", SENSITIVE );
 
        return $both_groups;
    }
    function mysearchNestedGroups( $ldapconn, $groups, $groupsshort, &amp;$checkedgroups, &amp;$checkedgroupsshort ) {
        global $wgLDAPRequiredGroups;
 
        $this->printDebug( "Entering searchNestedGroups for : " . implode( " // ", $groups ), NONSENSITIVE );
 
        //base case, no more groups left to check
        if ( !$groups ) {
            $this->printDebug( "Couldn't find user in any nested groups.", NONSENSITIVE );
            return false;
        }
 
        $this->printDebug( "Checking groups:" . implode( ",", $groups ) . "", SENSITIVE );
 
        $reqgroups = $wgLDAPRequiredGroups[$_SESSION['wsDomain']];
        for ( $i = 0; $i &lt; count( $reqgroups ); $i++ ) {
            $reqgroups[$i] = strtolower( $reqgroups[$i] );
        }
 
        $checkedgroups = array_unique( array_merge( $groups, $checkedgroups ) );
        $checkedgroupsshort = array_unique( array_merge( $groupsshort, $checkedgroupsshort ) );
        $groupstocheck = array();
        $groupstocheckshort = array();
        foreach ( $groups as $group ) {
            list( $returnedgroups, $returnedgroupsshort ) = $this->mygetGroups( $ldapconn, $group );
            $this->printDebug( "*************** Group $group is in the following groups:" . implode( " // ", $returnedgroups ) . "", SENSITIVE );
            $i = 0;
            foreach ( $returnedgroups as $checkme ) {
                if ( in_array( $checkme, $checkedgroups ) ) {
                    //We already checked this, move on
                    continue;
                }
                $this->printDebug( "Checking membership for: $checkme", SENSITIVE );
                array_push( $groupstocheck, $checkme );
                                array_push( $groupstocheckshort, $returnedgroupsshort[i] );
                $i++;
            }
            $checkedgroups = array_unique( array_merge( $returnedgroups, $checkedgroups ) );
            $checkedgroupsshort = array_unique( array_merge( $returnedgroupsshort, $checkedgroupsshort ) );
        }        
 
        //Mmmmmm. Tail recursion. Tasty.
        return $this->mysearchNestedGroups( $ldapconn, $groupstocheck, $groupstocheckshort, $checkedgroups, $checkedgroupsshort );
     }

2. Add code to the authenticate function (new code is in bold):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//Synch LDAP groups with MediaWiki groups 
if ( isset( $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) && $wgLDAPUseLDAPGroups[$_SESSION['wsDomain']] ) {
     $this->printDebug( "Retrieving LDAP group membership", NONSENSITIVE );
 
     //Let's get the user's LDAP groups
     if ( isset( $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseFullDN[$_SESSION['wsDomain']] ) {
         $this-&gt;userLDAPGroups = $this->getUserGroups( $ldapconn, $userdn, true );
         <strong>$userLDAPGroupsFull = $this-&gt;getUserGroups( $ldapconn, $userdn, false );</strong>
       } else {
         if ( ( isset( $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] ) && $wgLDAPGroupUseRetrievedUsername[$_SESSION['wsDomain']] )
             && $this->LDAPUsername != '' ) {
 
             $this->userLDAPGroups = $this->getUserGroups( $ldapconn, $this->LDAPUsername, true );
             <strong>$userLDAPGroupsFull = $this->getUserGroups( $ldapconn, $this-&gt;LDAPUsername, false );</strong>
           } else {
             $this-&gt;userLDAPGroups = $this->getUserGroups( $ldapconn, $username, true );
             <strong>$userLDAPGroupsFull = $this-&gt;getUserGroups( $ldapconn, $username, false );  </strong>
         }
     }
 
     <strong>global $wgLDAPGroupSearchNestedGroups;
     $searchnested = $wgLDAPGroupSearchNestedGroups[$_SESSION['wsDomain']];
     if ( $searchnested ) {
         $this-&gt;printDebug("Searching nested..", SENSITIVE);
         $foundgroups = array();
         $foundgroupsshort = array();
         $this->mysearchNestedGroups( $ldapconn, $userLDAPGroupsFull, $this-&gt;userLDAPGroups, $foundgroups, $foundgroupsshort );
         $this->printDebug("Found groups: " . implode( ",", $foundgroupsshort ) . "", SENSITIVE );
         $this-&gt;userLDAPGroups = $foundgroupsshort;
     }</strong>

12 Comments for “setting up mediawiki with active directory”

  1. Didn’t work for me!

    Yet.

    But thanks anyway!

    by Duncan Smith @ August 14th, 2008
  2. Worked great for me! Saved me a ton of time getting it right.

    I did have to change a couple of items:

    1. put a line break in to put require_once on it’s own line
    2. changed the contents of require_once to “$IP/includes/LdapAuthentication.php”

    Thanks for a great post!

    by Darin Pope @ August 25th, 2008
  3. Two things:
    1. Where do you define the groups, so I can setup a edit group and a read only group for Active Directory.
    2. What do you have to do after you make the changes to see the new login take affect

    by Richard Scott @ October 16th, 2008
  4. I’ve updated the post to reflect the changes Darin talks about, and bolded the group names for Richard.

    In order to have these changes take affect, you should restart your Apache server.

    by gil @ October 16th, 2008
  5. Gil…I had a feeling that is where the groups had to go, but I made the changes and restarted IIS…all I get now is a blank page…in you post above…towards the end…you have code that talks about making sure the login and logout pages are visible…I cannot see that section…it appears to go below that window and I cannot scroll…is there something else I need to do to get this to work…Thank you very much for your patience…this is my first adventure with PHP…

    by Richard Scott @ October 22nd, 2008
  6. Try uncommenting the line that prints logging, and see what it says. The line looks like this, just remove the hash in the beginning:
    #$wgLDAPDebug = 10

    by gil @ October 22nd, 2008
  7. Gil…I tried that and restarted IIS…I still am getting a blank page…if I were to email you a snapshot of our localsettings.php..could you have a better idea of what may need to be changed…I inherited this project and am trying to learn and implement at the same time…I really appreaciate your help on this…

    by Richard Scott @ October 23rd, 2008
  8. gil, awesome info. Do you know what settings I need to use if I can’t do an anonymous bind? I think this is the last thing I’m missing to get this to work like I need to.

    Thanks,
    – Jay

    by Jay @ May 20th, 2009
  9. Jay, the code above is not for an anonymous bind. Make sure you set the PROXYUSERDN and PROXYUSERPASSWORD to the user you want to bind with.

    by gil @ July 5th, 2009
  10. Thanks a lot for this article. This saved me lot of hassle :)

    by Harp Dhillon @ July 10th, 2009
  11. I know this post is fairly old, but I just ran across it. Nested group support is definitely supported, and it works without any code modification.

    The code even now supports memberOf…

    by Ryan Lane @ January 11th, 2010
  12. The FIRST thing a article should mention is the OS. Setting up mediawiki AD authentication is *a lot* different on Linux and Windows.

    And is a lot different if hosting in IIS (with php plugin) than Apache.

    by Dude123 @ September 10th, 2011