You need to sign in or sign up before continuing.
token.cgi 10.4 KB
Newer Older
1
#!/usr/bin/perl -T
2 3 4
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
#
6 7
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
8

9
use 5.10.1;
10
use strict;
11 12
use warnings;

13
use lib qw(. lib);
14

15
use Bugzilla;
16
use Bugzilla::Constants;
17
use Bugzilla::Util;
18 19 20
use Bugzilla::Error;
use Bugzilla::Token;
use Bugzilla::User;
21

22
use Date::Format;
23 24
use Date::Parse;

25
local our $cgi      = Bugzilla->cgi;
26
local our $template = Bugzilla->template;
27
local our $vars     = {};
28

29
my $action = $cgi->param('a');
30
my $token  = $cgi->param('t');
31

32
Bugzilla->login(LOGIN_OPTIONAL);
33 34 35

# Throw an error if the form does not contain an "action" field specifying
# what the user wants to do.
36
$action || ThrowUserError('unknown_action');
37

38 39
Bugzilla::Token::CleanTokenTable();
my ($user_id, $date, $data, $tokentype) = Bugzilla::Token::GetTokenData($token);
40

41 42 43
# Requesting a new password is the single action which doesn't require a token.
# XXX Ideally, these checks should be done inside the subroutines themselves.
unless ($action eq 'reqpw') {
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
  $tokentype || ThrowUserError("token_does_not_exist");

# Make sure the token is the correct type for the action being taken.
# The { user_error => 'wrong_token_for_*' } trick is to make 012throwables.t happy.
  my $error = {};
  if (grep($action eq $_, qw(cfmpw cxlpw chgpw)) && $tokentype ne 'password') {
    $error = {user_error => 'wrong_token_for_changing_passwd'};
  }
  elsif ($action eq 'cxlem'
    && ($tokentype ne 'emailold' && $tokentype ne 'emailnew'))
  {
    $error = {user_error => 'wrong_token_for_cancelling_email_change'};
  }
  elsif (grep($action eq $_, qw(cfmem chgem)) && $tokentype ne 'emailnew') {
    $error = {user_error => 'wrong_token_for_confirming_email_change'};
  }
  elsif ($action =~ /^(request|confirm|cancel)_new_account$/
    && $tokentype ne 'account')
  {
    $error = {user_error => 'wrong_token_for_creating_account'};
  }

  if (my $user_error = $error->{user_error}) {
    Bugzilla::Token::Cancel($token, $user_error);
    ThrowUserError($user_error);
  }
70 71
}

72
if ($action eq 'reqpw') {
73
  requestChangePassword();
74 75
}
elsif ($action eq 'cfmpw') {
76
  confirmChangePassword($token);
77 78
}
elsif ($action eq 'cxlpw') {
79
  cancelChangePassword($token);
80 81
}
elsif ($action eq 'chgpw') {
82
  changePassword($user_id, $token);
83 84
}
elsif ($action eq 'cfmem') {
85
  confirmChangeEmail($token);
86 87
}
elsif ($action eq 'cxlem') {
88
  cancelChangeEmail($user_id, $data, $tokentype, $token);
89 90
}
elsif ($action eq 'chgem') {
91
  changeEmail($user_id, $data, $token);
92 93
}
elsif ($action eq 'request_new_account') {
94
  request_create_account($date, $data, $token);
95 96
}
elsif ($action eq 'confirm_new_account') {
97
  confirm_create_account($data, $token);
98 99
}
elsif ($action eq 'cancel_new_account') {
100
  cancel_create_account($data, $token);
101 102
}
else {
103
  ThrowUserError('unknown_action', {action => $action});
104 105 106 107 108 109 110 111
}

exit;

################################################################################
# Functions
################################################################################

112 113 114
# If the user is requesting a password change, make sure they submitted
# their login name and it exists in the database, and that the DB module is in
# the list of allowed verification methods.
115
sub requestChangePassword {
116

117 118 119
  # check verification methods
  Bugzilla->user->authorizer->can_change_password
    || ThrowUserError("password_change_requests_not_allowed");
120

121 122 123 124
  # Check the hash token to make sure this user actually submitted
  # the forgotten password form.
  my $token = $cgi->param('token');
  check_hash_token($token, ['reqpw']);
125

126 127
  my $login_name = $cgi->param('loginname')
    or ThrowUserError("login_needed_for_password_change");
128

129 130
  check_email_syntax($login_name);
  my $user = new Bugzilla::User({name => $login_name});
131

132 133 134 135 136
  # Make sure the user account is active.
  if ($user && !$user->is_enabled) {
    ThrowUserError('account_disabled',
      {disabled_reason => get_text('account_disabled', {account => $login_name})});
  }
137

138
  Bugzilla::Token::IssuePasswordToken($user) if $user;
139

140 141 142 143 144 145
  $vars->{'message'}    = "password_change_request";
  $vars->{'login_name'} = $login_name;

  print $cgi->header();
  $template->process("global/message.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
146 147 148
}

sub confirmChangePassword {
149 150
  my $token = shift;
  $vars->{'token'} = $token;
151

152 153 154
  print $cgi->header();
  $template->process("account/password/set-forgotten-password.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
155 156
}

157
sub cancelChangePassword {
158 159 160
  my $token = shift;
  $vars->{'message'} = "password_change_canceled";
  Bugzilla::Token::Cancel($token, $vars->{'message'});
161

162 163 164
  print $cgi->header();
  $template->process("global/message.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
165 166
}

167 168
# If the user is changing their password, make sure they submitted a new
# password and that the new password is valid.
169
sub changePassword {
170 171 172 173 174 175
  my ($user_id, $token) = @_;
  my $dbh = Bugzilla->dbh;

  my $password = $cgi->param('password');
  (defined $password && defined $cgi->param('matchpassword'))
    || ThrowUserError("require_new_password");
176

177
  validate_password($password, $cgi->param('matchpassword'));
178

179 180
  # Make sure that these never show up in the UI under any circumstances.
  $cgi->delete('password', 'matchpassword');
181

182 183 184 185 186 187 188 189
  my $user = Bugzilla::User->check({id => $user_id});
  $user->set_password($password);
  $user->update();
  delete_token($token);
  $dbh->do(
    q{DELETE FROM tokens WHERE userid = ?
               AND tokentype = 'password'}, undef, $user_id
  );
190

191
  Bugzilla->logout_user_by_id($user_id);
192

193
  $vars->{'message'} = "password_changed";
194

195 196 197
  print $cgi->header();
  $template->process("global/message.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
198 199
}

200
sub confirmChangeEmail {
201 202
  my $token = shift;
  $vars->{'token'} = $token;
203

204 205 206
  print $cgi->header();
  $template->process("account/email/confirm.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
207 208 209
}

sub changeEmail {
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
  my ($userid, $eventdata, $token) = @_;
  my $dbh = Bugzilla->dbh;
  my ($old_email, $new_email) = split(/:/, $eventdata);

  $dbh->bz_start_transaction();

  my $user         = Bugzilla::User->check({id => $userid});
  my $realpassword = $user->cryptpassword;
  my $cgipassword  = $cgi->param('password');

  # Make sure the user who wants to change the email address
  # is the real account owner.
  if (bz_crypt($cgipassword, $realpassword) ne $realpassword) {
    ThrowUserError("old_password_incorrect");
  }

  # The new email address should be available as this was
  # confirmed initially so cancel token if it is not still available
  if (!is_available_username($new_email, $old_email)) {
    $vars->{'email'} = $new_email;    # Needed for Bugzilla::Token::Cancel's mail
    Bugzilla::Token::Cancel($token, "account_exists", $vars);
    ThrowUserError("account_exists", {email => $new_email});
  }

  # Update the user's login name in the profiles table.
  $user->set_login($new_email);
  $user->update({keep_session => 1, keep_tokens => 1});
  delete_token($token);
  $dbh->do(
    q{DELETE FROM tokens WHERE userid = ?
               AND tokentype = 'emailnew'}, undef, $userid
  );

  $dbh->bz_commit_transaction();

  # Return HTTP response headers.
  print $cgi->header();

  # Let the user know their email address has been changed.
  $vars->{'message'} = "login_changed";

  $template->process("global/message.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
253 254 255
}

sub cancelChangeEmail {
256 257
  my ($userid, $eventdata, $tokentype, $token) = @_;
  my $dbh = Bugzilla->dbh;
258

259
  $dbh->bz_start_transaction();
260

261
  my ($old_email, $new_email) = split(/:/, $eventdata);
262

263 264 265
  if ($tokentype eq "emailold") {
    $vars->{'message'} = "emailold_change_canceled";
    my $user = Bugzilla::User->check({id => $userid});
266

267 268 269 270
    # check to see if it has been altered
    if ($user->login ne $old_email) {
      $user->set_login($old_email);
      $user->update({keep_tokens => 1});
271

272 273 274 275 276 277
      $vars->{'message'} = "email_change_canceled_reinstated";
    }
  }
  else {
    $vars->{'message'} = 'email_change_canceled';
  }
278

279 280 281
  $vars->{'old_email'} = $old_email;
  $vars->{'new_email'} = $new_email;
  Bugzilla::Token::Cancel($token, $vars->{'message'}, $vars);
282

283 284 285 286
  $dbh->do(
    q{DELETE FROM tokens WHERE userid = ?
               AND tokentype = 'emailold' OR tokentype = 'emailnew'}, undef, $userid
  );
287

288
  $dbh->bz_commit_transaction();
289

290 291
  # Return HTTP response headers.
  print $cgi->header();
292

293 294
  $template->process("global/message.html.tmpl", $vars)
    || ThrowTemplateError($template->error());
295
}
296

297
sub request_create_account {
298
  my ($date, $login_name, $token) = @_;
299

300
  Bugzilla->user->check_account_creation_enabled;
301

302 303 304
  $vars->{'token'}         = $token;
  $vars->{'email'}         = $login_name . Bugzilla->params->{'emailsuffix'};
  $vars->{'expiration_ts'} = ctime(str2time($date) + MAX_TOKEN_AGE * 86400);
305

306 307 308
  print $cgi->header();
  $template->process('account/email/confirm-new.html.tmpl', $vars)
    || ThrowTemplateError($template->error());
309 310 311
}

sub confirm_create_account {
312 313 314
  my ($login_name, $token) = @_;

  Bugzilla->user->check_account_creation_enabled;
315

316 317
  my $password = $cgi->param('passwd1') || '';
  validate_password($password, $cgi->param('passwd2') || '');
318

319 320
  # Make sure that these never show up anywhere in the UI.
  $cgi->delete('passwd1', 'passwd2');
321

322 323 324 325 326
  my $otheruser = Bugzilla::User->create({
    login_name    => $login_name,
    realname      => scalar $cgi->param('realname'),
    cryptpassword => $password
  });
327

328 329
  # Now delete this token.
  delete_token($token);
330

331 332 333
  # Let the user know that their user account has been successfully created.
  $vars->{'message'}   = 'account_created';
  $vars->{'otheruser'} = $otheruser;
334

335 336 337 338
  # Log in the new user using credentials they just gave.
  $cgi->param('Bugzilla_login',    $otheruser->login);
  $cgi->param('Bugzilla_password', $password);
  Bugzilla->login(LOGIN_OPTIONAL);
339

340
  print $cgi->header();
341

342 343
  $template->process('index.html.tmpl', $vars)
    || ThrowTemplateError($template->error());
344 345 346
}

sub cancel_create_account {
347
  my ($login_name, $token) = @_;
348

349 350 351
  $vars->{'message'} = 'account_creation_canceled';
  $vars->{'account'} = $login_name;
  Bugzilla::Token::Cancel($token, $vars->{'message'});
352

353 354 355
  print $cgi->header();
  $template->process('global/message.html.tmpl', $vars)
    || ThrowTemplateError($template->error());
356
}