package Routes;
use Data::Printer;
use File::Slurper 'read_text';
use Time::HiRes qw(gettimeofday tv_interval);
use Dancer2;
use Dancer2::Plugin::Database;
use Dancer2::Plugin::Auth::Tiny;
use Dancer2::Plugin::CryptPassphrase; # this is (deliberately) slow due to hashing
use App::Model;
use experimental 'signatures'; # for pre-5.36 perl
our $VERSION = '0.1';
# add 'on_connect_do' statement from db/init_db.sql (text too long for
# config.yml without using line-break commands)
my $cfg = config(); # OK to make this global
$cfg->{plugins}->{Database}->{on_connect_do} = read_text( setting('db_init') ); # p $cfg;
# turn on/off DBI query output to console if supplied as command-line option:
database->trace('SQL|'.$ENV{DBI_TRACE}) if defined $ENV{DBI_TRACE}; # could have value '0'
set auto_page => 1; # maybe not in use, but useful anyway
my $app = App::Model->new( dbh => database() ); # p $app->model;
hook before => sub {
var t0 => [gettimeofday];
};
get '/' => needs login => sub {
template home => { title => 'DocsLibrary' };
};
get '/login' => sub { # my $p = params(); p $p;
my $return_url = query_parameters->get('return_url'); # set by Auth::Tiny
template login => { title => 'Login page', return_url => $return_url }
};
# this isn't needed except for testing:
get '/logout' => sub { app->destroy_session; redirect '/' };
post '/login' => sub { # my $p = params(); p $p;
my $password = body_parameters->get('password');
my $return_url = body_parameters->get('return_url');
if ( my $username = _authenticate_user($password) ) {
app->change_session_id;
session user => $username; # Plugin::Auth::Tiny requires session id = 'user'
info "$username successfully logged in";
redirect $return_url || '/';
}
else {
warning "failed login attempt for $username";
template login => { login_error => 1, return_url => $return_url };
}
};
true;
# using PBKDF2 for development, faster but less secure than Argon2 for deployment
sub _authenticate_user ($password) {
my $username = body_parameters->get('username');
if ( app->environment eq 'development' ) { # use faster PBKDF2 method
my $user = setting 'user';
$username ||= $user->{name}; # don't need to provide it for dev
return $username if $app->verify_password( $password, $user->{pwd} );
}
else { # Plugin::CryptPassphrase (uses more secure argon2 auth)
my $user = $app->find_user($username);
return $username if verify_password( $password, $user->{password} );
}
return undef; # failed authentication
}