@@ -, +, @@ 1 - Apply patch, updatedatabase 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 - Confirm their password_expiration_date field is set SELECT password_expiration_date FROM borrowers WHERE borrowernumber=51; 5 - Create a new patron, do not set a password 6 - Confirm their password_expiration_date field is NULL 7 - Update the patron with an expiration to be expired UPDATE borrowers SET password_expiration_date='2022-01-01' WHERE borrowernumber=51; 8 - Give the borrower catalogue permission 9 - Attempt to log in to Straff interface reset password reset Note: you will have to find the link in the message_queue unless you have emails setup on your test environment SELECT * FROM message_queue WHERE borrowernumber=51; is set 10 days in the future --- C4/Auth.pm | 12 ++++++++--- Koha/Patron.pm | 20 +++++++++++++++++++ Koha/Patron/Category.pm | 20 +++++++++++++++++++ Koha/REST/V1/Auth.pm | 7 ++++++- admin/categories.pl | 3 +++ .../prog/en/modules/admin/categories.tt | 11 ++++++++++ .../intranet-tmpl/prog/en/modules/auth.tt | 7 +++++++ .../bootstrap/en/modules/opac-auth.tt | 13 ++++++++++-- 8 files changed, 87 insertions(+), 6 deletions(-) --- a/C4/Auth.pm +++ a/C4/Auth.pm @@ -932,6 +932,8 @@ sub checkauth { $info{oldip} = $more_info->{old_ip}; $info{newip} = $more_info->{new_ip}; $info{different_ip} = 1; + } elsif ( $return eq 'password_expired' ) { + $info{password_has_expired} = 1; } } } @@ -1316,7 +1318,8 @@ sub checkauth { PatronSelfRegistration => C4::Context->preference("PatronSelfRegistration"), PatronSelfRegistrationDefaultCategory => C4::Context->preference("PatronSelfRegistrationDefaultCategory"), opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'}, - too_many_login_attempts => ( $patron and $patron->account_locked ) + too_many_login_attempts => ( $patron and $patron->account_locked ), + password_has_expired => ( $patron and $patron->password_expired ) ); $template->param( SCO_login => 1 ) if ( $query->param('sco_user_login') ); @@ -1693,6 +1696,9 @@ sub check_cookie_auth { } elsif ( $userid ) { $session->param( 'lasttime', time() ); + my $patron = Koha::Patrons->find({ userid => $userid }); + $patron = Koha::Patron->find({ cardnumber => $userid }) unless $patron; + return ("password_expired", undef ) if $patron->password_expired; my $flags = defined($flagsrequired) ? haspermission( $userid, $flagsrequired ) : 1; if ($flags) { C4::Context->_new_userenv($sessionID); @@ -1801,8 +1807,8 @@ sub checkpw { # 0 if auth is nok # -1 if user bind failed (LDAP only) - if ( $patron and $patron->account_locked ) { - # Nothing to check, account is locked + if ( $patron and ( $patron->account_locked || $patron->password_expired ) ) { + # Nothing to check, account is locked or password expired } elsif ($ldap && defined($password)) { my ( $retval, $retcard, $retuserid ) = checkpw_ldap(@_); # EXTERNAL AUTH if ( $retval == 1 ) { --- a/Koha/Patron.pm +++ a/Koha/Patron.pm @@ -264,6 +264,9 @@ sub store { # Make a copy of the plain text password for later use $self->plain_text_password( $self->password ); + $self->password_expiration_date( $self->password + ? $self->category->get_password_expiry_date + : undef ); # Create a disabled account if no password provided $self->password( $self->password ? Koha::AuthUtils::hash_password( $self->password ) @@ -777,6 +780,21 @@ sub is_expired { return 0; } +=head3 password_expired + +my $password_expired = $patron->password_expired; + +Returns 1 if the patron's password is expired or 0; + +=cut + +sub password_expired { + my ($self) = @_; + return 0 unless $self->password_expiration_date; + return 1 if dt_from_string( $self->password_expiration_date ) < dt_from_string->truncate( to => 'day' ); + return 0; +} + =head3 is_going_to_expire my $is_going_to_expire = $patron->is_going_to_expire; @@ -876,6 +894,8 @@ sub set_password { my $digest = Koha::AuthUtils::hash_password($password); + $self->password_expiration_date( $self->category->get_password_expiry_date ); + # We do not want to call $self->store and retrieve password from DB $self->password($digest); $self->login_attempts(0); --- a/Koha/Patron/Category.pm +++ a/Koha/Patron/Category.pm @@ -113,6 +113,26 @@ sub get_expiry_date { } } +=head3 get_password_expiry_date + +Returns date based on password expiry days set for the category. If the value is not set +we return undef, password does not expire + +my $expiry_date = $category->get_password_expiry_date(); + +=cut + +sub get_password_expiry_date { + my ($self, $date ) = @_; + if ( $self->password_expiry_days ) { + $date ||= dt_from_string; + $date = dt_from_string( $date ) unless ref $date; + return $date->add( days => $self->password_expiry_days )->ymd; + } else { + return; + } +} + =head3 effective_reset_password Returns if patrons in this category can reset their password. If set in $self->reset_password --- a/Koha/REST/V1/Auth.pm +++ a/Koha/REST/V1/Auth.pm @@ -491,7 +491,12 @@ sub _basic_auth { Koha::Exceptions::Authorization::Unauthorized->throw( error => 'Invalid password' ); } - return Koha::Patrons->find({ userid => $user_id }); + my $patron = Koha::Patrons->find({ userid => $user_id }); + if ( $patron->password_expired ) { + Koha::Exceptions::Authorization::Unauthorized->throw( error => 'Password has expired' ); + } + + return $patron; } =head3 _set_userenv --- a/admin/categories.pl +++ a/admin/categories.pl @@ -63,6 +63,7 @@ elsif ( $op eq 'add_validate' ) { my $description = $input->param('description'); my $enrolmentperiod = $input->param('enrolmentperiod'); my $enrolmentperioddate = $input->param('enrolmentperioddate') || undef; + my $password_expiry_days = $input->param('password_expiry_days') || undef; my $upperagelimit = $input->param('upperagelimit'); my $dateofbirthrequired = $input->param('dateofbirthrequired'); my $enrolmentfee = $input->param('enrolmentfee'); @@ -103,6 +104,7 @@ elsif ( $op eq 'add_validate' ) { $category->description($description); $category->enrolmentperiod($enrolmentperiod); $category->enrolmentperioddate($enrolmentperioddate); + $category->password_expiry_days($password_expiry_days); $category->upperagelimit($upperagelimit); $category->dateofbirthrequired($dateofbirthrequired); $category->enrolmentfee($enrolmentfee); @@ -134,6 +136,7 @@ elsif ( $op eq 'add_validate' ) { description => $description, enrolmentperiod => $enrolmentperiod, enrolmentperioddate => $enrolmentperioddate, + password_expiry_days => $password_expiry_days, upperagelimit => $upperagelimit, dateofbirthrequired => $dateofbirthrequired, enrolmentfee => $enrolmentfee, --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categories.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categories.tt @@ -162,6 +162,10 @@ +
  • + + days +
  • years @@ -449,6 +453,7 @@ [% END %] + Password expiration: [% category.password_expiry_days | html %] days Age required: [% category.dateofbirthrequired | html %] years Upperage limit: [% category.upperagelimit | html %] years Enrollment fee: [% category.enrolmentfee | $Price %] @@ -517,6 +522,7 @@ Category name Type Enrollment period + Password expiration Age required Upper age limit Enrollment fee @@ -559,6 +565,11 @@ until [% category.enrolmentperioddate | $KohaDates %] [% END %] + [% IF (category.password_expiry_days) %] + [% category.password_expiry_days | html %] days + [% ELSE %] + - + [% END %] [% IF (category.dateofbirthrequired) %] [% category.dateofbirthrequired | html %] years [% ELSE %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt @@ -49,6 +49,13 @@ [% IF Categories.can_any_reset_password && Koha.Preference('OpacBaseURL') %] You must reset your password. [% END %] +[% ELSIF password_has_expired %] +
    Error: Your password has expired!
    + [% IF Categories.can_any_reset_password && Koha.Preference('OpacBaseURL') %] + You must reset your password. + [% ELSE %] +

    You must contact the library to have your password reset

    + [% END %] [% ELSIF invalid_username_or_password %]
    Error: Invalid username or password
    [% END %] --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt @@ -167,7 +167,16 @@ [% END # /IF GoogleOpenIDConnect %] [% END # /UNLESS OPACShibOnly %] - [% IF !Koha.Preference('OPACShibOnly') or SCO_login or SCI_login %] + [% IF password_has_expired %] +

    Error: Your password has expired!

    + [% IF Koha.Preference('OpacPasswordChange') && Categories.can_any_reset_password %] +
    + Reset your password? +
    + [% ELSE %] +

    You must contact the library to reset your password

    + [% END %] + [% ELSIF !Koha.Preference('OPACShibOnly') or SCO_login or SCI_login %] [% IF SCO_login %]
    [% ELSIF SCI_login %] @@ -217,7 +226,7 @@ [% END %]
    - [% END # / IF !OPACShibOnly or SCO_login or SCI_login %] + [% END # / IF password_has_expired / ELSIF !OPACShibOnly or SCO_login or SCI_login %] [% END # / IF loginprompt %] [% ELSE %] --