X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=rt%2Flib%2FRT%2FGroups.pm;h=7ea42fd6e4cf5ab69d9da6ff5f5daed04b969f04;hb=de9d037528895f7151a9aead6724ce2df95f9586;hp=46337f7be7936197f7425e9cade7d520aa817dbe;hpb=d4d0590bef31071e8809ec046717444b95b3f30a;p=freeside.git diff --git a/rt/lib/RT/Groups.pm b/rt/lib/RT/Groups.pm index 46337f7be..7ea42fd6e 100755 --- a/rt/lib/RT/Groups.pm +++ b/rt/lib/RT/Groups.pm @@ -1,38 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC -# -# +# +# This software is Copyright (c) 1996-2017 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. -# +# # 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., 675 Mass Ave, Cambridge, MA 02139, USA. -# -# +# 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 @@ -41,97 +43,458 @@ # 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 }}} -# 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; - +# 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 base 'RT::SearchBuilder'; + +sub Table { 'Groups'} + use RT::Group; +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. -use vars qw( @ISA ); -@ISA= qw(RT::SearchBuilder); -sub _Init { +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 + +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', CASESENSITIVE => 0 ); + # All system internal groups have the same instance. No reason to limit down further + #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '0'); } -=head2 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', CASESENSITIVE => 0 ); + # All user-defined groups have the same instance. No reason to limit down further + #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => ''); } - eval "require RT::Groups_Overlay"; - if ($@ && $@ !~ qr{^Can't locate RT/Groups_Overlay.pm}) { - die $@; - }; +=head2 LimitToRolesForObject OBJECT - eval "require RT::Groups_Vendor"; - if ($@ && $@ !~ qr{^Can't locate RT/Groups_Vendor.pm}) { - die $@; - }; +Limits the set of groups to role groups specifically for the object in question +based on the object's class and ID. If the object has no ID, the roles are not +limited by group C. That is, calling this method on an unloaded +object will find all role groups for that class of object. - eval "require RT::Groups_Local"; - if ($@ && $@ !~ qr{^Can't locate RT/Groups_Local.pm}) { - die $@; - }; +Replaces L, L, and +L. +=cut +sub LimitToRolesForObject { + my $self = shift; + my $object = shift; + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => ref($object) . "-Role", CASESENSITIVE => 0 ); + $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $object->id); +} +=head2 LimitToRolesForQueue QUEUE_ID -=head1 SEE ALSO +B. Use L instead. -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. +Limits the set of groups found to role groups for queue QUEUE_ID -These overlay files can contain new subs or subs to replace existing subs in this module. +=cut + +sub LimitToRolesForQueue { + my $self = shift; + my $queue = shift; + RT->Deprecated( + Instead => "LimitToRolesForObject", + Remove => "4.4", + ); + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Queue-Role', CASESENSITIVE => 0 ); + $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $queue); +} + + + +=head2 LimitToRolesForTicket Ticket_ID + +B. Use L instead. -Each of these files should begin with the line +Limits the set of groups found to role groups for Ticket Ticket_ID - no warnings qw(redefine); +=cut + +sub LimitToRolesForTicket { + my $self = shift; + my $Ticket = shift; + RT->Deprecated( + Instead => "LimitToRolesForObject", + Remove => "4.4", + ); + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Ticket-Role', CASESENSITIVE => 0 ); + $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $Ticket); +} -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 + +=head2 LimitToRolesForSystem System_ID + +B. Use L instead. + +Limits the set of groups found to role groups for System System_ID =cut +sub LimitToRolesForSystem { + my $self = shift; + RT->Deprecated( + Instead => "LimitToRolesForObject", + Remove => "4.4", + ); + $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::System-Role', CASESENSITIVE => 0 ); + $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => RT::System->Id ); +} + + +=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 = $self->Join( + ALIAS1 => 'main', FIELD1 => 'id', + $args{'Recursively'} + ? (TABLE2 => 'CachedGroupMembers') + # (GroupId, MemberId) is unique in GM table + : (TABLE2 => 'GroupMembers', DISTINCT => 1) + , + 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; +} + +sub WithCurrentUser { + my $self = shift; + $self->{with_current_user} = 1; + return $self->WithMember( + PrincipalId => $self->CurrentUser->PrincipalId, + Recursively => 1, + ); +} + +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', + DISTINCT => $members eq 'GroupMembers', + ); + $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, + ); +} + +=head2 WithRight { Right => RIGHTNAME, Object => RT::Record, IncludeSystemRights => 1, IncludeSuperusers => 0, EquivObjects => [ ] } + + +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. + + + +=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(@_)); + +} + +RT::Base->_ImportOverlays(); 1;