Line 0
Link Here
|
0 |
- |
1 |
#!/usr/bin/perl |
|
|
2 |
|
3 |
use strict; |
4 |
use Modern::Perl; |
5 |
use CGI; |
6 |
|
7 |
use C4::Auth; |
8 |
use C4::Koha; |
9 |
use C4::Members qw(changepassword GetMember GetMemberDetails ); |
10 |
use C4::Output; |
11 |
use C4::Context; |
12 |
use Koha::AuthUtils qw(hash_password); |
13 |
|
14 |
my $query = new CGI; |
15 |
|
16 |
my ( $template, $dummy, $cookie ) = get_template_and_user( |
17 |
{ |
18 |
template_name => "opac-password-recovery.tmpl", |
19 |
query => $query, |
20 |
type => "opac", |
21 |
authnotrequired => 1, |
22 |
debug => 1, |
23 |
} |
24 |
); |
25 |
|
26 |
my $email = $query->param('email') // q{}; |
27 |
my $password = $query->param('password'); |
28 |
my $repeatPassword = $query->param('repeatPassword'); |
29 |
my $minPassLength = C4::Context->preference('minPasswordLength'); |
30 |
my $id = $query->param('id'); |
31 |
my $uniqueKey = $query->param('uniqueKey'); |
32 |
my $username = $query->param('username'); |
33 |
my $borrower_number; |
34 |
|
35 |
#errors |
36 |
my $hasError; |
37 |
|
38 |
#email form error |
39 |
my $errNoEmailFound; |
40 |
my $errAlreadyStartRecovery; |
41 |
|
42 |
#new password form error |
43 |
my $errLinkNotValid; |
44 |
my $errPassNotMatch; |
45 |
my $errPassTooShort; |
46 |
|
47 |
my $dbh = C4::Context->dbh; |
48 |
|
49 |
if ( $query->param('sendEmail') || $query->param('resendEmail') ) { |
50 |
#send mail + confirmation |
51 |
|
52 |
#try with the main email |
53 |
$email ||= ''; # avoid undef |
54 |
my $borrower_infos = GetMember( email => $email ); |
55 |
$borrower_infos = GetMember( emailpro => $email ) unless $borrower_infos; |
56 |
$borrower_infos = GetMember( B_email => $email ) unless $borrower_infos; |
57 |
if($borrower_infos) { |
58 |
$borrower_number = $borrower_infos->{'borrowernumber'}; |
59 |
} |
60 |
|
61 |
if ( !$email || !$borrower_number ) { |
62 |
$hasError = 1; |
63 |
$errNoEmailFound = 1; |
64 |
} |
65 |
elsif ( !$query->param('resendEmail') ) { |
66 |
my $sth = $dbh->prepare( |
67 |
"SELECT borrowernumber FROM borrower_password_recovery WHERE NOW() < valid_until AND borrowernumber = ?" |
68 |
); |
69 |
$sth->execute($borrower_number); |
70 |
if ( my $already = $sth->fetchrow ) { |
71 |
$hasError = 1; |
72 |
$errAlreadyStartRecovery = 1; |
73 |
} |
74 |
} |
75 |
|
76 |
if ($hasError) { |
77 |
$template->param( |
78 |
hasError => 1, |
79 |
errNoEmailFound => $errNoEmailFound, |
80 |
errAlreadyStartRecovery => $errAlreadyStartRecovery, |
81 |
password_recovery => 1, |
82 |
email => HTML::Entities::encode($email), |
83 |
); |
84 |
} |
85 |
elsif ( SendPasswordRecoveryEmail( $borrower_infos, $email, $query, $query->param('resendEmail') ) ) {#generate uuid and send recovery email |
86 |
$template->param( |
87 |
mail_sent => 1, |
88 |
email => $email |
89 |
); |
90 |
} |
91 |
else {# if it doesnt work.... |
92 |
$template->param( |
93 |
password_recovery => 1, |
94 |
sendmailError => 1 |
95 |
); |
96 |
} |
97 |
} |
98 |
elsif ( $query->param('passwordReset') ) { |
99 |
#new password form |
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 |
112 |
if ( ($borrower_number) |
113 |
&& ( $password eq $repeatPassword ) |
114 |
&& ( length($password) >= $minPassLength ) ) |
115 |
{ #apply changes |
116 |
changepassword( $username, $borrower_number, hash_password($password) ); |
117 |
|
118 |
#remove entry |
119 |
my $sth = $dbh->prepare("DELETE FROM borrower_password_recovery WHERE uuid = ? or NOW() > valid_until"); |
120 |
$sth->execute($uniqueKey); |
121 |
|
122 |
$template->param( |
123 |
password_reset_done => 1, |
124 |
username => $username |
125 |
); |
126 |
} |
127 |
else { #errors |
128 |
if ( !$borrower_number ) { #parameters not valid |
129 |
$errLinkNotValid = 1; |
130 |
} |
131 |
elsif ( $password ne $repeatPassword ) { #passwords does not match |
132 |
$errPassNotMatch = 1; |
133 |
} |
134 |
elsif ( length($password) < $minPassLength ) { #password too short |
135 |
$errPassTooShort = 1; |
136 |
} |
137 |
$template->param( |
138 |
new_password => 1, |
139 |
minPassLength => $minPassLength, |
140 |
email => $email, |
141 |
uniqueKey => $uniqueKey, |
142 |
errLinkNotValid => $errLinkNotValid, |
143 |
errPassNotMatch => $errPassNotMatch, |
144 |
errPassTooShort => $errPassTooShort, |
145 |
hasError => 1 |
146 |
); |
147 |
} |
148 |
} |
149 |
elsif ($uniqueKey) { #reset password form |
150 |
#check if the link is valid |
151 |
my $sth = $dbh->prepare( |
152 |
"SELECT borrower_password_recovery.borrowernumber, userid |
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 ) { |
161 |
$errLinkNotValid = 1; |
162 |
} |
163 |
warn "INLIBRO username $username"; |
164 |
$template->param( |
165 |
new_password => 1, |
166 |
minPassLength => $minPassLength, |
167 |
email => $email, |
168 |
uniqueKey => $uniqueKey, |
169 |
username => $username, |
170 |
errLinkNotValid => $errLinkNotValid |
171 |
); |
172 |
} |
173 |
else { #password recovery form (to send email) |
174 |
$template->param( password_recovery => 1 ); |
175 |
} |
176 |
|
177 |
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 |
|