[rt-users] username same but realname and email is different for existing user compare to ldap attribute, caused create ticket failed

ktm at rice.edu ktm at rice.edu
Wed Jul 25 15:26:00 EDT 2012


On Wed, Jul 25, 2012 at 03:17:44PM -0400, Asif Iqbal wrote:
> On Wed, Jul 25, 2012 at 3:14 PM, Thomas Sibley <trs at bestpractical.com> wrote:
> > On 07/25/2012 11:27 AM, Asif Iqbal wrote:
> >> 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 :-) . 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 at 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------------------------
> 
> -- 
> 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?
> 



More information about the rt-users mailing list