#!/usr/bin/perl

use strict;
use Getopt::Long;
use Regexp::Common qw(net);

use constant VALIDIP => qr/^(?:$RE{net}{IPv4}|$RE{net}{IPv6})$/;

our $VERSION = '2.22.0';

sub usage {
    print STDERR "Tool to push a IP-ban decision to CrowdSec

Usage:
\$ $0 <options> <IP>

Options:
 -v, --version:  display version
 -h, --help:     display this
 -d, --debug:    enable debug mode
 -u, --user:     override config to fix Crowdsec 'machine-id'
 -p, --password: override config to fix Crowdsec password
 -m, --message:  ban message (default: 'Manual ban via LLNG')
 --loglevel:     fix logLevel (default: error)
";
}

sub error {
    print STDERR join "\n", @_, "\n";
    usage;
    exit 1;
}

my %opts;

unless (
    GetOptions(
        \%opts,         'h|help',
        'v|version',    'd|debug',
        'u|user=s',     'p|password=s',
        'm|message=s',  'loglevel|log-level=s'
    )
  )
{
    usage;
    exit 1;
}

# HELP
if ( $opts{h} ) {
    usage;
    exit;
}

# VERSION
if ( $opts{v} ) {
    print "$VERSION\n";
    exit;
}

unless ( @ARGV >= 1 ) {
    error 'Missing IP';
}

my $exitCode = 0;
$opts{loglevel} ||= 'error';

# Create CrowdSec client
my $client = CrowdSecClient->new(
    logLevel => $opts{d} ? 'debug' : $opts{loglevel},
    ( $opts{u} ? ( crowdsecMachineId => $opts{u} ) : () ),
    ( $opts{p} ? ( crowdsecPassword  => $opts{p} ) : () ),
);

my $message = $opts{m} || 'Manual ban via LLNG';

foreach my $ip (@ARGV) {
    unless ( $ip =~ VALIDIP ) {
        print STDERR "Invalid IP: $ip\n";
        $exitCode++;
        next;
    }
    unless (
        $client->ban(
            $ip,
            $message,
            { scenario => 'Manual ban via LLNG' },
        )
      )
    {
        print STDERR "Decision to ban $ip refused\n";
        $exitCode++;
        next;
    }
}

exit $exitCode;

# Lightweight class that consumes CrowdSec role
package CrowdSecClient;

use strict;
use Mouse;
use Lemonldap::NG::Common::Conf;
use Lemonldap::NG::Common::Conf::Constants;
use Lemonldap::NG::Common::Logger::Std;

with 'Lemonldap::NG::Portal::Lib::CrowdSec';

has conf => (
    is      => 'ro',
    lazy    => 1,
    builder => '_buildConf',
);

has overrides => (
    is      => 'ro',
    default => sub { {} },
);

has logger => (
    is      => 'ro',
    lazy    => 1,
    builder => '_buildLogger',
);

has userLogger => (
    is      => 'ro',
    lazy    => 1,
    builder => '_buildLogger',
);

sub _buildConf {
    my ($self) = @_;
    my $lmConf = Lemonldap::NG::Common::Conf->new
      or die $Lemonldap::NG::Common::Conf::msg;
    my $conf = $lmConf->getConf
      or die "Unable to get configuration ($!)";
    my $localconf = $lmConf->getLocalConf(PORTALSECTION);
    if ($localconf) {
        $conf->{$_} = $localconf->{$_} foreach ( keys %$localconf );
    }
    # Apply overrides
    my $overrides = $self->overrides;
    $conf->{$_} = $overrides->{$_} foreach ( keys %$overrides );
    return $conf;
}

sub _buildLogger {
    my ($self) = @_;
    return Lemonldap::NG::Common::Logger::Std->new( $self->conf );
}

around BUILDARGS => sub {
    my ( $orig, $class, %args ) = @_;
    my %overrides;
    for my $key (qw(crowdsecMachineId crowdsecPassword logLevel)) {
        if ( exists $args{$key} ) {
            $overrides{$key} = delete $args{$key};
        }
    }
    $args{overrides} = \%overrides;
    return $class->$orig(%args);
};

sub BUILD {
    my ($self) = @_;
    $self->_init or die "Unable to initialize CrowdSec client";
}

1;
