From 7038dc3afaa78648ed42dd2ccc5b3f99babc3d70 Mon Sep 17 00:00:00 2001 From: Al Beano Date: Sat, 15 Jul 2017 19:24:05 +0100 Subject: [PATCH] misc improvements, basic authentication system (no session support yet) --- .gitignore | 2 + config.yml | 28 ++------ cpanfile | 5 ++ lib/cyberman.pm | 155 +++++++++++++++++++++++++++++++++++++++++- schema.sql | 16 +++++ views/index.tt | 1 + views/layouts/main.tt | 12 ++++ views/login.tt | 53 +++++++++++++++ views/register.tt | 67 ++++++++++++++++++ 9 files changed, 314 insertions(+), 25 deletions(-) create mode 100644 .gitignore create mode 100644 schema.sql create mode 100644 views/login.tt create mode 100644 views/register.tt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b37fe07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.swp +db.sqlite diff --git a/config.yml b/config.yml index ca3e7b4..35fcaab 100644 --- a/config.yml +++ b/config.yml @@ -27,26 +27,8 @@ engines: auto_page: 1 -# session engine -# -# Simple: in-memory session store - Dancer2::Session::Simple -# YAML: session stored in YAML files - Dancer2::Session::YAML -# -# Check out metacpan for other session storage options: -# https://metacpan.org/search?q=Dancer2%3A%3ASession&search_type=modules -# -# Default value for 'cookie_name' is 'dancer.session'. If you run multiple -# Dancer apps on the same host then you will need to make sure 'cookie_name' -# is different for each app. -# -#engines: -# session: -# Simple: -# cookie_name: testapp.session -# -#engines: -# session: -# YAML: -# cookie_name: eshop.session -# is_secure: 1 -# is_http_only: 1 +plugins: + Database: + driver: 'SQLite' + dbname: 'db.sqlite' + log_queries: 1 diff --git a/cpanfile b/cpanfile index 547c52d..51e1b97 100644 --- a/cpanfile +++ b/cpanfile @@ -5,6 +5,11 @@ requires "URL::Encode::XS" => "0"; requires "CGI::Deurl::XS" => "0"; requires "HTTP::Parser::XS" => "0"; requires "Plack::Middleware::Deflater" => "0"; +requires "Dancer2::Plugin::Database" => "0"; +requires "DBD::SQLite" => "0"; +requires "HTML::Entities" => "0"; +requires "Digest::Bcrypt" => "0"; +requires "Math::Random::Secure" => "0"; on "test" => sub { requires "Test::More" => "0"; diff --git a/lib/cyberman.pm b/lib/cyberman.pm index 614651c..11e8735 100644 --- a/lib/cyberman.pm +++ b/lib/cyberman.pm @@ -1,10 +1,161 @@ package cyberman; use Dancer2; +use Dancer2::Plugin::Database; +use Digest::Bcrypt; +use Math::Random::Secure qw(rand irand); -our $VERSION = '0.1'; +##### +# cyberman.pm +# index page and authentication +##### + +# misc authentication subs + +sub get_auth { + my $uid = shift; + my $token = shift; + + my $result = database->quick_select("session", {"uid" => $uid, "token" => $token}); + + if ($result) { + return $uid; + } else { + return 0; + } +} + +sub randstring { + my $len = shift; + + my @chars = (0..9, "a".."z", "A".."Z"); + my $ret; + + for (1..$len) { + $ret .= $chars[irand(scalar(@chars))]; + } + return $ret; +} + +prefix undef; + +hook 'before' => sub { + sub cookieval { + my $name = shift; + my $cookie = cookie($name); + if ($cookie) { + return $cookie->value; + } else { + return undef; + } + } + + my $uid = cookieval("token"); + my $token = cookieval("token"); + my $auth = 0; + if ($uid && $token) { + $auth = get_auth($uid, $token); + } + + var auth => $auth; +}; get '/' => sub { - template 'index' => { 'title' => 'cyberman' }; + if (!vars->{auth}) { + template 'index'; + } +}; + +post '/register' => sub { + my %errs; + + for my $param ("password", "password2", "email") { + if (!param($param)) { + $errs{"e_no_$param"} = 1; + } + } + + if (!exists $errs{"e_no_password"} || !exists $errs{"e_no_password2"}) { + if (param("password") ne param("password2")) { + $errs{"e_pass_match"} = 1; + } + + if (length param("password") < 8) { + $errs{"e_pass_len"} = 1; + } + } + + if (scalar(keys(%errs)) != 0) { + return template 'register' => { + error => 1, + params, + %errs, + }; + } + + # Hash password + my $salt = randstring(16); + + my $b = new Digest::Bcrypt; + $b->cost(8); + $b->salt($salt); + $b->add(param "password"); + + # Create the account in the database + database->quick_insert( + "user", + { + "email" => param("email"), + "password" => $b->bcrypt_b64digest, + "salt" => $salt, + }, + ); + + # TODO: send confirmation email + + template 'login' => { + account_created => 1, + params, + }; +}; + +post '/login' => sub { + my %errs; + + my $user = database->quick_select( + "user", + { + "email" => param("email"), + }, + ); + + if (!$user) { + $errs{"e_no_user"} = 1; + } + + if (scalar(keys(%errs)) == 0) { + my $b = new Digest::Bcrypt; + $b->cost(8); + $b->salt($user->{"salt"}); + $b->add(param "password"); + + $errs{"e_pass"} = 1 unless $b->bcrypt_b64digest eq $user->{"password"}; + } + + if (scalar(keys(%errs)) == 0) { + $errs{"e_not_confirmed"} = 1 unless $user->{"active"}; + } + + if (scalar(keys(%errs)) != 0) { + return template 'login' => { + error => 1, + %errs, + params, + }; + } + + # checks finished, we can create a session now + + return; }; true; diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..c65922c --- /dev/null +++ b/schema.sql @@ -0,0 +1,16 @@ +drop table if exists user; +create table user ( + id integer primary key, + email text not null, + password text not null, + salt text not null, + active integer not null default 0 +); + +drop table if exists session; +create table session ( + id integer primary key, + uid text not null, + since integer not null, + token text not null +); diff --git a/views/index.tt b/views/index.tt index a4f8315..2c3f8b9 100644 --- a/views/index.tt +++ b/views/index.tt @@ -10,6 +10,7 @@

.cyb is a new TLD (top level domain) for anything and everything cyberpunk-related, on the OpenNIC network. To get started, change your computer's DNS settings to use OpenNIC's servers - here's how. Registering a .cyb domain is free for everyone.

You can read our charter for more information and details on how to report abuse of cybNIC services.

+

Create or log in to an account in order to register or update .cyb domains.

log in ]  [ register ]

diff --git a/views/layouts/main.tt b/views/layouts/main.tt index 3ebfbff..7b80575 100644 --- a/views/layouts/main.tt +++ b/views/layouts/main.tt @@ -3,6 +3,7 @@ + cybNIC diff --git a/views/login.tt b/views/login.tt new file mode 100644 index 0000000..70154d6 --- /dev/null +++ b/views/login.tt @@ -0,0 +1,53 @@ +
+
+

Log in

+
+ + <% IF account_created %> +
+ Your account has been created and a confirmation email sent to <% email | html_entity %>. Please confirm your email address, then log in here. +
+

+ <% END %> + + <% IF error %> +
+ <% IF e_no_user %> + The email address <% email | html_entity %> is not registered. + <% END %> + <% IF e_pass %> + Your password was incorrect, sorry. + <% END %> + <% IF e_not_confirmed %> + Please confirm your email address using the link sent to <% email | html_entity %>. + <% END %> +
+

+ <% END %> + +
+ +
+ +
diff --git a/views/register.tt b/views/register.tt new file mode 100644 index 0000000..f12cad7 --- /dev/null +++ b/views/register.tt @@ -0,0 +1,67 @@ +
+
+

Register

+
+
+ +<% IF error %> +
+
+ There were some problems with your registration: +
+
    + <% IF e_no_email %> +
  • You need to enter a valid email address.
  • + <% END %> + <% IF e_no_password %> +
  • You need to enter a password.
  • + <% END %> + <% IF e_pass_len %> +
  • Your password must be at least 8 characters!
  • + <% END %> + <% IF e_no_password2 %> +
  • You need to enter a password confirmation.
  • + <% END %> + <% IF e_pass_match %> +
  • The two passwords you entered do not match!
  • + <% END %> +
+
+
+
+<% END %> + +
+ +