Username same but realname and email is different for existing user compare to ldap attribute, caused create ticket failed

Hi

We started using external auth for ldap auth using
RT::Authen::ExternalAuth on RT 3.8.2 for more than a month.

Our company name changed, so the email domain name changed from
foo.com to bar.com

I have user with valid account
1893818 muser “User, My” My.User@foo.com

But he failed to create a ticket since his email address is now My.User@bar.com

[Wed Jul 25 16:52:46 2012] [info]:
RT::Authen::ExternalAuth::CanonicalizeUserInfo returning Comments:
Autocreated on ticket submission, Disa
bled: , EmailAddress: My.User@bar.com, Name: muser, Password: ,
Privileged: , RealName: My User (/opt/rt3/local/plugins/
RT-Authen-ExternalAuth/lib/RT/Authen/ExternalAuth.pm:633)
[Wed Jul 25 16:52:46 2012] [crit]: User creation failed in
mailgateway: Name in use
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [warning]: Couldn’t load user
‘My.User@bar.com’.giving up
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:
806)
[Wed Jul 25 16:52:46 2012] [crit]: User ‘My.User@example.com’ could
not be loaded in the mail gateway (/opt/rt3/bin/…/lib/RT/Inter
face/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: RT could not load a valid user,
and RT’s configuration does not allow
for the creation of a new user for this email (My.User@example.com).

You might need to grant ‘Everyone’ the right ‘CreateTicket’ for the
queue syshelp. (/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: RT could not load a valid user,
and RT’s configuration does not allow
for the creation of a new user for your email.
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: Could not record email: Could not
load a valid user (/opt/rt3/share/html/REST/1.0/NoAuth/mail-gateway:75
)

Should I change the matching restriction on the config file from

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,
‘EmailAddress’,
‘RealName’,
],
# The mapping of RT attributes on to LDAP attributes
‘attr_map’ => { ‘Name’ => ‘uid’,
‘EmailAddress’
=> ‘mail’,
‘RealName’ => ‘cn’,
}

to just username which never changed ?

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,

                                            ],
            # The mapping of RT attributes on to LDAP attributes
            'attr_map'                  =>  {       'Name' => 'uid',

                                            }

In this case user real name (cn) is different in ldap that auto
created real name that was picked up from email address.

Please advise.

Asif Iqbal
PGP Key: 0xE62693C5 KeyServer: pgp.mit.edu
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?

Hi

We started using external auth for ldap auth using
RT::Authen::ExternalAuth on RT 3.8.2 for more than a month.

Our company name changed, so the email domain name changed from
foo.com to bar.com

I have user with valid account
1893818 muser “User, My” My.User@foo.com

But he failed to create a ticket since his email address is now My.User@bar.com

[Wed Jul 25 16:52:46 2012] [info]:
RT::Authen::ExternalAuth::CanonicalizeUserInfo returning Comments:
Autocreated on ticket submission, Disa
bled: , EmailAddress: My.User@bar.com, Name: muser, Password: ,
Privileged: , RealName: My User (/opt/rt3/local/plugins/
RT-Authen-ExternalAuth/lib/RT/Authen/ExternalAuth.pm:633)
[Wed Jul 25 16:52:46 2012] [crit]: User creation failed in
mailgateway: Name in use
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [warning]: Couldn’t load user
‘My.User@bar.com’.giving up
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:
806)
[Wed Jul 25 16:52:46 2012] [crit]: User ‘My.User@example.com’ could
not be loaded in the mail gateway (/opt/rt3/bin/…/lib/RT/Inter
face/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: RT could not load a valid user,
and RT’s configuration does not allow
for the creation of a new user for this email (My.User@example.com).

You might need to grant ‘Everyone’ the right ‘CreateTicket’ for the
queue syshelp. (/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: RT could not load a valid user,
and RT’s configuration does not allow
for the creation of a new user for your email.
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: Could not record email: Could not
load a valid user (/opt/rt3/share/html/REST/1.0/NoAuth/mail-gateway:75
)

Should I change the matching restriction on the config file from

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,
‘EmailAddress’,
‘RealName’,
],
# The mapping of RT attributes on to LDAP attributes
‘attr_map’ => { ‘Name’ => ‘uid’,
‘EmailAddress’
=> ‘mail’,
‘RealName’ => ‘cn’,
}

to just username which never changed ?

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,

                                            ],
            # The mapping of RT attributes on to LDAP attributes
            'attr_map'                  =>  {       'Name' => 'uid',

                                            }

In this case user real name (cn) is different in ldap that auto
created real name that was picked up from email address.

Please advise.

We added an LDAP lookup and some logic to CanonicalizeEmailAddress()
to map both the old and new Email addresses to the new Email address
and allow both to be valid for ticket creation. In our case we were
having different valid addresses due to users changing their preferred
Email address. The change hit LDAP first and only hit RT during the
nightly update.

Regards,
Ken

Hi

We started using external auth for ldap auth using
RT::Authen::ExternalAuth on RT 3.8.2 for more than a month.

Our company name changed, so the email domain name changed from
foo.com to bar.com

I have user with valid account
1893818 muser “User, My” My.User@foo.com

But he failed to create a ticket since his email address is now My.User@bar.com

[Wed Jul 25 16:52:46 2012] [info]:
RT::Authen::ExternalAuth::CanonicalizeUserInfo returning Comments:
Autocreated on ticket submission, Disa
bled: , EmailAddress: My.User@bar.com, Name: muser, Password: ,
Privileged: , RealName: My User (/opt/rt3/local/plugins/
RT-Authen-ExternalAuth/lib/RT/Authen/ExternalAuth.pm:633)
[Wed Jul 25 16:52:46 2012] [crit]: User creation failed in
mailgateway: Name in use
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [warning]: Couldn’t load user
‘My.User@bar.com’.giving up
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:
806)
[Wed Jul 25 16:52:46 2012] [crit]: User ‘My.User@example.com’ could
not be loaded in the mail gateway (/opt/rt3/bin/…/lib/RT/Inter
face/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: RT could not load a valid user,
and RT’s configuration does not allow
for the creation of a new user for this email (My.User@example.com).

You might need to grant ‘Everyone’ the right ‘CreateTicket’ for the
queue syshelp. (/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: RT could not load a valid user,
and RT’s configuration does not allow
for the creation of a new user for your email.
(/opt/rt3/bin/…/lib/RT/Interface/Email.pm:244)
[Wed Jul 25 16:52:46 2012] [error]: Could not record email: Could not
load a valid user (/opt/rt3/share/html/REST/1.0/NoAuth/mail-gateway:75
)

Should I change the matching restriction on the config file from

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,
‘EmailAddress’,
‘RealName’,
],
# The mapping of RT attributes on to LDAP attributes
‘attr_map’ => { ‘Name’ => ‘uid’,
‘EmailAddress’
=> ‘mail’,
‘RealName’ => ‘cn’,
}

to just username which never changed ?

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,

                                            ],
            # The mapping of RT attributes on to LDAP attributes
            'attr_map'                  =>  {       'Name' => 'uid',

                                            }

In this case user real name (cn) is different in ldap that auto
created real name that was picked up from email address.

Please advise.

We added an LDAP lookup and some logic to CanonicalizeEmailAddress()
to map both the old and new Email addresses to the new Email address
and allow both to be valid for ticket creation. In our case we were
having different valid addresses due to users changing their preferred
Email address. The change hit LDAP first and only hit RT during the
nightly update.

that’s what I asked in #rt channel and got no response. so kind a like
pam stacking.

check ldap, if fails then check local. make sense. is it possible to
share that snippet?

Regards,
Ken

Asif Iqbal
PGP Key: 0xE62693C5 KeyServer: pgp.mit.edu
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?

Should I change the matching restriction on the config file from

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,
‘EmailAddress’,
‘RealName’,
],

Don’t change attr_map, it’s not used for auth just updating info.
attr_match_list should almost never contain RealName. Read the comment
right above it, the last line of which you pasted above and recommends
changing the example “to just Name and EmailAddress to save encountering
problems later”.

Should I change the matching restriction on the config file from

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,
‘EmailAddress’,
‘RealName’,
],

Don’t change attr_map, it’s not used for auth just updating info.
attr_match_list should almost never contain RealName. Read the comment
right above it, the last line of which you pasted above and recommends
changing the example “to just Name and EmailAddress to save encountering
problems later”.

What was I thinking :slight_smile: . Thanks!

Also looking for a tip on how to do match
email address to ldap first and if fails check with existing email of that Name.

I have lots of “existing” user in RT with old email from years ago and
new email with new company name.

Asif Iqbal
PGP Key: 0xE62693C5 KeyServer: pgp.mit.edu
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?

Should I change the matching restriction on the config file from

            # to just the Name and EmailAddress to save

encountering problems later.
‘attr_match_list’ => [ ‘Name’,
‘EmailAddress’,
‘RealName’,
],

Don’t change attr_map, it’s not used for auth just updating info.
attr_match_list should almost never contain RealName. Read the comment
right above it, the last line of which you pasted above and recommends
changing the example “to just Name and EmailAddress to save encountering
problems later”.

What was I thinking :slight_smile: . Thanks!

Also looking for a tip on how to do match
email address to ldap first and if fails check with existing email of that Name.

I have lots of “existing” user in RT with old email from years ago and
new email with new company name.

Here is our version of local/lib/RT/User_Local.pm for RT 3.8.12
which should be really close to what you need.

Regards,
Ken

--------------local/lib/RT/User_Local.pm------------------------
no warnings qw(redefine);

use Digest::MD5;
use RT::Principals;
use RT::ACE;
use RT::Interface::Email;
use Encode;

use Net::LDAP;
use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_PARTIAL_RESULTS);
use Net::LDAP::Util qw (ldap_error_name);
use Net::LDAP::Filter;

sub Create {
my $self = shift;
my %args = (
Privileged => 0,
Disabled => 0,
EmailAddress => ‘’,
RecordTransaction => 1,
@
# get the real argumentlist
);

# remove the value so it does not cripple SUPER::Create
my $record_transaction = delete $args{'_RecordTransaction'};

#Check the ACL
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
    return ( 0, $self->loc('Permission Denied') );
}


unless ($self->CanonicalizeUserInfo(\%args)) {
    return ( 0, $self->loc("Could not set user info") );
}

$args{'EmailAddress'} = $self->CanonicalizeEmailAddress($args{'EmailAddress'});

# if the user doesn't have a name defined, set it to the email address
$args{'Name'} = $args{'EmailAddress'} unless ($args{'Name'});



my $privileged = delete $args{'Privileged'};


if ($args{'CryptedPassword'} ) {
    $args{'Password'} = $args{'CryptedPassword'};
    delete $args{'CryptedPassword'};
}
elsif ( !$args{'Password'} ) {
    $args{'Password'} = '*NO-PASSWORD*';
}
elsif ( length( $args{'Password'} ) < RT->Config->Get('MinimumPasswordLength') ) {
    return ( 0, $self->loc("Password needs to be at least [_1] characters long",RT->Config->Get('MinimumPasswordLength')) );
}

else {
    $args{'Password'} = $self->_GeneratePassword($args{'Password'});
}

#TODO Specify some sensible defaults.

unless ( $args{'Name'} ) {
    return ( 0, $self->loc("Must specify 'Name' attribute") );
}

We cannot assume uniqueness of Name for global RT use. ktm - 20091104

#SANITY CHECK THE NAME AND ABORT IF IT’S TAKEN

if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined

my $TempUser = RT::User->new($RT::SystemUser);

$TempUser->Load( $args{‘Name’} );

return ( 0, $self->loc(‘Name in use’) ) if ( $TempUser->Id );

my ($val, $message) = $self->ValidateEmailAddress( $args{‘EmailAddress’} );

return (0, $message) unless ( $val );

}

else {

$RT::Logger->warning( “$self couldn’t check for pre-existing users”);

}

$RT::Handle->BeginTransaction();
# Groups deal with principal ids, rather than user ids.
# When creating this user, set up a principal Id for it.
my $principal = RT::Principal->new($self->CurrentUser);
my $principal_id = $principal->Create(PrincipalType => 'User',
                            Disabled => $args{'Disabled'},
                            ObjectId => '0');
# If we couldn't create a principal Id, get the fuck out.
unless ($principal_id) {
    $RT::Handle->Rollback();
    $RT::Logger->crit("Couldn't create a Principal on new user create.");
    $RT::Logger->crit("Strange things are afoot at the circle K");
    return ( 0, $self->loc('Could not create user') );
}

$principal->__Set(Field => 'ObjectId', Value => $principal_id);
delete $args{'Disabled'};

$self->SUPER::Create(id => $principal_id , %args);
my $id = $self->Id;

#If the create failed.
unless ($id) {
    $RT::Handle->Rollback();
    $RT::Logger->error("Could not create a new user - " .join('-', %args));

    return ( 0, $self->loc('Could not create user') );
}

my $aclstash = RT::Group->new($self->CurrentUser);
my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);

unless ($stash_id) {
    $RT::Handle->Rollback();
    $RT::Logger->crit("Couldn't stash the user in groupmembers");
    return ( 0, $self->loc('Could not create user') );
}


my $everyone = RT::Group->new($self->CurrentUser);
$everyone->LoadSystemInternalGroup('Everyone');
unless ($everyone->id) {
    $RT::Logger->crit("Could not load Everyone group on user creation.");
    $RT::Handle->Rollback();
    return ( 0, $self->loc('Could not create user') );
}


my ($everyone_id, $everyone_msg) = $everyone->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
unless ($everyone_id) {
    $RT::Logger->crit("Could not add user to Everyone group on user creation.");
    $RT::Logger->crit($everyone_msg);
    $RT::Handle->Rollback();
    return ( 0, $self->loc('Could not create user') );
}


my $access_class = RT::Group->new($self->CurrentUser);
if ($privileged)  {
    $access_class->LoadSystemInternalGroup('Privileged');
} else {
    $access_class->LoadSystemInternalGroup('Unprivileged');
}

unless ($access_class->id) {
    $RT::Logger->crit("Could not load Privileged or Unprivileged group on user creation");
    $RT::Handle->Rollback();
    return ( 0, $self->loc('Could not create user') );
}


my ($ac_id, $ac_msg) = $access_class->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);  

unless ($ac_id) {
    $RT::Logger->crit("Could not add user to Privileged or Unprivileged group on user creation. Aborted");
    $RT::Logger->crit($ac_msg);
    $RT::Handle->Rollback();
    return ( 0, $self->loc('Could not create user') );
}


if ( $record_transaction ) {
$self->_NewTransaction( Type => "Create" );
}

$RT::Handle->Commit;

return ( $id, $self->loc('User created') );

}

sub CanonicalizeEmailAddress {
my $self = shift;
my $email = shift;

# Leave some addresses intact
if ( $email =~ /[\w-]+\@pager.rice.edu$/ ) {
    return ($email);
}
if ( $email =~ /[\w-]+\@help.rice.edu$/ ) {
    return ($email);
}
if ( $email =~ /[\w-]+\@alumni.rice.edu$/ ) {
    return ($email);
}

# Example: the following rule would treat all email
# coming from a subdomain as coming from second level domain
# foo.com
if ( my $match   = RT->Config->Get('CanonicalizeEmailAddressMatch') and
     my $replace = RT->Config->Get('CanonicalizeEmailAddressReplace') )
{
    $email =~ s/$match/$replace/gi;
}
$email .= '@rice.edu' if ($email =~ /^[\w-]+$/);

# Now we should have an Email address that is of the form addr@rice.edu
# Use LDAP to map this to the primary vanity Email alias.

my $ldap = new Net::LDAP($RT::LdapServer)
  or $RT::Logger->critical("CanonicalizeEmailAddress: Cannot connect to LDAP\n"),
    return ($email);

# We need to use a bind credential. ktm - 20120410

my $mesg = $ldap->start_tls; # turn on TLS or service token bind

$mesg = $ldap->bind($RT::LdapUser, password => $RT::LdapPass);

if ($mesg->code != LDAP_SUCCESS) {
  $RT::Logger->critical("CanonicalizeEmailAddress: Unable to bind to $RT::LdapServer: ",
    ldap_error_name($mesg->code), "\n");

  return ($email);
}

# First check to see if the E-mail address uniquely characterizes the
# user. If so, update the information with the LDAP query results.
my $filter = "(mailAlternateAddress=$email)";
$mesg = $ldap->search(base   => $RT::LdapBase,
                      filter => $filter,
                      attrs  => [ $RT::LdapMailAttr, $RT::LdapUidAttr ]);

if ($mesg->code != LDAP_SUCCESS and $mesg->code != LDAP_PARTIAL_RESULTS)  {
  $RT::Logger->critical("Unable to search in LDAP: ", ldap_error_name($mesg->code), "\n");

  return ($email);
}

# The search succeeded with just one match. The value returned is the current primary
# Email address in the LDAP directory. Check the Email address loaded by LdapUidAttr
# and if it is different, use it instead of the new primary Email address. This will
# prevent ticket creation problems caused by the fact the we only sync with LDAP
# once a day.
if ($mesg->count == 1) {
  my $User = new RT::User($RT::SystemUser);
  my $uid = ($mesg->first_entry->get_value($RT::LdapUidAttr))[0];
  $email = ($mesg->first_entry->get_value($RT::LdapMailAttr))[0];

  $User->Load($uid);

  # Check to see if we found a user, if so get the account's Email address
  if ($User->id > 0) {
    my $email_rt = $User->EmailAddress;

    # If the Email address is not the current preferred Email address return the
    # old address. This will allow tickets to be created until the LDAP is sync-ed
    # each night.
    if ($email ne $email_rt) {
      $RT::Logger->warning("Returning Email from RT:$email_rt not LDAP:$email\n");
      $email = $email_rt;
    }
  }
}

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

}
undef $ldap;
undef $mesg;

return ($email);

}

=head2 CanonicalizeUserInfo HASH of ARGS

CanonicalizeUserInfo can convert all User->Create options.
it takes a hashref of all the params sent to User->Create and
returns that same hash, by default nothing is done.

This function is intended to allow users to have their info looked up via
an outside source and modified upon creation.

=cut

sub CanonicalizeUserInfo {
my $self = shift;
my $args = shift;
my $success = 1;

return ($success);

}

1;

--------------local/lib/RT/User_Local.pm------------------------

We cannot assume uniqueness of Name for global RT use. ktm - 20091104

#SANITY CHECK THE NAME AND ABORT IF IT’S TAKEN

if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined

my $TempUser = RT::User->new($RT::SystemUser);

$TempUser->Load( $args{‘Name’} );

return ( 0, $self->loc(‘Name in use’) ) if ( $TempUser->Id );

my ($val, $message) = $self->ValidateEmailAddress( $args{‘EmailAddress’} );

return (0, $message) unless ( $val );

}

else {

$RT::Logger->warning( “$self couldn’t check for pre-existing users”);

}

Ken, I only took a quick skim of your overlay once I noticed you were
overriding RT::User::Create. The above commented out block is bound to
cause lots of problems in RT.

We cannot assume uniqueness of Name for global RT use. ktm - 20091104

#SANITY CHECK THE NAME AND ABORT IF IT’S TAKEN

if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined

my $TempUser = RT::User->new($RT::SystemUser);

$TempUser->Load( $args{‘Name’} );

return ( 0, $self->loc(‘Name in use’) ) if ( $TempUser->Id );

my ($val, $message) = $self->ValidateEmailAddress( $args{‘EmailAddress’} );

return (0, $message) unless ( $val );

}

else {

$RT::Logger->warning( “$self couldn’t check for pre-existing users”);

}

Ken, I only took a quick skim of your overlay once I noticed you were
overriding RT::User::Create. The above commented out block is bound to
cause lots of problems in RT.

I may be misremembering, but I ended up commenting this out because the
ability to change an Email address in the LDAP directory took place
immediately so RT ended up thinking that Email address could not be
created since it already existed. We are getting ready to upgrade to
3.8.12 from 3.8.5 and I will see if my recent changes to the
CanonicalizeEmailAddress() have addressed this problem.

Regards,
Ken