File Coverage

blib/lib/App/Dochazka/REST/ACL.pm
Criterion Covered Total %
statement 23 71 32.3
branch 2 30 6.6
condition 0 18 0.0
subroutine 8 11 72.7
pod 4 4 100.0
total 37 134 27.6


line stmt bran cond sub pod time code
1             # *************************************************************************
2             # Copyright (c) 2014-2015, SUSE LLC
3             #
4             # All rights reserved.
5             #
6             # Redistribution and use in source and binary forms, with or without
7             # modification, are permitted provided that the following conditions are met:
8             #
9             # 1. Redistributions of source code must retain the above copyright notice,
10             # this list of conditions and the following disclaimer.
11             #
12             # 2. Redistributions in binary form must reproduce the above copyright
13             # notice, this list of conditions and the following disclaimer in the
14             # documentation and/or other materials provided with the distribution.
15             #
16             # 3. Neither the name of SUSE LLC nor the names of its contributors may be
17             # used to endorse or promote products derived from this software without
18             # specific prior written permission.
19             #
20             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30             # POSSIBILITY OF SUCH DAMAGE.
31             # *************************************************************************
32             #
33             # ACL routines
34              
35              
36             use strict;
37 41     41   86115 use warnings;
  41         81  
  41         1063  
38 41     41   192  
  41         84  
  41         983  
39             use App::CELL qw( $CELL $log );
40 41     41   256 use App::Dochazka::REST::Model::Employee;
  41         78  
  41         2828  
41 41     41   14373 use Data::Dumper;
  41         108  
  41         1795  
42 41     41   242 use Params::Validate qw( :all );
  41         81  
  41         1609  
43 41     41   221  
  41         86  
  41         5627  
44              
45              
46             =head1 NAME
47              
48             App::Dochazka::REST::ACL - ACL module
49              
50              
51              
52              
53              
54             =head1 DESCRIPTION
55              
56             This module provides helper code for ACL checks.
57              
58             =cut
59              
60              
61              
62              
63             =head1 EXPORTS
64              
65             =cut
66              
67             use Exporter qw( import );
68 41     41   259 our @EXPORT_OK = qw(
  41         111  
  41         31461  
69             check_acl
70             check_acl_context
71             acl_check_is_me
72             acl_check_is_my_report
73             acl_check_iid_lid
74             );
75              
76              
77              
78             =head1 PACKAGE VARIABLES
79              
80             The 'check_acl' routine uses a hash to look up which privlevels
81             satisfy a given ACL profile.
82              
83             =cut
84              
85             my %acl_lookup = (
86             'admin' => { 'passerby' => '', 'inactive' => '', 'active' => '', 'admin' => '' },
87             'active' => { 'passerby' => '', 'inactive' => '', 'active' => '' },
88             'inactive' => { 'passerby' => '', 'inactive' => '' },
89             'passerby' => { 'passerby' => '', },
90             );
91              
92              
93              
94              
95             =head1 FUNCTIONS
96              
97             =head2 check_acl
98              
99             Takes a PARAMHASH with two properties: C<profile> and C<privlevel>. Their
100             values are assumed to be the ACL profile of a resource and the privlevel of an
101             employee, respectively. The function returns a true or false value indicating
102             whether that employee satisfies the given ACL profile.
103              
104             In addition to the usual privlevels, the C<profile> property can be
105             'forbidden', in which case the function returns false for all possible values
106             of C<privlevel>.
107              
108             =cut
109              
110             my ( %ARGS ) = validate( @_, {
111             profile => { type => SCALAR, regex => qr/^(passerby)|(inactive)|(active)|(admin)|(forbidden)$/ },
112 20     20 1 1715 privlevel => { type => SCALAR, regex => qr/^(passerby)|(inactive)|(active)|(admin)$/ },
113             } );
114             return exists( $acl_lookup{$ARGS{privlevel}}->{$ARGS{profile}} )
115             ? 1
116             : 0;
117 20 100       513 }
118              
119              
120             =head2 check_acl_context
121              
122             Check ACL and compare with eid in request body. This routine is designed
123             for resources that have an ACL profile of 'active'. If the request body
124             contains an 'eid' property, it is checked against the current user's EID. If
125             they are different and the current user's priv is 'active',
126             DOCHAZKA_FORBIDDEN_403 is returned; otherwise, an OK status is returned to
127             signify that the check passed.
128              
129             If the request body does not contain an 'eid' property, it is added.
130              
131             =cut
132              
133             my $context = shift;
134             my $current_eid = $context->{'current'}->{'eid'};
135             my $current_priv = $context->{'current_priv'};
136 0     0 1   if ( $current_priv eq 'passerby' or $current_priv eq 'inactive' ) {
137 0           return $CELL->status_err( 'DOCHAZKA_FORBIDDEN_403' );
138 0           }
139 0 0 0       if ( $context->{'request_entity'}->{'eid'} ) {
140 0           my $desired_eid = $context->{'request_entity'}->{'eid'};
141             if ( $desired_eid != $current_eid ) {
142 0 0         return $CELL->status_err( 'DOCHAZKA_FORBIDDEN_403' ) if $current_priv eq 'active';
143 0           }
144 0 0         } else {
145 0 0         $context->{'request_entity'}->{'eid'} = $current_eid;
146             }
147             return $CELL->status_ok('DOCHAZKA_ACL_CHECK');
148 0           }
149              
150 0            
151             =head2 acl_check_is_me
152              
153             Takes a property and a value. The property can be 'eid', 'nick', or 'sec_id'.
154             This routine checks the eid/nick/sec_id against C<< $self->context->{'current_obj'} >>
155             (the current employee object) and returns a boolean value answering the
156             question "is this me?"
157              
158             =cut
159              
160             my $self = shift;
161             my %pl = @_;
162             $log->debug( "Entering " . __PACKAGE__ . "::acl_check_is_me with " . Dumper( \%pl ) );
163              
164 0     0 1   my $ce = $self->context->{'current_obj'};
165 0           my $priv = $self->context->{'current_priv'};
166 0            
167             return 1 if $priv eq 'admin';
168 0            
169 0           if ( my $eid = $pl{'eid'} ) {
170             $log->debug( "acl_check_is_me: I am EID " . $ce->eid . " - checking against $eid" );
171 0 0         return ( defined($eid) and defined($ce->eid) and $eid == $ce->eid );
172             } elsif ( my $nick = $pl{'nick'} ) {
173 0 0         return ( defined($nick) and defined($ce->nick) and $nick eq $ce->nick );
    0          
    0          
174 0           } elsif ( my $sec_id = $pl{'sec_id'} ) {
175 0   0       return ( defined($sec_id) and defined($ce->sec_id) and $sec_id eq $ce->sec_id );
176             }
177 0   0        
178             die "AAAAGAGAGAHHHHAHAHAAJJAJAJAJAAHAHAHA! " . Dumper( \%pl );
179 0   0       }
180              
181              
182 0           =head2 acl_check_is_my_report
183              
184             Takes a property and a value. The property can be 'eid', 'nick', or 'sec_id'.
185             This routine first gets the employee object corresponding to the
186             eid/nick/sec_id and then checks if the current employee is that
187             employee's supervisor.
188              
189             =cut
190              
191             my $self = shift;
192             my %pl = @_;
193             $log->debug( "Entering " . __PACKAGE__ . "::acl_check_is_my_report with " . Dumper( \%pl ) );
194              
195             my $ce = $self->context->{'current_obj'};
196 0     0 1   my $priv = $self->context->{'current_priv'};
197 0           my $emp = App::Dochazka::REST::Model::Employee->spawn;
198 0           my $conn = $self->context->{'dbix_conn'};
199             my $status;
200 0            
201 0           return 1 if $priv eq 'admin';
202 0            
203 0           if ( my $eid = $pl{'eid'} ) {
204 0           $log->debug( "acl_check_is_my_report: given EID $eid" );
205             $status = $emp->load_by_eid( $conn, $eid );
206 0 0         } elsif ( my $nick = $pl{'nick'} ) {
207             $log->debug( "acl_check_is_my_report: given nick $nick" );
208 0 0         $status = $emp->load_by_nick( $conn, $nick );
    0          
    0          
209 0           } elsif ( my $sec_id = $pl{'sec_id'} ) {
210 0           $log->debug( "acl_check_is_my_report: given sec_id $sec_id" );
211             $status = $emp->load_by_sec_id( $conn, $sec_id );
212 0           } else {
213 0           die "AAAGAAHHAHAHAAJJAJAJAHAHA! " . Dumper( \%pl );
214             }
215 0            
216 0           if ( $status->not_ok ) {
217             $log->error( "acl_check_is_my_report: employee lookup failed (" . $status->text . ")" );
218 0           return 0;
219             }
220              
221 0 0         $emp = $status->payload;
222 0          
223 0           if ( defined($emp->supervisor) and defined($ce->eid) and $emp->supervisor eq $ce->eid ) {
224             $log->debug( "acl_check_is_my_report: I am the supervisor of ->" . $emp->nick . "<-" );
225             return 1;
226 0           }
227              
228 0 0 0       return 0;
      0        
229 0           }
230 0            
231              
232             1;