@@ -, +, @@ password overrides by category --- Koha/AuthUtils.pm | 19 +++++----- Koha/Patron.pm | 4 +- Koha/Patron/Category.pm | 32 ++++++++++++++++ admin/categories.pl | 8 ++++ .../prog/en/includes/password_check.inc | 42 ++++++++++++++------- .../prog/en/modules/admin/categories.tt | 37 ++++++++++++++++++ .../prog/en/modules/members/member-password.tt | 15 ++++---- .../prog/en/modules/members/memberentrygen.tt | 7 ++-- .../prog/en/modules/onboarding/onboardingstep3.tt | 5 +-- .../bootstrap/en/includes/password_check.inc | 44 +++++++++++++++------- .../bootstrap/en/modules/opac-memberentry.tt | 18 ++++----- .../opac-tmpl/bootstrap/en/modules/opac-passwd.tt | 9 ++--- .../bootstrap/en/modules/opac-password-recovery.tt | 6 +-- members/memberentry.pl | 6 ++- misc/admin/set_password.pl | 13 ++++--- opac/opac-memberentry.pl | 8 ++-- opac/opac-password-recovery.pl | 15 +++++++- opac/opac-registration-verify.pl | 3 +- 18 files changed, 207 insertions(+), 84 deletions(-) --- a/Koha/AuthUtils.pm +++ a/Koha/AuthUtils.pm @@ -139,21 +139,21 @@ sub generate_salt { =head2 is_password_valid -my ( $is_valid, $error ) = is_password_valid( $password ); +my ( $is_valid, $error ) = is_password_valid( $password, $category ); -return $is_valid == 1 if the password match minPasswordLength and RequireStrongPassword conditions +return $is_valid == 1 if the password match category's minimum password length and strength if provided, or general minPasswordLength and RequireStrongPassword conditions otherwise return $is_valid == 0 and $error will contain the error ('too_short' or 'too_weak') =cut sub is_password_valid { - my ($password) = @_; - my $minPasswordLength = C4::Context->preference('minPasswordLength'); + my ($password, $category) = @_; + my $minPasswordLength = $category?$category->effective_min_password_length:C4::Context->preference('minPasswordLength'); $minPasswordLength = 3 if not $minPasswordLength or $minPasswordLength < 3; if ( length($password) < $minPasswordLength ) { return ( 0, 'too_short' ); } - elsif ( C4::Context->preference('RequireStrongPassword') ) { + elsif ( $category?$category->effective_require_strong_password:C4::Context->preference('RequireStrongPassword') ) { return ( 0, 'too_weak' ) if $password !~ m|(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{$minPasswordLength,}|; } @@ -163,20 +163,21 @@ sub is_password_valid { =head2 generate_password -my password = generate_password(); +my password = generate_password($category); -Generate a password according to the minPasswordLength and RequireStrongPassword. +Generate a password according to category's minimum password length and strength if provided, or to the minPasswordLength and RequireStrongPassword system preferences. =cut sub generate_password { - my $minPasswordLength = C4::Context->preference('minPasswordLength'); + my ($category) = @_; + my $minPasswordLength = $category?$category->effective_min_password_length:C4::Context->preference('minPasswordLength'); $minPasswordLength = 8 if not $minPasswordLength or $minPasswordLength < 8; my ( $password, $is_valid ); do { $password = random_string('.' x $minPasswordLength ); - ( $is_valid, undef ) = is_password_valid( $password ); + ( $is_valid, undef ) = is_password_valid( $password, $category ); } while not $is_valid; return $password; } --- a/Koha/Patron.pm +++ a/Koha/Patron.pm @@ -731,11 +731,11 @@ sub set_password { my $password = $args->{password}; unless ( $args->{skip_validation} ) { - my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password ); + my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password, $self->category ); if ( !$is_valid ) { if ( $error eq 'too_short' ) { - my $min_length = C4::Context->preference('minPasswordLength'); + my $min_length = $self->category->effective_min_password_length; $min_length = 3 if not $min_length or $min_length < 3; my $password_length = length($password); --- a/Koha/Patron/Category.pm +++ a/Koha/Patron/Category.pm @@ -255,6 +255,38 @@ sub effective_change_password { : C4::Context->preference('OpacPasswordChange'); } +=head3 effective_min_password_length + + $category->effective_min_password_length() + +Retrieve category's password length if setted, or minPasswordLength otherwise + +=cut + +sub effective_min_password_length { + my ($self) = @_; + + return C4::Context->preference('minPasswordLength') unless defined $self->min_password_length; + + return $self->min_password_length; +} + +=head3 effective_require_strong_password + + $category->effective_require_strong_password() + +Retrieve category's password strength if setted, or RequireStrongPassword otherwise + +=cut + +sub effective_require_strong_password { + my ($self) = @_; + + return C4::Context->preference('RequireStrongPassword') unless defined $self->require_strong_password; + + return $self->require_strong_password; +} + =head3 override_hidden_items if ( $patron->category->override_hidden_items ) { --- a/admin/categories.pl +++ a/admin/categories.pl @@ -94,10 +94,14 @@ elsif ( $op eq 'add_validate' ) { my $default_privacy = $input->param('default_privacy'); my $reset_password = $input->param('reset_password'); my $change_password = $input->param('change_password'); + my $min_password_length = $input->param('min_password_length'); + my $require_strong_password = $input->param('require_strong_password'); my @branches = grep { $_ ne q{} } $input->multi_param('branches'); $reset_password = undef if $reset_password eq -1; $change_password = undef if $change_password eq -1; + $min_password_length = undef unless length($min_password_length); + $require_strong_password = undef if $require_strong_password eq -1; my $is_a_modif = $input->param("is_a_modif"); @@ -129,6 +133,8 @@ elsif ( $op eq 'add_validate' ) { $category->default_privacy($default_privacy); $category->reset_password($reset_password); $category->change_password($change_password); + $category->min_password_length($min_password_length); + $category->require_strong_password($require_strong_password); eval { $category->store; $category->replace_branch_limitations( \@branches ); @@ -157,6 +163,8 @@ elsif ( $op eq 'add_validate' ) { default_privacy => $default_privacy, reset_password => $reset_password, change_password => $change_password, + min_password_length => $min_password_length, + require_strong_password => $require_strong_password, }); eval { $category->store; --- a/koha-tmpl/intranet-tmpl/prog/en/includes/password_check.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/password_check.inc @@ -1,19 +1,36 @@ [% USE Koha %] -[% BLOCK add_password_check %] -[% END %] + --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categories.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categories.tt @@ -229,6 +229,43 @@ [% END %] +
  • + + + Leave blank to use system default ([% Koha.Preference('minPasswordLength') | html %]) +
  • +
  • + + +
  • - [% SET password_pattern = ".{" _ Koha.Preference('minPasswordLength') _ ",}" %] - [% IF Koha.Preference('RequireStrongPassword') %] - [% SET password_pattern = '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{' _ Koha.Preference('minPasswordLength') _ ',}' %] + [% SET password_pattern = ".{" _ patron.category.effective_min_password_length _ ",}" %] + [% IF patron.category.effective_require_strong_password %] + [% SET password_pattern = '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{' _ patron.category.effective_min_password_length _ ',}' %] [% END %]
  • @@ -105,7 +105,7 @@ function generate_password() { // Always generate a strong password var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - var length = [% Koha.Preference('minPasswordLength') | html %]; + var length = [% patron.category.effective_min_password_length | html %]; if ( length < 8 ) length = 8; var password=''; for ( var i = 0 ; i < length ; i++){ @@ -117,7 +117,7 @@ $("body").on('click', "#fillrandom",function(e) { e.preventDefault(); var password = ''; - var pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% Koha.Preference('minPasswordLength') | html %],}/; + var pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% patron.category.effective_min_password_length | html %],}/; while ( ! pattern_regex.test( password ) ) { password = generate_password(); } @@ -156,8 +156,7 @@ }); }); - [% PROCESS 'password_check.inc' %] - [% PROCESS 'add_password_check' new_password => 'newpassword' %] + [% PROCESS 'password_check.inc' new_password => 'newpassword', minPasswordLength => patron.category.effective_min_password_length, RequireStrongPassword => patron.category.effective_require_strong_password %] [% END %] [% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt @@ -825,9 +825,9 @@ legend:hover { [% IF ( typeloo.typename_X ) %][% END %] [% END %] [% IF ( categoryloo.categorycodeselected ) %] - + [% ELSE %] - + [% END %] [% IF ( loop.last ) %] @@ -1714,8 +1714,7 @@ legend:hover { [% Asset.js("js/members.js") | $raw %] [% Asset.js("js/messaging-preference-form.js") | $raw %] - [% PROCESS 'password_check.inc' %] - [% PROCESS 'add_password_check' new_password => 'password' %] + [% PROCESS 'password_check.inc' new_password => 'password', category_selector => '#categorycode_entry', minPasswordLength => patron.category.effective_min_password_length, RequireStrongPassword => patron.category.effective_require_strong_password %] [% END %] [% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/intranet-tmpl/prog/en/modules/onboarding/onboardingstep3.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/onboarding/onboardingstep3.tt @@ -61,7 +61,7 @@ Required

    @@ -116,8 +116,7 @@ [% INCLUDE 'validator-strings.inc' %] [% INCLUDE 'installer-strings.inc' %] [% Asset.js("js/onboarding.js") | $raw %] - [% PROCESS 'password_check.inc' %] - [% PROCESS 'add_password_check' new_password => 'password' %] + [% PROCESS 'password_check.inc' new_password => 'password', category_selector => '#categorycode_entry', RequireStrongPassword => Koha.Preference('RequireStrongPassword') %] [% END %] [% INCLUDE 'installer-intranet-bottom.inc' %] --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/password_check.inc +++ a/koha-tmpl/opac-tmpl/bootstrap/en/includes/password_check.inc @@ -1,25 +1,41 @@ [% USE Koha %] -[% BLOCK add_password_check %] -[% END %] + --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt @@ -82,7 +82,7 @@ [% IF field == "B_email" %]
  • Alternate address information: email address
  • [% END %] [% IF field == "password_match" %]
  • Passwords do not match! password
  • [% END %] [% IF field == "password_too_short" %] -
  • Password must be at least [% minPasswordLength | html %] characters long.
  • +
  • Password must be at least [% patron.category.effective_min_password_length | html %] characters long.
  • [% END %] [% IF field == "password_too_weak" %]
  • Password must contain at least one digit, one lowercase and one uppercase.
  • @@ -252,9 +252,9 @@ @@ -858,10 +858,10 @@
    Password
    - [% IF ( Koha.Preference('RequireStrongPassword') ) %] -

    Your password must contain at least [% Koha.Preference('minPasswordLength') | html %] characters, including UPPERCASE, lowercase and numbers.

    + [% IF ( patron.category.effective_require_strong_password ) %] +

    Your password must contain at least [% patron.category.effective_min_password_length | html %] characters, including UPPERCASE, lowercase and numbers.

    [% ELSE %] -

    Your password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.

    +

    Your password must be at least [% patron.category.effective_min_password_length | html %] characters long.

    [% END %] [% UNLESS mandatory.defined('password') %]

    If you do not enter a password a system generated password will be created.

    @@ -1001,8 +1001,7 @@ [% INCLUDE 'opac-bottom.inc' %] [% BLOCK jsinclude %] [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %] - [% PROCESS 'password_check.inc' %] - [% PROCESS 'add_password_check' new_password => 'borrower_password' %] + -[% INCLUDE 'calendar.inc' %] + [% PROCESS 'password_check.inc' new_password => 'borrower_password', category_selector => '#borrower_categorycode', RequireStrongPassword => patron.category.effective_require_strong_password, minPasswordLength => patron.category.effective_min_password_length %] + [% END %] --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-passwd.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-passwd.tt @@ -57,10 +57,10 @@
    - [% IF ( Koha.Preference('RequireStrongPassword') ) %] -
    Your password must contain at least [% Koha.Preference('minPasswordLength') | html %] characters, including UPPERCASE, lowercase and numbers.
    + [% IF ( logged_in_user.category.effective_require_strong_password ) %] +
    Your password must contain at least [% logged_in_user.category.effective_min_password_length | html %] characters, including UPPERCASE, lowercase and numbers.
    [% ELSE %] -
    Your password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.
    +
    Your password must be at least [% logged_in_user.category.effective_min_password_length | html %] characters long.
    [% END %] @@ -94,8 +94,7 @@ [% INCLUDE 'opac-bottom.inc' %] [% BLOCK jsinclude %] [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %] - [% PROCESS 'password_check.inc' %] - [% PROCESS 'add_password_check' new_password => 'Newkey' %] + [% PROCESS 'password_check.inc' new_password => 'Newkey', minPasswordLength => logged_in_user.category.effective_min_password_length, RequireStrongPassword => logged_in_user.category.effective_require_strong_password %]