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 )