import rt 3.8.7
[freeside.git] / rt / lib / RT / ACL_Overlay.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2
3 # COPYRIGHT:
4
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 #                                          <jesse@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           => 'MemberId',
180                       OPERATOR        => '=',
181                       VALUE           => $args{'Id'},
182                       ENTRYAGGREGATOR => 'OR' );
183     }
184     else {
185         if ( defined $args{'Type'} ) {
186             $self->Limit( FIELD           => 'PrincipalType',
187                           OPERATOR        => '=',
188                           VALUE           => $args{'Type'},
189                           ENTRYAGGREGATOR => 'OR' );
190         }
191     # if the principal id points to a user, we really want to point
192     # to their ACL equivalence group. The machinations we're going through
193     # lead me to start to suspect that we really want users and groups
194     # to just be the same table. or _maybe_ that we want an object db.
195     my $princ = RT::Principal->new($RT::SystemUser);
196     $princ->Load($args{'Id'});
197     if ($princ->PrincipalType eq 'User') {
198     my $group = RT::Group->new($RT::SystemUser);
199         $group->LoadACLEquivalenceGroup($princ);
200         $args{'Id'} = $group->PrincipalId;
201     }
202         $self->Limit( FIELD           => 'PrincipalId',
203                       OPERATOR        => '=',
204                       VALUE           => $args{'Id'},
205                       ENTRYAGGREGATOR => 'OR' );
206     }
207 }
208
209 # }}}
210
211
212
213 # {{{ ExcludeDelegatedRights
214
215 =head2 ExcludeDelegatedRights 
216
217 Don't list rights which have been delegated.
218
219 =cut
220
221 sub ExcludeDelegatedRights {
222     my $self = shift;
223     $self->DelegatedBy(Id => 0);
224     $self->DelegatedFrom(Id => 0);
225 }
226 # }}}
227
228 # {{{ DelegatedBy 
229
230 =head2 DelegatedBy { Id => undef }
231
232 Limit the ACL to rights delegated by the principal whose Principal Id is
233 B<Id>
234
235 Id is not optional.
236
237 =cut
238
239 sub DelegatedBy {
240     my $self = shift;
241     my %args = (
242         Id => undef,
243         @_
244     );
245     $self->Limit(
246         FIELD           => 'DelegatedBy',
247         OPERATOR        => '=',
248         VALUE           => $args{'Id'},
249         ENTRYAGGREGATOR => 'OR'
250     );
251
252 }
253
254 # }}}
255
256 # {{{ DelegatedFrom 
257
258 =head2 DelegatedFrom { Id => undef }
259
260 Limit the ACL to rights delegate from the ACE which has the Id specified 
261 by the Id parameter.
262
263 Id is not optional.
264
265 =cut
266
267 sub DelegatedFrom {
268     my $self = shift;
269     my %args = (
270                  Id => undef,
271                  @_);
272     $self->Limit(FIELD => 'DelegatedFrom', OPERATOR=> '=', VALUE => $args{'Id'}, ENTRYAGGREGATOR => 'OR');
273
274 }
275
276 # }}}
277
278
279 # {{{ sub Next 
280 sub Next {
281     my $self = shift;
282
283     my $ACE = $self->SUPER::Next();
284     if ( ( defined($ACE) ) and ( ref($ACE) ) ) {
285
286         if ( $self->CurrentUser->HasRight( Right  => 'ShowACL',
287                                            Object => $ACE->Object )
288              or $self->CurrentUser->HasRight( Right  => 'ModifyACL',
289                                               Object => $ACE->Object )
290           ) {
291             return ($ACE);
292         }
293
294         #If the user doesn't have the right to show this ACE
295         else {
296             return ( $self->Next() );
297         }
298     }
299
300     #if there never was any ACE
301     else {
302         return (undef);
303     }
304
305 }
306
307 # }}}
308
309
310
311 #wrap around _DoSearch  so that we can build the hash of returned
312 #values 
313 sub _DoSearch {
314     my $self = shift;
315    # $RT::Logger->debug("Now in ".$self."->_DoSearch");
316     my $return = $self->SUPER::_DoSearch(@_);
317   #  $RT::Logger->debug("In $self ->_DoSearch. return from SUPER::_DoSearch was $return");
318     $self->_BuildHash();
319     return ($return);
320 }
321
322
323 #Build a hash of this ACL's entries.
324 sub _BuildHash {
325     my $self = shift;
326
327     while (my $entry = $self->Next) {
328         my $hashkey = join '-', map $entry->__Value( $_ ),
329             qw(ObjectType ObjectId RightName PrincipalId PrincipalType);
330
331         $self->{'as_hash'}->{"$hashkey"} =1;
332
333     }
334 }
335
336
337 # {{{ HasEntry
338
339 =head2 HasEntry
340
341 =cut
342
343 sub HasEntry {
344
345     my $self = shift;
346     my %args = ( RightScope => undef,
347                  RightAppliesTo => undef,
348                  RightName => undef,
349                  PrincipalId => undef,
350                  PrincipalType => undef,
351                  @_ );
352
353     #if we haven't done the search yet, do it now.
354     $self->_DoSearch();
355
356     if ($self->{'as_hash'}->{ $args{'RightScope'} . "-" .
357                               $args{'RightAppliesTo'} . "-" . 
358                               $args{'RightName'} . "-" .
359                               $args{'PrincipalId'} . "-" .
360                               $args{'PrincipalType'}
361                             } == 1) {
362         return(1);
363     }
364     else {
365         return(undef);
366     }
367 }
368
369 # }}}
370 1;