From f8df3a77bdf64e4fa3de810b8e88d1af8733df15 Mon Sep 17 00:00:00 2001 From: dbs Date: Sun, 4 Apr 2010 04:05:16 +0000 Subject: [PATCH] Bare-bones self-serve password reset interface. Notifications via action/trigger infrastructure - requires "hostname" parameter for the event definition. Provides localized minimal HTML reset request / reset forms. OU settings for maximum number of concurrent reset requests per user, request time to live, and maximum concurrent requests for the system (this last is currently unused but will be the basis of throttling) TODO: * Add a "Forgot your password?" link from OPAC login screen to https:///password/ * Implement request throttling * Disable access to the password forms via unencrypted HTTP * Add OU setting to choose "barcode + email" authentication type over "barcode or user name" * Add OU setting to support the option of preventing staff from using this interface to reset their passwords * Add Dojo spice to the sad but functional HTML forms git-svn-id: svn://svn.open-ils.org/ILS/branches/rel_1_6@16121 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/apache/eg.conf | 2 +- Open-ILS/examples/apache/eg_vhost.conf | 10 + Open-ILS/examples/apache/startup.pl | 1 + Open-ILS/examples/fm_IDL.xml | 12 ++ Open-ILS/examples/opensrf.xml.example | 1 + Open-ILS/src/extras/ils_events.xml | 6 + Open-ILS/src/perlmods/OpenILS/Application/Actor.pm | 182 ++++++++++++++++++ Open-ILS/src/perlmods/OpenILS/WWW/PasswordReset.pm | 205 +++++++++++++++++++++ Open-ILS/src/sql/Pg/005.schema.actors.sql | 31 ++++ Open-ILS/src/sql/Pg/950.data.seed-values.sql | 31 ++++ .../src/templates/password-reset/request-form.tt2 | 17 ++ .../src/templates/password-reset/reset-form.tt2 | 15 ++ .../src/templates/password-reset/strings.en-US | 13 ++ Open-ILS/web/opac/locale/en-US/lang.dtd | 6 + .../server/admin/org_unit_settings.xhtml | 15 ++ 15 files changed, 546 insertions(+), 1 deletion(-) create mode 100644 Open-ILS/src/perlmods/OpenILS/WWW/PasswordReset.pm create mode 100644 Open-ILS/src/templates/password-reset/request-form.tt2 create mode 100644 Open-ILS/src/templates/password-reset/reset-form.tt2 create mode 100644 Open-ILS/src/templates/password-reset/strings.en-US diff --git a/Open-ILS/examples/apache/eg.conf b/Open-ILS/examples/apache/eg.conf index a3890606ab..17eb787bc7 100644 --- a/Open-ILS/examples/apache/eg.conf +++ b/Open-ILS/examples/apache/eg.conf @@ -19,7 +19,7 @@ PerlRequire /etc/apache2/startup.pl PerlChildInitHandler OpenILS::WWW::Reporter::child_init PerlChildInitHandler OpenILS::WWW::SuperCat::child_init PerlChildInitHandler OpenILS::WWW::AddedContent::child_init - +PerlChildInitHandler OpenILS::WWW::PasswordReset::child_init # ---------------------------------------------------------------------------------- # Set some defaults for our working directories diff --git a/Open-ILS/examples/apache/eg_vhost.conf b/Open-ILS/examples/apache/eg_vhost.conf index 43885311d4..9f54f3c041 100644 --- a/Open-ILS/examples/apache/eg_vhost.conf +++ b/Open-ILS/examples/apache/eg_vhost.conf @@ -163,6 +163,16 @@ RewriteRule - - [E=locale:en-US] [L] allow from all +# ---------------------------------------------------------------------------------- +# Self-serve password interface +# ---------------------------------------------------------------------------------- + + SetHandler perl-script + PerlHandler OpenILS::WWW::PasswordReset::password_reset + Options +ExecCGI + PerlSendHeader On + allow from all + # ---------------------------------------------------------------------------------- # Supercat feeds diff --git a/Open-ILS/examples/apache/startup.pl b/Open-ILS/examples/apache/startup.pl index 189f36b4ff..378aaa3174 100644 --- a/Open-ILS/examples/apache/startup.pl +++ b/Open-ILS/examples/apache/startup.pl @@ -6,6 +6,7 @@ use OpenILS::WWW::AddedContent qw( /openils/conf/opensrf_core.xml ); use OpenILS::WWW::Proxy ('/openils/conf/opensrf_core.xml'); use OpenILS::WWW::Vandelay qw( /openils/conf/opensrf_core.xml ); use OpenILS::WWW::EGWeb ('/openils/conf/oils_web.xml'); +use OpenILS::WWW::PasswordReset ('/openils/conf/opensrf_core.xml'); # - Uncoment the following 2 lines to make use of the IP redirection code # - The IP file should to contain a map with the following format: diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index d8b2cab3b7..5ad05601e0 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -1056,6 +1056,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + + + + + + + + diff --git a/Open-ILS/examples/opensrf.xml.example b/Open-ILS/examples/opensrf.xml.example index afaad16149..56be38c1ec 100644 --- a/Open-ILS/examples/opensrf.xml.example +++ b/Open-ILS/examples/opensrf.xml.example @@ -19,6 +19,7 @@ vim:et:ts=4:sw=4: LOCALSTATEDIR/xsl LOCALSTATEDIR + LOCALSTATEDIR/templates diff --git a/Open-ILS/src/extras/ils_events.xml b/Open-ILS/src/extras/ils_events.xml index 098c6bcebf..f775c87245 100644 --- a/Open-ILS/src/extras/ils_events.xml +++ b/Open-ILS/src/extras/ils_events.xml @@ -590,6 +590,12 @@ The requested config_circ_matrix_ruleset_not_found was not found + + There are too many active password reset request sessions for this patron. + + + The user attempted to update their password using a stale or inactive password reset request session. + The requested acq.picklist was not found diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm index 0a363c09b1..c623879ded 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Actor.pm @@ -34,6 +34,8 @@ use OpenILS::Application::Actor::Friends; use OpenILS::Utils::CStoreEditor qw/:funcs/; use OpenILS::Utils::Penalty; +use UUID::Tiny qw/:std/; + sub initialize { OpenILS::Application::Actor::Container->initialize(); OpenILS::Application::Actor::UserGroups->initialize(); @@ -3343,6 +3345,186 @@ sub update_events { return {complete => 1}; } +__PACKAGE__->register_method( + method => "request_password_reset", + api_name => "open-ils.actor.patron.password_reset.request", + signature => { + params => [ + { desc => 'user_id_type', type => 'string' }, + { desc => 'user_id', type => 'string' }, + ] + }, +); +sub request_password_reset { + my($self, $conn, $user_id_type, $user_id) = @_; + + # Check to see if password reset requests are already being throttled: + # 0. Check cache to see if we're in throttle mode (avoid hitting database) + + my $e = new_editor(xact => 1); + my $user; + + # Get the user, if any, depending on the input value + if ($user_id_type eq 'username') { + $user = $e->search_actor_user({usrname => $user_id})->[0]; + if (!$user) { + $e->die_event; + return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ); + } + } elsif ($user_id_type eq 'barcode') { + my $card = $e->search_actor_card([ + {barcode => $user_id}, + {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0]; + if (!$card) { + $e->die_event; + return OpenILS::Event->new('ACTOR_USER_NOT_FOUND'); + } + $user = $card->usr; + } + + # If the user doesn't have an email address, we can't help them + if (!$user->email) { + $e->die_event; + return OpenILS::Event->new('PATRON_NO_EMAIL_ADDRESS'); + } + _reset_password_request($conn, $e, $user); +} + +# Once we have the user, we can issue the password reset request +# XXX Add a wrapper method that accepts barcode + email input +sub _reset_password_request { + my ($conn, $e, $user) = @_; + + # 1. Get throttle threshold and time-to-live from OU_settings + my $aupr_throttle = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_throttle') || 1000; + my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60; + + my $threshold_time = DateTime->now(time_zone => 'local')->subtract(seconds => $aupr_ttl)->iso8601(); + + # 2. Get time of last request and number of active requests (num_active) + # we use the weird test of usr = -1000 to generate a FALSE condition + my $active_requests = $e->json_query({ + from => 'aupr', + select => { + aupr => [ + { + column => 'uuid', + transform => 'COUNT' + } + ] + }, + where => { + has_been_reset => { '=' => { 'usr' => { '=' => -1000 } } }, + request_time => { '>' => $threshold_time } + } + }); + + # 3. if (num_active > throttle_threshold) and (now - last_request < 1 minute) + # ... delay - set cache - return event correspondingly ... + # + # Otherwise, go ahead and try to get the user. + + # Check the number of active requests for this user + $active_requests = $e->json_query({ + from => 'aupr', + select => { + aupr => [ + { + column => 'usr', + transform => 'COUNT' + } + ] + }, + where => { + usr => { '=' => $user->id }, + has_been_reset => { '=' => { 'usr' => { '=' => -1000 } } }, + request_time => { '>' => $threshold_time } + } + }); + + $logger->info("User " . $user->id . " has " . $active_requests->[0]->{'usr'} . " active password reset requests."); + + # if less than or equal to per-user threshold, proceed; otherwise, return event + my $aupr_per_user_limit = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_per_user_limit') || 3; + if ($active_requests->[0]->{'usr'} > $aupr_per_user_limit) { + $e->die_event; + return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS'); + } + + # Create the aupr object and insert into the database + my $reset_request = Fieldmapper::actor::usr_password_reset->new; + my $uuid = create_uuid_as_string(UUID_V4); + $reset_request->uuid($uuid); + $reset_request->usr($user->id); + + my $aupr = $e->create_actor_usr_password_reset($reset_request) or return $e->die_event; + $e->commit; + + # Create an event to notify user of the URL to reset their password + + # Can we stuff this in the user_data param for trigger autocreate? + my $hostname = $U->ou_ancestor_setting_value($user->home_ou, 'lib.hostname') || 'localhost'; + + my $ses = OpenSRF::AppSession->create('open-ils.trigger'); + $ses->request('open-ils.trigger.event.autocreate', 'password.reset_request', $aupr, $user->home_ou); + + # Trunk only + # $U->create_trigger_event('password.reset_request', $aupr, $user->home_ou); + + return 1; +} + +__PACKAGE__->register_method( + method => "commit_password_reset", + api_name => "open-ils.actor.patron.password_reset.commit", + signature => { + params => [ + { desc => 'uuid', type => 'string' }, + { desc => 'password', type => 'string' }, + ] + }, +); +sub commit_password_reset { + my($self, $conn, $uuid, $password) = @_; + + # Check to see if password reset requests are already being throttled: + # 0. Check cache to see if we're in throttle mode (avoid hitting database) + + my $e = new_editor(xact => 1); + + my $aupr = $e->search_actor_usr_password_reset({ + uuid => $uuid, + has_been_reset => 0 + + }); + + if (!$aupr->[0]) { + $e->die_event; + return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST'); + } + my $user_id = $aupr->[0]->usr; + my $user = $e->retrieve_actor_user($user_id); + + # Ensure we're still within the TTL for the request + my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60; + my $threshold_time = DateTime->now(time_zone => 'local')->subtract(seconds => $aupr_ttl); + my $request_time = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($aupr->[0]->request_time)); + if ($request_time < $threshold_time) { + $e->die_event; + return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST'); + } + + # All is well; update the password + $user->passwd($password); + $e->update_actor_user($user); + + # And flag that this password reset request has been honoured + $aupr->[0]->has_been_reset('t'); + $e->update_actor_usr_password_reset($aupr->[0]); + $e->commit; + + return 1; +} 1; diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/PasswordReset.pm b/Open-ILS/src/perlmods/OpenILS/WWW/PasswordReset.pm new file mode 100644 index 0000000000..c8b390fe82 --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/WWW/PasswordReset.pm @@ -0,0 +1,205 @@ +package OpenILS::WWW::PasswordReset; + +# Copyright (C) 2010 Laurentian University +# Dan Scott +# +# This program 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 2 +# of the License, or (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +use strict; use warnings; + +use Apache2::Log; +use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log); +use APR::Const -compile => qw(:error SUCCESS); +use Apache2::RequestRec (); +use Apache2::RequestIO (); +use Apache2::RequestUtil; +use CGI; +use Template; + +use OpenSRF::EX qw(:try); +use OpenSRF::Utils qw/:datetime/; +use OpenSRF::Utils::Cache; +use OpenSRF::System; +use OpenSRF::AppSession; + +use OpenILS::Utils::Fieldmapper; +use OpenSRF::Utils::Logger qw/$logger/; +use OpenILS::Application::AppUtils; +use OpenILS::Utils::CStoreEditor qw/:funcs/; + +my $log = 'OpenSRF::Utils::Logger'; +my $U = 'OpenILS::Application::AppUtils'; + +my ($bootstrap, $actor, $templates); +my $i18n = {}; + +sub child_init { + OpenSRF::System->bootstrap_client( config_file => $bootstrap ); + + my $conf = OpenSRF::Utils::SettingsClient->new(); + my $idl = $conf->config_value("IDL"); + Fieldmapper->import(IDL => $idl); + $templates = $conf->config_value("dirs", "templates"); + $actor = OpenSRF::AppSession->create('open-ils.actor'); + load_i18n(); +} + +sub password_reset { + my $apache = shift; + return Apache2::Const::DECLINED if (-e $apache->filename); + + $apache->content_type('text/html'); + + my $cgi = new CGI; + my $ctx = {}; + + $ctx->{'uri'} = $apache->uri; + + # Get our locale from the URL + (my $locale = $apache->path_info) =~ s{^.*?/([a-z]{2}-[A-Z]{2})/.*?$}{$1}; + if (!$locale) { + $locale = 'en-US'; + } + + # If locale exists, use it; otherwise fall back to en-US + if (exists $i18n->{$locale}) { + $ctx->{'i18n'} = $i18n->{$locale}; + } else { + $ctx->{'i18n'} = $i18n->{'en-US'}; + } + + my $tt = Template->new({ + INCLUDE_PATH => $templates + }) || die "$Template::ERROR\n"; + + # Get our UUID: if no UUID, then display barcode / username / email prompt + (my $uuid = $apache->path_info) =~ s{^/$locale/([^/]*?)$}{$1}; + $logger->info("Password reset: UUID = $uuid"); + + if (!$uuid) { + request_password_reset($apache, $cgi, $tt, $ctx); + } else { + reset_password($apache, $cgi, $tt, $ctx, $uuid); + } +} + +sub reset_password { + my ($apache, $cgi, $tt, $ctx, $uuid) = @_; + + my $password_1 = $cgi->param('pwd1'); + my $password_2 = $cgi->param('pwd2'); + + $ctx->{'title'} = $ctx->{'i18n'}{'TITLE'}; + $ctx->{'password_prompt'} = $ctx->{'i18n'}{'PASSWORD_PROMPT'}; + $ctx->{'password_prompt2'} = $ctx->{'i18n'}{'PASSWORD_PROMPT2'}; + + # In case non-matching passwords slip through our funky Web interface + if ($password_1 and $password_2 and ($password_1 ne $password_2)) { + $apache->status(Apache2::Const::DECLINED); + $ctx->{'status'} = { + style => 'error', + msg => $ctx->{'i18n'}{'NO_MATCH'} + }; + $tt->process('password-reset/reset-form.tt2', $ctx) + || die $tt->error(); + return Apache2::Const::OK; + } + + if ($password_1 and $password_2 and ($password_1 eq $password_2)) { + my $response = $actor->request('open-ils.actor.patron.password_reset.commit', $uuid, $password_1)->gather(); + if (ref($response) && + $response->{'textcode'} && + $response->{'textcode'} eq 'PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST') { + $apache->status(Apache2::Const::DECLINED); + $ctx->{'status'} = { + style => 'error', + msg => $ctx->{'i18n'}{'NOT_ACTIVE'} + + }; + $tt->process('password-reset/reset-form.tt2', $ctx) + || die $tt->error(); + return Apache2::Const::OK; + } + $ctx->{'status'} = { + style => 'success', + msg => $ctx->{'i18n'}{'SUCCESS'} + }; + } + + # Either the password change was successful, or this is their first time through + $tt->process('password-reset/reset-form.tt2', $ctx) + || die $tt->error(); + + return Apache2::Const::OK; +} + +# Load our localized strings - lame, need to convert to Locale::Maketext +sub load_i18n { + foreach my $string_bundle (glob("$templates/password-reset/strings.*")) { + open(I18NFH, '<', $string_bundle); + (my $locale = $string_bundle) =~ s/^.*\.([a-z]{2}-[A-Z]{2})$/$1/; + $logger->debug("Loaded locale [$locale] from file: [$string_bundle]"); + while() { + my ($string_id, $string) = ($_ =~ m/^(.+?)=(.*?)$/); + $i18n->{$locale}{$string_id} = $string; + } + close(I18NFH); + } +} + +sub request_password_reset { + my ($apache, $cgi, $tt, $ctx) = @_; + + my $barcode = $cgi->param('barcode'); + my $username = $cgi->param('username'); + my $email = $cgi->param('email'); + + if (!($barcode or $username or $email)) { + $apache->status(Apache2::Const::OK); + $ctx->{'status'} = { + style => 'plain', + msg => $ctx->{'i18n'}{'IDENTIFY_YOURSELF'} + }; + $tt->process('password-reset/request-form.tt2', $ctx) + || die $tt->error(); + return Apache2::Const::OK; + } elsif ($barcode) { + my $response = $actor->request('open-ils.actor.patron.password_reset.request', 'barcode', $barcode)->gather(); + $apache->status(Apache2::Const::OK); + $ctx->{'status'} = { + style => 'plain', + msg => $ctx->{'i18n'}{'REQUEST_SUCCESS'} + }; + # Hide form + $tt->process('password-reset/request-form.tt2', $ctx) + || die $tt->error(); + return Apache2::Const::OK; + } elsif ($username) { + my $response = $actor->request('open-ils.actor.patron.password_reset.request', 'username', $username)->gather(); + $apache->status(Apache2::Const::OK); + $ctx->{'status'} = { + style => 'plain', + msg => $ctx->{'i18n'}{'REQUEST_SUCCESS'} + }; + # Hide form + $tt->process('password-reset/request-form.tt2', $ctx) + || die $tt->error(); + return Apache2::Const::OK; + } +} + +1; + +# vim: et:ts=4:sw=4 diff --git a/Open-ILS/src/sql/Pg/005.schema.actors.sql b/Open-ILS/src/sql/Pg/005.schema.actors.sql index 9880efcddc..11063ab7ce 100644 --- a/Open-ILS/src/sql/Pg/005.schema.actors.sql +++ b/Open-ILS/src/sql/Pg/005.schema.actors.sql @@ -523,5 +523,36 @@ $$; CREATE INDEX actor_usr_standing_penalty_usr_idx ON actor.usr_standing_penalty (usr); +CREATE TABLE actor.usr_password_reset ( + id SERIAL PRIMARY KEY, + uuid TEXT NOT NULL, + usr BIGINT NOT NULL REFERENCES actor.usr(id) DEFERRABLE INITIALLY DEFERRED, + request_time TIMESTAMP NOT NULL DEFAULT NOW(), + has_been_reset BOOL NOT NULL DEFAULT false +); +COMMENT ON TABLE actor.usr_password_reset IS $$ +/* + * Copyright (C) 2010 Laurentian University + * Dan Scott + * + * Self-serve password reset requests + * + * **** + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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. + */ +$$; +CREATE UNIQUE INDEX actor_usr_password_reset_uuid_idx ON actor.usr_password_reset (uuid); +CREATE INDEX actor_usr_password_reset_usr_idx ON actor.usr_password_reset (usr); +CREATE INDEX actor_usr_password_reset_request_time_idx ON actor.usr_password_reset (request_time); +CREATE INDEX actor_usr_password_reset_has_been_reset_idx ON actor.usr_password_reset (has_been_reset); COMMIT; diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index f4473a5edc..fefa703f8d 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -1973,6 +1973,37 @@ INSERT INTO action_trigger.environment (event_def, path) VALUES (5, 'usr'), (5, 'pickup_lib.billing_address'); +INSERT INTO action_trigger.hook (key,core_type,description) VALUES ('password.reset_request','aupr','Patron has requested a self-serve password reset'); +INSERT INTO action_trigger.event_definition (id, active, owner, name, hook, validator, reactor, delay, template) + VALUES (15, 'f', 1, 'Password reset request notification', 'password.reset_request', 'NOOP_True', 'SendEmail', '00:00:01', +$$ +[%- USE date -%] +[%- user = target.usr -%] +To: [%- params.recipient_email || user.email %] +From: [%- params.sender_email || user.home_ou.email || default_sender %] +Subject: [% user.home_ou.name %]: library account password reset request + +You have received this message because you, or somebody else, requested a reset +of your library system password. If you did not request a reset of your library +system password, just ignore this message and your current password will +continue to work. + +If you did request a reset of your library system password, please perform +the following steps to continue the process of resetting your password: + +1. Open the following link in a web browser: https://[% params.hostname %]/password-reset/en-US/[% target.uuid %] +The browser displays a password reset form. + +2. Enter your new password in the password reset form in the browser. You must +enter the password twice to ensure that you do not make a mistake. If the +passwords match, you will then be able to log in to your library system account +with the new password. + +$$); +INSERT INTO action_trigger.environment ( event_def, path) VALUES + ( 15, 'usr' ); +INSERT INTO action_trigger.environment ( event_def, path) VALUES + ( 15, 'usr.home_ou' ); SELECT SETVAL('action_trigger.event_definition_id_seq'::TEXT, 100); diff --git a/Open-ILS/src/templates/password-reset/request-form.tt2 b/Open-ILS/src/templates/password-reset/request-form.tt2 new file mode 100644 index 0000000000..0c09e36c4e --- /dev/null +++ b/Open-ILS/src/templates/password-reset/request-form.tt2 @@ -0,0 +1,17 @@ + + + [% i18n.REQUEST_TITLE %] + + +

[% i18n.REQUEST_TITLE %]

+

[% status.msg %]

+
+
+
+
+ + +
+
+ + diff --git a/Open-ILS/src/templates/password-reset/reset-form.tt2 b/Open-ILS/src/templates/password-reset/reset-form.tt2 new file mode 100644 index 0000000000..20eed14290 --- /dev/null +++ b/Open-ILS/src/templates/password-reset/reset-form.tt2 @@ -0,0 +1,15 @@ + + + [% title %] + + +

[% title %]

+

[% status.msg %]

+
+
+
+
+
+
+ + diff --git a/Open-ILS/src/templates/password-reset/strings.en-US b/Open-ILS/src/templates/password-reset/strings.en-US new file mode 100644 index 0000000000..1db08118ab --- /dev/null +++ b/Open-ILS/src/templates/password-reset/strings.en-US @@ -0,0 +1,13 @@ +REQUEST_TITLE=Library system password reset request form +IDENTIFY_YOURSELF=Please enter your user name or barcode to identify your library account and request a password reset. +REQUEST_SUCCESS=Your user name or barcode has been submitted for a password reset. If a matching account with an email address is found, you will soon receive an email at that address with further instructions for resetting your password. +BARCODE_PROMPT=Barcode: +USERNAME_PROMPT=User name: +EMAIL_PROMPT=Email address associated with the account: +NO_SESSION=Could not find the requested password reset session. +NO_MATCH=Passwords did not match. Please try again +NOT_ACTIVE=This was not an active password reset request. Your password has not been reset. +SUCCESS=Password has been reset. +TITLE=Library system password reset +PASSWORD_PROMPT=New password: +PASSWORD_PROMPT2=Re-enter new password: diff --git a/Open-ILS/web/opac/locale/en-US/lang.dtd b/Open-ILS/web/opac/locale/en-US/lang.dtd index 768be312f5..8919c7e6c6 100644 --- a/Open-ILS/web/opac/locale/en-US/lang.dtd +++ b/Open-ILS/web/opac/locale/en-US/lang.dtd @@ -1653,6 +1653,12 @@ + + + + + + diff --git a/Open-ILS/xul/staff_client/server/admin/org_unit_settings.xhtml b/Open-ILS/xul/staff_client/server/admin/org_unit_settings.xhtml index 9a4df7b04b..c5bbe0e2eb 100644 --- a/Open-ILS/xul/staff_client/server/admin/org_unit_settings.xhtml +++ b/Open-ILS/xul/staff_client/server/admin/org_unit_settings.xhtml @@ -142,6 +142,21 @@ desc : '&staff.server.admin.org_settings.patron.password.use_phone.desc;', type : 'bool' }, + 'circ.password_reset_request_time_to_live': { + label: '&staff.server.admin.org_settings.circ.password_reset_request_time_to_live;', + desc: '&staff.server.admin.org_settings.circ.password_reset_request_time_to_live.desc;', + type : 'integer' + }, + 'circ.password_reset_request_per_user_limit': { + label: '&staff.server.admin.org_settings.circ.password_reset_request_per_user_limit;', + desc: '&staff.server.admin.org_settings.circ.password_reset_request_per_user_limit.desc;', + type : 'integer' + }, + 'circ.password_reset_request_throttle': { + label: '&staff.server.admin.org_settings.circ.password_reset_request_throttle;', + desc: '&staff.server.admin.org_settings.circ.password_reset_request_throttle.desc;', + type : 'integer' + }, 'ui.circ.patron_summary.horizontal' : { label : '&ui.circ.patron_summary.horizontal;', desc : '&ui.circ.patron_summary.horizontal.desc;', -- 2.11.0