]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/WWW/PasswordReset.pm
Forward-port self-serve password reset implementation from rel_1_6
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / WWW / PasswordReset.pm
1 package OpenILS::WWW::PasswordReset;
2
3 # Copyright (C) 2010 Laurentian University
4 # Dan Scott <dscott@laurentian.ca>
5
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20 use strict; use warnings;
21
22 use Apache2::Log;
23 use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log);
24 use APR::Const    -compile => qw(:error SUCCESS);
25 use Apache2::RequestRec ();
26 use Apache2::RequestIO ();
27 use Apache2::RequestUtil;
28 use CGI;
29 use Template;
30
31 use OpenSRF::EX qw(:try);
32 use OpenSRF::Utils qw/:datetime/;
33 use OpenSRF::Utils::Cache;
34 use OpenSRF::System;
35 use OpenSRF::AppSession;
36
37 use OpenILS::Utils::Fieldmapper;
38 use OpenSRF::Utils::Logger qw/$logger/;
39 use OpenILS::Application::AppUtils;
40 use OpenILS::Utils::CStoreEditor qw/:funcs/;
41
42 my $log = 'OpenSRF::Utils::Logger';
43 my $U = 'OpenILS::Application::AppUtils';
44
45 my ($bootstrap, $actor, $templates);
46 my $i18n = {};
47
48 sub child_init {
49     OpenSRF::System->bootstrap_client( config_file => $bootstrap );
50     
51     my $conf = OpenSRF::Utils::SettingsClient->new();
52     my $idl = $conf->config_value("IDL");
53     Fieldmapper->import(IDL => $idl);
54     $templates = $conf->config_value("dirs", "templates");
55     $actor = OpenSRF::AppSession->create('open-ils.actor');
56     load_i18n();
57 }
58
59 sub password_reset {
60     my $apache = shift;
61     return Apache2::Const::DECLINED if (-e $apache->filename);
62
63     $apache->content_type('text/html');
64
65         my $cgi = new CGI;
66     my $ctx = {};
67
68     $ctx->{'uri'} = $apache->uri;
69
70     # Get our locale from the URL
71     (my $locale = $apache->path_info) =~ s{^.*?/([a-z]{2}-[A-Z]{2})/.*?$}{$1};
72     if (!$locale) {
73         $locale = 'en-US';
74     }
75
76     # If locale exists, use it; otherwise fall back to en-US
77     if (exists $i18n->{$locale}) {
78         $ctx->{'i18n'} = $i18n->{$locale};
79     } else {
80         $ctx->{'i18n'} = $i18n->{'en-US'};
81     }
82
83     my $tt = Template->new({
84         INCLUDE_PATH => $templates
85     }) || die "$Template::ERROR\n";
86
87     # Get our UUID: if no UUID, then display barcode / username / email prompt
88     (my $uuid = $apache->path_info) =~ s{^/$locale/([^/]*?)$}{$1};
89     $logger->info("Password reset: UUID = $uuid");
90
91     if (!$uuid) {
92         request_password_reset($apache, $cgi, $tt, $ctx);
93     } else {
94         reset_password($apache, $cgi, $tt, $ctx, $uuid);
95     }
96 }
97
98 sub reset_password {
99     my ($apache, $cgi, $tt, $ctx, $uuid) = @_;
100
101     my $password_1 = $cgi->param('pwd1');
102     my $password_2 = $cgi->param('pwd2');
103
104     $ctx->{'title'} = $ctx->{'i18n'}{'TITLE'};
105     $ctx->{'password_prompt'} = $ctx->{'i18n'}{'PASSWORD_PROMPT'};
106     $ctx->{'password_prompt2'} = $ctx->{'i18n'}{'PASSWORD_PROMPT2'};
107
108     # In case non-matching passwords slip through our funky Web interface
109     if ($password_1 and $password_2 and ($password_1 ne $password_2)) {
110         $apache->status(Apache2::Const::DECLINED);
111         $ctx->{'status'} = {
112             style => 'error',
113             msg => $ctx->{'i18n'}{'NO_MATCH'}
114         };
115         $tt->process('password-reset/reset-form.tt2', $ctx)
116             || die $tt->error();
117         return Apache2::Const::OK;
118     }
119
120     if ($password_1 and $password_2 and ($password_1 eq $password_2)) {
121         my $response = $actor->request('open-ils.actor.patron.password_reset.commit', $uuid, $password_1)->gather();
122         if (ref($response) && $response->{'textcode'}) {
123             $apache->status(Apache2::Const::DECLINED);
124
125             if ($response->{'textcode'} eq 'PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST') {
126                 $ctx->{'status'} = { 
127                     style => 'error',
128                     msg => $ctx->{'i18n'}{'NOT_ACTIVE'}
129
130                 };
131             }
132             if ($response->{'textcode'} eq 'PATRON_PASSWORD_WAS_NOT_STRONG') {
133                 $ctx->{'status'} = { 
134                     style => 'error',
135                     msg => $ctx->{'i18n'}{'NOT_STRONG'}
136
137                 };
138             }
139             $tt->process('password-reset/reset-form.tt2', $ctx)
140                 || die $tt->error();
141             return Apache2::Const::OK;
142         }
143         $ctx->{'status'} = { 
144             style => 'success',
145             msg => $ctx->{'i18n'}{'SUCCESS'}
146         };
147     }
148
149     # Either the password change was successful, or this is their first time through
150     $tt->process('password-reset/reset-form.tt2', $ctx)
151         || die $tt->error();
152
153     return Apache2::Const::OK;
154 }
155
156 # Load our localized strings - lame, need to convert to Locale::Maketext
157 sub load_i18n {
158     foreach my $string_bundle (glob("$templates/password-reset/strings.*")) {
159         open(I18NFH, '<', $string_bundle);
160         (my $locale = $string_bundle) =~ s/^.*\.([a-z]{2}-[A-Z]{2})$/$1/;
161         $logger->debug("Loaded locale [$locale] from file: [$string_bundle]");
162         while(<I18NFH>) {
163             my ($string_id, $string) = ($_ =~ m/^(.+?)=(.*?)$/);
164             $i18n->{$locale}{$string_id} = $string;
165         }
166         close(I18NFH);
167     }
168 }
169
170 sub request_password_reset {
171     my ($apache, $cgi, $tt, $ctx) = @_;
172
173     my $barcode = $cgi->param('barcode');
174     my $username = $cgi->param('username');
175     my $email = $cgi->param('email');
176
177     if (!($barcode or $username or $email)) {
178         $apache->status(Apache2::Const::OK);
179         $ctx->{'status'} = {
180             style => 'plain',
181             msg => $ctx->{'i18n'}{'IDENTIFY_YOURSELF'}
182         };
183         $tt->process('password-reset/request-form.tt2', $ctx)
184             || die $tt->error();
185         return Apache2::Const::OK;
186     } elsif ($barcode) {
187         my $response = $actor->request('open-ils.actor.patron.password_reset.request', 'barcode', $barcode)->gather();
188         $apache->status(Apache2::Const::OK);
189         $ctx->{'status'} = {
190             style => 'plain',
191             msg => $ctx->{'i18n'}{'REQUEST_SUCCESS'}
192         };
193         # Hide form
194         $tt->process('password-reset/request-form.tt2', $ctx)
195             || die $tt->error();
196         return Apache2::Const::OK;
197     } elsif ($username) {
198         my $response = $actor->request('open-ils.actor.patron.password_reset.request', 'username', $username)->gather();
199         $apache->status(Apache2::Const::OK);
200         $ctx->{'status'} = {
201             style => 'plain',
202             msg => $ctx->{'i18n'}{'REQUEST_SUCCESS'}
203         };
204         # Hide form
205         $tt->process('password-reset/request-form.tt2', $ctx)
206             || die $tt->error();
207         return Apache2::Const::OK;
208     }
209 }
210
211 1;
212
213 # vim: et:ts=4:sw=4
214 package OpenILS::WWW::PasswordReset;
215
216 # Copyright (C) 2010 Laurentian University
217 # Dan Scott <dscott@laurentian.ca>
218
219 # This program is free software; you can redistribute it and/or
220 # modify it under the terms of the GNU General Public License
221 # as published by the Free Software Foundation; either version 2
222 # of the License, or (at your option) any later version.
223
224 # This program is distributed in the hope that it will be useful,
225 # but WITHOUT ANY WARRANTY; without even the implied warranty of
226 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
227 # GNU General Public License for more details.
228
229 # You should have received a copy of the GNU General Public License
230 # along with this program; if not, write to the Free Software
231 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
232
233 use strict; use warnings;
234
235 use Apache2::Log;
236 use Apache2::Const -compile => qw(OK REDIRECT DECLINED NOT_FOUND :log);
237 use APR::Const    -compile => qw(:error SUCCESS);
238 use Apache2::RequestRec ();
239 use Apache2::RequestIO ();
240 use Apache2::RequestUtil;
241 use CGI;
242 use Template;
243
244 use OpenSRF::EX qw(:try);
245 use OpenSRF::Utils qw/:datetime/;
246 use OpenSRF::Utils::Cache;
247 use OpenSRF::System;
248 use OpenSRF::AppSession;
249
250 use OpenILS::Utils::Fieldmapper;
251 use OpenSRF::Utils::Logger qw/$logger/;
252 use OpenILS::Application::AppUtils;
253 use OpenILS::Utils::CStoreEditor qw/:funcs/;
254
255 my $log = 'OpenSRF::Utils::Logger';
256 my $U = 'OpenILS::Application::AppUtils';
257
258 my ($bootstrap, $actor, $templates);
259 my $i18n = {};
260
261 sub child_init {
262     OpenSRF::System->bootstrap_client( config_file => $bootstrap );
263     
264     my $conf = OpenSRF::Utils::SettingsClient->new();
265     my $idl = $conf->config_value("IDL");
266     Fieldmapper->import(IDL => $idl);
267     $templates = $conf->config_value("dirs", "templates");
268     $actor = OpenSRF::AppSession->create('open-ils.actor');
269     load_i18n();
270 }
271
272 sub password_reset {
273     my $apache = shift;
274     return Apache2::Const::DECLINED if (-e $apache->filename);
275
276     $apache->content_type('text/html');
277
278         my $cgi = new CGI;
279     my $ctx = {};
280
281     $ctx->{'uri'} = $apache->uri;
282
283     # Get our locale from the URL
284     (my $locale = $apache->path_info) =~ s{^.*?/([a-z]{2}-[A-Z]{2})/.*?$}{$1};
285     if (!$locale) {
286         $locale = 'en-US';
287     }
288
289     # If locale exists, use it; otherwise fall back to en-US
290     if (exists $i18n->{$locale}) {
291         $ctx->{'i18n'} = $i18n->{$locale};
292     } else {
293         $ctx->{'i18n'} = $i18n->{'en-US'};
294     }
295
296     my $tt = Template->new({
297         INCLUDE_PATH => $templates
298     }) || die "$Template::ERROR\n";
299
300     # Get our UUID: if no UUID, then display barcode / username / email prompt
301     (my $uuid = $apache->path_info) =~ s{^/$locale/([^/]*?)$}{$1};
302     $logger->info("Password reset: UUID = $uuid");
303
304     if (!$uuid) {
305         request_password_reset($apache, $cgi, $tt, $ctx);
306     } else {
307         reset_password($apache, $cgi, $tt, $ctx, $uuid);
308     }
309 }
310
311 sub reset_password {
312     my ($apache, $cgi, $tt, $ctx, $uuid) = @_;
313
314     my $password_1 = $cgi->param('pwd1');
315     my $password_2 = $cgi->param('pwd2');
316
317     $ctx->{'title'} = $ctx->{'i18n'}{'TITLE'};
318     $ctx->{'password_prompt'} = $ctx->{'i18n'}{'PASSWORD_PROMPT'};
319     $ctx->{'password_prompt2'} = $ctx->{'i18n'}{'PASSWORD_PROMPT2'};
320
321     # In case non-matching passwords slip through our funky Web interface
322     if ($password_1 and $password_2 and ($password_1 ne $password_2)) {
323         $apache->status(Apache2::Const::DECLINED);
324         $ctx->{'status'} = {
325             style => 'error',
326             msg => $ctx->{'i18n'}{'NO_MATCH'}
327         };
328         $tt->process('password-reset/reset-form.tt2', $ctx)
329             || die $tt->error();
330         return Apache2::Const::OK;
331     }
332
333     if ($password_1 and $password_2 and ($password_1 eq $password_2)) {
334         my $response = $actor->request('open-ils.actor.patron.password_reset.commit', $uuid, $password_1)->gather();
335         if (ref($response) && 
336                 $response->{'textcode'} && 
337                 $response->{'textcode'} eq 'PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST') {
338             $apache->status(Apache2::Const::DECLINED);
339             $ctx->{'status'} = { 
340                 style => 'error',
341                 msg => $ctx->{'i18n'}{'NOT_ACTIVE'}
342
343             };
344             $tt->process('password-reset/reset-form.tt2', $ctx)
345                 || die $tt->error();
346             return Apache2::Const::OK;
347         }
348         $ctx->{'status'} = { 
349             style => 'success',
350             msg => $ctx->{'i18n'}{'SUCCESS'}
351         };
352     }
353
354     # Either the password change was successful, or this is their first time through
355     $tt->process('password-reset/reset-form.tt2', $ctx)
356         || die $tt->error();
357
358     return Apache2::Const::OK;
359 }
360
361 # Load our localized strings - lame, need to convert to Locale::Maketext
362 sub load_i18n {
363     foreach my $string_bundle (glob("$templates/password-reset/strings.*")) {
364         open(I18NFH, '<', $string_bundle);
365         (my $locale = $string_bundle) =~ s/^.*\.([a-z]{2}-[A-Z]{2})$/$1/;
366         $logger->debug("Loaded locale [$locale] from file: [$string_bundle]");
367         while(<I18NFH>) {
368             my ($string_id, $string) = ($_ =~ m/^(.+?)=(.*?)$/);
369             $i18n->{$locale}{$string_id} = $string;
370         }
371         close(I18NFH);
372     }
373 }
374
375 sub request_password_reset {
376     my ($apache, $cgi, $tt, $ctx) = @_;
377
378     my $barcode = $cgi->param('barcode');
379     my $username = $cgi->param('username');
380     my $email = $cgi->param('email');
381
382     if (!($barcode or $username or $email)) {
383         $apache->status(Apache2::Const::OK);
384         $ctx->{'status'} = {
385             style => 'plain',
386             msg => $ctx->{'i18n'}{'IDENTIFY_YOURSELF'}
387         };
388         $tt->process('password-reset/request-form.tt2', $ctx)
389             || die $tt->error();
390         return Apache2::Const::OK;
391     } elsif ($barcode) {
392         my $response = $actor->request('open-ils.actor.patron.password_reset.request', 'barcode', $barcode)->gather();
393         $apache->status(Apache2::Const::OK);
394         $ctx->{'status'} = {
395             style => 'plain',
396             msg => $ctx->{'i18n'}{'REQUEST_SUCCESS'}
397         };
398         # Hide form
399         $tt->process('password-reset/request-form.tt2', $ctx)
400             || die $tt->error();
401         return Apache2::Const::OK;
402     } elsif ($username) {
403         my $response = $actor->request('open-ils.actor.patron.password_reset.request', 'username', $username)->gather();
404         $apache->status(Apache2::Const::OK);
405         $ctx->{'status'} = {
406             style => 'plain',
407             msg => $ctx->{'i18n'}{'REQUEST_SUCCESS'}
408         };
409         # Hide form
410         $tt->process('password-reset/request-form.tt2', $ctx)
411             || die $tt->error();
412         return Apache2::Const::OK;
413     }
414 }
415
416 1;
417
418 # vim: et:ts=4:sw=4