2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / BugsSite / token.cgi
blob3fe8e014098c30f0dbd612d3fb2455ff1775384a
1 #!/usr/bin/perl -wT
2 # -*- Mode: perl; indent-tabs-mode: nil -*-
4 # The contents of this file are subject to the Mozilla Public
5 # License Version 1.1 (the "License"); you may not use this file
6 # except in compliance with the License. You may obtain a copy of
7 # the License at http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS
10 # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 # implied. See the License for the specific language governing
12 # rights and limitations under the License.
14 # The Original Code is the Bugzilla Bug Tracking System.
16 # The Initial Developer of the Original Code is Netscape Communications
17 # Corporation. Portions created by Netscape are
18 # Copyright (C) 1998 Netscape Communications Corporation. All
19 # Rights Reserved.
21 # Contributor(s): Myk Melez <myk@mozilla.org>
23 ############################################################################
24 # Script Initialization
25 ############################################################################
27 # Make it harder for us to do dangerous things in Perl.
28 use strict;
30 use lib qw(.);
32 use vars qw($template $vars);
34 use Bugzilla;
35 use Bugzilla::Constants;
36 use Bugzilla::Util;
38 my $cgi = Bugzilla->cgi;
39 my $dbh = Bugzilla->dbh;
41 # Include the Bugzilla CGI and general utility library.
42 require "CGI.pl";
44 Bugzilla->login(LOGIN_OPTIONAL);
46 # Use the "Bugzilla::Token" module that contains functions for doing various
47 # token-related tasks.
48 use Bugzilla::Token;
50 use Bugzilla::User;
52 ################################################################################
53 # Data Validation / Security Authorization
54 ################################################################################
56 # Throw an error if the form does not contain an "action" field specifying
57 # what the user wants to do.
58 $cgi->param('a') || ThrowCodeError("unknown_action");
60 # Assign the action to a global variable.
61 $::action = $cgi->param('a');
63 # If a token was submitted, make sure it is a valid token that exists in the
64 # database and is the correct type for the action being taken.
65 if ($cgi->param('t')) {
66 # Assign the token and its SQL quoted equivalent to global variables.
67 $::token = $cgi->param('t');
68 $::quotedtoken = SqlQuote($::token);
70 # Make sure the token contains only valid characters in the right amount.
71 # Validate password will throw an error if token is invalid
72 ValidatePassword($::token);
74 Bugzilla::Token::CleanTokenTable();
76 # Make sure the token exists in the database.
77 SendSQL( "SELECT tokentype FROM tokens WHERE token = $::quotedtoken" );
78 (my $tokentype = FetchSQLData()) || ThrowUserError("token_inexistent");
80 # Make sure the token is the correct type for the action being taken.
81 if ( grep($::action eq $_ , qw(cfmpw cxlpw chgpw)) && $tokentype ne 'password' ) {
82 Bugzilla::Token::Cancel($::token, "wrong_token_for_changing_passwd");
83 ThrowUserError("wrong_token_for_changing_passwd");
85 if ( ($::action eq 'cxlem')
86 && (($tokentype ne 'emailold') && ($tokentype ne 'emailnew')) ) {
87 Bugzilla::Token::Cancel($::token, "wrong_token_for_cancelling_email_change");
88 ThrowUserError("wrong_token_for_cancelling_email_change");
90 if ( grep($::action eq $_ , qw(cfmem chgem))
91 && ($tokentype ne 'emailnew') ) {
92 Bugzilla::Token::Cancel($::token, "wrong_token_for_confirming_email_change");
93 ThrowUserError("wrong_token_for_confirming_email_change");
98 # If the user is requesting a password change, make sure they submitted
99 # their login name and it exists in the database, and that the DB module is in
100 # the list of allowed verification methids.
101 if ( $::action eq 'reqpw' ) {
102 defined $cgi->param('loginname')
103 || ThrowUserError("login_needed_for_password_change");
105 # check verification methods
106 unless (Bugzilla::Auth->has_db) {
107 ThrowUserError("password_change_requests_not_allowed");
110 # Make sure the login name looks like an email address. This function
111 # displays its own error and stops execution if the login name looks wrong.
112 CheckEmailSyntax($cgi->param('loginname'));
114 my $quotedloginname = SqlQuote($cgi->param('loginname'));
115 SendSQL("SELECT userid FROM profiles WHERE " .
116 $dbh->sql_istrcmp('login_name', $quotedloginname));
117 FetchSQLData()
118 || ThrowUserError("account_inexistent");
121 # If the user is changing their password, make sure they submitted a new
122 # password and that the new password is valid.
123 if ( $::action eq 'chgpw' ) {
124 defined $cgi->param('password')
125 && defined $cgi->param('matchpassword')
126 || ThrowUserError("require_new_password");
128 ValidatePassword($cgi->param('password'), $cgi->param('matchpassword'));
131 ################################################################################
132 # Main Body Execution
133 ################################################################################
135 # All calls to this script should contain an "action" variable whose value
136 # determines what the user wants to do. The code below checks the value of
137 # that variable and runs the appropriate code.
139 if ($::action eq 'reqpw') {
140 requestChangePassword();
141 } elsif ($::action eq 'cfmpw') {
142 confirmChangePassword();
143 } elsif ($::action eq 'cxlpw') {
144 cancelChangePassword();
145 } elsif ($::action eq 'chgpw') {
146 changePassword();
147 } elsif ($::action eq 'cfmem') {
148 confirmChangeEmail();
149 } elsif ($::action eq 'cxlem') {
150 cancelChangeEmail();
151 } elsif ($::action eq 'chgem') {
152 changeEmail();
153 } else {
154 # If the action that the user wants to take (specified in the "a" form field)
155 # is none of the above listed actions, display an error telling the user
156 # that we do not understand what they would like to do.
157 ThrowCodeError("unknown_action", { action => $::action });
160 exit;
162 ################################################################################
163 # Functions
164 ################################################################################
166 sub requestChangePassword {
167 Bugzilla::Token::IssuePasswordToken($cgi->param('loginname'));
169 $vars->{'message'} = "password_change_request";
171 print $cgi->header();
172 $template->process("global/message.html.tmpl", $vars)
173 || ThrowTemplateError($template->error());
176 sub confirmChangePassword {
177 $vars->{'token'} = $::token;
179 print $cgi->header();
180 $template->process("account/password/set-forgotten-password.html.tmpl", $vars)
181 || ThrowTemplateError($template->error());
184 sub cancelChangePassword {
185 $vars->{'message'} = "password_change_cancelled";
186 Bugzilla::Token::Cancel($::token, $vars->{'message'});
188 print $cgi->header();
189 $template->process("global/message.html.tmpl", $vars)
190 || ThrowTemplateError($template->error());
193 sub changePassword {
194 my $dbh = Bugzilla->dbh;
196 # Quote the password and token for inclusion into SQL statements.
197 my $cryptedpassword = bz_crypt($cgi->param('password'));
198 my $quotedpassword = SqlQuote($cryptedpassword);
200 # Get the user's ID from the tokens table.
201 SendSQL("SELECT userid FROM tokens WHERE token = $::quotedtoken");
202 my $userid = FetchSQLData();
204 # Update the user's password in the profiles table and delete the token
205 # from the tokens table.
206 $dbh->bz_lock_tables('profiles WRITE', 'tokens WRITE');
207 SendSQL("UPDATE profiles
208 SET cryptpassword = $quotedpassword
209 WHERE userid = $userid");
210 SendSQL("DELETE FROM tokens WHERE token = $::quotedtoken");
211 $dbh->bz_unlock_tables();
213 Bugzilla->logout_user_by_id($userid);
215 $vars->{'message'} = "password_changed";
217 print $cgi->header();
218 $template->process("global/message.html.tmpl", $vars)
219 || ThrowTemplateError($template->error());
222 sub confirmChangeEmail {
223 # Return HTTP response headers.
224 print $cgi->header();
226 $vars->{'token'} = $::token;
228 $template->process("account/email/confirm.html.tmpl", $vars)
229 || ThrowTemplateError($template->error());
232 sub changeEmail {
233 my $dbh = Bugzilla->dbh;
235 # Get the user's ID from the tokens table.
236 SendSQL("SELECT userid, eventdata FROM tokens
237 WHERE token = $::quotedtoken");
238 my ($userid, $eventdata) = FetchSQLData();
239 my ($old_email, $new_email) = split(/:/,$eventdata);
240 my $quotednewemail = SqlQuote($new_email);
242 # Check the user entered the correct old email address
243 if(lc($cgi->param('email')) ne lc($old_email)) {
244 ThrowUserError("email_confirmation_failed");
246 # The new email address should be available as this was
247 # confirmed initially so cancel token if it is not still available
248 if (! is_available_username($new_email,$old_email)) {
249 $vars->{'email'} = $new_email; # Needed for Bugzilla::Token::Cancel's mail
250 Bugzilla::Token::Cancel($::token,"account_exists");
251 ThrowUserError("account_exists", { email => $new_email } );
254 # Update the user's login name in the profiles table and delete the token
255 # from the tokens table.
256 $dbh->bz_lock_tables('profiles WRITE', 'tokens WRITE');
257 SendSQL("UPDATE profiles
258 SET login_name = $quotednewemail
259 WHERE userid = $userid");
260 SendSQL("DELETE FROM tokens WHERE token = $::quotedtoken");
261 SendSQL("DELETE FROM tokens WHERE userid = $userid
262 AND tokentype = 'emailnew'");
263 $dbh->bz_unlock_tables();
265 # The email address has been changed, so we need to rederive the groups
266 my $user = new Bugzilla::User($userid);
267 $user->derive_groups;
269 # Return HTTP response headers.
270 print $cgi->header();
272 # Let the user know their email address has been changed.
274 $vars->{'message'} = "login_changed";
276 $template->process("global/message.html.tmpl", $vars)
277 || ThrowTemplateError($template->error());
280 sub cancelChangeEmail {
281 my $dbh = Bugzilla->dbh;
283 # Get the user's ID from the tokens table.
284 SendSQL("SELECT userid, tokentype, eventdata FROM tokens
285 WHERE token = $::quotedtoken");
286 my ($userid, $tokentype, $eventdata) = FetchSQLData();
287 my ($old_email, $new_email) = split(/:/,$eventdata);
289 if($tokentype eq "emailold") {
290 $vars->{'message'} = "emailold_change_cancelled";
292 SendSQL("SELECT login_name FROM profiles WHERE userid = $userid");
293 my $actualemail = FetchSQLData();
295 # check to see if it has been altered
296 if($actualemail ne $old_email) {
297 my $quotedoldemail = SqlQuote($old_email);
299 $dbh->bz_lock_tables('profiles WRITE');
300 SendSQL("UPDATE profiles
301 SET login_name = $quotedoldemail
302 WHERE userid = $userid");
303 $dbh->bz_unlock_tables();
305 # email has changed, so rederive groups
306 # Note that this is done _after_ the tables are unlocked
307 # This is sort of a race condition (given the lack of transactions)
308 # but the user had access to it just now, so it's not a security
309 # issue
311 my $user = new Bugzilla::User($userid);
312 $user->derive_groups;
314 $vars->{'message'} = "email_change_cancelled_reinstated";
317 else {
318 $vars->{'message'} = 'email_change_cancelled'
321 $vars->{'old_email'} = $old_email;
322 $vars->{'new_email'} = $new_email;
323 Bugzilla::Token::Cancel($::token, $vars->{'message'});
325 $dbh->bz_lock_tables('tokens WRITE');
326 SendSQL("DELETE FROM tokens
327 WHERE userid = $userid
328 AND tokentype = 'emailold' OR tokentype = 'emailnew'");
329 $dbh->bz_unlock_tables();
331 # Return HTTP response headers.
332 print $cgi->header();
334 $template->process("global/message.html.tmpl", $vars)
335 || ThrowTemplateError($template->error());