RT 3.8.17
[freeside.git] / rt / lib / RT / ACL_Overlay.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::ACL - collection of RT ACE objects
52
53 =head1 SYNOPSIS
54
55   use RT::ACL;
56 my $ACL = new RT::ACL($CurrentUser);
57
58 =head1 DESCRIPTION
59
60
61 =head1 METHODS
62
63
64 =cut
65
66
67 package RT::ACL;
68
69 use strict;
70 no warnings qw(redefine);
71
72
73 =head2 Next
74
75 Hand out the next ACE that was found
76
77 =cut
78
79
80 # {{{ LimitToObject 
81
82 =head2 LimitToObject $object
83
84 Limit the ACL to rights for the object $object. It needs to be an RT::Record class.
85
86 =cut
87
88 sub LimitToObject {
89     my $self = shift;
90     my $obj  = shift;
91     unless ( defined($obj)
92         && ref($obj)
93         && UNIVERSAL::can( $obj, 'id' )
94         && $obj->id )
95     {
96         return undef;
97     }
98     $self->Limit(
99         FIELD           => 'ObjectType',
100         OPERATOR        => '=',
101         VALUE           => ref($obj),
102         ENTRYAGGREGATOR => 'OR'
103     );
104     $self->Limit(
105         FIELD           => 'ObjectId',
106         OPERATOR        => '=',
107         VALUE           => $obj->id,
108         ENTRYAGGREGATOR => 'OR',
109         QUOTEVALUE      => 0
110     );
111
112 }
113
114 # }}}
115
116 # {{{ LimitNotObject
117
118 =head2 LimitNotObject $object
119
120 Limit the ACL to rights NOT on the object $object.  $object needs to be
121 an RT::Record class.
122
123 =cut
124
125 sub LimitNotObject {
126     my $self = shift;
127     my $obj  = shift;
128     unless ( defined($obj)
129         && ref($obj)
130         && UNIVERSAL::can( $obj, 'id' )
131         && $obj->id )
132     {
133         return undef;
134     }
135     $self->Limit( FIELD => 'ObjectType',
136                   OPERATOR => '!=',
137                   VALUE => ref($obj),
138                   ENTRYAGGREGATOR => 'OR',
139                   SUBCLAUSE => $obj->id
140                 );
141     $self->Limit( FIELD => 'ObjectId',
142                   OPERATOR => '!=',
143                   VALUE => $obj->id,
144                   ENTRYAGGREGATOR => 'OR',
145                   QUOTEVALUE => 0,
146                   SUBCLAUSE => $obj->id
147                 );
148 }
149
150 # }}}
151
152 # {{{ LimitToPrincipal 
153
154 =head2 LimitToPrincipal { Type => undef, Id => undef, IncludeGroupMembership => undef }
155
156 Limit the ACL to the principal with PrincipalId Id and PrincipalType Type
157
158 Id is not optional.
159 Type is.
160
161 if IncludeGroupMembership => 1 is specified, ACEs which apply to the principal due to group membership will be included in the resultset.
162
163
164 =cut
165
166 sub LimitToPrincipal {
167     my $self = shift;
168     my %args = ( Type                               => undef,
169                  Id                                 => undef,
170                  IncludeGroupMembership => undef,
171                  @_ );
172     if ( $args{'IncludeGroupMembership'} ) {
173         my $cgm = $self->NewAlias('CachedGroupMembers');
174         $self->Join( ALIAS1 => 'main',
175                      FIELD1 => 'PrincipalId',
176                      ALIAS2 => $cgm,
177                      FIELD2 => 'GroupId' );
178         $self->Limit( ALIAS => $cgm,
179                       FIELD => 'Disabled',
180                       VALUE => 0 );
181         $self->Limit( ALIAS           => $cgm,
182                       FIELD           => 'MemberId',
183                       OPERATOR        => '=',
184                       VALUE           => $args{'Id'},
185                       ENTRYAGGREGATOR => 'OR' );
186     }
187     else {
188         if ( defined $args{'Type'} ) {
189             $self->Limit( FIELD           => 'PrincipalType',
190                           OPERATOR        => '=',
191                           VALUE           => $args{'Type'},
192                           ENTRYAGGREGATOR => 'OR' );
193         }
194     # if the principal id points to a user, we really want to point
195     # to their ACL equivalence group. The machinations we're going through
196     # lead me to start to suspect that we really want users and groups
197     # to just be the same table. or _maybe_ that we want an object db.
198     my $princ = RT::Principal->new($RT::SystemUser);
199     $princ->Load($args{'Id'});
200     if ($princ->PrincipalType eq 'User') {
201     my $group = RT::Group->new($RT::SystemUser);
202         $group->LoadACLEquivalenceGroup($princ);
203         $args{'Id'} = $group->PrincipalId;
204     }
205         $self->Limit( FIELD           => 'PrincipalId',
206                       OPERATOR        => '=',
207                       VALUE           => $args{'Id'},
208                       ENTRYAGGREGATOR => 'OR' );
209     }
210 }
211
212 # }}}
213
214
215
216 # {{{ ExcludeDelegatedRights
217
218 =head2 ExcludeDelegatedRights 
219
220 Don't list rights which have been delegated.
221
222 =cut
223
224 sub ExcludeDelegatedRights {
225     my $self = shift;
226     $self->DelegatedBy(Id => 0);
227     $self->DelegatedFrom(Id => 0);
228 }
229 # }}}
230
231 # {{{ DelegatedBy 
232
233 =head2 DelegatedBy { Id => undef }
234
235 Limit the ACL to rights delegated by the principal whose Principal Id is
236 B<Id>
237
238 Id is not optional.
239
240 =cut
241
242 sub DelegatedBy {
243     my $self = shift;
244     my %args = (
245         Id => undef,
246         @_
247     );
248     $self->Limit(
249         FIELD           => 'DelegatedBy',
250         OPERATOR        => '=',
251         VALUE           => $args{'Id'},
252         ENTRYAGGREGATOR => 'OR'
253     );
254
255 }
256
257 # }}}
258
259 # {{{ DelegatedFrom 
260
261 =head2 DelegatedFrom { Id => undef }
262
263 Limit the ACL to rights delegate from the ACE which has the Id specified 
264 by the Id parameter.
265
266 Id is not optional.
267
268 =cut
269
270 sub DelegatedFrom {
271     my $self = shift;
272     my %args = (
273                  Id => undef,
274                  @_);
275     $self->Limit(FIELD => 'DelegatedFrom', OPERATOR=> '=', VALUE => $args{'Id'}, ENTRYAGGREGATOR => 'OR');
276
277 }
278
279 # }}}
280
281
282 # {{{ sub Next 
283 sub Next {
284     my $self = shift;
285
286     my $ACE = $self->SUPER::Next();
287     if ( ( defined($ACE) ) and ( ref($ACE) ) ) {
288
289         if ( $self->CurrentUser->HasRight( Right  => 'ShowACL',
290                                            Object => $ACE->Object )
291              or $self->CurrentUser->HasRight( Right  => 'ModifyACL',
292                                               Object => $ACE->Object )
293           ) {
294             return ($ACE);
295         }
296
297         #If the user doesn't have the right to show this ACE
298         else {
299             return ( $self->Next() );
300         }
301     }
302
303     #if there never was any ACE
304     else {
305         return (undef);
306     }
307
308 }
309
310 # }}}
311
312
313
314 #wrap around _DoSearch  so that we can build the hash of returned
315 #values 
316 sub _DoSearch {
317     my $self = shift;
318    # $RT::Logger->debug("Now in ".$self."->_DoSearch");
319     my $return = $self->SUPER::_DoSearch(@_);
320   #  $RT::Logger->debug("In $self ->_DoSearch. return from SUPER::_DoSearch was $return");
321     if ( $self->{'must_redo_search'} ) {
322         $RT::Logger->crit(
323 "_DoSearch is not so successful as it still needs redo search, won't call _BuildHash"
324         );
325     }
326     else {
327         $self->_BuildHash();
328     }
329     return ($return);
330 }
331
332
333 #Build a hash of this ACL's entries.
334 sub _BuildHash {
335     my $self = shift;
336
337     while (my $entry = $self->Next) {
338         my $hashkey = join '-', map $entry->__Value( $_ ),
339             qw(ObjectType ObjectId RightName PrincipalId PrincipalType);
340
341         $self->{'as_hash'}->{"$hashkey"} =1;
342
343     }
344 }
345
346
347 # {{{ HasEntry
348
349 =head2 HasEntry
350
351 =cut
352
353 sub HasEntry {
354
355     my $self = shift;
356     my %args = ( RightScope => undef,
357                  RightAppliesTo => undef,
358                  RightName => undef,
359                  PrincipalId => undef,
360                  PrincipalType => undef,
361                  @_ );
362
363     #if we haven't done the search yet, do it now.
364     $self->_DoSearch();
365
366     if ($self->{'as_hash'}->{ $args{'RightScope'} . "-" .
367                               $args{'RightAppliesTo'} . "-" . 
368                               $args{'RightName'} . "-" .
369                               $args{'PrincipalId'} . "-" .
370                               $args{'PrincipalType'}
371                             } == 1) {
372         return(1);
373     }
374     else {
375         return(undef);
376     }
377 }
378
379 # }}}
380 1;