rt 4.0.23
[freeside.git] / rt / lib / RT / ACE.pm
index d4681cf..83e19ce 100755 (executable)
-#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/ACE.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $
-
-=head1 NAME
-
-  RT::ACE - RT\'s ACE object
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
+#                                          <sales@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
 
 =head1 SYNOPSIS
 
   use RT::ACE;
 
 =head1 SYNOPSIS
 
   use RT::ACE;
-  my $ace = new RT::ACE($CurrentUser);
+  my $ace = RT::ACE->new($CurrentUser);
 
 
 =head1 DESCRIPTION
 
 
 
 
 =head1 DESCRIPTION
 
 
-=head1 METHODS
-
-=begin testing
 
 
-ok(require RT::TestHarness);
-ok(require RT::ACE);
+=head1 METHODS
 
 
-=end testing
 
 =cut
 
 
 =cut
 
+
 package RT::ACE;
 package RT::ACE;
-use RT::Record;
-@ISA= qw(RT::Record);
+use base 'RT::Record';
+
+sub Table {'ACL'}
+
+
+use strict;
+use warnings;
+
+use RT::Principals;
+use RT::Queues;
+use RT::Groups;
+
+use vars qw (
+  %LOWERCASERIGHTNAMES
+  %OBJECT_TYPES
+  %TICKET_METAPRINCIPALS
+);
 
 
-use vars qw (%SCOPES
-            %QUEUERIGHTS
-            %SYSTEMRIGHTS
-            %LOWERCASERIGHTNAMES
-           ); 
 
 
-%SCOPES = (
-          System => 'System-level right',
-          Queue => 'Queue-level right'
-         );
 
 
-# {{{ Descriptions of rights
+=head1 Rights
 
 # Queue rights are the sort of queue rights that can only be granted
 # to real people or groups
 
 # Queue rights are the sort of queue rights that can only be granted
 # to real people or groups
-%QUEUERIGHTS = ( 
-               SeeQueue => 'Can this principal see this queue',
-               AdminQueue => 'Create, delete and modify queues', 
-               ShowACL => 'Display Access Control List',
-               ModifyACL => 'Modify Access Control List',
-               ModifyQueueWatchers => 'Modify the queue watchers',
-                AdminKeywordSelects => 'Create, delete and modify keyword selections',
-
-               
-               ModifyTemplate => 'Modify email templates for this queue',
-               ShowTemplate => 'Display email templates for this queue',
-               ModifyScrips => 'Modify Scrips for this queue',
-               ShowScrips => 'Display Scrips for this queue',
-
-               ShowTicket => 'Show ticket summaries',
-               ShowTicketComments => 'Show ticket private commentary',
-
-               Watch => 'Sign up as a ticket Requestor or ticket or queue Cc',
-               WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc',
-               CreateTicket => 'Create tickets in this queue',
-               ReplyToTicket => 'Reply to tickets',
-               CommentOnTicket => 'Comment on tickets',
-               OwnTicket => 'Own tickets',
-               ModifyTicket => 'Modify tickets',
-               DeleteTicket => 'Delete tickets'
-
-              );       
-
-
-# System rights are rights granted to the whole system
-%SYSTEMRIGHTS = (
-                SuperUser => 'Do anything and everything',
-               AdminKeywords => 'Creatte, delete and modify keywords',  
-               AdminGroups => 'Create, delete and modify groups',
-               AdminUsers => 'Create, Delete and Modify users',
-               ModifySelf => 'Modify one\'s own RT account',
-
-               );
 
 
-# }}}
+=cut
 
 
-# {{{ Descriptions of principals
 
 
-%TICKET_METAPRINCIPALS = ( Owner => 'The owner of a ticket',
-                                  Requestor => 'The requestor of a ticket',
-                                  Cc => 'The CC of a ticket',
-                                      AdminCc => 'The administrative CC of a ticket',
-                        );
 
 
-# }}}
 
 
-# {{{ We need to build a hash of all rights, keyed by lower case names
 
 
-#since you can't do case insensitive hash lookups
 
 
-foreach $right (keys %QUEUERIGHTS) {
-    $LOWERCASERIGHTNAMES{lc $right}=$right;
-}
-foreach $right (keys %SYSTEMRIGHTS) {
-    $LOWERCASERIGHTNAMES{lc $right}=$right;
-}
+%TICKET_METAPRINCIPALS = (
+    Owner     => 'The owner of a ticket',                             # loc_pair
+    Requestor => 'The requestor of a ticket',                         # loc_pair
+    Cc        => 'The CC of a ticket',                                # loc_pair
+    AdminCc   => 'The administrative CC of a ticket',                 # loc_pair
+);
 
 
-# }}}
 
 
-# {{{ sub _Init
-sub _Init  {
-  my $self = shift;
-  $self->{'table'} = "ACL";
-  return($self->SUPER::_Init(@_));
-}
-# }}}
 
 
-# {{{ sub LoadByValues
 
 =head2 LoadByValues PARAMHASH
 
 Load an ACE by specifying a paramhash with the following fields:
 
               PrincipalId => undef,
 
 =head2 LoadByValues PARAMHASH
 
 Load an ACE by specifying a paramhash with the following fields:
 
               PrincipalId => undef,
-             PrincipalType => undef,
+              PrincipalType => undef,
              RightName => undef,
              RightName => undef,
-             RightScope => undef,
-             RightAppliesTo => undef,
+
+        And either:
+
+             Object => undef,
+
+            OR
+
+             ObjectType => undef,
+             ObjectId => undef
 
 =cut
 
 sub LoadByValues {
 
 =cut
 
 sub LoadByValues {
-  my $self = shift;
-  my %args = (PrincipalId => undef,
-             PrincipalType => undef,
-             RightName => undef,
-             RightScope => undef,
-             RightAppliesTo => undef,
-             @_);
-  
-  $self->LoadByCols (PrincipalId => $args{'PrincipalId'},
-                    PrincipalType => $args{'PrincipalType'},
-                    RightName => $args{'RightName'},
-                    RightScope => $args{'RightScope'},
-                    RightAppliesTo => $args{'RightAppliesTo'}
-                   );
-  
-  #If we couldn't load it.
-  unless ($self->Id) {
-      return (0, "ACE not found");
-  }
-  # if we could
-  return ($self->Id, "ACE Loaded");
-  
+    my $self = shift;
+    my %args = ( PrincipalId   => undef,
+                 PrincipalType => undef,
+                 RightName     => undef,
+                 Object    => undef,
+                 ObjectId    => undef,
+                 ObjectType    => undef,
+                 @_ );
+
+    if ( $args{'RightName'} ) {
+        my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} );
+        unless ( $canonic_name ) {
+            return ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) );
+        }
+        $args{'RightName'} = $canonic_name;
+    }
+
+    my $princ_obj;
+    ( $princ_obj, $args{'PrincipalType'} ) =
+      $self->_CanonicalizePrincipal( $args{'PrincipalId'},
+                                     $args{'PrincipalType'} );
+
+    unless ( $princ_obj->id ) {
+        return ( 0,
+                 $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
+        );
+    }
+
+    my ($object, $object_type, $object_id) = $self->_ParseObjectArg( %args );
+    unless( $object ) {
+       return ( 0, $self->loc("System error. Right not granted.") );
+    }
+
+    $self->LoadByCols( PrincipalId   => $princ_obj->Id,
+                       PrincipalType => $args{'PrincipalType'},
+                       RightName     => $args{'RightName'},
+                       ObjectType    => $object_type,
+                       ObjectId      => $object_id);
+
+    #If we couldn't load it.
+    unless ( $self->Id ) {
+        return ( 0, $self->loc("ACE not found") );
+    }
+
+    # if we could
+    return ( $self->Id, $self->loc("Right Loaded") );
+
 }
 
 }
 
-# }}}
 
 
-# {{{ sub Create
 
 =head2 Create <PARAMS>
 
 PARAMS is a parameter hash with the following elements:
 
 
 =head2 Create <PARAMS>
 
 PARAMS is a parameter hash with the following elements:
 
-   PrincipalType => "Queue"|"User"
-   PrincipalId => an intentifier you can use to ->Load a user or group
+   PrincipalId => The id of an RT::Principal object
+   PrincipalType => "User" "Group" or any Role type
    RightName => the name of a right. in any case
    RightName => the name of a right. in any case
-   RightScope => "System" | "Queue"
-   RightAppliesTo => a queue id or undef
+
+
+    Either:
+
+   Object => An object to create rights for. ususally, an RT::Queue or RT::Group
+             This should always be a DBIx::SearchBuilder::Record subclass
+
+        OR
+
+   ObjectType => the type of the object in question (ref ($object))
+   ObjectId => the id of the object in question $object->Id
+
+
+
+   Returns a tuple of (STATUS, MESSAGE);  If the call succeeded, STATUS is true. Otherwise it's false.
+
+
 
 =cut
 
 sub Create {
     my $self = shift;
 
 =cut
 
 sub Create {
     my $self = shift;
-    my %args = ( PrincipalId => undef,
-                PrincipalType => undef,
-                RightName => undef,
-                RightScope => undef,
-                RightAppliesTo => undef,
-                @_
-              );
-    
-    # {{{ Validate the principal
-    my ($princ_obj);
-    if ($args{'PrincipalType'} eq 'User') {
-       $princ_obj = new RT::User($RT::SystemUser);
-       
-    }  
-    elsif ($args{'PrincipalType'} eq 'Group') {
-       require RT::Group;
-       $princ_obj = new RT::Group($RT::SystemUser);
+    my %args = (
+        PrincipalId   => undef,
+        PrincipalType => undef,
+        RightName     => undef,
+        Object        => undef,
+        @_
+    );
+
+    unless ( $args{'RightName'} ) {
+        return ( 0, $self->loc('No right specified') );
     }
     }
-    else {
-       return (0, 'Principal type '.$args{'PrincipalType'} . ' is invalid.');
-    }  
-    
-    $princ_obj->Load($args{'PrincipalId'});
-    my $princ_id = $princ_obj->Id();
-    
-    unless ($princ_id) {
-       return (0, 'Principal '.$args{'PrincipalId'}.' not found.');
+
+    #if we haven't specified any sort of right, we're talking about a global right
+    if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) {
+        $args{'Object'} = $RT::System;
+    }
+    ($args{'Object'}, $args{'ObjectType'}, $args{'ObjectId'}) = $self->_ParseObjectArg( %args );
+    unless( $args{'Object'} ) {
+       return ( 0, $self->loc("System error. Right not granted.") );
     }
 
     }
 
-    # }}}
-    
-    #TODO allow loading of queues by name.    
-    
-    # {{{ Check the ACL
-    if ($args{'RightScope'} eq 'System') {
-       
-       unless ($self->CurrentUserHasSystemRight('ModifyACL')) {
-           $RT::Logger->error("Permission Denied.");
-           return(undef);
-       }
+    # Validate the principal
+    my $princ_obj;
+    ( $princ_obj, $args{'PrincipalType'} ) =
+      $self->_CanonicalizePrincipal( $args{'PrincipalId'},
+                                     $args{'PrincipalType'} );
+
+    unless ( $princ_obj->id ) {
+        return ( 0,
+                 $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
+        );
     }
     }
-    
-    elsif ($args{'RightScope'} eq 'Queue') {
-       unless ($self->CurrentUserHasQueueRight( Queue => $args{'RightAppliesTo'},
-                                                Right => 'ModifyACL')) {
-           return (0, 'Permission Denied.');
-       }
-       
-       
-       
-       
+
+    # }}}
+
+    # Check the ACL
+
+    if (ref( $args{'Object'}) eq 'RT::Group' ) {
+        unless ( $self->CurrentUser->HasRight( Object => $args{'Object'},
+                                                  Right => 'AdminGroup' )
+          ) {
+            return ( 0, $self->loc('Permission Denied') );
+        }
     }
     }
-    #If it's not a scope we recognise, something scary is happening.
+
     else {
     else {
-       $RT::Logger->err("RT::ACE->Create got a scope it didn't recognize: ".
-                        $args{'RightScope'}." Bailing. \n");
-       return(0,"System error. Unable to grant rights.");
+        unless ( $self->CurrentUser->HasRight( Object => $args{'Object'}, Right => 'ModifyACL' )) {
+            return ( 0, $self->loc('Permission Denied') );
+        }
     }
     }
-
     # }}}
 
     # }}}
 
-    # {{{ Canonicalize and check the right name
-    $args{'RightName'} = $self->CanonicalizeRightName($args{'RightName'});
-    
+    # Canonicalize and check the right name
+    my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} );
+    unless ( $canonic_name ) {
+        return ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) );
+    }
+    $args{'RightName'} = $canonic_name;
+
     #check if it's a valid RightName
     #check if it's a valid RightName
-    if ($args{'RightScope'} eq 'Queue') {
-       unless (exists $QUEUERIGHTS{$args{'RightName'}}) {
-           return(0, 'Invalid right');
-       }       
-       }       
-    elsif ($args{'RightScope' eq 'System'}) {
-       unless (exists $SYSTEMRIGHTS{$args{'RightName'}}) {
-           return(0, 'Invalid right');
-       }                   
-    }  
+    if ( $args{'Object'}->can('AvailableRights') ) {
+        my $available = $args{'Object'}->AvailableRights;
+        unless ( grep $_ eq $args{'RightName'}, map $self->CanonicalizeRightName( $_ ), keys %$available ) {
+            $RT::Logger->warning(
+                "Couldn't validate right name '$args{'RightName'}'"
+                ." for object of ". ref( $args{'Object'} ) ." class"
+            );
+            return ( 0, $self->loc('Invalid right') );
+        }
+    }
     # }}}
     # }}}
-    
+
     # Make sure the right doesn't already exist.
     # Make sure the right doesn't already exist.
-    $self->LoadByCols (PrincipalId => $princ_id,
-                      PrincipalType => $args{'PrincipalType'},
-                      RightName => $args{'RightName'},
-                      RightScope => $args {'RightScope'},
-                      RightAppliesTo => $args{'RightAppliesTo'}
-                     );
-    if ($self->Id) {
-       return (0, 'That user already has that right');
-    }  
-
-    my $id = $self->SUPER::Create( PrincipalId => $princ_id,
-                                  PrincipalType => $args{'PrincipalType'},
-                                  RightName => $args{'RightName'},
-                                  RightScope => $args {'RightScope'},
-                                  RightAppliesTo => $args{'RightAppliesTo'}
-                                );
-    
-    
-    if ($id > 0 ) {
-       return ($id, 'Right Granted');
+    $self->LoadByCols( PrincipalId   => $princ_obj->id,
+                       PrincipalType => $args{'PrincipalType'},
+                       RightName     => $args{'RightName'},
+                       ObjectType    => $args{'ObjectType'},
+                       ObjectId      => $args{'ObjectId'},
+                   );
+    if ( $self->Id ) {
+        return ( 0, $self->loc('[_1] already has that right',
+                    $princ_obj->Object->Name) );
+    }
+
+    my $id = $self->SUPER::Create( PrincipalId   => $princ_obj->id,
+                                   PrincipalType => $args{'PrincipalType'},
+                                   RightName     => $args{'RightName'},
+                                   ObjectType    => ref( $args{'Object'} ),
+                                   ObjectId      => $args{'Object'}->id,
+                               );
+
+    #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space. 
+    RT::Principal->InvalidateACLCache();
+
+    if ( $id ) {
+        return ( $id, $self->loc('Right Granted') );
     }
     else {
     }
     else {
-       $RT::Logger->err('System error. right not granted.');
-       return(0, 'System Error. right not granted');
+        return ( 0, $self->loc('System error. Right not granted.') );
     }
 }
 
     }
 }
 
-# }}}
 
 
 
 
-# {{{ sub Delete 
+=head2 Delete { InsideTransaction => undef}
 
 
-=head2 Delete
+Delete this object. This method should ONLY ever be called from RT::User or RT::Group (or from itself)
+If this is being called from within a transaction, specify a true value for the parameter InsideTransaction.
+Really, DBIx::SearchBuilder should use and/or fake subtransactions
 
 
-Delete this object.
+This routine will also recurse and delete any delegations of this right
 
 =cut
 
 sub Delete {
     my $self = shift;
 
 =cut
 
 sub Delete {
     my $self = shift;
-    
-    unless ($self->CurrentUserHasRight('ModifyACL')) {
-       return (0, 'Permission Denied');
-    }  
-    
-    
-    my ($val,$msg) = $self->SUPER::Delete(@_);
+
+    unless ( $self->Id ) {
+        return ( 0, $self->loc('Right not loaded.') );
+    }
+
+    # A user can delete an ACE if the current user has the right to modify it and it's not a delegated ACE
+    # or if it's a delegated ACE and it was delegated by the current user
+    unless ($self->CurrentUser->HasRight(Right => 'ModifyACL', Object => $self->Object)) {
+        return ( 0, $self->loc('Permission Denied') );
+    }
+    $self->_Delete(@_);
+}
+
+# Helper for Delete with no ACL check
+sub _Delete {
+    my $self = shift;
+    my %args = ( InsideTransaction => undef,
+                 @_ );
+
+    my $InsideTransaction = $args{'InsideTransaction'};
+
+    $RT::Handle->BeginTransaction() unless $InsideTransaction;
+
+    my ( $val, $msg ) = $self->SUPER::Delete(@_);
+
     if ($val) {
     if ($val) {
-       return ($val, 'ACE Deleted');
-    }  
-    else {
-       return (0, 'ACE could not be deleted');
+       #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space. 
+       # TODO what about the groups key cache?
+       RT::Principal->InvalidateACLCache();
+        $RT::Handle->Commit() unless $InsideTransaction;
+        return ( $val, $self->loc('Right revoked') );
     }
     }
+
+    $RT::Handle->Rollback() unless $InsideTransaction;
+    return ( 0, $self->loc('Right could not be revoked') );
 }
 
 }
 
-# }}}
 
 
-# {{{ sub _BootstrapRight 
 
 
-=head2 _BootstrapRight
+=head2 _BootstrapCreate
 
 Grant a right with no error checking and no ACL. this is _only_ for 
 
 Grant a right with no error checking and no ACL. this is _only_ for 
-installation. If you use this routine without jesse@fsck.com's explicit 
+installation. If you use this routine without the author's explicit 
 written approval, he will hunt you down and make you spend eternity
 translating mozilla's code into FORTRAN or intercal.
 
 written approval, he will hunt you down and make you spend eternity
 translating mozilla's code into FORTRAN or intercal.
 
+If you think you need this routine, you've mistaken. 
+
 =cut
 
 =cut
 
-sub _BootstrapRight {
+sub _BootstrapCreate {
     my $self = shift;
     my $self = shift;
-    my %args = @_;
-
-    my $id = $self->SUPER::Create( PrincipalId => $args{'PrincipalId'},
-                                  PrincipalType => $args{'PrincipalType'},
-                                  RightName => $args{'RightName'},
-                                  RightScope => $args {'RightScope'},
-                                  RightAppliesTo => $args{'RightAppliesTo'}
-                                );
-    
-    if ($id > 0 ) {
-       return ($id);
+    my %args = (@_);
+
+    # When bootstrapping, make sure we get the _right_ users
+    if ( $args{'UserId'} ) {
+        my $user = RT::User->new( $self->CurrentUser );
+        $user->Load( $args{'UserId'} );
+        delete $args{'UserId'};
+        $args{'PrincipalId'}   = $user->PrincipalId;
+        $args{'PrincipalType'} = 'User';
+    }
+
+    my $id = $self->SUPER::Create(%args);
+
+    if ( $id > 0 ) {
+        return ($id);
     }
     else {
     }
     else {
-       $RT::Logger->err('System error. right not granted.');
-       return(undef);
+        $RT::Logger->err('System error. right not granted.');
+        return (undef);
     }
     }
-    
+
 }
 
 }
 
-# }}}
 
 
-# {{{ sub CanonicalizeRightName
+
+sub RightName {
+    my $self = shift;
+    my $val = $self->_Value('RightName');
+    return $val unless $val;
+
+    my $available = $self->Object->AvailableRights;
+    foreach my $right ( keys %$available ) {
+        return $right if $val eq $self->CanonicalizeRightName($right);
+    }
+
+    $RT::Logger->error("Invalid right. Couldn't canonicalize right '$val'");
+    return $val;
+}
 
 =head2 CanonicalizeRightName <RIGHT>
 
 
 =head2 CanonicalizeRightName <RIGHT>
 
@@ -351,424 +420,331 @@ the correct case. If it's not found, will return undef.
 =cut
 
 sub CanonicalizeRightName {
 =cut
 
 sub CanonicalizeRightName {
-    my $self = shift;
-    my $right = shift;
-    $right = lc $right;
-    if (exists $LOWERCASERIGHTNAMES{"$right"}) {
-       return ($LOWERCASERIGHTNAMES{"$right"});
-    }
-    else {
-       return (undef);
-    }
+    my $self  = shift;
+    return $LOWERCASERIGHTNAMES{ lc shift };
 }
 
 }
 
-# }}}
 
 
-# {{{ sub QueueRights
 
 
-=head2 QueueRights
 
 
-Returns a hash of all the possible rights at the queue scope
+=head2 Object
 
 
-=cut
+If the object this ACE applies to is a queue, returns the queue object. 
+If the object this ACE applies to is a group, returns the group object. 
+If it's the system object, returns undef. 
 
 
-sub QueueRights {
-        return (%QUEUERIGHTS);
-}
-
-# }}}
-
-# {{{ sub SystemRights
-
-=head2 SystemRights
-
-Returns a hash of all the possible rights at the system scope
+If the user has no rights, returns undef.
 
 =cut
 
 
 =cut
 
-sub SystemRights {
-       return (%SYSTEMRIGHTS);
-}
 
 
 
 
-# }}}
-
-# {{{ sub _Accessible 
-
-sub _Accessible  {
-  my $self = shift;  
-  my %Cols = (
-             PrincipalId => 'read/write',
-             PrincipalType => 'read/write',
-             RightName => 'read/write', 
-             RightScope => 'read/write',
-             RightAppliesTo => 'read/write'
-           );
-  return($self->SUPER::_Accessible(@_, %Cols));
-}
-# }}}
-
-# {{{ sub AppliesToObj
-
-=head2 AppliesToObj
 
 
-If the AppliesTo is a queue, returns the queue object. If it's 
-the system object, returns undef. If the user has no rights, returns undef.
+sub Object {
+    my $self = shift;
 
 
-=cut
+    my $appliesto_obj;
 
 
-sub AppliesToObj {
-    my $self = shift;
-    if ($self->RightScope eq 'Queue') {
-       my $appliesto_obj = new RT::Queue($self->CurrentUser);
-       $appliesto_obj->Load($self->RightAppliesTo);
-       return($appliesto_obj);
-    }
-    elsif ($self->RightScope eq 'System') {
-       return (undef);
-    }  
+    if ($self->__Value('ObjectType') && $OBJECT_TYPES{$self->__Value('ObjectType')} ) {
+        $appliesto_obj =  $self->__Value('ObjectType')->new($self->CurrentUser);
+        unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) {
+            return undef;
+        }
+        $appliesto_obj->Load( $self->__Value('ObjectId') );
+        return ($appliesto_obj);
+     }
     else {
     else {
-       $RT::Logger->warning("$self -> AppliesToObj called for an object ".
-                            "of an unknown scope:" . $self->RightScope);
-       return(undef);
+        $RT::Logger->warning( "$self -> Object called for an object "
+                              . "of an unknown type:"
+                              . $self->__Value('ObjectType') );
+        return (undef);
     }
     }
-}      
+}
 
 
-# }}}
 
 
-# {{{ sub PrincipalObj
 
 =head2 PrincipalObj
 
 
 =head2 PrincipalObj
 
-If the AppliesTo is a group, returns the group object.
-If the AppliesTo is a user, returns the user object.
-Otherwise, it logs a warning and returns undef.
+Returns the RT::Principal object for this ACE. 
 
 =cut
 
 sub PrincipalObj {
     my $self = shift;
 
 =cut
 
 sub PrincipalObj {
     my $self = shift;
-    my ($princ_obj);
 
 
-    if ($self->PrincipalType eq 'Group') {
-       use RT::Group;
-       $princ_obj = new RT::Group($self->CurrentUser);
-    }
-    elsif ($self->PrincipalType eq 'User') {
-       $princ_obj = new RT::User($self->CurrentUser);
-    }
-    else {
-       $RT::Logger->warning("$self -> PrincipalObj called for an object ".
-                            "of an unknown principal type:" . 
-                            $self->PrincipalType ."\n");
-       return(undef);
+    my $princ_obj = RT::Principal->new( $self->CurrentUser );
+    $princ_obj->Load( $self->__Value('PrincipalId') );
+
+    unless ( $princ_obj->Id ) {
+        $RT::Logger->err(
+                   "ACE " . $self->Id . " couldn't load its principal object" );
     }
     }
-    
-    $princ_obj->Load($self->PrincipalId);
-    return($princ_obj);
+    return ($princ_obj);
 
 
-}      
+}
 
 
-# }}}
 
 
-# {{{ ACL related methods
 
 
-# {{{ sub _Set
 
 sub _Set {
 
 sub _Set {
-  my $self = shift;
-  return (0, "ACEs can only be created and deleted.");
+    my $self = shift;
+    return ( 0, $self->loc("ACEs can only be created and deleted.") );
 }
 
 }
 
-# }}}
 
 
-# {{{ sub _Value
 
 sub _Value {
     my $self = shift;
 
 
 sub _Value {
     my $self = shift;
 
-    unless ($self->CurrentUserHasRight('ShowACL')) {
-       return (undef);
+    if ( $self->PrincipalObj->IsGroup
+            && $self->PrincipalObj->Object->HasMemberRecursively(
+                                                $self->CurrentUser->PrincipalObj
+            )
+      ) {
+        return ( $self->__Value(@_) );
+    }
+    elsif ( $self->CurrentUser->HasRight(Right => 'ShowACL', Object => $self->Object) ) {
+        return ( $self->__Value(@_) );
+    }
+    else {
+        return undef;
     }
     }
-
-    return ($self->__Value(@_));
 }
 
 }
 
-# }}}
 
 
 
 
-# {{{ sub CurrentUserHasQueueRight 
 
 
-=head2 CurrentUserHasQueueRight ( Queue => QUEUEID, Right => RIGHTNANAME )
 
 
-Check to see whether the current user has the specified right for the specified queue.
+=head2 _CanonicalizePrincipal (PrincipalId, PrincipalType)
+
+Takes a principal id and a principal type.
+
+If the principal is a user, resolves it to the proper acl equivalence group.
+Returns a tuple of  (RT::Principal, PrincipalType)  for the principal we really want to work with
 
 =cut
 
 
 =cut
 
-sub CurrentUserHasQueueRight {
-    my $self = shift;
-    my %args = (Queue => undef,
-               Right => undef,
-               @_
-               );
-    return ($self->HasRight( Right => $args{'Right'},
-                            Principal => $self->CurrentUser->UserObj,
-                            Queue => $args{'Queue'}));
-}
+sub _CanonicalizePrincipal {
+    my $self       = shift;
+    my $princ_id   = shift;
+    my $princ_type = shift || '';
 
 
-# }}}
+    my $princ_obj = RT::Principal->new(RT->SystemUser);
+    $princ_obj->Load($princ_id);
 
 
-# {{{ sub CurrentUserHasSystemRight 
-=head2 CurrentUserHasSystemRight RIGHTNAME
+    unless ( $princ_obj->Id ) {
+        use Carp;
+        $RT::Logger->crit(Carp::longmess);
+        $RT::Logger->crit("Can't load a principal for id $princ_id");
+        return ( $princ_obj, undef );
+    }
 
 
-Check to see whether the current user has the specified right for the 'system' scope.
+    # Rights never get granted to users. they get granted to their 
+    # ACL equivalence groups
+    if ( $princ_type eq 'User' ) {
+        my $equiv_group = RT::Group->new( $self->CurrentUser );
+        $equiv_group->LoadACLEquivalenceGroup($princ_obj);
+        unless ( $equiv_group->Id ) {
+            $RT::Logger->crit( "No ACL equiv group for princ " . $princ_obj->id );
+            return ( RT::Principal->new(RT->SystemUser), undef );
+        }
+        $princ_obj  = $equiv_group->PrincipalObj();
+        $princ_type = 'Group';
 
 
-=cut
+    }
+    return ( $princ_obj, $princ_type );
+}
 
 
-sub CurrentUserHasSystemRight {
+sub _ParseObjectArg {
     my $self = shift;
     my $self = shift;
-    my $right = shift;
-    return ($self->HasRight( Right => $right,
-                            Principal => $self->CurrentUser->UserObj,
-                            System => 1
-                          ));
+    my %args = ( Object    => undef,
+                 ObjectId    => undef,
+                 ObjectType    => undef,
+                 @_ );
+
+    if( $args{'Object'} && ($args{'ObjectId'} || $args{'ObjectType'}) ) {
+       $RT::Logger->crit( "Method called with an ObjectType or an ObjectId and Object args" );
+       return ();
+    } elsif( $args{'Object'} && ref($args{'Object'}) &&  !$args{'Object'}->can('id') ) {
+       $RT::Logger->crit( "Method called called Object that has no id method" );
+       return ();
+    } elsif( $args{'Object'} ) {
+       my $obj = $args{'Object'};
+       return ($obj, ref $obj, $obj->id);
+    } elsif ( $args{'ObjectType'} ) {
+       my $obj =  $args{'ObjectType'}->new( $self->CurrentUser );
+       $obj->Load( $args{'ObjectId'} );
+       return ($obj, ref $obj, $obj->id);
+    } else {
+       $RT::Logger->crit( "Method called with wrong args" );
+       return ();
+    }
 }
 
 
 # }}}
 
 }
 
 
 # }}}
 
-# {{{ sub CurrentUserHasRight
 
 
-=item CurrentUserHasRight RIGHT 
-Takes a rightname as a string.
 
 
-Helper menthod for HasRight. Presets Principal to CurrentUser then 
-calls HasRight.
+=head2 id
 
 
-=cut
+Returns the current value of id.
+(In the database, id is stored as int(11).)
 
 
-sub CurrentUserHasRight {
-    my $self = shift;
-    my $right = shift;
-    return ($self->HasRight( Principal => $self->CurrentUser->UserObj,
-                             Right => $right,
-                          ));
-}
 
 
-# }}}
+=cut
 
 
-# {{{ sub HasRight
 
 
-=item HasRight
+=head2 PrincipalType
 
 
-Takes a param-hash consisting of "Right" and "Principal"  Principal is 
-an RT::User object or an RT::CurrentUser object. "Right" is a textual
-Right string that applies to KeywordSelects
+Returns the current value of PrincipalType.
+(In the database, PrincipalType is stored as varchar(25).)
 
 
-=cut
 
 
-sub HasRight {
-    my $self = shift;
-    my %args = ( Right => undef,
-                 Principal => undef,
-                Queue => undef,
-                System => undef,
-                 @_ ); 
-
-    #If we're explicitly specifying a queue, as we need to do on create
-    if (defined $args{'Queue'}) {
-       return ($args{'Principal'}->HasQueueRight(Right => $args{'Right'},
-                                                 Queue => $args{'Queue'}));
-    }
-    #else if we're specifying to check a system right
-    elsif ((defined $args{'System'}) and (defined $args{'Right'})) {
-        return( $args{'Principal'}->HasSystemRight( $args{'Right'} ));
-    }  
-    
-    elsif ($self->__Value('RightScope') eq 'System') {
-       return $args{'Principal'}->HasSystemRight($args{'Right'});
-    }
-    elsif ($self->__Value('RightScope') eq 'Queue') {
-       return $args{'Principal'}->HasQueueRight( Queue => $self->__Value('RightAppliesTo'),
-                                                 Right => $args{'Right'} );
-    }  
-    else {
-       $RT::Logger->warning("$self: Trying to check an acl for a scope we ".
-                            "don't understand:" . $self->__Value('RightScope') ."\n");
-       return undef;
-    }
 
 
+=head2 SetPrincipalType VALUE
 
 
 
 
-}
-# }}}
+Set PrincipalType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PrincipalType will be stored as a varchar(25).)
 
 
-# }}}
 
 
-1;
+=cut
 
 
-__DATA__
 
 
-# {{{ POD
+=head2 PrincipalId
 
 
-=head1 Out of date docs
+Returns the current value of PrincipalId.
+(In the database, PrincipalId is stored as int(11).)
 
 
-=head2 Table Structure
 
 
-PrincipalType, PrincipalId, Right,Scope,AppliesTo
 
 
-=head1 The docs are out of date. so you know.
+=head2 SetPrincipalId VALUE
 
 
-=head1 Scopes
 
 
-Scope is the scope of the right granted, not the granularity of the grant.
-For example, Queue and Ticket rights are both granted for a "queue." 
-Rights with a scope of 'System' don't have an AppliesTo. (They're global).
-Rights with a scope of "Queue" are rights that act on a queue.
-Rights with a scope of "System" are rights that act on some other aspect
-of the system.
+Set PrincipalId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, PrincipalId will be stored as a int(11).)
 
 
 
 
-=item Queue
-=item System
+=cut
 
 
 
 
-=head1 Rights
+=head2 RightName
 
 
-=head2 Scope: Queue
+Returns the current value of RightName.
+(In the database, RightName is stored as varchar(25).)
 
 
-=head2 Queue rights that apply to a ticket within a queue
 
 
-Create Ticket in <queue>
 
 
-        Name: Create
-       Principals: <user> <group>
-Display Ticket Summary in <queue>
+=head2 SetRightName VALUE
 
 
-       Name: Show
-       Principals: <user> <group> Owner Requestor Cc AdminCc
 
 
-Display Ticket History  <queue>
+Set RightName to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, RightName will be stored as a varchar(25).)
 
 
-       Name: ShowHistory
-       Principals: <user> <group> Owner Requestor Cc AdminCc
 
 
-Display Ticket Private Comments  <queue>
+=cut
 
 
-       Name: ShowComments
-       Principals: <user> <group> Owner Requestor Cc AdminCc
 
 
-Reply to Ticket in <queue>
+=head2 ObjectType
 
 
-       Name: Reply
-       Principals: <user> <group> Owner Requestor Cc AdminCc
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(25).)
 
 
-Comment on Ticket in <queue>
 
 
-       Name: Comment
-       Principals: <user> <group> Owner Requestor Cc AdminCc
 
 
-Modify Ticket in <queue>
+=head2 SetObjectType VALUE
 
 
-       Name: Modify
-       Principals: <user> <group> Owner Requestor Cc AdminCc
 
 
-Delete Tickets in <queue>
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(25).)
 
 
-       Name: Delete
-       Principals: <user> <group> Owner Requestor Cc AdminCc
 
 
+=cut
 
 
-=head2 Queue Rights that apply to a whole queue
 
 
-These rights can only be granted to "real people"
+=head2 ObjectId
 
 
-List Tickets in <queue>
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
 
 
-       Name: ListQueue
-       Principals: <user> <group>
 
 
-Know that <queue> exists
-    
-    Name: See
-    Principals: <user> <group>
 
 
-Display queue settings
+=head2 SetObjectId VALUE
 
 
-    Name: Explore
-    Principals: <user> <group>
 
 
-Modify Queue Watchers for <queue>
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
 
 
-       Name: ModifyQueueWatchers
-       Principals: <user> <group>
 
 
-Modify Queue Attributes for <queue> 
+=cut
 
 
-       Name: ModifyQueue
-       Principals: <user> <group>
 
 
-Modify Queue ACL for queue <queue>
+=head2 Creator
 
 
-       Name: ModifyACL
-       Principals: <user> <group>
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
 
 
+=cut
 
 
-=head2 Rights that apply to the System scope
 
 
-=head2 SystemRights
+=head2 Created
 
 
-Create Queue
-  
-        Name: CreateQueue
-       Principals: <user> <group>
-Delete Queue
-  
-        Name: DeleteQueue
-       Principals: <user> <group>
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
 
 
-Create Users
-  
-        Name: CreateUser
-       Principals: <user> <group>
+=cut
 
 
-Delete Users
-  
-        Name: DeleteUser
-       Principals: <user> <group>
-  
-Modify Users
-  
-        Name: ModifyUser
-       Principals: <user> <group>
 
 
-Modify Self
-        Name: ModifySelf
-       Principals: <user> <group>
+=head2 LastUpdatedBy
 
 
-Browse Users
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
 
 
-        Name: BrowseUsers (NOT IMPLEMENTED in 2.0)
-       Principals: <user> <group>
+=cut
 
 
-Modify Self
-                   
-       Name: ModifySelf
-       Principals: <user> <group>
 
 
-Modify System ACL
+=head2 LastUpdated
 
 
-       Name: ModifyACL           
-       Principals: <user> <group>
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
 
 
-=head1 The Principal Side of the ACE
+=cut
 
 
-=head2 PrincipalTypes,PrincipalIds in our Neighborhood
 
 
-  User,<userid>
-  Group,<groupip>
-  Everyone,NULL
 
 
-=cut
+sub _CoreAccessible {
+    {
+
+        id =>
+               {read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        PrincipalType =>
+               {read => 1, write => 1, sql_type => 12, length => 25,  is_blob => 0,  is_numeric => 0,  type => 'varchar(25)', default => ''},
+        PrincipalId =>
+               {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        RightName =>
+               {read => 1, write => 1, sql_type => 12, length => 25,  is_blob => 0,  is_numeric => 0,  type => 'varchar(25)', default => ''},
+        ObjectType =>
+               {read => 1, write => 1, sql_type => 12, length => 25,  is_blob => 0,  is_numeric => 0,  type => 'varchar(25)', default => ''},
+        ObjectId =>
+               {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        Creator =>
+               {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        Created =>
+               {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
+        LastUpdatedBy =>
+               {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        LastUpdated =>
+               {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
+
+ }
+};
+
+RT::Base->_ImportOverlays();
 
 
-# }}}
+1;