@@ -, +, @@ 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 field is set SELECT password_expiration FROM borrowers WHERE borrowernumber=51; 5 - Create a new patron, do not set a password 6 - Confirm their password_expiration field is NULL 7 - Update the patron with an expiration to be expired UPDATE borrowers SET password_expiration='2022-01-01' WHERE borrowernumber=51; 8 - Give the borrower catalogue permission 9 - Attempt to log in to Straff interface 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; --- C4/Auth.pm | 31 +++++++++++++++++++ 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, 109 insertions(+), 3 deletions(-) --- a/C4/Auth.pm +++ a/C4/Auth.pm @@ -278,6 +278,37 @@ sub get_template_and_user { # FIXME What to do if $patron does not exist? } + if ($patron->password_expired) { + my $reset_template_name = ( $in->{type} eq 'opac' ) ? 'opac-auth.tt' : 'auth.tt'; + $template = C4::Templates::gettemplate( $reset_template_name, $in->{type}, + $in->{query} ); + $cookie = $in->{query}->cookie( + -name => 'CGISESSID', + -value => '', + -expires => '', + -HttpOnly => 1, + -secure => ( C4::Context->https_enabled() ? 1 : 0 ), + ); + + $template->param( + loginprompt => 1, + login => 1, + password_has_expired => 1, + script_name => get_script_name(), + ); + + print $in->{query}->header( + { + type => 'text/html', + charset => 'utf-8', + cookie => $cookie, + 'X-Frame-Options' => 'SAMEORIGIN' + } + ), + $template->output; + safe_exit; + } + # user info $template->param( loggedinusername => $user ); # OBSOLETE - Do not reuse this in template, use logged_in_user.userid instead $template->param( loggedinusernumber => $borrowernumber ); # FIXME Should be replaced with logged_in_user.borrowernumber --- 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 @@ -490,7 +490,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 @@ -51,6 +51,13 @@ [% END %] [% ELSIF invalid_username_or_password %]
    Error: Invalid username or password
    +[% 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 %] [% END %] [% IF (shibbolethAuthentication) %] --- 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 %] --