#!/usr/bin/perl -w
#
# Zymonic Business Process and Information Management System
# Copyright Zednax Limited 2008 -
# For Authors and Changelog see the subversion history
use strict;

# Modules
use Zymonic::Config;
$Zymonic::system  = '';
$Zymonic::session = '';

use XML::Simple;

# Library path
BEGIN
{
    use Zymonic::Utils qw(clean death_handler debug get_array);
    $main::SIG{__DIE__} = \&death_handler;
}

our $PASS = 'PASS';
our $FAIL = 'FAIL';

our %lists = ();
our %listfield = ( 'ip' => 'ip_address', 'fap' => 'fap_zname' );

our @holders = ();

$Zymonic::system = clean( $ARGV[0], '_' );
our $username = clean( $ARGV[1], '_\*@' );
my $sec_id = clean( $ARGV[2] );
our $ip_address = clean( $ARGV[3], '.' );
our $fap        = clean( $ARGV[4], '_' );
my $log_file = clean( $ARGV[5], '-_./\\' );
unless ( $Zymonic::system and $username )
{
    print "permission_check.pl [system] [username] [sec_id] [ip adress] [filter/process zname] [logfile (optional)]\n";
    exit(1);
}

if ($log_file)
{
    my $fh;
    if ($log_file)
    {
        if ( $log_file eq '-' )
        {
            open $fh, ">&STDOUT" or die "Unable to open STDOUT for writing";
        }
        else
        {
            open $fh, '>', $log_file or die "Unable to open $log_file for writing";
        }
        $Zymonic::Utils::debugfile = $fh;
    }
    else
    {
        $Zymonic::Utils::debugs_written = 1;
    }
}

$Zymonic::ZCONFIG{$Zymonic::system} = Zymonic::Config->new(
    system_name => $Zymonic::system,
    config_dir  => "/etc/zymonic",
    ip_address  => '127.0.0.1',
    protocol    => 'http'
);

our $db = Zymonic::DB->new( config => $Zymonic::ZCONFIG{$Zymonic::system} );

our $auth = Zymonic::Auth->new(
    config     => $Zymonic::ZCONFIG{$Zymonic::system},
    session    => undef,
    DB         => $db,
    ip_address => $ip_address,
);

$auth->{user}      = $username;
$auth->{logged_in} = 'Y';

$auth->initialise;

print "USER: " . $username . "\n";
print "ROLE: " . $auth->{userdata}->{role} . "\n";
print "GROUP NAMES: " . join( ",", @{ $auth->{userdata}->{groupnames} } ) . "\n";
print "GROUPS: " . join( ",", @{ $auth->{userdata}->{groups} } ) . "\n";

our @role_perms = ();

if ( $auth->{userdata}->{role} )
{
    my $role_p_recs = $db->run_query(
        {
            string => 'SELECT role_permission FROM zz_role_permissions WHERE role = ? AND allowed = ?',
            params => [ $auth->{userdata}->{role}, 'Y' ]
        }
    );

    @role_perms = map { $_->{role_permission} } @{$role_p_recs};
}

print "ROLE PERMISSIONS: \n" . join( "\n", @role_perms ) . "\n\n";

if ($sec_id)
{
    my $perms = $auth->get_permissions( 'record', $sec_id, $fap );

    print "GET PERMISSIONS RESULTS: \n" . join( "\n", map { $_ . ' => ' . $perms->{$_} } keys( %{$perms} ) ) . "\n\n";

    my $all_perms = $db->run_query(
        {
            string => 'SELECT id, zzlu, permission_holder, roleperm, ip_list, fap_list, '
              . join( ", ", map { 'permission_' . $_ } $Zymonic::ZCONFIG{$Zymonic::system}->record_permissions )
              . '  FROM zz_record_security WHERE sec_id = ?',
            params => [$sec_id]
        }
    );

    print "==ALL PERMISSIONS FOR SEC ID : $sec_id ==\n";
    foreach my $pr ( @{$all_perms} )
    {
        print "ID: " . $pr->{id} . "\n";
        print "ZZLU: " . $pr->{zzlu} . "\n";
        print "PERMISSION HOLDER: " . $pr->{permission_holder} . " " . check_holder($pr) . "\n";
        print "ROLE PERM: " . $pr->{roleperm} . " " . check_roleperm($pr) . "\n";
        print "IP LIST: " . $pr->{ip_list} . " " . check_list( 'ip', $pr ) . "\n";
        print "FAP LIST: " . $pr->{fap_list} . " " . check_list( 'fap', $pr ) . "\n";
        print "OVERALL MATCH: "
          . (
            (
                      check_holder($pr) eq $PASS
                  and check_roleperm($pr) eq $PASS
                  and check_list( 'ip',  $pr ) =~ /^$PASS/
                  and check_list( 'fap', $pr ) =~ /^$PASS/
            )
            ? $PASS
            : $FAIL
          ) . "\n";
        foreach my $p ( $Zymonic::ZCONFIG{$Zymonic::system}->record_permissions )
        {
            print "PERMISSION $p : " . ( $pr->{ 'permission_' . $p } || '' ) . "\n";
        }
        print "\n";
    }

    print "\n" . join(
        "\n\n",
        map {
            my $list_type = $_;
            "==="
              . $list_type . "===\n"
              . join( "\n",
                map { my $list = $_; $list . ":" . join( ",", @{ $lists{$list_type}->{$list} } ) }
                  keys( %{ $lists{$list_type} } ) )
        } keys(%lists)
    ) . "\n";
}

sub check_holder
{
    my $pr = shift;

    unless ( scalar(@holders) )
    {
        @holders = (
            "USER:" . $auth->{user},
            ( map { "GROUP:" . $_ } @{ $auth->{userdata}->{groups} } ),
            ( map { "GROUPNAME:" . $_ } @{ $auth->{userdata}->{groupnames} } ),
            'AUTHENTICATED'
        );
    }

    return ( grep { $_ eq $pr->{permission_holder} } @holders ) ? $PASS : $FAIL;
}

sub check_roleperm
{
    my $pr = shift;

    return ( $pr->{roleperm} eq 'any' or ( grep { $_ eq $pr->{roleperm} } @role_perms ) ) ? $PASS : $FAIL;
}

sub check_list
{
    my $list = shift;
    my $pr   = shift;

    $lists{$list} = {} unless ( ref( $lists{$list} ) );

    unless ( ref( $lists{$list}->{ $pr->{ $list . "_list" } } ) )
    {
        my $list_recs = $db->run_query(
            {
                string => "SELECT $listfield{$list} FROM zz_" . $list . "_lists WHERE name = ?",
                params => [ $pr->{ $list . '_list' } ]
            }
        );
        $lists{$list}->{ $pr->{ $list . "_list" } } = [ map { $_->{ $listfield{$list} } } @{$list_recs} ];
    }

    my $check_rec = $db->run_query(
        {
            string => "SELECT $listfield{$list} FROM  zz_" . $list . "_lists WHERE ? LIKE $listfield{$list}",
            params => [ ( $list eq 'ip' ) ? $ip_address : $fap ]
        }
    );

    return ( scalar( @{$check_rec} ) > 0 ) ? $PASS . " ($check_rec->[0]->{$listfield{$list}}) " : $FAIL;
}
