Putting 4.2.0 on top of 4.0.17
[usit-rt.git] / lib / RT / Groups.pm
... / ...
CommitLineData
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
5# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
6# <sales@bestpractical.com>
7#
8# (Except where explicitly superseded by other copyright notices)
9#
10#
11# LICENSE:
12#
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
16# from www.gnu.org.
17#
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.
22#
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.
28#
29#
30# CONTRIBUTION SUBMISSION POLICY:
31#
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.)
37#
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.
46#
47# END BPS TAGGED BLOCK }}}
48
49=head1 NAME
50
51 RT::Groups - a collection of RT::Group objects
52
53=head1 SYNOPSIS
54
55 use RT::Groups;
56 my $groups = RT::Groups->new($CurrentUser);
57 $groups->UnLimit();
58 while (my $group = $groups->Next()) {
59 print $group->Id ." is a group id\n";
60 }
61
62=head1 DESCRIPTION
63
64
65=head1 METHODS
66
67
68
69=cut
70
71
72package RT::Groups;
73
74use strict;
75use warnings;
76
77use base 'RT::SearchBuilder';
78
79sub Table { 'Groups'}
80
81use RT::Group;
82use RT::Users;
83
84# XXX: below some code is marked as subject to generalize in Groups, Users classes.
85# RUZ suggest name Principals::Generic or Principals::Base as abstract class, but
86# Jesse wants something that doesn't imply it's a Principals.pm subclass.
87# See comments below for candidats.
88
89
90
91sub _Init {
92 my $self = shift;
93 $self->{'with_disabled_column'} = 1;
94
95 my @result = $self->SUPER::_Init(@_);
96
97 $self->OrderBy( ALIAS => 'main',
98 FIELD => 'Name',
99 ORDER => 'ASC');
100
101 # XXX: this code should be generalized
102 $self->{'princalias'} = $self->Join(
103 ALIAS1 => 'main',
104 FIELD1 => 'id',
105 TABLE2 => 'Principals',
106 FIELD2 => 'id'
107 );
108
109 # even if this condition is useless and ids in the Groups table
110 # only match principals with type 'Group' this could speed up
111 # searches in some DBs.
112 $self->Limit( ALIAS => $self->{'princalias'},
113 FIELD => 'PrincipalType',
114 VALUE => 'Group',
115 );
116
117 return (@result);
118}
119
120=head2 PrincipalsAlias
121
122Returns the string that represents this Users object's primary "Principals" alias.
123
124=cut
125
126# XXX: should be generalized, code duplication
127sub PrincipalsAlias {
128 my $self = shift;
129 return($self->{'princalias'});
130
131}
132
133
134
135=head2 LimitToSystemInternalGroups
136
137Return only SystemInternal Groups, such as "privileged" "unprivileged" and "everyone"
138
139=cut
140
141
142sub LimitToSystemInternalGroups {
143 my $self = shift;
144 $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'SystemInternal', CASESENSITIVE => 0 );
145 # All system internal groups have the same instance. No reason to limit down further
146 #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '0');
147}
148
149
150
151
152=head2 LimitToUserDefinedGroups
153
154Return only UserDefined Groups
155
156=cut
157
158
159sub LimitToUserDefinedGroups {
160 my $self = shift;
161 $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'UserDefined', CASESENSITIVE => 0 );
162 # All user-defined groups have the same instance. No reason to limit down further
163 #$self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => '');
164}
165
166=head2 LimitToRolesForObject OBJECT
167
168Limits the set of groups to role groups specifically for the object in question
169based on the object's class and ID. If the object has no ID, the roles are not
170limited by group C<Instance>. That is, calling this method on an unloaded
171object will find all role groups for that class of object.
172
173Replaces L</LimitToRolesForQueue>, L</LimitToRolesForTicket>, and
174L</LimitToRolesForSystem>.
175
176=cut
177
178sub LimitToRolesForObject {
179 my $self = shift;
180 my $object = shift;
181 $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => ref($object) . "-Role", CASESENSITIVE => 0 );
182 $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $object->id);
183}
184
185=head2 LimitToRolesForQueue QUEUE_ID
186
187B<DEPRECATED>. Use L</LimitToRolesForObject> instead.
188
189Limits the set of groups found to role groups for queue QUEUE_ID
190
191=cut
192
193sub LimitToRolesForQueue {
194 my $self = shift;
195 my $queue = shift;
196 RT->Deprecated(
197 Instead => "LimitToRolesForObject",
198 Remove => "4.4",
199 );
200 $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Queue-Role', CASESENSITIVE => 0 );
201 $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $queue);
202}
203
204
205
206=head2 LimitToRolesForTicket Ticket_ID
207
208B<DEPRECATED>. Use L</LimitToRolesForObject> instead.
209
210Limits the set of groups found to role groups for Ticket Ticket_ID
211
212=cut
213
214sub LimitToRolesForTicket {
215 my $self = shift;
216 my $Ticket = shift;
217 RT->Deprecated(
218 Instead => "LimitToRolesForObject",
219 Remove => "4.4",
220 );
221 $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::Ticket-Role', CASESENSITIVE => 0 );
222 $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => $Ticket);
223}
224
225
226
227=head2 LimitToRolesForSystem System_ID
228
229B<DEPRECATED>. Use L</LimitToRolesForObject> instead.
230
231Limits the set of groups found to role groups for System System_ID
232
233=cut
234
235sub LimitToRolesForSystem {
236 my $self = shift;
237 RT->Deprecated(
238 Instead => "LimitToRolesForObject",
239 Remove => "4.4",
240 );
241 $self->Limit(FIELD => 'Domain', OPERATOR => '=', VALUE => 'RT::System-Role', CASESENSITIVE => 0 );
242 $self->Limit(FIELD => 'Instance', OPERATOR => '=', VALUE => RT::System->Id );
243}
244
245
246=head2 WithMember {PrincipalId => PRINCIPAL_ID, Recursively => undef}
247
248Limits the set of groups returned to groups which have
249Principal PRINCIPAL_ID as a member. Returns the alias used for the join.
250
251=cut
252
253sub WithMember {
254 my $self = shift;
255 my %args = ( PrincipalId => undef,
256 Recursively => undef,
257 @_);
258 my $members = $self->Join(
259 ALIAS1 => 'main', FIELD1 => 'id',
260 $args{'Recursively'}
261 ? (TABLE2 => 'CachedGroupMembers')
262 # (GroupId, MemberId) is unique in GM table
263 : (TABLE2 => 'GroupMembers', DISTINCT => 1)
264 ,
265 FIELD2 => 'GroupId',
266 );
267
268 $self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'});
269 $self->Limit(ALIAS => $members, FIELD => 'Disabled', VALUE => 0)
270 if $args{'Recursively'};
271
272 return $members;
273}
274
275sub WithoutMember {
276 my $self = shift;
277 my %args = (
278 PrincipalId => undef,
279 Recursively => undef,
280 @_
281 );
282
283 my $members = $args{'Recursively'} ? 'CachedGroupMembers' : 'GroupMembers';
284 my $members_alias = $self->Join(
285 TYPE => 'LEFT',
286 FIELD1 => 'id',
287 TABLE2 => $members,
288 FIELD2 => 'GroupId',
289 DISTINCT => $members eq 'GroupMembers',
290 );
291 $self->Limit(
292 LEFTJOIN => $members_alias,
293 ALIAS => $members_alias,
294 FIELD => 'MemberId',
295 OPERATOR => '=',
296 VALUE => $args{'PrincipalId'},
297 );
298 $self->Limit(
299 LEFTJOIN => $members_alias,
300 ALIAS => $members_alias,
301 FIELD => 'Disabled',
302 VALUE => 0
303 ) if $args{'Recursively'};
304 $self->Limit(
305 ALIAS => $members_alias,
306 FIELD => 'MemberId',
307 OPERATOR => 'IS',
308 VALUE => 'NULL',
309 QUOTEVALUE => 0,
310 );
311}
312
313=head2 WithRight { Right => RIGHTNAME, Object => RT::Record, IncludeSystemRights => 1, IncludeSuperusers => 0, EquivObjects => [ ] }
314
315
316Find all groups which have RIGHTNAME for RT::Record. Optionally include global rights and superusers. By default, include the global rights, but not the superusers.
317
318
319
320=cut
321
322#XXX: should be generilized
323sub WithRight {
324 my $self = shift;
325 my %args = ( Right => undef,
326 Object => => undef,
327 IncludeSystemRights => 1,
328 IncludeSuperusers => undef,
329 IncludeSubgroupMembers => 0,
330 EquivObjects => [ ],
331 @_ );
332
333 my $from_role = $self->Clone;
334 $from_role->WithRoleRight( %args );
335
336 my $from_group = $self->Clone;
337 $from_group->WithGroupRight( %args );
338
339 #XXX: DIRTY HACK
340 use DBIx::SearchBuilder::Union;
341 my $union = DBIx::SearchBuilder::Union->new();
342 $union->add($from_role);
343 $union->add($from_group);
344 %$self = %$union;
345 bless $self, ref($union);
346
347 return;
348}
349
350#XXX: methods are active aliases to Users class to prevent code duplication
351# should be generalized
352sub _JoinGroups {
353 my $self = shift;
354 my %args = (@_);
355 return 'main' unless $args{'IncludeSubgroupMembers'};
356 return $self->RT::Users::_JoinGroups( %args );
357}
358sub _JoinGroupMembers {
359 my $self = shift;
360 my %args = (@_);
361 return 'main' unless $args{'IncludeSubgroupMembers'};
362 return $self->RT::Users::_JoinGroupMembers( %args );
363}
364sub _JoinGroupMembersForGroupRights {
365 my $self = shift;
366 my %args = (@_);
367 my $group_members = $self->_JoinGroupMembers( %args );
368 unless( $group_members eq 'main' ) {
369 return $self->RT::Users::_JoinGroupMembersForGroupRights( %args );
370 }
371 $self->Limit( ALIAS => $args{'ACLAlias'},
372 FIELD => 'PrincipalId',
373 VALUE => "main.id",
374 QUOTEVALUE => 0,
375 );
376}
377sub _JoinACL { return (shift)->RT::Users::_JoinACL( @_ ) }
378sub _RoleClauses { return (shift)->RT::Users::_RoleClauses( @_ ) }
379sub _WhoHaveRoleRightSplitted { return (shift)->RT::Users::_WhoHaveRoleRightSplitted( @_ ) }
380sub _GetEquivObjects { return (shift)->RT::Users::_GetEquivObjects( @_ ) }
381sub WithGroupRight { return (shift)->RT::Users::WhoHaveGroupRight( @_ ) }
382sub WithRoleRight { return (shift)->RT::Users::WhoHaveRoleRight( @_ ) }
383
384sub ForWhichCurrentUserHasRight {
385 my $self = shift;
386 my %args = (
387 Right => undef,
388 IncludeSuperusers => undef,
389 @_,
390 );
391
392 # Non-disabled groups...
393 $self->LimitToEnabled;
394
395 # ...which are the target object of an ACL with that right, or
396 # where the target is the system object (a global right)
397 my $acl = $self->_JoinACL( %args );
398 $self->_AddSubClause(
399 ACLObjects => "( (main.id = $acl.ObjectId AND $acl.ObjectType = 'RT::Group')"
400 . " OR $acl.ObjectType = 'RT::System')");
401
402 # ...and where that right is granted to any group..
403 my $member = $self->Join(
404 ALIAS1 => $acl,
405 FIELD1 => 'PrincipalId',
406 TABLE2 => 'CachedGroupMembers',
407 FIELD2 => 'GroupId',
408 );
409 $self->Limit(
410 ALIAS => $member,
411 FIELD => 'Disabled',
412 VALUE => '0',
413 );
414
415 # ...with the current user in it
416 $self->Limit(
417 ALIAS => $member,
418 FIELD => 'MemberId',
419 VALUE => $self->CurrentUser->Id,
420 );
421
422 return;
423}
424
425=head2 LimitToEnabled
426
427Only find items that haven't been disabled
428
429=cut
430
431sub LimitToEnabled {
432 my $self = shift;
433
434 $self->{'handled_disabled_column'} = 1;
435 $self->Limit(
436 ALIAS => $self->PrincipalsAlias,
437 FIELD => 'Disabled',
438 VALUE => '0',
439 );
440}
441
442
443=head2 LimitToDeleted
444
445Only find items that have been deleted.
446
447=cut
448
449sub LimitToDeleted {
450 my $self = shift;
451
452 $self->{'handled_disabled_column'} = $self->{'find_disabled_rows'} = 1;
453 $self->Limit(
454 ALIAS => $self->PrincipalsAlias,
455 FIELD => 'Disabled',
456 VALUE => 1,
457 );
458}
459
460
461
462sub Next {
463 my $self = shift;
464
465 # Don't show groups which the user isn't allowed to see.
466
467 my $Group = $self->SUPER::Next();
468 if ((defined($Group)) and (ref($Group))) {
469 unless ($Group->CurrentUserHasRight('SeeGroup')) {
470 return $self->Next();
471 }
472
473 return $Group;
474 }
475 else {
476 return undef;
477 }
478}
479
480
481
482sub _DoSearch {
483 my $self = shift;
484
485 #unless we really want to find disabled rows, make sure we're only finding enabled ones.
486 unless($self->{'find_disabled_rows'}) {
487 $self->LimitToEnabled();
488 }
489
490 return($self->SUPER::_DoSearch(@_));
491
492}
493
494RT::Base->_ImportOverlays();
495
4961;