View | Details | Raw Unified | Return to bug 23816
Collapse All | Expand All

(-)a/Koha/AuthUtils.pm (-9 / +10 lines)
Lines 139-159 sub generate_salt { Link Here
139
139
140
=head2 is_password_valid
140
=head2 is_password_valid
141
141
142
my ( $is_valid, $error ) = is_password_valid( $password );
142
my ( $is_valid, $error ) = is_password_valid( $password, $category );
143
143
144
return $is_valid == 1 if the password match minPasswordLength and RequireStrongPassword conditions
144
return $is_valid == 1 if the password match category's minimum password length and strength if provided, or general minPasswordLength and RequireStrongPassword conditions
145
otherwise return $is_valid == 0 and $error will contain the error ('too_short' or 'too_weak')
145
otherwise return $is_valid == 0 and $error will contain the error ('too_short' or 'too_weak')
146
146
147
=cut
147
=cut
148
148
149
sub is_password_valid {
149
sub is_password_valid {
150
    my ($password) = @_;
150
    my ($password, $category) = @_;
151
    my $minPasswordLength = C4::Context->preference('minPasswordLength');
151
    my $minPasswordLength = $category?$category->effective_min_password_length:C4::Context->preference('minPasswordLength');
152
    $minPasswordLength = 3 if not $minPasswordLength or $minPasswordLength < 3;
152
    $minPasswordLength = 3 if not $minPasswordLength or $minPasswordLength < 3;
153
    if ( length($password) < $minPasswordLength ) {
153
    if ( length($password) < $minPasswordLength ) {
154
        return ( 0, 'too_short' );
154
        return ( 0, 'too_short' );
155
    }
155
    }
156
    elsif ( C4::Context->preference('RequireStrongPassword') ) {
156
    elsif ( $category?$category->effective_require_strong_password:C4::Context->preference('RequireStrongPassword') ) {
157
        return ( 0, 'too_weak' )
157
        return ( 0, 'too_weak' )
158
          if $password !~ m|(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{$minPasswordLength,}|;
158
          if $password !~ m|(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{$minPasswordLength,}|;
159
    }
159
    }
Lines 163-182 sub is_password_valid { Link Here
163
163
164
=head2 generate_password
164
=head2 generate_password
165
165
166
my password = generate_password();
166
my password = generate_password($category);
167
167
168
Generate a password according to the minPasswordLength and RequireStrongPassword.
168
Generate a password according to category's minimum password length and strength if provided, or to the minPasswordLength and RequireStrongPassword system preferences.
169
169
170
=cut
170
=cut
171
171
172
sub generate_password {
172
sub generate_password {
173
    my $minPasswordLength = C4::Context->preference('minPasswordLength');
173
    my ($category) = @_;
174
    my $minPasswordLength = $category?$category->effective_min_password_length:C4::Context->preference('minPasswordLength');
174
    $minPasswordLength = 8 if not $minPasswordLength or $minPasswordLength < 8;
175
    $minPasswordLength = 8 if not $minPasswordLength or $minPasswordLength < 8;
175
176
176
    my ( $password, $is_valid );
177
    my ( $password, $is_valid );
177
    do {
178
    do {
178
        $password = random_string('.' x $minPasswordLength );
179
        $password = random_string('.' x $minPasswordLength );
179
        ( $is_valid, undef ) = is_password_valid( $password );
180
        ( $is_valid, undef ) = is_password_valid( $password, $category );
180
    } while not $is_valid;
181
    } while not $is_valid;
181
    return $password;
182
    return $password;
182
}
183
}
(-)a/Koha/Patron.pm (-2 / +2 lines)
Lines 744-754 sub set_password { Link Here
744
    my $password = $args->{password};
744
    my $password = $args->{password};
745
745
746
    unless ( $args->{skip_validation} ) {
746
    unless ( $args->{skip_validation} ) {
747
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password );
747
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password, $self->category );
748
748
749
        if ( !$is_valid ) {
749
        if ( !$is_valid ) {
750
            if ( $error eq 'too_short' ) {
750
            if ( $error eq 'too_short' ) {
751
                my $min_length = C4::Context->preference('minPasswordLength');
751
                my $min_length = $self->category->effective_min_password_length;
752
                $min_length = 3 if not $min_length or $min_length < 3;
752
                $min_length = 3 if not $min_length or $min_length < 3;
753
753
754
                my $password_length = length($password);
754
                my $password_length = length($password);
(-)a/Koha/Patron/Category.pm (+32 lines)
Lines 255-260 sub effective_change_password { Link Here
255
        : C4::Context->preference('OpacPasswordChange');
255
        : C4::Context->preference('OpacPasswordChange');
256
}
256
}
257
257
258
=head3 effective_min_password_length
259
260
    $category->effective_min_password_length()
261
262
Retrieve category's password length if setted, or minPasswordLength otherwise
263
264
=cut
265
266
sub effective_min_password_length {
267
    my ($self) = @_;
268
269
    return C4::Context->preference('minPasswordLength') unless defined $self->min_password_length;
270
271
    return $self->min_password_length;
272
}
273
274
=head3 effective_require_strong_password
275
276
    $category->effective_require_strong_password()
277
278
Retrieve category's password strength if setted, or RequireStrongPassword otherwise
279
280
=cut
281
282
sub effective_require_strong_password {
283
    my ($self) = @_;
284
285
    return C4::Context->preference('RequireStrongPassword') unless defined $self->require_strong_password;
286
287
    return $self->require_strong_password;
288
}
289
258
=head3 override_hidden_items
290
=head3 override_hidden_items
259
291
260
    if ( $patron->category->override_hidden_items ) {
292
    if ( $patron->category->override_hidden_items ) {
(-)a/admin/categories.pl (+8 lines)
Lines 94-103 elsif ( $op eq 'add_validate' ) { Link Here
94
    my $default_privacy = $input->param('default_privacy');
94
    my $default_privacy = $input->param('default_privacy');
95
    my $reset_password = $input->param('reset_password');
95
    my $reset_password = $input->param('reset_password');
96
    my $change_password = $input->param('change_password');
96
    my $change_password = $input->param('change_password');
97
    my $min_password_length = $input->param('min_password_length');
98
    my $require_strong_password = $input->param('require_strong_password');
97
    my @branches = grep { $_ ne q{} } $input->multi_param('branches');
99
    my @branches = grep { $_ ne q{} } $input->multi_param('branches');
98
100
99
    $reset_password = undef if $reset_password eq -1;
101
    $reset_password = undef if $reset_password eq -1;
100
    $change_password = undef if $change_password eq -1;
102
    $change_password = undef if $change_password eq -1;
103
    $min_password_length = undef unless length($min_password_length);
104
    $require_strong_password = undef if $require_strong_password eq -1;
101
105
102
    my $is_a_modif = $input->param("is_a_modif");
106
    my $is_a_modif = $input->param("is_a_modif");
103
107
Lines 129-134 elsif ( $op eq 'add_validate' ) { Link Here
129
        $category->default_privacy($default_privacy);
133
        $category->default_privacy($default_privacy);
130
        $category->reset_password($reset_password);
134
        $category->reset_password($reset_password);
131
        $category->change_password($change_password);
135
        $category->change_password($change_password);
136
        $category->min_password_length($min_password_length);
137
        $category->require_strong_password($require_strong_password);
132
        eval {
138
        eval {
133
            $category->store;
139
            $category->store;
134
            $category->replace_branch_limitations( \@branches );
140
            $category->replace_branch_limitations( \@branches );
Lines 157-162 elsif ( $op eq 'add_validate' ) { Link Here
157
            default_privacy => $default_privacy,
163
            default_privacy => $default_privacy,
158
            reset_password => $reset_password,
164
            reset_password => $reset_password,
159
            change_password => $change_password,
165
            change_password => $change_password,
166
            min_password_length => $min_password_length,
167
            require_strong_password => $require_strong_password,
160
        });
168
        });
161
        eval {
169
        eval {
162
            $category->store;
170
            $category->store;
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/password_check.inc (-12 / +28 lines)
Lines 1-20 Link Here
1
[% USE Koha %]
1
[% USE Koha %]
2
[% BLOCK add_password_check %]
3
<!-- password_check.inc -->
2
<!-- password_check.inc -->
4
<script>
3
<script>
5
    var pwd_title = "";
4
    var pwd_title = "";
6
    var pattern_title = "";
5
    var pattern_title = "";
7
    var new_password_node_name = "[% new_password | html %]";
6
    var new_password_node_name = "[% new_password | html %]";
8
    [% IF Koha.Preference('RequireStrongPassword') %]
7
    var category_selector = "[% category_selector | html %]";
9
        pwd_title = _("Password must contain at least %s characters, including UPPERCASE, lowercase and numbers").format([% minPasswordLength | html %]);
8
    var STRONG_MSG = _("Password must contain at least %s characters, including UPPERCASE, lowercase and numbers");
10
        pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% minPasswordLength | html %],}/;
9
    var WEAK_MSG = _("Password must contain at least %s characters");
11
    [% ELSIF minPasswordLength %]
10
12
        pwd_title = _("Password must contain at least %s characters").format([% minPasswordLength | html %]);
11
    if(category_selector && $('select'+category_selector).length) {
13
        pattern_regex = /.{[% minPasswordLength | html %],}/;
12
        jQuery.validator.addMethod("password_strong", function(value, element){
14
    [% END %]
13
            var require_strong = $('select'+category_selector+' option:selected').data('pwdStrong');
15
    jQuery.validator.addMethod("password_strong", function(value, element){
14
            var min_lenght = $('select'+category_selector+' option:selected').data('pwdLength');
16
        return this.optional(element) || value == '****' || pattern_regex.test(value);
15
            var regex_text = require_strong?"(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{"+min_lenght+",}":".{"+min_lenght+",}";
17
    }, pwd_title);
16
            var pattern_regex = new RegExp(regex_text);
17
            return this.optional(element) || pattern_regex.test(value);
18
        }, function(unused, element) {
19
            var require_strong = $('select'+category_selector+' option:selected').data('pwdStrong');
20
            var min_lenght = $('select'+category_selector+' option:selected').data('pwdLength');
21
            return (require_strong?STRONG_MSG:WEAK_MSG).format(min_lenght)
22
        });
23
    } else {
24
        [% IF RequireStrongPassword %]
25
            pwd_title = STRONG_MSG.format([% minPasswordLength | html %]);
26
            pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% minPasswordLength | html %],}/;
27
        [% ELSIF minPasswordLength %]
28
            pwd_title = WEAK_MSG.format([% minPasswordLength | html %]);
29
            pattern_regex = /.{[% minPasswordLength | html %],}/;
30
        [% END %]
31
        jQuery.validator.addMethod("password_strong", function(value, element){
32
            return this.optional(element) || value == '****' || pattern_regex.test(value);
33
        }, pwd_title);
34
    }
18
    jQuery.validator.addMethod("password_no_spaces", function(value, element){
35
    jQuery.validator.addMethod("password_no_spaces", function(value, element){
19
        return ( this.optional(element) || !value.match(/^\s/) && !value.match(/\s$/) );
36
        return ( this.optional(element) || !value.match(/^\s/) && !value.match(/\s$/) );
20
    }, _("Password contains leading and/or trailing spaces"));
37
    }, _("Password contains leading and/or trailing spaces"));
Lines 24-27 Link Here
24
    }, _("Please enter the same password as above"));
41
    }, _("Please enter the same password as above"));
25
</script>
42
</script>
26
<!-- / password_check.inc -->
43
<!-- / password_check.inc -->
27
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/categories.tt (+37 lines)
Lines 229-234 Link Here
229
                      [% END %]
229
                      [% END %]
230
                    </select>
230
                    </select>
231
                </li>
231
                </li>
232
                <li>
233
                    <label for="min_password_length">Minimum password length:</label>
234
                    <input id="min_password_length" type="number" name="min_password_length" value="[% category.min_password_length | html %]"/>
235
                    <span>Leave blank to use system default ([% Koha.Preference('minPasswordLength') | html %])</span>
236
                </li>
237
                <li class="pwd_setting_wrapper">
238
                    <label for="require_strong_password">Require strong password:</label>
239
                    <select id="require_strong_password" name="require_strong_password">
240
                    [% IF category.require_strong_password.defined %]
241
                        [% IF category.require_strong_password %]
242
                          [% IF Koha.Preference('RequireStrongPassword') %]
243
                            <option value="-1">Follow system preference RequireStrongPassword (yes)</option>
244
                          [% ELSE %]
245
                            <option value="-1">Follow system preference RequireStrongPassword (no)</option>
246
                          [% END %]
247
                            <option value="1" selected="selected">Yes</option>
248
                            <option value="0">No</option>
249
                        [% ELSE %]
250
                          [% IF Koha.Preference('RequireStrongPassword') %]
251
                            <option value="-1">Follow system preference RequireStrongPassword (yes)</option>
252
                          [% ELSE %]
253
                            <option value="-1">Follow system preference RequireStrongPassword (no)</option>
254
                          [% END %]
255
                            <option value="1">Yes</option>
256
                            <option value="0" selected="selected">No</option>
257
                        [% END %]
258
                      [% ELSE %]
259
                          [% IF Koha.Preference('RequireStrongPassword') %]
260
                            <option value="-1">Follow system preference RequireStrongPassword (yes)</option>
261
                          [% ELSE %]
262
                            <option value="-1">Follow system preference RequireStrongPassword (no)</option>
263
                          [% END %]
264
                            <option value="1">Yes</option>
265
                            <option value="0">No</option>
266
                      [% END %]
267
                    </select>
268
                </li>
232
                <li><label for="block_expired">Block expired patrons:</label>
269
                <li><label for="block_expired">Block expired patrons:</label>
233
                    <select name="BlockExpiredPatronOpacActions" id="block_expired">
270
                    <select name="BlockExpiredPatronOpacActions" id="block_expired">
234
                        [% IF not category or category.BlockExpiredPatronOpacActions == -1%]
271
                        [% IF not category or category.BlockExpiredPatronOpacActions == -1%]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-password.tt (-8 / +7 lines)
Lines 37-43 Link Here
37
        <li>You have entered a username that already exists. Please choose another one.</li>
37
        <li>You have entered a username that already exists. Please choose another one.</li>
38
		[% END %]
38
		[% END %]
39
        [% IF ( ERROR_password_too_short ) %]
39
        [% IF ( ERROR_password_too_short ) %]
40
            <li id="ERROR_short_password">Password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.</li>
40
            <li id="ERROR_short_password">Password must be at least [% patron.category.effective_min_password_length | html %] characters long.</li>
41
        [% END %]
41
        [% END %]
42
        [% IF ( ERROR_password_too_weak ) %]
42
        [% IF ( ERROR_password_too_weak ) %]
43
            <li id="ERROR_weak_password">Password must contain at least one digit, one lowercase and one uppercase.</li>
43
            <li id="ERROR_weak_password">Password must contain at least one digit, one lowercase and one uppercase.</li>
Lines 63-71 Link Here
63
	<ol>
63
	<ol>
64
    <li><label for="newuserid">New username:</label>
64
    <li><label for="newuserid">New username:</label>
65
    <input type="hidden" name="member" value="[% patron.borrowernumber | html %]" /><input type="text" id="newuserid" name="newuserid" size="20" value="[% patron.userid | html %]" /></li>
65
    <input type="hidden" name="member" value="[% patron.borrowernumber | html %]" /><input type="text" id="newuserid" name="newuserid" size="20" value="[% patron.userid | html %]" /></li>
66
    [% SET password_pattern = ".{" _ Koha.Preference('minPasswordLength') _ ",}" %]
66
    [% SET password_pattern = ".{" _ patron.category.effective_min_password_length _ ",}" %]
67
    [% IF Koha.Preference('RequireStrongPassword') %]
67
    [% IF patron.category.effective_require_strong_password %]
68
        [% SET password_pattern = '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{' _ Koha.Preference('minPasswordLength') _ ',}' %]
68
        [% SET password_pattern = '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{' _ patron.category.effective_min_password_length _ ',}' %]
69
    [% END %]
69
    [% END %]
70
    <li>
70
    <li>
71
        <label for="newpassword">New password:</label>
71
        <label for="newpassword">New password:</label>
Lines 105-111 Link Here
105
        function generate_password() {
105
        function generate_password() {
106
            // Always generate a strong password
106
            // Always generate a strong password
107
            var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
107
            var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
108
            var length = [% Koha.Preference('minPasswordLength') | html %];
108
            var length = [% patron.category.effective_min_password_length | html %];
109
            if ( length < 8 ) length = 8;
109
            if ( length < 8 ) length = 8;
110
            var password='';
110
            var password='';
111
            for ( var i = 0 ; i < length ; i++){
111
            for ( var i = 0 ; i < length ; i++){
Lines 117-123 Link Here
117
            $("body").on('click', "#fillrandom",function(e) {
117
            $("body").on('click', "#fillrandom",function(e) {
118
                e.preventDefault();
118
                e.preventDefault();
119
                var password = '';
119
                var password = '';
120
                var pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% Koha.Preference('minPasswordLength') | html %],}/;
120
                var pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% patron.category.effective_min_password_length | html %],}/;
121
                while ( ! pattern_regex.test( password ) ) {
121
                while ( ! pattern_regex.test( password ) ) {
122
                    password = generate_password();
122
                    password = generate_password();
123
                }
123
                }
Lines 156-163 Link Here
156
            });
156
            });
157
        });
157
        });
158
    </script>
158
    </script>
159
    [% PROCESS 'password_check.inc' %]
159
    [% PROCESS 'password_check.inc' new_password => 'newpassword', minPasswordLength => patron.category.effective_min_password_length, RequireStrongPassword => patron.category.effective_require_strong_password %]
160
    [% PROCESS 'add_password_check' new_password => 'newpassword' %]
161
[% END %]
160
[% END %]
162
161
163
[% INCLUDE 'intranet-bottom.inc' %]
162
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt (-4 / +3 lines)
Lines 849-857 legend:hover { Link Here
849
                                                            [% IF ( typeloo.typename_X ) %]<optgroup label="Statistical">[% END %]
849
                                                            [% IF ( typeloo.typename_X ) %]<optgroup label="Statistical">[% END %]
850
                                                        [% END %]
850
                                                        [% END %]
851
                                                        [% IF ( categoryloo.categorycodeselected ) %]
851
                                                        [% IF ( categoryloo.categorycodeselected ) %]
852
                                                            <option value="[% categoryloo.categorycode | html %]" selected="selected" data-typename="[% typeloo.typename | html %]">[% categoryloo.categoryname | html %]</option>
852
                                                            <option value="[% categoryloo.categorycode | html %]" selected="selected" data-pwd-length="[% categoryloo.effective_min_password_length | html %]" data-pwd-strong="[% categoryloo.effective_require_strong_password | html %]" data-typename="[% typeloo.typename | html %]">[% categoryloo.categoryname | html %]</option>
853
                                                        [% ELSE %]
853
                                                        [% ELSE %]
854
                                                            <option value="[% categoryloo.categorycode | html %]" data-typename="[% typeloo.typename | html %]">[% categoryloo.categoryname | html %]</option>
854
                                                            <option value="[% categoryloo.categorycode | html %]" data-pwd-length="[% categoryloo.effective_min_password_length | html %]" data-pwd-strong="[% categoryloo.effective_require_strong_password | html %]" data-typename="[% typeloo.typename | html %]">[% categoryloo.categoryname | html %]</option>
855
                                                        [% END %]
855
                                                        [% END %]
856
                                                        [% IF ( loop.last ) %]
856
                                                        [% IF ( loop.last ) %]
857
                                                            </optgroup>
857
                                                            </optgroup>
Lines 1749-1756 legend:hover { Link Here
1749
    </script>
1749
    </script>
1750
    [% Asset.js("js/members.js") | $raw %]
1750
    [% Asset.js("js/members.js") | $raw %]
1751
    [% Asset.js("js/messaging-preference-form.js") | $raw %]
1751
    [% Asset.js("js/messaging-preference-form.js") | $raw %]
1752
    [% PROCESS 'password_check.inc' %]
1752
    [% 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 %]
1753
    [% PROCESS 'add_password_check' new_password => 'password' %]
1754
[% END %]
1753
[% END %]
1755
1754
1756
[% INCLUDE 'intranet-bottom.inc' %]
1755
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/onboarding/onboardingstep3.tt (-3 / +2 lines)
Lines 61-67 Link Here
61
                                <label for="categorycode_entry" class="required"> Patron category</label>
61
                                <label for="categorycode_entry" class="required"> Patron category</label>
62
                                <select id="categorycode_entry" name="categorycode_entry">
62
                                <select id="categorycode_entry" name="categorycode_entry">
63
                                    [% FOREACH category IN categories %]
63
                                    [% FOREACH category IN categories %]
64
                                        <option value = "[% category.categorycode | html %]">[% category.description | html %]</option>
64
                                        <option value = "[% category.categorycode | html %]" data-pwd-length="[% category.effective_min_password_length | html %]" data-pwd-strong="[% category.effective_require_strong_password | html %]">[% category.description | html %]</option>
65
                                    [% END %]
65
                                    [% END %]
66
                                </select>
66
                                </select>
67
                                <span class="required">Required</span><br><br>
67
                                <span class="required">Required</span><br><br>
Lines 116-123 Link Here
116
    [% INCLUDE 'validator-strings.inc' %]
116
    [% INCLUDE 'validator-strings.inc' %]
117
    [% INCLUDE 'installer-strings.inc' %]
117
    [% INCLUDE 'installer-strings.inc' %]
118
    [% Asset.js("js/onboarding.js") | $raw %]
118
    [% Asset.js("js/onboarding.js") | $raw %]
119
    [% PROCESS 'password_check.inc' %]
119
    [% PROCESS 'password_check.inc' new_password => 'password', category_selector => '#categorycode_entry', RequireStrongPassword => Koha.Preference('RequireStrongPassword') %]
120
    [% PROCESS 'add_password_check' new_password => 'password' %]
121
[% END %]
120
[% END %]
122
121
123
[% INCLUDE 'installer-intranet-bottom.inc' %]
122
[% INCLUDE 'installer-intranet-bottom.inc' %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/password_check.inc (-14 / +30 lines)
Lines 1-25 Link Here
1
[% USE Koha %]
1
[% USE Koha %]
2
[% BLOCK add_password_check %]
3
<script>
2
<script>
4
    var pwd_title = "";
3
    var pwd_title = "";
5
    var pattern_title = "";
4
    var pattern_title = "";
6
    var new_password_node_name = "[% new_password | html %]";
5
    var new_password_node_name = "[% new_password | html %]";
7
    [% IF Koha.Preference('RequireStrongPassword') %]
6
    var category_selector = "[% category_selector | html %]";
8
        pwd_title = _("Password must contain at least %s characters, including UPPERCASE, lowercase and numbers").format([% minPasswordLength | html %]);
7
    var STRONG_MSG = _("Password must contain at least %s characters, including UPPERCASE, lowercase and numbers");
9
        pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% minPasswordLength | html %],}/;
8
    var WEAK_MSG = _("Password must contain at least %s characters");
10
    [% ELSIF minPasswordLength %]
9
11
        pwd_title = _("Password must contain at least %s characters").format([% minPasswordLength | html %]);
10
    if(category_selector && $('select'+category_selector).length) {
12
        pattern_regex = /.{[% minPasswordLength | html %],}/;
11
        jQuery.validator.addMethod("password_strong", function(value, element){
13
    [% END %]
12
            var require_strong = $('select'+category_selector+' option:selected').data('pwdStrong');
14
    jQuery.validator.addMethod("password_strong", function(value, element){
13
            var min_lenght = $('select'+category_selector+' option:selected').data('pwdLength');
15
        return this.optional(element) || pattern_regex.test(value);
14
            var regex_text = require_strong?"(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{"+min_lenght+",}":".{"+min_lenght+",}";
16
    }, pwd_title);
15
            var pattern_regex = new RegExp(regex_text);
16
            return this.optional(element) || pattern_regex.test(value);
17
        }, function(unused, element) {
18
            var require_strong = $('select'+category_selector+' option:selected').data('pwdStrong');
19
            var min_lenght = $('select'+category_selector+' option:selected').data('pwdLength');
20
            return (require_strong?STRONG_MSG:WEAK_MSG).format(min_lenght)
21
        });
22
    } else {
23
        [% IF RequireStrongPassword %]
24
            pwd_title = STRONG_MSG.format([% minPasswordLength | html %]);
25
            pattern_regex = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{[% minPasswordLength | html %],}/;
26
        [% ELSIF minPasswordLength %]
27
            pwd_title = WEAK_MSG.format([% minPasswordLength | html %]);
28
            pattern_regex = /.{[% minPasswordLength | html %],}/;
29
        [% END %]
30
        jQuery.validator.addMethod("password_strong", function(value, element){
31
            return this.optional(element) || value == '****' || pattern_regex.test(value);
32
        }, pwd_title);
33
    }
17
    jQuery.validator.addMethod("password_no_spaces", function(value, element){
34
    jQuery.validator.addMethod("password_no_spaces", function(value, element){
18
        return ( this.optional(element) || !value.match(/^\s/) && !value.match(/\s$/) );
35
        return ( this.optional(element) || !value.match(/^\s/) && !value.match(/\s$/) );
19
    }, _("Password contains leading and/or trailing spaces"));
36
    }, _("Password contains leading and/or trailing spaces"));
20
    jQuery.validator.addMethod("password_match", function(value, element){
37
    jQuery.validator.addMethod("password_match", function(value, element){
21
        var new_password_node = $("input[name='" + new_password_node_name + "']:first");
38
        var new_password_node = $("input[name='" + new_password_node_name + "']:first");
22
        return this.optional(element) || value == $(new_password_node).val();
39
        return value == $(new_password_node).val();
23
    }, _("Please enter the same password as above"));
40
    }, _("Please enter the same password as above"));
24
</script>
41
</script>
25
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt (-9 / +27 lines)
Lines 82-88 Link Here
82
                                [% IF field == "B_email" %]<li>Alternate address information: <a href="#borrower_B_email">email address</a></li>[% END %]
82
                                [% IF field == "B_email" %]<li>Alternate address information: <a href="#borrower_B_email">email address</a></li>[% END %]
83
                                [% IF field == "password_match" %]<li>Passwords do not match! <a href="#password">password</a></li>[% END %]
83
                                [% IF field == "password_match" %]<li>Passwords do not match! <a href="#password">password</a></li>[% END %]
84
                                [% IF field == "password_too_short" %]
84
                                [% IF field == "password_too_short" %]
85
                                    <li>Password must be at least [% minPasswordLength | html %] characters long.</li>
85
                                    <li>Password must be at least [% patron.category.effective_min_password_length | html %] characters long.</li>
86
                                [% END %]
86
                                [% END %]
87
                                [% IF field == "password_too_weak" %]
87
                                [% IF field == "password_too_weak" %]
88
                                    <li>Password must contain at least one digit, one lowercase and one uppercase.</li>
88
                                    <li>Password must contain at least one digit, one lowercase and one uppercase.</li>
Lines 252-260 Link Here
252
                                            <select id="borrower_categorycode" name="borrower_categorycode">
252
                                            <select id="borrower_categorycode" name="borrower_categorycode">
253
                                                [% FOREACH c IN Categories.all() %]
253
                                                [% FOREACH c IN Categories.all() %]
254
                                                    [% IF c.categorycode == Koha.Preference('PatronSelfRegistrationDefaultCategory') %]
254
                                                    [% IF c.categorycode == Koha.Preference('PatronSelfRegistrationDefaultCategory') %]
255
                                                        <option value="[% c.categorycode | html %]" selected="selected">[% c.description | html %]</option>
255
                                                        <option value="[% c.categorycode | html %]" data-pwd-length="[% c.effective_min_password_length | html %]" data-pwd-strong="[% c.effective_require_strong_password | html %]" selected="selected">[% c.description | html %]</option>
256
                                                    [% ELSE %]
256
                                                    [% ELSE %]
257
                                                        <option value="[% c.categorycode | html %]">[% c.description | html %]</option>
257
                                                        <option value="[% c.categorycode | html %]" data-pwd-length="[% c.effective_min_password_length | html %]" data-pwd-strong="[% c.effective_require_strong_password | html %]">[% c.description | html %]</option>
258
                                                    [% END %]
258
                                                    [% END %]
259
                                                [% END %]
259
                                                [% END %]
260
                                            </select>
260
                                            </select>
Lines 858-867 Link Here
858
                    <fieldset class="rows" id="memberentry_password">
858
                    <fieldset class="rows" id="memberentry_password">
859
                        <legend id="contact_legend">Password</legend>
859
                        <legend id="contact_legend">Password</legend>
860
                        <div class="alert alert-info">
860
                        <div class="alert alert-info">
861
                            [% IF ( Koha.Preference('RequireStrongPassword') ) %]
861
                            [% IF patron %]
862
                                <p>Your password must contain at least [% Koha.Preference('minPasswordLength') | html %] characters, including UPPERCASE, lowercase and numbers.</p>
862
                                [% IF ( patron.category.effective_require_strong_password ) %]
863
                                    <p>Your password must contain at least [% patron.category.effective_min_password_length | html %] characters, including UPPERCASE, lowercase and numbers.</p>
864
                                [% ELSE %]
865
                                    <p>Your password must be at least [% patron.category.effective_min_password_length | html %] characters long.</p>
866
                                [% END %]
863
                            [% ELSE %]
867
                            [% ELSE %]
864
                                <p>Your password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.</p>
868
                                <p id="password_alert"></p>
865
                            [% END %]
869
                            [% END %]
866
                            [% UNLESS mandatory.defined('password') %]
870
                            [% UNLESS mandatory.defined('password') %]
867
                                <p>If you do not enter a password a system generated password will be created.</p>
871
                                <p>If you do not enter a password a system generated password will be created.</p>
Lines 1008-1015 Link Here
1008
[% INCLUDE 'opac-bottom.inc' %]
1012
[% INCLUDE 'opac-bottom.inc' %]
1009
[% BLOCK jsinclude %]
1013
[% BLOCK jsinclude %]
1010
    [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
1014
    [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
1011
    [% PROCESS 'password_check.inc' %]
1015
1012
    [% PROCESS 'add_password_check' new_password => 'borrower_password' %]
1013
    <script>
1016
    <script>
1014
        //<![CDATA[
1017
        //<![CDATA[
1015
        $(document).ready(function() {
1018
        $(document).ready(function() {
Lines 1170-1176 Link Here
1170
            });
1173
            });
1171
        });
1174
        });
1172
    [% END %]
1175
    [% END %]
1176
1177
    [% UNLESS patron %]
1178
        var PWD_STRONG_MSG = _("Password must contain at least %s characters, including UPPERCASE, lowercase and numbers");
1179
        var PWD_WEAK_MSG = _("Password must contain at least %s characters");
1180
        $(document).ready(function() {
1181
            var setPwdMessage = function() {
1182
                var require_strong = $('select#borrower_categorycode option:selected').data('pwdStrong');
1183
                var min_lenght = $('select#borrower_categorycode option:selected').data('pwdLength');
1184
                $('#password_alert').html((require_strong?PWD_STRONG_MSG:PWD_WEAK_MSG).format(min_lenght));
1185
            };
1186
            setPwdMessage();
1187
            $('select#borrower_categorycode').change(setPwdMessage);
1188
        });
1189
    [% END %]
1173
    //]]>
1190
    //]]>
1174
    </script>
1191
    </script>
1175
[% INCLUDE 'calendar.inc' %]
1192
    [% 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 %]
1193
1176
[% END %]
1194
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-passwd.tt (-5 / +4 lines)
Lines 57-66 Link Here
57
57
58
                            <form action="/cgi-bin/koha/opac-passwd.pl" name="mainform" id="mainform" method="post" autocomplete="off">
58
                            <form action="/cgi-bin/koha/opac-passwd.pl" name="mainform" id="mainform" method="post" autocomplete="off">
59
                                <fieldset>
59
                                <fieldset>
60
                                    [% IF ( Koha.Preference('RequireStrongPassword') ) %]
60
                                    [% IF ( logged_in_user.category.effective_require_strong_password ) %]
61
                                        <div class="alert alert-info">Your password must contain at least [% Koha.Preference('minPasswordLength') | html %] characters, including UPPERCASE, lowercase and numbers.</div>
61
                                        <div class="alert alert-info">Your password must contain at least [% logged_in_user.category.effective_min_password_length | html %] characters, including UPPERCASE, lowercase and numbers.</div>
62
                                    [% ELSE %]
62
                                    [% ELSE %]
63
                                        <div class="alert alert-info">Your password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.</div>
63
                                        <div class="alert alert-info">Your password must be at least [% logged_in_user.category.effective_min_password_length | html %] characters long.</div>
64
                                    [% END %]
64
                                    [% END %]
65
                                    <label for="Oldkey">Current password:</label> <input type="password" id="Oldkey" size="25"  name="Oldkey" />
65
                                    <label for="Oldkey">Current password:</label> <input type="password" id="Oldkey" size="25"  name="Oldkey" />
66
                                    <label for="Newkey">New password:</label> <input type="password" id="Newkey"  size="25"  name="Newkey" />
66
                                    <label for="Newkey">New password:</label> <input type="password" id="Newkey"  size="25"  name="Newkey" />
Lines 94-101 Link Here
94
[% INCLUDE 'opac-bottom.inc' %]
94
[% INCLUDE 'opac-bottom.inc' %]
95
[% BLOCK jsinclude %]
95
[% BLOCK jsinclude %]
96
    [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
96
    [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
97
    [% PROCESS 'password_check.inc' %]
97
    [% 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 %]
98
    [% PROCESS 'add_password_check' new_password => 'Newkey' %]
99
    <script>
98
    <script>
100
        $(document).ready(function() {
99
        $(document).ready(function() {
101
            $("#mainform").validate({
100
            $("#mainform").validate({
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-password-recovery.tt (-3 / +3 lines)
Lines 107-116 Link Here
107
                    <form action="/cgi-bin/koha/opac-password-recovery.pl" method="post" autocomplete="off">
107
                    <form action="/cgi-bin/koha/opac-password-recovery.pl" method="post" autocomplete="off">
108
                        <input type="hidden" name="koha_login_context" value="opac" />
108
                        <input type="hidden" name="koha_login_context" value="opac" />
109
                        <fieldset>
109
                        <fieldset>
110
                            [% IF ( Koha.Preference('RequireStrongPassword') ) %]
110
                            [% IF ( RequireStrongPassword ) %]
111
                                <div class="alert alert-info">Your password must contain at least [% Koha.Preference('minPasswordLength') | html %] characters, including UPPERCASE, lowercase and numbers.</div>
111
                                <div class="alert alert-info">Your password must contain at least [% minPasswordLength | html %] characters, including UPPERCASE, lowercase and numbers.</div>
112
                            [% ELSE %]
112
                            [% ELSE %]
113
                                <div class="alert alert-info">Your password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.</div>
113
                                <div class="alert alert-info">Your password must be at least [% minPasswordLength | html %] characters long.</div>
114
                            [% END %]
114
                            [% END %]
115
                            <label for="password">New password:</label>
115
                            <label for="password">New password:</label>
116
                            <input type="password" id="password" size="40" name="password" />
116
                            <input type="password" id="password" size="40" name="password" />
(-)a/members/memberentry.pl (-2 / +4 lines)
Lines 368-374 if ($op eq 'save' || $op eq 'insert'){ Link Here
368
  # the edited values list when editing certain sub-forms. Get it straight
368
  # the edited values list when editing certain sub-forms. Get it straight
369
  # from the DB if absent.
369
  # from the DB if absent.
370
  my $userid = $newdata{ userid } // $borrower_data->{ userid };
370
  my $userid = $newdata{ userid } // $borrower_data->{ userid };
371
  my $p = $borrowernumber ? Koha::Patrons->find( $borrowernumber ) : Koha::Patron->new;
371
  my $p = $borrowernumber ? Koha::Patrons->find( $borrowernumber ) : Koha::Patron->new();
372
  $p->userid( $userid );
372
  $p->userid( $userid );
373
  unless ( $p->has_valid_userid ) {
373
  unless ( $p->has_valid_userid ) {
374
    push @errors, "ERROR_login_exist";
374
    push @errors, "ERROR_login_exist";
Lines 379-385 if ($op eq 'save' || $op eq 'insert'){ Link Here
379
  push @errors, "ERROR_password_mismatch" if ( $password ne $password2 );
379
  push @errors, "ERROR_password_mismatch" if ( $password ne $password2 );
380
380
381
  if ( $password and $password ne '****' ) {
381
  if ( $password and $password ne '****' ) {
382
      my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password );
382
      my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password, Koha::Patron::Categories->find($categorycode) );
383
      unless ( $is_valid ) {
383
      unless ( $is_valid ) {
384
          push @errors, 'ERROR_password_too_short' if $error eq 'too_short';
384
          push @errors, 'ERROR_password_too_short' if $error eq 'too_short';
385
          push @errors, 'ERROR_password_too_weak' if $error eq 'too_weak';
385
          push @errors, 'ERROR_password_too_weak' if $error eq 'too_weak';
Lines 668-673 foreach my $category_type (qw(C A S P I X)) { Link Here
668
        push @categoryloop,
668
        push @categoryloop,
669
          { 'categorycode' => $patron_category->categorycode,
669
          { 'categorycode' => $patron_category->categorycode,
670
            'categoryname' => $patron_category->description,
670
            'categoryname' => $patron_category->description,
671
            'effective_min_password_length' => $patron_category->effective_min_password_length,
672
            'effective_require_strong_password' => $patron_category->effective_require_strong_password,
671
            'categorycodeselected' =>
673
            'categorycodeselected' =>
672
              ( defined($categorycode) && $patron_category->categorycode eq $categorycode ),
674
              ( defined($categorycode) && $patron_category->categorycode eq $categorycode ),
673
          };
675
          };
(-)a/misc/admin/set_password.pl (-5 / +8 lines)
Lines 44-54 unless ( $userid or $patron_id or $cardnumber ) { Link Here
44
    pod2usage("cardnumber is mandatory")   unless $cardnumber;
44
    pod2usage("cardnumber is mandatory")   unless $cardnumber;
45
}
45
}
46
46
47
unless ($password) {
48
    my $generator  = String::Random->new( rand_gen => \&alt_rand );
49
    $password      = $generator->randregex('[A-Za-z][A-Za-z0-9_]{6}.[A-Za-z][A-Za-z0-9_]{6}\d');
50
}
51
52
my $filter;
47
my $filter;
53
48
54
if ( $userid ) {
49
if ( $userid ) {
Lines 70-75 unless ( $patrons->count > 0 ) { Link Here
70
}
65
}
71
66
72
my $patron = $patrons->next;
67
my $patron = $patrons->next;
68
69
unless ($password) {
70
    my $generator  = String::Random->new( rand_gen => \&alt_rand );
71
    my $n = $patron->category->effective_min_password_length;
72
    $n = $n<6?6:$n;
73
    $password      = $generator->randregex('[A-Za-z][A-Za-z0-9_]{6}.[A-Za-z][A-Za-z0-9_]{'.$n.'}\d');
74
}
75
73
$patron->set_password({ password => $password, skip_validation => 1 });
76
$patron->set_password({ password => $password, skip_validation => 1 });
74
77
75
print $patron->userid . " " . $password . "\n";
78
print $patron->userid . " " . $password . "\n";
(-)a/opac/opac-memberentry.pl (-4 / +4 lines)
Lines 41-47 use Koha::Patron::Attributes; Link Here
41
use Koha::Patron::Images;
41
use Koha::Patron::Images;
42
use Koha::Patron::Modification;
42
use Koha::Patron::Modification;
43
use Koha::Patron::Modifications;
43
use Koha::Patron::Modifications;
44
use Koha::Patrons;
44
use Koha::Patron::Categories;
45
use Koha::Token;
45
use Koha::Token;
46
46
47
my $cgi = new CGI;
47
my $cgi = new CGI;
Lines 177-183 if ( $action eq 'create' ) { Link Here
177
                $verification_token = md5_hex( time().{}.rand().{}.$$ );
177
                $verification_token = md5_hex( time().{}.rand().{}.$$ );
178
            }
178
            }
179
179
180
            $borrower{password}          = Koha::AuthUtils::generate_password unless $borrower{password};
180
            $borrower{password}          = Koha::AuthUtils::generate_password(Koha::Patron::Categories->find($borrower{categorycode})) unless $borrower{password};
181
            $borrower{verification_token} = $verification_token;
181
            $borrower{verification_token} = $verification_token;
182
182
183
            Koha::Patron::Modification->new( \%borrower )->store();
183
            Koha::Patron::Modification->new( \%borrower )->store();
Lines 216-222 if ( $action eq 'create' ) { Link Here
216
            );
216
            );
217
217
218
            $borrower{categorycode}     ||= C4::Context->preference('PatronSelfRegistrationDefaultCategory');
218
            $borrower{categorycode}     ||= C4::Context->preference('PatronSelfRegistrationDefaultCategory');
219
            $borrower{password}         ||= Koha::AuthUtils::generate_password;
219
            $borrower{password}         ||= Koha::AuthUtils::generate_password(Koha::Patron::Categories->find($borrower{categorycode}));
220
            my $consent_dt = delete $borrower{gdpr_proc_consent};
220
            my $consent_dt = delete $borrower{gdpr_proc_consent};
221
            my $patron = Koha::Patron->new( \%borrower )->store;
221
            my $patron = Koha::Patron->new( \%borrower )->store;
222
            Koha::Patron::Consent->new({ borrowernumber => $patron->borrowernumber, type => 'GDPR_PROCESSING', given_on => $consent_dt })->store if $consent_dt;
222
            Koha::Patron::Consent->new({ borrowernumber => $patron->borrowernumber, type => 'GDPR_PROCESSING', given_on => $consent_dt })->store if $consent_dt;
Lines 467-473 sub CheckForInvalidFields { Link Here
467
        push( @invalidFields, "password_match" );
467
        push( @invalidFields, "password_match" );
468
    }
468
    }
469
    if ( $borrower->{'password'} ) {
469
    if ( $borrower->{'password'} ) {
470
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $borrower->{password} );
470
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $borrower->{password}, Koha::Patron::Categories->find($borrower->{categorycode}) );
471
          unless ( $is_valid ) {
471
          unless ( $is_valid ) {
472
              push @invalidFields, 'password_too_short' if $error eq 'too_short';
472
              push @invalidFields, 'password_too_short' if $error eq 'too_short';
473
              push @invalidFields, 'password_too_weak' if $error eq 'too_weak';
473
              push @invalidFields, 'password_too_weak' if $error eq 'too_weak';
(-)a/opac/opac-password-recovery.pl (-2 / +13 lines)
Lines 54-60 if ( $query->param('sendEmail') || $query->param('resendEmail') ) { Link Here
54
    #try with the main email
54
    #try with the main email
55
    my $borrower;
55
    my $borrower;
56
    my $search_results;
56
    my $search_results;
57
58
    # Find the borrower by userid, card number, or email
57
    # Find the borrower by userid, card number, or email
59
    if ($username) {
58
    if ($username) {
60
        $search_results = Koha::Patrons->search( { -or => { userid => $username, cardnumber => $username }, login_attempts => { '!=', Koha::Patron::ADMINISTRATIVE_LOCKOUT } } );
59
        $search_results = Koha::Patrons->search( { -or => { userid => $username, cardnumber => $username }, login_attempts => { '!=', Koha::Patron::ADMINISTRATIVE_LOCKOUT } } );
Lines 124-129 if ( $query->param('sendEmail') || $query->param('resendEmail') ) { Link Here
124
    if ($hasError) {
123
    if ($hasError) {
125
        $template->param(
124
        $template->param(
126
            hasError                => 1,
125
            hasError                => 1,
126
127
            errNoBorrowerFound      => $errNoBorrowerFound,
127
            errNoBorrowerFound      => $errNoBorrowerFound,
128
            errTooManyEmailFound    => $errTooManyEmailFound,
128
            errTooManyEmailFound    => $errTooManyEmailFound,
129
            errAlreadyStartRecovery => $errAlreadyStartRecovery,
129
            errAlreadyStartRecovery => $errAlreadyStartRecovery,
Lines 154-166 elsif ( $query->param('passwordReset') ) { Link Here
154
    ( $borrower_number, $username ) = GetValidLinkInfo($uniqueKey);
154
    ( $borrower_number, $username ) = GetValidLinkInfo($uniqueKey);
155
155
156
    my $error;
156
    my $error;
157
    my $min_password_length = C4::Context->preference('minPasswordPreference');
158
    my $require_strong_password = C4::Context->preference('RequireStrongPassword');
157
    if ( not $borrower_number ) {
159
    if ( not $borrower_number ) {
158
        $error = 'errLinkNotValid';
160
        $error = 'errLinkNotValid';
159
    } elsif ( $password ne $repeatPassword ) {
161
    } elsif ( $password ne $repeatPassword ) {
160
        $error = 'errPassNotMatch';
162
        $error = 'errPassNotMatch';
161
    } else {
163
    } else {
164
        my $borrower = Koha::Patrons->find($borrower_number);
165
        $min_password_length = $borrower->category->effective_min_password_length;
166
        $require_strong_password = $borrower->category->effective_require_strong_password;
162
        try {
167
        try {
163
            Koha::Patrons->find($borrower_number)->set_password({ password => $password });
168
            $borrower->set_password({ password => $password });
164
169
165
            CompletePasswordRecovery($uniqueKey);
170
            CompletePasswordRecovery($uniqueKey);
166
            $template->param(
171
            $template->param(
Lines 187-192 elsif ( $query->param('passwordReset') ) { Link Here
187
            uniqueKey    => $uniqueKey,
192
            uniqueKey    => $uniqueKey,
188
            hasError     => 1,
193
            hasError     => 1,
189
            $error       => 1,
194
            $error       => 1,
195
            minPasswordLength => $min_password_length,
196
            RequireStrongPassword => $require_strong_password
190
        );
197
        );
191
    }
198
    }
192
}
199
}
Lines 198-203 elsif ($uniqueKey) { #reset password form Link Here
198
        $errLinkNotValid = 1;
205
        $errLinkNotValid = 1;
199
    }
206
    }
200
207
208
    my $borrower = Koha::Patrons->find($borrower_number);
209
201
    $template->param(
210
    $template->param(
202
        new_password    => 1,
211
        new_password    => 1,
203
        email           => $email,
212
        email           => $email,
Lines 205-210 elsif ($uniqueKey) { #reset password form Link Here
205
        username        => $username,
214
        username        => $username,
206
        errLinkNotValid => $errLinkNotValid,
215
        errLinkNotValid => $errLinkNotValid,
207
        hasError        => ( $errLinkNotValid ? 1 : 0 ),
216
        hasError        => ( $errLinkNotValid ? 1 : 0 ),
217
        minPasswordLength => $borrower->category->effective_min_password_length,
218
        RequireStrongPassword => $borrower->category->effective_require_strong_password
208
    );
219
    );
209
}
220
}
210
else {    #password recovery form (to send email)
221
else {    #password recovery form (to send email)
(-)a/opac/opac-registration-verify.pl (-2 / +2 lines)
Lines 27-32 use Koha::AuthUtils; Link Here
27
use Koha::Patrons;
27
use Koha::Patrons;
28
use Koha::Patron::Consent;
28
use Koha::Patron::Consent;
29
use Koha::Patron::Modifications;
29
use Koha::Patron::Modifications;
30
use Koha::Patron::Categories;
30
31
31
my $cgi = new CGI;
32
my $cgi = new CGI;
32
my $dbh = C4::Context->dbh;
33
my $dbh = C4::Context->dbh;
Lines 59-65 if ( Link Here
59
    );
60
    );
60
61
61
    my $patron_attrs = $m->unblessed;
62
    my $patron_attrs = $m->unblessed;
62
    $patron_attrs->{password} ||= Koha::AuthUtils::generate_password;
63
    $patron_attrs->{password} ||= Koha::AuthUtils::generate_password(Koha::Patron::Categories->find($patron_attrs->{categorycode}));
63
    my $consent_dt = delete $patron_attrs->{gdpr_proc_consent};
64
    my $consent_dt = delete $patron_attrs->{gdpr_proc_consent};
64
    $patron_attrs->{categorycode} ||= C4::Context->preference('PatronSelfRegistrationDefaultCategory');
65
    $patron_attrs->{categorycode} ||= C4::Context->preference('PatronSelfRegistrationDefaultCategory');
65
    delete $patron_attrs->{timestamp};
66
    delete $patron_attrs->{timestamp};
66
- 

Return to bug 23816