Extension:LDAP Authentication

From MediaWiki.org

Jump to: navigation, search
Manual on MediaWiki Extensions
List of MediaWiki Extensions
LDAP Authentication

Release status: stable

Implementation User identity
Description Provides LDAP authentication, and some authorization functionality for MediaWiki
Author(s) Ryan Lane
Version 1.1g, 1.0h (2006-12-04)
MediaWiki 1.6+ for 1.1g, 1.5+ for 1.0h
Download [1]

Contents

[edit] LDAP Authentication configuration examples

Examples of how to use this plugin can be found here:

[edit] About this page

[edit] The documentation has been updated to reflect version 1.1c and higher

Caution! Caution: Some options changed from 1.1b to 1.1c, make sure when configuring a new version that the options you are currently using are still valid. The changelog mentions which options have changed.

Support will no longer be given for version 1.0; the prior version of the documentation can be found here.

[edit] Post support questions on the discussion page or on the mediawiki-enterprise list

Please post all support questions on this page's discussion page or on the mediawiki-enterprise list. If a problem needs special attention, I can contact you directly by email. Posting the questions on the discussion page allows everyone to see how the problem was resolved.

Posting anywhere else will usually cause your problem to be ignored, or cause people to get upset with you.

[edit] Current version

Version 1.1g of the LdapAuthentication.php plugin (for MediaWiki 1.6+)

This plugin should be scalable for use in small to large organizations, and provides the following functionality:

  • Single and multi domain authentication (including local database)
    • Simple bind authentication
    • Proxy bind authentication
    • Smartcard/CAC/PKI Soft Certificate authentication
    • SSL/TLS or non-SSL/TLS binding allowed
    • Nested/Unnested Group based restriction support
    • Filter based restriction support
  • Retrieval of user information from LDAP
    • Email address
    • Real name
    • Nickname
    • Language
  • Synchronization of LDAP groups to MediaWiki security groups (LDAP->MediaWiki only, nested groups not supported)
  • Storing preferences in LDAP
    • Update passwords [2]
    • Mail me a password [2]
    • Update all preferences that are currently retrievable
  • Creation of users in LDAP

[edit] Requirements

  • MediaWiki 1.6+ for current version of the plugin (I will no longer be backporting to the 1.5 series)
  • PHP must be compiled with LDAP support for any functionality at all
  • PHP must be compiled with SSL support if you wish to authenticate over SSL (HIGHLY Recommended!!)
    • Your server must trust the LDAP server's Certificate's Root CA for SSL to work (mostly affects you if you are using self signed certificates)
    • The DNS name for your LDAP server must match the name in the LDAP server's certificate for SSL to work
  • Smartcard/CAC authentication requires a PEM encoded list of CAs, proxy or anonymous (if allowed) LDAP credentials, and an SSL enabled webserver
  • If you would like to use LDAP as a backend for MediaWiki (creating users, changing passwords, etc), you must provide a user who has write permissions to specific user attributes (please only give this user the minimum amount of access that is required)

[edit] Tested on

Current Version (1.1) has been tested on:

  • MediaWiki
    • MediaWiki 1.6 (does not work on 1.5, but version 1.0 does!)
    • MediaWiki 1.7
    • MediaWiki 1.8
    • MediaWiki 1.9 (NOTE: there are some bugs here (including a fatal source code bug for 1.9.3), a couple workarounds should be used for the time being, see the talk page)
    • MediaWiki 1.10
    • MediaWiki 1.11
    • MediaWiki 1.12
  • Operating Systems
    • Debian GNU/Linux 4.0 ("Etch")
    • Ubuntu 7.04
    • Red Hat Enterprise Linux v4 AS
    • Red Hat Enterprise Linux v4 ES
    • Red Hat Enterprise Linux v4 WS
    • Red Hat Enterprise Linux v5
    • Fedora Core 6
    • Solaris 10
    • Suse Linux Enterprise Server 10
    • Microsoft Windows 2003
    • Gentoo Linux (extension revision 20306)
    • CentOS 4
    • CentOS 5
    • Novell NetWare 6.5 SP7
  • LDAP Directories
    • CA Directory (eTrust Directory)
    • Sun Directory Server 5.2
    • Sun Directory Server Enterprise Edition 6.1
    • Active Directory 2003
    • Novell eDirectory (NDS) v8.7.3
    • Novell eDirectory (NDS) v8.8.2
    • OpenLDAP (extension revision 20306)
    • Mac OS X Open Directory v10.4.9
    • Fedora Directory Server 1.0.4
    • ApacheDS 1.5.2
  • Web Servers
    • Apache 2.0
    • Apache 2.2
    • IIS6+PHP ISAPI
  • Combinations
    • Debian 4.0, MediaWiki 1.7, PHP 5.2.0, MySQL 5.0.32, Apache 2.2.3, OpenLDAP
    • Solaris 10, MediaWiki 1.9.x, PHP, MySQL, Apache2, CA Directory
    • RHEL v4 AS, MediaWiki 1.6.8, PHP 4.3.9, MySQL 4.1.12-3, Apache 2.0.52-22, Sun Directory Server 5.2 patch 4
    • Windows 2003, MediaWiki 1.8.3, PHP 5.2.0, MySQL 5.0, IIS6, Microsoft Active Directory
    • Windows 2003, MediaWiki 1.12, PHP 5.2.5, MySQL 5.0, Apache Server 2.2, apacheds 1.5.2
    • Gentoo Linux, MediaWiki 1.9.x, PHP, MySQL, Apache 2, OpenLDAP, extension revision 20306, Samba LDAP schema
    • CentOS 5, MediaWiki 1.10.0, PHP 5, MySQL 5, Fedora Directory Server 1.0.4
    • CentOS 5, MediaWiki 1.10.1, PHP 5.1.6, MySQL 5.0.22, Microsoft Active Directory
    • SLES 10, MediaWiki 1.10.0, PHP 5, MySQL 5, Apache 2.2, Openldap
    • SLES 9, MediaWiki 1.6.7, PHP4, MySQL 5, Novel eDirectory
    • OpenSuse 10.2, MediaWiki 1.9.x, PHP5, MySQL 5, Microsoft Active Directory
    • Novell NetWare 6.5 SP7, MediaWiki 1.11.0, PHP 5.2.5, MySQL 5.0.45, Apache 2.0.61, Novell eDirectory 8.8.2
    • CentOS 4, MediaWiki 1.11.1, PHP 5.2.5, MySQL 4.1.22, Apache 2.0.26, OpenLDAP 2.2.13
    • Ubuntu 6.06, MediaWiki 1.12.0, PHP 5.1.2-1ubuntu3.10, MySQL 5.0.22-0ubuntu6.06.10, Apache 2.0.55-4ubuntu2.3, Microsoft Active Directory
    • CentOS (vmware on a MAC OS X), MW 1.12.0, PHP 5.1.6 (apache2handler), MySQL 5.0.45, MicroSoft Active Directory

If you have a working wiki with a working version of the patch on something not listed above, please add it to the list!

[edit] Changelog

[edit] v1.1g

  • Fixed compatability with php4 (MediaWiki 1.6) - Patch from Bill Allison
  • Only call getAllGroups() if $wgLDAPGroupsPrevail is enabled
  • Added option $wgLDAPLocallyManagedGroups, to specify which groups won't have members automatically removed
    • The sysop, bureaucrat, and bot groups are always considered locally managed
  • Fixed security issue where users weren't removed from groups when the LDAP group was deleted

[edit] v1.1f

  • Added options to specify search bases for users, and groups
    • Updated documentation to come
  • Fixed Special:Preferences prints PHP warning, "Undefined index: wsDomain in PATH/LdapAuthentication.php", for lines 591 and 594. MediaWiki 1.9.3, Windows 2003 server, PHP 5.2.2-dev snapshot. 66.147.182.92 14:27, 29 March 2007 (UTC)
  • Fixed Extension_talk:LDAP_Authentication#allowPasswordChange_doesn.27t_check_for_.24wgLDAPUseLocal
  • Integrated the code from Extension:LDAP_Authentication#Better_support_for_groups so that other extensions can use all available LDAP groups
  • Added better debug information for (nested) group restrictions
  • Added checks for php_ldap; plugin will fail gracefully if it does not exist
  • Added configuration option for modifying LDAP options on connect (patch from Daniel Marczisovszky)
    • Updated documentation to come
  • Fixed issue where local usernames were munged when $wgLDAPUseLocal was enabled
  • Fixed issue where local users were created with a blank password when $wgLDAPUseLocal was enabled
  • Changed link in source to point to mediawiki.org instead of meta
  • Fixed Nested Groups bug Extension_talk:LDAP_Authentication#Nested_Groups_option_not_working

[edit] v1.1e

[edit] v1.1d

[edit] v1.1c

  • Added support for Mediawiki security groups based upon LDAP groups
  • Added an option to disable auto-creation of accounts ($wgLDAPDisableAutoCreate - defaults to false)
  • Fixed TLS/SSL issue discussed in the Suggestions section on the Meta page
    • Removed options $wgLDAPUseSSL and $wgLDAPUseTLS; added option $wgLDAPEncryptionType (an array) with allowed values "ssl", "tls", and "clear"
  • Moved $wgLDAPLowerCaseUsername a little higher up in the authentication chain
  • Added $wgLDAPGroupUseRetrievedUsername so that you can use the exact username pulled from LDAP to search for groups
  • Changed $wgLDAPUseSmartcardAuth to $wgLDAPAutoAuthMethod (a string); this will allow users to define which type of auto authenticate methods they would like to use. Smartcard auth will only be available at first, but other methods will follow.
  • Changed $wgLDAPLowerCaseUsername to allow for multiple domains
  • Moved authenticate part of smartcard login out of getCanonicalName to the SSLAuth function (I have no clue what I was thinking before ;) )

[edit] v1.1b

  • Fixed bug in 1.1a with user's preferences being overwritten when $wgLDAPRetrievePrefs was not set, or was set to false. The issue should only have affected 1.1a; 1.0h should not be affected.

[edit] v1.1a

  • TLS is now the default encryption mode. To disable SSL/TLS, you need to specifically disable both.
  • Fixed bug in getGroups, searchNestedGroups, and isMemberOfRequiredLdapGroup where warnings are thrown if no groups are found. This was a symptom of a problem in Comment #133 (this would not fix that issue however).
  • Fixed bug with pulled preferences not being saved in the local database.
  • Options have changed to work for multiple domains. All options that make sense with multi-domain support can be configured to work for multiple domains.
  • Smartcard/CAC support has been added to the plugin using the AutoAuthenticate hook.
    • Most options supported by password authentication are supported in smartcard authentication
    • Only a single smartcard domain can be used due to the way AutoAuthenticate works; however, smartcard authentication and password authentication can be mixed allowing multiple domains through the use of clever hackery
    • Smartcard authentication does not have to be turned on for the entire server, but can instead be turned on for certain locations, or even specific wiki pages

Smartcard authentication requires the getCanonicalName() function which is only available in MediaWiki 1.6+. Do not use this version of the plugin for mediawiki 1.5 as it has not been tested and will not be supported; instead, please use version 1.0h.

Many options have changed syntax in this version. Please check the new syntax rules before upgrading to this version.

[edit] v1.0h

  • Fixed #118 on the bugzilla page (lowercasing the username in the groups checking)
    • The fix proposed may have caused issues with other users who need case sensitive searches. I've added the fix as a boolean option ($wgLDAPGroupLowerCaseUsername).
  • Fixed #125 on the bugzilla page (redundant auth attr check)
    • This fix disabled the check, it did not remove it. If you would like to re-enable it for performance reasons, just uncomment the section that was commented.
  • Fixed #126 on the bugzilla page (setPassword function erasing the user's password in LDAP)

[edit] v1.0g

  • Added a new group based restrictions method
    • Added a number of new options to support this method
    • This method will try to use proxyagent credentials if available to search for groups; the plugin falls back on user credentials if proxyagent credentials fail
    • Support for nested groups is available with this new method as long as the attribute for nested groups is the same attribute used for holding users in groups (such as member=testuser, and member=nestedgroup)
    • Support for username or DNs in groups (testuser vs cn=testuser,ou=people,dc=example,dc=com)
    • Support for multiple groups
    • Support for multiple domains

[edit] v1.0f

  • Fixed the version number at the top of the file.
  • Fixed the preferences bug from: Talk:LDAP Authentication#Problem with preferences from LDAP
  • Added function in for changing usernames to lowercase to fix: Extension Talk:LDAP_Authentication#Username_modified_.28capital_letter.29.2C_authentication_fails (only works in versions 1.6+)
  • Fixed an undeclared global variable $wgLDAPWriteLocation in addUsers
  • Cleaned out some unused global variables (I think there may be a couple still hanging around. I'll try to clean them out next version)
  • Added debugging code (let me know what extra debugging info you want, or if some things should be showing at a different debug level)
  • Added the password switching statement from comment #111 in the bugzilla (notice I added it for changing passwords, and for creating users)
  • Added the ability to use TLS as well as LDAPS (I'm not 100% sure this is working, let me know!)

[edit] Roadmap

[edit] Next version

I have a bad memory, and need a to-do list. If I have promised to add something for you in the next version, and it isn't in the list below, please add it.

[edit] v1.2a

  • Rework the auto authentication code
  • Add support for web server authentication
  • Add support for Kerberos Authentication (through web server authentication)
  • Add support for automatic domain discovery
  • Add an easy AD configuration option
  • Fix group syncing bug with removed LDAP groups
  • Fix check for returns with no entries (AD always returns an entry, which says no entries were returned - I hate you AD)
  • Add "functionality to ignore email confirmation"
  • Add option for defining attributes to use when pulling preferences
  • Fix PHP warnings on User Profile tab of Special:Preferences (1.1f, Mediawiki 1.10.1, PHP 5.2.2):
    Notice: Undefined index: wsDomain in LdapAuthentication.php on line 716
    Notice: Undefined index: wsDomain in LdapAuthentication.php on line 720
  • Fix PHP warnings in allowPasswordChange when Special:Preferences has "Edit pages on double click (JavaScript)" checked on the Editing tab:
    Notice: Undefined index: wsDomain in LdapAuthentication.php on line 727
    Notice: Undefined index: wsDomain in LdapAuthentication.php on line 736
  • Add support for ldapi (patch by Matej Vela)
  • Fix Extension talk:LDAP Authentication#undefined variables: .24mailpassword and .24updateLDAP
  • Fix Extension talk:LDAP Authentication#Bugs when adding user
  • Fix SSLAuth function to work with newer versions of MediaWiki

[edit] v1.2b

  • Allow group syncronization to work with nested groups

[edit] Possibly in a future version

  • Allow changes to LDAP groups via Special:Userrights
  • Support for choosing default search scope, and defining it for multiple domains.
  • Support for adding users/changing passwords in Active Directory.
  • Support for using LDAP as a complete user backend (including user options and such). Using ldap as a backend will require a custom schema to be loaded in the LDAP server.

[edit] Explanation of options

The following are options that are usable in "LocalSettings.php":

(These are examples of the extension options, this is not a working example however)

Options will not work if put at the beginning of LocalSettings.php.

[edit] Enabling the plugin

[edit] When using password authentication

If the plugin is dropped in includes:

require_once( 'LdapAuthentication.php' );
$wgAuth = new LdapAuthenticationPlugin();

If the plugin is dropped in extensions:

require_once( "$IP/extensions/LdapAuthentication.php" );
$wgAuth = new LdapAuthenticationPlugin();

[edit] When using smartcard authentication

If the plugin is dropped in includes:

require_once( 'LdapAuthentication.php' );
//options go here
AutoAuthSetup();

If the plugin is dropped in extensions:

require_once( "$IP/extensions/LdapAuthentication.php" );
//options go here
AutoAuthSetup();

[edit] Domain, server and connection configuration options

//The names of one or more domains you wish to use
//These names will be used for the other options, it is freely choosable and not dependent
//on your system. These names will show in the Login-Screen, so it is important that the user 
//understands the meaning.
$wgLDAPDomainNames = array(
  "testADdomain","testLDAPdomain"
  );
 
//The fully qualified name of one or more servers per domain you wish to use. If you are
//going to use SSL or StartTLS, it is important that the server names provided here exactly
//match the name provided by the SSL certificate returned by the server; otherwise, you may
//have problems.
$wgLDAPServerNames = array(
  "testADdomain"=>"testADserver.AD.example.com",
  "testLDAPdomain"=>"testLDAPserver.LDAP.example.com testLDAPserver2.LDAP.example.com"
  );
 
//Allow the use of the local database as well as the LDAP database.
//Good for transitional purposes.
//Default: false
$wgLDAPUseLocal = false;
 
//The type of encryption you would like to use when connecting to the LDAP server.
//Available options are "tls", "ssl", and "clear"
//Default: tls
$wgLDAPEncryptionType = array(
  "testADdomain"=>"tls",
  "testLDAPdomain"=>"clear"
  );

[edit] Binding configuration options

[edit] Straight DN bind options

//The search string to be used for straight binds to the directory; USER-NAME will be
//replaced by the username of the user logging in.
//This option is not required (and shouldn't be provided) if you are using a proxyagent
//and proxyagent password.
//If you are using AD style binding (TDOMAIN\\USER-NAME or USER-NAME@TDOMAIN) and
//want to be able to use group syncing, preference pulling, etc., you'll need to set
//$wgLDAPBaseDNs and $wgLDAPSearchAttributes for the domain.
$wgLDAPSearchStrings = array(
  "testADdomain"=>"TDOMAIN\\USER-NAME",
  "testLDAPdomain"=>"uid=USER-NAME,ou=people,dc=LDAP,dc=example,dc=com"
  );

[edit] Proxied or search based bind options

//User and password used for proxyagent access.
//Please use a user with limited access, NOT your directory manager!
$wgLDAPProxyAgent = array(
  "testLDAPdomain"=>"cn=proxyagent,ou=profile,dc=LDAP,dc=example,dc=com"
  );
$wgLDAPProxyAgentPassword = array(
  "testLDAPdomain"=>"S0M3L0ngP@$$w0r6ofS0meV@rie222y!"
  );
 
//Search filter.
//These options are only needed if you want to search for users to bind with them. In otherwords,
//if you cannot do direct binds based upon $wgLDAPSearchStrings, then you'll need these two options.
//If you need a proxyagent to search, remember to set $wgLDAPProxyAgent, and $wgLDAPProxyAgentPassword.
//Anonymous searching is supported. To do an anonymous search, use SearchAttibutes and don't set a Proxy
//agent for the domain required.
$wgLDAPSearchAttributes = array(
  "testADdomain"=>"sAMAccountName",
  "testLDAPdomain"=>"uid"
  );
 
//Base DNs. Group and User base DNs will be used if available; if they are not defined, the search
//will default to $wgLDAPBaseDNs
$wgLDAPBaseDNs = array(
  "testADdomain"=>"dc=AD,dc=example,dc=com",
  "testLDAPdomain"=>"dc=LDAP,dc=example,dc=com"
  );
$wgLDAPGroupBaseDNs = array(
  "testADdomain"=>"ou=Domain Groups,dc=AD,dc=example,dc=com",
  "testLDAPdomain"=>"ou=group,dc=LDAP,dc=example,dc=com"
  );
$wgLDAPUserBaseDNs = array(
  "testADdomain"=>"ou=Domain Users,dc=AD,dc=example,dc=com",
  "testLDAPdomain"=>"ou=people,dc=LDAP,dc=example,dc=com"
  );

[edit] Options for using LDAP as a user backend

//User and password used for writing to the directory.
//Please use a user with limited access, NOT your directory manager!
$wgLDAPWriterDN = array(
  "testLDAPdomain"=>"uid=priviledgedUser,ou=people,dc=LDAP,dc=example,dc=com"
  );
$wgLDAPWriterPassword = array(
  "testLDAPdomain"=>"S0M3L0ngP@$$w0r6ofS0meV@rie222y!"
  );
 
//A location to add users to if you are using $wgLDAPSearchAttributes and $wgLDAPAddLDAPUsers.
//This option requires $wgLDAPWriterDN and $wgLDAPWriterPassword to be set.
$wgLDAPWriteLocation = array(
  "testLDAPdomain"=>"ou=people,dc=LDAP,dc=example,dc=com"
  );
 
//Options for adding users, and/or updating user preferences in LDAP. If you use these options
//you must set $wgLDAPWriterDN and $wgLDAPWriterPassword.
//Defaults: false
$wgLDAPAddLDAPUsers = array(
  "testADdomain"=>false,
  "testLDAPdomain"=>true
  );
$wgLDAPUpdateLDAP = array(
  "testADdomain"=>false,
  "testLDAPdomain"=>true
  );
 
//Change the hashing algorithm that is used when changing passwords or creating
//user accounts. The default (not setting this variable) will use a base64 encoded
//SHA encrypted password. I do not recommend setting this variable unless you need to
//store clear text or crypt passwords.
//Default: sha
$wgLDAPPasswordHash = array(
  "testLDAPdomain"=>"crypt"
  );
 
//Option for mailing temporary passwords to users
//(notice, this will store the temporary password in the local directory
// if you cannot write LDAP passwords because writing is turned off,
// this probably won't help you much since users will not be able to change
// their password)
//This option requires $wgLDAPWriterDN, $wgLDAPWriterPassword and $wgLDAPUpdateLDAP
//Default: false
$wgLDAPMailPassword = true;
 
//Option for allowing the retreival of user preferences from LDAP.
//Only pulls a small amount of info currently.
//Default: false
$wgLDAPRetrievePrefs = array(
  "testADdomain"=>true,
  "testLDAPdomain"=>true
  );

[edit] MediaWiki user creation options

//Don't automatically create an account for a user if the account exists in LDAP
//but not in MediaWiki.
//Default: false.
$wgLDAPDisableAutoCreate = array(
  "testADdomain"=>true
  );
 
//Shortest password a user is allowed to login using. Notice that 1 is the minimum so that
//when using a local domain, local users cannot login as domain users (as domain user's
//passwords are not stored)
//Default: 0
$wgMinimalPasswordLength = 1;

[edit] Debugging options

//Option for getting debug output from the plugin. 1-3 available. 1 will show
//non-sensitive info, 2 will show possibly sensitive user info, 3+ will show
//sensitive system info. Setting this on a live public site is probably a bad
//idea.
//Default: 0
$wgLDAPDebug = 1;

[edit] Group options

Using LDAP groups in any way requires $wgLDAPBaseDNs to be set!

The following settings pertain to both synchronizing groups, and group based login restriction.

//Whether the username in the group is a full DN (AD generally does this), or
//just the username (posix groups generally do this)
//Default: false
$wgLDAPGroupUseFullDN = array(
  "testLDAPdomain"=>true,
  "testADdomain"=>true
  );
 
//Munge the case of the username to lowercase when doing searches in groups
//Default: false
$wgLDAPLowerCaseUsername = array(
  "testLDAPdomain"=>true,
  "testADdomain"=>true
  );
 
//Use the exact name retrieved from LDAP after the user has authenticated to search for groups.
//This requires the SetUsernameAttributeFromLDAP hook to be used (see the smartcard section).
//Default: false
$wgLDAPGroupUseRetrievedUsername = array(
  "testLDAPdomain"=>false,
  "testADdomain"=>false
  );
 
//The objectclass of the groups we want to search for
$wgLDAPGroupObjectclass = array(
  "testLDAPdomain"=>"groupofuniquenames",
  "testADdomain"=>"group"
  );
 
//The attribute used for group members
$wgLDAPGroupAttribute = array(
  "testLDAPdomain"=>"uniquemember",
  "testADdomain"=>"member"
  );
 
//The naming attribute of the group
$wgLDAPGroupNameAttribute = array(
  "testLDAPdomain"=>"cn",
  "testADdomain"=>"cn"
  );

[edit] Syncronizing LDAP groups with MediaWiki security groups

//Pull LDAP groups a user is in, and update local wiki security group.
//Default: false
$wgLDAPUseLDAPGroups = array(
  "testADdomain"=>true,
  "testLDAPdomain"=>true
  );
 
//A list of groups that won't automatically have their members
//removed, but will have them added. The sysop, bureaucrat, and bot
//groups are always considered locally managed.
$wgLDAPLocallyManagedGroups = array(
  "testADdomain"=>array( "adtestgroup","adtestgroup2" ),
  "testLDAPdomain"=>array( "ldaptestgroup", "ldaptestgroup2" )
  );
 
//Get every group from LDAP, and add it to $wgGroupPermissions. This
//is useful for plugins like Group Based Access Control. This is very
//resource intensive, and probably shouldn't be used in very large
//environments.
//Default: false
$wgLDAPGroupsPrevail = array(
  "testADdomain"=>true,
  "testLDAPdomain"=>true
  );

[edit] Group based login restriction configuration options

//An array of the groups the user is required to be a member of.
$wgLDAPRequiredGroups = array(
  "testLDAPdomain"=>array(
      "cn=testgroup,ou=groups,dc=LDAP,dc=example,dc=com",
      "cn=testgroup2,ou=groups,dc=LDAP,dc=example,dc=com"
      ),
  "testADdomain"=>array(
      "cn=testgroup,ou=groups,dc=AD,dc=example,dc=com"
      )
  );
 
//Whether or not the plugin should search in nested groups
//Not currently used for group syncronization
//Default: false
$wgLDAPGroupSearchNestedGroups = array(
  "testLDAPdomain"=>false,
  "testADdomain"=>true
  );

[edit] Search based login restriction configuration options

This must be used with a proxy search.

//Require a search attribute
//Default: false
$wgLDAPRequireAuthAttribute = array(
  "testADdomain"=>true,
  "testLDAPdomain"=>true
  );
 
//Require the following additional search string.
$wgLDAPAuthAttribute = array(
  "testADdomain"=>"!(userAccountControl:1.2.840.113556.1.4.803:=2)",
  "testLDAPdomain"=>"!(nsaccountlock=true)"
  );

[edit] Smartcard authentication options

It is highly recommended to see the Smartcard Configuration Examples page before messing with these options.

If you use smartcard authentication, it would be foolish not to use HTTPS and SSL/TLS

//Enable smartcard authentication
$wgLDAPAutoAuthMethod = "smartcard";
 
//The domain that will be using smartcard authentication
$wgLDAPSmartcardDomain = "testADdomain-smartcard";
 
//The attribute from the smartcard you wish to search LDAP for
$wgLDAPSSLUsername = $_SERVER['SSL_CLIENT_S_DN_CN'];
 
//This hook is called by the LdapAuthentication plugin. It is a configuration hook. Here we
//are specifying what attibute we want to use for a username in the wiki.
//The hook calls the function defined below.
$wgHooks['SetUsernameAttributeFromLDAP'][] = 'SetUsernameAttribute';
 
//This function allows you to get the username from LDAP however you need to do it.
//This is the username MediaWiki will use.
function SetUsernameAttribute(&$LDAPUsername, $info) {
        $LDAPUsername = $info[0]['samaccountname'][0];
        return true;
}

[edit] Group based restrictions (DEPRECATED)

[edit] Configuration for non-AD Directory Servers

To use the group based restrictions with a non-AD directory, add:

$wgLDAPGroupDN = "cn=test,ou=groups,dc=mycompany,dc=com";

where "cn=test,ou=groups,dc=mycompany,dc=com" is the dn of the group you wish to restrict access to.

Note: The plugin searches for users in the "member" attribute in groups. If you are not using the "member" attribute for your group members, then you will have to change what attribute is being searched for in the plugin.

[edit] Configuration for AD

Although the plugin does support group and role based restrictions (in version 1.0c), it was really written for non-AD style groups. It is actually pretty hard to write this for AD and non-AD style since AD uses groups in a smart way (although hard to use for other purposes way). AD does use the "member" attribute like used in the script, but it stores the members as their actual entry in the directory server ie:

cn=Lane\, Ryan,ou=Test_Group,ou=Domain_users,dc=example,dc=com

The problem lies in finding this member based upon the user's uid (or sAMAccountName). Now, this is possible if you add some code to the patch. If you pull preferences already, this should be pretty easy. If you do not pull preferences, you'll have to pull one. You'll need to pull the user's "distinguishedName" attribute, and search for that as a member in the function that checks for group membership. So, you could make the following changes:

(this example shows how to change the plugin to work with AD when you are not already pulling preferences, although it will work even if you are pulling preferences, just not as efficiently)

At the class definition, change:

class LdapAuthenticationPlugin extends AuthPlugin {
        var $email, $lang, $realname, $nickname, $SearchType;

to

class LdapAuthenticationPlugin extends AuthPlugin {
        var $distinguishedName, $email, $lang, $realname, $nickname, $SearchType;

In function "authenticate" change:

if ($wgLDAPGroupDN) {
                                return $this->isMemberOfLdapGroup($ldapconn, $userdn, $wgLDAPGroupDN);
                        }

to:

if ($wgLDAPGroupDN) {
                                $entry = @ldap_read($ldapconn, $userdn, "objectclass=*");
                                $info = @ldap_get_entries($ldapconn, $entry);
                                $this->distinguishedName = $info[0]["distinguishedname"][0];
                                return $this->isMemberOfLdapGroup($ldapconn, $userdn, $wgLDAPGroupDN);
                        }

In function "isMemberOfLdapGroup" change

//we need to do a subbase search for the entry
                $filter = "(member=".$userDN.")";

to:

//we need to do a subbase search for the entry
                $filter = "(member=".$this->distinguishedName.")";

In LocalSettings.php add:

$wgLDAPGroupDN = "cn=test,ou=groups,dc=mycompany,dc=com";

where "cn=test,ou=groups,dc=mycompany,dc=com" is the dn of the group you wish to restrict access to.

Let me know if this is working for you or not.


  • 2006-04-26 : it was not working for because I used this configuration in LocalSettings.php :
$wgLDAPSearchStrings = array( "domain"=>"USER-NAME@domain" );

So the modification in the file LDAPAuthentication.php, the function "authenticate" was not working for me (wgLDAPSearchStrings does not return the user DN but the userPrincipalName) I changed the modification to :

global $wgLDAPBaseDNs;
$entry = @ldap_search($ldapconn,$wgLDAPBaseDNs[$_SESSION['wsDomain']], "(&(objectclass=*)(userPrincipalName=$userdn))");
$info = @ldap_get_entries($ldapconn, $entry);
$this->distinguishedName = $info[0]["distinguishedname"][0];
return $this->isMemberOfLdapGroup($ldapconn, $userdn, $wgLDAPGroupDN);

and it is now working for me.

See also: German explanation

[edit] Group based restrictions (NEW)

[edit] Configuration for non-AD domains

You'll need to change around the options some depending on your setup. The below example is a configuration to find "testuser" in the following group:

dn: cn=testgroup,ou=groups,dc=LDAP,dc=example,dc=com
cn: testgroup
objectclass: groupofuniquenames
uniqueMember: uid=testuser,ou=people,dc=LDAP,dc=example,dc=com
uniqueMember: uid=testuser2,ou=people,dc=LDAP,dc=example,dc=com
uniqueMember: uid=testuser3,ou=people,dc=LDAP,dc=example,dc=com

Example:

$wgLDAPRequiredGroups = array( "testLDAPdomain"=>array("cn=testgroup,ou=groups,dc=LDAP,dc=example,dc=com") );
$wgLDAPGroupUseFullDN = array( "testLDAPdomain"=>true );
$wgLDAPGroupObjectclass = array( "testLDAPdomain"=>"groupofuniquenames" );
$wgLDAPGroupAttribute = array( "testLDAPdomain"=>"uniquemember" );
$wgLDAPGroupSearchNestedGroups = array( "testLDAPdomain"=>false );
$wgLDAPGroupNameAttribute = array( "testLDAPdomain"=>"cn" );
$wgLDAPBaseDNs = array( "testLDAPdomain"=>"dc=LDAP,dc=example,dc=com" );

The below example is a configuration to find "testuser" in the following group:

dn: cn=testgroup,ou=groups,dc=LDAP,dc=example,dc=com
cn: testgroup
objectclass: posixgroup
gidnumber: 10000
memberuid: testuser
memberuid: testuser2
memberuid: testuser3

Example:

$wgLDAPRequiredGroups = array( "testLDAPdomain"=>array("cn=testgroup,ou=group,dc=LDAP,dc=example,dc=com") );
$wgLDAPGroupUseFullDN = array( "testLDAPdomain"=>false );
$wgLDAPGroupObjectclass = array( "testLDAPdomain"=>"posixgroup" );
$wgLDAPGroupAttribute = array( "testLDAPdomain"=>"memberuid" );
$wgLDAPGroupSearchNestedGroups = array( "testLDAPdomain"=>false );
$wgLDAPGroupNameAttribute = array( "testLDAPdomain"=>"cn" );
$wgLDAPBaseDNs = array( "testLDAPdomain"=>"dc=LDAP,dc=example,dc=com" );

[edit] Configuration for AD domains

Notice that if you have a multi-domain or multi-forest environment, you need to make sure your configuration is pointing at your global catalog!

Example:

#DNs in $wgLDAPRequiredGroups must be lowercase, as search result attribute values are...
$wgLDAPRequiredGroups = array( "testADLDAPdomain"=>array("cn=testgroup,ou=groups,dc=adldap,dc=example,dc=com") );
$wgLDAPGroupUseFullDN = array( "testADLDAPdomain"=>true );
$wgLDAPGroupObjectclass = array( "testADLDAPdomain"=>"group" );
$wgLDAPGroupAttribute = array( "testADLDAPdomain"=>"member" );
$wgLDAPGroupSearchNestedGroups = array( "testADLDAPdomain"=>false );
$wgLDAPGroupNameAttribute = array( "testADLDAPdomain"=>"cn" );
$wgLDAPBaseDNs = array( "testADLDAPdomain"=>"dc=ADLDAP,dc=example,dc=com" );

If you are using straight binds to AD, using AD-style straight binds (DOMAIN\\USER-NAME or USER-NAME@DOMAIN), you'll need one more option to make this work correctly:

$wgLDAPSearchAttributes = array( "testADLDAPdomain"=>"sAMAccountName" );

This allows the plugin to find the user's full DN for searching groups. Without finding the user's full DN, the plugin will search groups with (member=DOMAIN\username), which is not what is in your groups.

[edit] Group synchronization

To use group synchronization, you'll set up the plugin just like new style group based restrictions, and add the following two options to any of the examples from Extension:LDAP_Authentication#Group_based_restrictions_.28NEW.29:

$wgLDAPUseLDAPGroups = array( "testLDAPdomain"=>"true" );
$wgLDAPGroupNameAttribute = array( "testLDAPdomain"=>"cn" );

You would of course need to change "testLDAPdomain" to whatever was appropriate.

Notice that $wgLDAPGroupNameAttribute is set to "cn" for every example because in every example, the naming attribute for the groups is "cn", if for some reason you had a group that looked like:

dn: group=testgroup,ou=groups,dc=adldap,dc=example,dc=com
member: samaccountname=testuser,ou=users,dc=adldap,dc=example,dc=com

you would instead set $wgLDAPGroupNameAttribute like this instead:

$wgLDAPGroupNameAttribute = array( "testLDAPdomain"=>"group" );

If you only want to synchronize groups, and not do group based login restriction as well, just remove the $wgLDAPRequiredGroups option.

[edit] LDAP attributes used for preference pulling

The following four attributes can be set in LDAP on a user for the wiki to use as user preferences:

mail (email address)
displayName (nickname)
cn (real name)
preferredLanguage (language)

preferredLanguage must use the language code as it would be found in "languages/Names.php"


[edit] Suggestions

  • Is it possible that when using the ldap authentication plugin that the users email address would be automatically confirmed since it is being pulled out of active directory? Axelseaa 14:29, 17 July 2007 (UTC)
    • I was under the impression that this was the case; I'll take a look into this.
      --Ryan lane 13:42, 17 August 2007 (UTC)
  • I've had a recurring problem where mediawiki won't create a new account if the username and password in LDAP are identical - no errors are thrown, it just shows the login as incorrect. Is it possible to get an error to appear for this, or is it a restriction of mediawiki rather than the plugin? Other than that, it works perfectly, thanks a lot for your work.
    -- Nickt
    • I don't see why this would fail, unless it is an issue with mediawiki; I think it is a good idea of mediawiki to do this, but I generally handle this with LDAP, so I've never tested it (as that is a terribly insecure password).
      --Ryan lane 14:06, 5 November 2007 (UTC)
      • Understood - thanks a lot for the reply.
        -- Nickt
        • Seems another user had this issue, and I took the time to dig through MediaWiki core. This is a MediaWiki core issue. They don't allow username and password to be identical. This was most likely added after they had that massive sysop password crack incident.
          --Ryan lane 16:00, 13 December 2007 (UTC)
  • Hello Ryan and all. I just spent the better part of a working week getting Active Directory 2003 auth with Groups over SSL working with Mediawiki using this plugin. Firstly, thanks so much for writing it! However, figuring out what needs to be done is not easy, as the different versions of parameters, modifications and config examples on this page and on the net make it all very confusing. The lack of clear error returning can sometimes make it difficult to troubleshoot where a problem lies (SSL, certs, PHP, etc). I'm going to make the same changes from my test server to production next week, so I'll document it and make a step-by-step howto if anyone's interested? I imagine alot of people would be.
    • Further, would it be possible to tidy up this documentation any? I'd suggest moving the "OLD DEPRECATED" group auth into a lower section, or maybe a subpage, and the current group auth moved up, so people use that first instead of the old stuff which doesn't work. Also, a quick overview of other required auth modules and a few pointers to their config tips would also be infinitely helpful, not a walkthru, just one sentence with the basic requirements, I found just a small amount of advice like this helped me immensely when getting AD LDAP Auth working with PmWiki as well.
      --Super Jamie 05:32, 2 November 2007 (UTC)
      • Yes, I'd like to agree that once I understood what to do, getting it working was easy, but the documentation here is terse. It partly assumes that you know LDAP through and through, I think, but that's not always the case - I happen to be learning LDAP at the same time. I"m not expecting for this to be an LDAP tutorial in any way, but to have more examples of how the code would be used in a real situation would have helped me understand what was meant to go where your placeholder text is much sooner. — Timotab 13:01, 2 November 2007 (UTC)
    • My #1 tip to getting this plugin working, is to never, ever, read other people's configuration examples. Only use the documentation here, as the documentation everywhere else is going to almost definately be wrong. I have to have the ability to change configuration options every once in a while when the code needs rearranging, or when I find a better way of doing something.
    • As for error returning, using "$wgLDAPDebug = 3;" will return quite a bit of error messages. I've tried adding in error reporting for when php_ldap is missing, but haven't successfully been able to check for SSL support. The prerequisites for using this plugin are listed at the top of this page.
    • A tutorial on how to set up active directory and the client system for plugin use is fine by me, but I'm unlikely to ever write it (as I don't have easy access to a Windows Server 2003 OS, if someone buys me a copy, I'll happily use it for testing).
      --Ryan lane 14:06, 5 November 2007 (UTC)
    • Hi guys, I've written that documentation up, located at User:Super_Jamie/LDAP_HOWTO, hope it helps someone. --Super Jamie 05:14, 7 November 2007 (UTC)

[edit] User provided Information

[edit] Require user patch

You'll want to patch this by hand, not using the patch command

Here's a quick patch which allows an associative array of required users like so

$wgLDAPRequireUser = array( "Foo"=>true, "Bar"=>true, );

this allows authentication to be done in the LDAP whilst the authorization is done locally.

--- LdapAuthentication.php~     2006-02-28 18:24:41.152769000 +0100
  +++ LdapAuthentication.php      2006-03-01 12:30:44.685474331 +0100
  @@ -135,6 +135,7 @@
                  global $wgLDAPProxyAgent;
                  global $wgLDAPGroupDN;
                  global $wgLDAPRequireAuthAttribute, $wgLDAPAuthAttribute;
  +               global $wgLDAPRequireUser;
 
                  //We don't handle local authentication
                  if ( 'local' == $_SESSION['wsDomain'] ) {
  @@ -144,6 +145,13 @@
                  if ( '' == $password ) {
                          return false;
                  }
  +
  +               if ( $wgLDAPRequireUser ) {
  +                       if ( ! $wgLDAPRequireUser[$username] ) {
  +                               return false;
  +                       }
  +               }
  +
                   $ldapconn = $this->connect( );
                   if ( $ldapconn ) {
                           ldap_set_option( $ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);

I tried experimenting with other methods to achieve this, but none of them seemed to work right. If I turn off auto-user-creation, there's no way for admins to do it manually. Creating directly in the database seems a hassle. Setting up a group isn't really an option in this case. Is there a better way? What I really wanted to find was an admin special page for creating new users.

Patch considered to small and obvious to be copyright, use it if you like without need for attribution. This isn't a totally general solution since it doesn't deal with different policies for different domains. E.g. everybody in a group + exceptions.

Hmm, can't set up an LDAP group? Seems like the easiest method, but if it isn't possibly in your enviroment, this is probably a viable option. Let's change your code up a little here though:
$wgLDAPRequireUser = array( "DomainName"=>"<user1>,<user2>", "DomainName2"=>"<user3>,<user4>" ); //where <user1> would be "Foo"
 
  +
  +               if ( isset( $wgLDAPRequireUser ) && isset( $wgLDAPRequireUser[$_SESSION['wsDomain']] ) ) {
  +                       $users = explode( ",", $wgLDAPRequireUser[$_SESSION['wsDomain']] );
  +                       if ( ! in_array( $username, $users ) ) {
  +                               return false;
  +                       }
  +               }
  +
Now it is generic ;). Make sure to check my code for stupid errors, as I didn't actually try this, but it should work.
-- Ryan Lane

[edit] Trusting self-signed SSL certificates

Anybody know how to have LdapAuthentication trust self-signed certificates, as alluded to in the "Requirements" section? It doesn't currently appear to be able to trust certs that haven't been signed by a trusted provider.

This is dependant upon what type of OS you are using. I'm not sure if this is the right spot for the documentation of how to do this...
--Ryan Lane
For Fedora Core 4 I added a line to /etc/openldap/ldap.conf:
TLS_REQCERT never
A similar change to this should work for any system using OpenLDAP. Be advised this tells OpenLDAP NOT to do any checking on the certificate (not the default), which will make you suceptible to a man-in-the middle attack.
A more secure option is to add this line:
TLS_CACERT /etc/openldap/cacerts/myselfsigned.crt
You can also add "myselfsigned.crt" to /usr/share/ssl/certs and set the following in /etc/ldap.conf:
tls_cacertdir /usr/share/ssl/certs
If you use TLS_CACERT, you may need to also add the same info on TLS_CACERTFILE, depending on the OS (RHEL 3 seems to need this).

_____________

I think the issue is can your ldap even recognize your self-signed certs; it may be just a matter of adding the public key of the CA you used to sign your certs to your ldap configuration.


I think this is the same problem I am having. My ldap.conf is configured for TLS_REQCERT=never. The ldaps is working fine for ssh authentication and other(verified with ssldump and ethereal). But for some reason it seems that the /etc/ldap.conf is ignored by php.

Hmmmmmm.

I believe ALL of /etc/ldap.conf is ignored by php. php uses /etc/openldap/ldap.conf I believe. I run into this problem every time I set up a system by hand. The easy solution (and a generally safe solution) is to remove /etc/openldap/ldap.conf and symlink it to /etc/ldap.conf. Of course, you can't do that if you are using openLDAP server on that system, but no one in their right mind would run a webserver on their LDAP server right? :)
-- Ryan Lane

Right. And I have symlinked it. I guess it is just odd behavior how the php is behaving. I wonder if there is a case sensitivity issue with php? I did notice tls_reqcert in my ldap.conf is all lowercase.

I will test and post back.


Just tested. No joy.


I wrote this up after I finally figured out this issue: [1]


Here is some documentation from microsoft for enabling LDAP over SSL: [2]
--Ryan Lane


/etc/ldap.conf belongs to the operation system authorization and authentification interfaces pam_ldap and nss_ldap. It would not be used from PHP in this case. /etc/openldap/ldap.conf belongs to the openldap client libraries and would be used indirect from PHP. Both files should have different content. TLS_CACERTDIR could be used with all CA certificates in own files. It should be necessary to use c_rehash on a directory with CA certificates and TLS_CACERTDIR.

-- Ralf Hansen

[edit] Adding WikiSysop

Finally you'll need to add a WikiSysop user to LDAP. Which is a little tricky because WikiSysop isn't a real user. This is what I used.

 dn: ou=applications,dc=example,dc=org
 objectClass: top
 objectClass: organizationalUnit
 ou: applications
 
 dn: uid=WikiSysop,ou=applications,dc=example,dc=org
 uid: WikiSysop
 cn: WikiSysop
 userPassword: secret
 objectClass: shadowAccount
 objectClass: simpleSecurityObject
 objectClass: applicationProcess

Full instructions here http://caldergroup.com/configure/wikildapauthentication.html

You can also create your own user before the system is configured for LDAP (make sure to use all lowercase letters...), and give your user sysop privileges, then configure it for LDAP. After doing so, you can give anyone else sysop privileges with your user.
-- Ryan Lane
If you are feeling adventurous you can also modify the 'user_groups' table in the database. First find your user id in the 'user' table:
SELECT user_id, user_name FROM `user` WHERE `user_name` REGEXP '^MYNAME';
then do something like
INSERT INTO `user_groups` ( `ug_user` , `ug_group` ) VALUES ( 'UID', 'bureaucrat');
INSERT INTO `user_groups` ( `ug_user` , `ug_group` ) VALUES ( 'UID', 'sysop')
All of this assumes you have write access to the DB and that you are pretty proficient/comfortable with SQL.
--James Candalino

[edit] Windows configuration

If your instance of MediaWiki is hosted on a Windows machine running IIS, Apache, etc, there are some key items to configure to confirm that PHP has secure LDAP functionality enabled.

  1. In php.ini (in your apache/bin directory NOT your php directory!!!) make sure the following lines are uncommented
    • extension=php_ldap.dll
    • extension=php_openssl.dll
  2. Also in PHP.ini; Confirm those dll's are in the directory referenced by extension_dir (ex. c:\php\ext)
  3. Copy several files from the DLL folder of the PHP/Win32 binary package to the SYSTEM folder of your windows machine. (Ex: C:\WINNT\SYSTEM32, or C:\WINDOWS\SYSTEM). For PHP <= 4.2.0 copy libsasl.dll, for PHP >= 4.3.0 copy libeay32.dll and ssleay32.dll to your SYSTEM folder.
  4. Create a text file called ldap.conf in the directory C:\openldap\sysconf\. The first line must be TLS_REQCERT never . (For some reason OpenLdap has this as a hard-coded required file.)

[edit] Deactivating manual password change

Since I disabled usage of the passwords stored in the MediaWiki database and since there is no way for me to write into our LDAP directory I chose to deactivate the passwort fields in Special:Preferences and to write a message into them how to change your password. Simply insert these lines into MediaWiki:Monobook.js:

document.addEventListener("DOMContentLoaded", function() 
{
 if(document.getElementById('wpOldpass')!=null)
 {
  document.getElementById('wpOldpass').disabled=true;     
  document.getElementById('wpOldpass').type='text';     
  document.getElementById('wpOldpass').value='call helpdesk';     
  document.getElementById('wpNewpass').disabled=true;  
  document.getElementById('wpRetypePass').disabled=true; 
 }
}, false);
  • Note: This works on Firefox, but not on IE 6 --Flominator 07:04, 6 August 2007 (UTC)
  • Note: This will only work if the user has javascript enabled --Ryan lane 19:12, 6 August 2007 (UTC)

[edit] Testing LDAP binding and searching

LDAP Browser is a good way to test your searching and binding configuration. --Flominator 18:59, 10 August 2007 (UTC)

[edit] Active Directory Primary Group Patch

Active Directory does not list the Primary Group of the user when getting members from the group object. This patch fixes that by getting the primary group id from the user object, creating a SID to find the group and then add the group to the rest of the user's groups. There is no easier way of doing this through LDAP. This code has not been tested on non-AD LDAPs, but should just skip if it is not querying an AD LDAP. This also fixes an issue where the groups are not added soon enough when using $wgLDAPGroupsPrevail and the Group Based Access Control extension. This prevented new groups added to AD from being available for access control.

--- LdapAuthentication.old/LdapAuthentication.php      2008-02-22 17:21:31.000000000 -0700
+++ LdapAuthentication.new/LdapAuthentication.php      2008-02-22 17:15:11.000000000 -0700
@@ -1405,10 +1405,6 @@
                 if ( $value != "*" )
                         $value = $this->getLdapEscapedString( $value );
 
-               $filter = "(&($attribute=$value)(objectclass=$objectclass))";
-
-               $this->printDebug( "Search string: $filter", SENSITIVE );
-
                if ( isset( $wgLDAPProxyAgent[$_SESSION['wsDomain']] ) ) {
                        //We'll try to bind as the proxyagent as the proxyagent should normally have more
                        //rights than the user. If the proxyagent fails to bind, we will still be able
@@ -1417,6 +1413,51 @@
                        $bind = $this->bindAs( $ldapconn, $wgLDAPProxyAgent[$_SESSION['wsDomain']], $wgLDAPProxyAgentPassword[$_SESSION['wsDomain']] );
                }
 
+               $groups = array();
+               $shortnamegroups = array();
+
+               // AD does not include the primary group in the list of groups, we have to find it ourselves.
+               
+               if ($dn != "*"){
+                       $PGfilter = "(&(distinguishedName=$value)(objectclass=user))";
+                       $this->printDebug ( "User Filter: $PGfilter", SENSITIVE);
+                       $PGinfo = @ldap_search ( $ldapconn, $base, $PGfilter );
+                       $PGentries = @ldap_get_entries ($ldapconn, $PGinfo);
+                       if ($PGentries ){
+                               $Usid = $PGentries[0]['objectsid'][0];
+                               $PGrid = $PGentries[0]['primarygroupid'][0];
+                               $PGsid = bin2hex ($Usid);
+                               for ($i=0; $i < 56; $i += 2) {
+                                       $PGSID[] = substr($PGsid, $i, 2);
+                               }
+                               $dPGrid = dechex($PGrid);
+                               $dPGrid = str_pad ( $dPGrid, 8, '0', STR_PAD_LEFT);
+                               $PGRID = array();
+                               for ($i = 0; $i < 8; $i += 2){
+                                       array_push( $PGRID, substr($dPGrid, $i, 2));
+                               }
+                               for ($i = 24; $i < 28; $i++){
+                                       $PGSID[$i] = array_pop