fix agent-specific invoice_default_terms on new customer add, RT#76862
[freeside.git] / rt / lib / RT / ACL.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2015 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 = RT::ACL->new($CurrentUser);
57
58 =head1 DESCRIPTION
59
60
61 =head1 METHODS
62
63
64 =cut
65
66
67 package RT::ACL;
68 use RT::ACE;
69
70 use base 'RT::SearchBuilder';
71
72 sub Table { 'ACL'}
73
74 use strict;
75 use warnings;
76
77
78
79 =head2 Next
80
81 Hand out the next ACE that was found
82
83 =cut
84
85
86
87 =head2 LimitToObject $object
88
89 Limit the ACL to rights for the object $object. It needs to be an RT::Record class.
90
91 =cut
92
93 sub LimitToObject {
94     my $self = shift;
95     my $obj  = shift;
96
97     my $obj_type = ref($obj)||$obj;
98     my $obj_id = eval { $obj->id};
99
100     my $object_clause = 'possible_objects';
101     $self->_OpenParen($object_clause);
102     $self->Limit(
103         SUBCLAUSE       => $object_clause,
104         FIELD           => 'ObjectType',
105         OPERATOR        => '=',
106         VALUE           => (ref($obj)||$obj),
107         ENTRYAGGREGATOR => 'OR' # That "OR" applies to the separate objects we're searching on, not "Type Or ID"
108     );
109     if ($obj_id) {
110     $self->Limit(
111         SUBCLAUSE       => $object_clause,
112         FIELD           => 'ObjectId',
113         OPERATOR        => '=',
114         VALUE           => $obj_id,
115         ENTRYAGGREGATOR => 'AND',
116         QUOTEVALUE      => 0
117     );
118     }
119     $self->_CloseParen($object_clause);
120
121 }
122
123
124
125 =head2 LimitNotObject $object
126
127 Limit the ACL to rights NOT on the object $object.  $object needs to be
128 an RT::Record class.
129
130 =cut
131
132 sub LimitNotObject {
133     my $self = shift;
134     my $obj  = shift;
135     unless ( defined($obj)
136         && ref($obj)
137         && UNIVERSAL::can( $obj, 'id' )
138         && $obj->id )
139     {
140         return undef;
141     }
142     $self->Limit( FIELD => 'ObjectType',
143                   OPERATOR => '!=',
144                   VALUE => ref($obj),
145                   ENTRYAGGREGATOR => 'OR',
146                   SUBCLAUSE => $obj->id
147                 );
148     $self->Limit( FIELD => 'ObjectId',
149                   OPERATOR => '!=',
150                   VALUE => $obj->id,
151                   ENTRYAGGREGATOR => 'OR',
152                   QUOTEVALUE => 0,
153                   SUBCLAUSE => $obj->id
154                 );
155 }
156
157
158
159 =head2 LimitToPrincipal { Type => undef, Id => undef, IncludeGroupMembership => undef }
160
161 Limit the ACL to the principal with PrincipalId Id and PrincipalType Type
162
163 Id is not optional.
164 Type is.
165
166 if IncludeGroupMembership => 1 is specified, ACEs which apply to the principal due to group membership will be included in the resultset.
167
168
169 =cut
170
171 sub LimitToPrincipal {
172     my $self = shift;
173     my %args = ( Type                   => undef,
174                  Id                     => undef,
175                  IncludeGroupMembership => undef,
176                  @_
177                );
178     if ( $args{'IncludeGroupMembership'} ) {
179         my $cgm = $self->NewAlias('CachedGroupMembers');
180         $self->Join( ALIAS1 => 'main',
181                      FIELD1 => 'PrincipalId',
182                      ALIAS2 => $cgm,
183                      FIELD2 => 'GroupId'
184                    );
185         $self->Limit( ALIAS => $cgm,
186                       FIELD => 'Disabled',
187                       VALUE => 0 );
188         $self->Limit( ALIAS           => $cgm,
189                       FIELD           => 'MemberId',
190                       OPERATOR        => '=',
191                       VALUE           => $args{'Id'},
192                       ENTRYAGGREGATOR => 'OR'
193                     );
194     } else {
195         if ( defined $args{'Type'} ) {
196             $self->Limit( FIELD           => 'PrincipalType',
197                           OPERATOR        => '=',
198                           VALUE           => $args{'Type'},
199                           ENTRYAGGREGATOR => 'OR'
200                         );
201         }
202
203         # if the principal id points to a user, we really want to point
204         # to their ACL equivalence group. The machinations we're going through
205         # lead me to start to suspect that we really want users and groups
206         # to just be the same table. or _maybe_ that we want an object db.
207         my $princ = RT::Principal->new( RT->SystemUser );
208         $princ->Load( $args{'Id'} );
209         if ( $princ->PrincipalType eq 'User' ) {
210             my $group = RT::Group->new( RT->SystemUser );
211             $group->LoadACLEquivalenceGroup($princ);
212             $args{'Id'} = $group->PrincipalId;
213         }
214         $self->Limit( FIELD           => 'PrincipalId',
215                       OPERATOR        => '=',
216                       VALUE           => $args{'Id'},
217                       ENTRYAGGREGATOR => 'OR'
218                     );
219     }
220 }
221
222
223
224
225 sub AddRecord {
226     my $self = shift;
227     my ($record) = @_;
228
229     # Short-circuit having to load up the ->Object
230     return $self->SUPER::AddRecord( $record )
231         if $record->CurrentUser->PrincipalObj->Id == RT->SystemUser->Id;
232
233     my $obj = $record->Object;
234     return unless $self->CurrentUser->HasRight( Right  => 'ShowACL',
235                                                 Object => $obj )
236                or $self->CurrentUser->HasRight( Right  => 'ModifyACL',
237                                                 Object => $obj );
238
239     return $self->SUPER::AddRecord( $record );
240 }
241
242
243
244
245 #wrap around _DoSearch  so that we can build the hash of returned
246 #values 
247 sub _DoSearch {
248     my $self = shift;
249    # $RT::Logger->debug("Now in ".$self."->_DoSearch");
250     my $return = $self->SUPER::_DoSearch(@_);
251   #  $RT::Logger->debug("In $self ->_DoSearch. return from SUPER::_DoSearch was $return");
252     if ( $self->{'must_redo_search'} ) {
253         $RT::Logger->crit(
254 "_DoSearch is not so successful as it still needs redo search, won't call _BuildHash"
255         );
256     }
257     else {
258         $self->_BuildHash();
259     }
260     return ($return);
261 }
262
263
264 #Build a hash of this ACL's entries.
265 sub _BuildHash {
266     my $self = shift;
267
268     while (my $entry = $self->Next) {
269         my $hashkey = join '-', map $entry->__Value( $_ ),
270             qw(ObjectType ObjectId RightName PrincipalId PrincipalType);
271
272         $self->{'as_hash'}->{"$hashkey"} =1;
273
274     }
275 }
276
277
278
279 =head2 HasEntry
280
281 =cut
282
283 sub HasEntry {
284
285     my $self = shift;
286     my %args = ( RightScope => undef,
287                  RightAppliesTo => undef,
288                  RightName => undef,
289                  PrincipalId => undef,
290                  PrincipalType => undef,
291                  @_ );
292
293     #if we haven't done the search yet, do it now.
294     $self->_DoSearch();
295
296     if ($self->{'as_hash'}->{ $args{'RightScope'} . "-" .
297                               $args{'RightAppliesTo'} . "-" . 
298                               $args{'RightName'} . "-" .
299                               $args{'PrincipalId'} . "-" .
300                               $args{'PrincipalType'}
301                             } == 1) {
302         return(1);
303     }
304     else {
305         return(undef);
306     }
307 }
308
309 # }}}
310
311
312 =head2 NewItem
313
314 Returns an empty new RT::ACE item
315
316 =cut
317
318 sub NewItem {
319     my $self = shift;
320     return(RT::ACE->new($self->CurrentUser));
321 }
322 RT::Base->_ImportOverlays();
323
324 1;