package Lemonldap::NG::Portal::Plugins::CrowdSec;

use strict;
use Mouse;
use JSON qw(from_json);
use Lemonldap::NG::Common::UserAgent;
use Lemonldap::NG::Portal::Main::Constants qw(
  PE_OK
  PE_ERROR
  PE_SESSIONNOTGRANTED
);

our $VERSION = '2.19.0';

extends 'Lemonldap::NG::Portal::Main::Plugin';

# Entrypoint
use constant beforeAuth => 'check';

has ua => (
    is      => 'rw',
    lazy    => 1,
    builder => sub {

        # TODO : LWP options to use a proxy for example
        my $ua = Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
        $ua->env_proxy();
        return $ua;
    }
);
has crowdsecUrl => ( is => 'rw' );

sub init {
    my ($self) = @_;
    if ( $self->conf->{crowdsecUrl} ) {
        my $tmp = $self->conf->{crowdsecUrl};
        $tmp =~ s#/+$##;
        $self->crowdsecUrl($tmp);
    }
    else {
        $self->logger->warn(
            "crowdsecUrl isn't set, fallback to http://localhost:8080");
        $self->crowdsecUrl('http://localhost:8080');
    }
    $self->logger->info( 'CrowdSec policy is: '
          . ( $self->conf->{crowdsecAction} ? 'reject' : 'warn' ) );
    return 1;
}

sub check {
    my ( $self, $req ) = @_;
    my $ip   = $req->address;
    my $resp = $self->ua->get(
        $self->crowdsecUrl . "/v1/decisions?ip=$ip",
        'Accept'    => 'application/json',
        'X-Api-Key' => $self->conf->{crowdsecKey},
    );
    if ( $resp->is_error ) {
        $self->logger->error( 'Bad CrowdSec response: ' . $resp->message );
        $self->logger->debug( $resp->content );
        return PE_ERROR;
    }
    my $content = $resp->decoded_content;
    if ( !$content or $content eq 'null' ) {
        $self->userLogger->debug("$ip isn't known by CrowdSec");
        return PE_OK;
    }
    my $json_hash;
    eval { $json_hash = from_json( $content, { allow_nonref => 1 } ); };
    if ($@) {
        $self->logger->error("Unable to decode CrowdSec response: $content");
        $self->logger->debug($@);
        return PE_ERROR;
    }
    $self->logger->debug("CrowdSec response: $content");

    # Response is "null" when IP is unknown
    if ($json_hash) {

        # CrowdSec may return more than one decision
        foreach my $decision (@$json_hash) {
            if ( $decision->{type} and $decision->{type} eq 'ban' ) {
                $self->userLogger->warn( "$ip banned by CrowdSec ('"
                      . $decision->{scenario}
                      . "' for $decision->{duration})" );
                if ( $self->conf->{crowdsecAction} eq 'reject' ) {
                    $self->userLogger->error("$ip rejected by CrowdSec");
                    return PE_SESSIONNOTGRANTED;
                }
                else {
                    $self->userLogger->error("$ip is banned by CrowdSec");
                    $req->env->{CROWDSEC_REJECT} = 1;
                    return PE_OK;
                }
            }
        }
        $self->userLogger->info("$ip not banned by CrowdSec");
        return PE_OK;
    }
}

1;
