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             package App::Dochazka::REST::ACL;
36              
37 41     41   60423 use strict;
  41         80  
  41         973  
38 41     41   179 use warnings;
  41         76  
  41         1025  
39              
40 41     41   235 use App::CELL qw( $CELL $log );
  41         76  
  41         2840  
41 41     41   10761 use App::Dochazka::REST::Model::Employee;
  41         98  
  41         1740  
42 41     41   238 use Data::Dumper;
  41         90  
  41         1545  
43 41     41   216 use Params::Validate qw( :all );
  41         72  
  41         5451  
44              
45              
46              
47             =head1 NAME
48              
49             App::Dochazka::REST::ACL - ACL module
50              
51              
52              
53              
54              
55             =head1 DESCRIPTION
56              
57             This module provides helper code for ACL checks.
58              
59             =cut
60              
61              
62              
63              
64             =head1 EXPORTS
65              
66             =cut
67              
68 41     41   244 use Exporter qw( import );
  41         104  
  41         27062  
69             our @EXPORT_OK = qw(
70             check_acl
71             check_acl_context
72             acl_check_is_me
73             acl_check_is_my_report
74             acl_check_iid_lid
75             );
76              
77              
78              
79             =head1 PACKAGE VARIABLES
80              
81             The 'check_acl' routine uses a hash to look up which privlevels
82             satisfy a given ACL profile.
83              
84             =cut
85              
86             my %acl_lookup = (
87             'admin' => { 'passerby' => '', 'inactive' => '', 'active' => '', 'admin' => '' },
88             'active' => { 'passerby' => '', 'inactive' => '', 'active' => '' },
89             'inactive' => { 'passerby' => '', 'inactive' => '' },
90             'passerby' => { 'passerby' => '', },
91             );
92              
93              
94              
95              
96             =head1 FUNCTIONS
97              
98             =head2 check_acl
99              
100             Takes a PARAMHASH with two properties: C<profile> and C<privlevel>. Their
101             values are assumed to be the ACL profile of a resource and the privlevel of an
102             employee, respectively. The function returns a true or false value indicating
103             whether that employee satisfies the given ACL profile.
104              
105             In addition to the usual privlevels, the C<profile> property can be
106             'forbidden', in which case the function returns false for all possible values
107             of C<privlevel>.
108              
109             =cut
110              
111             sub check_acl {
112 20     20 1 1757 my ( %ARGS ) = validate( @_, {
113             profile => { type => SCALAR, regex => qr/^(passerby)|(inactive)|(active)|(admin)|(forbidden)$/ },
114             privlevel => { type => SCALAR, regex => qr/^(passerby)|(inactive)|(active)|(admin)$/ },
115             } );
116             return exists( $acl_lookup{$ARGS{privlevel}}->{$ARGS{profile}} )
117 20 100       474 ? 1
118             : 0;
119             }
120              
121              
122             =head2 check_acl_context
123              
124             Check ACL and compare with eid in request body. This routine is designed
125             for resources that have an ACL profile of 'active'. If the request body
126             contains an 'eid' property, it is checked against the current user's EID. If
127             they are different and the current user's priv is 'active',
128             DOCHAZKA_FORBIDDEN_403 is returned; otherwise, an OK status is returned to
129             signify that the check passed.
130              
131             If the request body does not contain an 'eid' property, it is added.
132              
133             =cut
134              
135             sub check_acl_context {
136 0     0 1   my $context = shift;
137 0           my $current_eid = $context->{'current'}->{'eid'};
138 0           my $current_priv = $context->{'current_priv'};
139 0 0 0       if ( $current_priv eq 'passerby' or $current_priv eq 'inactive' ) {
140 0           return $CELL->status_err( 'DOCHAZKA_FORBIDDEN_403' );
141             }
142 0 0         if ( $context->{'request_entity'}->{'eid'} ) {
143 0           my $desired_eid = $context->{'request_entity'}->{'eid'};
144 0 0         if ( $desired_eid != $current_eid ) {
145 0 0         return $CELL->status_err( 'DOCHAZKA_FORBIDDEN_403' ) if $current_priv eq 'active';
146             }
147             } else {
148 0           $context->{'request_entity'}->{'eid'} = $current_eid;
149             }
150 0           return $CELL->status_ok('DOCHAZKA_ACL_CHECK');
151             }
152              
153              
154             =head2 acl_check_is_me
155              
156             Takes a property and a value. The property can be 'eid', 'nick', or 'sec_id'.
157             This routine checks the eid/nick/sec_id against C<< $self->context->{'current_obj'} >>
158             (the current employee object) and returns a boolean value answering the
159             question "is this me?"
160              
161             =cut
162              
163             sub acl_check_is_me {
164 0     0 1   my $self = shift;
165 0           my %pl = @_;
166 0           $log->debug( "Entering " . __PACKAGE__ . "::acl_check_is_me with " . Dumper( \%pl ) );
167              
168 0           my $ce = $self->context->{'current_obj'};
169 0           my $priv = $self->context->{'current_priv'};
170              
171 0 0         return 1 if $priv eq 'admin';
172              
173 0 0         if ( my $eid = $pl{'eid'} ) {
    0          
    0          
174 0           $log->debug( "acl_check_is_me: I am EID " . $ce->eid . " - checking against $eid" );
175 0   0       return ( defined($eid) and defined($ce->eid) and $eid == $ce->eid );
176             } elsif ( my $nick = $pl{'nick'} ) {
177 0   0       return ( defined($nick) and defined($ce->nick) and $nick eq $ce->nick );
178             } elsif ( my $sec_id = $pl{'sec_id'} ) {
179 0   0       return ( defined($sec_id) and defined($ce->sec_id) and $sec_id eq $ce->sec_id );
180             }
181              
182 0           die "AAAAGAGAGAHHHHAHAHAAJJAJAJAJAAHAHAHA! " . Dumper( \%pl );
183             }
184              
185              
186             =head2 acl_check_is_my_report
187              
188             Takes a property and a value. The property can be 'eid', 'nick', or 'sec_id'.
189             This routine first gets the employee object corresponding to the
190             eid/nick/sec_id and then checks if the current employee is that
191             employee's supervisor.
192              
193             =cut
194              
195             sub acl_check_is_my_report {
196 0     0 1   my $self = shift;
197 0           my %pl = @_;
198 0           $log->debug( "Entering " . __PACKAGE__ . "::acl_check_is_my_report with " . Dumper( \%pl ) );
199              
200 0           my $ce = $self->context->{'current_obj'};
201 0           my $priv = $self->context->{'current_priv'};
202 0           my $emp = App::Dochazka::REST::Model::Employee->spawn;
203 0           my $conn = $self->context->{'dbix_conn'};
204 0           my $status;
205              
206 0 0         return 1 if $priv eq 'admin';
207              
208 0 0         if ( my $eid = $pl{'eid'} ) {
    0          
    0          
209 0           $log->debug( "acl_check_is_my_report: given EID $eid" );
210 0           $status = $emp->load_by_eid( $conn, $eid );
211             } elsif ( my $nick = $pl{'nick'} ) {
212 0           $log->debug( "acl_check_is_my_report: given nick $nick" );
213 0           $status = $emp->load_by_nick( $conn, $nick );
214             } elsif ( my $sec_id = $pl{'sec_id'} ) {
215 0           $log->debug( "acl_check_is_my_report: given sec_id $sec_id" );
216 0           $status = $emp->load_by_sec_id( $conn, $sec_id );
217             } else {
218 0           die "AAAGAAHHAHAHAAJJAJAJAHAHA! " . Dumper( \%pl );
219             }
220              
221 0 0         if ( $status->not_ok ) {
222 0           $log->error( "acl_check_is_my_report: employee lookup failed (" . $status->text . ")" );
223 0           return 0;
224             }
225              
226 0           $emp = $status->payload;
227            
228 0 0 0       if ( defined($emp->supervisor) and defined($ce->eid) and $emp->supervisor eq $ce->eid ) {
      0        
229 0           $log->debug( "acl_check_is_my_report: I am the supervisor of ->" . $emp->nick . "<-" );
230 0           return 1;
231             }
232              
233 0           return 0;
234             }
235              
236              
237             1;