SQL for database insertions. Added ability to check password age against variable...
authorLlewellyn Marshall <llewellyn.marshall@ncdcr.gov>
Mon, 13 Jun 2022 20:59:54 +0000 (16:59 -0400)
committerLlewellyn Marshall <llewellyn.marshall@ncdcr.gov>
Fri, 17 Jun 2022 18:16:16 +0000 (14:16 -0400)
wrap all english text in i18n functions, fix error in seed vals & upgrade sql

change "action" to "actor" in field mapped class

remove log, show message 7 days earlier on staff splash

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm
Open-ILS/src/perlmods/live_t/33-password-age.t [new file with mode: 0644]
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/xxxx.data.password_age_reset.sql [new file with mode: 0644]
Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2
Open-ILS/src/templates/staff/circ/patron/t_edit.tt2
Open-ILS/src/templates/staff/t_splash.tt2
Open-ILS/web/js/ui/default/staff/circ/patron/regctl.js

index 013ee90..91c3915 100644 (file)
@@ -2409,7 +2409,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        </actions>
                </permacrud>
        </class>
-       <class id="aupsd" controller="open-ils.cstore" oils_obj:fieldmapper="action::usr_password_set_date" reporter:label="User Password Set Date" oils_persist:readonly="true">
+       <class id="aupsd" controller="open-ils.cstore" oils_obj:fieldmapper="actor::usr_password_set_date" reporter:label="User Password Set Date" oils_persist:readonly="true">
                <oils_persist:source_definition><![CDATA[
                        SELECT usr,create_date,edit_date FROM actor.passwd
                ]]></oils_persist:source_definition>
index 46dda80..432cacd 100644 (file)
@@ -4997,11 +4997,20 @@ sub get_barcodes {
 
 __PACKAGE__->register_method(
     method   => "get_password_last_edit_age",
-    api_name => "open-ils.actor.get_password_age"
+    api_name => "open-ils.actor.get_password_age",
+    signature => {
+        desc => "Finds the number of days since a user's password was last updated.",
+        params => [
+            {desc => 'Authentication token',  type => 'string'},
+            {desc => 'Patron ID',             type => 'number'},
+            {desc => 'Reference Time',        type => 'string'},
+        ],
+        return => {desc => 'Number of days since password update'}
+    }
 );
 
 sub get_password_last_edit_age {
-    my( $self, $client, $auth, $patron_id ) = @_;
+    my( $self, $client, $auth, $patron_id, $ref_time ) = @_;
     my $e = new_editor(authtoken => $auth);
     return $e->event unless $e->checkauth;
     my $patron = $e->retrieve_actor_user($patron_id);
@@ -5021,13 +5030,12 @@ sub get_password_last_edit_age {
     });
 
     if(defined $aupsds){
-        my $pwd = $aupsds->[0];
-        #convert the dates with the DateTime module
+        my $pwd = $aupsds->[0];        
         if($pwd){
+            #convert the dates with the DateTime module
             my $edit_datetime = DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($pwd->{'edit_date'}));
-            #get time in days since last password update
-            #my $now = DateTime->today()->iso8601();
-            my $now = DateTime->now();
+            #get the time we are subtracting from, use ref_time if it's defined or the current datetime otherwise
+            my $now = defined($ref_time) ?  DateTime::Format::ISO8601->parse_datetime(clean_ISO8601($ref_time)) : DateTime->now();
             
             my $duration = $now->subtract_datetime_absolute($edit_datetime)->delta_seconds / (24*60*60);
             return int($duration);            
diff --git a/Open-ILS/src/perlmods/live_t/33-password-age.t b/Open-ILS/src/perlmods/live_t/33-password-age.t
new file mode 100644 (file)
index 0000000..f2d2007
--- /dev/null
@@ -0,0 +1,87 @@
+#!perl
+use constant FUTURE_DAYS => 150;
+use strict; use warnings;
+use Test::More tests => 4;
+use OpenILS::Utils::TestUtils;
+use OpenILS::Const qw(:const);
+use OpenILS::Utils::CStoreEditor qw/:funcs/;
+use OpenILS::Utils::Fieldmapper;
+use DateTime;
+use DateTime::Format::ISO8601;
+
+diag("test password age");
+
+my $U = 'OpenILS::Application::AppUtils';
+
+my $script = OpenILS::Utils::TestUtils->new();
+$script->bootstrap;
+
+$script->authenticate({
+    username => 'admin',
+    password => 'demo123',
+    type => 'staff'
+});
+
+my $authtoken = $script->authtoken;
+ok($authtoken, 'was able to authenticate');
+
+my $new_user = Fieldmapper::actor::user->new();
+my $new_card = Fieldmapper::actor::card->new();
+
+$new_card->barcode("lew_$$");
+$new_card->id(-1); # virtual ID
+$new_card->usr(undef);
+$new_card->isnew(1);
+
+$new_user->cards([ $new_card ]);
+$new_user->card($new_card);
+$new_user->usrname("lew_$$");
+$new_user->passwd('lew_$$');
+$new_user->family_name('Marshall');
+$new_user->first_given_name('Llewellyn');
+$new_user->profile(2);
+$new_user->home_ou(4);
+$new_user->ident_type(1);
+$new_user->isnew(1);
+
+my $resp = $U->simplereq(
+    'open-ils.actor',
+    'open-ils.actor.patron.update',
+    $authtoken,
+    $new_user
+);
+
+isa_ok($resp, 'Fieldmapper::actor::user', 'new patron');
+
+my $new_id = $resp->id();
+
+$resp = $U->simplereq(
+    'open-ils.actor',
+    'open-ils.actor.get_password_age',
+    $authtoken,
+    $new_id
+);
+
+cmp_ok($resp, '==', 0, 'Password age on new user is 0 days');
+
+my $dt = DateTime->now();
+$dt->add( days => FUTURE_DAYS );
+
+$resp = $U->simplereq(
+    'open-ils.actor',
+    'open-ils.actor.get_password_age',
+    $authtoken,
+    $new_id,
+    $dt->iso8601()
+);
+
+cmp_ok($resp, '==', FUTURE_DAYS, FUTURE_DAYS." days from now, Password age on new user is ".FUTURE_DAYS." days");
+
+# clean up
+$U->simplereq(
+    'open-ils.actor',
+    'open-ils.actor.user.delete',
+    $authtoken,
+    $new_id
+);
\ No newline at end of file
index ad3c3e5..2993544 100644 (file)
@@ -21305,6 +21305,40 @@ INSERT INTO permission.grp_perm_map (grp, perm, depth, grantable)
         aout.name = 'Consortium' AND
         (perm.code = 'ADMIN_GEOLOCATION_SERVICES' OR perm.code = 'VIEW_GEOLOCATION_SERVICES');
 
+-- Password age reset
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'global.password_reset_age',
+        'glob',
+        oils_i18n_gettext(
+            'global.password_reset_age',
+            'Password Reset Age',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'global.password_reset_age',
+            'The number of days after a password has been changed before ' || 
+                       'users will be alerted that they should update it.',
+            'coust',
+            'description'
+        ),
+        'integer'
+    );
+
+INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
+    'au.passwd_changed',
+    'au',
+    'A user\'s password was updated',
+       false
+);
+
+INSERT INTO action_trigger.validator (module, description) VALUES (
+    'PatronOldPassword', 'Confirm that the patron has not updated their password since this event was created.'
+);
+
 ------------------- Disabled example A/T defintions ------------------------------
 
 -- Create a "dummy" slot when applicable, and trigger the "offer curbside" events
diff --git a/Open-ILS/src/sql/Pg/upgrade/xxxx.data.password_age_reset.sql b/Open-ILS/src/sql/Pg/upgrade/xxxx.data.password_age_reset.sql
new file mode 100644 (file)
index 0000000..63a3c87
--- /dev/null
@@ -0,0 +1,40 @@
+BEGIN;
+
+--SELECT evergreen.upgrade_deps_block_check('xxxx', :eg_version);
+
+-- password age display setting
+
+INSERT INTO config.org_unit_setting_type
+    (name, grp, label, description, datatype)
+    VALUES (
+        'global.password_reset_age',
+        'glob',
+        oils_i18n_gettext(
+            'global.password_reset_age',
+            'Password Reset Age',
+            'coust',
+            'label'
+        ),
+        oils_i18n_gettext(
+            'global.password_reset_age',
+            'The number of days after a password has been changed before ' || 
+                       'users will be alerted that they should update it.',
+            'coust',
+            'description'
+        ),
+        'integer'
+    );
+
+INSERT INTO action_trigger.hook (key,core_type,description,passive) VALUES (
+    'au.passwd_changed',
+    'au',
+    'A user\'s password was updated',
+       false
+);
+
+INSERT INTO action_trigger.validator (module, description) VALUES (
+    'PatronOldPassword', 'Confirm that the patron has not updated their password since this event was created.'
+);
+
+--ROLLBACK;
+COMMIT;
\ No newline at end of file
index 1454b4d..2a11559 100755 (executable)
@@ -19,9 +19,9 @@
                     [% need_password_change = ctx.password_age == -1 || ctx.password_age >= ctx.password_age_reminder %]
                     <div class="col-12">
                             [% IF ctx.password_age == -1 %]
-                            You have never changed your password. Please consider updating your password.
+                            [% l('You have never changed your password. Please consider updating your password.') %]
                             [% ELSIF ctx.password_age >= (ctx.password_age_reminder - 7) %]
-                            Your password is <b>[% ctx.password_age %]</b> days old[%- IF !need_password_change %], you will be asked to change it soon.[%- ELSE %]. We recommend passwords be updated every <b>[% ctx.password_age_reminder %]</b> days. Please consider updating your password.[% END %]
+                            [% l('Your password is <b>[_1]</b> days old.',ctx.password_age) %][%- IF !need_password_change %] [% l('You will be asked to change your password soon.') %][%- ELSE %] [% l('It is recommended to update your password every <b>[_1]</b> days. Please consider updating your password.',ctx.password_age_reminder) %][% END %]
                             [% END %]
                             [% IF need_password_change %]
                             <br>
index a08b05c..25de4ef 100644 (file)
@@ -226,8 +226,8 @@ within the "form" by name for validation.
     <div class="col-md-3">
     </div>
     <div class="col-md-9">
-        <div ng-show="password_age !== undefined && password_age !== '' && password_age !== '-1'"><i>Password last changed <b>{{password_age}}</b> day(s) ago</i></div>
-        <div ng-show="password_age === '-1'"><i>User has never changed their password</i></div>
+        <div ng-show="password_age !== undefined && password_age !== '' && password_age !== '-1'"><i>[% l('Password last changed <b>[_1]</b> day(s) ago', '{{password_age}}') %]</i></div>
+        <div ng-show="password_age === '-1'"><i>[% l('User has never changed their password') %]</i></div>
     </div>
 </div>
 <div class="row reg-field-row">
index b7184a2..e25febd 100644 (file)
@@ -5,14 +5,14 @@
       <h1 class="sr-only" i18n>Evergreen Staff Client Home Page</h1>
     </div>
   </div>
-  <div ng-if="password_reset_age && password_age >= password_reset_age" class="alert alert-danger row">Your password is <b>{{password_age}} days</b> old. It is recommended that passwords be updated every <b>{{password_reset_age}} days</b>. 
+  <div ng-if="password_reset_age && password_age >= (password_reset_age - 7)" class="alert alert-danger row">[% l('Your password is <b>[_1] days</b> old. It is recommended that passwords be updated every <b>[_2] days</b>.','{{password_age}}','{{password_reset_age}}') %] 
       <p ng-if="!can_self_update">
-        Please contact an adminstrator to have your password changed.
+        [% l('Please contact an administrator to have your password changed.') %]
       </p>
       <div ng-if="can_self_update">
       <br>
         <a class="btn btn-warning btn-block" target="_top" href="{{self_update_link}}">
-        Update Password
+        [% l('Update Password') %]
         </a>
       </div>
   </div>
index de023c3..38b9dc0 100644 (file)
@@ -413,7 +413,6 @@ angular.module('egCoreMod')
             service.org_settings = settings;
             if (egCore && egCore.env && !egCore.env.aous) {
                 egCore.env.aous = settings;
-                console.log('setting egCore.env.aous');
             }
             return service.process_org_settings(settings);
         });