App-Dochazka-REST
view release on metacpan or search on metacpan
lib/App/Dochazka/REST/Shared.pm view on Meta::CPAN
# *************************************************************************
# Copyright (c) 2014-2017, SUSE LLC
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of SUSE LLC nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# *************************************************************************
# ------------------------
# Shared dispatch functions
# ------------------------
package App::Dochazka::REST::Shared;
use strict;
use warnings;
use App::CELL qw( $CELL $log $site );
use App::Dochazka::REST::ACL qw( acl_check_is_me acl_check_is_my_report );
use App::Dochazka::REST::ConnBank qw( conn_status );
use App::Dochazka::REST::Model::Activity;
use App::Dochazka::REST::Model::Employee;
use App::Dochazka::REST::Model::Interval;
use App::Dochazka::REST::Model::Lock;
use App::Dochazka::REST::Model::Privhistory;
use App::Dochazka::REST::Model::Schedhistory;
use App::Dochazka::REST::Model::Schedule;
use App::Dochazka::REST::Model::Shared qw( priv_by_eid schedule_by_eid );
use App::Dochazka::REST::Util qw( hash_the_password pre_update_comparison );
use Data::Dumper;
use Params::Validate qw( :all );
use Try::Tiny;
my $fail = $CELL->status_not_ok;
=head1 NAME
App::Dochazka::REST::Dispatch::Shared - Shared dispatch functions
=head1 DESCRIPTION
This module provides code that is, or may be, used by more than one resource
handler method.
=head1 EXPORTS
=cut
use Exporter qw( import );
our @EXPORT_OK = qw(
shared_first_pass_lookup
shared_entity_check
shared_get_employee
shared_get_employee_pass1
shared_insert_employee
shared_update_employee
shared_update_schedule
shared_get_class_prop_id
shared_history_init
shared_get_privsched
shared_employee_acl_part1
shared_employee_acl_part2
shared_update_activity
shared_update_component
shared_update_history
shared_insert_activity
shared_insert_component
shared_insert_interval
shared_insert_lock
shared_update_intlock
shared_process_quals
);
our %EXPORT_TAGS = ( ALL => [ @EXPORT_OK ] );
=head1 PACKAGE VARIABLES
The package variable C<%f_dispatch> is used in C<fetch_by_eid>, C<fetch_by_nick>,
and C<fetch_own>.
=cut
my %f_dispatch = (
"attendance" => \&App::Dochazka::REST::Model::Interval::fetch_by_eid_and_tsrange,
"lock" => \&App::Dochazka::REST::Model::Lock::fetch_by_eid_and_tsrange,
);
my %id_dispatch = (
lib/App/Dochazka/REST/Shared.pm view on Meta::CPAN
=cut
sub shared_first_pass_lookup {
my ( $d_obj, $key, $value ) = @_;
$log->debug( "Entering " . __PACKAGE__ . "::shared_first_pass_lookup with key $key, value $value" );
my $conn = $d_obj->context->{'dbix_conn'};
my ( $status, $thing );
if ( uc($key) eq 'AID' ) {
$thing = 'activity';
$status = App::Dochazka::REST::Model::Activity->load_by_aid( $conn, $value );
} elsif ( $key eq 'code' ) {
$thing = 'activity';
$status = App::Dochazka::REST::Model::Activity->load_by_code( $conn, $value );
} elsif ( uc($key) eq 'CID' ) {
$thing = 'component';
$status = App::Dochazka::REST::Model::Component->load_by_cid( $conn, $value );
} elsif ( $key eq 'path' ) {
$thing = 'component';
$status = App::Dochazka::REST::Model::Component->load_by_path( $conn, $value );
} elsif ( uc($key) eq 'EID' ) {
$thing = 'employee';
$status = App::Dochazka::REST::Model::Employee->load_by_eid( $conn, $value );
} elsif ( $key eq 'nick' ) {
$thing = 'employee';
$status = App::Dochazka::REST::Model::Employee->load_by_nick( $conn, $value );
} elsif ( $key eq 'sec_id' ) {
$thing = 'employee';
$status = App::Dochazka::REST::Model::Employee->load_by_sec_id( $conn, $value );
} elsif ( uc($key) eq 'IID' ) {
$thing = 'interval';
$status = App::Dochazka::REST::Model::Interval->load_by_iid( $conn, $value );
} elsif ( uc($key) eq 'LID' ) {
$thing = 'lock';
$status = App::Dochazka::REST::Model::Lock->load_by_lid( $conn, $value );
} elsif ( uc($key) eq 'PHID' ) {
$thing = 'privilege history record';
$status = App::Dochazka::REST::Model::Privhistory->load_by_phid( $conn, $value );
} elsif ( uc($key) eq 'SHID' ) {
$thing = 'schedule history record';
$status = App::Dochazka::REST::Model::Schedhistory->load_by_shid( $conn, $value );
} elsif ( uc($key) eq 'SID' ) {
$thing = 'schedule';
$status = App::Dochazka::REST::Model::Schedule->load_by_sid( $conn, $value );
} elsif ( $key eq 'scode' ) {
$thing = 'schedule';
$status = App::Dochazka::REST::Model::Schedule->load_by_scode( $conn, $value );
} else {
die "shared_first_pass_lookup could not do anything with key $key!";
}
if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
$d_obj->mrest_declare_status( code => 404,
explanation => 'DISPATCH_SEARCH_EMPTY',
args => [ $thing, "$key equals $value" ],
);
return;
}
if ( $status->not_ok ) {
$d_obj->mrest_declare_status( code => 500, explanation => $status->code,
args => $status->args
);
return;
}
return $status->payload;
}
=head2 shared_entity_check
Check request entity for presence of properties
=cut
sub shared_entity_check {
my ( $d_obj, @props ) = @_;
$log->debug( "Entering " . __PACKAGE__ . "::shared_entity_check with properties " .
join( ' ', @props ) . " and entity: " . Dumper( $d_obj->context->{'request_entity'} ) );
my $entity = $d_obj->context->{'request_entity'};
if ( not $entity ) {
$d_obj->mrest_declare_status( code => 400,
explanation => 'DISPATCH_ENTITY_MISSING'
);
return $fail;
}
if ( ref( $entity ) ne 'HASH' ) {
$d_obj->mrest_declare_status( code => 400,
explanation => 'DISPATCH_ENTITY_NOT_KEY_VALUE'
);
return $fail;
}
foreach my $p ( @props ) {
if ( not $entity->{$p} ) {
$d_obj->mrest_declare_status( code => 400,
explanation => 'DISPATCH_PROP_MISSING_IN_ENTITY', args => [ $p ]
);
return $fail;
}
}
return $CELL->status_ok;
}
=head2 shared_get_employee_pass1
=cut
sub shared_get_employee_pass1 {
my ( $d_obj, $pass, $key, $value ) = @_;
$log->debug( "Entering " . __PACKAGE__ . "::shared_get_employee_pass1" );
#
# ACL checks
#
if (
! acl_check_is_my_report( $d_obj, ( lc $key ) => $value ) and
! acl_check_is_me( $d_obj, ( lc $key ) => $value )
)
lib/App/Dochazka/REST/Shared.pm view on Meta::CPAN
=over
=cut
sub shared_get_privsched {
my ( $d_obj, $t, $pass, $key, $value ) = @_;
$log->debug( "Entering " . __PACKAGE__ . ":shared_get_privsched" );
# first pass
if ( $pass == 1 ) {
#
# 403 (ACL) check - passerby can only look up him- or herself
#
if ( ! acl_check_is_me( $d_obj, ( lc $key ) => $value ) ) {
$d_obj->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
return 0;
}
#
# 404 check
#
my $emp = shared_first_pass_lookup( $d_obj, $key => $value );
return 0 unless $emp;
$d_obj->context->{'stashed_employee_object'} = $emp;
return 1;
}
# second pass
# - initialization
my $status;
my %dispatch = (
'priv' => \&priv_by_eid,
'schedule' => \&schedule_by_eid,
);
my $emp = $d_obj->context->{'stashed_employee_object'};
my $eid = $emp->eid;
my $nick = $emp->nick;
my $ts = $d_obj->context->{'mapping'}->{'ts'};
my $conn = $d_obj->context->{'dbix_conn'};
# - run priv_by_eid or schedule_by_eid, as appropriate
my $return_value = $dispatch{$t}->( $conn, $eid, $ts );
# on success, $return_value will be a SCALAR like 'inactive' (priv) or 8 (SID of schedule)
if ( ref( $return_value ) ne 'App::CELL::Status' ) {
if ( $return_value and $t eq 'schedule' ) {
# $return_value is SID of the schedule, but we want the schedule itself
my $status = App::Dochazka::REST::Model::Schedule->load_by_sid( $conn, $return_value );
$return_value = $status->payload;
}
my @privsched = ( $t, $return_value );
if ( $ts ) {
if ( ! $return_value ) {
$d_obj->mrest_declare_status(
code => 404,
explanation => "Employee $nick (EID $eid) has no $t assigned as of $ts"
);
return $CELL->status_not_ok;
}
my $code;
if ( 'PRIV' eq uc( $t ) ) {
$code = 'DISPATCH_EMPLOYEE_PRIV_AS_AT';
} elsif ( 'SCHEDULE' eq uc( $t ) ) {
$code = 'DISPATCH_EMPLOYEE_SCHEDULE_AS_AT';
} else {
die "AGHNEVERNEVERNEVERPRIVSCHED1";
}
return $CELL->status_ok( $code,
args => [ $ts, $emp->nick, $return_value ],
payload => {
eid => $eid += 0, # "numify"
nick => $emp->nick,
timestamp => $ts,
@privsched,
},
);
} else {
if ( ! $return_value ) {
$d_obj->mrest_declare_status(
code => 404,
explanation => "Employee $nick (EID $eid) has no $t assigned"
);
return $CELL->status_not_ok;
}
my $code;
if ( 'PRIV' eq uc( $t ) ) {
$code = 'DISPATCH_EMPLOYEE_PRIV';
} elsif ( 'SCHEDULE' eq uc( $t ) ) {
$code = 'DISPATCH_EMPLOYEE_SCHEDULE';
} else {
die "AGHNEVERNEVERNEVERPRIVSCHED2";
}
return $CELL->status_ok( $code,
args => [ $emp->nick, $return_value ],
payload => {
eid => $eid += 0, # "numify"
nick => $emp->nick,
@privsched,
},
);
}
}
# There was a DBI error
return $return_value;
}
=head2 shared_employee_acl_part1
ACL check -- 'inactive' and 'active' employees can only operate on their own
EID. Returns boolean 1 or 0, where 1 means "ACL check passed".
=cut
sub shared_employee_acl_part1 {
my ( $d_obj, $this_emp ) = @_;
$log->debug( "Entering " . __PACKAGE__ . "::shared_employee_acl_part1" );
my $context = $d_obj->context;
my $cp = $context->{'current_priv'} || "none";
# insert
if ( ! defined( $this_emp ) ) {
if ( $cp ne 'admin' ) {
$d_obj->mrest_declare_status( code => 403,
explanation => "Only administrators can insert new employee records"
);
return 0;
}
}
# update
if ( $cp eq 'admin' ) {
return 1;
} else {
if ( $this_emp->eid == $context->{'current'}->{'eid'} ) {
return 1;
}
}
$d_obj->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
return 0;
}
( run in 0.668 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )