Project - LDAP for MW1.27 and beyond

From MWStake
Jump to navigation Jump to search

Original thoughts by User:Mark_Hershberger on 28th of September 2017

I've actually been thinking about this for some time, so let me put my thoughts out there and see what you all think of this division of labor?

(Extension/class names are in parens)

  • user information (LDAPUserInfo)
- name
- email
- gender
- timezone
  • authentication (LDAPAuthentication as a class extending PluggableAuth)
- using password stored in LDAP
  • authorization (LdapGroups)
- mapping LDAP criteria (usually "memberOf") to MW groups
- maybe something like a canUseWiki attribute, but people should probably be using memberOf for this

And, of course, then there is the actual communication with the LDAP server (an extension named LDAP).

I know that communication with LDAP (AD in my case, usually) is pretty fast -- ~0.005 s/call in my measurements. For most Enterprise wikis that is probably fast enough that you don't have to worry about consolidating calls, but I think that it makes sense to reduce the number of calls as much as possible.

Which means that I would like to have the LDAPUserInfo and LdapGroup calls consolidated into a single call. It is probably possible to consolidate the LDAPAuthentication call there as well, but, for now, I'm ignorant of a way to register that you want a given set of attributes for a user from LDAP when that user successfully authenticates.

(Thinking about it, in some cases, you can probably use the user's authentication information to return all the desired attributes of a user.)

In any cases, I'm thinking of creating a hook (say, InitLDAPParams) that would be called very early and would provide a list of attributes that you want for the user (by default) and a callback that the extensions would provide to handle whatever is gotten as a result.

In the style of docs/hooks.txt, here is the entry:

 'InitLDAPParams': Called by LDAP during SetupAfterCache. Use this to
 tell LDAP what information you need from the user's attributes.
 &$match: string|callback How to uniquely identfy the user. A string
 would be in the format "ATTR=VAL".  For example,
 "mail=user@example.com" or "SSOid=4025". A callback should be provided
 to return a string in this format if the information to find a user is
 not available when SetupAfterCache.
 &$attrCallbacks: associative array of atttributes desired and the
 callbacks to handle the information.  For example:
 [
   'mail' => 'CompanyUser::setEmail',
   'cn' => 'CompanyUser::setName'
   'preferredLanguage' => 'CompanyUser::setLanguage'
 ]
 The callbacks provided will be called with two parameters:
 ( User $user, string $value ) where $user is the user for which the
 information was discovered and $value is the value of the attribute.
 &$status: This isOK() if and only if a single matching LDAP user is found.

The DN is stored in a static property of the class whenever a user is found.

The above would handle the needs of LDAPUserInfo and LdapGroups, I think, so all that is left is authentication.

There has already been a lot of work done on authentication in general. I think the LDAP extension only needs to expose a static method for authentication:

 public static function verify( $username, $password ) : Status;

The Status object will return true when isOK is called if the authentication was successful.

A similar static method should be exposed to determine membership:

 public static function isMemberOf( $user, $dn ) : bool;

True would only be returned if the user is a member of the provided $dn.

The LDAP extension will use the value of $LDAPIniFile to connect to the LDAP server. The format is given here:

https://www.mediawiki.org/wiki/Extension:LdapGroups#ini_file

This should be extended to find other users who are mentioned (by, say, Echo) but have not yet been incorporated into the wiki.

This would serve my needs, of course, but I'm trying to see if you think the above would work for you.