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.
| Name | Version | Description |
|---|
| Net::LDAP | >=0.33 | Required |
| Digest::MD5 | >=2.36 | Required |
| Unicode::MapUTF8 | >=1.11 | Required. 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