import rt 2.0.14
[freeside.git] / rt / lib / RT / ACE.pm
1 #$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/ACE.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
2
3 =head1 NAME
4
5   RT::ACE - RT\'s ACE object
6
7 =head1 SYNOPSIS
8
9   use RT::ACE;
10   my $ace = new RT::ACE($CurrentUser);
11
12
13 =head1 DESCRIPTION
14
15
16 =head1 METHODS
17
18 =begin testing
19
20 ok(require RT::TestHarness);
21 ok(require RT::ACE);
22
23 =end testing
24
25 =cut
26
27 package RT::ACE;
28 use RT::Record;
29 @ISA= qw(RT::Record);
30
31 use vars qw (%SCOPES
32              %QUEUERIGHTS
33              %SYSTEMRIGHTS
34              %LOWERCASERIGHTNAMES
35             ); 
36
37 %SCOPES = (
38            System => 'System-level right',
39            Queue => 'Queue-level right'
40           );
41
42 # {{{ Descriptions of rights
43
44 # Queue rights are the sort of queue rights that can only be granted
45 # to real people or groups
46 %QUEUERIGHTS = ( 
47                 SeeQueue => 'Can this principal see this queue',
48                 AdminQueue => 'Create, delete and modify queues', 
49                 ShowACL => 'Display Access Control List',
50                 ModifyACL => 'Modify Access Control List',
51                 ModifyQueueWatchers => 'Modify the queue watchers',
52                 AdminKeywordSelects => 'Create, delete and modify keyword selections',
53
54                 
55                 ModifyTemplate => 'Modify email templates for this queue',
56                 ShowTemplate => 'Display email templates for this queue',
57                 ModifyScrips => 'Modify Scrips for this queue',
58                 ShowScrips => 'Display Scrips for this queue',
59
60                 ShowTicket => 'Show ticket summaries',
61                 ShowTicketComments => 'Show ticket private commentary',
62
63                 Watch => 'Sign up as a ticket Requestor or ticket or queue Cc',
64                 WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc',
65                 CreateTicket => 'Create tickets in this queue',
66                 ReplyToTicket => 'Reply to tickets',
67                 CommentOnTicket => 'Comment on tickets',
68                 OwnTicket => 'Own tickets',
69                 ModifyTicket => 'Modify tickets',
70                 DeleteTicket => 'Delete tickets'
71
72                );       
73
74
75 # System rights are rights granted to the whole system
76 %SYSTEMRIGHTS = (
77                 SuperUser => 'Do anything and everything',
78                 AdminKeywords => 'Creatte, delete and modify keywords',  
79                 AdminGroups => 'Create, delete and modify groups',
80                 AdminUsers => 'Create, Delete and Modify users',
81                 ModifySelf => 'Modify one\'s own RT account',
82
83                 );
84
85 # }}}
86
87 # {{{ Descriptions of principals
88
89 %TICKET_METAPRINCIPALS = ( Owner => 'The owner of a ticket',
90                                    Requestor => 'The requestor of a ticket',
91                                    Cc => 'The CC of a ticket',
92                                        AdminCc => 'The administrative CC of a ticket',
93                          );
94
95 # }}}
96
97 # {{{ We need to build a hash of all rights, keyed by lower case names
98
99 #since you can't do case insensitive hash lookups
100
101 foreach $right (keys %QUEUERIGHTS) {
102     $LOWERCASERIGHTNAMES{lc $right}=$right;
103 }
104 foreach $right (keys %SYSTEMRIGHTS) {
105     $LOWERCASERIGHTNAMES{lc $right}=$right;
106 }
107
108 # }}}
109
110 # {{{ sub _Init
111 sub _Init  {
112   my $self = shift;
113   $self->{'table'} = "ACL";
114   return($self->SUPER::_Init(@_));
115 }
116 # }}}
117
118 # {{{ sub LoadByValues
119
120 =head2 LoadByValues PARAMHASH
121
122 Load an ACE by specifying a paramhash with the following fields:
123
124               PrincipalId => undef,
125               PrincipalType => undef,
126               RightName => undef,
127               RightScope => undef,
128               RightAppliesTo => undef,
129
130 =cut
131
132 sub LoadByValues {
133   my $self = shift;
134   my %args = (PrincipalId => undef,
135               PrincipalType => undef,
136               RightName => undef,
137               RightScope => undef,
138               RightAppliesTo => undef,
139               @_);
140   
141   $self->LoadByCols (PrincipalId => $args{'PrincipalId'},
142                      PrincipalType => $args{'PrincipalType'},
143                      RightName => $args{'RightName'},
144                      RightScope => $args{'RightScope'},
145                      RightAppliesTo => $args{'RightAppliesTo'}
146                     );
147   
148   #If we couldn't load it.
149   unless ($self->Id) {
150       return (0, "ACE not found");
151   }
152   # if we could
153   return ($self->Id, "ACE Loaded");
154   
155 }
156
157 # }}}
158
159 # {{{ sub Create
160
161 =head2 Create <PARAMS>
162
163 PARAMS is a parameter hash with the following elements:
164
165    PrincipalType => "Queue"|"User"
166    PrincipalId => an intentifier you can use to ->Load a user or group
167    RightName => the name of a right. in any case
168    RightScope => "System" | "Queue"
169    RightAppliesTo => a queue id or undef
170
171 =cut
172
173 sub Create {
174     my $self = shift;
175     my %args = ( PrincipalId => undef,
176                  PrincipalType => undef,
177                  RightName => undef,
178                  RightScope => undef,
179                  RightAppliesTo => undef,
180                  @_
181                );
182     
183     # {{{ Validate the principal
184     my ($princ_obj);
185     if ($args{'PrincipalType'} eq 'User') {
186         $princ_obj = new RT::User($RT::SystemUser);
187         
188     }   
189     elsif ($args{'PrincipalType'} eq 'Group') {
190         require RT::Group;
191         $princ_obj = new RT::Group($RT::SystemUser);
192     }
193     else {
194         return (0, 'Principal type '.$args{'PrincipalType'} . ' is invalid.');
195     }   
196     
197     $princ_obj->Load($args{'PrincipalId'});
198     my $princ_id = $princ_obj->Id();
199     
200     unless ($princ_id) {
201         return (0, 'Principal '.$args{'PrincipalId'}.' not found.');
202     }
203
204     # }}}
205     
206     #TODO allow loading of queues by name.    
207     
208     # {{{ Check the ACL
209     if ($args{'RightScope'} eq 'System') {
210         
211         unless ($self->CurrentUserHasSystemRight('ModifyACL')) {
212             $RT::Logger->error("Permission Denied.");
213             return(undef);
214         }
215     }
216     
217     elsif ($args{'RightScope'} eq 'Queue') {
218         unless ($self->CurrentUserHasQueueRight( Queue => $args{'RightAppliesTo'},
219                                                  Right => 'ModifyACL')) {
220             return (0, 'Permission Denied.');
221         }
222         
223         
224         
225         
226     }
227     #If it's not a scope we recognise, something scary is happening.
228     else {
229         $RT::Logger->err("RT::ACE->Create got a scope it didn't recognize: ".
230                          $args{'RightScope'}." Bailing. \n");
231         return(0,"System error. Unable to grant rights.");
232     }
233
234     # }}}
235
236     # {{{ Canonicalize and check the right name
237     $args{'RightName'} = $self->CanonicalizeRightName($args{'RightName'});
238     
239     #check if it's a valid RightName
240     if ($args{'RightScope'} eq 'Queue') {
241         unless (exists $QUEUERIGHTS{$args{'RightName'}}) {
242             return(0, 'Invalid right');
243         }       
244         }       
245     elsif ($args{'RightScope' eq 'System'}) {
246         unless (exists $SYSTEMRIGHTS{$args{'RightName'}}) {
247             return(0, 'Invalid right');
248         }                   
249     }   
250     # }}}
251     
252     # Make sure the right doesn't already exist.
253     $self->LoadByCols (PrincipalId => $princ_id,
254                        PrincipalType => $args{'PrincipalType'},
255                        RightName => $args{'RightName'},
256                        RightScope => $args {'RightScope'},
257                        RightAppliesTo => $args{'RightAppliesTo'}
258                       );
259     if ($self->Id) {
260         return (0, 'That user already has that right');
261     }   
262
263     my $id = $self->SUPER::Create( PrincipalId => $princ_id,
264                                    PrincipalType => $args{'PrincipalType'},
265                                    RightName => $args{'RightName'},
266                                    RightScope => $args {'RightScope'},
267                                    RightAppliesTo => $args{'RightAppliesTo'}
268                                  );
269     
270     
271     if ($id > 0 ) {
272         return ($id, 'Right Granted');
273     }
274     else {
275         $RT::Logger->err('System error. right not granted.');
276         return(0, 'System Error. right not granted');
277     }
278 }
279
280 # }}}
281
282
283 # {{{ sub Delete 
284
285 =head2 Delete
286
287 Delete this object.
288
289 =cut
290
291 sub Delete {
292     my $self = shift;
293     
294     unless ($self->CurrentUserHasRight('ModifyACL')) {
295         return (0, 'Permission Denied');
296     }   
297     
298     
299     my ($val,$msg) = $self->SUPER::Delete(@_);
300     if ($val) {
301         return ($val, 'ACE Deleted');
302     }   
303     else {
304         return (0, 'ACE could not be deleted');
305     }
306 }
307
308 # }}}
309
310 # {{{ sub _BootstrapRight 
311
312 =head2 _BootstrapRight
313
314 Grant a right with no error checking and no ACL. this is _only_ for 
315 installation. If you use this routine without jesse@fsck.com's explicit 
316 written approval, he will hunt you down and make you spend eternity
317 translating mozilla's code into FORTRAN or intercal.
318
319 =cut
320
321 sub _BootstrapRight {
322     my $self = shift;
323     my %args = @_;
324
325     my $id = $self->SUPER::Create( PrincipalId => $args{'PrincipalId'},
326                                    PrincipalType => $args{'PrincipalType'},
327                                    RightName => $args{'RightName'},
328                                    RightScope => $args {'RightScope'},
329                                    RightAppliesTo => $args{'RightAppliesTo'}
330                                  );
331     
332     if ($id > 0 ) {
333         return ($id);
334     }
335     else {
336         $RT::Logger->err('System error. right not granted.');
337         return(undef);
338     }
339     
340 }
341
342 # }}}
343
344 # {{{ sub CanonicalizeRightName
345
346 =head2 CanonicalizeRightName <RIGHT>
347
348 Takes a queue or system right name in any case and returns it in
349 the correct case. If it's not found, will return undef.
350
351 =cut
352
353 sub CanonicalizeRightName {
354     my $self = shift;
355     my $right = shift;
356     $right = lc $right;
357     if (exists $LOWERCASERIGHTNAMES{"$right"}) {
358         return ($LOWERCASERIGHTNAMES{"$right"});
359     }
360     else {
361         return (undef);
362     }
363 }
364
365 # }}}
366
367 # {{{ sub QueueRights
368
369 =head2 QueueRights
370
371 Returns a hash of all the possible rights at the queue scope
372
373 =cut
374
375 sub QueueRights {
376         return (%QUEUERIGHTS);
377 }
378
379 # }}}
380
381 # {{{ sub SystemRights
382
383 =head2 SystemRights
384
385 Returns a hash of all the possible rights at the system scope
386
387 =cut
388
389 sub SystemRights {
390         return (%SYSTEMRIGHTS);
391 }
392
393
394 # }}}
395
396 # {{{ sub _Accessible 
397
398 sub _Accessible  {
399   my $self = shift;  
400   my %Cols = (
401               PrincipalId => 'read/write',
402               PrincipalType => 'read/write',
403               RightName => 'read/write', 
404               RightScope => 'read/write',
405               RightAppliesTo => 'read/write'
406             );
407   return($self->SUPER::_Accessible(@_, %Cols));
408 }
409 # }}}
410
411 # {{{ sub AppliesToObj
412
413 =head2 AppliesToObj
414
415 If the AppliesTo is a queue, returns the queue object. If it's 
416 the system object, returns undef. If the user has no rights, returns undef.
417
418 =cut
419
420 sub AppliesToObj {
421     my $self = shift;
422     if ($self->RightScope eq 'Queue') {
423         my $appliesto_obj = new RT::Queue($self->CurrentUser);
424         $appliesto_obj->Load($self->RightAppliesTo);
425         return($appliesto_obj);
426     }
427     elsif ($self->RightScope eq 'System') {
428         return (undef);
429     }   
430     else {
431         $RT::Logger->warning("$self -> AppliesToObj called for an object ".
432                              "of an unknown scope:" . $self->RightScope);
433         return(undef);
434     }
435 }       
436
437 # }}}
438
439 # {{{ sub PrincipalObj
440
441 =head2 PrincipalObj
442
443 If the AppliesTo is a group, returns the group object.
444 If the AppliesTo is a user, returns the user object.
445 Otherwise, it logs a warning and returns undef.
446
447 =cut
448
449 sub PrincipalObj {
450     my $self = shift;
451     my ($princ_obj);
452
453     if ($self->PrincipalType eq 'Group') {
454         use RT::Group;
455         $princ_obj = new RT::Group($self->CurrentUser);
456     }
457     elsif ($self->PrincipalType eq 'User') {
458         $princ_obj = new RT::User($self->CurrentUser);
459     }
460     else {
461         $RT::Logger->warning("$self -> PrincipalObj called for an object ".
462                              "of an unknown principal type:" . 
463                              $self->PrincipalType ."\n");
464         return(undef);
465     }
466     
467     $princ_obj->Load($self->PrincipalId);
468     return($princ_obj);
469
470 }       
471
472 # }}}
473
474 # {{{ ACL related methods
475
476 # {{{ sub _Set
477
478 sub _Set {
479   my $self = shift;
480   return (0, "ACEs can only be created and deleted.");
481 }
482
483 # }}}
484
485 # {{{ sub _Value
486
487 sub _Value {
488     my $self = shift;
489
490     unless ($self->CurrentUserHasRight('ShowACL')) {
491         return (undef);
492     }
493
494     return ($self->__Value(@_));
495 }
496
497 # }}}
498
499
500 # {{{ sub CurrentUserHasQueueRight 
501
502 =head2 CurrentUserHasQueueRight ( Queue => QUEUEID, Right => RIGHTNANAME )
503
504 Check to see whether the current user has the specified right for the specified queue.
505
506 =cut
507
508 sub CurrentUserHasQueueRight {
509     my $self = shift;
510     my %args = (Queue => undef,
511                 Right => undef,
512                 @_
513                 );
514     return ($self->HasRight( Right => $args{'Right'},
515                              Principal => $self->CurrentUser->UserObj,
516                              Queue => $args{'Queue'}));
517 }
518
519 # }}}
520
521 # {{{ sub CurrentUserHasSystemRight 
522 =head2 CurrentUserHasSystemRight RIGHTNAME
523
524 Check to see whether the current user has the specified right for the 'system' scope.
525
526 =cut
527
528 sub CurrentUserHasSystemRight {
529     my $self = shift;
530     my $right = shift;
531     return ($self->HasRight( Right => $right,
532                              Principal => $self->CurrentUser->UserObj,
533                              System => 1
534                            ));
535 }
536
537
538 # }}}
539
540 # {{{ sub CurrentUserHasRight
541
542 =item CurrentUserHasRight RIGHT 
543 Takes a rightname as a string.
544
545 Helper menthod for HasRight. Presets Principal to CurrentUser then 
546 calls HasRight.
547
548 =cut
549
550 sub CurrentUserHasRight {
551     my $self = shift;
552     my $right = shift;
553     return ($self->HasRight( Principal => $self->CurrentUser->UserObj,
554                              Right => $right,
555                            ));
556 }
557
558 # }}}
559
560 # {{{ sub HasRight
561
562 =item HasRight
563
564 Takes a param-hash consisting of "Right" and "Principal"  Principal is 
565 an RT::User object or an RT::CurrentUser object. "Right" is a textual
566 Right string that applies to KeywordSelects
567
568 =cut
569
570 sub HasRight {
571     my $self = shift;
572     my %args = ( Right => undef,
573                  Principal => undef,
574                  Queue => undef,
575                  System => undef,
576                  @_ ); 
577
578     #If we're explicitly specifying a queue, as we need to do on create
579     if (defined $args{'Queue'}) {
580         return ($args{'Principal'}->HasQueueRight(Right => $args{'Right'},
581                                                   Queue => $args{'Queue'}));
582     }
583     #else if we're specifying to check a system right
584     elsif ((defined $args{'System'}) and (defined $args{'Right'})) {
585         return( $args{'Principal'}->HasSystemRight( $args{'Right'} ));
586     }   
587     
588     elsif ($self->__Value('RightScope') eq 'System') {
589         return $args{'Principal'}->HasSystemRight($args{'Right'});
590     }
591     elsif ($self->__Value('RightScope') eq 'Queue') {
592         return $args{'Principal'}->HasQueueRight( Queue => $self->__Value('RightAppliesTo'),
593                                                   Right => $args{'Right'} );
594     }   
595     else {
596         $RT::Logger->warning("$self: Trying to check an acl for a scope we ".
597                              "don't understand:" . $self->__Value('RightScope') ."\n");
598         return undef;
599     }
600
601
602
603 }
604 # }}}
605
606 # }}}
607
608 1;
609
610 __DATA__
611
612 # {{{ POD
613
614 =head1 Out of date docs
615
616 =head2 Table Structure
617
618 PrincipalType, PrincipalId, Right,Scope,AppliesTo
619
620 =head1 The docs are out of date. so you know.
621
622 =head1 Scopes
623
624 Scope is the scope of the right granted, not the granularity of the grant.
625 For example, Queue and Ticket rights are both granted for a "queue." 
626 Rights with a scope of 'System' don't have an AppliesTo. (They're global).
627 Rights with a scope of "Queue" are rights that act on a queue.
628 Rights with a scope of "System" are rights that act on some other aspect
629 of the system.
630
631
632 =item Queue
633 =item System
634
635
636 =head1 Rights
637
638 =head2 Scope: Queue
639
640 =head2 Queue rights that apply to a ticket within a queue
641
642 Create Ticket in <queue>
643
644         Name: Create
645         Principals: <user> <group>
646 Display Ticket Summary in <queue>
647
648         Name: Show
649         Principals: <user> <group> Owner Requestor Cc AdminCc
650
651 Display Ticket History  <queue>
652
653         Name: ShowHistory
654         Principals: <user> <group> Owner Requestor Cc AdminCc
655
656 Display Ticket Private Comments  <queue>
657
658         Name: ShowComments
659         Principals: <user> <group> Owner Requestor Cc AdminCc
660
661 Reply to Ticket in <queue>
662
663         Name: Reply
664         Principals: <user> <group> Owner Requestor Cc AdminCc
665
666 Comment on Ticket in <queue>
667
668         Name: Comment
669         Principals: <user> <group> Owner Requestor Cc AdminCc
670
671 Modify Ticket in <queue>
672
673         Name: Modify
674         Principals: <user> <group> Owner Requestor Cc AdminCc
675
676 Delete Tickets in <queue>
677
678         Name: Delete
679         Principals: <user> <group> Owner Requestor Cc AdminCc
680
681
682 =head2 Queue Rights that apply to a whole queue
683
684 These rights can only be granted to "real people"
685
686 List Tickets in <queue>
687
688         Name: ListQueue
689         Principals: <user> <group>
690
691 Know that <queue> exists
692     
693     Name: See
694     Principals: <user> <group>
695
696 Display queue settings
697
698     Name: Explore
699     Principals: <user> <group>
700
701 Modify Queue Watchers for <queue>
702
703         Name: ModifyQueueWatchers
704         Principals: <user> <group>
705
706 Modify Queue Attributes for <queue> 
707
708         Name: ModifyQueue
709         Principals: <user> <group>
710
711 Modify Queue ACL for queue <queue>
712
713         Name: ModifyACL
714         Principals: <user> <group>
715
716
717 =head2 Rights that apply to the System scope
718
719 =head2 SystemRights
720
721 Create Queue
722   
723         Name: CreateQueue
724         Principals: <user> <group>
725 Delete Queue
726   
727         Name: DeleteQueue
728         Principals: <user> <group>
729
730 Create Users
731   
732         Name: CreateUser
733         Principals: <user> <group>
734
735 Delete Users
736   
737         Name: DeleteUser
738         Principals: <user> <group>
739   
740 Modify Users
741   
742         Name: ModifyUser
743         Principals: <user> <group>
744
745 Modify Self
746         Name: ModifySelf
747         Principals: <user> <group>
748
749 Browse Users
750
751         Name: BrowseUsers (NOT IMPLEMENTED in 2.0)
752         Principals: <user> <group>
753
754 Modify Self
755                     
756         Name: ModifySelf
757         Principals: <user> <group>
758
759 Modify System ACL
760
761         Name: ModifyACL           
762         Principals: <user> <group>
763
764 =head1 The Principal Side of the ACE
765
766 =head2 PrincipalTypes,PrincipalIds in our Neighborhood
767
768   User,<userid>
769   Group,<groupip>
770   Everyone,NULL
771
772 =cut
773
774 # }}}