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 = ();
62 sub _ClassAccessible {
66 {read => 1, type => 'int(11)', default => ''},
68 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(120)', default => ''},
70 { write => 1, type => 'varchar(40)', default => ''},
72 {read => 1, write => 1, admin => 1, type => 'blob', default => ''},
74 {read => 1, write => 1, type => 'blob', default => ''},
76 {read => 1, write => 1, public => 1, type => 'varchar(120)', default => ''},
77 FreeformContactInfo =>
78 {read => 1, write => 1, type => 'blob', default => ''},
80 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(200)', default => ''},
82 {read => 1, write => 1, public => 1, type => 'varchar(120)', default => ''},
84 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
86 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
88 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
90 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
91 ExternalContactInfoId =>
92 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(100)', default => ''},
94 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(30)', default => ''},
96 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(100)', default => ''},
98 {read => 1, write => 1, public => 1, admin => 1,type => 'varchar(30)', default => ''},
100 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(16)', default => ''},
103 {read => 1, write => 1, public => 1, admin => 1, type => 'text', default => ''},
106 {read => 1, write => 1, type => 'varchar(30)', default => ''},
108 {read => 1, write => 1, type => 'varchar(30)', default => ''},
110 {read => 1, write => 1, type => 'varchar(30)', default => ''},
112 {read => 1, write => 1, type => 'varchar(30)', default => ''},
114 {read => 1, write => 1, type => 'varchar(200)', default => ''},
116 {read => 1, write => 1, type => 'varchar(200)', default => ''},
118 {read => 1, write => 1, type => 'varchar(100)', default => ''},
120 {read => 1, write => 1, type => 'varchar(100)', default => ''},
122 {read => 1, write => 1, type => 'varchar(16)', default => ''},
124 {read => 1, write => 1, type => 'varchar(50)', default => ''},
126 {read => 1, auto => 1, type => 'int(11)', default => ''},
128 {read => 1, auto => 1, type => 'datetime', default => ''},
130 {read => 1, auto => 1, type => 'int(11)', default => ''},
132 {read => 1, auto => 1, type => 'datetime', default => ''},
142 =head2 Create { PARAMHASH }
147 # Make sure we can create a user
149 my $u1 = RT::User->new($RT::SystemUser);
150 is(ref($u1), 'RT::User');
151 my ($id, $msg) = $u1->Create(Name => 'CreateTest1', EmailAddress => 'create-test-1@example.com');
152 ok ($id, "Creating user CreateTest1 - " . $msg );
154 # Make sure we can't create a second user with the same name
155 my $u2 = RT::User->new($RT::SystemUser);
156 ($id, $msg) = $u2->Create(Name => 'CreateTest1', EmailAddress => 'create-test-2@example.com');
160 # Make sure we can't create a second user with the same EmailAddress address
161 my $u3 = RT::User->new($RT::SystemUser);
162 ($id, $msg) = $u3->Create(Name => 'CreateTest2', EmailAddress => 'create-test-1@example.com');
165 # Make sure we can create a user with no EmailAddress address
166 my $u4 = RT::User->new($RT::SystemUser);
167 ($id, $msg) = $u4->Create(Name => 'CreateTest3');
170 # make sure we can create a second user with no EmailAddress address
171 my $u5 = RT::User->new($RT::SystemUser);
172 ($id, $msg) = $u5->Create(Name => 'CreateTest4');
175 # make sure we can create a user with a blank EmailAddress address
176 my $u6 = RT::User->new($RT::SystemUser);
177 ($id, $msg) = $u6->Create(Name => 'CreateTest6', EmailAddress => '');
179 # make sure we can create a second user with a blankEmailAddress address
180 my $u7 = RT::User->new($RT::SystemUser);
181 ($id, $msg) = $u7->Create(Name => 'CreateTest7', EmailAddress => '');
184 # Can we change the email address away from from "";
185 ($id,$msg) = $u7->SetEmailAddress('foo@bar');
187 # can we change the address back to "";
188 ($id,$msg) = $u7->SetEmailAddress('');
190 is ($u7->EmailAddress, '');
204 @_ # get the real argumentlist
208 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
209 return ( 0, $self->loc('No permission to create users') );
212 $args{'EmailAddress'} = $self->CanonicalizeEmailAddress($args{'EmailAddress'});
213 # if the user doesn't have a name defined, set it to the email address
214 $args{'Name'} = $args{'EmailAddress'} unless ($args{'Name'});
218 # Privileged is no longer a column in users
219 my $privileged = $args{'Privileged'};
220 delete $args{'Privileged'};
223 if ($args{'CryptedPassword'} ) {
224 $args{'Password'} = $args{'CryptedPassword'};
225 delete $args{'CryptedPassword'};
227 elsif ( !$args{'Password'} ) {
228 $args{'Password'} = '*NO-PASSWORD*';
230 elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
231 return ( 0, $self->loc("Password too short") );
235 $args{'Password'} = $self->_GeneratePassword($args{'Password'});
238 #TODO Specify some sensible defaults.
240 unless ( $args{'Name'} ) {
242 $RT::Logger->crit(Dumper \%args);
243 return ( 0, $self->loc("Must specify 'Name' attribute") );
246 #SANITY CHECK THE NAME AND ABORT IF IT'S TAKEN
247 if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined
248 my $TempUser = RT::User->new($RT::SystemUser);
249 $TempUser->Load( $args{'Name'} );
250 return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
252 return ( 0, $self->loc('Email address in use') )
253 unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
256 $RT::Logger->warning( "$self couldn't check for pre-existing users");
260 $RT::Handle->BeginTransaction();
261 # Groups deal with principal ids, rather than user ids.
262 # When creating this user, set up a principal Id for it.
263 my $principal = RT::Principal->new($self->CurrentUser);
264 my $principal_id = $principal->Create(PrincipalType => 'User',
265 Disabled => $args{'Disabled'},
267 # If we couldn't create a principal Id, get the fuck out.
268 unless ($principal_id) {
269 $RT::Handle->Rollback();
270 $RT::Logger->crit("Couldn't create a Principal on new user create.");
271 $RT::Logger->crit("Strange things are afoot at the circle K");
272 return ( 0, $self->loc('Could not create user') );
275 $principal->__Set(Field => 'ObjectId', Value => $principal_id);
276 delete $args{'Disabled'};
278 $self->SUPER::Create(id => $principal_id , %args);
281 #If the create failed.
283 $RT::Handle->Rollback();
284 $RT::Logger->error("Could not create a new user - " .join('-'. %args));
286 return ( 0, $self->loc('Could not create user') );
289 my $aclstash = RT::Group->new($self->CurrentUser);
290 my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
293 $RT::Handle->Rollback();
294 $RT::Logger->crit("Couldn't stash the user in groupmembers");
295 return ( 0, $self->loc('Could not create user') );
299 my $everyone = RT::Group->new($self->CurrentUser);
300 $everyone->LoadSystemInternalGroup('Everyone');
301 unless ($everyone->id) {
302 $RT::Logger->crit("Could not load Everyone group on user creation.");
303 $RT::Handle->Rollback();
304 return ( 0, $self->loc('Could not create user') );
308 my ($everyone_id, $everyone_msg) = $everyone->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
309 unless ($everyone_id) {
310 $RT::Logger->crit("Could not add user to Everyone group on user creation.");
311 $RT::Logger->crit($everyone_msg);
312 $RT::Handle->Rollback();
313 return ( 0, $self->loc('Could not create user') );
317 my $access_class = RT::Group->new($self->CurrentUser);
319 $access_class->LoadSystemInternalGroup('Privileged');
321 $access_class->LoadSystemInternalGroup('Unprivileged');
324 unless ($access_class->id) {
325 $RT::Logger->crit("Could not load Privileged or Unprivileged group on user creation");
326 $RT::Handle->Rollback();
327 return ( 0, $self->loc('Could not create user') );
331 my ($ac_id, $ac_msg) = $access_class->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
334 $RT::Logger->crit("Could not add user to Privileged or Unprivileged group on user creation. Aborted");
335 $RT::Logger->crit($ac_msg);
336 $RT::Handle->Rollback();
337 return ( 0, $self->loc('Could not create user') );
342 return ( $id, $self->loc('User created') );
351 =head2 SetPrivileged BOOL
353 If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
354 Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
356 Returns a standard RT tuple of (val, msg);
361 ok(my $user = RT::User->new($RT::SystemUser));
362 ok($user->Load('root'), "Loaded user 'root'");
363 ok($user->Privileged, "User 'root' is privileged");
364 ok(my ($v,$m) = $user->SetPrivileged(0));
365 ok ($v ==1, "Set unprivileged suceeded ($m)");
366 ok(!$user->Privileged, "User 'root' is no longer privileged");
367 ok(my ($v2,$m2) = $user->SetPrivileged(1));
368 ok ($v2 ==1, "Set privileged suceeded ($m2");
369 ok($user->Privileged, "User 'root' is privileged again");
380 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
381 return ( 0, $self->loc('Permission Denied') );
383 my $priv = RT::Group->new($self->CurrentUser);
384 $priv->LoadSystemInternalGroup('Privileged');
387 $RT::Logger->crit("Could not find Privileged pseudogroup");
388 return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
391 my $unpriv = RT::Group->new($self->CurrentUser);
392 $unpriv->LoadSystemInternalGroup('Unprivileged');
393 unless ($unpriv->Id) {
394 $RT::Logger->crit("Could not find unprivileged pseudogroup");
395 return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
399 if ($priv->HasMember($self->PrincipalObj)) {
400 #$RT::Logger->debug("That user is already privileged");
401 return (0,$self->loc("That user is already privileged"));
403 if ($unpriv->HasMember($self->PrincipalObj)) {
404 $unpriv->_DeleteMember($self->PrincipalId);
406 # if we had layered transactions, life would be good
407 # sadly, we have to just go ahead, even if something
409 $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
410 "unprivileged. something is drastically wrong.");
412 my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
414 return (1, $self->loc("That user is now privileged"));
420 if ($unpriv->HasMember($self->PrincipalObj)) {
421 #$RT::Logger->debug("That user is already unprivileged");
422 return (0,$self->loc("That user is already unprivileged"));
424 if ($priv->HasMember($self->PrincipalObj)) {
425 $priv->_DeleteMember( $self->PrincipalId);
427 # if we had layered transactions, life would be good
428 # sadly, we have to just go ahead, even if something
430 $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
431 "unprivileged. something is drastically wrong.");
433 my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
435 return (1, $self->loc("That user is now unprivileged"));
448 Returns true if this user is privileged. Returns undef otherwise.
454 my $priv = RT::Group->new($self->CurrentUser);
455 $priv->LoadSystemInternalGroup('Privileged');
456 if ($priv->HasMember($self->PrincipalObj)) {
466 # {{{ sub _BootstrapCreate
468 #create a user without validating _any_ data.
470 #To be used only on database init.
471 # We can't localize here because it's before we _have_ a loc framework
473 sub _BootstrapCreate {
477 $args{'Password'} = '*NO-PASSWORD*';
480 $RT::Handle->BeginTransaction();
482 # Groups deal with principal ids, rather than user ids.
483 # When creating this user, set up a principal Id for it.
484 my $principal = RT::Principal->new($self->CurrentUser);
485 my $principal_id = $principal->Create(PrincipalType => 'User', ObjectId => '0');
486 $principal->__Set(Field => 'ObjectId', Value => $principal_id);
488 # If we couldn't create a principal Id, get the fuck out.
489 unless ($principal_id) {
490 $RT::Handle->Rollback();
491 $RT::Logger->crit("Couldn't create a Principal on new user create. Strange things are afoot at the circle K");
492 return ( 0, 'Could not create user' );
494 $self->SUPER::Create(id => $principal_id, %args);
496 #If the create failed.
498 $RT::Handle->Rollback();
499 return ( 0, 'Could not create user' ) ; #never loc this
502 my $aclstash = RT::Group->new($self->CurrentUser);
503 my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
506 $RT::Handle->Rollback();
507 $RT::Logger->crit("Couldn't stash the user in groupmembers");
508 return ( 0, $self->loc('Could not create user') );
512 $RT::Handle->Commit();
514 return ( $id, 'User created' );
524 return ( 0, $self->loc('Deleting this object would violate referential integrity') );
534 Load a user object from the database. Takes a single argument.
535 If the argument is numerical, load by the column 'id'. Otherwise, load by
536 the "Name" column which is the user's textual username.
542 my $identifier = shift || return undef;
544 #if it's an int, load by id. otherwise, load by name.
545 if ( $identifier !~ /\D/ ) {
546 $self->SUPER::LoadById($identifier);
549 $self->LoadByCol( "Name", $identifier );
555 # {{{ sub LoadByEmail
559 Tries to load this user object from the database by the user's email address.
568 # Never load an empty address as an email address.
573 $address = $self->CanonicalizeEmailAddress($address);
575 #$RT::Logger->debug("Trying to load an email address: $address\n");
576 return $self->LoadByCol( "EmailAddress", $address );
581 # {{{ LoadOrCreateByEmail
583 =head2 LoadOrCreateByEmail ADDRESS
585 Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
586 the provided email address. and loads them.
588 Returns a tuple of the user's id and a status message.
589 0 will be returned in place of the user's id in case of failure.
593 sub LoadOrCreateByEmail {
599 my ( $Address, $Name ) =
600 RT::EmailParser::ParseAddressFromHeader('', $email);
603 $self->LoadByEmail($email);
604 $message = $self->loc('User loaded');
606 ( $val, $message ) = $self->Create(
608 EmailAddress => $email,
611 Comments => 'Autocreated when added as a watcher');
613 # Deal with the race condition of two account creations at once
614 $self->LoadByEmail($email);
617 $self->LoadByEmail($email);
620 $RT::Logger->error("Recovered from creation failure due to race condition");
621 $message = $self->loc("User loaded");
624 $RT::Logger->crit("Failed to create user ".$email .": " .$message);
630 return($self->Id, $message);
641 # {{{ sub ValidateEmailAddress
643 =head2 ValidateEmailAddress ADDRESS
645 Returns true if the email address entered is not in use by another user or is
646 undef or ''. Returns false if it's in use.
650 sub ValidateEmailAddress {
654 # if the email address is null, it's always valid
655 return (1) if ( !$Value || $Value eq "" );
657 my $TempUser = RT::User->new($RT::SystemUser);
658 $TempUser->LoadByEmail($Value);
660 if ( $TempUser->id && ( $TempUser->id != $self->id ) )
661 { # if we found a user with that address
662 # it's invalid to set this user's address to it
665 else { #it's a valid email address
672 # {{{ sub CanonicalizeEmailAddress
676 =item CanonicalizeEmailAddress ADDRESS
678 # CanonicalizeEmailAddress converts email addresses into canonical form.
679 # it takes one email address in and returns the proper canonical
680 # form. You can dump whatever your proper local config is in here
684 sub CanonicalizeEmailAddress {
687 # Example: the following rule would treat all email
688 # coming from a subdomain as coming from second level domain
690 if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
691 $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
700 # {{{ Password related functions
702 # {{{ sub SetRandomPassword
704 =head2 SetRandomPassword
706 Takes no arguments. Returns a status code and a new password or an error message.
707 If the status is 1, the second value returned is the new password.
708 If the status is anything else, the new value returned is the error code.
712 sub SetRandomPassword {
715 unless ( $self->CurrentUserCanModify('Password') ) {
716 return ( 0, $self->loc("Permission Denied") );
719 my $pass = $self->GenerateRandomPassword( 6, 8 );
721 # If we have "notify user on
723 my ( $val, $msg ) = $self->SetPassword($pass);
725 #If we got an error return the error.
726 return ( 0, $msg ) unless ($val);
728 #Otherwise, we changed the password, lets return it.
735 # {{{ sub ResetPassword
739 Returns status, [ERROR or new password]. Resets this user\'s password to
740 a randomly generated pronouncable password and emails them, using a
741 global template called "RT_PasswordChange", which can be overridden
742 with global templates "RT_PasswordChange_Privileged" or "RT_PasswordChange_NonPrivileged"
743 for privileged and Non-privileged users respectively.
750 unless ( $self->CurrentUserCanModify('Password') ) {
751 return ( 0, $self->loc("Permission Denied") );
753 my ( $status, $pass ) = $self->SetRandomPassword();
756 return ( 0, "$pass" );
759 my $template = RT::Template->new( $self->CurrentUser );
761 if ( $self->Privileged ) {
762 $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
765 $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
768 unless ( $template->Id ) {
769 $template->LoadGlobalTemplate('RT_PasswordChange');
772 unless ( $template->Id ) {
773 $RT::Logger->crit( "$self tried to send "
775 . " a password reminder "
776 . "but couldn't find a password change template" );
779 my $notification = RT::Action::SendPasswordEmail->new(
780 TemplateObj => $template,
784 $notification->SetTo( $self->EmailAddress );
787 $ret = $notification->Prepare();
789 $ret = $notification->Commit();
793 return ( 1, $self->loc('New password notification sent') );
796 return ( 0, $self->loc('Notification could not be sent') );
803 # {{{ sub GenerateRandomPassword
805 =head2 GenerateRandomPassword MIN_LEN and MAX_LEN
807 Returns a random password between MIN_LEN and MAX_LEN characters long.
811 sub GenerateRandomPassword {
813 my $min_length = shift;
814 my $max_length = shift;
816 #This code derived from mpw.pl, a bit of code with a sordid history
819 # Perl cleaned up a bit by Jesse Vincent 1/14/2001.
820 # Converted to perl from C by Marc Horowitz, 1/20/2000.
821 # Converted to C from Multics PL/I by Bill Sommerfeld, 4/21/86.
822 # Original PL/I version provided by Jerry Saltzer.
824 my ( $frequency, $start_freq, $total_sum, $row_sums );
826 #When munging characters, we need to know where to start counting letters from
829 # frequency of English digraphs (from D Edwards 1/27/66)
832 4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62, 23, 167,
833 2, 14, 0, 83, 76, 127, 7, 25, 8, 1, 9, 1
836 13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0,
837 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0
840 32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1, 0,
841 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0
844 40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15, 6,
845 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0
848 84, 20, 55, 125, 51, 40, 19, 16, 50, 1,
849 4, 55, 54, 146, 35, 37, 6, 191, 149, 65,
853 19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1, 0,
854 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0
857 20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1, 4,
858 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0
861 101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3, 2,
862 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0
865 40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38, 25, 202,
866 56, 12, 1, 46, 79, 117, 1, 22, 0, 4, 0, 3
869 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0,
870 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0
873 1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2,
874 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0
877 44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2, 2,
878 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0
881 52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7, 1,
882 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0
885 42, 10, 47, 122, 63, 19, 106, 12, 30, 1,
886 6, 6, 9, 7, 54, 7, 1, 7, 44, 124,
890 7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19, 41, 134,
891 13, 23, 0, 91, 23, 42, 55, 16, 28, 0, 4, 1
894 19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0,
895 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0
898 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
899 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0
902 83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5, 26, 16,
903 60, 4, 0, 24, 37, 55, 6, 11, 4, 0, 28, 0
906 65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7, 11, 12,
907 56, 17, 6, 9, 48, 116, 35, 1, 28, 0, 4, 0
910 57, 22, 3, 1, 76, 5, 2, 330, 126, 1,
911 0, 14, 10, 6, 79, 7, 0, 49, 50, 56,
915 11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31,
916 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0
919 7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0,
920 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0
923 36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1, 8,
924 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0
927 1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0,
928 1, 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0
931 14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7, 5,
932 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0
935 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
936 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
940 #We need to know the totals for each row
944 map { $sum += $_ } @$_;
949 #Frequency with which a given letter starts a word.
951 1299, 425, 725, 271, 375, 470, 93, 223, 1009, 24,
952 20, 355, 379, 319, 823, 618, 21, 317, 962, 1991,
953 271, 104, 516, 6, 16, 14
957 map { $total_sum += $_ } @$start_freq;
959 my $length = $min_length + int( rand( $max_length - $min_length ) );
961 my $char = $self->_GenerateRandomNextChar( $total_sum, $start_freq );
962 my @word = ( $char + $a );
963 for ( 2 .. $length ) {
965 $self->_GenerateRandomNextChar( $row_sums->[$char],
966 $frequency->[$char] );
967 push ( @word, $char + $a );
971 return pack( "C*", @word );
975 #A private helper function for RandomPassword
976 # Takes a row summary and a frequency chart for the next character to be searched
977 sub _GenerateRandomNextChar {
979 my ( $all, $freq ) = @_;
982 for ( $pos = int( rand($all) ), $i = 0 ;
983 $pos >= $freq->[$i] ;
984 $pos -= $freq->[$i], $i++ )
993 # {{{ sub SetPassword
997 Takes a string. Checks the string's length and sets this user's password
1004 my $password = shift;
1006 unless ( $self->CurrentUserCanModify('Password') ) {
1007 return ( 0, $self->loc('Permission Denied') );
1011 return ( 0, $self->loc("No password set") );
1013 elsif ( length($password) < $RT::MinimumPasswordLength ) {
1014 return ( 0, $self->loc("Password too short") );
1017 $password = $self->_GeneratePassword($password);
1018 return ( $self->SUPER::SetPassword( $password));
1023 =head2 _GeneratePassword PASSWORD
1025 returns an MD5 hash of the password passed in, in base64 encoding.
1029 sub _GeneratePassword {
1031 my $password = shift;
1033 my $md5 = Digest::MD5->new();
1034 $md5->add($password);
1035 return ($md5->b64digest);
1041 # {{{ sub IsPassword
1045 Returns true if the passed in value is this user's password.
1046 Returns undef otherwise.
1054 #TODO there isn't any apparent way to legitimately ACL this
1056 # RT does not allow null passwords
1057 if ( ( !defined($value) ) or ( $value eq '' ) ) {
1061 if ( $self->PrincipalObj->Disabled ) {
1063 "Disabled user " . $self->Name . " tried to log in" );
1067 if ( ($self->__Value('Password') eq '') ||
1068 ($self->__Value('Password') eq undef) ) {
1072 # generate an md5 password
1073 if ($self->_GeneratePassword($value) eq $self->__Value('Password')) {
1077 # if it's a historical password we say ok.
1079 if ( $self->__Value('Password') eq crypt( $value, $self->__Value('Password') ) ) {
1083 # no password check has succeeded. get out
1092 # {{{ sub SetDisabled
1094 =head2 Sub SetDisabled
1096 Toggles the user's disabled flag.
1098 set, all password checks for this user will fail. All ACL checks for this
1099 user will fail. The user will appear in no user listings.
1107 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1108 return (0, $self->loc('Permission Denied'));
1110 return $self->PrincipalObj->SetDisabled(@_);
1115 return $self->PrincipalObj->Disabled(@_);
1119 # {{{ Principal related routines
1123 Returns the principal object for this user. returns an empty RT::Principal
1124 if there's no principal object matching this user.
1125 The response is cached. PrincipalObj should never ever change.
1129 ok(my $u = RT::User->new($RT::SystemUser));
1130 ok($u->Load(1), "Loaded the first user");
1131 ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
1132 ok($u->PrincipalObj->PrincipalType eq 'User' , "Principal 1 is a user, not a group");
1141 unless ($self->{'PrincipalObj'} &&
1142 ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
1143 ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
1145 $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
1146 $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
1147 'PrincipalType' => 'User') ;
1149 return($self->{'PrincipalObj'});
1155 Returns this user's PrincipalId
1168 # {{{ sub HasGroupRight
1170 =head2 HasGroupRight
1172 Takes a paramhash which can contain
1174 GroupObj => RT::Group or Group => integer
1178 Returns 1 if this user has the right specified in the paramhash for the Group
1181 Returns undef if they don't.
1195 if ( defined $args{'Group'} ) {
1196 $args{'GroupObj'} = RT::Group->new( $self->CurrentUser );
1197 $args{'GroupObj'}->Load( $args{'Group'} );
1200 # {{{ Validate and load up the GroupId
1201 unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
1208 # Figure out whether a user has the right we're asking about.
1209 my $retval = $self->HasRight(
1210 Object => $args{'GroupObj'},
1211 Right => $args{'Right'},
1221 # {{{ sub Rights testing
1223 =head2 Rights testing
1228 my $root = RT::User->new($RT::SystemUser);
1229 $root->Load('root');
1230 ok($root->Id, "Found the root user");
1231 my $rootq = RT::Queue->new($root);
1233 ok($rootq->Id, "Loaded the first queue");
1235 ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
1237 my $new_user = RT::User->new($RT::SystemUser);
1238 my ($id, $msg) = $new_user->Create(Name => 'ACLTest');
1240 ok ($id, "Created a new user for acl test $msg");
1242 my $q = RT::Queue->new($new_user);
1244 ok($q->Id, "Loaded the first queue");
1247 ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
1248 ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
1249 ok ($gval, "Grant succeeded - $gmsg");
1252 ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
1253 ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
1254 ok ($gval, "Revocation succeeded - $gmsg");
1255 ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
1261 # Create a ticket in the queue
1262 my $new_tick = RT::Ticket->new($RT::SystemUser);
1263 my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
1264 ok($tickid, "Created ticket: $tickid");
1265 # Make sure the user doesn't have the right to modify tickets in the queue
1266 ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1267 # Create a new group
1268 my $group = RT::Group->new($RT::SystemUser);
1269 $group->CreateUserDefinedGroup(Name => 'ACLTest');
1270 ok($group->Id, "Created a new group Ok");
1271 # Grant a group the right to modify tickets in a queue
1272 ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
1273 ok($gv,"Grant succeeed - $gm");
1274 # Add the user to the group
1275 ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
1276 ok ($aid, "Member added to group: $amsg");
1277 # Make sure the user does have the right to modify tickets in the queue
1278 ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
1281 # Remove the user from the group
1282 ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
1283 ok ($did,"Deleted the group member: $dmsg");
1284 # Make sure the user doesn't have the right to modify tickets in the queue
1285 ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1288 my $q_as_system = RT::Queue->new($RT::SystemUser);
1289 $q_as_system->Load(1);
1290 ok($q_as_system->Id, "Loaded the first queue");
1292 # Create a ticket in the queue
1293 my $new_tick2 = RT::Ticket->new($RT::SystemUser);
1294 my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
1295 ok($tick2id, "Created ticket: $tick2id");
1296 ok($new_tick2->QueueObj->id eq $q_as_system->Id, "Created a new ticket in queue 1");
1299 # make sure that the user can't do this without subgroup membership
1300 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1303 my $subgroup = RT::Group->new($RT::SystemUser);
1304 $subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest');
1305 ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
1306 #Add the subgroup as a subgroup of the group
1307 my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
1308 ok ($said, "Added the subgroup as a member of the group");
1309 # Add the user to a subgroup of the group
1311 my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
1312 ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
1313 # Make sure the user does have the right to modify tickets in the queue
1314 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
1316 # {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
1319 ($id, $msg) = $group->SetDisabled(1);
1321 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
1322 ($id, $msg) = $group->SetDisabled(0);
1324 # Test what happens when we disable the group the user is a member of directly
1326 ($id, $msg) = $subgroup->SetDisabled(1);
1328 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
1329 ($id, $msg) = $subgroup->SetDisabled(0);
1331 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
1336 my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
1337 ok($usrid,"removed the user from the group - $usrmsg");
1338 # Make sure the user doesn't have the right to modify tickets in the queue
1339 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1341 #revoke the right to modify tickets in a queue
1342 ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
1343 ok($gv,"revoke succeeed - $gm");
1345 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
1347 # Grant queue admin cc the right to modify ticket in the queue
1348 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");
1349 ok($qv, "Granted the right successfully - $qm");
1351 # Add the user as a queue admincc
1352 ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1353 ok ($add_id, "the user is now a queue admincc - $add_msg");
1355 # Make sure the user does have the right to modify tickets in the queue
1356 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1357 # Remove the user from the role group
1358 ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1360 # Make sure the user doesn't have the right to modify tickets in the queue
1361 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1365 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1367 # Add the user as a ticket admincc
1368 ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1369 ok ($add_id, "the user is now a queue admincc - $add_msg");
1371 # Make sure the user does have the right to modify tickets in the queue
1372 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1374 # Remove the user from the role group
1375 ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1377 # Make sure the user doesn't have the right to modify tickets in the queue
1378 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1381 # Revoke the right to modify ticket in the queue
1382 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");
1383 ok($rqv, "Revoked the right successfully - $rqm");
1389 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
1391 # Before we start Make sure the user does not have the right to modify tickets in the queue
1392 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
1393 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
1395 # Grant queue admin cc the right to modify ticket in the queue
1396 ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
1397 ok($qv, "Granted the right successfully - $qm");
1399 # Make sure the user can't modify the ticket before they're added as a watcher
1400 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
1401 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
1403 # Add the user as a queue admincc
1404 ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1405 ok ($add_id, "the user is now a queue admincc - $add_msg");
1407 # Make sure the user does have the right to modify tickets in the queue
1408 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1409 ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
1410 # Remove the user from the role group
1411 ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1413 # Make sure the user doesn't have the right to modify tickets in the queue
1414 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1415 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
1419 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1421 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
1422 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
1425 # Add the user as a ticket admincc
1426 ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1427 ok ($add_id, "the user is now a queue admincc - $add_msg");
1429 # Make sure the user does have the right to modify tickets in the queue
1430 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1431 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
1433 # Remove the user from the role group
1434 ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1436 # Make sure the user doesn't have the right to modify tickets in the queue
1437 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
1438 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
1441 # Revoke the right to modify ticket in the queue
1442 ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
1443 ok($rqv, "Revoked the right successfully - $rqm");
1450 # Grant "privileged users" the system right to create users
1451 # Create a privileged user.
1452 # have that user create another user
1453 # Revoke the right for privileged users to create users
1454 # have the privileged user try to create another user and fail the ACL check
1467 Shim around PrincipalObj->HasRight. See RT::Principal
1474 return $self->PrincipalObj->HasRight(@_);
1479 # {{{ sub CurrentUserCanModify
1481 =head2 CurrentUserCanModify RIGHT
1483 If the user has rights for this object, either because
1484 he has 'AdminUsers' or (if he\'s trying to edit himself and the right isn\'t an
1485 admin right) 'ModifySelf', return 1. otherwise, return undef.
1489 sub CurrentUserCanModify {
1493 if ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1497 #If the field is marked as an "administrators only" field,
1498 # don\'t let the user touch it.
1499 elsif ( $self->_Accessible( $right, 'admin' ) ) {
1503 #If the current user is trying to modify themselves
1504 elsif ( ( $self->id == $self->CurrentUser->id )
1505 and ( $self->CurrentUser->HasRight(Right => 'ModifySelf', Object => $RT::System) ) )
1510 #If we don\'t have a good reason to grant them rights to modify
1520 # {{{ sub CurrentUserHasRight
1522 =head2 CurrentUserHasRight
1524 Takes a single argument. returns 1 if $Self->CurrentUser
1525 has the requested right. returns undef otherwise
1529 sub CurrentUserHasRight {
1533 return ( $self->CurrentUser->HasRight(Right => $right, Object => $RT::System) );
1549 # Nobody is allowed to futz with RT_System or Nobody
1551 if ( ($self->Id == $RT::SystemUser->Id ) ||
1552 ($self->Id == $RT::Nobody->Id)) {
1553 return ( 0, $self->loc("Can not modify system users") );
1555 unless ( $self->CurrentUserCanModify( $args{'Field'} ) ) {
1556 return ( 0, $self->loc("Permission Denied") );
1560 my ( $ret, $msg ) = $self->SUPER::_Set(
1561 Field => $args{'Field'},
1562 Value => $args{'Value'}
1565 return ( $ret, $msg );
1574 Takes the name of a table column.
1575 Returns its value as a string, if the user passes an ACL check
1584 #If the current user doesn't have ACLs, don't let em at it.
1586 my @PublicFields = qw( Name EmailAddress Organization Disabled
1587 RealName NickName Gecos ExternalAuthId
1588 AuthSystem ExternalContactInfoId
1589 ContactInfoSystem );
1591 #if the field is public, return it.
1592 if ( $self->_Accessible( $field, 'public' ) ) {
1593 return ( $self->SUPER::_Value($field) );
1597 #If the user wants to see their own values, let them
1598 # TODO figure ouyt a better way to deal with this
1599 elsif ( $self->CurrentUser->Id == $self->Id ) {
1600 return ( $self->SUPER::_Value($field) );
1603 #If the user has the admin users right, return the field
1604 elsif ( $self->CurrentUser->HasRight(Right =>'AdminUsers', Object => $RT::System) ) {
1605 return ( $self->SUPER::_Value($field) );