3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
5 # (Except where explictly superceded by other copyright notices)
7 # This work is made available to you under the terms of Version 2 of
8 # the GNU General Public License. A copy of that license should have
9 # been provided with this software, but in any event can be snarfed
12 # This work is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # General Public License for more details.
17 # Unless otherwise specified, all modifications, corrections or
18 # extensions to this work which alter its source code become the
19 # property of Best Practical Solutions, LLC when submitted for
20 # inclusion in the work.
26 RT::User - RT User object
47 no warnings qw(redefine);
49 use vars qw(%_USERS_KEY_CACHE);
51 %_USERS_KEY_CACHE = ();
61 sub _ClassAccessible {
65 {read => 1, type => 'int(11)', default => ''},
67 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(120)', default => ''},
69 { write => 1, type => 'varchar(40)', default => ''},
71 {read => 1, write => 1, admin => 1, type => 'blob', default => ''},
73 {read => 1, write => 1, type => 'blob', default => ''},
75 {read => 1, write => 1, public => 1, type => 'varchar(120)', default => ''},
76 FreeformContactInfo =>
77 {read => 1, write => 1, type => 'blob', default => ''},
79 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(200)', default => ''},
81 {read => 1, write => 1, public => 1, type => 'varchar(120)', default => ''},
83 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
85 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
87 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
89 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
90 ExternalContactInfoId =>
91 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(100)', default => ''},
93 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(30)', default => ''},
95 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(100)', default => ''},
97 {read => 1, write => 1, public => 1, admin => 1,type => 'varchar(30)', default => ''},
99 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(16)', default => ''},
102 {read => 1, write => 1, public => 1, admin => 1, type => 'text', default => ''},
105 {read => 1, write => 1, type => 'varchar(30)', default => ''},
107 {read => 1, write => 1, type => 'varchar(30)', default => ''},
109 {read => 1, write => 1, type => 'varchar(30)', default => ''},
111 {read => 1, write => 1, type => 'varchar(30)', default => ''},
113 {read => 1, write => 1, type => 'varchar(200)', default => ''},
115 {read => 1, write => 1, type => 'varchar(200)', default => ''},
117 {read => 1, write => 1, type => 'varchar(100)', default => ''},
119 {read => 1, write => 1, type => 'varchar(100)', default => ''},
121 {read => 1, write => 1, type => 'varchar(16)', default => ''},
123 {read => 1, write => 1, type => 'varchar(50)', default => ''},
125 {read => 1, auto => 1, type => 'int(11)', default => ''},
127 {read => 1, auto => 1, type => 'datetime', default => ''},
129 {read => 1, auto => 1, type => 'int(11)', default => ''},
131 {read => 1, auto => 1, type => 'datetime', default => ''},
141 =head2 Create { PARAMHASH }
146 # Make sure we can create a user
148 my $u1 = RT::User->new($RT::SystemUser);
149 is(ref($u1), 'RT::User');
150 my ($id, $msg) = $u1->Create(Name => 'CreateTest1', EmailAddress => 'create-test-1@example.com');
151 ok ($id, "Creating user CreateTest1 - " . $msg );
153 # Make sure we can't create a second user with the same name
154 my $u2 = RT::User->new($RT::SystemUser);
155 ($id, $msg) = $u2->Create(Name => 'CreateTest1', EmailAddress => 'create-test-2@example.com');
159 # Make sure we can't create a second user with the same EmailAddress address
160 my $u3 = RT::User->new($RT::SystemUser);
161 ($id, $msg) = $u3->Create(Name => 'CreateTest2', EmailAddress => 'create-test-1@example.com');
164 # Make sure we can create a user with no EmailAddress address
165 my $u4 = RT::User->new($RT::SystemUser);
166 ($id, $msg) = $u4->Create(Name => 'CreateTest3');
169 # make sure we can create a second user with no EmailAddress address
170 my $u5 = RT::User->new($RT::SystemUser);
171 ($id, $msg) = $u5->Create(Name => 'CreateTest4');
174 # make sure we can create a user with a blank EmailAddress address
175 my $u6 = RT::User->new($RT::SystemUser);
176 ($id, $msg) = $u6->Create(Name => 'CreateTest6', EmailAddress => '');
178 # make sure we can create a second user with a blankEmailAddress address
179 my $u7 = RT::User->new($RT::SystemUser);
180 ($id, $msg) = $u7->Create(Name => 'CreateTest7', EmailAddress => '');
183 # Can we change the email address away from from "";
184 ($id,$msg) = $u7->SetEmailAddress('foo@bar');
186 # can we change the address back to "";
187 ($id,$msg) = $u7->SetEmailAddress('');
189 is ($u7->EmailAddress, '');
203 @_ # get the real argumentlist
207 $args{'EmailAddress'} = $self->CanonicalizeEmailAddress($args{'EmailAddress'});
210 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
211 return ( 0, $self->loc('No permission to create users') );
215 # Privileged is no longer a column in users
216 my $privileged = $args{'Privileged'};
217 delete $args{'Privileged'};
220 if ($args{'CryptedPassword'} ) {
221 $args{'Password'} = $args{'CryptedPassword'};
222 delete $args{'CryptedPassword'};
224 elsif ( !$args{'Password'} ) {
225 $args{'Password'} = '*NO-PASSWORD*';
227 elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
228 return ( 0, $self->loc("Password too short") );
232 $args{'Password'} = $self->_GeneratePassword($args{'Password'});
235 #TODO Specify some sensible defaults.
237 unless ( defined( $args{'Name'} ) ) {
238 return ( 0, $self->loc("Must specify 'Name' attribute") );
241 #SANITY CHECK THE NAME AND ABORT IF IT'S TAKEN
242 if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined
243 my $TempUser = RT::User->new($RT::SystemUser);
244 $TempUser->Load( $args{'Name'} );
245 return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
247 return ( 0, $self->loc('Email address in use') )
248 unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
251 $RT::Logger->warning( "$self couldn't check for pre-existing users");
255 $RT::Handle->BeginTransaction();
256 # Groups deal with principal ids, rather than user ids.
257 # When creating this user, set up a principal Id for it.
258 my $principal = RT::Principal->new($self->CurrentUser);
259 my $principal_id = $principal->Create(PrincipalType => 'User',
260 Disabled => $args{'Disabled'},
262 $principal->__Set(Field => 'ObjectId', Value => $principal_id);
263 # If we couldn't create a principal Id, get the fuck out.
264 unless ($principal_id) {
265 $RT::Handle->Rollback();
266 $RT::Logger->crit("Couldn't create a Principal on new user create. Strange things are afoot at the circle K");
267 return ( 0, $self->loc('Could not create user') );
270 delete $args{'Disabled'};
272 $self->SUPER::Create(id => $principal_id , %args);
275 #If the create failed.
277 $RT::Logger->error("Could not create a new user - " .join('-'. %args));
279 return ( 0, $self->loc('Could not create user') );
284 #if ($args{'SendWelcomeMessage'}) {
285 # #TODO: Check if the email exists and looks valid
286 # #TODO: Send the user a "welcome message"
291 my $aclstash = RT::Group->new($self->CurrentUser);
292 my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
295 $RT::Handle->Rollback();
296 $RT::Logger->crit("Couldn't stash the user in groumembers");
297 return ( 0, $self->loc('Could not create user') );
302 #$RT::Logger->debug("Adding the user as a member of everyone");
303 my $everyone = RT::Group->new($self->CurrentUser);
304 $everyone->LoadSystemInternalGroup('Everyone');
305 $everyone->AddMember($self->PrincipalId);
308 my $priv = RT::Group->new($self->CurrentUser);
309 #$RT::Logger->debug("Making ".$self->Id." a privileged user");
310 $priv->LoadSystemInternalGroup('Privileged');
311 $priv->AddMember($self->PrincipalId);
313 my $unpriv = RT::Group->new($self->CurrentUser);
314 #$RT::Logger->debug("Making ".$self->Id." an unprivileged user");
315 $unpriv->LoadSystemInternalGroup('Unprivileged');
316 $unpriv->AddMember($self->PrincipalId);
320 # $RT::Logger->debug("Finished creating the user");
321 return ( $id, $self->loc('User created') );
330 =head2 SetPrivileged BOOL
332 If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
333 Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
335 Returns a standard RT tuple of (val, msg);
340 ok(my $user = RT::User->new($RT::SystemUser));
341 ok($user->Load('root'), "Loaded user 'root'");
342 ok($user->Privileged, "User 'root' is privileged");
343 ok(my ($v,$m) = $user->SetPrivileged(0));
344 ok ($v ==1, "Set unprivileged suceeded ($m)");
345 ok(!$user->Privileged, "User 'root' is no longer privileged");
346 ok(my ($v2,$m2) = $user->SetPrivileged(1));
347 ok ($v2 ==1, "Set privileged suceeded ($m2");
348 ok($user->Privileged, "User 'root' is privileged again");
358 my $priv = RT::Group->new($self->CurrentUser);
359 $priv->LoadSystemInternalGroup('Privileged');
362 $RT::Logger->crit("Could not find Privileged pseudogroup");
363 return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
366 my $unpriv = RT::Group->new($self->CurrentUser);
367 $unpriv->LoadSystemInternalGroup('Unprivileged');
368 unless ($unpriv->Id) {
369 $RT::Logger->crit("Could not find unprivileged pseudogroup");
370 return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
374 if ($priv->HasMember($self->PrincipalObj)) {
375 #$RT::Logger->debug("That user is already privileged");
376 return (0,$self->loc("That user is already privileged"));
378 if ($unpriv->HasMember($self->PrincipalObj)) {
379 $unpriv->DeleteMember($self->PrincipalId);
381 # if we had layered transactions, life would be good
382 # sadly, we have to just go ahead, even if something
384 $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
385 "unprivileged. something is drastically wrong.");
387 my ($status, $msg) = $priv->AddMember($self->PrincipalId);
389 return (1, $self->loc("That user is now privileged"));
395 if ($unpriv->HasMember($self->PrincipalObj)) {
396 #$RT::Logger->debug("That user is already unprivileged");
397 return (0,$self->loc("That user is already unprivileged"));
399 if ($priv->HasMember($self->PrincipalObj)) {
400 $priv->DeleteMember($self->PrincipalId);
402 # if we had layered transactions, life would be good
403 # sadly, we have to just go ahead, even if something
405 $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
406 "unprivileged. something is drastically wrong.");
408 my ($status, $msg) = $unpriv->AddMember($self->PrincipalId);
410 return (1, $self->loc("That user is now unprivileged"));
423 Returns true if this user is privileged. Returns undef otherwise.
429 my $priv = RT::Group->new($self->CurrentUser);
430 $priv->LoadSystemInternalGroup('Privileged');
431 if ($priv->HasMember($self->PrincipalObj)) {
441 # {{{ sub _BootstrapCreate
443 #create a user without validating _any_ data.
445 #To be used only on database init.
446 # We can't localize here because it's before we _have_ a loc framework
448 sub _BootstrapCreate {
452 $args{'Password'} = '*NO-PASSWORD*';
455 $RT::Handle->BeginTransaction();
457 # Groups deal with principal ids, rather than user ids.
458 # When creating this user, set up a principal Id for it.
459 my $principal = RT::Principal->new($self->CurrentUser);
460 my $principal_id = $principal->Create(PrincipalType => 'User', ObjectId => '0');
461 $principal->__Set(Field => 'ObjectId', Value => $principal_id);
463 # If we couldn't create a principal Id, get the fuck out.
464 unless ($principal_id) {
465 $RT::Handle->Rollback();
466 $RT::Logger->crit("Couldn't create a Principal on new user create. Strange things are afoot at the circle K");
467 return ( 0, 'Could not create user' );
469 $self->SUPER::Create(id => $principal_id, %args);
471 #If the create failed.
473 $RT::Handle->Rollback();
474 return ( 0, 'Could not create user' ) ; #never loc this
477 my $aclstash = RT::Group->new($self->CurrentUser);
478 my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
481 $RT::Handle->Rollback();
482 $RT::Logger->crit("Couldn't stash the user in groupmembers");
483 return ( 0, $self->loc('Could not create user') );
487 $RT::Handle->Commit();
489 return ( $id, 'User created' );
499 return ( 0, $self->loc('Deleting this object would violate referential integrity') );
509 Load a user object from the database. Takes a single argument.
510 If the argument is numerical, load by the column 'id'. Otherwise, load by
511 the "Name" column which is the user's textual username.
517 my $identifier = shift || return undef;
519 #if it's an int, load by id. otherwise, load by name.
520 if ( $identifier !~ /\D/ ) {
521 $self->SUPER::LoadById($identifier);
524 $self->LoadByCol( "Name", $identifier );
530 # {{{ sub LoadByEmail
534 Tries to load this user object from the database by the user's email address.
543 # Never load an empty address as an email address.
548 $address = $self->CanonicalizeEmailAddress($address);
550 #$RT::Logger->debug("Trying to load an email address: $address\n");
551 return $self->LoadByCol( "EmailAddress", $address );
556 # {{{ LoadOrCreateByEmail
558 =head2 LoadOrCreateByEmail ADDRESS
560 Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
561 the provided email address. and loads them.
563 Returns a tuple of the user's id and a status message.
564 0 will be returned in place of the user's id in case of failure.
568 sub LoadOrCreateByEmail {
574 $self->LoadByEmail($email);
575 $message = $self->loc('User loaded');
577 ( $val, $message ) = $self->Create(
579 EmailAddress => $email,
582 Comments => 'Autocreated when added as a watcher');
584 # Deal with the race condition of two account creations at once
585 $self->LoadByEmail($email);
588 $self->LoadByEmail($email);
591 $RT::Logger->error("Recovered from creation failure due to race condition");
592 $message = $self->loc("User loaded");
595 $RT::Logger->crit("Failed to create user ".$email .": " .$message);
601 return($self->Id, $message);
612 # {{{ sub ValidateEmailAddress
614 =head2 ValidateEmailAddress ADDRESS
616 Returns true if the email address entered is not in use by another user or is
617 undef or ''. Returns false if it's in use.
621 sub ValidateEmailAddress {
625 # if the email address is null, it's always valid
626 return (1) if ( !$Value || $Value eq "" );
628 my $TempUser = RT::User->new($RT::SystemUser);
629 $TempUser->LoadByEmail($Value);
631 if ( $TempUser->id && ( $TempUser->id != $self->id ) )
632 { # if we found a user with that address
633 # it's invalid to set this user's address to it
636 else { #it's a valid email address
643 # {{{ sub CanonicalizeEmailAddress
647 =item CanonicalizeEmailAddress ADDRESS
649 # CanonicalizeEmailAddress converts email addresses into canonical form.
650 # it takes one email address in and returns the proper canonical
651 # form. You can dump whatever your proper local config is in here
655 sub CanonicalizeEmailAddress {
658 # Example: the following rule would treat all email
659 # coming from a subdomain as coming from second level domain
661 if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
662 $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
671 # {{{ Password related functions
673 # {{{ sub SetRandomPassword
675 =head2 SetRandomPassword
677 Takes no arguments. Returns a status code and a new password or an error message.
678 If the status is 1, the second value returned is the new password.
679 If the status is anything else, the new value returned is the error code.
683 sub SetRandomPassword {
686 unless ( $self->CurrentUserCanModify('Password') ) {
687 return ( 0, $self->loc("Permission Denied") );
690 my $pass = $self->GenerateRandomPassword( 6, 8 );
692 # If we have "notify user on
694 my ( $val, $msg ) = $self->SetPassword($pass);
696 #If we got an error return the error.
697 return ( 0, $msg ) unless ($val);
699 #Otherwise, we changed the password, lets return it.
706 # {{{ sub ResetPassword
710 Returns status, [ERROR or new password]. Resets this user\'s password to
711 a randomly generated pronouncable password and emails them, using a
712 global template called "RT_PasswordChange", which can be overridden
713 with global templates "RT_PasswordChange_Privileged" or "RT_PasswordChange_NonPrivileged"
714 for privileged and Non-privileged users respectively.
721 unless ( $self->CurrentUserCanModify('Password') ) {
722 return ( 0, $self->loc("Permission Denied") );
724 my ( $status, $pass ) = $self->SetRandomPassword();
727 return ( 0, "$pass" );
730 my $template = RT::Template->new( $self->CurrentUser );
732 if ( $self->IsPrivileged ) {
733 $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
736 $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
739 unless ( $template->Id ) {
740 $template->LoadGlobalTemplate('RT_PasswordChange');
743 unless ( $template->Id ) {
744 $RT::Logger->crit( "$self tried to send "
746 . " a password reminder "
747 . "but couldn't find a password change template" );
750 my $notification = RT::Action::SendPasswordEmail->new(
751 TemplateObj => $template,
755 $notification->SetTo( $self->EmailAddress );
758 $ret = $notification->Prepare();
760 $ret = $notification->Commit();
764 return ( 1, $self->loc('New password notification sent') );
767 return ( 0, $self->loc('Notification could not be sent') );
774 # {{{ sub GenerateRandomPassword
776 =head2 GenerateRandomPassword MIN_LEN and MAX_LEN
778 Returns a random password between MIN_LEN and MAX_LEN characters long.
782 sub GenerateRandomPassword {
784 my $min_length = shift;
785 my $max_length = shift;
787 #This code derived from mpw.pl, a bit of code with a sordid history
790 # Perl cleaned up a bit by Jesse Vincent 1/14/2001.
791 # Converted to perl from C by Marc Horowitz, 1/20/2000.
792 # Converted to C from Multics PL/I by Bill Sommerfeld, 4/21/86.
793 # Original PL/I version provided by Jerry Saltzer.
795 my ( $frequency, $start_freq, $total_sum, $row_sums );
797 #When munging characters, we need to know where to start counting letters from
800 # frequency of English digraphs (from D Edwards 1/27/66)
803 4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62, 23, 167,
804 2, 14, 0, 83, 76, 127, 7, 25, 8, 1, 9, 1
807 13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0,
808 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0
811 32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1, 0,
812 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0
815 40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15, 6,
816 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0
819 84, 20, 55, 125, 51, 40, 19, 16, 50, 1,
820 4, 55, 54, 146, 35, 37, 6, 191, 149, 65,
824 19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1, 0,
825 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0
828 20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1, 4,
829 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0
832 101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3, 2,
833 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0
836 40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38, 25, 202,
837 56, 12, 1, 46, 79, 117, 1, 22, 0, 4, 0, 3
840 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0,
841 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0
844 1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2,
845 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0
848 44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2, 2,
849 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0
852 52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7, 1,
853 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0
856 42, 10, 47, 122, 63, 19, 106, 12, 30, 1,
857 6, 6, 9, 7, 54, 7, 1, 7, 44, 124,
861 7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19, 41, 134,
862 13, 23, 0, 91, 23, 42, 55, 16, 28, 0, 4, 1
865 19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0,
866 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0
869 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
870 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0
873 83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5, 26, 16,
874 60, 4, 0, 24, 37, 55, 6, 11, 4, 0, 28, 0
877 65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7, 11, 12,
878 56, 17, 6, 9, 48, 116, 35, 1, 28, 0, 4, 0
881 57, 22, 3, 1, 76, 5, 2, 330, 126, 1,
882 0, 14, 10, 6, 79, 7, 0, 49, 50, 56,
886 11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31,
887 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0
890 7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0,
891 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0
894 36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1, 8,
895 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0
898 1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0,
899 1, 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0
902 14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7, 5,
903 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0
906 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
907 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
911 #We need to know the totals for each row
915 map { $sum += $_ } @$_;
920 #Frequency with which a given letter starts a word.
922 1299, 425, 725, 271, 375, 470, 93, 223, 1009, 24,
923 20, 355, 379, 319, 823, 618, 21, 317, 962, 1991,
924 271, 104, 516, 6, 16, 14
928 map { $total_sum += $_ } @$start_freq;
930 my $length = $min_length + int( rand( $max_length - $min_length ) );
932 my $char = $self->GenerateRandomNextChar( $total_sum, $start_freq );
933 my @word = ( $char + $a );
934 for ( 2 .. $length ) {
936 $self->_GenerateRandomNextChar( $row_sums->[$char],
937 $frequency->[$char] );
938 push ( @word, $char + $a );
942 return pack( "C*", @word );
946 #A private helper function for RandomPassword
947 # Takes a row summary and a frequency chart for the next character to be searched
948 sub _GenerateRandomNextChar {
950 my ( $all, $freq ) = @_;
953 for ( $pos = int( rand($all) ), $i = 0 ;
954 $pos >= $freq->[$i] ;
955 $pos -= $freq->[$i], $i++ )
964 # {{{ sub SetPassword
968 Takes a string. Checks the string's length and sets this user's password
975 my $password = shift;
977 unless ( $self->CurrentUserCanModify('Password') ) {
978 return ( 0, $self->loc('Permission Denied') );
982 return ( 0, $self->loc("No password set") );
984 elsif ( length($password) < $RT::MinimumPasswordLength ) {
985 return ( 0, $self->loc("Password too short") );
988 $password = $self->_GeneratePassword($password);
989 return ( $self->SUPER::SetPassword( $password));
994 =head2 _GeneratePassword PASSWORD
996 returns an MD5 hash of the password passed in, in base64 encoding.
1000 sub _GeneratePassword {
1002 my $password = shift;
1004 my $md5 = Digest::MD5->new();
1005 $md5->add($password);
1006 return ($md5->b64digest);
1012 # {{{ sub IsPassword
1016 Returns true if the passed in value is this user's password.
1017 Returns undef otherwise.
1025 #TODO there isn't any apparent way to legitimately ACL this
1027 # RT does not allow null passwords
1028 if ( ( !defined($value) ) or ( $value eq '' ) ) {
1032 if ( $self->PrincipalObj->Disabled ) {
1034 "Disabled user " . $self->Name . " tried to log in" );
1038 if ( ($self->__Value('Password') eq '') ||
1039 ($self->__Value('Password') eq undef) ) {
1043 # generate an md5 password
1044 if ($self->_GeneratePassword($value) eq $self->__Value('Password')) {
1048 # if it's a historical password we say ok.
1050 if ( $self->__Value('Password') eq crypt( $value, $self->__Value('Password') ) ) {
1054 # no password check has succeeded. get out
1063 # {{{ sub SetDisabled
1065 =head2 Sub SetDisabled
1067 Toggles the user's disabled flag.
1069 set, all password checks for this user will fail. All ACL checks for this
1070 user will fail. The user will appear in no user listings.
1078 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1079 return (0, $self->loc('Permission Denied'));
1081 return $self->PrincipalObj->SetDisabled(@_);
1086 return $self->PrincipalObj->Disabled(@_);
1090 # {{{ Principal related routines
1094 Returns the principal object for this user. returns an empty RT::Principal
1095 if there's no principal object matching this user.
1096 The response is cached. PrincipalObj should never ever change.
1100 ok(my $u = RT::User->new($RT::SystemUser));
1101 ok($u->Load(1), "Loaded the first user");
1102 ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
1103 ok($u->PrincipalObj->PrincipalType eq 'User' , "Principal 1 is a user, not a group");
1112 unless ($self->{'PrincipalObj'} &&
1113 ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
1114 ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
1116 $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
1117 $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
1118 'PrincipalType' => 'User') ;
1120 return($self->{'PrincipalObj'});
1126 Returns this user's PrincipalId
1139 # {{{ sub HasGroupRight
1141 =head2 HasGroupRight
1143 Takes a paramhash which can contain
1145 GroupObj => RT::Group or Group => integer
1149 Returns 1 if this user has the right specified in the paramhash for the Group
1152 Returns undef if they don't.
1166 if ( defined $args{'Group'} ) {
1167 $args{'GroupObj'} = RT::Group->new( $self->CurrentUser );
1168 $args{'GroupObj'}->Load( $args{'Group'} );
1171 # {{{ Validate and load up the GroupId
1172 unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
1179 # Figure out whether a user has the right we're asking about.
1180 my $retval = $self->HasRight(
1181 Object => $args{'GroupObj'},
1182 Right => $args{'Right'},
1192 # {{{ sub Rights testing
1194 =head2 Rights testing
1199 my $root = RT::User->new($RT::SystemUser);
1200 $root->Load('root');
1201 ok($root->Id, "Found the root user");
1202 my $rootq = RT::Queue->new($root);
1204 ok($rootq->Id, "Loaded the first queue");
1206 ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
1208 my $new_user = RT::User->new($RT::SystemUser);
1209 my ($id, $msg) = $new_user->Create(Name => 'ACLTest');
1211 ok ($id, "Created a new user for acl test $msg");
1213 my $q = RT::Queue->new($new_user);
1215 ok($q->Id, "Loaded the first queue");
1218 ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
1219 ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
1220 ok ($gval, "Grant succeeded - $gmsg");
1223 ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
1224 ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
1225 ok ($gval, "Revocation succeeded - $gmsg");
1226 ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
1232 # Create a ticket in the queue
1233 my $new_tick = RT::Ticket->new($RT::SystemUser);
1234 my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
1235 ok($tickid, "Created ticket: $tickid");
1236 # Make sure the user doesn't have the right to modify tickets in the queue
1237 ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1238 # Create a new group
1239 my $group = RT::Group->new($RT::SystemUser);
1240 $group->CreateUserDefinedGroup(Name => 'ACLTest');
1241 ok($group->Id, "Created a new group Ok");
1242 # Grant a group the right to modify tickets in a queue
1243 ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
1244 ok($gv,"Grant succeeed - $gm");
1245 # Add the user to the group
1246 ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
1247 ok ($aid, "Member added to group: $amsg");
1248 # Make sure the user does have the right to modify tickets in the queue
1249 ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
1252 # Remove the user from the group
1253 ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
1254 ok ($did,"Deleted the group member: $dmsg");
1255 # Make sure the user doesn't have the right to modify tickets in the queue
1256 ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1259 my $q_as_system = RT::Queue->new($RT::SystemUser);
1260 $q_as_system->Load(1);
1261 ok($q_as_system->Id, "Loaded the first queue");
1263 # Create a ticket in the queue
1264 my $new_tick2 = RT::Ticket->new($RT::SystemUser);
1265 my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
1266 ok($tick2id, "Created ticket: $tick2id");
1267 ok($new_tick2->QueueObj->id eq $q_as_system->Id, "Created a new ticket in queue 1");
1270 # make sure that the user can't do this without subgroup membership
1271 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1274 my $subgroup = RT::Group->new($RT::SystemUser);
1275 $subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest');
1276 ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
1277 #Add the subgroup as a subgroup of the group
1278 my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
1279 ok ($said, "Added the subgroup as a member of the group");
1280 # Add the user to a subgroup of the group
1282 my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
1283 ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
1284 # Make sure the user does have the right to modify tickets in the queue
1285 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
1287 # {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
1290 ($id, $msg) = $group->SetDisabled(1);
1292 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
1293 ($id, $msg) = $group->SetDisabled(0);
1295 # Test what happens when we disable the group the user is a member of directly
1297 ($id, $msg) = $subgroup->SetDisabled(1);
1299 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
1300 ($id, $msg) = $subgroup->SetDisabled(0);
1302 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
1307 my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
1308 ok($usrid,"removed the user from the group - $usrmsg");
1309 # Make sure the user doesn't have the right to modify tickets in the queue
1310 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1312 #revoke the right to modify tickets in a queue
1313 ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
1314 ok($gv,"revoke succeeed - $gm");
1316 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
1318 # Grant queue admin cc the right to modify ticket in the queue
1319 ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $q_as_system, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
1320 ok($qv, "Granted the right successfully - $qm");
1322 # Add the user as a queue admincc
1323 ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1324 ok ($add_id, "the user is now a queue admincc - $add_msg");
1326 # Make sure the user does have the right to modify tickets in the queue
1327 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1328 # Remove the user from the role group
1329 ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1331 # Make sure the user doesn't have the right to modify tickets in the queue
1332 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1336 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1338 # Add the user as a ticket admincc
1339 ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1340 ok ($add_id, "the user is now a queue admincc - $add_msg");
1342 # Make sure the user does have the right to modify tickets in the queue
1343 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1345 # Remove the user from the role group
1346 ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1348 # Make sure the user doesn't have the right to modify tickets in the queue
1349 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1352 # Revoke the right to modify ticket in the queue
1353 ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $q_as_system, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
1354 ok($rqv, "Revoked the right successfully - $rqm");
1360 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
1362 # Before we start Make sure the user does not have the right to modify tickets in the queue
1363 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
1364 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
1366 # Grant queue admin cc the right to modify ticket in the queue
1367 ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
1368 ok($qv, "Granted the right successfully - $qm");
1370 # Make sure the user can't modify the ticket before they're added as a watcher
1371 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
1372 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
1374 # Add the user as a queue admincc
1375 ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1376 ok ($add_id, "the user is now a queue admincc - $add_msg");
1378 # Make sure the user does have the right to modify tickets in the queue
1379 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1380 ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
1381 # Remove the user from the role group
1382 ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1384 # Make sure the user doesn't have the right to modify tickets in the queue
1385 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1386 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
1390 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1392 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
1393 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
1396 # Add the user as a ticket admincc
1397 ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1398 ok ($add_id, "the user is now a queue admincc - $add_msg");
1400 # Make sure the user does have the right to modify tickets in the queue
1401 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1402 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
1404 # Remove the user from the role group
1405 ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1407 # Make sure the user doesn't have the right to modify tickets in the queue
1408 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
1409 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
1412 # Revoke the right to modify ticket in the queue
1413 ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
1414 ok($rqv, "Revoked the right successfully - $rqm");
1421 # Grant "privileged users" the system right to create users
1422 # Create a privileged user.
1423 # have that user create another user
1424 # Revoke the right for privileged users to create users
1425 # have the privileged user try to create another user and fail the ACL check
1438 Shim around PrincipalObj->HasRight. See RT::Principal
1445 return $self->PrincipalObj->HasRight(@_);
1450 # {{{ sub CurrentUserCanModify
1452 =head2 CurrentUserCanModify RIGHT
1454 If the user has rights for this object, either because
1455 he has 'AdminUsers' or (if he\'s trying to edit himself and the right isn\'t an
1456 admin right) 'ModifySelf', return 1. otherwise, return undef.
1460 sub CurrentUserCanModify {
1464 if ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1468 #If the field is marked as an "administrators only" field,
1469 # don\'t let the user touch it.
1470 elsif ( $self->_Accessible( $right, 'admin' ) ) {
1474 #If the current user is trying to modify themselves
1475 elsif ( ( $self->id == $self->CurrentUser->id )
1476 and ( $self->CurrentUser->HasRight(Right => 'ModifySelf', Object => $RT::System) ) )
1481 #If we don\'t have a good reason to grant them rights to modify
1491 # {{{ sub CurrentUserHasRight
1493 =head2 CurrentUserHasRight
1495 Takes a single argument. returns 1 if $Self->CurrentUser
1496 has the requested right. returns undef otherwise
1500 sub CurrentUserHasRight {
1504 return ( $self->CurrentUser->HasRight(Right => $right, Object => $RT::System) );
1520 # Nobody is allowed to futz with RT_System or Nobody
1522 if ( ($self->Id == $RT::SystemUser->Id ) ||
1523 ($self->Id == $RT::Nobody->Id)) {
1524 return ( 0, $self->loc("Can not modify system users") );
1526 unless ( $self->CurrentUserCanModify( $args{'Field'} ) ) {
1527 return ( 0, $self->loc("Permission Denied") );
1531 my ( $ret, $msg ) = $self->SUPER::_Set(
1532 Field => $args{'Field'},
1533 Value => $args{'Value'}
1536 return ( $ret, $msg );
1545 Takes the name of a table column.
1546 Returns its value as a string, if the user passes an ACL check
1555 #If the current user doesn't have ACLs, don't let em at it.
1557 my @PublicFields = qw( Name EmailAddress Organization Disabled
1558 RealName NickName Gecos ExternalAuthId
1559 AuthSystem ExternalContactInfoId
1560 ContactInfoSystem );
1562 #if the field is public, return it.
1563 if ( $self->_Accessible( $field, 'public' ) ) {
1564 return ( $self->SUPER::_Value($field) );
1568 #If the user wants to see their own values, let them
1569 # TODO figure ouyt a better way to deal with this
1570 elsif ( $self->CurrentUser->Id == $self->Id ) {
1571 return ( $self->SUPER::_Value($field) );
1574 #If the user has the admin users right, return the field
1575 elsif ( $self->CurrentUser->HasRight(Right =>'AdminUsers', Object => $RT::System) ) {
1576 return ( $self->SUPER::_Value($field) );