This commit was generated by cvs2svn to compensate for changes in r3880,
[freeside.git] / rt / lib / RT / User_Overlay.pm
1 # BEGIN LICENSE BLOCK
2
3 # Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
4
5 # (Except where explictly superceded by other copyright notices)
6
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
10 # from www.gnu.org.
11
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.
16
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.
21
22
23 # END LICENSE BLOCK
24 =head1 NAME
25
26   RT::User - RT User object
27
28 =head1 SYNOPSIS
29
30   use RT::User;
31
32 =head1 DESCRIPTION
33
34
35 =head1 METHODS
36
37 =begin testing
38
39 ok(require RT::User);
40
41 =end testing
42
43
44 =cut
45
46 use strict;
47 no warnings qw(redefine);
48
49 use vars qw(%_USERS_KEY_CACHE);
50
51 %_USERS_KEY_CACHE = ();
52
53 use Digest::MD5;
54 use RT::Principals;
55 use RT::ACE;
56 use RT::EmailParser;
57
58
59 # {{{ sub _Accessible 
60
61
62 sub _ClassAccessible {
63     {
64      
65         id =>
66                 {read => 1, type => 'int(11)', default => ''},
67         Name => 
68                 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(120)', default => ''},
69         Password => 
70                 { write => 1, type => 'varchar(40)', default => ''},
71         Comments => 
72                 {read => 1, write => 1, admin => 1, type => 'blob', default => ''},
73         Signature => 
74                 {read => 1, write => 1, type => 'blob', default => ''},
75         EmailAddress => 
76                 {read => 1, write => 1, public => 1,  type => 'varchar(120)', default => ''},
77         FreeformContactInfo => 
78                 {read => 1, write => 1, type => 'blob', default => ''},
79         Organization => 
80                 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(200)', default => ''},
81         RealName => 
82                 {read => 1, write => 1, public => 1, type => 'varchar(120)', default => ''},
83         NickName => 
84                 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
85         Lang => 
86                 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
87         EmailEncoding => 
88                 {read => 1, write => 1, public => 1, type => 'varchar(16)', default => ''},
89         WebEncoding => 
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 => ''},
93         ContactInfoSystem => 
94                 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(30)', default => ''},
95         ExternalAuthId => 
96                 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(100)', default => ''},
97         AuthSystem => 
98                 {read => 1, write => 1, public => 1, admin => 1,type => 'varchar(30)', default => ''},
99         Gecos => 
100                 {read => 1, write => 1, public => 1, admin => 1, type => 'varchar(16)', default => ''},
101
102         PGPKey => {
103                 {read => 1, write => 1, public => 1, admin => 1, type => 'text', default => ''},
104         },
105         HomePhone => 
106                 {read => 1, write => 1, type => 'varchar(30)', default => ''},
107         WorkPhone => 
108                 {read => 1, write => 1, type => 'varchar(30)', default => ''},
109         MobilePhone => 
110                 {read => 1, write => 1, type => 'varchar(30)', default => ''},
111         PagerPhone => 
112                 {read => 1, write => 1, type => 'varchar(30)', default => ''},
113         Address1 => 
114                 {read => 1, write => 1, type => 'varchar(200)', default => ''},
115         Address2 => 
116                 {read => 1, write => 1, type => 'varchar(200)', default => ''},
117         City => 
118                 {read => 1, write => 1, type => 'varchar(100)', default => ''},
119         State => 
120                 {read => 1, write => 1, type => 'varchar(100)', default => ''},
121         Zip => 
122                 {read => 1, write => 1, type => 'varchar(16)', default => ''},
123         Country => 
124                 {read => 1, write => 1, type => 'varchar(50)', default => ''},
125         Creator => 
126                 {read => 1, auto => 1, type => 'int(11)', default => ''},
127         Created => 
128                 {read => 1, auto => 1, type => 'datetime', default => ''},
129         LastUpdatedBy => 
130                 {read => 1, auto => 1, type => 'int(11)', default => ''},
131         LastUpdated => 
132                 {read => 1, auto => 1, type => 'datetime', default => ''},
133
134  }
135 };
136
137
138 # }}}
139
140 # {{{ sub Create 
141
142 =head2 Create { PARAMHASH }
143
144
145 =begin testing
146
147 # Make sure we can create a user
148
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 );
153
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');
157 ok (!$id, $msg);
158
159
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');
163 ok (!$id, $msg);
164
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');
168 ok ($id, $msg);
169
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');
173 ok ($id, $msg);
174
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 => '');
178 ok ($id, $msg);
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 => '');
182 ok ($id, $msg);
183
184 # Can we change the email address away from from "";
185 ($id,$msg) = $u7->SetEmailAddress('foo@bar');
186 ok ($id, $msg);
187 # can we change the address back to "";  
188 ($id,$msg) = $u7->SetEmailAddress('');
189 ok ($id, $msg);
190 is ($u7->EmailAddress, '');
191
192
193 =end testing
194
195 =cut
196
197
198 sub Create {
199     my $self = shift;
200     my %args = (
201         Privileged => 0,
202         Disabled => 0,
203         EmailAddress => '',
204         @_    # get the real argumentlist
205     );
206
207     #Check the ACL
208     unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
209         return ( 0, $self->loc('No permission to create users') );
210     }
211
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'});
215
216
217
218     # Privileged is no longer a column in users
219     my $privileged = $args{'Privileged'};
220     delete $args{'Privileged'};
221
222
223     if ($args{'CryptedPassword'} ) {
224         $args{'Password'} = $args{'CryptedPassword'};
225         delete $args{'CryptedPassword'};
226     }
227     elsif ( !$args{'Password'} ) {
228         $args{'Password'} = '*NO-PASSWORD*';
229     }
230     elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
231         return ( 0, $self->loc("Password too short") );
232     }
233
234     else {
235         $args{'Password'} = $self->_GeneratePassword($args{'Password'});
236     }
237
238     #TODO Specify some sensible defaults.
239
240     unless ( $args{'Name'} ) {
241         use Data::Dumper;
242         $RT::Logger->crit(Dumper \%args);
243         return ( 0, $self->loc("Must specify 'Name' attribute") );
244     }
245
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 );
251
252         return ( 0, $self->loc('Email address in use') )
253           unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
254     }
255     else {
256         $RT::Logger->warning( "$self couldn't check for pre-existing users");
257     }
258
259
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'},
266                                 ObjectId => '0');
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') );
273     }
274
275     $principal->__Set(Field => 'ObjectId', Value => $principal_id);
276     delete $args{'Disabled'};
277
278     $self->SUPER::Create(id => $principal_id , %args);
279     my $id = $self->Id;
280
281     #If the create failed.
282     unless ($id) {
283         $RT::Handle->Rollback();
284         $RT::Logger->error("Could not create a new user - " .join('-'. %args));
285
286         return ( 0, $self->loc('Could not create user') );
287     }
288
289     my $aclstash = RT::Group->new($self->CurrentUser);
290     my $stash_id = $aclstash->_CreateACLEquivalenceGroup($principal);
291
292     unless ($stash_id) {
293         $RT::Handle->Rollback();
294         $RT::Logger->crit("Couldn't stash the user in groupmembers");
295         return ( 0, $self->loc('Could not create user') );
296     }
297
298
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') );
305     }
306
307
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') );
314     }
315
316
317     my $access_class = RT::Group->new($self->CurrentUser);
318     if ($privileged)  {
319         $access_class->LoadSystemInternalGroup('Privileged');
320     } else {
321         $access_class->LoadSystemInternalGroup('Unprivileged');
322     }
323
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') );
328     }
329
330
331     my ($ac_id, $ac_msg) = $access_class->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);  
332
333     unless ($ac_id) {
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') );
338     }
339
340
341     $RT::Handle->Commit;
342     return ( $id, $self->loc('User created') );
343 }
344
345 # }}}
346
347
348
349 # {{{ SetPrivileged
350
351 =head2 SetPrivileged BOOL
352
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. 
355
356 Returns a standard RT tuple of (val, msg);
357
358 =begin testing
359
360
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");
370
371 =end testing
372
373 =cut
374
375 sub SetPrivileged {
376     my $self = shift;
377     my $val = shift;
378
379     #Check the ACL
380     unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
381         return ( 0, $self->loc('Permission Denied') );
382     }
383     my $priv = RT::Group->new($self->CurrentUser);
384     $priv->LoadSystemInternalGroup('Privileged');
385    
386     unless ($priv->Id) {
387         $RT::Logger->crit("Could not find Privileged pseudogroup");
388         return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
389     }
390
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"));
396     }
397
398     if ($val) {
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"));
402         }
403         if ($unpriv->HasMember($self->PrincipalObj)) {
404             $unpriv->_DeleteMember($self->PrincipalId);
405         } else {
406         # if we had layered transactions, life would be good
407         # sadly, we have to just go ahead, even if something
408         # bogus happened
409             $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
410                 "unprivileged. something is drastically wrong.");
411         }
412         my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);  
413         if ($status) {
414             return (1, $self->loc("That user is now privileged"));
415         } else {
416             return (0, $msg);
417         }
418     }
419     else {
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"));
423         }
424         if ($priv->HasMember($self->PrincipalObj)) {
425             $priv->_DeleteMember( $self->PrincipalId);
426         } else {
427         # if we had layered transactions, life would be good
428         # sadly, we have to just go ahead, even if something
429         # bogus happened
430             $RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
431                 "unprivileged. something is drastically wrong.");
432         }
433         my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);  
434         if ($status) {
435             return (1, $self->loc("That user is now unprivileged"));
436         } else {
437             return (0, $msg);
438         }
439     }
440 }
441
442 # }}}
443
444 # {{{ Privileged
445
446 =head2 Privileged
447
448 Returns true if this user is privileged. Returns undef otherwise.
449
450 =cut
451
452 sub Privileged {
453     my $self = shift;
454     my $priv = RT::Group->new($self->CurrentUser);
455     $priv->LoadSystemInternalGroup('Privileged');
456     if ($priv->HasMember($self->PrincipalObj)) {
457         return(1);
458     }
459     else {
460         return(undef);
461     }
462 }
463
464 # }}}
465
466 # {{{ sub _BootstrapCreate 
467
468 #create a user without validating _any_ data.
469
470 #To be used only on database init.
471 # We can't localize here because it's before we _have_ a loc framework
472
473 sub _BootstrapCreate {
474     my $self = shift;
475     my %args = (@_);
476
477     $args{'Password'} = '*NO-PASSWORD*';
478
479
480     $RT::Handle->BeginTransaction(); 
481
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);
487    
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' );
493     }
494     $self->SUPER::Create(id => $principal_id, %args);
495     my $id = $self->Id;
496     #If the create failed.
497       unless ($id) {
498       $RT::Handle->Rollback();
499       return ( 0, 'Could not create user' ) ; #never loc this
500     }
501
502     my $aclstash = RT::Group->new($self->CurrentUser);
503     my $stash_id  = $aclstash->_CreateACLEquivalenceGroup($principal);
504
505     unless ($stash_id) {
506         $RT::Handle->Rollback();
507         $RT::Logger->crit("Couldn't stash the user in groupmembers");
508         return ( 0, $self->loc('Could not create user') );
509     }
510
511                                     
512     $RT::Handle->Commit();
513
514     return ( $id, 'User created' );
515 }
516
517 # }}}
518
519 # {{{ sub Delete 
520
521 sub Delete {
522     my $self = shift;
523
524     return ( 0, $self->loc('Deleting this object would violate referential integrity') );
525
526 }
527
528 # }}}
529
530 # {{{ sub Load 
531
532 =head2 Load
533
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.
537
538 =cut
539
540 sub Load {
541     my $self       = shift;
542     my $identifier = shift || return undef;
543
544     #if it's an int, load by id. otherwise, load by name.
545     if ( $identifier !~ /\D/ ) {
546         $self->SUPER::LoadById($identifier);
547     }
548     else {
549         $self->LoadByCol( "Name", $identifier );
550     }
551 }
552
553 # }}}
554
555 # {{{ sub LoadByEmail
556
557 =head2 LoadByEmail
558
559 Tries to load this user object from the database by the user's email address.
560
561
562 =cut
563
564 sub LoadByEmail {
565     my $self    = shift;
566     my $address = shift;
567
568     # Never load an empty address as an email address.
569     unless ($address) {
570         return (undef);
571     }
572
573     $address = $self->CanonicalizeEmailAddress($address);
574
575     #$RT::Logger->debug("Trying to load an email address: $address\n");
576     return $self->LoadByCol( "EmailAddress", $address );
577 }
578
579 # }}}
580
581 # {{{ LoadOrCreateByEmail 
582
583 =head2 LoadOrCreateByEmail ADDRESS
584
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.
587
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.
590
591 =cut
592
593 sub LoadOrCreateByEmail {
594     my $self = shift;
595     my $email = shift;
596
597         my ($val, $message);
598
599         my ( $Address, $Name ) =
600                 RT::EmailParser::ParseAddressFromHeader('', $email);
601         $email = $Address;
602
603         $self->LoadByEmail($email);
604         $message = $self->loc('User loaded');
605         unless ($self->Id) {
606             ( $val, $message ) = $self->Create(
607                 Name => $email,
608                 EmailAddress => $email,
609                 RealName     => $Name,
610                 Privileged   => 0,
611                 Comments     => 'Autocreated when added as a watcher');
612             unless ($val) {
613                 # Deal with the race condition of two account creations at once
614                 $self->LoadByEmail($email);
615                 unless ($self->Id) {
616                     sleep 5;
617                     $self->LoadByEmail($email);
618                 }
619                 if ($self->Id) {
620                     $RT::Logger->error("Recovered from creation failure due to race condition");
621                     $message = $self->loc("User loaded");
622                 }
623                 else {
624                     $RT::Logger->crit("Failed to create user ".$email .": " .$message);
625                 }
626             }
627         }
628
629         if ($self->Id) {
630             return($self->Id, $message);
631         }
632         else {
633             return(0, $message);
634         }
635
636
637     }
638
639 # }}}
640
641 # {{{ sub ValidateEmailAddress
642
643 =head2 ValidateEmailAddress ADDRESS
644
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. 
647
648 =cut
649
650 sub ValidateEmailAddress {
651     my $self  = shift;
652     my $Value = shift;
653
654     # if the email address is null, it's always valid
655     return (1) if ( !$Value || $Value eq "" );
656
657     my $TempUser = RT::User->new($RT::SystemUser);
658     $TempUser->LoadByEmail($Value);
659
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
663         return (undef);
664     }
665     else {    #it's a valid email address
666         return (1);
667     }
668 }
669
670 # }}}
671
672 # {{{ sub CanonicalizeEmailAddress
673
674
675
676 =item CanonicalizeEmailAddress ADDRESS
677
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
681
682 =cut
683
684 sub CanonicalizeEmailAddress {
685     my $self = shift;
686     my $email = shift;
687     # Example: the following rule would treat all email
688     # coming from a subdomain as coming from second level domain
689     # foo.com
690     if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
691         $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
692     }
693     return ($email);
694 }
695
696
697 # }}}
698
699
700 # {{{ Password related functions
701
702 # {{{ sub SetRandomPassword
703
704 =head2 SetRandomPassword
705
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.
709
710 =cut
711
712 sub SetRandomPassword {
713     my $self = shift;
714
715     unless ( $self->CurrentUserCanModify('Password') ) {
716         return ( 0, $self->loc("Permission Denied") );
717     }
718
719     my $pass = $self->GenerateRandomPassword( 6, 8 );
720
721     # If we have "notify user on 
722
723     my ( $val, $msg ) = $self->SetPassword($pass);
724
725     #If we got an error return the error.
726     return ( 0, $msg ) unless ($val);
727
728     #Otherwise, we changed the password, lets return it.
729     return ( 1, $pass );
730
731 }
732
733 # }}}
734
735 # {{{ sub ResetPassword
736
737 =head2 ResetPassword
738
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.
744
745 =cut
746
747 sub ResetPassword {
748     my $self = shift;
749
750     unless ( $self->CurrentUserCanModify('Password') ) {
751         return ( 0, $self->loc("Permission Denied") );
752     }
753     my ( $status, $pass ) = $self->SetRandomPassword();
754
755     unless ($status) {
756         return ( 0, "$pass" );
757     }
758
759     my $template = RT::Template->new( $self->CurrentUser );
760
761     if ( $self->Privileged ) {
762         $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
763     }
764     else {
765         $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
766     }
767
768     unless ( $template->Id ) {
769         $template->LoadGlobalTemplate('RT_PasswordChange');
770     }
771
772     unless ( $template->Id ) {
773         $RT::Logger->crit( "$self tried to send "
774               . $self->Name
775               . " a password reminder "
776               . "but couldn't find a password change template" );
777     }
778
779     my $notification = RT::Action::SendPasswordEmail->new(
780         TemplateObj => $template,
781         Argument    => $pass
782     );
783
784     $notification->SetTo( $self->EmailAddress );
785
786     my ($ret);
787     $ret = $notification->Prepare();
788     if ($ret) {
789         $ret = $notification->Commit();
790     }
791
792     if ($ret) {
793         return ( 1, $self->loc('New password notification sent') );
794     }
795     else {
796         return ( 0, $self->loc('Notification could not be sent') );
797     }
798
799 }
800
801 # }}}
802
803 # {{{ sub GenerateRandomPassword
804
805 =head2 GenerateRandomPassword MIN_LEN and MAX_LEN
806
807 Returns a random password between MIN_LEN and MAX_LEN characters long.
808
809 =cut
810
811 sub GenerateRandomPassword {
812     my $self       = shift;
813     my $min_length = shift;
814     my $max_length = shift;
815
816     #This code derived from mpw.pl, a bit of code with a sordid history
817     # Its notes: 
818
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.
823
824     my ( $frequency, $start_freq, $total_sum, $row_sums );
825
826     #When munging characters, we need to know where to start counting letters from
827     my $a = ord('a');
828
829     # frequency of English digraphs (from D Edwards 1/27/66) 
830     $frequency = [
831         [
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
834         ],    # aa - az
835         [
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
838         ],    # ba - bz
839         [
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
842         ],    # ca - cz
843         [
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
846         ],    # da - dz
847         [
848             84, 20, 55, 125, 51, 40, 19, 16,  50,  1,
849             4,  55, 54, 146, 35, 37, 6,  191, 149, 65,
850             9,  26, 21, 12,  5,  0
851         ],    # ea - ez
852         [
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
855         ],    # fa - fz
856         [
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
859         ],    # ga - gz
860         [
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
863         ],    # ha - hz
864         [
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
867         ],    # ia - iz
868         [
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
871         ],    # ja - jz
872         [
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
875         ],    # ka - kz
876         [
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
879         ],    # la - lz
880         [
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
883         ],    # ma - mz
884         [
885             42, 10, 47, 122, 63, 19, 106, 12, 30, 1,
886             6,  6,  9,  7,   54, 7,  1,   7,  44, 124,
887             6,  1,  15, 0,   12, 0
888         ],    # na - nz
889         [
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
892         ],    # oa - oz
893         [
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
896         ],    # pa - pz
897         [
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
900         ],    # qa - qz
901         [
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
904         ],    # ra - rz
905         [
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
908         ],    # sa - sz
909         [
910             57, 22, 3,  1, 76, 5, 2, 330, 126, 1,
911             0,  14, 10, 6, 79, 7, 0, 49,  50,  56,
912             21, 2,  27, 0, 24, 0
913         ],    # ta - tz
914         [
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
917         ],    # ua - uz
918         [
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
921         ],    # va - vz
922         [
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
925         ],    # wa - wz
926         [
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
929         ],    # xa - xz
930         [
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
933         ],    # ya - yz
934         [
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
937         ]
938     ];    # za - zz
939
940     #We need to know the totals for each row 
941     $row_sums = [
942         map {
943             my $sum = 0;
944             map { $sum += $_ } @$_;
945             $sum;
946           } @$frequency
947     ];
948
949     #Frequency with which a given letter starts a word.
950     $start_freq = [
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
954     ];
955
956     $total_sum = 0;
957     map { $total_sum += $_ } @$start_freq;
958
959     my $length = $min_length + int( rand( $max_length - $min_length ) );
960
961     my $char = $self->_GenerateRandomNextChar( $total_sum, $start_freq );
962     my @word = ( $char + $a );
963     for ( 2 .. $length ) {
964         $char =
965           $self->_GenerateRandomNextChar( $row_sums->[$char],
966             $frequency->[$char] );
967         push ( @word, $char + $a );
968     }
969
970     #Return the password
971     return pack( "C*", @word );
972
973 }
974
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 {
978     my $self = shift;
979     my ( $all, $freq ) = @_;
980     my ( $pos, $i );
981
982     for ( $pos = int( rand($all) ), $i = 0 ;
983         $pos >= $freq->[$i] ;
984         $pos -= $freq->[$i], $i++ )
985     {
986     }
987
988     return ($i);
989 }
990
991 # }}}
992
993 # {{{ sub SetPassword
994
995 =head2 SetPassword
996
997 Takes a string. Checks the string's length and sets this user's password 
998 to that string.
999
1000 =cut
1001
1002 sub SetPassword {
1003     my $self     = shift;
1004     my $password = shift;
1005
1006     unless ( $self->CurrentUserCanModify('Password') ) {
1007         return ( 0, $self->loc('Permission Denied') );
1008     }
1009
1010     if ( !$password ) {
1011         return ( 0, $self->loc("No password set") );
1012     }
1013     elsif ( length($password) < $RT::MinimumPasswordLength ) {
1014         return ( 0, $self->loc("Password too short") );
1015     }
1016     else {
1017         $password = $self->_GeneratePassword($password);
1018         return ( $self->SUPER::SetPassword( $password));
1019     }
1020
1021 }
1022
1023 =head2 _GeneratePassword PASSWORD
1024
1025 returns an MD5 hash of the password passed in, in base64 encoding.
1026
1027 =cut
1028
1029 sub _GeneratePassword {
1030     my $self = shift;
1031     my $password = shift;
1032
1033     my $md5 = Digest::MD5->new();
1034     $md5->add($password);
1035     return ($md5->b64digest);
1036
1037 }
1038
1039 # }}}
1040
1041 # {{{ sub IsPassword 
1042
1043 =head2 IsPassword
1044
1045 Returns true if the passed in value is this user's password.
1046 Returns undef otherwise.
1047
1048 =cut
1049
1050 sub IsPassword {
1051     my $self  = shift;
1052     my $value = shift;
1053
1054     #TODO there isn't any apparent way to legitimately ACL this
1055
1056     # RT does not allow null passwords 
1057     if ( ( !defined($value) ) or ( $value eq '' ) ) {
1058         return (undef);
1059     }
1060
1061    if ( $self->PrincipalObj->Disabled ) {
1062         $RT::Logger->info(
1063             "Disabled user " . $self->Name . " tried to log in" );
1064         return (undef);
1065     }
1066
1067     if ( ($self->__Value('Password') eq '') || 
1068          ($self->__Value('Password') eq undef) )  {
1069         return(undef);
1070      }
1071
1072     # generate an md5 password 
1073     if ($self->_GeneratePassword($value) eq $self->__Value('Password')) {
1074         return(1);
1075     }
1076
1077     #  if it's a historical password we say ok.
1078
1079     if ( $self->__Value('Password') eq crypt( $value, $self->__Value('Password') ) ) {
1080         return (1);
1081     }
1082
1083     # no password check has succeeded. get out
1084
1085     return (undef);
1086 }
1087
1088 # }}}
1089
1090 # }}}
1091
1092 # {{{ sub SetDisabled
1093
1094 =head2 Sub SetDisabled
1095
1096 Toggles the user's disabled flag.
1097 If this flag is
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.
1100
1101 =cut 
1102
1103 # }}}
1104
1105 sub SetDisabled {
1106     my $self = shift;
1107     unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1108         return (0, $self->loc('Permission Denied'));
1109     }
1110     return $self->PrincipalObj->SetDisabled(@_);
1111 }
1112
1113 sub Disabled {
1114     my $self = shift;
1115     return $self->PrincipalObj->Disabled(@_);
1116 }
1117
1118
1119 # {{{ Principal related routines
1120
1121 =head2 PrincipalObj 
1122
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.
1126
1127 =begin testing
1128
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");
1133
1134 =end testing
1135
1136 =cut
1137
1138
1139 sub PrincipalObj {
1140     my $self = shift;
1141     unless ($self->{'PrincipalObj'} && 
1142             ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
1143             ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
1144
1145             $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
1146             $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
1147                                                 'PrincipalType' => 'User') ;
1148             }
1149     return($self->{'PrincipalObj'});
1150 }
1151
1152
1153 =head2 PrincipalId  
1154
1155 Returns this user's PrincipalId
1156
1157 =cut
1158
1159 sub PrincipalId {
1160     my $self = shift;
1161     return $self->Id;
1162 }
1163
1164 # }}}
1165
1166
1167
1168 # {{{ sub HasGroupRight
1169
1170 =head2 HasGroupRight
1171
1172 Takes a paramhash which can contain
1173 these items:
1174     GroupObj => RT::Group or Group => integer
1175     Right => 'Right' 
1176
1177
1178 Returns 1 if this user has the right specified in the paramhash for the Group
1179 passed in.
1180
1181 Returns undef if they don't.
1182
1183 =cut
1184
1185 sub HasGroupRight {
1186     my $self = shift;
1187     my %args = (
1188         GroupObj    => undef,
1189         Group       => undef,
1190         Right       => undef,
1191         @_
1192     );
1193
1194
1195     if ( defined $args{'Group'} ) {
1196         $args{'GroupObj'} = RT::Group->new( $self->CurrentUser );
1197         $args{'GroupObj'}->Load( $args{'Group'} );
1198     }
1199
1200     # {{{ Validate and load up the GroupId
1201     unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
1202         return undef;
1203     }
1204
1205     # }}}
1206
1207
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'},
1212     );
1213
1214     return ($retval);
1215
1216
1217 }
1218
1219 # }}}
1220
1221 # {{{ sub Rights testing
1222
1223 =head2 Rights testing
1224
1225
1226 =begin testing
1227
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);
1232 $rootq->Load(1);
1233 ok($rootq->Id, "Loaded the first queue");
1234
1235 ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
1236
1237 my $new_user = RT::User->new($RT::SystemUser);
1238 my ($id, $msg) = $new_user->Create(Name => 'ACLTest');
1239
1240 ok ($id, "Created a new user for acl test $msg");
1241
1242 my $q = RT::Queue->new($new_user);
1243 $q->Load(1);
1244 ok($q->Id, "Loaded the first queue");
1245
1246
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");
1250
1251
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");
1256
1257
1258
1259
1260
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");
1279
1280
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");
1286
1287
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");
1291
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");
1297
1298
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");
1301
1302 # Create a subgroup
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
1310
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");
1315
1316 #  {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
1317
1318 my ($id, $msg);
1319  ($id, $msg) =  $group->SetDisabled(1);
1320  ok ($id,$msg);
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);
1323 ok($id,$msg);
1324 # Test what happens when we disable the group the user is a member of directly
1325
1326 ($id, $msg) =  $subgroup->SetDisabled(1);
1327  ok ($id,$msg);
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);
1330  ok ($id,$msg);
1331 ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
1332
1333 # }}}
1334
1335
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");
1340
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");
1344
1345 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
1346
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");
1350
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");
1354
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");
1359
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");
1362
1363 # }}}
1364
1365 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1366
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");
1370
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");
1373
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");
1376
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");
1379
1380
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");
1384
1385 # }}}
1386
1387
1388
1389 # {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
1390
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");
1394
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");
1398
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");
1402
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");
1406
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");
1412
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");
1416
1417 # }}}
1418
1419 # {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
1420
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");
1423
1424
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");
1428
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");
1432
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");
1435
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");
1439
1440
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");
1444
1445 # }}}
1446
1447
1448
1449
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
1455
1456 =end testing
1457
1458 =cut
1459
1460 # }}}
1461
1462
1463 # {{{ sub HasRight
1464
1465 =head2 sub HasRight
1466
1467 Shim around PrincipalObj->HasRight. See RT::Principal
1468
1469 =cut
1470
1471 sub HasRight {
1472
1473     my $self = shift;
1474     return $self->PrincipalObj->HasRight(@_);
1475 }
1476
1477 # }}}
1478
1479 # {{{ sub CurrentUserCanModify
1480
1481 =head2 CurrentUserCanModify RIGHT
1482
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.
1486
1487 =cut
1488
1489 sub CurrentUserCanModify {
1490     my $self  = shift;
1491     my $right = shift;
1492
1493     if ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
1494         return (1);
1495     }
1496
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' ) ) {
1500         return (undef);
1501     }
1502
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) ) )
1506     {
1507         return (1);
1508     }
1509
1510     #If we don\'t have a good reason to grant them rights to modify
1511     # by now, they lose
1512     else {
1513         return (undef);
1514     }
1515
1516 }
1517
1518 # }}}
1519
1520 # {{{ sub CurrentUserHasRight
1521
1522 =head2 CurrentUserHasRight
1523   
1524   Takes a single argument. returns 1 if $Self->CurrentUser
1525   has the requested right. returns undef otherwise
1526
1527 =cut
1528
1529 sub CurrentUserHasRight {
1530     my $self  = shift;
1531     my $right = shift;
1532
1533     return ( $self->CurrentUser->HasRight(Right => $right, Object => $RT::System) );
1534 }
1535
1536 # }}}
1537
1538 # {{{ sub _Set
1539
1540 sub _Set {
1541     my $self = shift;
1542
1543     my %args = (
1544         Field => undef,
1545         Value => undef,
1546         @_
1547     );
1548
1549     # Nobody is allowed to futz with RT_System or Nobody 
1550
1551     if ( ($self->Id == $RT::SystemUser->Id )  || 
1552          ($self->Id == $RT::Nobody->Id)) {
1553         return ( 0, $self->loc("Can not modify system users") );
1554     }
1555     unless ( $self->CurrentUserCanModify( $args{'Field'} ) ) {
1556         return ( 0, $self->loc("Permission Denied") );
1557     }
1558
1559     #Set the new value
1560     my ( $ret, $msg ) = $self->SUPER::_Set(
1561         Field => $args{'Field'},
1562         Value => $args{'Value'}
1563     );
1564
1565     return ( $ret, $msg );
1566 }
1567
1568 # }}}
1569
1570 # {{{ sub _Value 
1571
1572 =head2 _Value
1573
1574 Takes the name of a table column.
1575 Returns its value as a string, if the user passes an ACL check
1576
1577 =cut
1578
1579 sub _Value {
1580
1581     my $self  = shift;
1582     my $field = shift;
1583
1584     #If the current user doesn't have ACLs, don't let em at it.  
1585
1586     my @PublicFields = qw( Name EmailAddress Organization Disabled
1587       RealName NickName Gecos ExternalAuthId
1588       AuthSystem ExternalContactInfoId
1589       ContactInfoSystem );
1590
1591     #if the field is public, return it.
1592     if ( $self->_Accessible( $field, 'public' ) ) {
1593         return ( $self->SUPER::_Value($field) );
1594
1595     }
1596
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) );
1601     }
1602
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) );
1606     }
1607     else {
1608         return (undef);
1609     }
1610
1611 }
1612
1613 # }}}
1614
1615
1616 1;
1617
1618