-#$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-2016 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;
- my $ace = new RT::ACE($CurrentUser);
+ my $ace = RT::ACE->new($CurrentUser);
=head1 DESCRIPTION
-=head1 METHODS
-
-=begin testing
-ok(require RT::TestHarness);
-ok(require RT::ACE);
+=head1 METHODS
-=end testing
=cut
-package RT::ACE;
-use RT::Record;
-@ISA= qw(RT::Record);
-
-use vars qw (%SCOPES
- %QUEUERIGHTS
- %SYSTEMRIGHTS
- %LOWERCASERIGHTNAMES
- );
-
-%SCOPES = (
- System => 'System-level right',
- Queue => 'Queue-level right'
- );
-# {{{ Descriptions of rights
+package RT::ACE;
+use base 'RT::Record';
-# 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',
-
- );
+sub Table {'ACL'}
-# }}}
-# {{{ Descriptions of principals
+use strict;
+use warnings;
-%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',
- );
+require RT::Principals;
+require RT::Queues;
+require RT::Groups;
-# }}}
+our %RIGHTS;
-# {{{ We need to build a hash of all rights, keyed by lower case names
+my (@_ACL_CACHE_HANDLERS);
-#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;
-}
-# }}}
+=head1 Rights
-# {{{ sub _Init
-sub _Init {
- my $self = shift;
- $self->{'table'} = "ACL";
- return($self->SUPER::_Init(@_));
-}
-# }}}
+# Queue rights are the sort of queue rights that can only be granted
+# to real people or groups
-# {{{ sub LoadByValues
+=cut
=head2 LoadByValues PARAMHASH
Load an ACE by specifying a paramhash with the following fields:
PrincipalId => undef,
- PrincipalType => undef,
- RightName => undef,
- RightScope => undef,
- RightAppliesTo => undef,
+ PrincipalType => undef,
+ RightName => undef,
+
+ And either:
+
+ Object => undef,
+
+ OR
+
+ ObjectType => undef,
+ ObjectId => undef
=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 wantarray ? ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) ) : 0;
+ }
+ $args{'RightName'} = $canonic_name;
+ }
+
+ my $princ_obj;
+ ( $princ_obj, $args{'PrincipalType'} ) =
+ $self->_CanonicalizePrincipal( $args{'PrincipalId'},
+ $args{'PrincipalType'} );
+
+ unless ( $princ_obj->id ) {
+ return wantarray ? ( 0,
+ $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
+ ) : 0;
+ }
+
+ my ($object, $object_type, $object_id) = $self->_ParseObjectArg( %args );
+ unless( $object ) {
+ return wantarray ? ( 0, $self->loc("System error. Right not granted.")) : 0;
+ }
+
+ $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 wantarray ? ( 0, $self->loc("ACE not found") ) : 0;
+ }
+
+ # if we could
+ return wantarray ? ( $self->Id, $self->loc("Right Loaded") ) : $self->Id;
+
}
-# }}}
-# {{{ sub Create
=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
- 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;
- 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.") );
+ }
+
+ # 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'} )
+ );
}
# }}}
-
- #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);
- }
- }
-
- elsif ($args{'RightScope'} eq 'Queue') {
- unless ($self->CurrentUserHasQueueRight( Queue => $args{'RightAppliesTo'},
- Right => 'ModifyACL')) {
- return (0, 'Permission Denied.');
- }
-
-
-
-
- }
- #If it's not a scope we recognise, something scary is happening.
- 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.");
+
+ # 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') );
+ }
}
+ else {
+ 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
- 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($princ_obj);
+ 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.
- $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,
+ );
+
+ if ( $id ) {
+ RT::ACE->InvalidateCaches(
+ Action => "Grant",
+ RightName => $self->RightName,
+ ACE => $self,
+ );
+ return ( $id, $self->loc('Right Granted') );
}
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;
-
- 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 $right = $self->RightName;
+
+ my ( $val, $msg ) = $self->SUPER::Delete(@_);
+
if ($val) {
- return ($val, 'ACE Deleted');
- }
- else {
- return (0, 'ACE could not be deleted');
+ RT::ACE->InvalidateCaches( Action => "Revoke", RightName => $right );
+ $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
-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.
+If you think you need this routine, you've mistaken.
+
=cut
-sub _BootstrapRight {
+sub _BootstrapCreate {
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 {
- $RT::Logger->err('System error. right not granted.');
- return(undef);
+ $RT::Logger->err('System error. right not granted.');
+ return (undef);
}
-
-}
-# }}}
+}
-# {{{ sub CanonicalizeRightName
+=head2 InvalidateCaches
-=head2 CanonicalizeRightName <RIGHT>
+Calls any registered ACL cache handlers (see L</RegisterCacheHandler>).
-Takes a queue or system right name in any case and returns it in
-the correct case. If it's not found, will return undef.
+Usually called from L</Create> and L</Delete>.
=cut
-sub CanonicalizeRightName {
- my $self = shift;
- my $right = shift;
- $right = lc $right;
- if (exists $LOWERCASERIGHTNAMES{"$right"}) {
- return ($LOWERCASERIGHTNAMES{"$right"});
- }
- else {
- return (undef);
+sub InvalidateCaches {
+ my $class = shift;
+
+ for my $handler (@_ACL_CACHE_HANDLERS) {
+ next unless ref($handler) eq "CODE";
+ $handler->(@_);
}
}
-# }}}
+=head2 RegisterCacheHandler
-# {{{ sub QueueRights
+Class method. Takes a coderef and adds it to the ACL cache handlers. These
+handlers are called by L</InvalidateCaches>, usually called itself from
+L</Create> and L</Delete>.
-=head2 QueueRights
+The handlers are passed a hash which may contain any (or none) of these
+optional keys:
-Returns a hash of all the possible rights at the queue scope
+=over
-=cut
+=item Action
-sub QueueRights {
- return (%QUEUERIGHTS);
-}
+A string indicating the action that (may have) invalidated the cache. Expected
+values are currently:
-# }}}
+=over
+
+=item Grant
+
+=item Revoke
+
+=back
+
+However, other values may be passed in the future.
-# {{{ sub SystemRights
+=item RightName
-=head2 SystemRights
+The (canonicalized) right being granted or revoked.
-Returns a hash of all the possible rights at the system scope
+=item ACE
+
+The L<RT::ACE> object just created.
+
+=back
+
+Your handler should be flexible enough to account for additional arguments
+being passed in the future.
=cut
-sub SystemRights {
- return (%SYSTEMRIGHTS);
+sub RegisterCacheHandler {
+ push @_ACL_CACHE_HANDLERS, $_[1];
}
+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);
+ }
-# {{{ 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));
+ $RT::Logger->error("Invalid right. Couldn't canonicalize right '$val'");
+ return $val;
}
-# }}}
-# {{{ sub AppliesToObj
+=head2 CanonicalizeRightName <RIGHT>
+
+Takes a queue or system right name in any case and returns it in
+the correct case. If it's not found, will return undef.
+
+=cut
+
+sub CanonicalizeRightName {
+ my $self = shift;
+ my $name = shift;
+ for my $class (sort keys %RIGHTS) {
+ return $RIGHTS{$class}{ lc $name }{Name}
+ if $RIGHTS{$class}{ lc $name };
+ }
+ return undef;
+}
+
+
-=head2 AppliesToObj
+=head2 Object
-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.
+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.
+
+If the user has no rights, returns undef.
=cut
-sub AppliesToObj {
+
+
+
+sub Object {
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);
- }
+
+ my $appliesto_obj;
+
+ if ($self->__Value('ObjectType') && $self->__Value('ObjectType')->DOES('RT::Record::Role::Rights') ) {
+ $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 {
- $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
-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;
- 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 {
- 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;
- 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
-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 $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
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
=cut
-sub CurrentUserHasRight {
- my $self = shift;
- my $right = shift;
- return ($self->HasRight( Principal => $self->CurrentUser->UserObj,
- Right => $right,
- ));
-}
-# }}}
+=head2 PrincipalType
-# {{{ sub HasRight
+Returns the current value of PrincipalType.
+(In the database, PrincipalType is stored as varchar(25).)
-=item HasRight
-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
-=cut
+=head2 SetPrincipalType VALUE
-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;
- }
+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).)
-}
-# }}}
+=cut
-# }}}
-1;
+=head2 PrincipalId
-__DATA__
+Returns the current value of PrincipalId.
+(In the database, PrincipalId is stored as int(11).)
-# {{{ POD
-=head1 Out of date docs
-=head2 Table Structure
+=head2 SetPrincipalId VALUE
-PrincipalType, PrincipalId, Right,Scope,AppliesTo
-=head1 The docs are out of date. so you know.
+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).)
-=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.
+=cut
-=item Queue
-=item System
+=head2 RightName
+Returns the current value of RightName.
+(In the database, RightName is stored as varchar(25).)
-=head1 Rights
-=head2 Scope: Queue
-=head2 Queue rights that apply to a ticket within a queue
+=head2 SetRightName VALUE
-Create Ticket in <queue>
- Name: Create
- Principals: <user> <group>
-Display Ticket Summary in <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: Show
- Principals: <user> <group> Owner Requestor Cc AdminCc
-Display Ticket History <queue>
+=cut
- Name: ShowHistory
- Principals: <user> <group> Owner Requestor Cc AdminCc
-Display Ticket Private Comments <queue>
+=head2 ObjectType
- Name: ShowComments
- Principals: <user> <group> Owner Requestor Cc AdminCc
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(25).)
-Reply to Ticket in <queue>
- Name: Reply
- Principals: <user> <group> Owner Requestor Cc AdminCc
-Comment on Ticket in <queue>
+=head2 SetObjectType VALUE
- Name: Comment
- Principals: <user> <group> Owner Requestor Cc AdminCc
-Modify Ticket 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: Modify
- Principals: <user> <group> Owner Requestor Cc AdminCc
-Delete Tickets in <queue>
+=cut
- Name: Delete
- Principals: <user> <group> Owner Requestor Cc AdminCc
+=head2 ObjectId
-=head2 Queue Rights that apply to a whole queue
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
-These rights can only be granted to "real people"
-List Tickets in <queue>
- Name: ListQueue
- Principals: <user> <group>
+=head2 SetObjectId VALUE
-Know that <queue> exists
-
- Name: See
- Principals: <user> <group>
-Display queue settings
+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: Explore
- Principals: <user> <group>
-Modify Queue Watchers for <queue>
+=cut
- Name: ModifyQueueWatchers
- Principals: <user> <group>
-Modify Queue Attributes for <queue>
+=head2 Creator
- Name: ModifyQueue
- Principals: <user> <group>
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
-Modify Queue ACL for queue <queue>
+=cut
- Name: ModifyACL
- Principals: <user> <group>
+=head2 Created
-=head2 Rights that apply to the System scope
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
-=head2 SystemRights
+=cut
-Create Queue
-
- Name: CreateQueue
- Principals: <user> <group>
-Delete Queue
-
- Name: DeleteQueue
- Principals: <user> <group>
-Create Users
-
- Name: CreateUser
- Principals: <user> <group>
+=head2 LastUpdatedBy
-Delete Users
-
- Name: DeleteUser
- Principals: <user> <group>
-
-Modify Users
-
- Name: ModifyUser
- Principals: <user> <group>
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
-Modify Self
- Name: ModifySelf
- Principals: <user> <group>
+=cut
-Browse Users
- Name: BrowseUsers (NOT IMPLEMENTED in 2.0)
- Principals: <user> <group>
+=head2 LastUpdated
-Modify Self
-
- Name: ModifySelf
- Principals: <user> <group>
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
-Modify System ACL
+=cut
- Name: ModifyACL
- Principals: <user> <group>
-=head1 The Principal Side of the ACE
-=head2 PrincipalTypes,PrincipalIds in our Neighborhood
+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 => ''},
+
+ }
+};
+
+sub FindDependencies {
+ my $self = shift;
+ my ($walker, $deps) = @_;
- User,<userid>
- Group,<groupip>
- Everyone,NULL
+ $self->SUPER::FindDependencies($walker, $deps);
-=cut
+ $deps->Add( out => $self->PrincipalObj->Object );
+ $deps->Add( out => $self->Object );
+}
-# }}}
+RT::Base->_ImportOverlays();
+
+1;