-# BEGIN LICENSE BLOCK
+
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
+# <jesse@bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# (Except where explictly superceded 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
# 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.
+# 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/copyleft/gpl.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.)
#
-# END LICENSE BLOCK
+# 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 }}}
# Released under the terms of version 2 of the GNU Public License
=head1 NAME
=head1 SYNOPSIS
- use RT::Group;
+use RT::Group;
my $group = new RT::Group($CurrentUser);
=head1 DESCRIPTION
An RT group object.
-=head1 AUTHOR
-
-Jesse Vincent, jesse@bestpractical.com
-
-=head1 SEE ALSO
-
-RT
-
=head1 METHODS
=cut
+
+package RT::Group;
+
use strict;
no warnings qw(redefine);
AdminGroup => 'Modify group metadata or delete group', # loc_pair
AdminGroupMembership =>
'Modify membership roster for this group', # loc_pair
- ModifyOwnMembership => 'Join or leave this group' # loc_pair
+ ModifyOwnMembership => 'Join or leave this group', # loc_pair
+ EditSavedSearches => 'Edit saved searches for this group', # loc_pair
+ ShowSavedSearches => 'Display saved searches for this group', # loc_pair
+ SeeGroup => 'Make this group visible to user', # loc_pair
};
# Tell RT::ACE that this sort of object can get acls granted
my $identifier = shift;
$self->LoadByCols( "Domain" => 'SystemInternal',
- "Instance" => '',
- "Name" => '',
"Type" => $identifier );
}
sub LoadTicketRoleGroup {
my $self = shift;
- my %args = (Ticket => undef,
+ my %args = (Ticket => '0',
Type => undef,
@_);
$self->LoadByCols( Domain => 'RT::Ticket-Role',
# }}}
# {{{ sub Create
+
=head2 Create
You need to specify what sort of group you're creating by calling one of the other
Description => undef,
Domain => undef,
Type => undef,
- Instance => undef,
+ Instance => '0',
InsideTransaction => undef,
+ _RecordTransaction => 1,
@_
);
Description => $args{'Description'},
Type => $args{'Type'},
Domain => $args{'Domain'},
- Instance => $args{'Instance'}
+ Instance => ($args{'Instance'} || '0')
);
my $id = $self->Id;
unless ($id) {
# If we couldn't create a principal Id, get the fuck out.
unless ($principal_id) {
$RT::Handle->Rollback() unless ($args{'InsideTransaction'});
- $self->crit( "Couldn't create a Principal on new user create. Strange things are afoot at the circle K" );
+ $RT::Logger->crit( "Couldn't create a Principal on new user create. Strange things are afoot at the circle K" );
return ( 0, $self->loc('Could not create group') );
}
$cgm->Create(Group =>$self->PrincipalObj, Member => $self->PrincipalObj, ImmediateParent => $self->PrincipalObj);
+ if ( $args{'_RecordTransaction'} ) {
+ $self->_NewTransaction( Type => "Create" );
+ }
$RT::Handle->Commit() unless ($args{'InsideTransaction'});
+
return ( $id, $self->loc("Group created") );
}
It will not appear in most group listings.
This routine finds all the cached group members that are members of this group (recursively) and disables them.
+
=cut
# }}}
#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::Principal->InvalidateACLCache();
=head2 DeepMembersObj
-Returns an RT::CachedGroupMembers object of this group's members.
+Returns an RT::CachedGroupMembers object of this group's members,
+including all members of subgroups.
=cut
#If we don't have rights, don't include any results
# TODO XXX WHY IS THERE NO ACL CHECK HERE?
- my $principals = $users->NewAlias('Principals');
-
- $users->Join(ALIAS1 => 'main', FIELD1 => 'id',
- ALIAS2 => $principals, FIELD2 => 'ObjectId');
- $users->Limit(ALIAS =>$principals,
- FIELD => 'PrincipalType', OPERATOR => '=', VALUE => 'User');
-
my $cached_members = $users->NewAlias('CachedGroupMembers');
$users->Join(ALIAS1 => $cached_members, FIELD1 => 'MemberId',
- ALIAS2 => $principals, FIELD2 => 'id');
+ ALIAS2 => $users->PrincipalsAlias, FIELD2 => 'id');
$users->Limit(ALIAS => $cached_members,
FIELD => 'GroupId',
OPERATOR => '=',
VALUE => $self->PrincipalId);
-
return ( $users);
}
=head2 MembersObj
-Returns an RT::CachedGroupMembers object of this group's members.
+Returns an RT::GroupMembers object of this group's direct members.
=cut
return(undef);
}
+ unless ($principal->Id) {
+ return(undef);
+ }
+
my $member_obj = RT::GroupMember->new( $self->CurrentUser );
$member_obj->LoadByCols( MemberId => $principal->id,
GroupId => $self->PrincipalId );
# }}}
+# {{{ sub _CleanupInvalidDelegations
+
+=head2 _CleanupInvalidDelegations { InsideTransaction => undef }
+
+Revokes all ACE entries delegated by members of this group which are
+inconsistent with their current delegation rights. Does not perform
+permission checks. Should only ever be called from inside the RT
+library.
+
+If called from inside a transaction, specify a true value for the
+InsideTransaction parameter.
+
+Returns a true value if the deletion succeeded; returns a false value
+and logs an internal error if the deletion fails (should not happen).
+
+=cut
+
+# XXX Currently there is a _CleanupInvalidDelegations method in both
+# RT::User and RT::Group. If the recursive cleanup call for groups is
+# ever unrolled and merged, this code will probably want to be
+# factored out into RT::Principal.
+
+sub _CleanupInvalidDelegations {
+ my $self = shift;
+ my %args = ( InsideTransaction => undef,
+ @_ );
+
+ unless ( $self->Id ) {
+ $RT::Logger->warning("Group not loaded.");
+ return (undef);
+ }
+
+ my $in_trans = $args{InsideTransaction};
+
+ # TODO: Can this be unrolled such that the number of DB queries is constant rather than linear in exploded group size?
+ my $members = $self->DeepMembersObj();
+ $members->LimitToUsers();
+ $RT::Handle->BeginTransaction() unless $in_trans;
+ while ( my $member = $members->Next()) {
+ my $ret = $member->MemberObj->_CleanupInvalidDelegations(InsideTransaction => 1,
+ Object => $args{Object});
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ return (undef);
+ }
+ }
+ $RT::Handle->Commit() unless $in_trans;
+ return(1);
+}
+
+# }}}
+
# {{{ ACL Related routines
# {{{ sub _Set
sub _Set {
my $self = shift;
+ my %args = (
+ Field => undef,
+ Value => undef,
+ TransactionType => 'Set',
+ RecordTransaction => 1,
+ @_
+ );
if ($self->Domain eq 'Personal') {
if ($self->CurrentUser->PrincipalId == $self->Instance) {
return ( 0, $self->loc('Permission Denied') );
}
}
- return ( $self->SUPER::_Set(@_) );
+
+ my $Old = $self->SUPER::_Value("$args{'Field'}");
+
+ my ($ret, $msg) = $self->SUPER::_Set( Field => $args{'Field'},
+ Value => $args{'Value'} );
+
+ #If we can't actually set the field to the value, don't record
+ # a transaction. instead, get out of here.
+ if ( $ret == 0 ) { return ( 0, $msg ); }
+
+ if ( $args{'RecordTransaction'} == 1 ) {
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => $args{'TransactionType'},
+ Field => $args{'Field'},
+ NewValue => $args{'Value'},
+ OldValue => $Old,
+ TimeTaken => $args{'TimeTaken'},
+ );
+ return ( $Trans, scalar $TransObj->Description );
+ }
+ else {
+ return ( $ret, $msg );
+ }
}
# }}}
-=item CurrentUserHasRight RIGHTNAME
+=head2 CurrentUserHasRight RIGHTNAME
Returns true if the current user has the specified right for this group.
}
# }}}
+
+sub BasicColumns {
+ (
+ [ Name => 'Name' ],
+ [ Description => 'Description' ],
+ );
+}
+
1;
+=head1 AUTHOR
+
+Jesse Vincent, jesse@bestpractical.com
+
+=head1 SEE ALSO
+
+RT
+