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 726-736 sub set_password { Link Here
726
    my $password = $args->{password};
726
    my $password = $args->{password};
727
727
728
    unless ( $args->{skip_validation} ) {
728
    unless ( $args->{skip_validation} ) {
729
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password );
729
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password, $self->category );
730
730
731
        if ( !$is_valid ) {
731
        if ( !$is_valid ) {
732
            if ( $error eq 'too_short' ) {
732
            if ( $error eq 'too_short' ) {
733
                my $min_length = C4::Context->preference('minPasswordLength');
733
                my $min_length = $self->category->effective_min_password_length;
734
                $min_length = 3 if not $min_length or $min_length < 3;
734
                $min_length = 3 if not $min_length or $min_length < 3;
735
735
736
                my $password_length = length($password);
736
                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 (-13 / +29 lines)
Lines 1-19 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) || value == '****' || 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"));
Lines 21-25 Link Here
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 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/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 825-833 legend:hover { Link Here
825
                                                            [% IF ( typeloo.typename_X ) %]<optgroup label="Statistical">[% END %]
825
                                                            [% IF ( typeloo.typename_X ) %]<optgroup label="Statistical">[% END %]
826
                                                        [% END %]
826
                                                        [% END %]
827
                                                        [% IF ( categoryloo.categorycodeselected ) %]
827
                                                        [% IF ( categoryloo.categorycodeselected ) %]
828
                                                            <option value="[% categoryloo.categorycode | html %]" selected="selected" data-typename="[% typeloo.typename | html %]">[% categoryloo.categoryname | html %]</option>
828
                                                            <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>
829
                                                        [% ELSE %]
829
                                                        [% ELSE %]
830
                                                            <option value="[% categoryloo.categorycode | html %]" data-typename="[% typeloo.typename | html %]">[% categoryloo.categoryname | html %]</option>
830
                                                            <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>
831
                                                        [% END %]
831
                                                        [% END %]
832
                                                        [% IF ( loop.last ) %]
832
                                                        [% IF ( loop.last ) %]
833
                                                            </optgroup>
833
                                                            </optgroup>
Lines 1714-1721 legend:hover { Link Here
1714
    </script>
1714
    </script>
1715
    [% Asset.js("js/members.js") | $raw %]
1715
    [% Asset.js("js/members.js") | $raw %]
1716
    [% Asset.js("js/messaging-preference-form.js") | $raw %]
1716
    [% Asset.js("js/messaging-preference-form.js") | $raw %]
1717
    [% PROCESS 'password_check.inc' %]
1717
    [% 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 %]
1718
    [% PROCESS 'add_password_check' new_password => 'password' %]
1719
[% END %]
1718
[% END %]
1720
1719
1721
[% INCLUDE 'intranet-bottom.inc' %]
1720
[% 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 / +9 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 249-257 Link Here
249
                                            <select id="borrower_categorycode" name="borrower_categorycode">
249
                                            <select id="borrower_categorycode" name="borrower_categorycode">
250
                                                [% FOREACH c IN Categories.all() %]
250
                                                [% FOREACH c IN Categories.all() %]
251
                                                    [% IF c.categorycode == Koha.Preference('PatronSelfRegistrationDefaultCategory') %]
251
                                                    [% IF c.categorycode == Koha.Preference('PatronSelfRegistrationDefaultCategory') %]
252
                                                        <option value="[% c.categorycode | html %]" selected="selected">[% c.description | html %]</option>
252
                                                        <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>
253
                                                    [% ELSE %]
253
                                                    [% ELSE %]
254
                                                        <option value="[% c.categorycode | html %]">[% c.description | html %]</option>
254
                                                        <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>
255
                                                    [% END %]
255
                                                    [% END %]
256
                                                [% END %]
256
                                                [% END %]
257
                                            </select>
257
                                            </select>
Lines 842-851 Link Here
842
                    <fieldset class="rows" id="memberentry_password">
842
                    <fieldset class="rows" id="memberentry_password">
843
                        <legend id="contact_legend">Password</legend>
843
                        <legend id="contact_legend">Password</legend>
844
                        <div class="alert alert-info">
844
                        <div class="alert alert-info">
845
                            [% IF ( Koha.Preference('RequireStrongPassword') ) %]
845
                            [% IF ( patron.category.effective_require_strong_password ) %]
846
                                <p>Your password must contain at least [% Koha.Preference('minPasswordLength') | html %] characters, including UPPERCASE, lowercase and numbers.</p>
846
                                <p>Your password must contain at least [% patron.category.effective_min_password_length | html %] characters, including UPPERCASE, lowercase and numbers.</p>
847
                            [% ELSE %]
847
                            [% ELSE %]
848
                                <p>Your password must be at least [% Koha.Preference('minPasswordLength') | html %] characters long.</p>
848
                                <p>Your password must be at least [% patron.category.effective_min_password_length | html %] characters long.</p>
849
                            [% END %]
849
                            [% END %]
850
                            [% UNLESS mandatory.defined('password') %]
850
                            [% UNLESS mandatory.defined('password') %]
851
                                <p>If you do not enter a password a system generated password will be created.</p>
851
                                <p>If you do not enter a password a system generated password will be created.</p>
Lines 985-992 Link Here
985
[% INCLUDE 'opac-bottom.inc' %]
985
[% INCLUDE 'opac-bottom.inc' %]
986
[% BLOCK jsinclude %]
986
[% BLOCK jsinclude %]
987
    [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
987
    [% Asset.js("lib/jquery/plugins/jquery.validate.min.js") | $raw %]
988
    [% PROCESS 'password_check.inc' %]
988
989
    [% PROCESS 'add_password_check' new_password => 'borrower_password' %]
990
    <script>
989
    <script>
991
        //<![CDATA[
990
        //<![CDATA[
992
        $(document).ready(function() {
991
        $(document).ready(function() {
Lines 1131-1135 Link Here
1131
    });
1130
    });
1132
    //]]>
1131
    //]]>
1133
    </script>
1132
    </script>
1134
[% INCLUDE 'calendar.inc' %]
1133
    [% 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 %]
1134
1135
[% END %]
1135
[% 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 363-369 if ($op eq 'save' || $op eq 'insert'){ Link Here
363
  # the edited values list when editing certain sub-forms. Get it straight
363
  # the edited values list when editing certain sub-forms. Get it straight
364
  # from the DB if absent.
364
  # from the DB if absent.
365
  my $userid = $newdata{ userid } // $borrower_data->{ userid };
365
  my $userid = $newdata{ userid } // $borrower_data->{ userid };
366
  my $p = $borrowernumber ? Koha::Patrons->find( $borrowernumber ) : Koha::Patron->new;
366
  my $p = $borrowernumber ? Koha::Patrons->find( $borrowernumber ) : Koha::Patron->new();
367
  $p->userid( $userid );
367
  $p->userid( $userid );
368
  unless ( $p->has_valid_userid ) {
368
  unless ( $p->has_valid_userid ) {
369
    push @errors, "ERROR_login_exist";
369
    push @errors, "ERROR_login_exist";
Lines 374-380 if ($op eq 'save' || $op eq 'insert'){ Link Here
374
  push @errors, "ERROR_password_mismatch" if ( $password ne $password2 );
374
  push @errors, "ERROR_password_mismatch" if ( $password ne $password2 );
375
375
376
  if ( $password and $password ne '****' ) {
376
  if ( $password and $password ne '****' ) {
377
      my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password );
377
      my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $password, Koha::Patron::Categories->find($categorycode) );
378
      unless ( $is_valid ) {
378
      unless ( $is_valid ) {
379
          push @errors, 'ERROR_password_too_short' if $error eq 'too_short';
379
          push @errors, 'ERROR_password_too_short' if $error eq 'too_short';
380
          push @errors, 'ERROR_password_too_weak' if $error eq 'too_weak';
380
          push @errors, 'ERROR_password_too_weak' if $error eq 'too_weak';
Lines 644-649 foreach my $category_type (qw(C A S P I X)) { Link Here
644
        push @categoryloop,
644
        push @categoryloop,
645
          { 'categorycode' => $patron_category->categorycode,
645
          { 'categorycode' => $patron_category->categorycode,
646
            'categoryname' => $patron_category->description,
646
            'categoryname' => $patron_category->description,
647
            'effective_min_password_length' => $patron_category->effective_min_password_length,
648
            'effective_require_strong_password' => $patron_category->effective_require_strong_password,
647
            'categorycodeselected' =>
649
            'categorycodeselected' =>
648
              ( defined($categorycode) && $patron_category->categorycode eq $categorycode ),
650
              ( defined($categorycode) && $patron_category->categorycode eq $categorycode ),
649
          };
651
          };
(-)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 175-181 if ( $action eq 'create' ) { Link Here
175
                $verification_token = md5_hex( time().{}.rand().{}.$$ );
175
                $verification_token = md5_hex( time().{}.rand().{}.$$ );
176
            }
176
            }
177
177
178
            $borrower{password}          = Koha::AuthUtils::generate_password unless $borrower{password};
178
            $borrower{password}          = Koha::AuthUtils::generate_password(Koha::Patron::Categories->find($borrower{categorycode})) unless $borrower{password};
179
            $borrower{verification_token} = $verification_token;
179
            $borrower{verification_token} = $verification_token;
180
180
181
            Koha::Patron::Modification->new( \%borrower )->store();
181
            Koha::Patron::Modification->new( \%borrower )->store();
Lines 214-220 if ( $action eq 'create' ) { Link Here
214
            );
214
            );
215
215
216
            $borrower{categorycode}     ||= C4::Context->preference('PatronSelfRegistrationDefaultCategory');
216
            $borrower{categorycode}     ||= C4::Context->preference('PatronSelfRegistrationDefaultCategory');
217
            $borrower{password}         ||= Koha::AuthUtils::generate_password;
217
            $borrower{password}         ||= Koha::AuthUtils::generate_password(Koha::Patron::Categories->find($borrower{categorycode}));
218
            my $consent_dt = delete $borrower{gdpr_proc_consent};
218
            my $consent_dt = delete $borrower{gdpr_proc_consent};
219
            my $patron = Koha::Patron->new( \%borrower )->store;
219
            my $patron = Koha::Patron->new( \%borrower )->store;
220
            Koha::Patron::Consent->new({ borrowernumber => $patron->borrowernumber, type => 'GDPR_PROCESSING', given_on => $consent_dt })->store if $consent_dt;
220
            Koha::Patron::Consent->new({ borrowernumber => $patron->borrowernumber, type => 'GDPR_PROCESSING', given_on => $consent_dt })->store if $consent_dt;
Lines 445-451 sub CheckForInvalidFields { Link Here
445
        push( @invalidFields, "password_match" );
445
        push( @invalidFields, "password_match" );
446
    }
446
    }
447
    if ( $borrower->{'password'} ) {
447
    if ( $borrower->{'password'} ) {
448
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $borrower->{password} );
448
        my ( $is_valid, $error ) = Koha::AuthUtils::is_password_valid( $borrower->{password}, Koha::Patron::Categories->find($borrower->{categorycode}) );
449
          unless ( $is_valid ) {
449
          unless ( $is_valid ) {
450
              push @invalidFields, 'password_too_short' if $error eq 'too_short';
450
              push @invalidFields, 'password_too_short' if $error eq 'too_short';
451
              push @invalidFields, 'password_too_weak' if $error eq 'too_weak';
451
              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