diff options
Diffstat (limited to 'rt/lib/RT/ACE.pm')
-rwxr-xr-x | rt/lib/RT/ACE.pm | 806 |
1 files changed, 638 insertions, 168 deletions
diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm index 1501a125e..d4681cf44 100755 --- a/rt/lib/RT/ACE.pm +++ b/rt/lib/RT/ACE.pm @@ -1,304 +1,774 @@ -# BEGIN LICENSE BLOCK -# -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> -# -# (Except where explictly superceded by other copyright notices) -# -# 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. -# -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -# -# -# END LICENSE BLOCK -# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>) -# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST. -# -# !! DO NOT EDIT THIS FILE !! -# - -use strict; - +#$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::ACE - RT\'s ACE object =head1 SYNOPSIS + use RT::ACE; + my $ace = new RT::ACE($CurrentUser); + + =head1 DESCRIPTION + =head1 METHODS +=begin testing + +ok(require RT::TestHarness); +ok(require RT::ACE); + +=end testing + =cut package RT::ACE; -use RT::Record; +use RT::Record; +@ISA= qw(RT::Record); +use vars qw (%SCOPES + %QUEUERIGHTS + %SYSTEMRIGHTS + %LOWERCASERIGHTNAMES + ); -use vars qw( @ISA ); -@ISA= qw( RT::Record ); +%SCOPES = ( + System => 'System-level right', + Queue => 'Queue-level right' + ); -sub _Init { - my $self = shift; +# {{{ Descriptions of 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' + + ); - $self->Table('ACL'); - $self->SUPER::_Init(@_); -} +# 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', + ); +# }}} +# {{{ Descriptions of principals -=item Create PARAMHASH +%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', + ); -Create takes a hash of values and creates a row in the database: +# }}} - varchar(25) 'PrincipalType'. - int(11) 'PrincipalId'. - varchar(25) 'RightName'. - varchar(25) 'ObjectType'. - int(11) 'ObjectId'. - int(11) 'DelegatedBy'. - int(11) 'DelegatedFrom'. +# {{{ 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; +} + +# }}} + +# {{{ 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, + RightName => undef, + RightScope => undef, + RightAppliesTo => 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"); + +} + +# }}} +# {{{ 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 + RightName => the name of a right. in any case + RightScope => "System" | "Queue" + RightAppliesTo => a queue id or undef + +=cut sub Create { my $self = shift; - my %args = ( - PrincipalType => '', - PrincipalId => '0', - RightName => '', - ObjectType => '', - ObjectId => '0', - DelegatedBy => '0', - DelegatedFrom => '0', - - @_); - $self->SUPER::Create( - PrincipalType => $args{'PrincipalType'}, - PrincipalId => $args{'PrincipalId'}, - RightName => $args{'RightName'}, - ObjectType => $args{'ObjectType'}, - ObjectId => $args{'ObjectId'}, - DelegatedBy => $args{'DelegatedBy'}, - DelegatedFrom => $args{'DelegatedFrom'}, -); - + 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); + } + 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.'); + } + + # }}} + + #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."); + } + + # }}} + + # {{{ Canonicalize and check the right name + $args{'RightName'} = $self->CanonicalizeRightName($args{'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'); + } + } + # }}} + + # 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'); + } + else { + $RT::Logger->err('System error. right not granted.'); + return(0, 'System Error. right not granted'); + } } +# }}} -=item id +# {{{ sub Delete -Returns the current value of id. -(In the database, id is stored as int(11).) +=head2 Delete +Delete this object. =cut +sub Delete { + my $self = shift; + + unless ($self->CurrentUserHasRight('ModifyACL')) { + return (0, 'Permission Denied'); + } + + + my ($val,$msg) = $self->SUPER::Delete(@_); + if ($val) { + return ($val, 'ACE Deleted'); + } + else { + return (0, 'ACE could not be deleted'); + } +} -=item PrincipalType +# }}} -Returns the current value of PrincipalType. -(In the database, PrincipalType is stored as varchar(25).) +# {{{ sub _BootstrapRight +=head2 _BootstrapRight +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 +written approval, he will hunt you down and make you spend eternity +translating mozilla's code into FORTRAN or intercal. -=item SetPrincipalType VALUE +=cut + +sub _BootstrapRight { + 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); + } + else { + $RT::Logger->err('System error. right not granted.'); + return(undef); + } + +} + +# }}} +# {{{ sub CanonicalizeRightName -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).) +=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 $right = shift; + $right = lc $right; + if (exists $LOWERCASERIGHTNAMES{"$right"}) { + return ($LOWERCASERIGHTNAMES{"$right"}); + } + else { + return (undef); + } +} + +# }}} -=item PrincipalId +# {{{ sub QueueRights -Returns the current value of PrincipalId. -(In the database, PrincipalId is stored as int(11).) +=head2 QueueRights +Returns a hash of all the possible rights at the queue scope +=cut -=item SetPrincipalId VALUE +sub QueueRights { + return (%QUEUERIGHTS); +} +# }}} -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).) +# {{{ sub SystemRights +=head2 SystemRights + +Returns a hash of all the possible rights at the system scope =cut +sub SystemRights { + return (%SYSTEMRIGHTS); +} -=item RightName -Returns the current value of RightName. -(In the database, RightName is stored as varchar(25).) +# }}} +# {{{ 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)); +} +# }}} -=item SetRightName VALUE +# {{{ sub AppliesToObj +=head2 AppliesToObj -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).) +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. +=cut + +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); + } + else { + $RT::Logger->warning("$self -> AppliesToObj called for an object ". + "of an unknown scope:" . $self->RightScope); + 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. =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); + } + + $princ_obj->Load($self->PrincipalId); + return($princ_obj); + +} + +# }}} + +# {{{ ACL related methods + +# {{{ sub _Set + +sub _Set { + my $self = shift; + return (0, "ACEs can only be created and deleted."); +} -=item ObjectType +# }}} -Returns the current value of ObjectType. -(In the database, ObjectType is stored as varchar(25).) +# {{{ sub _Value +sub _Value { + my $self = shift; + unless ($self->CurrentUserHasRight('ShowACL')) { + return (undef); + } -=item SetObjectType VALUE + return ($self->__Value(@_)); +} + +# }}} -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).) +# {{{ sub CurrentUserHasQueueRight +=head2 CurrentUserHasQueueRight ( Queue => QUEUEID, Right => RIGHTNANAME ) + +Check to see whether the current user has the specified right for the specified queue. =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 CurrentUserHasSystemRight +=head2 CurrentUserHasSystemRight RIGHTNAME + +Check to see whether the current user has the specified right for the 'system' scope. + +=cut + +sub CurrentUserHasSystemRight { + my $self = shift; + my $right = shift; + return ($self->HasRight( Right => $right, + Principal => $self->CurrentUser->UserObj, + System => 1 + )); +} + -=item ObjectId +# }}} -Returns the current value of ObjectId. -(In the database, ObjectId is stored as int(11).) +# {{{ sub CurrentUserHasRight +=item CurrentUserHasRight RIGHT +Takes a rightname as a string. +Helper menthod for HasRight. Presets Principal to CurrentUser then +calls HasRight. -=item SetObjectId VALUE +=cut +sub CurrentUserHasRight { + my $self = shift; + my $right = shift; + return ($self->HasRight( Principal => $self->CurrentUser->UserObj, + Right => $right, + )); +} -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).) +# }}} +# {{{ sub HasRight + +=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 +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; + } -=item DelegatedBy -Returns the current value of DelegatedBy. -(In the database, DelegatedBy is stored as int(11).) +} +# }}} +# }}} -=item SetDelegatedBy VALUE +1; +__DATA__ -Set DelegatedBy to VALUE. -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, DelegatedBy will be stored as a int(11).) +# {{{ POD +=head1 Out of date docs -=cut +=head2 Table Structure +PrincipalType, PrincipalId, Right,Scope,AppliesTo -=item DelegatedFrom +=head1 The docs are out of date. so you know. -Returns the current value of DelegatedFrom. -(In the database, DelegatedFrom is stored as 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. -=item SetDelegatedFrom VALUE +=item Queue +=item System -Set DelegatedFrom to VALUE. -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, DelegatedFrom will be stored as a int(11).) +=head1 Rights +=head2 Scope: Queue -=cut +=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> -sub _ClassAccessible { - { - - id => - {read => 1, type => 'int(11)', default => ''}, - PrincipalType => - {read => 1, write => 1, type => 'varchar(25)', default => ''}, - PrincipalId => - {read => 1, write => 1, type => 'int(11)', default => '0'}, - RightName => - {read => 1, write => 1, type => 'varchar(25)', default => ''}, - ObjectType => - {read => 1, write => 1, type => 'varchar(25)', default => ''}, - ObjectId => - {read => 1, write => 1, type => 'int(11)', default => '0'}, - DelegatedBy => - {read => 1, write => 1, type => 'int(11)', default => '0'}, - DelegatedFrom => - {read => 1, write => 1, type => 'int(11)', default => '0'}, + Name: Show + Principals: <user> <group> Owner Requestor Cc AdminCc - } -}; +Display Ticket History <queue> + Name: ShowHistory + Principals: <user> <group> Owner Requestor Cc AdminCc - eval "require RT::ACE_Overlay"; - if ($@ && $@ !~ qr{^Can't locate RT/ACE_Overlay.pm}) { - die $@; - }; +Display Ticket Private Comments <queue> - eval "require RT::ACE_Vendor"; - if ($@ && $@ !~ qr{^Can't locate RT/ACE_Vendor.pm}) { - die $@; - }; + Name: ShowComments + Principals: <user> <group> Owner Requestor Cc AdminCc - eval "require RT::ACE_Local"; - if ($@ && $@ !~ qr{^Can't locate RT/ACE_Local.pm}) { - die $@; - }; +Reply to Ticket in <queue> + Name: Reply + Principals: <user> <group> Owner Requestor Cc AdminCc +Comment on Ticket in <queue> + Name: Comment + Principals: <user> <group> Owner Requestor Cc AdminCc -=head1 SEE ALSO +Modify Ticket in <queue> -This class allows "overlay" methods to be placed -into the following files _Overlay is for a System overlay by the original author, -_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations. + Name: Modify + Principals: <user> <group> Owner Requestor Cc AdminCc -These overlay files can contain new subs or subs to replace existing subs in this module. +Delete Tickets in <queue> -If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line + Name: Delete + Principals: <user> <group> Owner Requestor Cc AdminCc - no warnings qw(redefine); -so that perl does not kick and scream when you redefine a subroutine or variable in your overlay. +=head2 Queue Rights that apply to a whole queue -RT::ACE_Overlay, RT::ACE_Vendor, RT::ACE_Local +These rights can only be granted to "real people" -=cut +List Tickets in <queue> + Name: ListQueue + Principals: <user> <group> -1; +Know that <queue> exists + + Name: See + Principals: <user> <group> + +Display queue settings + + Name: Explore + Principals: <user> <group> + +Modify Queue Watchers for <queue> + + Name: ModifyQueueWatchers + Principals: <user> <group> + +Modify Queue Attributes for <queue> + + Name: ModifyQueue + Principals: <user> <group> + +Modify Queue ACL for queue <queue> + + Name: ModifyACL + Principals: <user> <group> + + +=head2 Rights that apply to the System scope + +=head2 SystemRights + +Create Queue + + Name: CreateQueue + Principals: <user> <group> +Delete Queue + + Name: DeleteQueue + Principals: <user> <group> + +Create Users + + Name: CreateUser + Principals: <user> <group> + +Delete Users + + Name: DeleteUser + Principals: <user> <group> + +Modify Users + + Name: ModifyUser + Principals: <user> <group> + +Modify Self + Name: ModifySelf + Principals: <user> <group> + +Browse Users + + Name: BrowseUsers (NOT IMPLEMENTED in 2.0) + Principals: <user> <group> + +Modify Self + + Name: ModifySelf + Principals: <user> <group> + +Modify System ACL + + Name: ModifyACL + Principals: <user> <group> + +=head1 The Principal Side of the ACE + +=head2 PrincipalTypes,PrincipalIds in our Neighborhood + + User,<userid> + Group,<groupip> + Everyone,NULL + +=cut + +# }}} |