From fb17a96c26267986b45de568a4c51b41dd722fed Mon Sep 17 00:00:00 2001 From: Al Beano Date: Sat, 19 Aug 2017 15:03:54 +0100 Subject: [PATCH] Implement password resets --- dbupdate/2.sql | 2 + lib/cyberman.pm | 1 + lib/cyberman/Forgot.pm | 117 ++++++++++++++++++++++++++++++++++++++++ schema.sql | 5 +- views/confirm_forgot.tt | 49 +++++++++++++++++ views/email/forgot.tt | 6 +++ views/forgot.tt | 27 ++++++++++ views/login.tt | 2 + 8 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 dbupdate/2.sql create mode 100644 lib/cyberman/Forgot.pm create mode 100644 views/confirm_forgot.tt create mode 100644 views/email/forgot.tt create mode 100644 views/forgot.tt diff --git a/dbupdate/2.sql b/dbupdate/2.sql new file mode 100644 index 0000000..5a11dae --- /dev/null +++ b/dbupdate/2.sql @@ -0,0 +1,2 @@ +alter table user add column recoverytoken text; +update cyberman set dbrev=3; diff --git a/lib/cyberman.pm b/lib/cyberman.pm index 18b949e..8949b7d 100644 --- a/lib/cyberman.pm +++ b/lib/cyberman.pm @@ -9,6 +9,7 @@ use cyberman::Account; use cyberman::Helper; use cyberman::API; use cyberman::Records; +use cyberman::Forgot; # Index route, hook and helper functions for authentication diff --git a/lib/cyberman/Forgot.pm b/lib/cyberman/Forgot.pm new file mode 100644 index 0000000..b10a26c --- /dev/null +++ b/lib/cyberman/Forgot.pm @@ -0,0 +1,117 @@ +package cyberman::Forgot; + +use Dancer2 appname => "cyberman"; +use Dancer2::Plugin::Database; +use URI::Escape; + +use cyberman::Helper; + +post '/forgot' => sub { + my $user = database->quick_select( + "user", + { + "email" => param("email"), + }, + ); + + if (!$user) { + return template 'forgot' => { + err => 1, + e_no_user => 1, + }; + } + + my $token = randstring(32); + database->quick_update( + "user", + { + "id" => $user->{"id"}, + }, + { + "recoverytoken" => $token, + }, + ); + + my $email = template 'email/forgot' => { + "link" => config->{"mail"}->{"baseurl"} . "/confirm_forgot?e=" . uri_escape(param("email")) . "&t=$token", + }, + { + "layout" => undef, + }; + send_email(param("email"), $email); + + template 'forgot' => { + success => 1, + }; +}; + +post '/confirm_forgot' => sub { + my %errs; + + if (!param("e") || !param("t")) { + $errs{"e_bad_link"} = 1; + } elsif (!param("password")) { + $errs{"e_no_pass"} = 1; + } elsif (length(param("password")) < 8) { + $errs{"e_pass_len"} = 1; + } elsif (param("password") ne param("password2")) { + $errs{"e_pass_mismatch"} = 1; + } + + if (scalar(keys(%errs)) == 0) { + my $user = database->quick_select( + "user", + { + "email" => param("e"), + "recoverytoken" => param("t"), + }, + ); + + if (!$user) { + $errs{"e_bad_link"} = 1; + } else { + # Update the password + my ($hash, $salt) = hash_password(param "password"); + database->quick_update( + "user", + { + "id" => $user->{"id"}, + }, + { + "password" => $hash, + "salt" => $salt, + }, + ); + + database->quick_delete( + "session", + { + "uid" => $user->{"id"}, + }, + ); + + database->quick_update( + "user", + { + "id" => $user->{"id"}, + }, + { + "recoverytoken" => "", + }, + ); + } + } + + if (scalar(keys(%errs)) != 0) { + return template 'confirm_forgot' => { + "err" => 1, + %errs, + }; + } + + template 'redir' => { + "redir" => "login?pwchange=1", + }; +}; + +true; diff --git a/schema.sql b/schema.sql index 9e0bb8a..17c3222 100644 --- a/schema.sql +++ b/schema.sql @@ -3,7 +3,7 @@ create table cyberman ( id integer primary key, dbrev integer not null ); -insert into cyberman (dbrev) values (2); +insert into cyberman (dbrev) values (3); drop table if exists user; create table user ( @@ -13,7 +13,8 @@ create table user ( salt text not null, active integer not null default 0, conftoken text not null, - newemail text + newemail text, + recoverytoken text ); drop table if exists session; diff --git a/views/confirm_forgot.tt b/views/confirm_forgot.tt new file mode 100644 index 0000000..1408ef2 --- /dev/null +++ b/views/confirm_forgot.tt @@ -0,0 +1,49 @@ +
+
+

Update your password

+
+ <% IF err %> +
+ Error: + <% IF e_bad_link %> + The link you clicked on is invalid! + <% ELSIF e_no_pass %> + You must enter a password. + <% ELSIF e_pass_len %> + Your password must be at least 8 characters long. + <% ELSIF e_pass_mismatch %> + The two passwords you entered do not match. + <% END%> +
+

+ <% END %> +
+ +
+ Enter a new password and click update. +
+ +
diff --git a/views/email/forgot.tt b/views/email/forgot.tt new file mode 100644 index 0000000..dfaca4e --- /dev/null +++ b/views/email/forgot.tt @@ -0,0 +1,6 @@ +Hello! + +You're receiving this email because someone tried to reset your cybNIC password. If this was you, please click the link below: +<% link %> + +If it wasn't, you can safely ignore this email. diff --git a/views/forgot.tt b/views/forgot.tt new file mode 100644 index 0000000..01e78e9 --- /dev/null +++ b/views/forgot.tt @@ -0,0 +1,27 @@ +
+
+

Forgotten password

+
+ <% IF err || success %> +
+ <% IF e_no_user %> + Error: There is no user account associated with that email address. + <% END %> + <% IF success %> + An email has been sent to <% params.email | html_entity %>. Please click the link to reset your password. + <% END %> +
+

+ <% END %> +
+ +
+ Enter your email into the field below and we'll send an email with a link to reset your password. +
+
+ + +
+ [  ] +
+
diff --git a/views/login.tt b/views/login.tt index 9935b8b..8123cd2 100644 --- a/views/login.tt +++ b/views/login.tt @@ -57,5 +57,7 @@
 ] [ go back ] +

+ [ recover forgotten password ]