import rt 3.2.2
[freeside.git] / rt / lib / RT / CachedGroupMember_Overlay.pm
1 # {{{ BEGIN BPS TAGGED BLOCK
2
3 # COPYRIGHT:
4 #  
5 # This software is Copyright (c) 1996-2004 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., 675 Mass Ave, Cambridge, MA 02139, USA.
26
27
28 # CONTRIBUTION SUBMISSION POLICY:
29
30 # (The following paragraph is not intended to limit the rights granted
31 # to you to modify and distribute this software under the terms of
32 # the GNU General Public License and is only of importance to you if
33 # you choose to contribute your changes and enhancements to the
34 # community by submitting them to Best Practical Solutions, LLC.)
35
36 # By intentionally submitting any modifications, corrections or
37 # derivatives to this work, or any other work intended for use with
38 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
39 # you are the copyright holder for those contributions and you grant
40 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
41 # royalty-free, perpetual, license to use, copy, create derivative
42 # works based on those contributions, and sublicense and distribute
43 # those contributions and any derivatives thereof.
44
45 # }}} END BPS TAGGED BLOCK
46 use strict;
47 no warnings qw(redefine);
48
49 # {{{ Create
50
51 =item Create PARAMHASH
52
53 Create takes a hash of values and creates a row in the database:
54
55   'Group' is the "top level" group we're building the cache for. This is an 
56   RT::Principal object
57
58   'Member' is the RT::Principal  of the user or group we're adding
59   to the cache.
60
61   'ImmediateParent' is the RT::Principal of the group that this principal
62   belongs to to get here
63
64   int(11) 'Via' is an internal reference to CachedGroupMembers->Id of
65   the "parent" record of this cached group member. It should be empty if this
66   member is a "direct" member of this group. (In that case, it will be set to this 
67   cached group member's id after creation)
68
69   This routine should _only_ be called by GroupMember->Create
70
71 =cut
72
73 sub Create {
74     my $self = shift;
75     my %args = ( Group           => '',
76                  Member          => '',
77                  ImmediateParent => '',
78                  Via             => '0',
79                  Disabled        => '0',
80                  @_ );
81
82     unless (    $args{'Member'}
83              && UNIVERSAL::isa( $args{'Member'}, 'RT::Principal' )
84              && $args{'Member'}->Id ) {
85         $RT::Logger->debug("$self->Create: bogus Member argument");
86     }
87
88     unless (    $args{'Group'}
89              && UNIVERSAL::isa( $args{'Group'}, 'RT::Principal' )
90              && $args{'Group'}->Id ) {
91         $RT::Logger->debug("$self->Create: bogus Group argument");
92     }
93
94     unless (    $args{'ImmediateParent'}
95              && UNIVERSAL::isa( $args{'ImmediateParent'}, 'RT::Principal' )
96              && $args{'ImmediateParent'}->Id ) {
97         $RT::Logger->debug("$self->Create: bogus ImmediateParent argument");
98     }
99
100     # If the parent group for this group member is disabled, it's disabled too, along with all its children
101     if ( $args{'ImmediateParent'}->Disabled ) {
102         $args{'Disabled'} = $args{'ImmediateParent'}->Disabled;
103     }
104
105     my $id = $self->SUPER::Create(
106                               GroupId           => $args{'Group'}->Id,
107                               MemberId          => $args{'Member'}->Id,
108                               ImmediateParentId => $args{'ImmediateParent'}->Id,
109                               Disabled          => $args{'Disabled'},
110                               Via               => $args{'Via'}, );
111
112     unless ($id) {
113         $RT::Logger->warning( "Couldn't create "
114                            . $args{'Member'}
115                            . " as a cached member of "
116                            . $args{'Group'}->Id . " via "
117                            . $args{'Via'} );
118         return (undef);  #this will percolate up and bail out of the transaction
119     }
120     if ( $self->__Value('Via') == 0 ) {
121         my ( $vid, $vmsg ) = $self->__Set( Field => 'Via', Value => $id );
122         unless ($vid) {
123             $RT::Logger->warning( "Due to a via error, couldn't create "
124                                . $args{'Member'}
125                                . " as a cached member of "
126                                . $args{'Group'}->Id . " via "
127                                . $args{'Via'} );
128             return (undef)
129               ;          #this will percolate up and bail out of the transaction
130         }
131     }
132
133     if ( $args{'Member'}->IsGroup() ) {
134         my $GroupMembers = $args{'Member'}->Object->MembersObj();
135         while ( my $member = $GroupMembers->Next() ) {
136             my $cached_member =
137               RT::CachedGroupMember->new( $self->CurrentUser );
138             my $c_id = $cached_member->Create(
139                                              Group  => $args{'Group'},
140                                              Member => $member->MemberObj,
141                                              ImmediateParent => $args{'Member'},
142                                              Disabled => $args{'Disabled'},
143                                              Via      => $id );
144             unless ($c_id) {
145                 return (undef);    #percolate the error upwards.
146                      # the caller will log an error and abort the transaction
147             }
148
149         }
150     }
151
152     return ($id);
153
154 }
155
156 # }}}
157
158 # {{{ Delete
159
160 =head2 Delete
161
162 Deletes the current CachedGroupMember from the group it's in and cascades 
163 the delete to all submembers. This routine could be completely excised if
164 mysql supported foreign keys with cascading deletes.
165
166 =cut 
167
168 sub Delete {
169     my $self = shift;
170
171     
172     my $member = $self->MemberObj();
173     if ( $member->IsGroup ) {
174         my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
175
176         $deletable->Limit( FIELD    => 'id',
177                            OPERATOR => '!=',
178                            VALUE    => $self->id );
179         $deletable->Limit( FIELD    => 'Via',
180                            OPERATOR => '=',
181                            VALUE    => $self->id );
182
183         while ( my $kid = $deletable->Next ) {
184             my $kid_err = $kid->Delete();
185             unless ($kid_err) {
186                 $RT::Logger->error(
187                               "Couldn't delete CachedGroupMember " . $kid->Id );
188                 return (undef);
189             }
190         }
191     }
192     my $err = $self->SUPER::Delete();
193     unless ($err) {
194         $RT::Logger->error( "Couldn't delete CachedGroupMember " . $self->Id );
195         return (undef);
196     }
197
198     # Unless $self->GroupObj still has the member recursively $self->MemberObj
199     # (Since we deleted the database row above, $self no longer counts)
200     unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
201
202
203         #   Find all ACEs granted to $self->GroupId
204         my $acl = RT::ACL->new($RT::SystemUser);
205         $acl->LimitToPrincipal( Id => $self->GroupId );
206
207
208         while ( my $this_ace = $acl->Next() ) {
209             #       Find all ACEs which $self-MemberObj has delegated from $this_ace
210             my $delegations = RT::ACL->new($RT::SystemUser);
211             $delegations->DelegatedFrom( Id => $this_ace->Id );
212             $delegations->DelegatedBy( Id => $self->MemberId );
213
214             # For each delegation 
215             while ( my $delegation = $delegations->Next ) {
216                 # WHACK IT
217                 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
218                 unless ($del_ret) {
219                     $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
220                     return(undef);
221                 }
222             }
223         }
224     }
225     return ($err);
226 }
227
228 # }}}
229
230 # {{{ SetDisabled
231
232 =head2 SetDisabled
233
234 SetDisableds the current CachedGroupMember from the group it's in and cascades 
235 the SetDisabled to all submembers. This routine could be completely excised if
236 mysql supported foreign keys with cascading SetDisableds.
237
238 =cut 
239
240 sub SetDisabled {
241     my $self = shift;
242     my $val = shift;
243     
244     my $err = $self->SUPER::SetDisabled($val);
245     unless ($err) {
246         $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $self->Id );
247         return (undef);
248     }
249     
250     my $member = $self->MemberObj();
251     if ( $member->IsGroup ) {
252         my $deletable = RT::CachedGroupMembers->new( $self->CurrentUser );
253
254         $deletable->Limit( FIELD    => 'Via', OPERATOR => '=', VALUE    => $self->id );
255         $deletable->Limit( FIELD    => 'id', OPERATOR => '!=', VALUE    => $self->id );
256
257         while ( my $kid = $deletable->Next ) {
258             my $kid_err = $kid->SetDisabled($val );
259             unless ($kid_err) {
260                 $RT::Logger->error( "Couldn't SetDisabled CachedGroupMember " . $kid->Id );
261                 return (undef);
262             }
263         }
264     }
265
266     # Unless $self->GroupObj still has the member recursively $self->MemberObj
267     # (Since we SetDisabledd the database row above, $self no longer counts)
268     unless ( $self->GroupObj->Object->HasMemberRecursively( $self->MemberObj ) ) {
269         #   Find all ACEs granted to $self->GroupId
270         my $acl = RT::ACL->new($RT::SystemUser);
271         $acl->LimitToPrincipal( Id => $self->GroupId );
272
273         while ( my $this_ace = $acl->Next() ) {
274             #       Find all ACEs which $self-MemberObj has delegated from $this_ace
275             my $delegations = RT::ACL->new($RT::SystemUser);
276             $delegations->DelegatedFrom( Id => $this_ace->Id );
277             $delegations->DelegatedBy( Id => $self->MemberId );
278
279             # For each delegation,  blow away the delegation
280             while ( my $delegation = $delegations->Next ) {
281                 # WHACK IT
282                 my $del_ret = $delegation->_Delete(InsideTransaction => 1);
283                 unless ($del_ret) {
284                     $RT::Logger->crit("Couldn't delete an ACL delegation that we know exists ". $delegation->Id);
285                     return(undef);
286                 }
287             }
288         }
289     }
290     return ($err);
291 }
292
293 # }}}
294
295 # {{{ GroupObj
296
297 =head2 GroupObj  
298
299 Returns the RT::Principal object for this group Group
300
301 =cut
302
303 sub GroupObj {
304     my $self      = shift;
305     my $principal = RT::Principal->new( $self->CurrentUser );
306     $principal->Load( $self->GroupId );
307     return ($principal);
308 }
309
310 # }}}
311
312 # {{{ ImmediateParentObj
313
314 =head2 ImmediateParentObj  
315
316 Returns the RT::Principal object for this group ImmediateParent
317
318 =cut
319
320 sub ImmediateParentObj {
321     my $self      = shift;
322     my $principal = RT::Principal->new( $self->CurrentUser );
323     $principal->Load( $self->ImmediateParentId );
324     return ($principal);
325 }
326
327 # }}}
328
329 # {{{ MemberObj
330
331 =head2 MemberObj  
332
333 Returns the RT::Principal object for this group member
334
335 =cut
336
337 sub MemberObj {
338     my $self      = shift;
339     my $principal = RT::Principal->new( $self->CurrentUser );
340     $principal->Load( $self->MemberId );
341     return ($principal);
342 }
343
344 # }}}
345 1;