App-Dochazka-REST
view release on metacpan or search on metacpan
lib/App/Dochazka/REST/Shared.pm view on Meta::CPAN
=head2 shared_first_pass_lookup
Takes two scalar arguments, "key" and "value" and determines whether or not the
database contains an object answering to that description.
This should be used only for resources that require an exact match.
=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 )
)
{
$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;
}
=head2 shared_get_employee
=cut
sub shared_get_employee {
my ( $d_obj, $pass, $key, $value ) = @_;
$log->debug( "Entering " . __PACKAGE__ . "::shared_get_employee" );
# first pass
if ( $pass == 1 ) {
return shared_get_employee_pass1(
$d_obj, $pass, $key, $value
);
}
# second pass
return $CELL->status_ok( 'DISPATCH_EMPLOYEE_FOUND',
payload => $d_obj->context->{'stashed_employee_object'},
);
}
=head2 shared_update_employee
Takes three arguments:
- $d_obj is the App::Dochazka::REST::Dispatch object
- $emp is an employee object (blessed hashref)
- $over is a hashref with zero or more employee properties and new values
The values from $over replace those in $emp
=cut
sub shared_update_employee {
my ( $d_obj, $emp, $over ) = @_;
$log->debug("Entering " . __PACKAGE__ . "::shared_update_employee" );
$log->debug("Updating employee: " . Dumper( $emp ) );
$log->debug("With key:value pairs " . Dumper( $over ) );
ACL: {
my $explanation = "Update operations require at least one key:value pair in the request entity";
if ( ref( $over ) ne 'HASH' ) {
$d_obj->mrest_declare_status( code => 400, explanation => $explanation );
return $fail;
}
delete $over->{'eid'};
if ( $over == {} ) {
$d_obj->mrest_declare_status( code => 400, explanation => $explanation );
return $fail;
}
}
lib/App/Dochazka/REST/Shared.pm view on Meta::CPAN
=cut
sub shared_history_init {
my $context = shift;
my $method = $context->{'method'};
$log->debug( "Method is $method" );
my $mapping = $context->{'mapping'};
my $tsrange = $mapping->{'tsrange'};
my $ts = $mapping->{'ts'};
my ( $key, $value );
if ( defined( my $nick = $mapping->{'nick'} ) ) {
$key = 'nick';
$value = $nick;
} elsif ( defined ( my $eid = $mapping->{'eid'} ) ) {
$key = 'EID';
$value = $eid;
} else {
die "AAFAAAGAGAGGAGAGGGGGGH! mapping contains neither nick nor eid property: " . Dumper( $mapping );
}
return ( $context, $method, $mapping, $tsrange, $ts, $key, $value );
}
=head2 shared_get_privsched
Shared GET handler for 'priv' and 'schedule' lookups. Takes four arguments:
=over
=item C<$d_obj> - dispatch object
=item C<$t> - either 'priv' or 'schedule'
=item C<$pass> - either 1 or 2
=item C<$key> - either 'EID' or 'nick'
=item C<$value> - EID or nick value to lookup
=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;
}
}
( run in 0.923 second using v1.01-cache-2.11-cpan-39bf76dae61 )