aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla/Token.pm')
-rw-r--r--Bugzilla/Token.pm646
1 files changed, 338 insertions, 308 deletions
diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm
index 28122e818..62106c5e5 100644
--- a/Bugzilla/Token.pm
+++ b/Bugzilla/Token.pm
@@ -25,8 +25,8 @@ use Digest::SHA qw(hmac_sha256_base64);
use parent qw(Exporter);
@Bugzilla::Token::EXPORT = qw(issue_api_token issue_session_token
- check_token_data delete_token
- issue_hash_token check_hash_token);
+ check_token_data delete_token
+ issue_hash_token check_hash_token);
use constant SEND_NOW => 1;
@@ -36,394 +36,420 @@ use constant SEND_NOW => 1;
# Create a token used for internal API authentication
sub issue_api_token {
- # Generates a random token, adds it to the tokens table if one does not
- # already exist, and returns the token to the caller.
- my $dbh = Bugzilla->dbh;
- my $user = Bugzilla->user;
- my ($token) = $dbh->selectrow_array("
+
+ # Generates a random token, adds it to the tokens table if one does not
+ # already exist, and returns the token to the caller.
+ my $dbh = Bugzilla->dbh;
+ my $user = Bugzilla->user;
+ my ($token) = $dbh->selectrow_array("
SELECT token FROM tokens
WHERE userid = ? AND tokentype = 'api_token'
- AND (" . $dbh->sql_date_math('issuedate', '+', (MAX_TOKEN_AGE * 24 - 12), 'HOUR') . ") > NOW()",
- undef, $user->id);
- return $token // _create_token($user->id, 'api_token', '');
+ AND ("
+ . $dbh->sql_date_math('issuedate', '+', (MAX_TOKEN_AGE * 24 - 12), 'HOUR')
+ . ") > NOW()", undef, $user->id);
+ return $token // _create_token($user->id, 'api_token', '');
}
# Creates and sends a token to create a new user account.
# It assumes that the login has the correct format and is not already in use.
sub issue_new_user_account_token {
- my $login_name = shift;
- my $dbh = Bugzilla->dbh;
- my $template = Bugzilla->template;
- my $vars = {};
-
- # Is there already a pending request for this login name? If yes, do not throw
- # an error because the user may have lost their email with the token inside.
- # But to prevent using this way to mailbomb an email address, make sure
- # the last request is old enough before sending a new email (default: 10 minutes).
-
- my $pending_requests = $dbh->selectrow_array(
- 'SELECT COUNT(*)
+ my $login_name = shift;
+ my $dbh = Bugzilla->dbh;
+ my $template = Bugzilla->template;
+ my $vars = {};
+
+# Is there already a pending request for this login name? If yes, do not throw
+# an error because the user may have lost their email with the token inside.
+# But to prevent using this way to mailbomb an email address, make sure
+# the last request is old enough before sending a new email (default: 10 minutes).
+
+ my $pending_requests = $dbh->selectrow_array(
+ 'SELECT COUNT(*)
FROM tokens
WHERE tokentype = ?
AND ' . $dbh->sql_istrcmp('eventdata', '?') . '
AND issuedate > '
- . $dbh->sql_date_math('NOW()', '-', ACCOUNT_CHANGE_INTERVAL, 'MINUTE'),
- undef, ('account', $login_name));
+ . $dbh->sql_date_math('NOW()', '-', ACCOUNT_CHANGE_INTERVAL, 'MINUTE'),
+ undef, ('account', $login_name)
+ );
- ThrowUserError('too_soon_for_new_token', {'type' => 'account'}) if $pending_requests;
+ ThrowUserError('too_soon_for_new_token', {'type' => 'account'})
+ if $pending_requests;
- my ($token, $token_ts) = _create_token(undef, 'account', $login_name);
+ my ($token, $token_ts) = _create_token(undef, 'account', $login_name);
- $vars->{'email'} = $login_name . Bugzilla->params->{'emailsuffix'};
- $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400);
- $vars->{'token'} = $token;
+ $vars->{'email'} = $login_name . Bugzilla->params->{'emailsuffix'};
+ $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400);
+ $vars->{'token'} = $token;
- my $message;
- $template->process('account/email/request-new.txt.tmpl', $vars, \$message)
- || ThrowTemplateError($template->error());
+ my $message;
+ $template->process('account/email/request-new.txt.tmpl', $vars, \$message)
+ || ThrowTemplateError($template->error());
- # In 99% of cases, the user getting the confirmation email is the same one
- # who made the request, and so it is reasonable to send the email in the same
- # language used to view the "Create a New Account" page (we cannot use their
- # user prefs as the user has no account yet!).
- MessageToMTA($message, SEND_NOW);
+ # In 99% of cases, the user getting the confirmation email is the same one
+ # who made the request, and so it is reasonable to send the email in the same
+ # language used to view the "Create a New Account" page (we cannot use their
+ # user prefs as the user has no account yet!).
+ MessageToMTA($message, SEND_NOW);
}
sub IssueEmailChangeToken {
- my $new_email = shift;
- my $user = Bugzilla->user;
+ my $new_email = shift;
+ my $user = Bugzilla->user;
- my ($token, $token_ts) = _create_token($user->id, 'emailold', $user->login . ":$new_email");
- my $newtoken = _create_token($user->id, 'emailnew', $user->login . ":$new_email");
+ my ($token, $token_ts)
+ = _create_token($user->id, 'emailold', $user->login . ":$new_email");
+ my $newtoken
+ = _create_token($user->id, 'emailnew', $user->login . ":$new_email");
- # Mail the user the token along with instructions for using it.
+ # Mail the user the token along with instructions for using it.
- my $template = Bugzilla->template_inner($user->setting('lang'));
- my $vars = {};
+ my $template = Bugzilla->template_inner($user->setting('lang'));
+ my $vars = {};
- $vars->{'newemailaddress'} = $new_email . Bugzilla->params->{'emailsuffix'};
- $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400);
+ $vars->{'newemailaddress'} = $new_email . Bugzilla->params->{'emailsuffix'};
+ $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400);
- # First send an email to the new address. If this one doesn't exist,
- # then the whole process must stop immediately. This means the email must
- # be sent immediately and must not be stored in the queue.
- $vars->{'token'} = $newtoken;
+ # First send an email to the new address. If this one doesn't exist,
+ # then the whole process must stop immediately. This means the email must
+ # be sent immediately and must not be stored in the queue.
+ $vars->{'token'} = $newtoken;
- my $message;
- $template->process('account/email/change-new.txt.tmpl', $vars, \$message)
- || ThrowTemplateError($template->error());
+ my $message;
+ $template->process('account/email/change-new.txt.tmpl', $vars, \$message)
+ || ThrowTemplateError($template->error());
- MessageToMTA($message, SEND_NOW);
+ MessageToMTA($message, SEND_NOW);
- # If we come here, then the new address exists. We now email the current
- # address, but we don't want to stop the process if it no longer exists,
- # to give a chance to the user to confirm the email address change.
- $vars->{'token'} = $token;
+ # If we come here, then the new address exists. We now email the current
+ # address, but we don't want to stop the process if it no longer exists,
+ # to give a chance to the user to confirm the email address change.
+ $vars->{'token'} = $token;
- $message = '';
- $template->process('account/email/change-old.txt.tmpl', $vars, \$message)
- || ThrowTemplateError($template->error());
+ $message = '';
+ $template->process('account/email/change-old.txt.tmpl', $vars, \$message)
+ || ThrowTemplateError($template->error());
- eval { MessageToMTA($message, SEND_NOW); };
+ eval { MessageToMTA($message, SEND_NOW); };
- # Give the user a chance to cancel the process even if he never got
- # the email above. The token is required.
- return $token;
+ # Give the user a chance to cancel the process even if he never got
+ # the email above. The token is required.
+ return $token;
}
# Generates a random token, adds it to the tokens table, and sends it
# to the user with instructions for using it to change their password.
sub IssuePasswordToken {
- my $user = shift;
- my $dbh = Bugzilla->dbh;
+ my $user = shift;
+ my $dbh = Bugzilla->dbh;
- my $too_soon = $dbh->selectrow_array(
- 'SELECT 1 FROM tokens
+ my $too_soon = $dbh->selectrow_array(
+ 'SELECT 1 FROM tokens
WHERE userid = ? AND tokentype = ?
- AND issuedate > '
- . $dbh->sql_date_math('NOW()', '-', ACCOUNT_CHANGE_INTERVAL, 'MINUTE'),
- undef, ($user->id, 'password'));
+ AND issuedate > '
+ . $dbh->sql_date_math('NOW()', '-', ACCOUNT_CHANGE_INTERVAL, 'MINUTE'),
+ undef, ($user->id, 'password')
+ );
- ThrowUserError('too_soon_for_new_token', {'type' => 'password'}) if $too_soon;
+ ThrowUserError('too_soon_for_new_token', {'type' => 'password'}) if $too_soon;
- my $ip_addr = remote_ip();
- my ($token, $token_ts) = _create_token($user->id, 'password', $ip_addr);
+ my $ip_addr = remote_ip();
+ my ($token, $token_ts) = _create_token($user->id, 'password', $ip_addr);
- # Mail the user the token along with instructions for using it.
- my $template = Bugzilla->template_inner($user->setting('lang'));
- my $vars = {};
+ # Mail the user the token along with instructions for using it.
+ my $template = Bugzilla->template_inner($user->setting('lang'));
+ my $vars = {};
- $vars->{'token'} = $token;
- $vars->{'ip_addr'} = $ip_addr;
- $vars->{'emailaddress'} = $user->email;
- $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400);
- # The user is not logged in (else they wouldn't request a new password).
- # So we have to pass this information to the template.
- $vars->{'timezone'} = $user->timezone;
-
- my $message = "";
- $template->process("account/password/forgotten-password.txt.tmpl",
- $vars, \$message)
- || ThrowTemplateError($template->error());
+ $vars->{'token'} = $token;
+ $vars->{'ip_addr'} = $ip_addr;
+ $vars->{'emailaddress'} = $user->email;
+ $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400);
+
+ # The user is not logged in (else they wouldn't request a new password).
+ # So we have to pass this information to the template.
+ $vars->{'timezone'} = $user->timezone;
- MessageToMTA($message);
+ my $message = "";
+ $template->process("account/password/forgotten-password.txt.tmpl",
+ $vars, \$message)
+ || ThrowTemplateError($template->error());
+
+ MessageToMTA($message);
}
sub issue_session_token {
- # Generates a random token, adds it to the tokens table, and returns
- # the token to the caller.
- my $data = shift;
- return _create_token(Bugzilla->user->id, 'session', $data);
+ # Generates a random token, adds it to the tokens table, and returns
+ # the token to the caller.
+
+ my $data = shift;
+ return _create_token(Bugzilla->user->id, 'session', $data);
}
sub issue_hash_token {
- my ($data, $time) = @_;
- $data ||= [];
- $time ||= time();
-
- # For the user ID, use the actual ID if the user is logged in.
- # Otherwise, use the remote IP, in case this is for something
- # such as creating an account or logging in.
- my $user_id = Bugzilla->user->id || remote_ip();
-
- # The concatenated string is of the form
- # token creation time + user ID (either ID or remote IP) + data
- my @args = ($time, $user_id, @$data);
-
- my $token = join('*', @args);
- # Wide characters cause Digest::SHA to die.
- if (Bugzilla->params->{'utf8'}) {
- utf8::encode($token) if utf8::is_utf8($token);
- }
- $token = hmac_sha256_base64($token, Bugzilla->localconfig->{'site_wide_secret'});
- $token =~ s/\+/-/g;
- $token =~ s/\//_/g;
-
- # Prepend the token creation time, unencrypted, so that the token
- # lifetime can be validated.
- return $time . '-' . $token;
+ my ($data, $time) = @_;
+ $data ||= [];
+ $time ||= time();
+
+ # For the user ID, use the actual ID if the user is logged in.
+ # Otherwise, use the remote IP, in case this is for something
+ # such as creating an account or logging in.
+ my $user_id = Bugzilla->user->id || remote_ip();
+
+ # The concatenated string is of the form
+ # token creation time + user ID (either ID or remote IP) + data
+ my @args = ($time, $user_id, @$data);
+
+ my $token = join('*', @args);
+
+ # Wide characters cause Digest::SHA to die.
+ if (Bugzilla->params->{'utf8'}) {
+ utf8::encode($token) if utf8::is_utf8($token);
+ }
+ $token
+ = hmac_sha256_base64($token, Bugzilla->localconfig->{'site_wide_secret'});
+ $token =~ s/\+/-/g;
+ $token =~ s/\//_/g;
+
+ # Prepend the token creation time, unencrypted, so that the token
+ # lifetime can be validated.
+ return $time . '-' . $token;
}
sub check_hash_token {
- my ($token, $data) = @_;
- $data ||= [];
- my ($time, $expected_token);
-
- if ($token) {
- ($time, undef) = split(/-/, $token);
- # Regenerate the token based on the information we have.
- $expected_token = issue_hash_token($data, $time);
- }
+ my ($token, $data) = @_;
+ $data ||= [];
+ my ($time, $expected_token);
- if (!$token
- || $expected_token ne $token
- || time() - $time > MAX_TOKEN_AGE * 86400)
- {
- my $template = Bugzilla->template;
- my $vars = {};
- $vars->{'script_name'} = basename($0);
- $vars->{'token'} = issue_hash_token($data);
- $vars->{'reason'} = (!$token) ? 'missing_token' :
- ($expected_token ne $token) ? 'invalid_token' :
- 'expired_token';
- print Bugzilla->cgi->header();
- $template->process('global/confirm-action.html.tmpl', $vars)
- || ThrowTemplateError($template->error());
- exit;
- }
+ if ($token) {
+ ($time, undef) = split(/-/, $token);
+
+ # Regenerate the token based on the information we have.
+ $expected_token = issue_hash_token($data, $time);
+ }
+
+ if (!$token
+ || $expected_token ne $token
+ || time() - $time > MAX_TOKEN_AGE * 86400)
+ {
+ my $template = Bugzilla->template;
+ my $vars = {};
+ $vars->{'script_name'} = basename($0);
+ $vars->{'token'} = issue_hash_token($data);
+ $vars->{'reason'}
+ = (!$token) ? 'missing_token'
+ : ($expected_token ne $token) ? 'invalid_token'
+ : 'expired_token';
+ print Bugzilla->cgi->header();
+ $template->process('global/confirm-action.html.tmpl', $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
- # If we come here, then the token is valid and not too old.
- return 1;
+ # If we come here, then the token is valid and not too old.
+ return 1;
}
sub CleanTokenTable {
- my $dbh = Bugzilla->dbh;
- $dbh->do('DELETE FROM tokens
- WHERE ' . $dbh->sql_to_days('NOW()') . ' - ' .
- $dbh->sql_to_days('issuedate') . ' >= ?',
- undef, MAX_TOKEN_AGE);
+ my $dbh = Bugzilla->dbh;
+ $dbh->do(
+ 'DELETE FROM tokens
+ WHERE '
+ . $dbh->sql_to_days('NOW()') . ' - '
+ . $dbh->sql_to_days('issuedate')
+ . ' >= ?', undef, MAX_TOKEN_AGE
+ );
}
sub GenerateUniqueToken {
- # Generates a unique random token. Uses generate_random_password
- # for the tokens themselves and checks uniqueness by searching for
- # the token in the "tokens" table. Gives up if it can't come up
- # with a token after about one hundred tries.
- my ($table, $column) = @_;
-
- my $token;
- my $duplicate = 1;
- my $tries = 0;
- $table ||= "tokens";
- $column ||= "token";
-
- my $dbh = Bugzilla->dbh;
- my $sth = $dbh->prepare("SELECT 1 FROM $table WHERE $column = ?");
-
- while ($duplicate) {
- ++$tries;
- if ($tries > 100) {
- ThrowCodeError("token_generation_error");
- }
- $token = generate_random_password();
- $sth->execute($token);
- $duplicate = $sth->fetchrow_array;
+
+ # Generates a unique random token. Uses generate_random_password
+ # for the tokens themselves and checks uniqueness by searching for
+ # the token in the "tokens" table. Gives up if it can't come up
+ # with a token after about one hundred tries.
+ my ($table, $column) = @_;
+
+ my $token;
+ my $duplicate = 1;
+ my $tries = 0;
+ $table ||= "tokens";
+ $column ||= "token";
+
+ my $dbh = Bugzilla->dbh;
+ my $sth = $dbh->prepare("SELECT 1 FROM $table WHERE $column = ?");
+
+ while ($duplicate) {
+ ++$tries;
+ if ($tries > 100) {
+ ThrowCodeError("token_generation_error");
}
- return $token;
+ $token = generate_random_password();
+ $sth->execute($token);
+ $duplicate = $sth->fetchrow_array;
+ }
+ return $token;
}
# Cancels a previously issued token and notifies the user.
# This should only happen when the user accidentally makes a token request
# or when a malicious hacker makes a token request on behalf of a user.
sub Cancel {
- my ($token, $cancelaction, $vars) = @_;
- my $dbh = Bugzilla->dbh;
- $vars ||= {};
-
- # Get information about the token being canceled.
- trick_taint($token);
- my ($db_token, $issuedate, $tokentype, $eventdata, $userid) =
- $dbh->selectrow_array('SELECT token, ' . $dbh->sql_date_format('issuedate') . ',
+ my ($token, $cancelaction, $vars) = @_;
+ my $dbh = Bugzilla->dbh;
+ $vars ||= {};
+
+ # Get information about the token being canceled.
+ trick_taint($token);
+ my ($db_token, $issuedate, $tokentype, $eventdata, $userid)
+ = $dbh->selectrow_array(
+ 'SELECT token, '
+ . $dbh->sql_date_format('issuedate') . ',
tokentype, eventdata, userid
FROM tokens
- WHERE token = ?',
- undef, $token);
-
- # Some DBs such as MySQL are case-insensitive by default so we do
- # a quick comparison to make sure the tokens are indeed the same.
- (defined $db_token && $db_token eq $token)
- || ThrowCodeError("cancel_token_does_not_exist");
-
- # If we are canceling the creation of a new user account, then there
- # is no entry in the 'profiles' table.
- my $user = new Bugzilla::User($userid);
-
- $vars->{'emailaddress'} = $userid ? $user->email : $eventdata;
- $vars->{'remoteaddress'} = remote_ip();
- $vars->{'token'} = $token;
- $vars->{'tokentype'} = $tokentype;
- $vars->{'issuedate'} = $issuedate;
- # The user is probably not logged in.
- # So we have to pass this information to the template.
- $vars->{'timezone'} = $user->timezone;
- $vars->{'eventdata'} = $eventdata;
- $vars->{'cancelaction'} = $cancelaction;
-
- # Notify the user via email about the cancellation.
- my $template = Bugzilla->template_inner($user->setting('lang'));
-
- my $message;
- $template->process("account/cancel-token.txt.tmpl", $vars, \$message)
- || ThrowTemplateError($template->error());
+ WHERE token = ?', undef, $token
+ );
- MessageToMTA($message);
+ # Some DBs such as MySQL are case-insensitive by default so we do
+ # a quick comparison to make sure the tokens are indeed the same.
+ (defined $db_token && $db_token eq $token)
+ || ThrowCodeError("cancel_token_does_not_exist");
- # Delete the token from the database.
- delete_token($token);
+ # If we are canceling the creation of a new user account, then there
+ # is no entry in the 'profiles' table.
+ my $user = new Bugzilla::User($userid);
+
+ $vars->{'emailaddress'} = $userid ? $user->email : $eventdata;
+ $vars->{'remoteaddress'} = remote_ip();
+ $vars->{'token'} = $token;
+ $vars->{'tokentype'} = $tokentype;
+ $vars->{'issuedate'} = $issuedate;
+
+ # The user is probably not logged in.
+ # So we have to pass this information to the template.
+ $vars->{'timezone'} = $user->timezone;
+ $vars->{'eventdata'} = $eventdata;
+ $vars->{'cancelaction'} = $cancelaction;
+
+ # Notify the user via email about the cancellation.
+ my $template = Bugzilla->template_inner($user->setting('lang'));
+
+ my $message;
+ $template->process("account/cancel-token.txt.tmpl", $vars, \$message)
+ || ThrowTemplateError($template->error());
+
+ MessageToMTA($message);
+
+ # Delete the token from the database.
+ delete_token($token);
}
sub DeletePasswordTokens {
- my ($userid, $reason) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($userid, $reason) = @_;
+ my $dbh = Bugzilla->dbh;
- detaint_natural($userid);
- my $tokens = $dbh->selectcol_arrayref('SELECT token FROM tokens
+ detaint_natural($userid);
+ my $tokens = $dbh->selectcol_arrayref(
+ 'SELECT token FROM tokens
WHERE userid = ? AND tokentype = ?',
- undef, ($userid, 'password'));
+ undef, ($userid, 'password')
+ );
- foreach my $token (@$tokens) {
- Bugzilla::Token::Cancel($token, $reason);
- }
+ foreach my $token (@$tokens) {
+ Bugzilla::Token::Cancel($token, $reason);
+ }
}
-# Returns an email change token if the user has one.
+# Returns an email change token if the user has one.
sub HasEmailChangeToken {
- my $userid = shift;
- my $dbh = Bugzilla->dbh;
+ my $userid = shift;
+ my $dbh = Bugzilla->dbh;
- my $token = $dbh->selectrow_array('SELECT token FROM tokens
+ my $token = $dbh->selectrow_array(
+ 'SELECT token FROM tokens
WHERE userid = ?
- AND (tokentype = ? OR tokentype = ?) ' .
- $dbh->sql_limit(1),
- undef, ($userid, 'emailnew', 'emailold'));
- return $token;
+ AND (tokentype = ? OR tokentype = ?) '
+ . $dbh->sql_limit(1), undef, ($userid, 'emailnew', 'emailold')
+ );
+ return $token;
}
# Returns the userid, issuedate and eventdata for the specified token
sub GetTokenData {
- my ($token) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($token) = @_;
+ my $dbh = Bugzilla->dbh;
- return unless defined $token;
- $token = clean_text($token);
- trick_taint($token);
+ return unless defined $token;
+ $token = clean_text($token);
+ trick_taint($token);
- my @token_data = $dbh->selectrow_array(
- "SELECT token, userid, " . $dbh->sql_date_format('issuedate') . ", eventdata, tokentype
+ my @token_data = $dbh->selectrow_array(
+ "SELECT token, userid, "
+ . $dbh->sql_date_format('issuedate')
+ . ", eventdata, tokentype
FROM tokens
- WHERE token = ?", undef, $token);
+ WHERE token = ?", undef, $token
+ );
- # Some DBs such as MySQL are case-insensitive by default so we do
- # a quick comparison to make sure the tokens are indeed the same.
- my $db_token = shift @token_data;
- return undef if (!defined $db_token || $db_token ne $token);
+ # Some DBs such as MySQL are case-insensitive by default so we do
+ # a quick comparison to make sure the tokens are indeed the same.
+ my $db_token = shift @token_data;
+ return undef if (!defined $db_token || $db_token ne $token);
- return @token_data;
+ return @token_data;
}
# Deletes specified token
sub delete_token {
- my ($token) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($token) = @_;
+ my $dbh = Bugzilla->dbh;
- return unless defined $token;
- trick_taint($token);
+ return unless defined $token;
+ trick_taint($token);
- $dbh->do("DELETE FROM tokens WHERE token = ?", undef, $token);
+ $dbh->do("DELETE FROM tokens WHERE token = ?", undef, $token);
}
# Given a token, makes sure it comes from the currently logged in user
# and match the expected event. Returns 1 on success, else displays a warning.
sub check_token_data {
- my ($token, $expected_action, $alternate_script) = @_;
- my $user = Bugzilla->user;
- my $template = Bugzilla->template;
- my $cgi = Bugzilla->cgi;
-
- my ($creator_id, $date, $token_action) = GetTokenData($token);
- unless ($creator_id
- && $creator_id == $user->id
- && $token_action eq $expected_action)
- {
- # Something is going wrong. Ask confirmation before processing.
- # It is possible that someone tried to trick an administrator.
- # In this case, we want to know their name!
- require Bugzilla::User;
-
- my $vars = {};
- $vars->{'abuser'} = Bugzilla::User->new($creator_id)->identity;
- $vars->{'token_action'} = $token_action;
- $vars->{'expected_action'} = $expected_action;
- $vars->{'script_name'} = basename($0);
- $vars->{'alternate_script'} = $alternate_script || basename($0);
-
- # Now is a good time to remove old tokens from the DB.
- CleanTokenTable();
-
- # If no token was found, create a valid token for the given action.
- unless ($creator_id) {
- $token = issue_session_token($expected_action);
- $cgi->param('token', $token);
- }
-
- print $cgi->header();
-
- $template->process('admin/confirm-action.html.tmpl', $vars)
- || ThrowTemplateError($template->error());
- exit;
+ my ($token, $expected_action, $alternate_script) = @_;
+ my $user = Bugzilla->user;
+ my $template = Bugzilla->template;
+ my $cgi = Bugzilla->cgi;
+
+ my ($creator_id, $date, $token_action) = GetTokenData($token);
+ unless ($creator_id
+ && $creator_id == $user->id
+ && $token_action eq $expected_action)
+ {
+ # Something is going wrong. Ask confirmation before processing.
+ # It is possible that someone tried to trick an administrator.
+ # In this case, we want to know their name!
+ require Bugzilla::User;
+
+ my $vars = {};
+ $vars->{'abuser'} = Bugzilla::User->new($creator_id)->identity;
+ $vars->{'token_action'} = $token_action;
+ $vars->{'expected_action'} = $expected_action;
+ $vars->{'script_name'} = basename($0);
+ $vars->{'alternate_script'} = $alternate_script || basename($0);
+
+ # Now is a good time to remove old tokens from the DB.
+ CleanTokenTable();
+
+ # If no token was found, create a valid token for the given action.
+ unless ($creator_id) {
+ $token = issue_session_token($expected_action);
+ $cgi->param('token', $token);
}
- return 1;
+
+ print $cgi->header();
+
+ $template->process('admin/confirm-action.html.tmpl', $vars)
+ || ThrowTemplateError($template->error());
+ exit;
+ }
+ return 1;
}
################################################################################
@@ -433,34 +459,38 @@ sub check_token_data {
# Generates a unique token and inserts it into the database
# Returns the token and the token timestamp
sub _create_token {
- my ($userid, $tokentype, $eventdata) = @_;
- my $dbh = Bugzilla->dbh;
+ my ($userid, $tokentype, $eventdata) = @_;
+ my $dbh = Bugzilla->dbh;
- detaint_natural($userid) if defined $userid;
- trick_taint($tokentype);
- trick_taint($eventdata);
+ detaint_natural($userid) if defined $userid;
+ trick_taint($tokentype);
+ trick_taint($eventdata);
- my $is_shadow = Bugzilla->is_shadow_db;
- $dbh = Bugzilla->switch_to_main_db() if $is_shadow;
+ my $is_shadow = Bugzilla->is_shadow_db;
+ $dbh = Bugzilla->switch_to_main_db() if $is_shadow;
- $dbh->bz_start_transaction();
+ $dbh->bz_start_transaction();
- my $token = GenerateUniqueToken();
+ my $token = GenerateUniqueToken();
- $dbh->do("INSERT INTO tokens (userid, issuedate, token, tokentype, eventdata)
- VALUES (?, NOW(), ?, ?, ?)", undef, ($userid, $token, $tokentype, $eventdata));
+ $dbh->do(
+ "INSERT INTO tokens (userid, issuedate, token, tokentype, eventdata)
+ VALUES (?, NOW(), ?, ?, ?)", undef,
+ ($userid, $token, $tokentype, $eventdata)
+ );
- $dbh->bz_commit_transaction();
+ $dbh->bz_commit_transaction();
- if (wantarray) {
- my (undef, $token_ts, undef) = GetTokenData($token);
- $token_ts = str2time($token_ts);
- Bugzilla->switch_to_shadow_db() if $is_shadow;
- return ($token, $token_ts);
- } else {
- Bugzilla->switch_to_shadow_db() if $is_shadow;
- return $token;
- }
+ if (wantarray) {
+ my (undef, $token_ts, undef) = GetTokenData($token);
+ $token_ts = str2time($token_ts);
+ Bugzilla->switch_to_shadow_db() if $is_shadow;
+ return ($token, $token_ts);
+ }
+ else {
+ Bugzilla->switch_to_shadow_db() if $is_shadow;
+ return $token;
+ }
}
1;