https://rt-wiki.bestpractical.com/index.php?title=AutoCreateAndCanonicalizeUserInfo&feed=atom&action=historyAutoCreateAndCanonicalizeUserInfo - Revision history2024-03-29T13:41:37ZRevision history for this page on the wikiMediaWiki 1.37.2https://rt-wiki.bestpractical.com/index.php?title=AutoCreateAndCanonicalizeUserInfo&diff=244&oldid=prevAdmin: 4 revisions imported2016-04-06T20:03:17Z<p>4 revisions imported</p>
<p><b>New page</b></p><div>{{Outdated}}<br />
<br />
== Auto Create User and Lookup Alternate Addresses via LDAP ==<br />
<br />
To make the [[AutoCreateFromExternalUserInfo]] overlay recognise that a person may have multiple users I added amended scripts. This allows RT to work the LDAP directory in MS Exchange 5.5 (I don't know about later versions). e.g.<br />
<br />
cn = joeb<br />
email = joe.bloggs@company.com<br />
otherMailbox = smtp$joeb@company.com<br />
otherMailbox = smtp$joebloggs@company.com<br />
<br />
Will all refer back to RT user joeb with RT email = [mailto:joe.bloggs@company.com joe.bloggs@company.com]<br />
<br />
It also allows the helpdesk staff to enter requestors as joeb, [mailto:joeb@company.com joeb@company.com], [mailto:joe.bloggs@company.com joe.bloggs@company.com] or [mailto:joebloggs@company.com joebloggs@company.com] and still refer to user joeb.<br />
<br />
Place [[CurrentUser Local|CurrentUser_Local]].pm and [[User Local|User_Local]].pm in $RT_HOME/lib/RT folders<br />
<br />
=== User_Local.pm ===<br />
<br />
<nowiki>#<br />
# User_Local.pm overlay<br />
#<br />
# author: Phillip Cole (phillip d cole @ uk d coltgroup d com)<br />
# based on CurrentUser_Local.pm and much help from the mailing lists<br />
#<br />
no warnings qw(redefine);<br />
<br />
# {{{ sub CanonicalizeEmailAddress<br />
<br />
=head2 CanonicalizeEmailAddress ADDRESS<br />
<br />
# Lookup this email address in Exchange LDAP to make sure it is the<br />
# users primary email address<br />
# i.e. replace philcole@company.com with phillip.cole@company.com<br />
<br />
=cut<br />
<br />
sub CanonicalizeEmailAddress {<br />
my $self = shift;<br />
my $email = shift;<br />
<br />
$RT::Logger-&gt;debug("CanonicalizeEmailAddress: Inside User_Local.pm version email=$email\n");<br />
if ($email) {<br />
<br />
my ($found, %params) = RT::User::LookupExternalUserInfo("otherMailbox", "smtp\$$email");<br />
if ($found) {<br />
if ($params{'EmailAddress'} and ($email ne $params{'EmailAddress'})) {<br />
$RT::Logger-&gt;info("CanonicalizeEmailAddress: Changing email to " . $params{'EmailAddress'} . "\n");<br />
return($params{'EmailAddress'});<br />
}<br />
}<br />
}<br />
return($email);<br />
}<br />
<br />
<br />
# {{{ sub CanonicalizeUserInfo<br />
<br />
=head2 CanonicalizeUserInfo<br />
<br />
Colt Custom function to lookup a new users details from within an LDAP directory<br />
<br />
=cut<br />
<br />
sub CanonicalizeUserInfo {<br />
my $self = shift;<br />
my $args = shift;<br />
my $success = 1;<br />
$success = 0;<br />
<br />
$RT::Logger-&gt;debug("CanonicalizeUserInfo: Inside User_Local.pm version\n" .<br />
' $args-&gt;{\'RealName\'} =' . $args-&gt;{'RealName'} . "\n" .<br />
' $args-&gt;{\'Name\'} =' . $args-&gt;{'Name'} . "\n" .<br />
' $args-&gt;{\'EmailAddress\'}=' . $args-&gt;{'EmailAddress'} . "\n");<br />
<br />
# $args is a hash reference. To get at it's values need to call $args-&gt;{'key'}<br />
# $args has keys:<br />
# RealName - User human name (e.g. Cole, Phillip)<br />
# Name - Username (e.g. ukpgc)<br />
# EmailAddress - Email address (e.g. phillip.cole@company.com)<br />
# Comments - Comments created during creation<br />
<br />
# Try to match by User ID first<br />
my ($found, %params) = RT::User::LookupExternalUserInfo("uid", $args-&gt;{'Name'});<br />
if (!$found) {<br />
# If not found try to match email address<br />
my $email = $self-&gt;CanonicalizeEmailAddress($args-&gt;{'EmailAddress'});<br />
($found, %params) = RT::User::LookupExternalUserInfo("mail", $email);<br />
}<br />
<br />
if ($found) {<br />
$RT::Logger-&gt;info("CanonicalizeUserInfo: Setting args as returned via LDAP,\n");<br />
$args-&gt;{'Name'} = $params{'Name'};<br />
$args-&gt;{'RealName'} = $params{'RealName'};<br />
$args-&gt;{'EmailAddress'} = $params{'EmailAddress'};<br />
$args-&gt;{'Comments'} = 'Autocreated via LDAP when added as a watcher';<br />
$success = 1;<br />
}<br />
<br />
return($success);<br />
}<br />
# }}}<br />
<br />
# {{{ sub LookupExternalUserInfo<br />
<br />
=head2 LookupExternalUserInfo<br />
<br />
LookupExternalUserInfo takes a key/value pair, looks it up in LDAP,<br />
and returns a (sparse) params hash suitable for creating a User object<br />
<br />
=cut<br />
<br />
sub LookupExternalUserInfo {<br />
my $key = shift;<br />
my $value = shift;<br />
<br />
# I have dreams of this function using an $LdapAttrMap hash fpr<br />
# Ldap -&gt; RT account creation, in the form of:<br />
#<br />
# %RT::LdapAttrMap = ( Name=&gt; 'uid',<br />
# RealName=&gt; 'cn',<br />
# EmailAdress=&gt; 'mail',<br />
# [anyUserAttr]=&gt; [anyLdapAttr]);<br />
#<br />
# But until then, you better have the following variables defined<br />
# in your RT_SiteConfig.pm:<br />
#<br />
# Set($LdapServer, "ldap.domain.tld");<br />
# Set($LdapBase, "cn=Users,dc=domain,dc=tld");<br />
# Set($LdapUidAttr, "uid");<br />
# Set($LdapNameAttr, "cn");<br />
# Set($LdapMailAttr, "mail");<br />
# Set($LdapFilter, "(objectclass=person)");<br />
# Set($AutoCreateFromExternalUserInfo, 1);<br />
#<br />
# enjoy!<br />
<br />
<br />
my $FoundInExternalDatabase = 0;<br />
my %params = ( Name=&gt; undef,<br />
EmailAddress=&gt; undef,<br />
RealName=&gt; undef);<br />
<br />
<br />
$RT::Logger-&gt;debug("LookupExternalUserInfo: Entered with:\n",<br />
"\tkey = $key\n",<br />
"\tvalue = $value\n");<br />
<br />
use Net::LDAP;<br />
use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_PARTIAL_RESULTS);<br />
<br />
my $ldap = new Net::LDAP($RT::LdapServer)<br />
or $RT::Logger-&gt;critical("LookupExternalUserInfo: Cannot connect to ",<br />
"LDAP'\n"),<br />
return ($FoundInExternalDatabase, %params);<br />
<br />
my $mesg = $ldap-&gt;bind();<br />
if ($mesg-&gt;code != LDAP_SUCCESS) {<br />
$RT::Logger-&gt;critical("LookupExternalUserInfo: Cannot bind anonymously ",<br />
"to LDAP:", $mesg-&gt;code, "\n");<br />
#$params{'RealName'} = "\"$params{'RealName'}\"";<br />
return ($FoundInExternalDatabase, %params);<br />
}<br />
<br />
<br />
my $filter = "@{[ $key ]}=$value";<br />
$RT::Logger-&gt;debug("LookupExternalUserInfo: First search filter ",<br />
"'$filter'\n");<br />
$mesg = $ldap-&gt;search(base =&gt; $RT::LdapBase,<br />
filter =&gt; $filter,<br />
attrs =&gt; [ $RT::LdapUidAttr, $RT::LdapMailAttr, $RT::LdapNameAttr ]);<br />
if ($mesg-&gt;code != LDAP_SUCCESS and $mesg-&gt;code != LDAP_PARTIAL_RESULTS) {<br />
$RT::Logger-&gt;critical("LookupExternalUserInfo: Could not search for ",<br />
"$filter: ", $mesg-&gt;code, "\n");<br />
$params{'RealName'} = "\"$params{'RealName'}\"";<br />
return ($FoundInExternalDatabase, %params);<br />
}<br />
<br />
$RT::Logger-&gt;debug("LookupExternalUserInfo: Search produced ",<br />
$mesg-&gt;count, " results\n");<br />
<br />
# The search succeeded with just one match<br />
if ($mesg-&gt;count == 1) {<br />
$params{'Name'} = ($mesg-&gt;first_entry-&gt;get_value($RT::LdapUidAttr))[0];<br />
$params{'EmailAddress'} = ($mesg-&gt;first_entry-&gt;get_value($RT::LdapMailAttr))[0];<br />
$params{'RealName'} = ($mesg-&gt;first_entry-&gt;get_value($RT::LdapNameAttr))[0];<br />
#$params{'RealName'} = "\"$params{'RealName'}\"";<br />
<br />
$FoundInExternalDatabase = 1;<br />
}<br />
<br />
$mesg = $ldap-&gt;unbind();<br />
if ($mesg-&gt;code != LDAP_SUCCESS) {<br />
$RT::Logger-&gt;critical("LookupExternalUserInfo: Could not unbind from ",<br />
"LDAP: ", $mesg-&gt;code, "\n");<br />
}<br />
undef $ldap;<br />
undef $mesg;<br />
<br />
$RT::Logger-&gt;debug("LookupExternalUserInfo: Leaving LDAP examination ",<br />
"with:\n",<br />
"\tName = $params{'Name'}\n",<br />
"\tEmailAddress = $params{'EmailAddress'}\n",<br />
"\tRealName = $params{'RealName'}\n",<br />
"\tFound = $FoundInExternalDatabase\n");<br />
<br />
return ($FoundInExternalDatabase, %params);<br />
}<br />
# }}}<br />
<br />
1;<br />
<br />
</nowiki><br />
<br />
=== CurrentUser_Local.pm ===<br />
<br />
<nowiki>#<br />
# AutoCreateFromExternalUserInfo overlay<br />
#<br />
# author: Robin Battey &lt;zanfur at zanfur dot com&gt;<br />
# modified by Phillip Cole to remove $self-&gt; from LookupExternalUserInfo<br />
# and move LookupExternalUserInfo into User_Local.pm<br />
#<br />
# based in part by the LookupExternalUserInfo function<br />
# written by Jeff Hoover &lt;jeff.hoover at infotechfl dot com&gt;<br />
#<br />
# This Overlay is useful if you want the following behavior:<br />
# * Whenever RT tries -- and fails -- to load a user from the<br />
# RT user database, it queries an Ldap server for the account<br />
# information and creates it on the fly (for both email lookups<br />
# and web logins)<br />
# * RT behaves as though the account has always been there -- i.e.<br />
# there is no reason to give "Everyone" the "Create Ticket" right,<br />
# because the account has been there all along ...<br />
# * NO synchronization with the Ldap server once the account has<br />
# been created, so any changes in RT stay in RT and any changes<br />
# in Ldap stay in Ldap (unless you run some other script, of course)<br />
#<br />
# The situation I had at my place of employment, which caused me<br />
# to write this, is we have an Active Directory server with a fairly<br />
# large number of users on it, with a fair bit of adding/deleting of<br />
# users going on. I wanted a way for users to send a request to the<br />
# help queue, and then immediately be able to see the ticket, the<br />
# ticket's status, etc using their Active Directory (i.e. windows<br />
# domain) username and password. I wrote this overlay for the account<br />
# generation, and used the LdapOverlay available in the "Contributions"<br />
# section of the RT3 Wiki (http://wiki.bestpractical.com) for the<br />
# authentication. This overlay was therefore designed to work well<br />
# with the LdapOverlay, and actually uses (some of) the same Ldap<br />
# variables.<br />
#<br />
# I spent a bit of effort making sure this was modular, particularly<br />
# in the case of a different external user info source. It currently<br />
# works with LDAP (and, of course, Active Directory), but there's no<br />
# good reason why you can't just replace the LookupExternalUserInfo<br />
# function to get your data from somewhere else.<br />
<br />
no warnings qw(redefine);<br />
<br />
<br />
# {{{ sub CreateFromExternalUserInfo<br />
<br />
=head2 CreateFromExternalUserInfo<br />
<br />
Calls LookupExternalUserInfo and creates a user from the results<br />
<br />
=cut<br />
<br />
sub CreateFromExternalUserInfo {<br />
my $self = shift;<br />
my @lookupargs = @_;<br />
<br />
$RT::Logger-&gt;debug( "CreateFromExternalUserInfo: entered with args: ", @lookupargs, "\n");<br />
<br />
my ($found, %params) = RT::User::LookupExternalUserInfo (@lookupargs);<br />
$RT::Logger-&gt;debug( "CreateFromExternalUserInfo: params hash: ", %params, "\n");<br />
unless ($found) {<br />
$RT::Logger-&gt;info ("CreateFromExternalUserInfo: failed to find user with args: ", @lookupargs, "\n");<br />
return undef;<br />
}<br />
<br />
my $UserObj = RT::User-&gt;new($RT::SystemUser);<br />
my ($val, $msg) = $UserObj-&gt;Create(<br />
%{ref($RT::AutoCreate) ? $RT::AutoCreate : {}},<br />
%params<br />
);<br />
<br />
unless ($val) {<br />
$RT::Logger-&gt;info ("CreateFromExternalUserInfo: failed to create user with args: ", @lookupargs, "\n");<br />
return undef;<br />
}<br />
<br />
$RT::Logger-&gt;info ("CreateFromExternalUserInfo: created user $val (\"$msg\")\n");<br />
$self-&gt;Load($val);<br />
}<br />
# }}}<br />
<br />
<br />
# {{{ sub LoadByEmail<br />
<br />
=head2 LoadByEmail<br />
<br />
Loads a User into this CurrentUser object.<br />
Takes the email address of the user to load.<br />
<br />
=cut<br />
<br />
sub LoadByEmail {<br />
my $self = shift;<br />
my $identifier = shift;<br />
$identifier = RT::User::CanonicalizeEmailAddress(undef, $identifier);<br />
<br />
$self-&gt;LoadByCol("EmailAddress",$identifier);<br />
<br />
if (!$self-&gt;Id and $RT::AutoCreateFromExternalUserInfo) {<br />
$self-&gt;CreateFromExternalUserInfo ("mail", $identifier);<br />
}<br />
}<br />
# }}}<br />
<br />
# {{{ sub LoadByName<br />
<br />
=head2 LoadByName<br />
<br />
Loads a User into this CurrentUser object.<br />
Takes a Name.<br />
=cut<br />
<br />
sub LoadByName {<br />
my $self = shift;<br />
my $identifier = shift;<br />
<br />
$self-&gt;LoadByCol("Name",$identifier);<br />
if (!$self-&gt;Id and $RT::AutoCreateFromExternalUserInfo) {<br />
$self-&gt;CreateFromExternalUserInfo ("uid", $identifier);<br />
$self-&gt;LoadByCol("Name",$identifier);<br />
}<br />
}<br />
# }}}<br />
<br />
# {{{ sub Load<br />
<br />
=head2 Load<br />
<br />
Loads a User into this CurrentUser object.<br />
Takes either an integer (users id column reference) or a Name<br />
The latter is deprecated. Instead, you should use LoadByName.<br />
Formerly, this routine also took email addresses.<br />
<br />
=cut<br />
<br />
sub Load {<br />
my $self = shift;<br />
my $identifier = shift;<br />
<br />
#if it's an int, load by id. otherwise, load by name.<br />
if ($identifier !~ /\D/) {<br />
$self-&gt;SUPER::LoadById($identifier);<br />
}<br />
<br />
elsif (UNIVERSAL::isa($identifier,"RT::User")) {<br />
# DWIM if they pass a user in<br />
$self-&gt;SUPER::LoadById($identifier-&gt;Id);<br />
}<br />
else {<br />
# This is a bit dangerous, we might get false authen if somebody<br />
# uses ambigous userids or real names:<br />
$self-&gt;LoadByName($identifier);<br />
}<br />
}<br />
<br />
# }}}<br />
<br />
1;<br />
<br />
<br />
<br />
</nowiki><br />
<br />
--Phillip Cole 30/09/05<br />
[[Category:Outdated]]</div>Admin