Putting 4.2.0 on top of 4.0.17
[usit-rt.git] / lib / RT / Groups.pm
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
72 package RT::Groups;
73
74 use strict;
75 use warnings;
76
77 use base 'RT::SearchBuilder';
78
79 sub Table { 'Groups'}
80
81 use RT::Group;
82 use 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
91 sub _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
122 Returns the string that represents this Users object's primary "Principals" alias.
123
124 =cut
125
126 # XXX: should be generalized, code duplication
127 sub PrincipalsAlias {
128     my $self = shift;
129     return($self->{'princalias'});
130
131 }
132
133
134
135 =head2 LimitToSystemInternalGroups
136
137 Return only SystemInternal Groups, such as "privileged" "unprivileged" and "everyone" 
138
139 =cut
140
141
142 sub 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
154 Return only UserDefined Groups
155
156 =cut
157
158
159 sub 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
168 Limits the set of groups to role groups specifically for the object in question
169 based on the object's class and ID.  If the object has no ID, the roles are not
170 limited by group C<Instance>.  That is, calling this method on an unloaded
171 object will find all role groups for that class of object.
172
173 Replaces L</LimitToRolesForQueue>, L</LimitToRolesForTicket>, and
174 L</LimitToRolesForSystem>.
175
176 =cut
177
178 sub 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
187 B<DEPRECATED>. Use L</LimitToRolesForObject> instead.
188
189 Limits the set of groups found to role groups for queue QUEUE_ID
190
191 =cut
192
193 sub 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
208 B<DEPRECATED>. Use L</LimitToRolesForObject> instead.
209
210 Limits the set of groups found to role groups for Ticket Ticket_ID
211
212 =cut
213
214 sub 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
229 B<DEPRECATED>. Use L</LimitToRolesForObject> instead.
230
231 Limits the set of groups found to role groups for System System_ID
232
233 =cut
234
235 sub 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
248 Limits the set of groups returned to groups which have
249 Principal PRINCIPAL_ID as a member. Returns the alias used for the join.
250
251 =cut
252
253 sub 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
275 sub 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
316 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.
317
318
319
320 =cut
321
322 #XXX: should be generilized
323 sub 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
352 sub _JoinGroups {
353     my $self = shift;
354     my %args = (@_);
355     return 'main' unless $args{'IncludeSubgroupMembers'};
356     return $self->RT::Users::_JoinGroups( %args );
357 }
358 sub _JoinGroupMembers {
359     my $self = shift;
360     my %args = (@_);
361     return 'main' unless $args{'IncludeSubgroupMembers'};
362     return $self->RT::Users::_JoinGroupMembers( %args );
363 }
364 sub _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 }
377 sub _JoinACL                  { return (shift)->RT::Users::_JoinACL( @_ ) }
378 sub _RoleClauses              { return (shift)->RT::Users::_RoleClauses( @_ ) }
379 sub _WhoHaveRoleRightSplitted { return (shift)->RT::Users::_WhoHaveRoleRightSplitted( @_ ) }
380 sub _GetEquivObjects          { return (shift)->RT::Users::_GetEquivObjects( @_ ) }
381 sub WithGroupRight            { return (shift)->RT::Users::WhoHaveGroupRight( @_ ) }
382 sub WithRoleRight             { return (shift)->RT::Users::WhoHaveRoleRight( @_ ) }
383
384 sub 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
427 Only find items that haven't been disabled
428
429 =cut
430
431 sub 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
445 Only find items that have been deleted.
446
447 =cut
448
449 sub 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
462 sub 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
482 sub _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
494 RT::Base->_ImportOverlays();
495
496 1;