From 27fda1f00aaf89ee1b86fe8910e5fe5708186a57 Mon Sep 17 00:00:00 2001 From: swag Date: Fri, 21 Apr 2023 21:40:39 -0400 Subject: [PATCH] Actions for resetting passwords --- README.md | 1 - lib/PostText.pm | 8 ++++ lib/PostText/Controller/Moderator.pm | 53 +++++++++++++++++++++++++ lib/PostText/Model/Moderator.pm | 20 ++++++++++ t/admin.t | 23 ++++++----- t/moderator.t | 20 +++++++--- templates/layouts/default.html.ep | 4 +- templates/moderator/admin_reset.html.ep | 15 +++++++ templates/moderator/mod_reset.html.ep | 11 +++++ 9 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 templates/moderator/admin_reset.html.ep create mode 100644 templates/moderator/mod_reset.html.ep diff --git a/README.md b/README.md index 3a0426f..c1978bf 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ Run the tests locally (against development environment): ## TODOs -1. Action for resetting passwords 1. Action for locking/unlocking accounts 1. CSS 1. "All new posts flagged" mode (require approval for new posts) diff --git a/lib/PostText.pm b/lib/PostText.pm index dd3070a..539e350 100644 --- a/lib/PostText.pm +++ b/lib/PostText.pm @@ -160,6 +160,10 @@ sub startup($self) { ->to('moderator#hidden') ->name('hidden_list'); + $moderator->any([qw{GET POST}], '/reset') + ->to('moderator#mod_reset') + ->name('mod_reset'); + my $mod_thread = $moderator->under('/thread'); $mod_thread->get('/unflag/:thread_id', [thread_id => qr/\d+/]) @@ -198,6 +202,10 @@ sub startup($self) { $mod_admin->any([qw{GET POST}], '/create') ->to('moderator#create') ->name('create_mod'); + + $mod_admin->any([qw{GET POST}], '/reset') + ->to('moderator#admin_reset') + ->name('admin_reset'); } 1; diff --git a/lib/PostText/Controller/Moderator.pm b/lib/PostText/Controller/Moderator.pm index e566729..8d1cff0 100644 --- a/lib/PostText/Controller/Moderator.pm +++ b/lib/PostText/Controller/Moderator.pm @@ -180,4 +180,57 @@ sub create($self) { return $self->render; } +sub admin_reset($self) { + my $v; + + $v = $self->validation if $self->req->method eq 'POST'; + + if ($v && $v->has_data) { + $v->required('email' ); + $v->required('password'); + + if ($v->has_error) { + $self->stash(status => 400) + } + else { + my ($email, $password); + + $email = $self->param('email' ); + $password = $self->param('password'); + + $self->moderator->admin_reset($email, $password); + $self->stash(info => "Reset password for $email 🔐"); + } + } + + return $self->render; +} + +sub mod_reset($self) { + my $v; + + $v = $self->validation if $self->req->method eq 'POST'; + + if ($v && $v->has_data) { + $v->required('password'); + + if ($v->has_error) { + $self->stash(status => 400) + } + else { + my ($password, $mod_id); + + $password = $self->param('password'); + $mod_id = $self->session->{'mod_id'}; + + $self->moderator->mod_reset($mod_id, $password); + $self->flash(info => "Password has been reset 🔐"); + + return $self->redirect_to('flagged_list'); + } + } + + return $self->render; +} + 1; diff --git a/lib/PostText/Model/Moderator.pm b/lib/PostText/Model/Moderator.pm index 3b34c71..847f2f2 100644 --- a/lib/PostText/Model/Moderator.pm +++ b/lib/PostText/Model/Moderator.pm @@ -172,4 +172,24 @@ sub hidden($self) { END_SQL } +sub admin_reset($self, $email, $password) { + my $password_hash = $self->authenticator->hash_password($password); + + $self->pg->db->query(<<~'END_SQL', $password_hash, $email); + UPDATE moderators + SET password_hash = ? + WHERE email_addr = ?; + END_SQL +} + +sub mod_reset($self, $mod_id, $password) { + my $password_hash = $self->authenticator->hash_password($password); + + $self->pg->db->query(<<~'END_SQL', $password_hash, $mod_id); + UPDATE moderators + SET password_hash = ? + WHERE moderator_id = ?; + END_SQL +} + 1; diff --git a/t/admin.t b/t/admin.t index 1d01e77..21d2e0d 100644 --- a/t/admin.t +++ b/t/admin.t @@ -23,19 +23,24 @@ subtest Login => sub { $t->get_ok('/moderator/flagged') ->status_is(200) ->text_like(h2 => qr/Flagged Posts/) - ->element_exists('a[href*="/moderator/admin/create"]' ); - - $t->get_ok('/moderator/hidden') - ->status_is(200) - ->text_like(h2 => qr/Hidden Posts/) - ->element_exists('a[href*="/moderator/admin/create"]' ); + ->element_exists('a[href*="/moderator/admin/create"]') + ->element_exists('a[href*="/moderator/admin/reset"]' ) }; subtest Create => sub { $t->get_ok('/moderator/admin/create') ->status_is(200) ->text_like(h2 => qr/Create Moderator/) - ->element_exists('a[href*="/moderator/admin/create"]' ) + ->element_exists('form input[name="name"]' ) + ->element_exists('form input[name="email"]' ) + ->element_exists('form input[name="password"]') + }; + + subtest Reset => sub { + $t->get_ok('/moderator/admin/reset') + ->status_is(200) + ->text_like(h2 => qr/Reset Password/) + ->element_exists('a[href*="/moderator/admin/reset"]') }; # Admin session ends @@ -46,11 +51,11 @@ subtest Login => sub { subtest 'No admin, no buttons', sub { $t->get_ok('/thread/single/1') ->status_is(200) - ->element_exists_not('a[href*="/moderator/admin/create"]' ); + ->element_exists_not('a[href*="/moderator/admin/create"]'); $t->get_ok('/remark/single/1') ->status_is(200) - ->element_exists_not('a[href*="/moderator/admin/create"]' ); + ->element_exists_not('a[href*="/moderator/admin/create"]'); $t->get_ok('/moderator/admin/create') ->status_is(302) diff --git a/t/moderator.t b/t/moderator.t index 41fcb08..dfdbcb2 100644 --- a/t/moderator.t +++ b/t/moderator.t @@ -80,18 +80,26 @@ subtest Login => sub { $t->get_ok('/moderator/flagged') ->status_is(200) ->text_like(h2 => qr/Flagged Posts/) - ->element_exists('a[href*="/moderator/flagged"]' ) - ->element_exists('a[href*="/moderator/hidden"]' ) - ->element_exists('a[href*="/logout"]' ) + ->element_exists('a[href*="/moderator/flagged"]') + ->element_exists('a[href*="/moderator/hidden"]' ) + ->element_exists('a[href*="/logout"]' ) }; subtest Hidden => sub { $t->get_ok('/moderator/hidden') ->status_is(200) ->text_like(h2 => qr/Hidden Posts/) - ->element_exists('a[href*="/moderator/flagged"]' ) - ->element_exists('a[href*="/moderator/hidden"]' ) - ->element_exists('a[href*="/logout"]' ) + ->element_exists('a[href*="/moderator/flagged"]') + ->element_exists('a[href*="/moderator/hidden"]' ) + ->element_exists('a[href*="/logout"]' ) + }; + + subtest Reset => sub { + $t->get_ok('/moderator/reset') + ->status_is(200) + ->text_like(h2 => qr/Reset Password/) + ->element_exists('a[href*="/moderator/reset"]') + ->element_exists('form input[name="password"]') }; # Mod session ends diff --git a/templates/layouts/default.html.ep b/templates/layouts/default.html.ep index 6e61eb7..52f846c 100644 --- a/templates/layouts/default.html.ep +++ b/templates/layouts/default.html.ep @@ -19,13 +19,15 @@ Moderate: <%= link_to Flagged => 'flagged_list' %> <%= link_to Hidden => 'hidden_list' %> + <%= link_to Reset => 'mod_reset' %> <%= link_to Logout => 'mod_logout' %> <% } =%>
<% if (is_admin) { =%> Admin: - <%= link_to Create => 'create_mod' %> + <%= link_to Create => 'create_mod' %> + <%= link_to Reset => 'admin_reset' %> <% } =%>
diff --git a/templates/moderator/admin_reset.html.ep b/templates/moderator/admin_reset.html.ep new file mode 100644 index 0000000..81f05cb --- /dev/null +++ b/templates/moderator/admin_reset.html.ep @@ -0,0 +1,15 @@ +% layout 'default'; +% title 'Reset Password'; +

<%= title %>

+
+ +
+ <%= label_for password => 'Password' %> + <%= password_field 'password' %> +
+ <%= submit_button 'Reset' %> +
+ diff --git a/templates/moderator/mod_reset.html.ep b/templates/moderator/mod_reset.html.ep new file mode 100644 index 0000000..4d4ac44 --- /dev/null +++ b/templates/moderator/mod_reset.html.ep @@ -0,0 +1,11 @@ +% layout 'default'; +% title 'Reset Password'; +

<%= title %>

+
+
+ <%= label_for password => 'Password' %> + <%= password_field 'password' %> +
+ <%= submit_button 'Reset' %> +
+