X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FACE.pm;h=83e19ce853371c7e0ab5bd196256b4978752a99c;hb=31f3763747b82764bb019cfab5b2a2945fc9a99d;hp=d4681cf44e77054a79f3a68df58b099604f4c2ae;hpb=c0567c688084e89fcd11bf82348b6c418f1254ac;p=freeside.git diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm index d4681cf44..83e19ce85 100755 --- a/rt/lib/RT/ACE.pm +++ b/rt/lib/RT/ACE.pm @@ -1,347 +1,416 @@ -#$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 +# +# +# (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 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 -%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, - PrincipalType => undef, + PrincipalType => undef, RightName => undef, - RightScope => undef, - RightAppliesTo => 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 ( 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 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.") ); } - # }}} - - #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 { - $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 - 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. - $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 { - $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 ( $val, $msg ) = $self->SUPER::Delete(@_); + 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 -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 + +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 @@ -351,424 +420,331 @@ the correct case. If it's not found, will return undef. =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 -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 { - $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 -=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 - Name: Create - Principals: -Display Ticket Summary in +=head2 SetRightName VALUE - Name: Show - Principals: Owner Requestor Cc AdminCc -Display Ticket History +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: Owner Requestor Cc AdminCc -Display Ticket Private Comments +=cut - Name: ShowComments - Principals: Owner Requestor Cc AdminCc -Reply to Ticket in +=head2 ObjectType - Name: Reply - Principals: Owner Requestor Cc AdminCc +Returns the current value of ObjectType. +(In the database, ObjectType is stored as varchar(25).) -Comment on Ticket in - Name: Comment - Principals: Owner Requestor Cc AdminCc -Modify Ticket in +=head2 SetObjectType VALUE - Name: Modify - Principals: Owner Requestor Cc AdminCc -Delete Tickets in +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: 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 +Returns the current value of ObjectId. +(In the database, ObjectId is stored as int(11).) - Name: ListQueue - Principals: -Know that exists - - Name: See - Principals: -Display queue settings +=head2 SetObjectId VALUE - Name: Explore - Principals: -Modify Queue Watchers for +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: -Modify Queue Attributes for +=cut - Name: ModifyQueue - Principals: -Modify Queue ACL for queue +=head2 Creator - Name: ModifyACL - Principals: +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: -Delete Queue - - Name: DeleteQueue - Principals: +Returns the current value of Created. +(In the database, Created is stored as datetime.) -Create Users - - Name: CreateUser - Principals: +=cut -Delete Users - - Name: DeleteUser - Principals: - -Modify Users - - Name: ModifyUser - Principals: -Modify Self - Name: ModifySelf - Principals: +=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: +=cut -Modify Self - - Name: ModifySelf - Principals: -Modify System ACL +=head2 LastUpdated - Name: ModifyACL - Principals: +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, - Group, - 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;