Bug 8753 - Add forgot password link to OPAC
[koha.git] / opac / opac-password-recovery.pl
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 Search);
10 use C4::Output;
11 use C4::Context;
12 use C4::Passwordrecovery qw(SendPasswordRecoveryEmail ValidateBorrowernumber GetValidLinkInfo CompletePasswordRecovery);
13 use Koha::AuthUtils qw(hash_password);
14 my $query = new CGI;
15 use HTML::Entities;
16
17 my ( $template, $dummy, $cookie ) = get_template_and_user(
18     {
19         template_name   => "opac-password-recovery.tt",
20         query           => $query,
21         type            => "opac",
22         authnotrequired => 1,
23         debug           => 1,
24     }
25 );
26
27 my $email          = $query->param('email') // q{};
28 my $password       = $query->param('password');
29 my $repeatPassword = $query->param('repeatPassword');
30 my $minPassLength  = C4::Context->preference('minPasswordLength');
31 my $id             = $query->param('id');
32 my $uniqueKey      = $query->param('uniqueKey');
33 my $username       = $query->param('username');
34 my $borrower_number;
35
36 #errors
37 my $hasError;
38
39 #email form error
40 my $errNoBorrowerFound;
41 my $errNoBorrowerEmail;
42 my $errAlreadyStartRecovery;
43 my $errTooManyEmailFound;
44 my $errBadEmail;
45
46 #new password form error
47 my $errLinkNotValid;
48 my $errPassNotMatch;
49 my $errPassTooShort;
50
51 if ( $query->param('sendEmail') || $query->param('resendEmail') ) {
52     my $protocol = $query->https() ? "https://" : "http://";
53     #try with the main email
54     $email ||= ''; # avoid undef
55     my $borrower;
56     my $search_results;
57
58     # Find the borrower by his userid or email
59     if( $username ){
60         $search_results = Search({ userid => $username });
61     }
62     elsif ( $email ){
63         $search_results = Search({ '' => $email }, undef, undef, undef, ['emailpro', 'email', 'B_email']);
64     }
65
66     if(scalar @$search_results > 1){ # Many matching borrowers
67        $hasError             = 1;
68        $errTooManyEmailFound = 1;
69     }
70     elsif( $borrower = shift @$search_results ){ # One matching borrower
71         $username ||= $borrower->{'userid'};
72         my @emails = ( $borrower->{'email'}, $borrower->{'emailpro'}, $borrower->{'B_email'} );
73         # Is the given email one of the borrower's ?
74         if( $email && !($email ~~ @emails) ){
75              $hasError    = 1;
76              $errBadEmail = 1;
77         }
78         # If we dont have an email yet. Get one of the borrower's email or raise an error.
79         # FIXME: That ugly shift-grep contraption.
80         # $email = shift [ grep { length() } @emails ]
81         # It's supposed to get a non-empty string from the @emails array. There's surely a simpler way
82         elsif( !$email && !($email = shift [ grep { length() } @emails ]) ){
83              $hasError           = 1;
84              $errNoBorrowerEmail = 1;
85         }
86         # Check if a password reset already issued for this borrower AND we are not asking for a new email
87         elsif( ValidateBorrowernumber( $borrower->{'borrowernumber'} ) && !$query->param('resendEmail') ){
88             $hasError                = 1;
89             $errAlreadyStartRecovery = 1;
90         }
91     }
92     else{ # 0 matching borrower
93         $hasError           = 1;
94         $errNoBorrowerFound = 1;
95     }
96     if ($hasError) {
97         $template->param(
98             hasError                => 1,
99             errNoBorrowerFound      => $errNoBorrowerFound,
100             errTooManyEmailFound    => $errTooManyEmailFound,
101             errAlreadyStartRecovery => $errAlreadyStartRecovery,
102             errBadEmail             => $errBadEmail,
103             errNoBorrowerEmail      => $errNoBorrowerEmail,
104             password_recovery       => 1,
105             email                   => HTML::Entities::encode($email),
106             username                => $username
107         );
108     }
109     elsif ( SendPasswordRecoveryEmail( $borrower, $email, $protocol, $query->param('resendEmail') ) ) {#generate uuid and send recovery email
110         $template->param(
111             mail_sent => 1,
112             email     => $email
113         );
114     }
115     else {# if it doesnt work....
116         $template->param(
117             password_recovery => 1,
118             sendmailError     => 1
119         );
120     }
121 }
122 elsif ( $query->param('passwordReset') ) {
123     ( $borrower_number, $username ) = GetValidLinkInfo($uniqueKey);
124     #validate password length & match
125     if (   ($borrower_number)
126         && ( $password eq $repeatPassword )
127         && ( length($password) >= $minPassLength ) )
128     {  #apply changes
129         changepassword( $username, $borrower_number, hash_password($password) );
130         CompletePasswordRecovery($uniqueKey);
131         $template->param(
132             password_reset_done => 1,
133             username            => $username
134         );
135     }
136     else { #errors
137         if ( !$borrower_number ) { #parameters not valid
138             $errLinkNotValid = 1;
139         }
140         elsif ( $password ne $repeatPassword ) { #passwords does not match
141             $errPassNotMatch = 1;
142         }
143         elsif ( length($password) < $minPassLength ) { #password too short
144             $errPassTooShort = 1;
145         }
146         $template->param(
147             new_password    => 1,
148             minPassLength   => $minPassLength,
149             email           => $email,
150             uniqueKey       => $uniqueKey,
151             errLinkNotValid => $errLinkNotValid,
152             errPassNotMatch => $errPassNotMatch,
153             errPassTooShort => $errPassTooShort,
154             hasError        => 1
155         );
156     }
157 }
158 elsif ($uniqueKey) {  #reset password form
159     #check if the link is valid
160     ( $borrower_number, $username ) = GetValidLinkInfo($uniqueKey);
161
162     if ( !$borrower_number ) {
163         $errLinkNotValid = 1;
164     }
165
166     $template->param(
167         new_password    => 1,
168         minPassLength   => $minPassLength,
169         email           => $email,
170         uniqueKey       => $uniqueKey,
171         username        => $username,
172         errLinkNotValid => $errLinkNotValid
173     );
174 }
175 else { #password recovery form (to send email)
176     $template->param( password_recovery => 1 );
177 }
178
179 output_html_with_http_headers $query, $cookie, $template->output;