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

(-)a/C4/Passwordrecovery.pm (+159 lines)
Line 0 Link Here
1
package C4::Passwordrecovery;
2
3
# Copyright 2014 PTFS Europe
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use C4::Context;
22
23
use vars qw($VERSION @ISA @EXPORT);
24
25
BEGIN {
26
    # set the version for version checking
27
    $VERSION = 3.07.00.049;
28
    require Exporter;
29
    @ISA    = qw(Exporter);
30
    push @EXPORT, qw(
31
        &ValidateBorrowernumber
32
        &SendPasswordRecoveryEmail
33
        &GetValidLinkInfo
34
    );
35
}
36
37
=head1 NAME
38
39
C4::Passwordrecovery - Koha password recovery module
40
41
=head1 SYNOPSIS
42
43
use C4::Passwordrecovery;
44
45
=head1 FUNCTIONS
46
47
=head2 ValidateBorrowernumber
48
49
$alread = ValidateBorrowernumber( $borrower_number );
50
51
Check if the system already start recovery
52
53
Returns true false
54
55
=cut
56
57
sub ValidateBorrowernumber {
58
    my ($borrower_number) = @_;
59
    my $schema = Koha::Database->new->schema;
60
61
    my $rs = $schema->resultset('BorrowerPasswordRecovery')->search(
62
    {
63
       borrowernumber => $borrower_number,
64
       valid_until => \'> NOW()'
65
    }, {
66
        columns => 'borrowernumber'
67
    });
68
69
    if ($rs->next){
70
        return 1;
71
    }
72
73
    return 0;
74
}
75
76
=head2 GetValidLinkInfo
77
78
    Check if the link is still valid and return some info.
79
80
=cut
81
82
sub GetValidLinkInfo {
83
    my ($uniqueKey) = @_;
84
    my $dbh = C4::Context->dbh;
85
    my $query = '
86
    SELECT borrower_password_recovery.borrowernumber, userid
87
    FROM borrower_password_recovery, borrowers
88
    WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
89
    AND NOW() < valid_until
90
    AND uuid = ?
91
    ';
92
    my $sth = $dbh->prepare($query);
93
    $sth->execute($uniqueKey);
94
    return $sth->fetchrow;
95
}
96
97
=head2 SendPasswordRecoveryEmail
98
99
 It creates an email using the templates and send it to the user, using the specified email
100
101
=cut
102
103
sub SendPasswordRecoveryEmail {
104
    my $borrower = shift; # from GetMember
105
    my $userEmail = shift; #to_address (the one specified in the request)
106
    my $protocol = shift; #only required to determine if 'http' or 'https'
107
    my $update = shift;
108
109
    my $schema = Koha::Database->new->schema;
110
111
    # generate UUID
112
    my @chars = ("A".."Z", "a".."z", "0".."9");
113
    my $uuid_str;
114
    $uuid_str .= $chars[rand @chars] for 1..32;
115
116
    # insert into database
117
    my $expirydate = DateTime->now(time_zone => C4::Context->tz())->add( days => 2 );
118
    if($update){
119
        my $rs = $schema->resultset('BorrowerPasswordRecovery')->search(
120
        {
121
            borrowernumber => $borrower->{'borrowernumber'},
122
        });
123
        $rs->update({uuid => $uuid_str, valid_until => $expirydate->datetime()});
124
    } else {
125
         my $rs = $schema->resultset('BorrowerPasswordRecovery')->create({
126
            borrowernumber=>$borrower->{'borrowernumber'},
127
            uuid => $uuid_str,
128
            valid_until=> $expirydate->datetime()
129
         });
130
    }
131
132
    # create link
133
    my $uuidLink = $protocol . C4::Context->preference( 'OPACBaseURL' ) . "/cgi-bin/koha/opac-password-recovery.pl?uniqueKey=$uuid_str";
134
135
    # prepare the email
136
    my $letter = C4::Letters::GetPreparedLetter (
137
        module => 'members',
138
        letter_code => 'PASSWORD_RESET',
139
        branchcode => $borrower->{branchcode},
140
        substitute => {passwordreseturl => $uuidLink, user => $borrower->{userid} },
141
    );
142
143
    # define to/from emails
144
    my $kohaEmail = C4::Context->preference( 'KohaAdminEmailAddress' ); # from
145
146
    C4::Letters::EnqueueLetter( {
147
         letter => $letter,
148
         borrowernumber => $borrower->{borrowernumber},
149
         to_address => $userEmail,
150
         from_address => $kohaEmail,
151
         message_transport_type => 'email',
152
    } );
153
154
    return 1;
155
}
156
157
END { }    # module clean-up code here (global destructor)
158
159
1;
(-)a/installer/data/mysql/en/mandatory/sample_notices.sql (-1 / +1 lines)
Lines 144-148 Your library.' Link Here
144
);
144
);
145
145
146
INSERT INTO `letter` (module, code, branchcode, name, is_html, title, content, message_transport_type)
146
INSERT INTO `letter` (module, code, branchcode, name, is_html, title, content, message_transport_type)
147
VALUES ('members','PASSWORD_RESET','','Online password reset',1,'Koha password recovery','<html>\r\n<p>This email has been sent in response to your password recovery request for the account <strong><< borrowers.userid>></strong>.\r\n</p>\r\n<p>\r\nYou can now create your new password using the following link:\r\n<br/><a href=\"<<passwordreseturl>>\"><<passwordreseturl>></a>\r\n</p>\r\n<p>This link will be valid for 2 days from this email\'s reception, then you must reapply if you do not change your password.</p>\r\n<p>Thank you.</p>\r\n</html>\r\n','email'
147
VALUES ('members','PASSWORD_RESET','','Online password reset',1,'Koha password recovery','<html>\r\n<p>This email has been sent in response to your password recovery request for the account <strong><<user>></strong>.\r\n</p>\r\n<p>\r\nYou can now create your new password using the following link:\r\n<br/><a href=\"<<passwordreseturl>>\"><<passwordreseturl>></a>\r\n</p>\r\n<p>This link will be valid for 2 days from this email\'s reception, then you must reapply if you do not change your password.</p>\r\n<p>Thank you.</p>\r\n</html>\r\n','email'
148
);
148
);
(-)a/opac/opac-password-recovery.pl (-88 / +13 lines)
Lines 9-14 use C4::Koha; Link Here
9
use C4::Members qw(changepassword GetMember GetMemberDetails );
9
use C4::Members qw(changepassword GetMember GetMemberDetails );
10
use C4::Output;
10
use C4::Output;
11
use C4::Context;
11
use C4::Context;
12
use C4::Passwordrecovery qw(SendPasswordRecoveryEmail ValidateBorrowernumber GetValidLinkInfo);
12
use Koha::AuthUtils qw(hash_password);
13
use Koha::AuthUtils qw(hash_password);
13
my $query = new CGI;
14
my $query = new CGI;
14
use HTML::Entities;
15
use HTML::Entities;
Lines 44-54 my $errLinkNotValid; Link Here
44
my $errPassNotMatch;
45
my $errPassNotMatch;
45
my $errPassTooShort;
46
my $errPassTooShort;
46
47
47
my $dbh = C4::Context->dbh;
48
49
if ( $query->param('sendEmail') || $query->param('resendEmail') ) {
48
if ( $query->param('sendEmail') || $query->param('resendEmail') ) {
50
    #send mail + confirmation
49
    my $protocol = $query->https() ? "https://" : "http://";
51
52
    #try with the main email
50
    #try with the main email
53
    $email ||= ''; # avoid undef
51
    $email ||= ''; # avoid undef
54
    my $borrower_infos = GetMember( email => $email );
52
    my $borrower_infos = GetMember( email => $email );
Lines 63-73 if ( $query->param('sendEmail') || $query->param('resendEmail') ) { Link Here
63
        $errNoEmailFound = 1;
61
        $errNoEmailFound = 1;
64
    }
62
    }
65
    elsif ( !$query->param('resendEmail') ) {
63
    elsif ( !$query->param('resendEmail') ) {
66
        my $sth = $dbh->prepare(
64
        my $already = ValidateBorrowernumber( $borrower_number );
67
"SELECT borrowernumber FROM borrower_password_recovery WHERE NOW() < valid_until AND borrowernumber = ?"
65
68
        );
66
        if ( $already ) {
69
        $sth->execute($borrower_number);
70
        if ( my $already = $sth->fetchrow ) {
71
            $hasError                = 1;
67
            $hasError                = 1;
72
            $errAlreadyStartRecovery = 1;
68
            $errAlreadyStartRecovery = 1;
73
        }
69
        }
Lines 82-88 if ( $query->param('sendEmail') || $query->param('resendEmail') ) { Link Here
82
            email                   => HTML::Entities::encode($email),
78
            email                   => HTML::Entities::encode($email),
83
        );
79
        );
84
    }
80
    }
85
    elsif ( SendPasswordRecoveryEmail( $borrower_infos, $email, $query, $query->param('resendEmail') ) ) {#generate uuid and send recovery email
81
    elsif ( SendPasswordRecoveryEmail( $borrower_infos, $email, $protocol, $query->param('resendEmail') ) ) {#generate uuid and send recovery email
86
        $template->param(
82
        $template->param(
87
            mail_sent => 1,
83
            mail_sent => 1,
88
            email     => $email
84
            email     => $email
Lines 96-113 if ( $query->param('sendEmail') || $query->param('resendEmail') ) { Link Here
96
    }
92
    }
97
}
93
}
98
elsif ( $query->param('passwordReset') ) {
94
elsif ( $query->param('passwordReset') ) {
99
    #new password form
95
    ( $borrower_number, $username ) = GetValidLinkInfo($uniqueKey);
100
    #check if the link is still valid
101
    my $sth = $dbh->prepare(
102
        "SELECT borrower_password_recovery.borrowernumber, userid
103
                              FROM borrower_password_recovery, borrowers
104
                              WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
105
                              AND NOW() < valid_until
106
                              AND uuid = ?"
107
    );
108
    $sth->execute($uniqueKey);
109
    ( $borrower_number, $username ) = $sth->fetchrow;
110
111
    #validate password length & match
96
    #validate password length & match
112
    if (   ($borrower_number)
97
    if (   ($borrower_number)
113
        && ( $password eq $repeatPassword )
98
        && ( $password eq $repeatPassword )
Lines 116-123 elsif ( $query->param('passwordReset') ) { Link Here
116
        changepassword( $username, $borrower_number, hash_password($password) );
101
        changepassword( $username, $borrower_number, hash_password($password) );
117
102
118
        #remove entry
103
        #remove entry
119
        my $sth = $dbh->prepare("DELETE FROM borrower_password_recovery WHERE uuid = ? or NOW() > valid_until");
104
        my $schema = Koha::Database->new->schema;
120
        $sth->execute($uniqueKey);
105
        my $rs = $schema->resultset('BorrowerPasswordRecovery')->search({-or => [uuid => $uniqueKey, valid_until => \'< NOW()']});
106
        $rs->delete;
121
107
122
        $template->param(
108
        $template->param(
123
            password_reset_done => 1,
109
            password_reset_done => 1,
Lines 148-166 elsif ( $query->param('passwordReset') ) { Link Here
148
}
134
}
149
elsif ($uniqueKey) {  #reset password form
135
elsif ($uniqueKey) {  #reset password form
150
    #check if the link is valid
136
    #check if the link is valid
151
    my $sth = $dbh->prepare(
137
    ( $borrower_number, $username ) = GetValidLinkInfo($uniqueKey);
152
        "SELECT borrower_password_recovery.borrowernumber, userid
138
153
                              FROM borrower_password_recovery, borrowers
154
                              WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
155
                              AND NOW() < valid_until
156
                              AND uuid = ?"
157
    );
158
    $sth->execute($uniqueKey);
159
    ( $borrower_number, $username ) = $sth->fetchrow;
160
    if ( !$borrower_number ) {
139
    if ( !$borrower_number ) {
161
        $errLinkNotValid = 1;
140
        $errLinkNotValid = 1;
162
    }
141
    }
163
warn "INLIBRO username $username";
142
164
    $template->param(
143
    $template->param(
165
        new_password    => 1,
144
        new_password    => 1,
166
        minPassLength   => $minPassLength,
145
        minPassLength   => $minPassLength,
Lines 175-230 else { #password recovery form (to send email) Link Here
175
}
154
}
176
155
177
output_html_with_http_headers $query, $cookie, $template->output;
156
output_html_with_http_headers $query, $cookie, $template->output;
178
179
#
180
# It creates an email using the templates and send it to the user, using the specified email
181
#
182
sub SendPasswordRecoveryEmail {
183
    my $borrower = shift; # from GetMember
184
    my $userEmail = shift; #to_address (the one specified in the request)
185
    my $query = shift; #only required to determine if 'http' or 'https'
186
    my $update = shift;
187
188
    my $dbh = C4::Context->dbh;
189
190
    # generate UUID
191
    my @chars = ("A".."Z", "a".."z", "0".."9");
192
    my $uuid_str;
193
    $uuid_str .= $chars[rand @chars] for 1..32;
194
195
    # insert into database
196
    my $expirydate = DateTime->now(time_zone => C4::Context->tz())->add( days => 2 );
197
    if($update){
198
       my $sth = $dbh->prepare( 'UPDATE borrower_password_recovery set uuid=?, valid_until=? where borrowernumber=? ');
199
       $sth->execute($uuid_str, $expirydate->datetime(), $borrower->{'borrowernumber'});
200
    } else {
201
       my $sth = $dbh->prepare( 'INSERT INTO borrower_password_recovery VALUES (?, ?, ?)');
202
       $sth->execute($borrower->{'borrowernumber'}, $uuid_str, $expirydate->datetime());
203
    }
204
205
    # create link
206
    my $protocol = $query->https() ? "https://" : "http://";
207
    my $uuidLink = $protocol . C4::Context->preference( 'OPACBaseURL' ) . "/cgi-bin/koha/opac-password-recovery.pl?uniqueKey=$uuid_str";
208
209
    # prepare the email
210
    my $letter = C4::Letters::GetPreparedLetter (
211
        module => 'members',
212
        letter_code => 'PASSWORD_RESET',
213
        branchcode => $borrower->{branchcode},
214
        tables => {borrowers => $borrower},
215
        substitute => {passwordreseturl => $uuidLink},
216
    );
217
218
    # define to/from emails
219
    my $kohaEmail = C4::Context->preference( 'KohaAdminEmailAddress' ); # from
220
221
    C4::Letters::EnqueueLetter( {
222
         letter => $letter,
223
         borrowernumber => $borrower->{'borrowernumber'},
224
         to_address => $userEmail,
225
         from_address => $kohaEmail,
226
         message_transport_type => 'email',
227
    } );
228
229
    return 1;
230
}
231
- 

Return to bug 8753