From: Llewellyn Marshall Date: Thu, 30 Mar 2023 20:48:51 +0000 (-0400) Subject: fieldmapper and database def for password policies, added password policy to permissi... X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=ad02beb197b7bcf2522985162a43e121336e3b62;p=working%2FEvergreen.git fieldmapper and database def for password policies, added password policy to permission group flesh password policy, show on perm group tree page, fix issues on fm_IDL grab all password policies in perm group tree editor function to get password policy password hint modal dynamically load password hint in OPAC move the OPAC password stuff into the myopac pages only since that's the only place where it's relevant. parity between features on bootstrap and oldschool opacs --- diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index 9823a505b6..68ec282b61 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -8402,10 +8402,12 @@ SELECT usr, + + @@ -14121,6 +14123,24 @@ SELECT usr, + + + + + + + + + + + + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html index c6a4108031..9efbf54758 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/admin-server-splash.component.html @@ -85,6 +85,8 @@ routerLink="/staff/admin/server/actor/org_unit_type"> + {{selected.callerData.application_perm()}} + +
+
+ +
+
+ {{selected.callerData.password_policy() ? passwordPolicyById(selected.callerData.password_policy()).name() : "No password policy"}} +
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts index 5c8b083eae..9baab9e656 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/perm-group-tree.component.ts @@ -24,6 +24,7 @@ export class PermGroupTreeComponent implements OnInit { tree: Tree; selected: TreeNode; permissions: IdlObject[]; + passwordPolicyIDMap: {[id: number]: IdlObject}; permIdMap: {[id: number]: IdlObject}; permEntries: ComboboxEntry[]; permMaps: IdlObject[]; @@ -54,7 +55,8 @@ export class PermGroupTreeComponent implements OnInit { this.permissions = []; this.permEntries = []; this.permMaps = []; - this.permIdMap = {}; + this.permIdMap = {}; + this.passwordPolicyIDMap = {}; } @@ -65,6 +67,8 @@ export class PermGroupTreeComponent implements OnInit { await this.loadPermissions(); this.loadProgress.increment(); await this.loadPermMaps(); + this.loadProgress.increment(); + await this.loadCpp(); this.loadProgress.increment(); this.setOrgDepths(); this.loadProgress.increment(); @@ -128,6 +132,14 @@ export class PermGroupTreeComponent implements OnInit { return this.pcrud.search('pgt', {parent: null}, {flesh: -1, flesh_fields: {pgt: ['children']}} ).pipe(map(pgtTree => this.ingestPgtTree(pgtTree))).toPromise(); + } + + async loadCpp(): Promise { + return this.pcrud.retrieveAll('cpp', {order_by: {cpp: 'id'}}) + .pipe(map(pol => { + this.loadProgress.increment(); + this.passwordPolicyIDMap[+pol.id()] = pol; + })).toPromise(); } async loadPermissions(): Promise { @@ -196,6 +208,10 @@ export class PermGroupTreeComponent implements OnInit { permById(id: number): IdlObject { return this.permIdMap[id]; } + + passwordPolicyById(id: number): IdlObject { + return this.passwordPolicyIDMap[id]; + } // Returns true if the perm map belongs to an ancestore of the // currently selected group. diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts index caadbcb897..dd0283ed87 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/server/routing.module.ts @@ -29,6 +29,14 @@ const routes: Routes = [{ fieldOrder: 'name,owner,ceiling_date,forceto' }] }, { + path: 'config/password_policy', + component: BasicAdminPageComponent, + data: [{ + schema: 'config', + table: 'password_policy', + fieldOrder: 'id,name,regex,max_age' + }] +}, { path: 'config/print_template', component: PrintTemplateComponent }, { diff --git a/Open-ILS/src/eg2/src/app/staff/splash.component.html b/Open-ILS/src/eg2/src/app/staff/splash.component.html index 9f1336892d..7fd1c79792 100644 --- a/Open-ILS/src/eg2/src/app/staff/splash.component.html +++ b/Open-ILS/src/eg2/src/app/staff/splash.component.html @@ -29,13 +29,14 @@
-
-

Your password is {{passwordAge}} days old. It is recommended that passwords be updated every {{passwordExpireAge}} days.

-

- Please contact an administrator to have your password changed or change your password through the OPAC. -

-
-
+
+
+

Your password is {{passwordAge}} days old. It is recommended that passwords be updated every {{passwordExpireAge}} days.

+

+ Please contact an administrator to have your password changed or change your password through the OPAC. +

+
+
Update Password diff --git a/Open-ILS/src/eg2/src/app/staff/splash.component.ts b/Open-ILS/src/eg2/src/app/staff/splash.component.ts index 69626d713a..6b8763549e 100644 --- a/Open-ILS/src/eg2/src/app/staff/splash.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/splash.component.ts @@ -100,12 +100,21 @@ export class StaffSplashComponent implements OnInit { } ); - //get the maximum password age - this.org.settings(['auth.password_expire_age']) - .then(settings => { - this.passwordExpireAge = Number(settings['auth.password_expire_age']); - console.log('password expire age: ', settings); - }); + //get the password expire age for the current user + this.net.request( + 'open-ils.actor', + 'open-ils.actor.get_password_expire_age', + this.auth.token(), + this.auth.user().id()).subscribe( + (res) => { + console.log('password expire age: ', res); + this.passwordExpireAge = Number(res); + }, + (err) => { + console.error('splash', err); + } + ); + //get the age of the current user's password this.net.request( 'open-ils.actor', diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm index d9ee1a1ccc..851bb8687f 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Actor.pm @@ -5066,6 +5066,96 @@ sub get_password_last_edit_age { } __PACKAGE__->register_method( + method => "get_password_expire_age", + api_name => "open-ils.actor.get_password_expire_age", + signature => { + desc => "Finds the number of days before a user's password should be updated.", + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Patron ID', type => 'number'}, + ], + return => {desc => 'Number of days before password update is required'} + } +); + +sub get_password_expire_age { + 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); + + #return unless the requestor is either the patron in question, or can view users at that patron's home ou + unless($patron && ($patron_id == $e->requestor->id || $e->allowed('VIEW_USER', $patron->home_ou))) { + return $e->event; + } + + my $cpp = $e->json_query({ + select => {cpp => ['max_age']}, + from => {pgt => 'cpp'}, + where => { + '+pgt' => { + id => $patron->profile + } + } + }); + + if(defined $cpp){ + my $max_age = $cpp->[0]->{'max_age'}; + if($max_age){ + return int($max_age); + } + } + #there's either no password policy or no max_age on the password policy use + return $U->ou_ancestor_setting_value( + $patron->home_ou, 'auth.password_expire_age') || 0; +} + +__PACKAGE__->register_method( + method => "get_password_hint", + api_name => "open-ils.actor.get_password_hint", + signature => { + desc => "Finds the password hint for a user based on their password policy.", + params => [ + {desc => 'Authentication token', type => 'string'}, + {desc => 'Patron ID', type => 'number'}, + ], + return => {desc => 'password policy hint'} + } +); + +sub get_password_hint { + 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); + + #return unless the requestor is either the patron in question, or can view users at that patron's home ou + unless($patron && ($patron_id == $e->requestor->id || $e->allowed('VIEW_USER', $patron->home_ou))) { + return $e->event; + } + + my $cpp = $e->json_query({ + select => {cpp => ['hint']}, + from => {pgt => 'cpp'}, + where => { + '+pgt' => { + id => $patron->profile + } + } + }); + + if(defined $cpp){ + my $hint = $cpp->[0]->{'hint'}; + if($hint){ + return $hint; + } + } + + #there's either no password policy or no hint on the password policy + return ""; +} + +__PACKAGE__->register_method( method => 'address_alert_test', api_name => 'open-ils.actor.address_alert.test', signature => { diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm index cbe6ac0d7e..2ff4f8317d 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader.pm @@ -383,9 +383,6 @@ sub load_common { $ctx->{authtoken} = $e->authtoken; $ctx->{authtime} = $e->authtime; $ctx->{user} = $e->requestor; - $ctx->{password_age} = int($U->simplereq( - 'open-ils.actor', - 'open-ils.actor.get_password_age', $e->authtoken, $ctx->{user}->id)); my $card = $self->editor->retrieve_actor_card($ctx->{user}->card); $ctx->{active_card} = (ref $card) ? $card->barcode : undef; $ctx->{place_unfillable} = 1 if $e->requestor->wsid && $e->allowed('PLACE_UNFILLABLE_HOLD', $e->requestor->ws_ou); diff --git a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm index 3bc5b42ac3..de312996e1 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/WWW/EGCatLoader/Account.pm @@ -58,6 +58,32 @@ sub prepare_extended_user_info { return; } +sub prepare_user_password_info { + my $self = shift; + my $e = $self->editor; + my $usr = $self->ctx->{user}; + + my $cpp = $e->json_query({ + select => {cpp => ['hint','max_age','regex']}, + from => {pgt => 'cpp'}, + where => { + '+pgt' => { + id => $usr->profile + } + } + })->[0]; + + if(defined $cpp){ + $self->ctx->{password_hint} = $cpp->{'hint'}; + $self->ctx->{password_age} = $cpp->{'max_age'}; + $self->ctx->{password_regex} = $cpp->{'regex'}; + } + + $self->ctx->{password_expire_age} = int($U->simplereq( + 'open-ils.actor', + 'open-ils.actor.get_password_expire_age', $e->authtoken, $usr->id)); +} + # Given an event returned by a failed attempt to create a hold, do we have # permission to override? XXX Should the permission check be scoped to a # given org_unit context? @@ -2643,6 +2669,7 @@ sub load_myopac_main { pub => 't' }) ); + $self->prepare_user_password_info; return $self->prepare_fines($limit, $offset) || Apache2::Const::OK; } @@ -2811,6 +2838,8 @@ sub load_myopac_update_password { my $e = $self->editor; my $ctx = $self->ctx; + $self->prepare_user_password_info; + return Apache2::Const::OK unless $self->cgi->request_method eq 'POST'; @@ -2828,7 +2857,7 @@ sub load_myopac_update_password { return Apache2::Const::OK; } - my $pw_regex = $ctx->{get_org_setting}->($e->requestor->home_ou, 'global.password_regex'); + my $pw_regex = $ctx->{password_regex} || $ctx->{get_org_setting}->($e->requestor->home_ou, 'global.password_regex'); if(!$pw_regex) { # This regex duplicates the JSPac's default "digit, letter, and 7 characters" rule diff --git a/Open-ILS/src/sql/Pg/upgrade/xxxx.schema.password_policy.sql b/Open-ILS/src/sql/Pg/upgrade/xxxx.schema.password_policy.sql new file mode 100644 index 0000000000..be7fcb1155 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/xxxx.schema.password_policy.sql @@ -0,0 +1,21 @@ +BEGIN; + +--SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version); + +CREATE TABLE config.password_policy ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + hint TEXT, + regex TEXT, + max_age INT, + CONSTRAINT policy_name_unique UNIQUE (name) +); + +ALTER TABLE permission.grp_tree ADD COLUMN password_policy BIGINT REFERENCES config.password_policy(id); + +CREATE INDEX cpp_id_idx ON config.password_policy (id); +--INSERT INTO permission.perm_list ( id, code, description ) VALUES +-- ( XX, 'ADMIN_PASSWORD_POLICY', oils_i18n_gettext( XX, +-- 'Allow a user to view and modify password policies', 'ppl', 'description' )); + +COMMIT; \ No newline at end of file diff --git a/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 b/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 index 4370012b97..4634c791b9 100755 --- a/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 +++ b/Open-ILS/src/templates-bootstrap/opac/myopac/main.tt2 @@ -15,13 +15,13 @@

[% l('My Account Summary') %]


- [% IF ctx.password_age && ctx.password_age_reminder && ctx.disable_password_change != 'true' %] - [% need_password_change = ctx.password_age == -1 || ctx.password_age >= ctx.password_age_reminder %] + [% IF ctx.password_age && ctx.password_expire_age && ctx.disable_password_change != 'true' %] + [% need_password_change = ctx.password_age == -1 || ctx.password_age >= ctx.password_expire_age %]
[% IF ctx.password_age == -1 %] [% l('You have never changed your password. Please consider updating your password.') %] - [% ELSIF ctx.password_age >= (ctx.password_age_reminder - 7) %] - [% l('Your password is [_1] 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 [_1] days. Please consider updating your password.',ctx.password_age_reminder) %][% END %] + [% ELSIF ctx.password_age >= (ctx.ctx.password_expire_age - 7) %] + [% l('Your password is [_1] 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 [_1] days. Please consider updating your password.',ctx.ctx.password_expire_age) %][% END %] [% END %] [% IF need_password_change %]
diff --git a/Open-ILS/src/templates-bootstrap/opac/myopac/update_password_msg.tt2 b/Open-ILS/src/templates-bootstrap/opac/myopac/update_password_msg.tt2 index 23d5914d3b..555192fb43 100755 --- a/Open-ILS/src/templates-bootstrap/opac/myopac/update_password_msg.tt2 +++ b/Open-ILS/src/templates-bootstrap/opac/myopac/update_password_msg.tt2 @@ -1,3 +1,7 @@
-[% l('Note: The password must be at least 7 characters in length, contain at least one letter (a-z/A-Z), and contain at least one number.'); %] +[% IF ctx.password_hint != "" %] + [% ctx.password_hint %] +[% ELSE %] + [% l('Note: The password must be at least 7 characters in length, contain at least one letter (a-z/A-Z), and contain at least one number.'); %] +[% END %]
\ No newline at end of file diff --git a/Open-ILS/src/templates/opac/myopac/update_password_msg.tt2 b/Open-ILS/src/templates/opac/myopac/update_password_msg.tt2 index 23d5914d3b..555192fb43 100644 --- a/Open-ILS/src/templates/opac/myopac/update_password_msg.tt2 +++ b/Open-ILS/src/templates/opac/myopac/update_password_msg.tt2 @@ -1,3 +1,7 @@
-[% l('Note: The password must be at least 7 characters in length, contain at least one letter (a-z/A-Z), and contain at least one number.'); %] +[% IF ctx.password_hint != "" %] + [% ctx.password_hint %] +[% ELSE %] + [% l('Note: The password must be at least 7 characters in length, contain at least one letter (a-z/A-Z), and contain at least one number.'); %] +[% END %]
\ No newline at end of file diff --git a/Open-ILS/src/templates/opac/parts/config.tt2 b/Open-ILS/src/templates/opac/parts/config.tt2 index da7c15dde6..fbd8b4e1f3 100644 --- a/Open-ILS/src/templates/opac/parts/config.tt2 +++ b/Open-ILS/src/templates/opac/parts/config.tt2 @@ -284,13 +284,3 @@ contents_truncate_length = 50; # Edit parts/record/contents.tt2 to designate character length on a field-by- # field basis for notes. - - -############################################################################## -# Password Reminder Settings -############################################################################## - -# days since last password change to start reminding a patron to change their password -# commenting out this line will disable the reminder -ctx.password_age_reminder = ctx.get_org_setting(ctx.physical_loc || 1, 'auth.password_expire_age'); -%] diff --git a/Open-ILS/src/templates/opac/parts/myopac/main_base.tt2 b/Open-ILS/src/templates/opac/parts/myopac/main_base.tt2 index 3c1271b9df..cf7d29756e 100644 --- a/Open-ILS/src/templates/opac/parts/myopac/main_base.tt2 +++ b/Open-ILS/src/templates/opac/parts/myopac/main_base.tt2 @@ -56,6 +56,21 @@ %] [% l("Your library card expired on [_1]. Please contact a librarian to resolve this issue.", fmt_expire_date) %] [% END %] + [% IF ctx.password_age && ctx.password_expire_age && ctx.disable_password_change != 'true' %] + [% need_password_change = ctx.password_age == -1 || ctx.password_age >= ctx.password_expire_age %] + + [% IF ctx.password_age == -1 %] + [% l('You have never changed your password. Please consider updating your password.') %] + [% ELSIF ctx.password_age >= (ctx.ctx.password_expire_age - 7) %] + [% l('Your password is [_1] 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 [_1] days. Please consider updating your password.',ctx.ctx.password_expire_age) %][% END %] + [% END %] + [% IF need_password_change %] + [% l("Change Password") %] +
+ [% END %] +
+ [% END %]
diff --git a/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 b/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 index cf30d2d6cd..2bd813d636 100644 --- a/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 +++ b/Open-ILS/src/templates/staff/circ/patron/t_edit.tt2 @@ -218,6 +218,8 @@ within the "form" by name for validation. [% draw_field_label('au', 'passwd') %] [% draw_form_input('au', 'passwd'); %]
+