LDAP auth: should I still create the users in RT even if I use WebExternalAuth?

I defined $WebExternalAuth in config.pm and now RT no longer asks a
password, it relies on Apache to do so (Apache uses LDAP).

But, if I try to log in as an user which is in LDAP but not in RT, RT
yields:

Not logged in.

Error
You are not an authorized user

Does it mean that I still have to create the users in RT (may be using
URL:http://www.fsck.com/pub/rt/contrib/2.0/rtimportldap.tar from
cron to automatize it)? Or will LookupExternalUserInfo help me?
(Reading its comments, I do not believe it.)

The section “Plugging in your own user metadata” in the Admin. manual
seems interesting but it is currently empty :-}

But, if I try to log in as an user which is in LDAP but not in RT, RT
yields:

Not logged in.

Error
You are not an authorized user

I think the problem is that to be able to login, you must have the
checkbox "Grant login to user"
That’s available in the Config->Users panel of RT
To be able to check this box, the user must exists.
But when sending a mail, the user is autocreated, that means that
perhaps the user is already created but not granted to login in RT web page.
-jec

Stephane Bortzmeyer bortzmeyer@nic.fr writes:

I defined $WebExternalAuth in config.pm and now RT no longer asks a
password, it relies on Apache to do so (Apache uses LDAP).

But, if I try to log in as an user which is in LDAP but not in RT, RT
yields:

Not logged in.

Error
You are not an authorized user

Does it mean that I still have to create the users in RT (may be using
URL:http://www.fsck.com/pub/rt/contrib/2.0/rtimportldap.tar from
cron to automatize it)? Or will LookupExternalUserInfo help me?
(Reading its comments, I do not believe it.)

correct. you still need to create RT users. I have patches that will
autocreate users using a user-defined function in config.pm. In my
case it pulled the data from the presented web-cert, but is should be
trivial to change it to do what you want. search the mailing list
archives.

seph

Stephane Bortzmeyer bortzmeyer@nic.fr writes:

I defined $WebExternalAuth in config.pm and now RT no longer asks a
password, it relies on Apache to do so (Apache uses LDAP).

But, if I try to log in as an user which is in LDAP but not in RT, RT
yields:

Not logged in.

Error
You are not an authorized user

Does it mean that I still have to create the users in RT (may be using
URL:http://www.fsck.com/pub/rt/contrib/2.0/rtimportldap.tar from
cron to automatize it)? Or will LookupExternalUserInfo help me?
(Reading its comments, I do not believe it.)

rtimportldap will definetely help. That’s why I created it :slight_smile:

On Fri, 13 Dec 2002 09:20:53 -0800 , I’ve sent the updated version
to this list, but Jesse didn’t seem to have time for uploading it to contrib.

Regards,
Stan

Stephane Bortzmeyer bortzmeyer@nic.fr writes:

Does it mean that I still have to create the users in RT (may be using
URL:http://www.fsck.com/pub/rt/contrib/2.0/rtimportldap.tar from
cron to automatize it)? Or will LookupExternalUserInfo help me?
(Reading its comments, I do not believe it.)

rtimportldap will definetely help. That’s why I created it :slight_smile:

Here is what I’m playing with at the moment. I use
Apache::AuthenNetLDAP in Apache to authenticate against our LDAP server.

Here is what I’m using in Apache’s httpd.conf.

--------------- Cut Here -----------------------------------

Alias /rt2 /usr/local/rt2/WebRT/html
PerlRequire /usr/local/rt2/bin/webmux.pl
<Location /rt2>
SetHandler perl-script
PerlHandler RT::Mason

AuthName "Request Tracker"
AuthType Basic

PerlSetVar BaseDN "ou=people,dc=ipaustralia,dc=gov,dc=au"
PerlSetVar LDAPServer our.directory.server.gov.au
PerlSetVar LDAPPort 389
PerlSetVar UIDAttr uid

require valid-user

PerlAuthenHandler Apache::AuthNetLDAP

--------------- Cut Here -----------------------------------

and here is the LookupExternalUserInfo() subroutine that maps inbound
email to valid userids.

Below that is the WebExternalAutoInfo() subroutine that automatically
creates new users based on their LDAP attributes. The bad side effect
of this is that requestors no longer get the simple page. I’m still
considering if that is what I want.

Thanks to the authors of the code that I hacked around to get the
following bits.

One thing that is missing is managing group memberships via LDAP
groups. It’s not critical to what I’m doing here, but probably worth
looking at in the new year.

--------------- Cut Here -----------------------------------

use Net::LDAP;
use Net::LDAP::Constant qw(LDAP_SUCCESS);

use constant LDAP => q(your.ldap.server);
use constant LDAP_PORT => q(389);
use constant LDAP_BASE => q(your=ldap.base);
use constant LDAP_UID => q(uid);
use constant LDAP_EMAIL => q(mail);
use constant LDAP_CN => q(cn);

sub LookupExternalUserInfo {
my ($EmailAddress, $RealName) = @_;

my $FoundInExternalDatabase = 0;
my %params;

#Name is the RT username you want to use for this user.
$params{‘Name’} = $EmailAddress;
$params{‘EmailAddress’} = $EmailAddress;
$params{‘RealName’} = $RealName;

$RT::Logger->debug(“LookupExternalUserInfo: Entered with:\n”,
"\tName = $params{‘Name’}\n",
"\tEmailAddress = $params{‘EmailAddress’}\n",
"\tRealName = $params{‘RealName’}\n",
"\tFound = $FoundInExternalDatabase\n");

$params{‘RealName’} =~ s/"//g;

my $ldap = new Net::LDAP(LDAP, port => LDAP_PORT)
or $RT::Logger->critical(“LookupExternalUserInfo: Cannot connect to
”,
“LDAP’\n”),
return ($FoundInExternalDatabase, %params);

my $mesg = $ldap->bind();
if ($mesg->code != LDAP_SUCCESS) {
$RT::Logger->critical("LookupExternalUserInfo: Cannot bind
anonymously ",
“to LDAP:”, $mesg->code, “\n”);
return ($FoundInExternalDatabase, %params);
}

my $filter = “@{[ LDAP_EMAIL ]}=$params{‘EmailAddress’}”;
$RT::Logger->debug(“LookupExternalUserInfo: First search filter “,
”’$filter’\n”);
$mesg = $ldap->search(base => LDAP_BASE,
filter => $filter,
attrs => [ LDAP_EMAIL, LDAP_CN, LDAP_UID ]);
if ($mesg->code != LDAP_SUCCESS) {
$RT::Logger->critical(“LookupExternalUserInfo: Could not search for
”,
"$filter: ", $mesg->code, “\n”);
return ($FoundInExternalDatabase, %params);
}

$RT::Logger->debug("LookupExternalUserInfo: First search produced “,
$mesg->count, " results\n”);

E-mail search failed

unless ($mesg->count == 1) {
$filter = “@{[ LDAP_CN ]}=$params{‘RealName’}”;

$RT::Logger->debug("LookupExternalUserInfo: Second search filter ",
	       "'$filter'\n");
$mesg = $ldap->search(base   => LDAP_BASE,
                      filter => $filter,
		  attrs  => [ LDAP_EMAIL, LDAP_CN, LDAP_UID ]);
if ($mesg->code != LDAP_SUCCESS)  {
  $RT::Logger->critical("LookupExternalUserInfo: Could not search

for “,
”$filter: ", $mesg->code, “\n”);
return ($FoundInExternalDatabase, %params);
}
}

$RT::Logger->debug("LookupExternalUserInfo: Second search produced “,
$mesg->count, " results with filter $filter\n”);

One of the two searches succeeded with just one match

if ($mesg->count == 1) {
$params{‘Name’} = ($mesg->first_entry->get_value(LDAP_UID))[0];
$params{‘EmailAddress’} =
($mesg->first_entry->get_value(LDAP_EMAIL))[0];
$params{‘RealName’} = ($mesg->first_entry->get_value(LDAP_CN))[0];
++$FoundInExternalDatabase;
}

$mesg = $ldap->unbind();
if ($mesg->code != LDAP_SUCCESS) {
$RT::Logger->critical(“LookupExternalUserInfo: Could not unbind from
”,
"LDAP: ", $mesg->code, “\n”);
}
undef $ldap;
undef $mesg;

$RT::Logger->debug(“LookupExternalUserInfo: Leaving LDAP examination
”,
“with:\n”,
"\tName = $params{‘Name’}\n",
"\tEmailAddress = $params{‘EmailAddress’}\n",
"\tRealName = $params{‘RealName’}\n",
"\tFound = $FoundInExternalDatabase\n");

return ($FoundInExternalDatabase, %params) if
$FoundInExternalDatabase;

use Fcntl;
use NDBM_File;

use constant LocalUserFile => ‘/usr/local/rt2/local/localusers’;
use constant LockFile => ‘/tmp/rt2-localusers.lock’;

Lock the database

my $timeout = 20 + time;
while (-e LockFile && time < $timeout) {
sleep(1);
}
open(LOCK, “>@{[ LockFile ]}”) or
die "Can’t create “, LockFile, " file: $!\n”;

tie(my %users, ‘NDBM_File’, LocalUserFile, O_RDONLY, 0444);
my ($tempaddress, $tempname) =
($users{$params{‘EmailAddress’}} =~ /^([^"]+)(.*)/);
untie(%users);

Unlock the database

close(LOCK);
unlink(@{[ LockFile ]});

if ($tempaddress) {
($params{‘EmailAddress’}, $params{‘RealName’}) =
($tempaddress, $tempname);

$params{‘Name’} = $params{‘EmailAddress’};

++$FoundInExternalDatabase;

}

$RT::Logger->debug(“LookupExternalUserInfo: Leaving local file “,
“examination with:\n”,
”\tName = $params{‘Name’}\n”,
"\tEmailAddress = $params{‘EmailAddress’}\n",
"\tRealName = $params{‘RealName’}\n",
"\tFound = $FoundInExternalDatabase\n");

return ($FoundInExternalDatabase, %params);
}

define this if you want to auto create web users

$WebExternalAuto = 1;

if you’re auto creating users, they get their info from this function

it should be returning an array with various User attributes.

sub WebExternalAutoInfo {

my %info;

my $username = $ENV{‘REMOTE_USER’};
$info{‘Name’} = $username;
print “username -> $username\n”;

my $ldap = new Net::LDAP(LDAP, port => LDAP_PORT)
or $RT::Logger->critical(“LookupExternalUserInfo: Cannot connect to
”,
“LDAP’\n”),
return (%info);

my $mesg = $ldap->bind();
if ($mesg->code != LDAP_SUCCESS) {
$RT::Logger->critical("LookupExternalUserInfo: Cannot bind
anonymously ",
“to LDAP:”, $mesg->code, “\n”);
return(%info);
}

my $filter = “(uid=” . $username . “)”;
$RT::Logger->debug(“LookupExternalUserInfo: First search filter “,
”’$filter’\n”);
$mesg = $ldap->search(base => LDAP_BASE,
filter => $filter,
attrs => [ LDAP_EMAIL, LDAP_CN, LDAP_UID,
“mobile”, “departmentNumber”, “extensionNumber”, “givenName” ]);
if ($mesg->code != LDAP_SUCCESS) {
$RT::Logger->critical(“LookupExternalUserInfo: Could not search for
”,
"$filter: ", $mesg->code, “\n”);
return (%info);
}

$info{‘EmailAddress’} =
($mesg->first_entry->get_value(LDAP_EMAIL))[0];
$info{‘RealName’} = ($mesg->first_entry->get_value(LDAP_CN))[0];
$info{‘MobilePhone’} = ($mesg->first_entry->get_value(‘mobile’))[0];
$info{‘WorkPhone’} =
($mesg->first_entry->get_value(‘extensionNumber’))[0];
$info{‘Organization’} =
($mesg->first_entry->get_value(‘departmentNumber’))[0];
$info{‘NickName’} = ($mesg->first_entry->get_value(‘givenName’))[0];

$mesg = $ldap->unbind();
if ($mesg->code != LDAP_SUCCESS) {
$RT::Logger->critical(“LookupExternalUserInfo: Could not unbind from
”,
"LDAP: ", $mesg->code, “\n”);
}

$info{'Privileged'} = 1;
# and return the wad of stuff
return {%info};

}

--------------- Cut Here -----------------------------------

Enjoy!

Carl.

Here is what I’m playing with at the moment. I use
Apache::AuthenNetLDAP in Apache to authenticate against our LDAP server.

my $filter = “(uid=” . $username . “)”;
$RT::Logger->debug(“LookupExternalUserInfo: First search filter “,
”’$filter’\n”);
$mesg = $ldap->search(base => LDAP_BASE,
filter => $filter,
attrs => [ LDAP_EMAIL, LDAP_CN, LDAP_UID,
“mobile”, “departmentNumber”, “extensionNumber”, “givenName” ]);

Two things missing here:

In your filter, you hardcoded “uid” attribute. This will not work
in some setups, like MS Active Directory. Because it uses
"sAMAccountName" for that purpose.

Again, you hardcoded mapping ‘extensionNumber’ and “departmentNumber”, and
this will work in some specific setups only.

Thus, I still prefer my own script :slight_smile:

Stan

a few days ago i submit a patch to this list wich allows to authenticate
directly from ldap, but user information is obtained or imported to rt
using rtimportldap. if you wish i can resend you the patch, with
modifies User.pm.

Regards

I defined $WebExternalAuth in config.pm and now RT no longer asks a
password, it relies on Apache to do so (Apache uses LDAP).

But, if I try to log in as an user which is in LDAP but not in RT, RT
yields:

Not logged in.

Error
You are not an authorized user

Does it mean that I still have to create the users in RT (may be using
URL:http://www.fsck.com/pub/rt/contrib/2.0/rtimportldap.tar from
cron to automatize it)? Or will LookupExternalUserInfo help me?
(Reading its comments, I do not believe it.)

The section “Plugging in your own user metadata” in the Admin. manual
seems interesting but it is currently empty :-}


rt-users mailing list
rt-users@lists.fsck.com
http://lists.fsck.com/mailman/listinfo/rt-users

Have you read the FAQ? The RT FAQ Manager lives at http://fsck.com/rtfm
Marcelo Bartsch
mbartsch@netglobalis.net
www.netglobalis.net

PGP Fingerprint :
877E 3A56 F523 B44A 3260 8F83 8916 E158 6100 F721

Two things missing here:

In your filter, you hardcoded “uid” attribute. This will not work
in some setups, like MS Active Directory. Because it uses
"sAMAccountName" for that purpose.

Again, you hardcoded mapping ‘extensionNumber’ and “departmentNumber”, and
this will work in some specific setups only.

Thus, I still prefer my own script :slight_smile:

Either way works. I prefer this way as I don’t have to regularly run the
script to bring in new people.

The code snippets are hacks, but seem to work here against our iPlanet
Directory Server. The mappings should be extracted into a configuration
hash but if you are inserting large sections of perl code into the
config.pm file to do non-standard things then perhaps some perl
experience might help. :slight_smile:

What I would really prefer is for user and group information to source
from the LDAP server as the definitive source rather than simply using
it to initially populate RT’s own user information. That would help
when user details change.

Carl.

Carl Makin said:

What I would really prefer is for user and group information to source
from the LDAP server as the definitive source rather than simply using
it to initially populate RT’s own user information. That would help
when user details change.

Here, here! This is the way to do things.

-jeff

Carl Makin said:

What I would really prefer is for user and group information to source
from the LDAP server as the definitive source rather than simply using
it to initially populate RT’s own user information. That would help
when user details change.

Here, here! This is the way to do things.

-jeff

I agree!

-ray

What I would really prefer is for user and group information to source
from the LDAP server as the definitive source rather than simply using
it to initially populate RT’s own user information. That would help
when user details change.

well, adding to groups is already done in rtimportldap. You can
establish several filters and run the script several times,
with the groups setting you need.

Deleting from groups is a different thing. If you do that
automatically, you must be sure that you have one-to-one both-ways
mapping of group membership in LDAP and in RT, which is
not always convinient.

What I can do in my script, is replacing the --group option
with two different ones:

–groupadd: this is the old behaviour of “–group”
–groupbind: do the one-to-one match, e.g. those users
selected by filter will remain in the RT group, all others being deleted.

OT: proposal to Jesse:

what about creating a new SourceForge project, “rt-addons” ?
SF is really convinient in that, because it allows different access levels,
and the CVS public access.
I can do all the initial setup, and then add you with administrative rights.

Regards,
Stan

Enjoy the --groupbind option in rtimportldap.

Or, should I rename it to --groupsync?

Stan— Ray Thompson rthompson@interpublic.com wrote:

Carl Makin said:

What I would really prefer is for user and group information to source
from the LDAP server as the definitive source rather than simply using
it to initially populate RT’s own user information. That would help
when user details change.

Here, here! This is the way to do things.

-jeff

I agree!

-ray


rt-users mailing list
rt-users@lists.fsck.com
http://lists.fsck.com/mailman/listinfo/rt-users

Have you read the FAQ? The RT FAQ Manager lives at http://fsck.com/rtfm

a message of 25 lines which said:

correct. you still need to create RT users.

OK, this is what I do, now, with the excellent rtimportldap.

I have patches that
autocreate users using a user-defined function in config.pm.

No thanks, I prefer to keep RT pristine: that way, when a new version
comes in, I do not have to port the patches.

a message of 35 lines which said:

rtimportldap will definetely help. That’s why I created it :slight_smile:

Yes. Thanks for this creation. I now use it and I’m happy with the
current setup (I will document it one day).

a message of 15 lines which said:

current setup (I will document it one day).

Here it is. You can include it in contrib, on the Web site, whereever.

How I have convinced Request Tracker to use my LDAP directory
Stephane Bortzmeyer
bortzmeyer@eureg.org
2003-01-06

Warning: this is a documentation of my setup. There are many ways to
enable LDAP for Request Tracker (RT).

Background: EUREG URL:http://www.eureg.org/ uses an LDAP directory
for every user account. All the machines authenticate users against
it. So, if you log in via SSH, authenticate on a private Web page with
Apache, or use RT, you have only one account and one password.

To enable LDAP access from RT, you have to do the following:

  1. configure Apache to authenticate against LDAP:

LoadModule auth_ldap_module /usr/lib/apache/1.3/auth_ldap.so

SSL only, we do not have “weak” and “strong” passwords, every

password is important! So, we redirect everything on the secure server.

<VirtualHost 192.134.7.250:80>
ServerName rt.eureg.org
SSLDisable
RewriteEngine on
RewriteRule ^/(.*)$ https://rt.eureg.org/$1 [R,L]

The secure server

<VirtualHost 192.134.7.250:443>
DocumentRoot /local/rt2/WebRT/html
ServerName rt.eureg.org
SSLEnable

A few RT-specific lines ommitted

AuthType Basic AuthName EUREG AuthLDAPURL ldap://ldap.eureg.org/ou=People,dc=eureg,dc=eu?uid require valid-user
  1. Now, we can tell RT to trust Apache:

If $WebExternalAuth is defined, RT will defer to the environment’s

REMOTE_USER variable.

$WebExternalAuth = 1;

This way, RT will no longer asks a password.

  1. The above is not sufficient: RT has no real LDAP support, it needs
    to find locally, in its own database, the info about an user (wether
    he can log in, for instance). We therefore use the rtimportldap script
    URL:http://www.fsck.com/pub/rt/contrib/2.0/ to synchronize RT’s
    database with LDAP.

Every day, /etc/cron.daily/rtimportldap is run by root. It contains
(be sure to set its mode to 700, there is a LDAP password! Choose a DN
which has only read access):

#!/bin/sh

PATH=${PATH}:/local/sbin:/local/bin
rtimportldap.pl --server ldap.eureg.org
–binddn ‘cn=backup,dc=eureg,dc=eu’ --bindpw verysecret
–basedn ‘dc=eureg,dc=eu’ --groupadd EUreg
–filter ‘(objectClass=posixAccount)’ | grep -v ‘Updating user’

  1. For us, this is all. There is still the issue of
    $LookupSenderInExternalDatabase (for the email interface, if you use
    it), but I’ve not configured it yet.

rtimportldap.pl --server ldap.eureg.org
–binddn ‘cn=backup,dc=eureg,dc=eu’ --bindpw verysecret
–basedn ‘dc=eureg,dc=eu’ --groupadd EUreg
–filter ‘(objectClass=posixAccount)’ | grep -v ‘Updating user’

Be noted, that the next verion of rtimportldap (when Andi Hofmeister
finds time to test it in non-Microsoft environment) will
not print “Updating user”. It will print “Changing…” when something
is changed only.

or, shall I publish it as-is?..

Regards,
Stan

a message of 16 lines which said:

Be noted, that the next verion of rtimportldap (when Andi Hofmeister
finds time to test it in non-Microsoft environment) will
not print “Updating user”. It will print “Changing…” when something
is changed only.

Good idea. Even better: to shut up cron, I would prefer the
implementation of the following option.

–quiet : No messages (except errors)