1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
52 my $ace = RT::ACE->new($CurrentUser);
66 use base 'RT::Record';
81 %TICKET_METAPRINCIPALS
88 # Queue rights are the sort of queue rights that can only be granted
89 # to real people or groups
98 %TICKET_METAPRINCIPALS = (
99 Owner => 'The owner of a ticket', # loc_pair
100 Requestor => 'The requestor of a ticket', # loc_pair
101 Cc => 'The CC of a ticket', # loc_pair
102 AdminCc => 'The administrative CC of a ticket', # loc_pair
108 =head2 LoadByValues PARAMHASH
110 Load an ACE by specifying a paramhash with the following fields:
112 PrincipalId => undef,
113 PrincipalType => undef,
129 my %args = ( PrincipalId => undef,
130 PrincipalType => undef,
137 if ( $args{'RightName'} ) {
138 my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} );
139 unless ( $canonic_name ) {
140 return ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) );
142 $args{'RightName'} = $canonic_name;
146 ( $princ_obj, $args{'PrincipalType'} ) =
147 $self->_CanonicalizePrincipal( $args{'PrincipalId'},
148 $args{'PrincipalType'} );
150 unless ( $princ_obj->id ) {
152 $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
156 my ($object, $object_type, $object_id) = $self->_ParseObjectArg( %args );
158 return ( 0, $self->loc("System error. Right not granted.") );
161 $self->LoadByCols( PrincipalId => $princ_obj->Id,
162 PrincipalType => $args{'PrincipalType'},
163 RightName => $args{'RightName'},
164 ObjectType => $object_type,
165 ObjectId => $object_id);
167 #If we couldn't load it.
168 unless ( $self->Id ) {
169 return ( 0, $self->loc("ACE not found") );
173 return ( $self->Id, $self->loc("Right Loaded") );
179 =head2 Create <PARAMS>
181 PARAMS is a parameter hash with the following elements:
183 PrincipalId => The id of an RT::Principal object
184 PrincipalType => "User" "Group" or any Role type
185 RightName => the name of a right. in any case
190 Object => An object to create rights for. ususally, an RT::Queue or RT::Group
191 This should always be a DBIx::SearchBuilder::Record subclass
195 ObjectType => the type of the object in question (ref ($object))
196 ObjectId => the id of the object in question $object->Id
200 Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's false.
209 PrincipalId => undef,
210 PrincipalType => undef,
216 unless ( $args{'RightName'} ) {
217 return ( 0, $self->loc('No right specified') );
220 #if we haven't specified any sort of right, we're talking about a global right
221 if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) {
222 $args{'Object'} = $RT::System;
224 ($args{'Object'}, $args{'ObjectType'}, $args{'ObjectId'}) = $self->_ParseObjectArg( %args );
225 unless( $args{'Object'} ) {
226 return ( 0, $self->loc("System error. Right not granted.") );
229 # Validate the principal
231 ( $princ_obj, $args{'PrincipalType'} ) =
232 $self->_CanonicalizePrincipal( $args{'PrincipalId'},
233 $args{'PrincipalType'} );
235 unless ( $princ_obj->id ) {
237 $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
245 if (ref( $args{'Object'}) eq 'RT::Group' ) {
246 unless ( $self->CurrentUser->HasRight( Object => $args{'Object'},
247 Right => 'AdminGroup' )
249 return ( 0, $self->loc('Permission Denied') );
254 unless ( $self->CurrentUser->HasRight( Object => $args{'Object'}, Right => 'ModifyACL' )) {
255 return ( 0, $self->loc('Permission Denied') );
260 # Canonicalize and check the right name
261 my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} );
262 unless ( $canonic_name ) {
263 return ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) );
265 $args{'RightName'} = $canonic_name;
267 #check if it's a valid RightName
268 if ( $args{'Object'}->can('AvailableRights') ) {
269 my $available = $args{'Object'}->AvailableRights;
270 unless ( grep $_ eq $args{'RightName'}, map $self->CanonicalizeRightName( $_ ), keys %$available ) {
271 $RT::Logger->warning(
272 "Couldn't validate right name '$args{'RightName'}'"
273 ." for object of ". ref( $args{'Object'} ) ." class"
275 return ( 0, $self->loc('Invalid right') );
280 # Make sure the right doesn't already exist.
281 $self->LoadByCols( PrincipalId => $princ_obj->id,
282 PrincipalType => $args{'PrincipalType'},
283 RightName => $args{'RightName'},
284 ObjectType => $args{'ObjectType'},
285 ObjectId => $args{'ObjectId'},
288 return ( 0, $self->loc('[_1] already has that right',
289 $princ_obj->Object->Name) );
292 my $id = $self->SUPER::Create( PrincipalId => $princ_obj->id,
293 PrincipalType => $args{'PrincipalType'},
294 RightName => $args{'RightName'},
295 ObjectType => ref( $args{'Object'} ),
296 ObjectId => $args{'Object'}->id,
299 #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
300 RT::Principal->InvalidateACLCache();
303 return ( $id, $self->loc('Right Granted') );
306 return ( 0, $self->loc('System error. Right not granted.') );
312 =head2 Delete { InsideTransaction => undef}
314 Delete this object. This method should ONLY ever be called from RT::User or RT::Group (or from itself)
315 If this is being called from within a transaction, specify a true value for the parameter InsideTransaction.
316 Really, DBIx::SearchBuilder should use and/or fake subtransactions
318 This routine will also recurse and delete any delegations of this right
325 unless ( $self->Id ) {
326 return ( 0, $self->loc('Right not loaded.') );
329 # A user can delete an ACE if the current user has the right to modify it and it's not a delegated ACE
330 # or if it's a delegated ACE and it was delegated by the current user
331 unless ($self->CurrentUser->HasRight(Right => 'ModifyACL', Object => $self->Object)) {
332 return ( 0, $self->loc('Permission Denied') );
337 # Helper for Delete with no ACL check
340 my %args = ( InsideTransaction => undef,
343 my $InsideTransaction = $args{'InsideTransaction'};
345 $RT::Handle->BeginTransaction() unless $InsideTransaction;
347 my ( $val, $msg ) = $self->SUPER::Delete(@_);
350 #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
351 # TODO what about the groups key cache?
352 RT::Principal->InvalidateACLCache();
353 $RT::Handle->Commit() unless $InsideTransaction;
354 return ( $val, $self->loc('Right revoked') );
357 $RT::Handle->Rollback() unless $InsideTransaction;
358 return ( 0, $self->loc('Right could not be revoked') );
363 =head2 _BootstrapCreate
365 Grant a right with no error checking and no ACL. this is _only_ for
366 installation. If you use this routine without the author's explicit
367 written approval, he will hunt you down and make you spend eternity
368 translating mozilla's code into FORTRAN or intercal.
370 If you think you need this routine, you've mistaken.
374 sub _BootstrapCreate {
378 # When bootstrapping, make sure we get the _right_ users
379 if ( $args{'UserId'} ) {
380 my $user = RT::User->new( $self->CurrentUser );
381 $user->Load( $args{'UserId'} );
382 delete $args{'UserId'};
383 $args{'PrincipalId'} = $user->PrincipalId;
384 $args{'PrincipalType'} = 'User';
387 my $id = $self->SUPER::Create(%args);
393 $RT::Logger->err('System error. right not granted.');
403 my $val = $self->_Value('RightName');
404 return $val unless $val;
406 my $available = $self->Object->AvailableRights;
407 foreach my $right ( keys %$available ) {
408 return $right if $val eq $self->CanonicalizeRightName($right);
411 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$val'");
415 =head2 CanonicalizeRightName <RIGHT>
417 Takes a queue or system right name in any case and returns it in
418 the correct case. If it's not found, will return undef.
422 sub CanonicalizeRightName {
424 return $LOWERCASERIGHTNAMES{ lc shift };
432 If the object this ACE applies to is a queue, returns the queue object.
433 If the object this ACE applies to is a group, returns the group object.
434 If it's the system object, returns undef.
436 If the user has no rights, returns undef.
448 if ($self->__Value('ObjectType') && $OBJECT_TYPES{$self->__Value('ObjectType')} ) {
449 $appliesto_obj = $self->__Value('ObjectType')->new($self->CurrentUser);
450 unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) {
453 $appliesto_obj->Load( $self->__Value('ObjectId') );
454 return ($appliesto_obj);
457 $RT::Logger->warning( "$self -> Object called for an object "
458 . "of an unknown type:"
459 . $self->__Value('ObjectType') );
468 Returns the RT::Principal object for this ACE.
475 my $princ_obj = RT::Principal->new( $self->CurrentUser );
476 $princ_obj->Load( $self->__Value('PrincipalId') );
478 unless ( $princ_obj->Id ) {
480 "ACE " . $self->Id . " couldn't load its principal object" );
491 return ( 0, $self->loc("ACEs can only be created and deleted.") );
499 if ( $self->PrincipalObj->IsGroup
500 && $self->PrincipalObj->Object->HasMemberRecursively(
501 $self->CurrentUser->PrincipalObj
504 return ( $self->__Value(@_) );
506 elsif ( $self->CurrentUser->HasRight(Right => 'ShowACL', Object => $self->Object) ) {
507 return ( $self->__Value(@_) );
518 =head2 _CanonicalizePrincipal (PrincipalId, PrincipalType)
520 Takes a principal id and a principal type.
522 If the principal is a user, resolves it to the proper acl equivalence group.
523 Returns a tuple of (RT::Principal, PrincipalType) for the principal we really want to work with
527 sub _CanonicalizePrincipal {
529 my $princ_id = shift;
530 my $princ_type = shift || '';
532 my $princ_obj = RT::Principal->new(RT->SystemUser);
533 $princ_obj->Load($princ_id);
535 unless ( $princ_obj->Id ) {
537 $RT::Logger->crit(Carp::longmess);
538 $RT::Logger->crit("Can't load a principal for id $princ_id");
539 return ( $princ_obj, undef );
542 # Rights never get granted to users. they get granted to their
543 # ACL equivalence groups
544 if ( $princ_type eq 'User' ) {
545 my $equiv_group = RT::Group->new( $self->CurrentUser );
546 $equiv_group->LoadACLEquivalenceGroup($princ_obj);
547 unless ( $equiv_group->Id ) {
548 $RT::Logger->crit( "No ACL equiv group for princ " . $princ_obj->id );
549 return ( RT::Principal->new(RT->SystemUser), undef );
551 $princ_obj = $equiv_group->PrincipalObj();
552 $princ_type = 'Group';
555 return ( $princ_obj, $princ_type );
558 sub _ParseObjectArg {
560 my %args = ( Object => undef,
565 if( $args{'Object'} && ($args{'ObjectId'} || $args{'ObjectType'}) ) {
566 $RT::Logger->crit( "Method called with an ObjectType or an ObjectId and Object args" );
568 } elsif( $args{'Object'} && ref($args{'Object'}) && !$args{'Object'}->can('id') ) {
569 $RT::Logger->crit( "Method called called Object that has no id method" );
571 } elsif( $args{'Object'} ) {
572 my $obj = $args{'Object'};
573 return ($obj, ref $obj, $obj->id);
574 } elsif ( $args{'ObjectType'} ) {
575 my $obj = $args{'ObjectType'}->new( $self->CurrentUser );
576 $obj->Load( $args{'ObjectId'} );
577 return ($obj, ref $obj, $obj->id);
579 $RT::Logger->crit( "Method called with wrong args" );
591 Returns the current value of id.
592 (In the database, id is stored as int(11).)
600 Returns the current value of PrincipalType.
601 (In the database, PrincipalType is stored as varchar(25).)
605 =head2 SetPrincipalType VALUE
608 Set PrincipalType to VALUE.
609 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
610 (In the database, PrincipalType will be stored as a varchar(25).)
618 Returns the current value of PrincipalId.
619 (In the database, PrincipalId is stored as int(11).)
623 =head2 SetPrincipalId VALUE
626 Set PrincipalId to VALUE.
627 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
628 (In the database, PrincipalId will be stored as a int(11).)
636 Returns the current value of RightName.
637 (In the database, RightName is stored as varchar(25).)
641 =head2 SetRightName VALUE
644 Set RightName to VALUE.
645 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
646 (In the database, RightName will be stored as a varchar(25).)
654 Returns the current value of ObjectType.
655 (In the database, ObjectType is stored as varchar(25).)
659 =head2 SetObjectType VALUE
662 Set ObjectType to VALUE.
663 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
664 (In the database, ObjectType will be stored as a varchar(25).)
672 Returns the current value of ObjectId.
673 (In the database, ObjectId is stored as int(11).)
677 =head2 SetObjectId VALUE
680 Set ObjectId to VALUE.
681 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
682 (In the database, ObjectId will be stored as a int(11).)
690 Returns the current value of Creator.
691 (In the database, Creator is stored as int(11).)
698 Returns the current value of Created.
699 (In the database, Created is stored as datetime.)
706 Returns the current value of LastUpdatedBy.
707 (In the database, LastUpdatedBy is stored as int(11).)
714 Returns the current value of LastUpdated.
715 (In the database, LastUpdated is stored as datetime.)
721 sub _CoreAccessible {
725 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
727 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
729 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
731 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
733 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
735 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
737 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
739 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
741 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
743 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
748 RT::Base->_ImportOverlays();