X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=rt%2Flib%2FRT%2FGroups.pm;h=576c99dc9b5798a4b5a88e00fb240421ef716e7a;hb=31f3763747b82764bb019cfab5b2a2945fc9a99d;hp=29f12a5a0941eeee5e7b57cb3213beca7c282540;hpb=fb4ab1073f0d15d660c6cdc4e07afebf68ef3924;p=freeside.git diff --git a/rt/lib/RT/Groups.pm b/rt/lib/RT/Groups.pm index 29f12a5a0..576c99dc9 100755 --- a/rt/lib/RT/Groups.pm +++ b/rt/lib/RT/Groups.pm @@ -1,115 +1,479 @@ -# BEGIN LICENSE BLOCK -# -# Copyright (c) 1996-2003 Jesse Vincent -# -# (Except where explictly superceded by other copyright notices) -# +# 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. -# -# 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 ) -# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST. -# -# !! DO NOT EDIT THIS FILE !! # - -use strict; - +# 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 NAME - RT::Groups -- Class Description - + RT::Groups - a collection of RT::Group objects + =head1 SYNOPSIS - use RT::Groups + use RT::Groups; + my $groups = RT::Groups->new($CurrentUser); + $groups->UnLimit(); + while (my $group = $groups->Next()) { + print $group->Id ." is a group id\n"; + } =head1 DESCRIPTION =head1 METHODS + + =cut + package RT::Groups; -use RT::SearchBuilder; +use strict; +use warnings; + + + use RT::Group; -use vars qw( @ISA ); -@ISA= qw(RT::SearchBuilder); +use base 'RT::SearchBuilder'; + +sub Table { 'Groups'} + +use RT::Users; + +# XXX: below some code is marked as subject to generalize in Groups, Users classes. +# RUZ suggest name Principals::Generic or Principals::Base as abstract class, but +# Jesse wants something that doesn't imply it's a Principals.pm subclass. +# See comments below for candidats. + + + +sub _Init { + my $self = shift; + $self->{'with_disabled_column'} = 1; + + my @result = $self->SUPER::_Init(@_); + + $self->OrderBy( ALIAS => 'main', + FIELD => 'Name', + ORDER => 'ASC'); + + # XXX: this code should be generalized + $self->{'princalias'} = $self->Join( + ALIAS1 => 'main', + FIELD1 => 'id', + TABLE2 => 'Principals', + FIELD2 => 'id' + ); + + # even if this condition is useless and ids in the Groups table + # only match principals with type 'Group' this could speed up + # searches in some DBs. + $self->Limit( ALIAS => $self->{'princalias'}, + FIELD => 'PrincipalType', + VALUE => 'Group', + ); + + return (@result); +} +=head2 PrincipalsAlias -sub _Init { +Returns the string that represents this Users object's primary "Principals" alias. + +=cut + +# XXX: should be generalized, code duplication +sub PrincipalsAlias { my $self = shift; - $self->{'table'} = 'Groups'; - $self->{'primary_key'} = 'id'; + return($self->{'princalias'}); + +} - return ( $self->SUPER::_Init(@_) ); + +=head2 LimitToSystemInternalGroups + +Return only SystemInternal Groups, such as "privileged" "unprivileged" and "everyone" + +=cut + + +sub LimitToSystemInternalGroups { + my $self = shift; + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'SystemInternal'); + # All system internal groups have the same instance. No reason to limit down further + #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '0'); } -=item NewItem -Returns an empty new RT::Group item + +=head2 LimitToUserDefinedGroups + +Return only UserDefined Groups =cut -sub NewItem { + +sub LimitToUserDefinedGroups { my $self = shift; - return(RT::Group->new($self->CurrentUser)); + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined'); + # All user-defined groups have the same instance. No reason to limit down further + #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => ''); +} + + + + +=head2 LimitToRolesForQueue QUEUE_ID + +Limits the set of groups found to role groups for queue QUEUE_ID + +=cut + +sub LimitToRolesForQueue { + my $self = shift; + my $queue = shift; + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Queue-Role'); + $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $queue); +} + + + +=head2 LimitToRolesForTicket Ticket_ID + +Limits the set of groups found to role groups for Ticket Ticket_ID + +=cut + +sub LimitToRolesForTicket { + my $self = shift; + my $Ticket = shift; + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Ticket-Role'); + $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '$Ticket'); } - eval "require RT::Groups_Overlay"; - if ($@ && $@ !~ qr{^Can't locate RT/Groups_Overlay.pm}) { - die $@; - }; - eval "require RT::Groups_Vendor"; - if ($@ && $@ !~ qr{^Can't locate RT/Groups_Vendor.pm}) { - die $@; - }; - eval "require RT::Groups_Local"; - if ($@ && $@ !~ qr{^Can't locate RT/Groups_Local.pm}) { - die $@; - }; +=head2 LimitToRolesForSystem System_ID + +Limits the set of groups found to role groups for System System_ID + +=cut + +sub LimitToRolesForSystem { + my $self = shift; + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::System-Role'); +} + + +=head2 WithMember {PrincipalId => PRINCIPAL_ID, Recursively => undef} +Limits the set of groups returned to groups which have +Principal PRINCIPAL_ID as a member. Returns the alias used for the join. +=cut +sub WithMember { + my $self = shift; + my %args = ( PrincipalId => undef, + Recursively => undef, + @_); + my $members; + + if ($args{'Recursively'}) { + $members = $self->NewAlias('CachedGroupMembers'); + } else { + $members = $self->NewAlias('GroupMembers'); + } + $self->Join(ALIAS1 => 'main', FIELD1 => 'id', + ALIAS2 => $members, FIELD2 => 'GroupId'); + + $self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'}); + $self->Limit(ALIAS => $members, FIELD => 'Disabled', VALUE => 0) + if $args{'Recursively'}; + + return $members; +} -=head1 SEE ALSO +sub WithCurrentUser { + my $self = shift; + $self->{with_current_user} = 1; + return $self->WithMember( + PrincipalId => $self->CurrentUser->PrincipalId, + Recursively => 1, + ); +} -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. +sub WithoutMember { + my $self = shift; + my %args = ( + PrincipalId => undef, + Recursively => undef, + @_ + ); + + my $members = $args{'Recursively'} ? 'CachedGroupMembers' : 'GroupMembers'; + my $members_alias = $self->Join( + TYPE => 'LEFT', + FIELD1 => 'id', + TABLE2 => $members, + FIELD2 => 'GroupId', + ); + $self->Limit( + LEFTJOIN => $members_alias, + ALIAS => $members_alias, + FIELD => 'MemberId', + OPERATOR => '=', + VALUE => $args{'PrincipalId'}, + ); + $self->Limit( + LEFTJOIN => $members_alias, + ALIAS => $members_alias, + FIELD => 'Disabled', + VALUE => 0 + ) if $args{'Recursively'}; + $self->Limit( + ALIAS => $members_alias, + FIELD => 'MemberId', + OPERATOR => 'IS', + VALUE => 'NULL', + QUOTEVALUE => 0, + ); +} -These overlay files can contain new subs or subs to replace existing subs in this module. +=head2 WithRight { Right => RIGHTNAME, Object => RT::Record, IncludeSystemRights => 1, IncludeSuperusers => 0, EquivObjects => [ ] } -If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line - no warnings qw(redefine); +Find all groups which have RIGHTNAME for RT::Record. Optionally include global rights and superusers. By default, include the global rights, but not the superusers. -so that perl does not kick and scream when you redefine a subroutine or variable in your overlay. -RT::Groups_Overlay, RT::Groups_Vendor, RT::Groups_Local =cut +#XXX: should be generilized +sub WithRight { + my $self = shift; + my %args = ( Right => undef, + Object => => undef, + IncludeSystemRights => 1, + IncludeSuperusers => undef, + IncludeSubgroupMembers => 0, + EquivObjects => [ ], + @_ ); + + my $from_role = $self->Clone; + $from_role->WithRoleRight( %args ); + + my $from_group = $self->Clone; + $from_group->WithGroupRight( %args ); + + #XXX: DIRTY HACK + use DBIx::SearchBuilder 1.50; #no version on ::Union :( + use DBIx::SearchBuilder::Union; + my $union = DBIx::SearchBuilder::Union->new(); + $union->add($from_role); + $union->add($from_group); + %$self = %$union; + bless $self, ref($union); + + return; +} + +#XXX: methods are active aliases to Users class to prevent code duplication +# should be generalized +sub _JoinGroups { + my $self = shift; + my %args = (@_); + return 'main' unless $args{'IncludeSubgroupMembers'}; + return $self->RT::Users::_JoinGroups( %args ); +} +sub _JoinGroupMembers { + my $self = shift; + my %args = (@_); + return 'main' unless $args{'IncludeSubgroupMembers'}; + return $self->RT::Users::_JoinGroupMembers( %args ); +} +sub _JoinGroupMembersForGroupRights { + my $self = shift; + my %args = (@_); + my $group_members = $self->_JoinGroupMembers( %args ); + unless( $group_members eq 'main' ) { + return $self->RT::Users::_JoinGroupMembersForGroupRights( %args ); + } + $self->Limit( ALIAS => $args{'ACLAlias'}, + FIELD => 'PrincipalId', + VALUE => "main.id", + QUOTEVALUE => 0, + ); +} +sub _JoinACL { return (shift)->RT::Users::_JoinACL( @_ ) } +sub _RoleClauses { return (shift)->RT::Users::_RoleClauses( @_ ) } +sub _WhoHaveRoleRightSplitted { return (shift)->RT::Users::_WhoHaveRoleRightSplitted( @_ ) } +sub _GetEquivObjects { return (shift)->RT::Users::_GetEquivObjects( @_ ) } +sub WithGroupRight { return (shift)->RT::Users::WhoHaveGroupRight( @_ ) } +sub WithRoleRight { return (shift)->RT::Users::WhoHaveRoleRight( @_ ) } + +sub ForWhichCurrentUserHasRight { + my $self = shift; + my %args = ( + Right => undef, + IncludeSuperusers => undef, + @_, + ); + + # Non-disabled groups... + $self->LimitToEnabled; + + # ...which are the target object of an ACL with that right, or + # where the target is the system object (a global right) + my $acl = $self->_JoinACL( %args ); + $self->_AddSubClause( + ACLObjects => "( (main.id = $acl.ObjectId AND $acl.ObjectType = 'RT::Group')" + . " OR $acl.ObjectType = 'RT::System')"); + + # ...and where that right is granted to any group.. + my $member = $self->Join( + ALIAS1 => $acl, + FIELD1 => 'PrincipalId', + TABLE2 => 'CachedGroupMembers', + FIELD2 => 'GroupId', + ); + $self->Limit( + ALIAS => $member, + FIELD => 'Disabled', + VALUE => '0', + ); + + # ...with the current user in it + $self->Limit( + ALIAS => $member, + FIELD => 'MemberId', + VALUE => $self->CurrentUser->Id, + ); + + return; +} + +=head2 LimitToEnabled + +Only find items that haven't been disabled + +=cut + +sub LimitToEnabled { + my $self = shift; + + $self->{'handled_disabled_column'} = 1; + $self->Limit( + ALIAS => $self->PrincipalsAlias, + FIELD => 'Disabled', + VALUE => '0', + ); +} + + +=head2 LimitToDeleted + +Only find items that have been deleted. + +=cut + +sub LimitToDeleted { + my $self = shift; + + $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1; + $self->Limit( + ALIAS => $self->PrincipalsAlias, + FIELD => 'Disabled', + VALUE => 1, + ); +} + + + +sub AddRecord { + my $self = shift; + my ($record) = @_; + + # If we've explicitly limited to groups the user is a member of (for + # dashboard or savedsearch privacy objects), skip the ACL. + return unless $self->{with_current_user} + or $record->CurrentUserHasRight('SeeGroup'); + + return $self->SUPER::AddRecord( $record ); +} + + + +sub _DoSearch { + my $self = shift; + + #unless we really want to find disabled rows, make sure we're only finding enabled ones. + unless($self->{'find_disabled_rows'}) { + $self->LimitToEnabled(); + } + + return($self->SUPER::_DoSearch(@_)); + +} + + + +=head2 NewItem + +Returns an empty new RT::Group item + +=cut + +sub NewItem { + my $self = shift; + return(RT::Group->new($self->CurrentUser)); +} +RT::Base->_ImportOverlays(); 1;