Browse Source
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>22.05.x
6 changed files with 294 additions and 3 deletions
@ -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"; |
|||
}, |
|||
}; |
@ -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