File Coverage

blib/lib/SPOPS/Secure/Hierarchy.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package SPOPS::Secure::Hierarchy;
2              
3             # $Id: Hierarchy.pm,v 3.6 2004/06/02 00:48:24 lachoy Exp $
4              
5 1     1   1414 use strict;
  1         2  
  1         36  
6 1     1   5 use base qw( Exporter SPOPS::Secure );
  1         2  
  1         108  
7 1     1   5 use Log::Log4perl qw( get_logger );
  1         2  
  1         5  
8 1     1   45 use vars qw( $ROOT_OBJECT_NAME );
  1         2  
  1         48  
9              
10 1     1   5 use Data::Dumper qw( Dumper );
  1         1  
  1         43  
11 1     1   2469 use SPOPS;
  0            
  0            
12             use SPOPS::Exception::Security;
13             use SPOPS::Secure qw( :scope :level $EMPTY );
14             use SPOPS::Secure::Util;
15              
16             my $log = get_logger();
17              
18             @SPOPS::Secure::Hierarchy::EXPORT_OK = qw( $ROOT_OBJECT_NAME );
19             $SPOPS::Secure::Hierarchy::VERSION = sprintf("%d.%02d", q$Revision: 3.6 $ =~ /(\d+)\.(\d+)/);
20              
21             $ROOT_OBJECT_NAME = 'ROOT_OBJECT';
22              
23              
24             # Override this method from SPOPS::Secure -- return a hashref with the
25             # scopes as keys and the values as security information (level or
26             # hashref of scope_id => level)
27              
28             sub get_security {
29             my ( $item, $p ) = @_;
30              
31             # Find object info for debugging and for passing to the
32             # fetch_by_object method later
33              
34             my ( $class, $oid ) = SPOPS::Secure::Util->find_class_and_oid( $item, $p );
35             $log->is_info &&
36             $log->info( "Checking security for [$class: $oid]" );
37              
38             # Punt the request back to our parent if we're getting security
39             # explicitly for the ROOT_OBJECT, which generally only happens when
40             # we're editing its security
41              
42             if ( $oid eq $ROOT_OBJECT_NAME ) {
43             $log->is_info &&
44             $log->info( "Object ID == ROOT_OBJECT_NAME: punting to parent" );
45             return SUPER->get_security({ %{ $p },
46             class => $class,
47             object_id => $oid });
48             }
49              
50             unless ( exists $p->{user} and exists $p->{group} ) {
51             ( $p->{user}, $p->{group} ) = $item->get_security_scopes( $p );
52             }
53              
54             # superuser (record with user_id 1) can do anything
55              
56             if ( my $security_info = $item->_check_superuser( $p->{user}, $p->{group} ) ) {
57             $log->is_info &&
58             $log->info( "Superuser is logged in" );
59             return $security_info;
60             }
61              
62             my ( $all_levels, $first_level ) = $item->get_hierarchy_levels( $p );
63             $log->is_info &&
64             $log->info( "First level with security ($first_level)" );
65              
66             # Dereference $EMPTY so there's no chance of anyone putting
67             # information into the ref and screwing up the package variable...
68              
69             return $all_levels->{ $first_level } || \%{ $EMPTY };
70             }
71              
72              
73             sub get_hierarchy_levels {
74             my ( $item, $p ) = @_;
75              
76             # Grab hierarchy config info from the params or from the object
77              
78             my $object_id = $p->{oid} || $p->{object_id};
79             my $h_info = $item->_get_hierarchy_parameters({ %{ $p },
80             hierarchy_value => $object_id });
81              
82             # Ensure we have necessary info
83              
84             unless ( $h_info->{hierarchy_value} ) {
85             $log->warn( "No value available to split into hierarchy! Returning ",
86             "empty security." );
87             return ();
88             }
89             unless ( ref $h_info->{hierarchy_manip} eq 'CODE' ) {
90             $log->warn( "Cannot split hierarchy into pieces without either a ",
91             "separator or processing code. Returning empty security." );
92             return ();
93             }
94              
95             # Now comes the interesting part. Setup a list of the object value
96             # followed by all the parents. Note that we can either use the
97             # default generated list (splitting the value by the separator) or
98             # create a subroutine to do it for us, passing it in via
99             # 'hierarchy_manip' in the routine parameters or in our object
100             # config.
101              
102             my $check_list = $h_info->{hierarchy_manip}->( $h_info->{hierarchy_sep},
103             $h_info->{hierarchy_value} );
104              
105             return $item->_fetch_hierarchy_levels({ %{ $p },
106             check_list => $check_list,
107             ordered => 1 });
108             }
109              
110              
111             sub create_root_object_security {
112             my ( $item, $p ) = @_;
113             my ( $class, $oid ) = SPOPS::Secure::Util->find_class_and_oid( $item, $p );
114             return $class->set_security({ object_id => $ROOT_OBJECT_NAME,
115             scope => $p->{scope},
116             security_level => $p->{level} });
117             }
118              
119              
120             # Override so that the WORLD scope doesn't get any default setting
121              
122             sub create_initial_security { return 1 }
123              
124              
125             # Retrieve and store a security level for each item in the hierarchy
126             # check_list, returning these security levels plus a marker denoting
127             # the first one found. This is used not only in get_security but can
128             # also be useful when displaying all the parents of a particular
129             # object and how security is inherited.
130              
131             sub _fetch_hierarchy_levels {
132             my ( $item, $p ) = @_;
133             my $class = $p->{class} || ref $item || $item;
134             my $so_class = $p->{security_object_class} ||
135             $class->global_security_object_class;
136              
137             my $first_found = undef;
138             my $level_track = {};
139             my @ordered = ();
140              
141             unless ( $p->{class} ) {
142             my $object_id = $p->{oid} || $p->{object_id};
143             ( $p->{class}, $p->{oid} ) = SPOPS::Secure::Util->find_class_and_oid(
144             $item, $p );
145             $log->is_info &&
146             $log->info( "Checking security for [$p->{class}] [$p->{oid}]" );
147             }
148              
149             # Yes, I know, grep in a void context...
150              
151             unless ( grep /^$ROOT_OBJECT_NAME$/, @{ $p->{check_list} } ) {
152             push @{ $p->{check_list} }, $ROOT_OBJECT_NAME;
153             $log->is_info &&
154             $log->info( "$ROOT_OBJECT_NAME not found in checklist; added manually" );
155             }
156              
157             SECVALUE:
158             foreach my $security_check ( @{ $p->{check_list} } ) {
159             $log->is_info &&
160             $log->info( "Find value for $p->{class} ($security_check)" );
161             push @ordered, $security_check if ( $p->{ordered} );
162             my $sec_listing = $so_class->fetch_by_object( $p->{class},
163             { object_id => $security_check,
164             user => $p->{user},
165             group => $p->{group} });
166             $log->is_info &&
167             $log->info( "Security found for ($security_check):\n",
168             Dumper( $sec_listing ) );
169              
170             $first_found ||= $security_check if ( $sec_listing );
171             $level_track->{ $security_check } = $sec_listing;
172             }
173              
174             # If we don't find a single item that has security, we need to
175             # create security for this class's root object.
176              
177             unless ( $first_found ) {
178             $log->is_info &&
179             $log->info( "Cannot find ANY security for [$p->{class}] [$p->{oid}] -- ",
180             "creating extremely stringent root object security" );
181             $item->create_root_object_security({ class => $p->{class},
182             scope => SEC_SCOPE_WORLD,
183             level => SEC_LEVEL_NONE });
184             }
185             return ( $level_track, $first_found ) unless ( $p->{ordered} );
186             return ( $level_track, $first_found, \@ordered );
187             }
188              
189              
190             # Set the parameters for hierarchy information whether it came in
191             # through a parameter list or from an object and its configuration.
192              
193             sub _get_hierarchy_parameters {
194             my ( $item, $p ) = @_;
195              
196             # Find the hierarchy information -- info passed into the routine via
197             # parameters takes precedence, and we only query the object config if
198             # we actually have an object.
199              
200             my $h_info = {};
201             $h_info->{hierarchy_field} = $p->{hierarchy_field};
202             $h_info->{hierarchy_sep} = $p->{hierarchy_separator};
203             $h_info->{hierarchy_manip} = $p->{hierarchy_manip};
204              
205             my $class = $p->{class} || ref $item || $item;
206             my $CONF = eval { $class->CONFIG };
207             if ( ref $CONF ) {
208             $h_info->{hierarchy_field} ||= $CONF->{hierarchy_field};
209             $h_info->{hierarchy_sep} ||= $CONF->{hierarchy_separator};
210             $h_info->{hierarchy_manip} ||= $CONF->{hierarchy_manip};
211             }
212              
213             # Only use the default check_list maker if there is a hierarchy
214             # separator
215              
216             if ( $h_info->{hierarchy_sep} ) {
217             $h_info->{hierarchy_manip} ||= \&_make_check_list;
218             }
219              
220             # If this is an object, find the hierarchy value from the object
221              
222             $h_info->{hierarchy_value} = $p->{hierarchy_value};
223             my $object = ( ref $item ) ? $item : $p->{object}; # this is a nasty hack
224             if ( $object ) {
225             $log->is_info &&
226             $log->info( "Getting value from object, overriding previously ",
227             "set value of '$h_info->{hierarchy_value}'" );
228             $h_info->{hierarchy_value} = $object->{ $h_info->{hierarchy_field} };
229             }
230              
231             $log->is_info &&
232             $log->info( "Found parameters:\n", Dumper( $h_info ) );
233             return $h_info;
234             }
235              
236              
237             # Note: don't push the root object reference onto the stack in this
238             # procedure -- we handle it automatically in ->get_hierarchy_levels()
239              
240             sub _make_check_list {
241             my ( $hierarchy_sep, $hierarchy_value ) = @_;
242             my @check_list = ( $hierarchy_value );
243              
244             # don't get into an infinite loop!
245             unless ( $hierarchy_value =~ m!$hierarchy_sep! ) {
246             return \@check_list;
247             }
248              
249             while ( $hierarchy_value ) {
250             $hierarchy_value =~ s|^(.*)$hierarchy_sep.*$|$1|;
251             push @check_list, $hierarchy_value if ( $hierarchy_value );
252             }
253             return \@check_list;
254             }
255              
256             1;
257              
258              
259             __END__