1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2012 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('That principal already has that right') );
291 my $id = $self->SUPER::Create( PrincipalId => $princ_obj->id,
292 PrincipalType => $args{'PrincipalType'},
293 RightName => $args{'RightName'},
294 ObjectType => ref( $args{'Object'} ),
295 ObjectId => $args{'Object'}->id,
298 #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
299 RT::Principal->InvalidateACLCache();
302 return ( $id, $self->loc('Right Granted') );
305 return ( 0, $self->loc('System error. Right not granted.') );
311 =head2 Delete { InsideTransaction => undef}
313 Delete this object. This method should ONLY ever be called from RT::User or RT::Group (or from itself)
314 If this is being called from within a transaction, specify a true value for the parameter InsideTransaction.
315 Really, DBIx::SearchBuilder should use and/or fake subtransactions
317 This routine will also recurse and delete any delegations of this right
324 unless ( $self->Id ) {
325 return ( 0, $self->loc('Right not loaded.') );
328 # A user can delete an ACE if the current user has the right to modify it and it's not a delegated ACE
329 # or if it's a delegated ACE and it was delegated by the current user
330 unless ($self->CurrentUser->HasRight(Right => 'ModifyACL', Object => $self->Object)) {
331 return ( 0, $self->loc('Permission Denied') );
336 # Helper for Delete with no ACL check
339 my %args = ( InsideTransaction => undef,
342 my $InsideTransaction = $args{'InsideTransaction'};
344 $RT::Handle->BeginTransaction() unless $InsideTransaction;
346 my ( $val, $msg ) = $self->SUPER::Delete(@_);
349 #Clear the key cache. TODO someday we may want to just clear a little bit of the keycache space.
350 # TODO what about the groups key cache?
351 RT::Principal->InvalidateACLCache();
352 $RT::Handle->Commit() unless $InsideTransaction;
353 return ( $val, $self->loc('Right revoked') );
356 $RT::Handle->Rollback() unless $InsideTransaction;
357 return ( 0, $self->loc('Right could not be revoked') );
362 =head2 _BootstrapCreate
364 Grant a right with no error checking and no ACL. this is _only_ for
365 installation. If you use this routine without the author's explicit
366 written approval, he will hunt you down and make you spend eternity
367 translating mozilla's code into FORTRAN or intercal.
369 If you think you need this routine, you've mistaken.
373 sub _BootstrapCreate {
377 # When bootstrapping, make sure we get the _right_ users
378 if ( $args{'UserId'} ) {
379 my $user = RT::User->new( $self->CurrentUser );
380 $user->Load( $args{'UserId'} );
381 delete $args{'UserId'};
382 $args{'PrincipalId'} = $user->PrincipalId;
383 $args{'PrincipalType'} = 'User';
386 my $id = $self->SUPER::Create(%args);
392 $RT::Logger->err('System error. right not granted.');
402 my $val = $self->_Value('RightName');
403 return $val unless $val;
405 my $available = $self->Object->AvailableRights;
406 foreach my $right ( keys %$available ) {
407 return $right if $val eq $self->CanonicalizeRightName($right);
410 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$val'");
414 =head2 CanonicalizeRightName <RIGHT>
416 Takes a queue or system right name in any case and returns it in
417 the correct case. If it's not found, will return undef.
421 sub CanonicalizeRightName {
423 return $LOWERCASERIGHTNAMES{ lc shift };
431 If the object this ACE applies to is a queue, returns the queue object.
432 If the object this ACE applies to is a group, returns the group object.
433 If it's the system object, returns undef.
435 If the user has no rights, returns undef.
447 if ($self->__Value('ObjectType') && $OBJECT_TYPES{$self->__Value('ObjectType')} ) {
448 $appliesto_obj = $self->__Value('ObjectType')->new($self->CurrentUser);
449 unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) {
452 $appliesto_obj->Load( $self->__Value('ObjectId') );
453 return ($appliesto_obj);
456 $RT::Logger->warning( "$self -> Object called for an object "
457 . "of an unknown type:"
458 . $self->__Value('ObjectType') );
467 Returns the RT::Principal object for this ACE.
474 my $princ_obj = RT::Principal->new( $self->CurrentUser );
475 $princ_obj->Load( $self->__Value('PrincipalId') );
477 unless ( $princ_obj->Id ) {
479 "ACE " . $self->Id . " couldn't load its principal object" );
490 return ( 0, $self->loc("ACEs can only be created and deleted.") );
498 if ( $self->PrincipalObj->IsGroup
499 && $self->PrincipalObj->Object->HasMemberRecursively(
500 $self->CurrentUser->PrincipalObj
503 return ( $self->__Value(@_) );
505 elsif ( $self->CurrentUser->HasRight(Right => 'ShowACL', Object => $self->Object) ) {
506 return ( $self->__Value(@_) );
517 =head2 _CanonicalizePrincipal (PrincipalId, PrincipalType)
519 Takes a principal id and a principal type.
521 If the principal is a user, resolves it to the proper acl equivalence group.
522 Returns a tuple of (RT::Principal, PrincipalType) for the principal we really want to work with
526 sub _CanonicalizePrincipal {
528 my $princ_id = shift;
529 my $princ_type = shift || '';
531 my $princ_obj = RT::Principal->new(RT->SystemUser);
532 $princ_obj->Load($princ_id);
534 unless ( $princ_obj->Id ) {
536 $RT::Logger->crit(Carp::longmess);
537 $RT::Logger->crit("Can't load a principal for id $princ_id");
538 return ( $princ_obj, undef );
541 # Rights never get granted to users. they get granted to their
542 # ACL equivalence groups
543 if ( $princ_type eq 'User' ) {
544 my $equiv_group = RT::Group->new( $self->CurrentUser );
545 $equiv_group->LoadACLEquivalenceGroup($princ_obj);
546 unless ( $equiv_group->Id ) {
547 $RT::Logger->crit( "No ACL equiv group for princ " . $princ_obj->id );
548 return ( RT::Principal->new(RT->SystemUser), undef );
550 $princ_obj = $equiv_group->PrincipalObj();
551 $princ_type = 'Group';
554 return ( $princ_obj, $princ_type );
557 sub _ParseObjectArg {
559 my %args = ( Object => undef,
564 if( $args{'Object'} && ($args{'ObjectId'} || $args{'ObjectType'}) ) {
565 $RT::Logger->crit( "Method called with an ObjectType or an ObjectId and Object args" );
567 } elsif( $args{'Object'} && ref($args{'Object'}) && !$args{'Object'}->can('id') ) {
568 $RT::Logger->crit( "Method called called Object that has no id method" );
570 } elsif( $args{'Object'} ) {
571 my $obj = $args{'Object'};
572 return ($obj, ref $obj, $obj->id);
573 } elsif ( $args{'ObjectType'} ) {
574 my $obj = $args{'ObjectType'}->new( $self->CurrentUser );
575 $obj->Load( $args{'ObjectId'} );
576 return ($obj, ref $obj, $obj->id);
578 $RT::Logger->crit( "Method called with wrong args" );
590 Returns the current value of id.
591 (In the database, id is stored as int(11).)
599 Returns the current value of PrincipalType.
600 (In the database, PrincipalType is stored as varchar(25).)
604 =head2 SetPrincipalType VALUE
607 Set PrincipalType to VALUE.
608 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
609 (In the database, PrincipalType will be stored as a varchar(25).)
617 Returns the current value of PrincipalId.
618 (In the database, PrincipalId is stored as int(11).)
622 =head2 SetPrincipalId VALUE
625 Set PrincipalId to VALUE.
626 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
627 (In the database, PrincipalId will be stored as a int(11).)
635 Returns the current value of RightName.
636 (In the database, RightName is stored as varchar(25).)
640 =head2 SetRightName VALUE
643 Set RightName to VALUE.
644 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
645 (In the database, RightName will be stored as a varchar(25).)
653 Returns the current value of ObjectType.
654 (In the database, ObjectType is stored as varchar(25).)
658 =head2 SetObjectType VALUE
661 Set ObjectType to VALUE.
662 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
663 (In the database, ObjectType will be stored as a varchar(25).)
671 Returns the current value of ObjectId.
672 (In the database, ObjectId is stored as int(11).)
676 =head2 SetObjectId VALUE
679 Set ObjectId to VALUE.
680 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
681 (In the database, ObjectId will be stored as a int(11).)
689 Returns the current value of Creator.
690 (In the database, Creator is stored as int(11).)
697 Returns the current value of Created.
698 (In the database, Created is stored as datetime.)
705 Returns the current value of LastUpdatedBy.
706 (In the database, LastUpdatedBy is stored as int(11).)
713 Returns the current value of LastUpdated.
714 (In the database, LastUpdated is stored as datetime.)
720 sub _CoreAccessible {
724 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
726 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
728 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
730 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
732 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
734 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
736 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
738 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
740 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
742 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
747 RT::Base->_ImportOverlays();