Bug 29925: Add a password reset page for expired passwords
This patch adds a new page opac-reset-password where a user cna enter their login
(userid or carndumber), current password, and new password.
If the user has a password expiration date and the current password is correct and
the new passwords match and meet requirements their password will be updated and the
expiration date reset
A patron whose password does not expire will be reidrected to login to change their password
To test:
1 - Apply patch, updatedatabase, enable new syspref EnableExpiredPasswordReset
2 - Set 'Password expiration' for a patron category
Home->Administration->Patron categories->Edit
3 - Create a new patron in this category with a userid/password set, and an email
4 - Update the patron with an expiration to be expired
UPDATE borrowers SET password_expiration='2022-01-01' WHERE borrowernumber=51;
5 - Give the borrower catalogue permission
6 - Attempt to log in to Straff interface
7 - Confirm you are signed out and notified that password must be reset
8 - Click 'Reset your password' link
9 - You should see the reset password page with fields for: login, current password, new password, conmfirm password
10 - enter invalid/incomplete credentials
11 - Confirm you are notified of invlaid credentials
12 - Fill in all fields, but enter current password as new password
13 - Confirm you are notified of no change
14 - Set minimum password length / strong password requirement for category
15 - Confirm you receive error if new password too short or not secure
16 - Enter a valid new password and submit and confirm update is successful
17 - Confirm you have buttons to go to OPAC or Staff and that both work
18 - Confirm you cna log in (i.e. expiration has been reset)
19 - Expire the users password
20 - Remove catalogue permission
21 - Reset password again and confirm only OPAC link
Signed-off-by: Bob Bennhoff <bbennhoff@clicweb.org>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Fridolin Somers <fridolin.somers@biblibre.com>
This commit is contained in:
parent
9c55003cc3
commit
e941dfdc99
|
|
@ -0,0 +1,15 @@
|
|||
use Modern::Perl;
|
||||
|
||||
return {
|
||||
bug_number => "29925",
|
||||
description => "Add password reset option",
|
||||
up => sub {
|
||||
my ($args) = @_;
|
||||
my ($dbh, $out) = @$args{qw(dbh out)};
|
||||
$dbh->do(q{
|
||||
INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES
|
||||
('EnableExpiredPasswordReset', '0', NULL, 'Enable ability for patrons with expired password to reset their password directly', 'YesNo')
|
||||
});
|
||||
say $out "Added EnableExpirePasswordReset system preference";
|
||||
},
|
||||
};
|
||||
|
|
@ -365,6 +365,14 @@ Patrons:
|
|||
0: "Don't require"
|
||||
- a strong password for staff and patrons
|
||||
- (must contain at least one digit, one lowercase and one uppercase).
|
||||
-
|
||||
- pref: EnableExpiredPasswordReset
|
||||
choices:
|
||||
1: "Enable"
|
||||
0: "Don't enable"
|
||||
- the ability for patrons to directly reset their password when it is expired.
|
||||
- If not enable patrons must either use the 'Forgot your password' feature or have staff
|
||||
- reset their password.
|
||||
-
|
||||
- Block a patron's account if it reaches
|
||||
- pref: FailedLoginAttempts
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@
|
|||
[% END %]
|
||||
[% ELSIF password_has_expired %]
|
||||
<div id="login_error"><strong>Error: </strong>Your password has expired!</div>
|
||||
[% IF Categories.can_any_reset_password && Koha.Preference('OpacBaseURL') %]
|
||||
[% IF Koha.Preference('EnableExpiredPasswordReset') && Koha.Preference('OpacBaseURL') %]
|
||||
<a href="[% Koha.Preference('OpacBaseURL') | url %]/cgi-bin/koha/opac-reset-password.pl">You must reset your password</a>.
|
||||
[% ELSIF Categories.can_any_reset_password && Koha.Preference('OpacBaseURL') %]
|
||||
<a href="[% Koha.Preference('OpacBaseURL') | url %]/cgi-bin/koha/opac-password-recovery.pl">You must reset your password</a>.
|
||||
[% ELSE %]
|
||||
<p>You must contact the library to have your password reset</p>
|
||||
|
|
|
|||
|
|
@ -171,9 +171,11 @@
|
|||
<div class="alert alert-info">
|
||||
<p><strong>Error: </strong>Your password has expired!</p>
|
||||
</div>
|
||||
[% IF Koha.Preference('OpacPasswordChange') && Categories.can_any_reset_password %]
|
||||
[% IF Koha.Preference('EnableExpiredPasswordReset') %]
|
||||
<a href="/cgi-bin/koha/opac-reset-password.pl">Reset your password</a>.
|
||||
[% ELSIF Koha.Preference('OpacPasswordChange') && Categories.can_any_reset_password %]
|
||||
<div id="resetpassword">
|
||||
<a href="/cgi-bin/koha/opac-password-recovery.pl">Reset your password?</a>
|
||||
<a href="/cgi-bin/koha/opac-password-recovery.pl">Reset your password</a>
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<p>You must contact the library to reset your password</p>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
[% USE raw %]
|
||||
[% USE Koha %]
|
||||
[% USE Categories %]
|
||||
[% USE AdditionalContents %]
|
||||
[% PROCESS 'html_helpers.inc' %]
|
||||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title>
|
||||
Reset your password
|
||||
›
|
||||
[% IF ( LibraryNameTitle ) %][% LibraryNameTitle | html %][% ELSE %]Koha online[% END %] catalog
|
||||
</title>
|
||||
[% INCLUDE 'doc-head-close.inc' %]
|
||||
[% BLOCK cssinclude %][% END %]
|
||||
</head>
|
||||
[% INCLUDE 'bodytag.inc' bodyid='opac-login-page' bodyclass='scrollto' %]
|
||||
[% INCLUDE 'masthead.inc' %]
|
||||
|
||||
<div class="main">
|
||||
<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumbs">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="/cgi-bin/koha/opac-main.pl">Home</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">
|
||||
<a href="#" aria-current="page">Reset your password</a>
|
||||
</li>
|
||||
</
|
||||
</nav> <!-- /#breadcrumbs -->
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-10 col-lg-6">
|
||||
<div id="opac-auth" class="maincontent">
|
||||
[% IF Koha.Preference( 'opacuserlogin' ) && Koha.Preference('EnableExpiredPasswordReset') %]
|
||||
[% IF ( error ) %]
|
||||
<div class="alert alert-warning">
|
||||
<h2>There was a problem with your submission</h2>
|
||||
<p>
|
||||
[% SWITCH error %]
|
||||
[% CASE 'no_change' %]
|
||||
New password must not be the same as old password.
|
||||
[% CASE 'passwords_mismatch' %]
|
||||
Passwords do not match. Please re-type your new password.
|
||||
[% CASE 'password_too_short' %]
|
||||
Password must be at least [% minPasswordLength | html %] characters long.
|
||||
[% CASE 'password_too_weak' %]
|
||||
Password must contain at least one digit, one lowercase and one uppercase.
|
||||
[% CASE 'password_has_whitespaces' %]
|
||||
Password must not contain leading or trailing whitespaces.
|
||||
[% CASE 'invalid_credentials' %]
|
||||
You entered an incorrect username or password. Please try again! But note that passwords are case sensitive[% IF Koha.Preference('FailedLoginAttempts') %] and that your account will be locked out after a fixed number of failed login attempts[% END %]. Please contact a library staff member if you continue to have problems.
|
||||
[% CASE 'no_expire' %]
|
||||
Please log-in to account to update your password.</br>
|
||||
<a href="/cgi-bin/koha/opac-user.pl" class="nav-link login-link loginModal-trigger"><i class="fa fa-user fa-icon-black fa-fw" aria-hidden="true"></i> <span class="userlabel">Log in to your account</span></a>
|
||||
[% CASE 'account_locked' %]
|
||||
This account has been locked!
|
||||
[% IF Categories.can_any_reset_password && Koha.Preference('OpacBaseURL') %]
|
||||
<a href="[% Koha.Preference('OpacBaseURL') | url %]/cgi-bin/koha/opac-password-recovery.pl">You must reset your password via e-mail</a>.
|
||||
[% ELSE %]
|
||||
You must contact the library for assistance
|
||||
[% END %]
|
||||
[% CASE %]
|
||||
An unknown error occurred. PLease try again or contact the library for assistance
|
||||
[% END %]
|
||||
</p>
|
||||
</div>
|
||||
[% END %]
|
||||
[% IF ( password_updated ) %]
|
||||
<div class="alert dialog-alert">
|
||||
<h2>Your password has successfully been updated</h2>
|
||||
<a href="/cgi-bin/koha/opac-main.pl" class="btn btn-primary">Go to OPAC</a>
|
||||
[% IF ( Koha.Preference('staffClientBaseURL') && staff_access ) %]
|
||||
<a href="[% Koha.Preference('staffClientBaseURL') | url %]" class="btn btn-primary">Go to Staff client</a>
|
||||
[% END %]
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<form action="/cgi-bin/koha/opac-reset-password.pl" name="mainform" id="mainform" method="post" autocomplete="off">
|
||||
<legend class="sr-only">Reset your password</legend>
|
||||
|
||||
<fieldset class="brief">
|
||||
<div class="form-group">
|
||||
<label for="userid">Login:</label>
|
||||
<input class="form-control" type="text" size="25" id="userid" name="userid" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="currentpassword">Current password:</label>
|
||||
<input class="form-control" autocomplete="off" type="password" size="25" id="currentpassword" name="currentpassword" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newpassword">New password:</label>
|
||||
<input class="form-control" autocomplete="off" type="password" size="25" id="newpassword" name="newpassword" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirmpassword">Confirm new password:</label>
|
||||
<input class="form-control" autocomplete="off" type="password" size="25" id="confirmpassword" name="confirmpassword" />
|
||||
</div>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Update password" class="btn btn-primary" />
|
||||
</fieldset>
|
||||
<input type="hidden" name="op" value="update" />
|
||||
</fieldset>
|
||||
[% IF Koha.Preference('OpacPasswordChange') && Categories.can_any_reset_password %]
|
||||
<div id="resetpassword">
|
||||
<a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a>
|
||||
</div>
|
||||
[% END %]
|
||||
</form>
|
||||
[% END # /IF Error_messages %]
|
||||
|
||||
[% ELSE %]
|
||||
<h1>Resetting your password has not been enabled by the library.</h1>
|
||||
[% IF Koha.Preference('OpacPasswordChange') && Categories.can_any_reset_password %]
|
||||
<div id="resetpassword">
|
||||
<a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a>
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<p>You must contact the library to reset your password</p>
|
||||
[% END %]
|
||||
[% END # / IF opacuserlogin %]
|
||||
|
||||
</div> <!-- /.opac-auth -->
|
||||
</div> <!-- /.col-md-10 col-lg-6 -->
|
||||
</div> <!-- /.row -->
|
||||
</div> <!-- /.container-fluid -->
|
||||
</div> <!-- /.main -->
|
||||
|
||||
[% INCLUDE 'opac-bottom.inc' %]
|
||||
[% BLOCK jsinclude %]
|
||||
[% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
|
||||
<script>
|
||||
jQuery.validator.addMethod("password_no_spaces", function(value, element){
|
||||
return ( this.optional(element) || !value.match(/^\s/) && !value.match(/\s$/) );
|
||||
}, _("Password contains leading and/or trailing spaces"));
|
||||
jQuery.validator.addMethod("password_match", function(value, element){
|
||||
var new_password_node = $("input[name='newpassword']:first");
|
||||
return value == $(new_password_node).val();
|
||||
}, _("Please enter the same password as above"));
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#mainform").validate({
|
||||
rules: {
|
||||
currentpassword: {
|
||||
required: true,
|
||||
},
|
||||
newpassword: {
|
||||
required: true,
|
||||
password_no_spaces: true
|
||||
},
|
||||
confirmpassword: {
|
||||
required: true,
|
||||
password_match: true
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
[% END %]
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# This file is part of Koha.
|
||||
#
|
||||
# Koha is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Koha is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use CGI qw ( -utf8 );
|
||||
|
||||
use C4::Auth qw( get_template_and_user checkpw checkpw_hash );
|
||||
use C4::Context;
|
||||
use C4::Output qw( output_html_with_http_headers );
|
||||
use Koha::Patrons;
|
||||
|
||||
use Try::Tiny qw( catch try );
|
||||
|
||||
my $query = CGI->new;
|
||||
|
||||
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
|
||||
{
|
||||
template_name => "opac-reset-password.tt",
|
||||
query => $query,
|
||||
type => "opac",
|
||||
authnotrequired => 1,
|
||||
}
|
||||
);
|
||||
|
||||
my $op = $query->param('op');
|
||||
|
||||
if ( $op eq 'update' ) {
|
||||
my $userid = $query->param('userid');
|
||||
my $currentpassword = $query->param('currentpassword');
|
||||
my $newpassword = $query->param('newpassword');
|
||||
my $confirmpassword = $query->param('confirmpassword');
|
||||
|
||||
my $patron = Koha::Patrons->find( { userid => $userid } );
|
||||
$patron = Koha::Patrons->find( { cardnumber => $userid } ) unless $patron;
|
||||
|
||||
if ( $patron && $patron->password_expiration_date ) {
|
||||
if ( $patron->account_locked ) {
|
||||
$template->param( error => 'account_locked' );
|
||||
}
|
||||
elsif ( $currentpassword && $newpassword && $confirmpassword ) {
|
||||
my $error;
|
||||
if ( C4::Auth::checkpw_hash( $currentpassword, $patron->password ) ) {
|
||||
|
||||
if ( $newpassword ne $confirmpassword ) {
|
||||
$template->param( 'error' => 'passwords_mismatch' );
|
||||
}
|
||||
elsif ( $currentpassword eq $newpassword ) {
|
||||
$template->param( 'error' => 'no_change' );
|
||||
}
|
||||
else {
|
||||
try {
|
||||
$patron->set_password( { password => $newpassword } );
|
||||
$template->param( 'password_updated' => '1' );
|
||||
$template->param( 'staff_access' => 1 )
|
||||
if $patron->has_permission( { catalogue => 1 } );
|
||||
}
|
||||
catch {
|
||||
$error = 'password_too_short'
|
||||
if $_->isa('Koha::Exceptions::Password::TooShort');
|
||||
$error = 'password_too_weak'
|
||||
if $_->isa('Koha::Exceptions::Password::TooWeak');
|
||||
$error = 'password_has_whitespaces'
|
||||
if $_->isa(
|
||||
'Koha::Exceptions::Password::WhitespaceCharacters');
|
||||
$template->param( 'error' => $error );
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
$template->param( 'error' => 'invalid_credentials' );
|
||||
$patron->update(
|
||||
{ login_attempts => $patron->login_attempts + 1 } )
|
||||
if !$patron->account_locked;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$template->param( 'incomplete_form' => '1' );
|
||||
}
|
||||
}
|
||||
elsif ( !$patron ) {
|
||||
template->param( 'error' => 'invalid_credentials' );
|
||||
}
|
||||
elsif ( !$patron->password_expiration_date ) {
|
||||
$template->param( 'error' => 'no_expire' );
|
||||
}
|
||||
else {
|
||||
$template->param( 'error' => 'unknown' );
|
||||
}
|
||||
}
|
||||
|
||||
output_html_with_http_headers $query, $cookie, $template->output, undef,
|
||||
{ force_no_caching => 1 };
|
||||
Loading…
Reference in New Issue