r0 - 20 Apr 2007 - 12:12:53 - TWikiContributorYou are here: TWiki >  TWiki Web > LdapContrib

LdapContrib

This package offers basic LDAP services for TWiki and offers authentication of TWiki users by binding to an LDAP server as well as incorporate LDAP user groups into TWiki's access control. Note, however that you need at least TWiki-4.0.3 for that. Optionally, if you need an interface to query your LDAP directory and display the results in a TWiki topic install the TWiki:Plugins/LdapNgPlugin which will make use of the LdapContrib services. This work is a rewrite of the TWiki:Plugins/LdapPlugin by TWiki:Main/GerardHickey while bringing authentication, user management and other LDAP applications onto a common base.

Note: if you have a large LDAP directory it is a good idea to install a perl accelerator like mod_perl, SpeedyCGI (aka PersistentPerl) or FastCGI. The LdapUserMapping will be cached in memory then resulting in an considerable speedup. The cache can be configured to refresh at a certain hit rate or manually here:

Tip: You can add this button on any page by adding %INCLUDE{"TWiki.LdapContrib"}% to it.

Configuration

The LdapContrib package is configured using a set of variables that need to be added to the lib/LocalSite.cfg configuration file. Either configure them using the configure tool or have a look at the lib/ldap.cfg example that comes with this package. These allow to accomodate your TWiki installation to your specifc LDAP installation and user accounting.

Simple Example

For the sake of this documentation we assume that users accounts in your database are at leat of the type posixAccount and optionally also of type inetOrgPerson storing email addresses. Moreover users are stored in a subtree ou=people and groups are defined in ou=group. Here are some example LDAP records:
dn: uid=testuser1,ou=people,dc=my,dc=domain,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
cn: Test User1
uid: testuser1
uidNumber: 1024
gidNumber: 100
homeDirectory: /home/testuser1
loginShell: /bin/bash
mail: testuser1@my.domain.com

dn: uid=testuser2,ou=people,dc=my,dc=domain,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
cn: Test User2
uid: testuser2
uidNumber: 1024
gidNumber: 100
homeDirectory: /home/testuser2
loginShell: /bin/bash
mail: testuser2@my.domain.com
mail: testuser2@gmx.com

# users, Group, nats.informatik.uni-hamburg.de
dn: cn=users,ou=group,dc=my,dc=domain,dc=com
objectClass: posixGroup
cn: users
gidNumber: 100
memberUid: testuser1
memberUid: testuser2

Please have a look at your LDAP manual on how to set up an LDAP server and populate it with user account records. Have a look at the Quick-Start Guide on how to install OpenLdap.

Basic Settings

If your LDAP server has the name ldap.my.domain.com use the following options to attatch your TWiki installation to a directory like the one given in the simple example
$TWiki::cfg{Ldap}{Host} = 'ldap.my.domain.com';
$TWiki::cfg{Ldap}{Base} = 'dc=my,dc=domain,dc=com'; 

Advanced Settings

All other options use sensible defaults that match the above example situations. These are:

  • port used when binding to the LDAP server
    $TWiki::cfg{Ldap}{Port} = 389; 
  • LDAP protocol version to use when querying the server; possible values: 2, 3
    $TWiki::cfg{Ldap}{Version} = '3'; 
  • the base DN to use in searches
    $TWiki::cfg{Ldap}{Base} = 'dc=my,dc=domain,dc=com'; 
  • define the DN of the users tree (defaults to $TWiki::cfg{Ldap}{Base})
    $TWiki::cfg{Ldap}{UserBase} = 'ou=people,dc=my,dc=domain,dc=com'; 
  • define the DN of the groups tree (defaults to $TWiki::cfg{Ldap}{Base})
    $TWiki::cfg{Ldap}{GroupBase} = 'ou=group,dc=my,dc=domain,dc=com'; 
  • define the user login name attribute; note, that the uid will be mapped to the correct WikiUserName using the TWikiUsers topic.
    $TWiki::cfg{Ldap}{LoginAttribute} = 'uid'; 
  • filter to be used to find login accounts
    $TWiki::cfg{Ldap}{LoginFilter} = 'objectClass=posixAccount'; 
  • define the group name
    $TWiki::cfg{Ldap}{GroupAttribute} = 'cn'; 
  • filter to be used to find groups
    $TWiki::cfg{Ldap}{GroupFilter} = 'objectClass=posixGroup'; 
  • define the attribute that should be used to collect group members
    $TWiki::cfg{Ldap}{MemberAttribute} = 'memberUid'; 
  • flag indicating wether the member attribute of a group stores a DN
    $TWiki::cfg{Ldap}{MemberIndirection} = 0; 
  • define the WikiUserName attribute(s)
    $TWiki::cfg{Ldap}{WikiNameAttribute} = 'cn'; 
  • flag to indicate normalization of WikiUserNames that come from LDAP;
    $TWiki::cfg{Ldap}{NormalizeWikiName} = 1; 
  • flag indicating wether we fallback to TWikiGroups;
    $TWiki::cfg{Ldap}{TWikiGroupsBackoff} = 1; 
  • the dn to use when binding to the LDAP server; if undefined anonymous binding will be used
    $TWiki::cfg{Ldap}{BindDN} = 'cn=proxyuser,dc=my,dc=domain,dc=com'; 
  • the password used when binding to the LDAP server
    $TWiki::cfg{Ldap}{BindPassword} = 'secret'; 
  • negotiate ssl when binding to the server; possible values: 0, 1
    $TWiki::cfg{Ldap}{SSL} = 0; 
  • refresh rate when the LDAP cache is fetched from the LDAP server; a value of -1 means unlimitted caching; a value of 0 disables the caching; default is -1
    $TWiki::cfg{Ldap}{MaxCacheHits} = -1; 
  • enable/disable mapping LDAP groups; default is 0
    $TWiki::cfg{Ldap}{MapGroups} = 0; 
  • prevent certain names from being looked up in LDAP
    $TWiki::cfg{Ldap}{Exclude} = 'TWikiGuest, TWikiContributor, TWikiRegistrationAgent, TWikiAdminGroup, NobodyGroup'; 

Membership

LDAP servers follow different schemata to define "membership". They store the information either by storing a set of unique ids in the group object (posixGroup) or the full DNs of the user objects (groupOfNames). In the latter case the user objects' unique ids have to be fetched separately based on their distinguished name. This mode may be switched on the using the MemberIndirection option.

Note, that the reverse relation where the user objects store in which group they participate (for example using a memberOf attribute) is maintained by some LDAP servers automatically. Those that encode membership this way only are not supported by the LdapContrib yet.

Furthermore, user objects may have a primary group attribute (posixAccount). This is a simple value (vs. multi-value) and stores the id of a default group that user is member of. This information is not used by the LdapContrib.

In short, the LdapContrib reads membership information reading the group objects only, and may map the member object indirectly to the login name.

Authentication

If you want to authenticate TWiki users by binding to an LDAP server that stores their credentials you have to register the LdapUser class as the so called PasswordManager. This is done by adding the following lines in the lib/LocalSite.cfg configuration file:
$TWiki::cfg{PasswordManager} = 'TWiki::Users::LdapUser';
$TWiki::cfg{Ldap}{WikiNameAttribute} = 'cn';
$TWiki::cfg{Ldap}{NormalizeWikiName} = 1;
This will use the 'cn' attribute of an LDAP user account

Normalizing the WikiName

The WikiName used by TWiki to identify a user can be generated by setting the two parameters WikiUserNameAttribute and NormalizeWikiName. WikiUserNameAttribute can be a comma separated list of LDAP attributes that are then assembled to form the WikiName. If the NormalizeWikiName flag is set a couple of extra operations are performed to generate a proper WikiName. Given the setting
$TWiki::cfg{Ldap}{WikiNameAttribute} = 'givenName, sn';
$TWiki::cfg{Ldap}{NormalizeWikiName} = 1;
The givenName and sn (surname) LDAP attributes will be fetched and concatenated to form a WikiName, so that "givenName=Hans-Peter,sn=Leuthäuser-Schnarrenberg" will result in the WikiName "HansPeterLeuthaeuserSchnarrenberg". If one of the WikiNameAttributes is 'mail' the @mydomain.com part will be removed all together.

User Groups

LDAP group records can be used in TWiki's access control by registering a UserMappingManager implementation. This is done by adding
$TWiki::cfg{UserMappingManager} = 'TWiki::Users::LdapUserMapping';
to your lib/LocalSite.cfg configuration file.

In addition you can decide if you want to add the LDAP groups to TWiki or use LDAP groups solely. This is controled by the TWikiGroupsBackoff flag. If switched on then LDAP groups will be added. If there's a name clash LDAP groups take precedence. If switched off TWikiGroups are ignored.

Documentation

TWiki::Contrib::LdapContrib

General LDAP services for TWiki. This class encapsulates the TWiki-specific means to integrate an LDAP directory service. Used by TWiki::Users::LdapUser for authentication, TWiki::Users::LdapUserMapping for group definitions and TWiki::Plugins::LdapNgPlugin to interface general query services.

Typical usage:

my $ldap = new TWiki::Contrib::LdapContrib;

my $result = $ldap->search(filter=>'mail=*@gmx*');
my $errorMsg = $ldap->getError();

my $count = $result->count();

my @entries = $result->sorted('sn');
my $entry = $result->entry(0);

my $value = $entry->get_value('cn');
my @emails = $entry->get_value('mail');

writeDebug($msg, $level)

Method to write a debug messages. The $msg is only written if the given current debug level is high enough ($level <= $TWiki::cfg{Ldap}{Debug}). The higher the debug level, the more verbose the debug output.

Debug output is written to STDERR.

new(host=>'...', base=>'...', ...) -> $ldap

Construct a new TWiki::Contrib::LdapContrib object

Possible options are:

  • host: ip address (or hostname)
  • base: the base DN to use in searches
  • port: port address used when binding to the LDAP server
  • version: protocol version
  • userBase: sub-tree DN of user accounts
  • groupBase: sub-tree DN of group definitions
  • loginAttribute: user login name attribute
  • loginFilter: filter to be used to find login accounts
  • groupAttribute: the group name attribute
  • groupFilter: filter to be used to find groups
  • memberAttribute: the attribute that should be used to collect group members
  • bindDN: the dn to use when binding to the LDAP server
  • bindPassword: the password used when binding to the LDAP server
  • ssl: negotiate ssl when binding to the server

Options not passed to the constructor are taken from the global settings in lib/LocalSite.cfg.

getLdapContrib() -> $ldap

Returns a standard singleton TWiki::Contrib::LdapContrib object based on the site-wide configuration.

connect($login, $passwd) -> $boolean

Connect to LDAP server. If a $login name and a $passwd is given then a bind is done. Otherwise the communication is anonymous. You don't have to connect() explicitely by calling this method. The methods below will do that automatically when needed.

disconnect()

Unbind the LDAP object from the server. This method can be used to force a reconnect and possibly rebind as a different user.

checkError($msg) -> $errorCode

Private method to check a Net::LDAP::Message object for an error, sets $ldap->{error} and returns the ldap error code. This method is called internally whenever a message object is returned by the server. Use $ldap->getError() to return the actual error message.

getError() -> $errorMsg

Returns the error message of the last LDAP action or undef it no error occured.

getAccount($login) -> Net::LDAP::Entry object

Fetches an account entry from the database and returns a Net::LDAP::Entry object on success and undef otherwise. Note, the login name is match against the attribute defined in $ldap->{loginAttribute}. Account records are search using $ldap->{loginFilter} in the subtree defined by $ldap->{userBase}.

getAccount() -> $search

CAUTION this can get expensive, don't use.

Returns a Net::LDAP::Search object searching for all user accounts in the database.

getGroup($name) -> $entry

Returns the named group as a NET::LDAP::Entry object on success and undef otherwise. Check the error message using $ldap->getError().

getGroups() -> $search

Returns a Net::LDAP::Search object searching for all groups defined in the database.

CAUTION: this can get expensive, if you are only interested in the groups' ids the use getGroupNames()

getGroupNames() -> @array

Returns a list of known group names.

isGroup($user) -> $boolean

check if a given user is an ldap group actually

search($filter, %args) -> $msg

Returns an Net::LDAP::Search object for the given query on success and undef otherwise. If $args{base} is not defined $ldap->{base} is used. If $args{scope} is not defined 'sub' is used (searching down the subtree under $args{base}. If no $args{limit} is set all matching records are returned. The $attrs is a reference to an array of all those attributes that matching entries should contain. If no $args{attrs} is defined all attributes are returned.

If undef is returned as an error occured use $ldap->getError() to get the cleartext message of this search() operation.

Typical usage:

my $result = $ldap->search(filter=>'uid=TestUser');

cacheBlob($entry, $attribute, $refresh) -> $pubUrlPath

Takes an Net::LDAP::Entry and an $attribute name, and stores its value into a file. Returns the pubUrlPath to it. This can be used to store binary large objects like images (jpegPhotos) into the filesystem accessible to the httpd which can serve it in return to the client browser.

Filenames containing the blobs are named using a hash value that is generated using its DN and the actual attribute name whose value is extracted from the database. If the blob already exists in the cache it is not extracted once again except the $refresh parameter is defined.

Typical usage:

my $blobUrlPath = $ldap->cacheBlob($entry, $attr);

getPageControl($size) -> $pageControl

Constructs a new page control object of type Net::LDAP::Control::Paged useful to do paged queries on large datasets.

# TODO: write a better api for paged results and move it out of _loadMapping # to a place where it is reusable, e.g. an object of its own bundling # the search, page and cookie bits.

TWiki::Users::LdapUserMapping

This class allows to use user names and groups stored in an LDAP database inside TWiki in a transparent way. This replaces TWiki's native way to represent users and groups using topics with according LDAP records.

new($session) -> $ldapUserMapping

create a new TWiki::Users::LdapUserMapping object and constructs an LdapContrib object to delegate LDAP services to.

getListOfGroups( ) -> @listOfUserObjects

Get a list of groups defined in the LDAP database. If twikiGroupsBackoff is defined the set of LDAP and native groups will merged whereas LDAP groups have precedence in case of a name clash.

groupMembers($group) -> @listOfTWikiUsers

Returns a list of all members of a given group. Members are TWiki::User objects.

addUserToMapping($user, $me)

overrides and thus disables the SUPER method

lookupLoginName($loginName) -> $wikiName

Map a loginName to the corresponding wikiName. This is used for lookups during user resolution, and should be as fast as possible.

lookupWikiName($wikiName) -> $loginName

Map a wikiName to the corresponding loginName. This is used for lookups during user resolution, and should be as fast as possible.

lookupDistinguishedName($dn) -> $loginName

Map a DN to the corresponding loginName. This is used for getting members of a group where their membership is stored as a DN but we need the loginName.

loadLdapMapping() -> $boolean

This is the workhorse of this module, loading user objects on demand and harvest the needed information into internal caches. Returns true if an additional page of results was fetched, and false if the search result has been cached completely.

getListOfAllWikiNames() -> @wikiNames

CAUTION: This function is rarely used if at all. Asking large LDAP directories for all of their content is insane anyway. This function gets called by the %GROUPS% and the %USERINFO{userdebug="1"}% tags. These should be avoided, i.e. better remove the %GROUPS% tag from the TWikiGroups topic in such cases. Better use a TWikiApplication? build on top of the TWiki:Plugins/LdapNgPlugin that is able to display groups and members in a paginated way.

isGroup($user) -> $boolean

Establish if a user object refers to a user group or not. This returns true for the SuperAdminGroup or the known LDAP groups. Finally, if twikiGroupsBackoff is set the native mechanism are used to check if $user is a group

isMemberOf($user, $group) -> $boolean

Returns true if the $user is a member of the $group. Note, that both $user and $group can either be a WikiName or a reference to a User object

getGroupMembers($name) -> \@members

Returns a list of user ids that are in a given group, undef if the group does not exist.

finish()

Complete processing after the client's HTTP request has been responded to. I.e. it disconnects the LDAP database connection.

TWiki::Users::LdapUser

Password manager that uses Net::LDAP to manage users and passwords.

Subclass of TWiki::Users::Password .

This class does not grant any write access to the ldap server for security reasons. So you need to use your ldap tools to create user accounts or change passwords.

Configuration: add the following variables to your LocalSite.cfg

  • $TWiki::cfg{Ldap}{server} = <ldap-server uri>, defaults to localhost
  • $TWiki::cfg{Ldap}{base} = <base dn> subtree that holds the user accounts e.g. ou=people,dc=your,dc=domain,dc=com

Implemented Interface:

  • checkPassword(login, password)
  • error()
  • fetchPass(login)
  • getEmails(login)
  • setEmails(login, @emails)

new($session) -> $ldapUser

Takes a session object, creates an LdapContrib object used to delegate LDAP calls and returns a new TWiki::User::LdapUser object

error() -> $errorMsg

return the last error during LDAP operations

fetchPass($login) -> $passwd

SMELL: this method is used most of the time to detect if a given login user is known to the database. the concrete (encrypted) password is of no interest: so better would be to implement an interface like existsUser() or the like

checkPassword($login, $password) -> $boolean

check passwd by binding to the ldap server

getEmails($login) -> @emails

emails might be stored in the ldap account as well if the record is of type possixAccount and inetOrgPerson. if this is not the case we fallback to twiki's default behavior

Complete processing after the client's HTTP request has been responded. i.e. destroy the ldap object.

Installation Instructions

  • Download the ZIP file from the Plugin web (see below)
  • Unzip LdapContrib.zip in your twiki installation directory. Content:
    File: Description:
    data/TWiki/LdapContrib.txt  
    lib/TWiki/Contrib/LdapContrib/Config.spec  
    lib/TWiki/Contrib/LdapContrib.pm  
    lib/TWiki/Users/LdapUserMapping.pm  
    lib/TWiki/Users/LdapUser.pm  

  • Optionally, run LdapContrib_installer to automatically check and install other TWiki modules that this module depends on. You can also do this step manually.
  • Alternatively, manually make sure the dependencies listed in the table below are resolved.
    NameVersionDescription
    Net::LDAP>=0.33Required
    Digest::MD5>=2.36Required
    Unicode::MapUTF8>=1.11Required. Download from CPAN:Unicode::MapUTF8
  • Use configure to set the LDAP settings.
  • Optionally, copy the content of the file lib/ldap.cfg into lib/LocalSite.cfg and edit the settings to your needs (see the Configuration chapter)

Contrib Info

  • Set SHORTDESCRIPTION = LDAP services for TWiki

Author: TWiki:Main/MichaelDaum
Copyright ©: 2006 Michael Daum http://wikiring.com
This work was partly funded by Spanlink Communications and Trivadis
License: GPL (GNU General Public License)
Version: v1.01
Change History:  
30 Apr 2007: fixed return value on illegal lookup calls
24 Apr 2007: be robust against the lookup-API being called with the wrong parameters; added Debug flag; fixed/improved group loading; deprecating BasePasswd in favor of UserBase; deprecating BaseGroup in favor of GroupBase
04 Apr 2007: fixed group mapping on >4.1.2; renamed BasePasswd config parameter to UserBase; renamed BaseGroup config parameter to GroupBase; working around broken configure in 4.1.x
12 Jan 2007: enhanced normalization of WikiNames so that they are proper WikiWords; WikiNames can be constructed from a list of LDAP attributes now
18 Dec 2006: various performance improvements; fixed usage of limit argument; renamed configuration option "WikiNameRemoveWhiteSpace" to "NormalizeWikiName"; support for large databases using paged LDAP search results; new configuration option "Exclude" to exclude standard TWiki user accounts, e.g. TWikiRegistrationAgent, from being looked up in LDAP; added support for faster API implementing isMemberOf; added Config.spec file to integrate the LadpContrib into Twiki's "configure" tool; added support for WikiNames derived from mail attributes
03 Nov 2006: fixed binding to the server by first searching the full dn instead of assuming a fixed one (issue found by Cederic Weber); added new feature MapGroup to be able to switch off group mapping and have ; login-to-wikiname conversion only
02 Aug 2006: added a user accounts in memory cache
19 July 2006: public release
24 May 2006: api adjustments, improved wikiname generation
28 Apr 2006: Initial version
Home: TWiki:Plugins/LdapContrib
Feedback: TWiki:Plugins/LdapContribDev
Appraisal: TWiki:Plugins/LdapContribAppraisal

-- TWiki:Main/MichaelDaum - 30 April 2007

Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History:  | More topic actions
 
Powered by TWiki
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback
Note: Please contribute updates to this topic on TWiki.org at TWiki:TWiki.LdapContrib