Upgrade to 4.2.8
[usit-rt.git] / lib / RT / ACE.pm
CommitLineData
84fb5b46
MKG
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
3ffc5f4f 5# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
84fb5b46
MKG
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 SYNOPSIS
50
51 use RT::ACE;
52 my $ace = RT::ACE->new($CurrentUser);
53
54
55=head1 DESCRIPTION
56
57
58
59=head1 METHODS
60
61
62=cut
63
64
65package RT::ACE;
66use base 'RT::Record';
67
68sub Table {'ACL'}
69
70
71use strict;
72use warnings;
73
3ffc5f4f
MKG
74require RT::Principals;
75require RT::Queues;
76require RT::Groups;
84fb5b46 77
3ffc5f4f
MKG
78our %RIGHTS;
79
80my (@_ACL_CACHE_HANDLERS);
84fb5b46
MKG
81
82
83
84=head1 Rights
85
86# Queue rights are the sort of queue rights that can only be granted
87# to real people or groups
88
89=cut
90
84fb5b46
MKG
91=head2 LoadByValues PARAMHASH
92
93Load an ACE by specifying a paramhash with the following fields:
94
95 PrincipalId => undef,
96 PrincipalType => undef,
3ffc5f4f 97 RightName => undef,
84fb5b46
MKG
98
99 And either:
100
3ffc5f4f 101 Object => undef,
84fb5b46
MKG
102
103 OR
104
3ffc5f4f
MKG
105 ObjectType => undef,
106 ObjectId => undef
84fb5b46
MKG
107
108=cut
109
110sub LoadByValues {
111 my $self = shift;
112 my %args = ( PrincipalId => undef,
113 PrincipalType => undef,
114 RightName => undef,
115 Object => undef,
116 ObjectId => undef,
117 ObjectType => undef,
118 @_ );
119
120 if ( $args{'RightName'} ) {
121 my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} );
122 unless ( $canonic_name ) {
3ffc5f4f 123 return wantarray ? ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) ) : 0;
84fb5b46
MKG
124 }
125 $args{'RightName'} = $canonic_name;
126 }
127
128 my $princ_obj;
129 ( $princ_obj, $args{'PrincipalType'} ) =
130 $self->_CanonicalizePrincipal( $args{'PrincipalId'},
131 $args{'PrincipalType'} );
132
133 unless ( $princ_obj->id ) {
3ffc5f4f 134 return wantarray ? ( 0,
84fb5b46 135 $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
3ffc5f4f 136 ) : 0;
84fb5b46
MKG
137 }
138
139 my ($object, $object_type, $object_id) = $self->_ParseObjectArg( %args );
140 unless( $object ) {
3ffc5f4f 141 return wantarray ? ( 0, $self->loc("System error. Right not granted.")) : 0;
84fb5b46
MKG
142 }
143
144 $self->LoadByCols( PrincipalId => $princ_obj->Id,
145 PrincipalType => $args{'PrincipalType'},
146 RightName => $args{'RightName'},
147 ObjectType => $object_type,
148 ObjectId => $object_id);
149
150 #If we couldn't load it.
151 unless ( $self->Id ) {
3ffc5f4f 152 return wantarray ? ( 0, $self->loc("ACE not found") ) : 0;
84fb5b46
MKG
153 }
154
155 # if we could
3ffc5f4f 156 return wantarray ? ( $self->Id, $self->loc("Right Loaded") ) : $self->Id;
84fb5b46
MKG
157
158}
159
160
161
162=head2 Create <PARAMS>
163
164PARAMS is a parameter hash with the following elements:
165
166 PrincipalId => The id of an RT::Principal object
167 PrincipalType => "User" "Group" or any Role type
168 RightName => the name of a right. in any case
169
170
171 Either:
172
173 Object => An object to create rights for. ususally, an RT::Queue or RT::Group
174 This should always be a DBIx::SearchBuilder::Record subclass
175
176 OR
177
178 ObjectType => the type of the object in question (ref ($object))
179 ObjectId => the id of the object in question $object->Id
180
181
182
183 Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's false.
184
185
186
187=cut
188
189sub Create {
190 my $self = shift;
191 my %args = (
192 PrincipalId => undef,
193 PrincipalType => undef,
194 RightName => undef,
195 Object => undef,
196 @_
197 );
198
199 unless ( $args{'RightName'} ) {
200 return ( 0, $self->loc('No right specified') );
201 }
202
203 #if we haven't specified any sort of right, we're talking about a global right
204 if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) {
205 $args{'Object'} = $RT::System;
206 }
207 ($args{'Object'}, $args{'ObjectType'}, $args{'ObjectId'}) = $self->_ParseObjectArg( %args );
208 unless( $args{'Object'} ) {
3ffc5f4f 209 return ( 0, $self->loc("System error. Right not granted.") );
84fb5b46
MKG
210 }
211
212 # Validate the principal
213 my $princ_obj;
214 ( $princ_obj, $args{'PrincipalType'} ) =
215 $self->_CanonicalizePrincipal( $args{'PrincipalId'},
216 $args{'PrincipalType'} );
217
218 unless ( $princ_obj->id ) {
219 return ( 0,
220 $self->loc( 'Principal [_1] not found.', $args{'PrincipalId'} )
221 );
222 }
223
224 # }}}
225
226 # Check the ACL
227
228 if (ref( $args{'Object'}) eq 'RT::Group' ) {
229 unless ( $self->CurrentUser->HasRight( Object => $args{'Object'},
230 Right => 'AdminGroup' )
231 ) {
232 return ( 0, $self->loc('Permission Denied') );
233 }
234 }
235
236 else {
237 unless ( $self->CurrentUser->HasRight( Object => $args{'Object'}, Right => 'ModifyACL' )) {
238 return ( 0, $self->loc('Permission Denied') );
239 }
240 }
241 # }}}
242
243 # Canonicalize and check the right name
244 my $canonic_name = $self->CanonicalizeRightName( $args{'RightName'} );
245 unless ( $canonic_name ) {
246 return ( 0, $self->loc("Invalid right. Couldn't canonicalize right '[_1]'", $args{'RightName'}) );
247 }
248 $args{'RightName'} = $canonic_name;
249
250 #check if it's a valid RightName
251 if ( $args{'Object'}->can('AvailableRights') ) {
3ffc5f4f 252 my $available = $args{'Object'}->AvailableRights($princ_obj);
84fb5b46
MKG
253 unless ( grep $_ eq $args{'RightName'}, map $self->CanonicalizeRightName( $_ ), keys %$available ) {
254 $RT::Logger->warning(
255 "Couldn't validate right name '$args{'RightName'}'"
256 ." for object of ". ref( $args{'Object'} ) ." class"
257 );
258 return ( 0, $self->loc('Invalid right') );
259 }
260 }
261 # }}}
262
263 # Make sure the right doesn't already exist.
264 $self->LoadByCols( PrincipalId => $princ_obj->id,
265 PrincipalType => $args{'PrincipalType'},
266 RightName => $args{'RightName'},
267 ObjectType => $args{'ObjectType'},
268 ObjectId => $args{'ObjectId'},
269 );
270 if ( $self->Id ) {
403d7b0b
MKG
271 return ( 0, $self->loc('[_1] already has that right',
272 $princ_obj->Object->Name) );
84fb5b46
MKG
273 }
274
275 my $id = $self->SUPER::Create( PrincipalId => $princ_obj->id,
276 PrincipalType => $args{'PrincipalType'},
277 RightName => $args{'RightName'},
278 ObjectType => ref( $args{'Object'} ),
279 ObjectId => $args{'Object'}->id,
280 );
281
84fb5b46 282 if ( $id ) {
3ffc5f4f
MKG
283 RT::ACE->InvalidateCaches(
284 Action => "Grant",
285 RightName => $self->RightName,
286 ACE => $self,
287 );
84fb5b46
MKG
288 return ( $id, $self->loc('Right Granted') );
289 }
290 else {
291 return ( 0, $self->loc('System error. Right not granted.') );
292 }
293}
294
295
296
297=head2 Delete { InsideTransaction => undef}
298
299Delete this object. This method should ONLY ever be called from RT::User or RT::Group (or from itself)
300If this is being called from within a transaction, specify a true value for the parameter InsideTransaction.
301Really, DBIx::SearchBuilder should use and/or fake subtransactions
302
303This routine will also recurse and delete any delegations of this right
304
305=cut
306
307sub Delete {
308 my $self = shift;
309
310 unless ( $self->Id ) {
311 return ( 0, $self->loc('Right not loaded.') );
312 }
313
314 # A user can delete an ACE if the current user has the right to modify it and it's not a delegated ACE
315 # or if it's a delegated ACE and it was delegated by the current user
316 unless ($self->CurrentUser->HasRight(Right => 'ModifyACL', Object => $self->Object)) {
317 return ( 0, $self->loc('Permission Denied') );
318 }
319 $self->_Delete(@_);
320}
321
322# Helper for Delete with no ACL check
323sub _Delete {
324 my $self = shift;
325 my %args = ( InsideTransaction => undef,
326 @_ );
327
328 my $InsideTransaction = $args{'InsideTransaction'};
329
330 $RT::Handle->BeginTransaction() unless $InsideTransaction;
331
3ffc5f4f
MKG
332 my $right = $self->RightName;
333
84fb5b46
MKG
334 my ( $val, $msg ) = $self->SUPER::Delete(@_);
335
336 if ($val) {
3ffc5f4f 337 RT::ACE->InvalidateCaches( Action => "Revoke", RightName => $right );
84fb5b46
MKG
338 $RT::Handle->Commit() unless $InsideTransaction;
339 return ( $val, $self->loc('Right revoked') );
340 }
341
342 $RT::Handle->Rollback() unless $InsideTransaction;
343 return ( 0, $self->loc('Right could not be revoked') );
344}
345
346
347
348=head2 _BootstrapCreate
349
350Grant a right with no error checking and no ACL. this is _only_ for
351installation. If you use this routine without the author's explicit
352written approval, he will hunt you down and make you spend eternity
353translating mozilla's code into FORTRAN or intercal.
354
355If you think you need this routine, you've mistaken.
356
357=cut
358
359sub _BootstrapCreate {
360 my $self = shift;
361 my %args = (@_);
362
363 # When bootstrapping, make sure we get the _right_ users
364 if ( $args{'UserId'} ) {
365 my $user = RT::User->new( $self->CurrentUser );
366 $user->Load( $args{'UserId'} );
367 delete $args{'UserId'};
368 $args{'PrincipalId'} = $user->PrincipalId;
369 $args{'PrincipalType'} = 'User';
370 }
371
372 my $id = $self->SUPER::Create(%args);
373
374 if ( $id > 0 ) {
375 return ($id);
376 }
377 else {
378 $RT::Logger->err('System error. right not granted.');
379 return (undef);
380 }
381
382}
383
3ffc5f4f
MKG
384=head2 InvalidateCaches
385
386Calls any registered ACL cache handlers (see L</RegisterCacheHandler>).
387
388Usually called from L</Create> and L</Delete>.
389
390=cut
391
392sub InvalidateCaches {
393 my $class = shift;
394
395 for my $handler (@_ACL_CACHE_HANDLERS) {
396 next unless ref($handler) eq "CODE";
397 $handler->(@_);
398 }
399}
400
401=head2 RegisterCacheHandler
402
403Class method. Takes a coderef and adds it to the ACL cache handlers. These
404handlers are called by L</InvalidateCaches>, usually called itself from
405L</Create> and L</Delete>.
406
407The handlers are passed a hash which may contain any (or none) of these
408optional keys:
84fb5b46 409
3ffc5f4f
MKG
410=over
411
412=item Action
413
414A string indicating the action that (may have) invalidated the cache. Expected
415values are currently:
416
417=over
418
419=item Grant
420
421=item Revoke
422
423=back
424
425However, other values may be passed in the future.
426
427=item RightName
428
429The (canonicalized) right being granted or revoked.
430
431=item ACE
432
433The L<RT::ACE> object just created.
434
435=back
436
437Your handler should be flexible enough to account for additional arguments
438being passed in the future.
439
440=cut
441
442sub RegisterCacheHandler {
443 push @_ACL_CACHE_HANDLERS, $_[1];
444}
84fb5b46
MKG
445
446sub RightName {
447 my $self = shift;
448 my $val = $self->_Value('RightName');
449 return $val unless $val;
450
451 my $available = $self->Object->AvailableRights;
452 foreach my $right ( keys %$available ) {
453 return $right if $val eq $self->CanonicalizeRightName($right);
454 }
455
456 $RT::Logger->error("Invalid right. Couldn't canonicalize right '$val'");
457 return $val;
458}
459
460=head2 CanonicalizeRightName <RIGHT>
461
462Takes a queue or system right name in any case and returns it in
463the correct case. If it's not found, will return undef.
464
465=cut
466
467sub CanonicalizeRightName {
3ffc5f4f
MKG
468 my $self = shift;
469 my $name = shift;
470 for my $class (sort keys %RIGHTS) {
471 return $RIGHTS{$class}{ lc $name }{Name}
472 if $RIGHTS{$class}{ lc $name };
473 }
474 return undef;
84fb5b46
MKG
475}
476
477
478
84fb5b46
MKG
479=head2 Object
480
481If the object this ACE applies to is a queue, returns the queue object.
482If the object this ACE applies to is a group, returns the group object.
483If it's the system object, returns undef.
484
485If the user has no rights, returns undef.
486
487=cut
488
489
490
491
492sub Object {
493 my $self = shift;
494
495 my $appliesto_obj;
496
3ffc5f4f 497 if ($self->__Value('ObjectType') && $self->__Value('ObjectType')->DOES('RT::Record::Role::Rights') ) {
84fb5b46
MKG
498 $appliesto_obj = $self->__Value('ObjectType')->new($self->CurrentUser);
499 unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) {
500 return undef;
501 }
502 $appliesto_obj->Load( $self->__Value('ObjectId') );
503 return ($appliesto_obj);
504 }
505 else {
506 $RT::Logger->warning( "$self -> Object called for an object "
507 . "of an unknown type:"
508 . $self->__Value('ObjectType') );
509 return (undef);
510 }
511}
512
513
514
515=head2 PrincipalObj
516
517Returns the RT::Principal object for this ACE.
518
519=cut
520
521sub PrincipalObj {
522 my $self = shift;
523
524 my $princ_obj = RT::Principal->new( $self->CurrentUser );
525 $princ_obj->Load( $self->__Value('PrincipalId') );
526
527 unless ( $princ_obj->Id ) {
528 $RT::Logger->err(
529 "ACE " . $self->Id . " couldn't load its principal object" );
530 }
531 return ($princ_obj);
532
533}
534
535
536
537
538sub _Set {
539 my $self = shift;
540 return ( 0, $self->loc("ACEs can only be created and deleted.") );
541}
542
543
544
545sub _Value {
546 my $self = shift;
547
548 if ( $self->PrincipalObj->IsGroup
549 && $self->PrincipalObj->Object->HasMemberRecursively(
550 $self->CurrentUser->PrincipalObj
551 )
552 ) {
553 return ( $self->__Value(@_) );
554 }
555 elsif ( $self->CurrentUser->HasRight(Right => 'ShowACL', Object => $self->Object) ) {
556 return ( $self->__Value(@_) );
557 }
558 else {
559 return undef;
560 }
561}
562
563
564
565
566
567=head2 _CanonicalizePrincipal (PrincipalId, PrincipalType)
568
569Takes a principal id and a principal type.
570
571If the principal is a user, resolves it to the proper acl equivalence group.
572Returns a tuple of (RT::Principal, PrincipalType) for the principal we really want to work with
573
574=cut
575
576sub _CanonicalizePrincipal {
577 my $self = shift;
578 my $princ_id = shift;
579 my $princ_type = shift || '';
580
581 my $princ_obj = RT::Principal->new(RT->SystemUser);
582 $princ_obj->Load($princ_id);
583
584 unless ( $princ_obj->Id ) {
585 use Carp;
586 $RT::Logger->crit(Carp::longmess);
587 $RT::Logger->crit("Can't load a principal for id $princ_id");
588 return ( $princ_obj, undef );
589 }
590
591 # Rights never get granted to users. they get granted to their
592 # ACL equivalence groups
593 if ( $princ_type eq 'User' ) {
594 my $equiv_group = RT::Group->new( $self->CurrentUser );
595 $equiv_group->LoadACLEquivalenceGroup($princ_obj);
596 unless ( $equiv_group->Id ) {
597 $RT::Logger->crit( "No ACL equiv group for princ " . $princ_obj->id );
598 return ( RT::Principal->new(RT->SystemUser), undef );
599 }
600 $princ_obj = $equiv_group->PrincipalObj();
601 $princ_type = 'Group';
602
603 }
604 return ( $princ_obj, $princ_type );
605}
606
607sub _ParseObjectArg {
608 my $self = shift;
609 my %args = ( Object => undef,
610 ObjectId => undef,
611 ObjectType => undef,
612 @_ );
613
614 if( $args{'Object'} && ($args{'ObjectId'} || $args{'ObjectType'}) ) {
3ffc5f4f
MKG
615 $RT::Logger->crit( "Method called with an ObjectType or an ObjectId and Object args" );
616 return ();
84fb5b46 617 } elsif( $args{'Object'} && ref($args{'Object'}) && !$args{'Object'}->can('id') ) {
3ffc5f4f
MKG
618 $RT::Logger->crit( "Method called called Object that has no id method" );
619 return ();
84fb5b46 620 } elsif( $args{'Object'} ) {
3ffc5f4f
MKG
621 my $obj = $args{'Object'};
622 return ($obj, ref $obj, $obj->id);
84fb5b46 623 } elsif ( $args{'ObjectType'} ) {
3ffc5f4f
MKG
624 my $obj = $args{'ObjectType'}->new( $self->CurrentUser );
625 $obj->Load( $args{'ObjectId'} );
626 return ($obj, ref $obj, $obj->id);
84fb5b46 627 } else {
3ffc5f4f
MKG
628 $RT::Logger->crit( "Method called with wrong args" );
629 return ();
84fb5b46
MKG
630 }
631}
632
633
634# }}}
635
636
637
638=head2 id
639
640Returns the current value of id.
641(In the database, id is stored as int(11).)
642
643
644=cut
645
646
647=head2 PrincipalType
648
649Returns the current value of PrincipalType.
650(In the database, PrincipalType is stored as varchar(25).)
651
652
653
654=head2 SetPrincipalType VALUE
655
656
657Set PrincipalType to VALUE.
658Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
659(In the database, PrincipalType will be stored as a varchar(25).)
660
661
662=cut
663
664
665=head2 PrincipalId
666
667Returns the current value of PrincipalId.
668(In the database, PrincipalId is stored as int(11).)
669
670
671
672=head2 SetPrincipalId VALUE
673
674
675Set PrincipalId to VALUE.
676Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
677(In the database, PrincipalId will be stored as a int(11).)
678
679
680=cut
681
682
683=head2 RightName
684
685Returns the current value of RightName.
686(In the database, RightName is stored as varchar(25).)
687
688
689
690=head2 SetRightName VALUE
691
692
693Set RightName to VALUE.
694Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
695(In the database, RightName will be stored as a varchar(25).)
696
697
698=cut
699
700
701=head2 ObjectType
702
703Returns the current value of ObjectType.
704(In the database, ObjectType is stored as varchar(25).)
705
706
707
708=head2 SetObjectType VALUE
709
710
711Set ObjectType to VALUE.
712Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
713(In the database, ObjectType will be stored as a varchar(25).)
714
715
716=cut
717
718
719=head2 ObjectId
720
721Returns the current value of ObjectId.
722(In the database, ObjectId is stored as int(11).)
723
724
725
726=head2 SetObjectId VALUE
727
728
729Set ObjectId to VALUE.
730Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
731(In the database, ObjectId will be stored as a int(11).)
732
733
734=cut
735
736
737=head2 Creator
738
739Returns the current value of Creator.
740(In the database, Creator is stored as int(11).)
741
742=cut
743
744
745=head2 Created
746
747Returns the current value of Created.
748(In the database, Created is stored as datetime.)
749
750=cut
751
752
753=head2 LastUpdatedBy
754
755Returns the current value of LastUpdatedBy.
756(In the database, LastUpdatedBy is stored as int(11).)
757
758=cut
759
760
761=head2 LastUpdated
762
763Returns the current value of LastUpdated.
764(In the database, LastUpdated is stored as datetime.)
765
766=cut
767
768
769
770sub _CoreAccessible {
771 {
772
773 id =>
3ffc5f4f 774 {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
84fb5b46 775 PrincipalType =>
3ffc5f4f 776 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
84fb5b46 777 PrincipalId =>
3ffc5f4f 778 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
84fb5b46 779 RightName =>
3ffc5f4f 780 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
84fb5b46 781 ObjectType =>
3ffc5f4f 782 {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
84fb5b46 783 ObjectId =>
3ffc5f4f 784 {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
84fb5b46 785 Creator =>
3ffc5f4f 786 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
84fb5b46 787 Created =>
3ffc5f4f 788 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
84fb5b46 789 LastUpdatedBy =>
3ffc5f4f 790 {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
84fb5b46 791 LastUpdated =>
3ffc5f4f 792 {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
84fb5b46
MKG
793
794 }
795};
796
3ffc5f4f
MKG
797sub FindDependencies {
798 my $self = shift;
799 my ($walker, $deps) = @_;
800
801 $self->SUPER::FindDependencies($walker, $deps);
802
803 $deps->Add( out => $self->PrincipalObj->Object );
804 $deps->Add( out => $self->Object );
805}
806
84fb5b46
MKG
807RT::Base->_ImportOverlays();
808
8091;