#!/bin/bash

# read rc files if exist
[ -e ~/.thruk   ] && . ~/.thruk
[ -e ~/.profile ] && . ~/.profile

if [ ! -z $OMD_ROOT ]; then
  export PERL5LIB=$OMD_ROOT/share/thruk/lib:$PERL5LIB
  if [ -z $CATALYST_CONFIG ]; then export CATALYST_CONFIG="$OMD_ROOT/etc/thruk"; fi
elif [ -e $(dirname $0)/../lib/Thruk.pm ]; then
  export PERL5LIB="$PERL5LIB:$(dirname $0)/../lib";
  if [ -z $CATALYST_CONFIG ]; then export CATALYST_CONFIG="$(dirname $0)/../"; fi
else
  export PERL5LIB=$PERL5LIB:/usr/share/thruk/lib:/usr/lib/thruk/perl5;
  if [ -z $CATALYST_CONFIG ]; then export CATALYST_CONFIG='/etc/thruk'; fi
fi

eval 'exec perl -x $0 ${1+"$@"} ;'
    if 0;

#! -*- perl -*-
# vim: expandtab:ts=4:sw=4:syntax=perl
#line 24

use warnings;
use strict;
use File::Slurp;
use Time::HiRes qw( gettimeofday tv_interval );
use Thruk::Config;
use Thruk::Utils::CookieAuth;

$|            = 1;
my $prefix    = '';
my $urlprefix = "thruk";
if(defined $ENV{'OMD_SITE'}) {
    $prefix    = $ENV{'OMD_SITE'}.'/';
    $urlprefix = "[^/]+/thruk";
}
my $verbose = 0;
$verbose = $ENV{'THRUK_COOKIE_AUTH_VERBOSE'} if $ENV{'THRUK_COOKIE_AUTH_VERBOSE'};

my $guest;
$guest = $ENV{'THRUK_COOKIE_AUTH_GUEST_USER'} if $ENV{'THRUK_COOKIE_AUTH_GUEST_USER'};

##########################################################
my $config              = Thruk::Config::get_config();
my $spath               = $config->{'tmp_path'}."/sessions";
my $loginurl            = $config->{'cookie_auth_login_url'}             || $prefix."thruk/cgi-bin/login.cgi";
my $sessiontimeout      = $config->{'cookie_auth_session_timeout'}       || 86400;
my $sessioncachetimeout = $config->{'cookie_auth_session_cache_timeout'} || 5;
my $sessioncache        = {};
$loginurl               = "/redirect/".$loginurl;
my $line_regex          = qr|^/(.*?)/(.*?)/____/(.*)$|mx;
my $pass_regex          = qr#^$urlprefix/(startup\.html|themes|javascript|cgi\-bin/(login|remote|restricted)\.cgi)#mx;
my $cookie_regex        = qr/thruk_auth=(\w+)/mx;
my $session_split       = qr/~~~/mx;
my $last_cache_clean    = time();

##########################################################
while (<STDIN>) {
    chomp(my $in = $_);

    my $t0 = [gettimeofday];
    print STDERR "thruk_auth: url '$in' " if $verbose > 1;
    print process($in);
    my $elapsed = tv_interval ( $t0 );
    printf(STDERR " (took %.3f sec)\n",$elapsed) if $verbose;
}

##########################################################
sub process {
    my($in) = @_;

    if($in =~ $line_regex) {
        my($cookies, $remote, $path) = ($1, $2, $3);

        # remove last &
        $path =~ s|/____/|?|mxo;
        $path =~ s|\?$||mxo;

        # some urls must pass
        if($path =~ $pass_regex) {
            print STDERR "thruk_auth: pass $path" if $verbose;
            return "/pass/$path\n";
        }

        # did we get a cookie
        if($cookies eq '' or $cookies !~ $cookie_regex) {
            print STDERR "thruk_auth: no cookie $path" if $verbose;
            return "/loginok/".$guest."/",$path,"\n" if $guest;
            return $loginurl."?".$path."\n";;
        }
        my $auth = $1;

        # use session cache for a few seconds
        my $now = time();
        if(defined $sessioncache->{$auth} and $sessioncache->{$auth}->{'time'} > $now - $sessioncachetimeout) {
            print STDERR "thruk_auth: login by cache hit: ".$sessioncache->{$auth}->{'login'} if $verbose;
            return "/loginok/", $sessioncache->{$auth}->{'login'} ,"/",$path,"\n";
        }

        # does our sessionfile exist?
        my $sessionfile = $spath.'/'.$auth;
        if(!-f $sessionfile) {
            print STDERR "thruk_auth: session expired: ".($sessioncache->{$auth}->{'login'} || '?') if $verbose;
            delete $sessioncache->{$auth} if defined $sessioncache->{$auth};
            return "/loginok/".$guest."/",$path,"\n" if $guest;
            return $loginurl."?expired&".$path."\n";
        }

        # session timeout reached?
        if(defined $sessioncache->{$auth} and $sessioncache->{$auth}->{'time'} < $now - $sessiontimeout) {
            print STDERR "thruk_auth: session timeout: ".$sessioncache->{$auth}->{'login'} if $verbose;
            unlink($sessionfile);
            delete $sessioncache->{$auth};
            return "/loginok/".$guest."/",$path,"\n" if $guest;
            return $loginurl, "?expired&", $path, "\n";
        }

        chomp(my $cont = read_file($sessionfile));
        my($basicauth, $ip, $user) = split($session_split, $cont, 3);

        # won't work in omds own apache mode
        #if($ip ne $remote) {
        #    return "/loginok/".$guest."/",$path,"\n" if $guest;
        #    return $loginurl, "?", $path, "\n";
        #}

        # validate session data
        my $rc;
        eval {
            $rc = Thruk::Utils::CookieAuth::verify_basic_auth($config, $basicauth, $user);
        };
        if($rc == -1 or $@) {
            print STDERR "thruk_auth: technical problem during login for ".$user if $verbose;
            print STDERR "thruk_auth: ".$@ if $@;
            unlink($sessionfile);
            delete $sessioncache->{$auth};
            return $loginurl, "?problem&", $path, "\n";
        }
        if($rc == 0) {
            print STDERR "thruk_auth: basic auth verify failed for ".$user if $verbose;
            unlink($sessionfile);
            delete $sessioncache->{$auth};
            return $loginurl, "?invalid&", $path, "\n";
        }

        # clean old session cache every hour
        clean_session_cache($sessioncache) if($last_cache_clean < $now - 3600);

        # grant access
        $sessioncache->{$auth} = {
                'login' => $user,
                'time'  => $now,
        };
        print STDERR "thruk_auth: basic auth ok for ".$user if $verbose;
        return "/loginok/", $user, "/", $path, "\n";
    }

    # everything else redirects to login
    print STDERR "thruk_auth: unknown url ".$in if $verbose;
    return $loginurl, "\n";
}

##########################################################
sub clean_session_cache {
    my($sessioncache) = @_;
    my $clean_before = time() - $sessiontimeout - $sessioncachetimeout;
    for my $key (keys %{$sessioncache}) {
        delete $sessioncache->{$key} if $sessioncache->{$key}->{'time'} < $clean_before;
    }
    return;
}
