1 # {{{ BEGIN BPS TAGGED BLOCK
5 # This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 # CONTRIBUTION SUBMISSION POLICY:
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
45 # }}} END BPS TAGGED BLOCK
48 RT::User - RT User object
69 no warnings qw(redefine);
71 use vars qw(%_USERS_KEY_CACHE);
73 %_USERS_KEY_CACHE = ();
84 sub _OverlayAccessible {
87 Name => { public => 1, admin => 1 },
88 Password => { read => 0 },
89 EmailAddress => { public => 1 },
90 Organization => { public => 1, admin => 1 },
91 RealName => { public => 1 },
92 NickName => { public => 1 },
93 Lang => { public => 1 },
94 EmailEncoding => { public => 1 },
95 WebEncoding => { public => 1 },
96 ExternalContactInfoId => { public => 1, admin => 1 },
97 ContactInfoSystem => { public => 1, admin => 1 },
98 ExternalAuthId => { public => 1, admin => 1 },
99 AuthSystem => { public => 1, admin => 1 },
100 Gecos => { public => 1, admin => 1 },
101 PGPKey => { public => 1, admin => 1 },
112 =head2 Create { PARAMHASH }
117 # Make sure we can create a user
119 my $u1 = RT::User->new($RT::SystemUser);
120 is(ref($u1), 'RT::User');
121 my ($id, $msg) = $u1->Create(Name => 'CreateTest1', EmailAddress => 'create-test-1@example.com');
122 ok ($id, "Creating user CreateTest1 - " . $msg );
124 # Make sure we can't create a second user with the same name
125 my $u2 = RT::User->new($RT::SystemUser);
126 ($id, $msg) = $u2->Create(Name => 'CreateTest1', EmailAddress => 'create-test-2@example.com');
130 # Make sure we can't create a second user with the same EmailAddress address
131 my $u3 = RT::User->new($RT::SystemUser);
132 ($id, $msg) = $u3->Create(Name => 'CreateTest2', EmailAddress => 'create-test-1@example.com');
135 # Make sure we can create a user with no EmailAddress address
136 my $u4 = RT::User->new($RT::SystemUser);
137 ($id, $msg) = $u4->Create(Name => 'CreateTest3');
140 # make sure we can create a second user with no EmailAddress address
141 my $u5 = RT::User->new($RT::SystemUser);
142 ($id, $msg) = $u5->Create(Name => 'CreateTest4');
145 # make sure we can create a user with a blank EmailAddress address
146 my $u6 = RT::User->new($RT::SystemUser);
147 ($id, $msg) = $u6->Create(Name => 'CreateTest6', EmailAddress => '');
149 # make sure we can create a second user with a blankEmailAddress address
150 my $u7 = RT::User->new($RT::SystemUser);
151 ($id, $msg) = $u7->Create(Name => 'CreateTest7', EmailAddress => '');
154 # Can we change the email address away from from "";
155 ($id,$msg) = $u7->SetEmailAddress('foo@bar');
157 # can we change the address back to "";
158 ($id,$msg) = $u7->SetEmailAddress('');
160 is ($u7->EmailAddress, '');
174 @_ # get the real argumentlist
178 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
179 return ( 0, $self->loc('No permission to create users') );
183 unless ($self->CanonicalizeUserInfo(\%args)) {
184 return ( 0, $self->loc("Could not set user info") );
187 $args{'EmailAddress'} = $self->CanonicalizeEmailAddress($args{'EmailAddress'});
189 # if the user doesn't have a name defined, set it to the email address
190 $args{'Name'} = $args{'EmailAddress'} unless ($args{'Name'});
194 # Privileged is no longer a column in users
195 my $privileged = $args{'Privileged'};
196 delete $args{'Privileged'};
199 if ($args{'CryptedPassword'} ) {
200 $args{'Password'} = $args{'CryptedPassword'};
201 delete $args{'CryptedPassword'};
203 elsif ( !$args{'Password'} ) {
204 $args{'Password'} = '*NO-PASSWORD*';
206 elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
207 return ( 0, $self->loc("Password too short") );
211 $args{'Password'} = $self->_GeneratePassword($args{'Password'});
214 #TODO Specify some sensible defaults.
216 unless ( $args{'Name'} ) {
218 $RT::Logger->crit(Dumper \%args);
219 return ( 0, $self->loc("Must specify 'Name' attribute") );
222 #SANITY CHECK THE NAME AND ABORT IF IT'S TAKEN
223 if ($RT::SystemUser) { #This only works if RT::SystemUser has been defined
224 my $TempUser = RT::User->new($RT::SystemUser);
225 $TempUser->Load( $args{'Name'} );
226 return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
228 return ( 0, $self->loc('Email address in use') )
229 unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
232 $RT::Logger->warning( "$self couldn't check for pre-existing users");
236 $RT::Handle->BeginTransaction();
237 # Groups deal with principal ids, rather than user ids.
238 # When creating this user, set up a principal Id for it.
239 my $principal = RT::Principal->new($self->CurrentUser);
240 my $principal_id = $principal->Create(PrincipalType => 'User',
241 Disabled => $args{'Disabled'},
243 # If we couldn't create a principal Id, get the fuck out.
244 unless ($principal_id) {
245 $RT::Handle->Rollback();
246 $RT::Logger->crit("Couldn't create a Principal on new user create.");
247 $RT::Logger->crit("Strange things are afoot at the circle K");
248 return ( 0, $self->loc('Could not create user') );
251 $principal->__Set(Field => 'ObjectId', Value => $principal_id);
252 delete $args{'Disabled'};
254 $self->SUPER::Create(id => $principal_id , %args);
257 #If the create failed.
259 $RT::Handle->Rollback();
260 $RT::Logger->error("Could not create a new user - " .join('-'. %args));
262 return ( 0, $self->loc('Could not create user') );
265 my $aclstash = RT::Group->new($self->CurrentUser);
266 my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
269 $RT::Handle->Rollback();
270 $RT::Logger->crit("Couldn't stash the user in groupmembers");
271 return ( 0, $self->loc('Could not create user') );
275 my $everyone = RT::Group->new($self->CurrentUser);
276 $everyone->LoadSystemInternalGroup('Everyone');
277 unless ($everyone->id) {
278 $RT::Logger->crit("Could not load Everyone group on user creation.");
279 $RT::Handle->Rollback();
280 return ( 0, $self->loc('Could not create user') );
284 my ($everyone_id, $everyone_msg) = $everyone->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
285 unless ($everyone_id) {
286 $RT::Logger->crit("Could not add user to Everyone group on user creation.");
287 $RT::Logger->crit($everyone_msg);
288 $RT::Handle->Rollback();
289 return ( 0, $self->loc('Could not create user') );
293 my $access_class = RT::Group->new($self->CurrentUser);
295 $access_class->LoadSystemInternalGroup('Privileged');
297 $access_class->LoadSystemInternalGroup('Unprivileged');
300 unless ($access_class->id) {
301 $RT::Logger->crit("Could not load Privileged or Unprivileged group on user creation");
302 $RT::Handle->Rollback();
303 return ( 0, $self->loc('Could not create user') );
307 my ($ac_id, $ac_msg) = $access_class->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
310 $RT::Logger->crit("Could not add user to Privileged or Unprivileged group on user creation. Aborted");
311 $RT::Logger->crit($ac_msg);
312 $RT::Handle->Rollback();
313 return ( 0, $self->loc('Could not create user') );
318 return ( $id, $self->loc('User created') );
327 =head2 SetPrivileged BOOL
329 If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
330 Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
332 Returns a standard RT tuple of (val, msg);
337 ok(my $user = RT::User->new($RT::SystemUser));
338 ok($user->Load('root'), "Loaded user 'root'");
339 ok($user->Privileged, "User 'root' is privileged");
340 ok(my ($v,$m) = $user->SetPrivileged(0));
341 ok ($v ==1, "Set unprivileged suceeded ($m)");
342 ok(!$user->Privileged, "User 'root' is no longer privileged");
343 ok(my ($v2,$m2) = $user->SetPrivileged(1));
344 ok ($v2 ==1, "Set privileged suceeded ($m2");
345 ok($user->Privileged, "User 'root' is privileged again");
356 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
357 return ( 0, $self->loc('Permission Denied') );
359 my $priv = RT::Group->new($self->CurrentUser);
360 $priv->LoadSystemInternalGroup('Privileged');
363 $RT::Logger->crit("Could not find Privileged pseudogroup");
364 return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
367 my $unpriv = RT::Group->new($self->CurrentUser);
368 $unpriv->LoadSystemInternalGroup('Unprivileged');
369 unless ($unpriv->Id) {
370 $RT::Logger->crit("Could not find unprivileged pseudogroup");
371 return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
375 if ($priv->HasMember($self->PrincipalObj)) {
376 #$RT::Logger->debug("That user is already privileged");
377 return (0,$self->loc("That user is already privileged"));
379 if ($unpriv->HasMember($self->PrincipalObj)) {
380 $unpriv->_DeleteMember($self->PrincipalId);
382 # if we had layered transactions, life would be good
383 # sadly, we have to just go ahead, even if something
385 $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
386 "unprivileged. something is drastically wrong.");
388 my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
390 return (1, $self->loc("That user is now privileged"));
396 if ($unpriv->HasMember($self->PrincipalObj)) {
397 #$RT::Logger->debug("That user is already unprivileged");
398 return (0,$self->loc("That user is already unprivileged"));
400 if ($priv->HasMember($self->PrincipalObj)) {
401 $priv->_DeleteMember( $self->PrincipalId);
403 # if we had layered transactions, life would be good
404 # sadly, we have to just go ahead, even if something
406 $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
407 "unprivileged. something is drastically wrong.");
409 my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
411 return (1, $self->loc("That user is now unprivileged"));
424 Returns true if this user is privileged. Returns undef otherwise.
430 my $priv = RT::Group->new($self->CurrentUser);
431 $priv->LoadSystemInternalGroup('Privileged');
432 if ($priv->HasMember($self->PrincipalObj)) {
442 # {{{ sub _BootstrapCreate
444 #create a user without validating _any_ data.
446 #To be used only on database init.
447 # We can't localize here because it's before we _have_ a loc framework
449 sub _BootstrapCreate {
453 $args{'Password'} = '*NO-PASSWORD*';
456 $RT::Handle->BeginTransaction();
458 # Groups deal with principal ids, rather than user ids.
459 # When creating this user, set up a principal Id for it.
460 my $principal = RT::Principal->new($self->CurrentUser);
461 my $principal_id = $principal->Create(PrincipalType => 'User', ObjectId => '0');
462 $principal->__Set(Field => 'ObjectId', Value => $principal_id);
464 # If we couldn't create a principal Id, get the fuck out.
465 unless ($principal_id) {
466 $RT::Handle->Rollback();
467 $RT::Logger->crit("Couldn't create a Principal on new user create. Strange things are afoot at the circle K");
468 return ( 0, 'Could not create user' );
470 $self->SUPER::Create(id => $principal_id, %args);
472 #If the create failed.
474 $RT::Handle->Rollback();
475 return ( 0, 'Could not create user' ) ; #never loc this
478 my $aclstash = RT::Group->new($self->CurrentUser);
479 my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
482 $RT::Handle->Rollback();
483 $RT::Logger->crit("Couldn't stash the user in groupmembers");
484 return ( 0, $self->loc('Could not create user') );
488 $RT::Handle->Commit();
490 return ( $id, 'User created' );
500 return ( 0, $self->loc('Deleting this object would violate referential integrity') );
510 Load a user object from the database. Takes a single argument.
511 If the argument is numerical, load by the column 'id'. Otherwise, load by
512 the "Name" column which is the user's textual username.
518 my $identifier = shift || return undef;
520 #if it's an int, load by id. otherwise, load by name.
521 if ( $identifier !~ /\D/ ) {
522 $self->SUPER::LoadById($identifier);
525 $self->LoadByCol( "Name", $identifier );
531 # {{{ sub LoadByEmail
535 Tries to load this user object from the database by the user's email address.
544 # Never load an empty address as an email address.
549 $address = $self->CanonicalizeEmailAddress($address);
551 #$RT::Logger->debug("Trying to load an email address: $address\n");
552 return $self->LoadByCol( "EmailAddress", $address );
557 # {{{ LoadOrCreateByEmail
559 =head2 LoadOrCreateByEmail ADDRESS
561 Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
562 the provided email address. and loads them.
564 Returns a tuple of the user's id and a status message.
565 0 will be returned in place of the user's id in case of failure.
569 sub LoadOrCreateByEmail {
575 my ( $Address, $Name ) =
576 RT::EmailParser::ParseAddressFromHeader('', $email);
579 $self->LoadByEmail($email);
580 $message = $self->loc('User loaded');
582 ( $val, $message ) = $self->Create(
584 EmailAddress => $email,
587 Comments => 'Autocreated when added as a watcher');
589 # Deal with the race condition of two account creations at once
590 $self->LoadByEmail($email);
593 $self->LoadByEmail($email);
596 $RT::Logger->error("Recovered from creation failure due to race condition");
597 $message = $self->loc("User loaded");
600 $RT::Logger->crit("Failed to create user ".$email .": " .$message);
606 return($self->Id, $message);
617 # {{{ sub ValidateEmailAddress
619 =head2 ValidateEmailAddress ADDRESS
621 Returns true if the email address entered is not in use by another user or is
622 undef or ''. Returns false if it's in use.
626 sub ValidateEmailAddress {
630 # if the email address is null, it's always valid
631 return (1) if ( !$Value || $Value eq "" );
633 my $TempUser = RT::User->new($RT::SystemUser);
634 $TempUser->LoadByEmail($Value);
636 if ( $TempUser->id && ( $TempUser->id != $self->id ) )
637 { # if we found a user with that address
638 # it's invalid to set this user's address to it
641 else { #it's a valid email address
648 # {{{ sub CanonicalizeEmailAddress
652 =item CanonicalizeEmailAddress ADDRESS
654 # CanonicalizeEmailAddress converts email addresses into canonical form.
655 # it takes one email address in and returns the proper canonical
656 # form. You can dump whatever your proper local config is in here
660 sub CanonicalizeEmailAddress {
663 # Example: the following rule would treat all email
664 # coming from a subdomain as coming from second level domain
666 if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
667 $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
675 # {{{ sub CanonicalizeUserInfo
679 =item CanonicalizeUserInfo HASH of ARGS
681 # CanonicalizeUserInfo can convert all User->Create options.
682 # it takes a hashref of all the params sent to User->Create and
683 # returns that same hash, by default nothing is done.
685 # This function is intended to allow users to have their info looked up via
686 # an outside source and modified upon creation.
690 sub CanonicalizeUserInfo {
702 # {{{ Password related functions
704 # {{{ sub SetRandomPassword
706 =head2 SetRandomPassword
708 Takes no arguments. Returns a status code and a new password or an error message.
709 If the status is 1, the second value returned is the new password.
710 If the status is anything else, the new value returned is the error code.
714 sub SetRandomPassword {
717 unless ( $self->CurrentUserCanModify('Password') ) {
718 return ( 0, $self->loc("Permission Denied") );
721 my $pass = $self->GenerateRandomPassword( 6, 8 );
723 # If we have "notify user on
725 my ( $val, $msg ) = $self->SetPassword($pass);
727 #If we got an error return the error.
728 return ( 0, $msg ) unless ($val);
730 #Otherwise, we changed the password, lets return it.
737 # {{{ sub ResetPassword
741 Returns status, [ERROR or new password]. Resets this user\'s password to
742 a randomly generated pronouncable password and emails them, using a
743 global template called "RT_PasswordChange", which can be overridden
744 with global templates "RT_PasswordChange_Privileged" or "RT_PasswordChange_NonPrivileged"
745 for privileged and Non-privileged users respectively.
752 unless ( $self->CurrentUserCanModify('Password') ) {
753 return ( 0, $self->loc("Permission Denied") );
755 my ( $status, $pass ) = $self->SetRandomPassword();
758 return ( 0, "$pass" );
761 my $template = RT::Template->new( $self->CurrentUser );
763 if ( $self->Privileged ) {
764 $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
767 $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
770 unless ( $template->Id ) {
771 $template->LoadGlobalTemplate('RT_PasswordChange');
774 unless ( $template->Id ) {
775 $RT::Logger->crit( "$self tried to send "
777 . " a password reminder "
778 . "but couldn't find a password change template" );
781 my $notification = RT::Action::SendPasswordEmail->new(
782 TemplateObj => $template,
786 $notification->SetHeader( 'To', $self->EmailAddress );
789 $ret = $notification->Prepare();
791 $ret = $notification->Commit();
795 return ( 1, $self->loc('New password notification sent') );
798 return ( 0, $self->loc('Notification could not be sent') );
805 # {{{ sub GenerateRandomPassword
807 =head2 GenerateRandomPassword MIN_LEN and MAX_LEN
809 Returns a random password between MIN_LEN and MAX_LEN characters long.
813 sub GenerateRandomPassword {
815 my $min_length = shift;
816 my $max_length = shift;
818 #This code derived from mpw.pl, a bit of code with a sordid history
821 # Perl cleaned up a bit by Jesse Vincent 1/14/2001.
822 # Converted to perl from C by Marc Horowitz, 1/20/2000.
823 # Converted to C from Multics PL/I by Bill Sommerfeld, 4/21/86.
824 # Original PL/I version provided by Jerry Saltzer.
826 my ( $frequency, $start_freq, $total_sum, $row_sums );
828 #When munging characters, we need to know where to start counting letters from
831 # frequency of English digraphs (from D Edwards 1/27/66)
834 4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62, 23, 167,
835 2, 14, 0, 83, 76, 127, 7, 25, 8, 1, 9, 1
838 13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0,
839 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0
842 32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1, 0,
843 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0
846 40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15, 6,
847 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0
850 84, 20, 55, 125, 51, 40, 19, 16, 50, 1,
851 4, 55, 54, 146, 35, 37, 6, 191, 149, 65,
855 19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1, 0,
856 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0
859 20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1, 4,
860 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0
863 101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3, 2,
864 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0
867 40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38, 25, 202,
868 56, 12, 1, 46, 79, 117, 1, 22, 0, 4, 0, 3
871 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0,
872 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0
875 1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2,
876 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0
879 44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2, 2,
880 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0
883 52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7, 1,
884 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0
887 42, 10, 47, 122, 63, 19, 106, 12, 30, 1,
888 6, 6, 9, 7, 54, 7, 1, 7, 44, 124,
892 7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19, 41, 134,
893 13, 23, 0, 91, 23, 42, 55, 16, 28, 0, 4, 1
896 19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0,
897 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0
900 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
901 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0
904 83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5, 26, 16,
905 60, 4, 0, 24, 37, 55, 6, 11, 4, 0, 28, 0
908 65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7, 11, 12,
909 56, 17, 6, 9, 48, 116, 35, 1, 28, 0, 4, 0
912 57, 22, 3, 1, 76, 5, 2, 330, 126, 1,
913 0, 14, 10, 6, 79, 7, 0, 49, 50, 56,
917 11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31,
918 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0
921 7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0,
922 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0
925 36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1, 8,
926 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0
929 1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0,
930 1, 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0
933 14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7, 5,
934 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0
937 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
938 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
942 #We need to know the totals for each row
946 map { $sum += $_ } @$_;
951 #Frequency with which a given letter starts a word.
953 1299, 425, 725, 271, 375, 470, 93, 223, 1009, 24,
954 20, 355, 379, 319, 823, 618, 21, 317, 962, 1991,
955 271, 104, 516, 6, 16, 14
959 map { $total_sum += $_ } @$start_freq;
961 my $length = $min_length + int( rand( $max_length - $min_length ) );
963 my $char = $self->_GenerateRandomNextChar( $total_sum, $start_freq );
964 my @word = ( $char + $a );
965 for ( 2 .. $length ) {
967 $self->_GenerateRandomNextChar( $row_sums->[$char],
968 $frequency->[$char] );
969 push ( @word, $char + $a );
973 return pack( "C*", @word );
977 #A private helper function for RandomPassword
978 # Takes a row summary and a frequency chart for the next character to be searched
979 sub _GenerateRandomNextChar {
981 my ( $all, $freq ) = @_;
984 for ( $pos = int( rand($all) ), $i = 0 ;
985 $pos >= $freq->[$i] ;
986 $pos -= $freq->[$i], $i++ )
995 # {{{ sub SetPassword
999 Takes a string. Checks the string's length and sets this user's password
1006 my $password = shift;
1008 unless ( $self->CurrentUserCanModify('Password') ) {
1009 return ( 0, $self->loc('Permission Denied') );
1013 return ( 0, $self->loc("No password set") );
1015 elsif ( length($password) < $RT::MinimumPasswordLength ) {
1016 return ( 0, $self->loc("Password too short") );
1019 $password = $self->_GeneratePassword($password);
1020 return ( $self->SUPER::SetPassword( $password));
1025 =head2 _GeneratePassword PASSWORD
1027 returns an MD5 hash of the password passed in, in base64 encoding.
1031 sub _GeneratePassword {
1033 my $password = shift;
1035 my $md5 = Digest::MD5->new();
1036 $md5->add($password);
1037 return ($md5->b64digest);
1043 # {{{ sub IsPassword
1047 Returns true if the passed in value is this user's password.
1048 Returns undef otherwise.
1056 #TODO there isn't any apparent way to legitimately ACL this
1058 # RT does not allow null passwords
1059 if ( ( !defined($value) ) or ( $value eq '' ) ) {
1063 if ( $self->PrincipalObj->Disabled ) {
1065 "Disabled user " . $self->Name . " tried to log in" );
1069 if ( ($self->__Value('Password') eq '') ||
1070 ($self->__Value('Password') eq undef) ) {
1074 # generate an md5 password
1075 if ($self->_GeneratePassword($value) eq $self->__Value('Password')) {
1079 # if it's a historical password we say ok.
1081 if ( $self->__Value('Password') eq crypt( $value, $self->__Value('Password') ) ) {
1085 # no password check has succeeded. get out
1094 # {{{ sub SetDisabled
1096 =head2 Sub SetDisabled
1098 Toggles the user's disabled flag.
1100 set, all password checks for this user will fail. All ACL checks for this
1101 user will fail. The user will appear in no user listings.
1109 unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1110 return (0, $self->loc('Permission Denied'));
1112 return $self->PrincipalObj->SetDisabled(@_);
1117 return $self->PrincipalObj->Disabled(@_);
1121 # {{{ Principal related routines
1125 Returns the principal object for this user. returns an empty RT::Principal
1126 if there's no principal object matching this user.
1127 The response is cached. PrincipalObj should never ever change.
1131 ok(my $u = RT::User->new($RT::SystemUser));
1132 ok($u->Load(1), "Loaded the first user");
1133 ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
1134 ok($u->PrincipalObj->PrincipalType eq 'User' , "Principal 1 is a user, not a group");
1143 unless ($self->{'PrincipalObj'} &&
1144 ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
1145 ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
1147 $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
1148 $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
1149 'PrincipalType' => 'User') ;
1151 return($self->{'PrincipalObj'});
1157 Returns this user's PrincipalId
1170 # {{{ sub HasGroupRight
1172 =head2 HasGroupRight
1174 Takes a paramhash which can contain
1176 GroupObj => RT::Group or Group => integer
1180 Returns 1 if this user has the right specified in the paramhash for the Group
1183 Returns undef if they don't.
1197 if ( defined $args{'Group'} ) {
1198 $args{'GroupObj'} = RT::Group->new( $self->CurrentUser );
1199 $args{'GroupObj'}->Load( $args{'Group'} );
1202 # {{{ Validate and load up the GroupId
1203 unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
1210 # Figure out whether a user has the right we're asking about.
1211 my $retval = $self->HasRight(
1212 Object => $args{'GroupObj'},
1213 Right => $args{'Right'},
1223 # {{{ sub Rights testing
1225 =head2 Rights testing
1230 my $root = RT::User->new($RT::SystemUser);
1231 $root->Load('root');
1232 ok($root->Id, "Found the root user");
1233 my $rootq = RT::Queue->new($root);
1235 ok($rootq->Id, "Loaded the first queue");
1237 ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
1239 my $new_user = RT::User->new($RT::SystemUser);
1240 my ($id, $msg) = $new_user->Create(Name => 'ACLTest');
1242 ok ($id, "Created a new user for acl test $msg");
1244 my $q = RT::Queue->new($new_user);
1246 ok($q->Id, "Loaded the first queue");
1249 ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
1250 ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
1251 ok ($gval, "Grant succeeded - $gmsg");
1254 ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
1255 ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
1256 ok ($gval, "Revocation succeeded - $gmsg");
1257 ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
1263 # Create a ticket in the queue
1264 my $new_tick = RT::Ticket->new($RT::SystemUser);
1265 my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
1266 ok($tickid, "Created ticket: $tickid");
1267 # Make sure the user doesn't have the right to modify tickets in the queue
1268 ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1269 # Create a new group
1270 my $group = RT::Group->new($RT::SystemUser);
1271 $group->CreateUserDefinedGroup(Name => 'ACLTest');
1272 ok($group->Id, "Created a new group Ok");
1273 # Grant a group the right to modify tickets in a queue
1274 ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
1275 ok($gv,"Grant succeeed - $gm");
1276 # Add the user to the group
1277 ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
1278 ok ($aid, "Member added to group: $amsg");
1279 # Make sure the user does have the right to modify tickets in the queue
1280 ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
1283 # Remove the user from the group
1284 ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
1285 ok ($did,"Deleted the group member: $dmsg");
1286 # Make sure the user doesn't have the right to modify tickets in the queue
1287 ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1290 my $q_as_system = RT::Queue->new($RT::SystemUser);
1291 $q_as_system->Load(1);
1292 ok($q_as_system->Id, "Loaded the first queue");
1294 # Create a ticket in the queue
1295 my $new_tick2 = RT::Ticket->new($RT::SystemUser);
1296 my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
1297 ok($tick2id, "Created ticket: $tick2id");
1298 ok($new_tick2->QueueObj->id eq $q_as_system->Id, "Created a new ticket in queue 1");
1301 # make sure that the user can't do this without subgroup membership
1302 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1305 my $subgroup = RT::Group->new($RT::SystemUser);
1306 $subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest');
1307 ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
1308 #Add the subgroup as a subgroup of the group
1309 my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
1310 ok ($said, "Added the subgroup as a member of the group");
1311 # Add the user to a subgroup of the group
1313 my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
1314 ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
1315 # Make sure the user does have the right to modify tickets in the queue
1316 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
1318 # {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
1321 ($id, $msg) = $group->SetDisabled(1);
1323 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
1324 ($id, $msg) = $group->SetDisabled(0);
1326 # Test what happens when we disable the group the user is a member of directly
1328 ($id, $msg) = $subgroup->SetDisabled(1);
1330 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
1331 ($id, $msg) = $subgroup->SetDisabled(0);
1333 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
1338 my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
1339 ok($usrid,"removed the user from the group - $usrmsg");
1340 # Make sure the user doesn't have the right to modify tickets in the queue
1341 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1343 #revoke the right to modify tickets in a queue
1344 ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
1345 ok($gv,"revoke succeeed - $gm");
1347 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
1349 # Grant queue admin cc the right to modify ticket in the queue
1350 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");
1351 ok($qv, "Granted the right successfully - $qm");
1353 # Add the user as a queue admincc
1354 ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1355 ok ($add_id, "the user is now a queue admincc - $add_msg");
1357 # Make sure the user does have the right to modify tickets in the queue
1358 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1359 # Remove the user from the role group
1360 ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1362 # Make sure the user doesn't have the right to modify tickets in the queue
1363 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1367 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1369 # Add the user as a ticket admincc
1370 ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1371 ok ($add_id, "the user is now a queue admincc - $add_msg");
1373 # Make sure the user does have the right to modify tickets in the queue
1374 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1376 # Remove the user from the role group
1377 ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1379 # Make sure the user doesn't have the right to modify tickets in the queue
1380 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1383 # Revoke the right to modify ticket in the queue
1384 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");
1385 ok($rqv, "Revoked the right successfully - $rqm");
1391 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
1393 # Before we start Make sure the user does not have the right to modify tickets in the queue
1394 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
1395 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
1397 # Grant queue admin cc the right to modify ticket in the queue
1398 ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
1399 ok($qv, "Granted the right successfully - $qm");
1401 # Make sure the user can't modify the ticket before they're added as a watcher
1402 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
1403 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
1405 # Add the user as a queue admincc
1406 ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1407 ok ($add_id, "the user is now a queue admincc - $add_msg");
1409 # Make sure the user does have the right to modify tickets in the queue
1410 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1411 ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
1412 # Remove the user from the role group
1413 ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1415 # Make sure the user doesn't have the right to modify tickets in the queue
1416 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
1417 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
1421 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1423 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
1424 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
1427 # Add the user as a ticket admincc
1428 ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
1429 ok ($add_id, "the user is now a queue admincc - $add_msg");
1431 # Make sure the user does have the right to modify tickets in the queue
1432 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
1433 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
1435 # Remove the user from the role group
1436 ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
1438 # Make sure the user doesn't have the right to modify tickets in the queue
1439 ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
1440 ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
1443 # Revoke the right to modify ticket in the queue
1444 ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
1445 ok($rqv, "Revoked the right successfully - $rqm");
1452 # Grant "privileged users" the system right to create users
1453 # Create a privileged user.
1454 # have that user create another user
1455 # Revoke the right for privileged users to create users
1456 # have the privileged user try to create another user and fail the ACL check
1469 Shim around PrincipalObj->HasRight. See RT::Principal
1476 return $self->PrincipalObj->HasRight(@_);
1481 # {{{ sub CurrentUserCanModify
1483 =head2 CurrentUserCanModify RIGHT
1485 If the user has rights for this object, either because
1486 he has 'AdminUsers' or (if he\'s trying to edit himself and the right isn\'t an
1487 admin right) 'ModifySelf', return 1. otherwise, return undef.
1491 sub CurrentUserCanModify {
1495 if ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1499 #If the field is marked as an "administrators only" field,
1500 # don\'t let the user touch it.
1501 elsif ( $self->_Accessible( $right, 'admin' ) ) {
1505 #If the current user is trying to modify themselves
1506 elsif ( ( $self->id == $self->CurrentUser->id )
1507 and ( $self->CurrentUser->HasRight(Right => 'ModifySelf', Object => $RT::System) ) )
1512 #If we don\'t have a good reason to grant them rights to modify
1522 # {{{ sub CurrentUserHasRight
1524 =head2 CurrentUserHasRight
1526 Takes a single argument. returns 1 if $Self->CurrentUser
1527 has the requested right. returns undef otherwise
1531 sub CurrentUserHasRight {
1535 return ( $self->CurrentUser->HasRight(Right => $right, Object => $RT::System) );
1551 # Nobody is allowed to futz with RT_System or Nobody
1553 if ( ($self->Id == $RT::SystemUser->Id ) ||
1554 ($self->Id == $RT::Nobody->Id)) {
1555 return ( 0, $self->loc("Can not modify system users") );
1557 unless ( $self->CurrentUserCanModify( $args{'Field'} ) ) {
1558 return ( 0, $self->loc("Permission Denied") );
1562 my ( $ret, $msg ) = $self->SUPER::_Set(
1563 Field => $args{'Field'},
1564 Value => $args{'Value'}
1567 return ( $ret, $msg );
1576 Takes the name of a table column.
1577 Returns its value as a string, if the user passes an ACL check
1586 #If the current user doesn't have ACLs, don't let em at it.
1588 my @PublicFields = qw( Name EmailAddress Organization Disabled
1589 RealName NickName Gecos ExternalAuthId
1590 AuthSystem ExternalContactInfoId
1591 ContactInfoSystem );
1593 #if the field is public, return it.
1594 if ( $self->_Accessible( $field, 'public' ) ) {
1595 return ( $self->SUPER::_Value($field) );
1599 #If the user wants to see their own values, let them
1600 # TODO figure ouyt a better way to deal with this
1601 elsif ( $self->CurrentUser->Id == $self->Id ) {
1602 return ( $self->SUPER::_Value($field) );
1605 #If the user has the admin users right, return the field
1606 elsif ( $self->CurrentUser->HasRight(Right =>'AdminUsers', Object => $RT::System) ) {
1607 return ( $self->SUPER::_Value($field) );