From 59285f71c6ed0d4db7d4b0455902130a2d7c83bd Mon Sep 17 00:00:00 2001 From: "lpsolit%gmail.com" <> Date: Sun, 20 Aug 2006 01:11:59 +0000 Subject: Bug 87795: Creating an account should send token and wait for confirmation (prevent user account abuse) - Patch by Frédéric Buclin r=mkanat r=bkor a=myk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bugzilla/BugMail.pm | 14 ------------ Bugzilla/Constants.pm | 5 ++++ Bugzilla/DB/Schema.pm | 2 +- Bugzilla/Install/DB.pm | 3 +++ Bugzilla/Token.pm | 62 +++++++++++++++++++++++++++++++++++++------------- Bugzilla/User.pm | 31 +++++++++++++------------ 6 files changed, 71 insertions(+), 46 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/BugMail.pm b/Bugzilla/BugMail.pm index 07ebf226b..35b05d231 100644 --- a/Bugzilla/BugMail.pm +++ b/Bugzilla/BugMail.pm @@ -654,20 +654,6 @@ sub sendMail { return 1; } -# Send the login name and password of the newly created account to the user. -sub MailPassword { - my ($login, $password) = (@_); - my $template = Bugzilla->template; - my $vars = { - mailaddress => $login . Bugzilla->params->{'emailsuffix'}, - login => $login, - password => $password }; - my $msg; - $template->process("email/password.txt.tmpl", $vars, \$msg) - || ThrowTemplateError($template->error()); - MessageToMTA($msg); -} - # Get bug comments for the given period and format them to be used in emails. sub get_comments_by_bug { my ($id, $start, $end) = @_; diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 9493ea400..2d6c2f561 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -120,6 +120,8 @@ use File::Basename; DB_MODULE ROOT_USER ON_WINDOWS + + MAX_TOKEN_AGE ); @Bugzilla::Constants::EXPORT_OK = qw(contenttypes); @@ -295,6 +297,9 @@ use constant SENDMAIL_EXE => '/usr/lib/sendmail.exe'; use constant FIELD_TYPE_UNKNOWN => 0; use constant FIELD_TYPE_FREETEXT => 1; +# The maximum number of days a token will remain valid. +use constant MAX_TOKEN_AGE => 3; + # States that are considered to be "open" for bugs. use constant BUG_STATE_OPEN => ('NEW', 'REOPENED', 'ASSIGNED', 'UNCONFIRMED'); diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index 50785c5b7..1888c76c0 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -715,7 +715,7 @@ use constant ABSTRACT_SCHEMA => { # for these changes. tokens => { FIELDS => [ - userid => {TYPE => 'INT3', NOTNULL => 1} , + userid => {TYPE => 'INT3'}, issuedate => {TYPE => 'DATETIME', NOTNULL => 1} , token => {TYPE => 'varchar(16)', NOTNULL => 1, PRIMARYKEY => 1}, diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index a4ab54260..d34a11f24 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -471,6 +471,9 @@ sub update_table_definitions { $dbh->bz_alter_column('keyworddefs', 'id', {TYPE => 'SMALLSERIAL', NOTNULL => 1, PRIMARYKEY => 1}); + # 2006-08-19 LpSolit@gmail.com - Bug 87795 + $dbh->bz_alter_column('tokens', 'userid', {TYPE => 'INT3'}); + ################################################################ # New --TABLE-- changes should go *** A B O V E *** this point # ################################################################ diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index 447d27507..6dd8baa6e 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -29,6 +29,7 @@ use strict; # Bundle the functions in this file together into the "Bugzilla::Token" package. package Bugzilla::Token; +use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Mailer; use Bugzilla::Util; @@ -37,15 +38,45 @@ use Date::Format; use Date::Parse; ################################################################################ -# Constants +# Public Functions ################################################################################ -# The maximum number of days a token will remain valid. -use constant MAX_TOKEN_AGE => 3; +# 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 = {}; -################################################################################ -# Public Functions -################################################################################ + # Is there already a pending request for this login name? If yes, do not throw + # an error because the user may have lost his email with the token inside. + # But to prevent using this way to mailbomb an email address, make sure + # the last request is at least 10 minutes old before sending a new email. + trick_taint($login_name); + + my $pending_requests = + $dbh->selectrow_array('SELECT COUNT(*) + FROM tokens + WHERE tokentype = ? + AND ' . $dbh->sql_istrcmp('eventdata', '?') . ' + AND issuedate > NOW() - ' . $dbh->sql_interval(10, 'MINUTE'), + undef, ('account', $login_name)); + + ThrowUserError('too_soon_for_new_token', {'type' => 'account'}) if $pending_requests; + + my ($token, $token_ts) = _create_token(undef, 'account', $login_name); + + $vars->{'email'} = $login_name . Bugzilla->params->{'emailsuffix'}; + $vars->{'token_ts'} = $token_ts; + $vars->{'token'} = $token; + + my $message; + $template->process('account/email/request-new.txt.tmpl', $vars, \$message) + || ThrowTemplateError($template->error()); + + MessageToMTA($message); +} sub IssueEmailChangeToken { my ($userid, $old_email, $new_email) = @_; @@ -106,7 +137,7 @@ sub IssuePasswordToken { WHERE ' . $dbh->sql_istrcmp('login_name', '?'), undef, ('password', $loginname)); - ThrowUserError('too_soon_for_new_token') if $too_soon; + ThrowUserError('too_soon_for_new_token', {'type' => 'password'}) if $too_soon; my ($token, $token_ts) = _create_token($userid, 'password', $::ENV{'REMOTE_ADDR'}); @@ -177,26 +208,25 @@ sub GenerateUniqueToken { sub Cancel { my ($token, $cancelaction, $vars) = @_; my $dbh = Bugzilla->dbh; + my $template = Bugzilla->template; $vars ||= {}; # Get information about the token being cancelled. trick_taint($token); - my ($issuedate, $tokentype, $eventdata, $loginname, $realname) = + my ($issuedate, $tokentype, $eventdata, $loginname) = $dbh->selectrow_array('SELECT ' . $dbh->sql_date_format('issuedate') . ', - tokentype, eventdata, login_name, realname + tokentype, eventdata, login_name FROM tokens - INNER JOIN profiles + LEFT JOIN profiles ON tokens.userid = profiles.userid WHERE token = ?', undef, $token); - # Get the email address of the Bugzilla maintainer. - my $maintainer = Bugzilla->params->{'maintainer'}; - - my $template = Bugzilla->template; - + # If we are cancelling the creation of a new user account, then there + # is no entry in the 'profiles' table. + $loginname ||= $eventdata; $vars->{'emailaddress'} = $loginname . Bugzilla->params->{'emailsuffix'}; - $vars->{'maintainer'} = $maintainer; + $vars->{'maintainer'} = Bugzilla->params->{'maintainer'}; $vars->{'remoteaddress'} = $::ENV{'REMOTE_ADDR'}; $vars->{'token'} = $token; $vars->{'tokentype'} = $tokentype; diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 12c680f78..54d84020f 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -1355,9 +1355,8 @@ sub insert_new_user { VALUES (?, ?, NOW(), ?, NOW())', undef, ($user->id, $who, $creation_date_fieldid)); - # Return the password to the calling code so it can be included - # in an email sent to the user. - return $password; + # Return the newly created user account. + return $user; } sub is_available_username { @@ -1377,15 +1376,18 @@ sub is_available_username { # was unsafe and required weird escaping; using substring to pull out # the new/old email addresses and sql_position() to find the delimiter (':') # is cleaner/safer - my $sth = $dbh->prepare( - "SELECT eventdata FROM tokens WHERE tokentype = 'emailold' - AND SUBSTRING(eventdata, 1, (" - . $dbh->sql_position(q{':'}, 'eventdata') . "- 1)) = ? - OR SUBSTRING(eventdata, (" - . $dbh->sql_position(q{':'}, 'eventdata') . "+ 1)) = ?"); - $sth->execute($username, $username); - - if (my ($eventdata) = $sth->fetchrow_array()) { + my $eventdata = $dbh->selectrow_array( + "SELECT eventdata + FROM tokens + WHERE (tokentype = 'emailold' + AND SUBSTRING(eventdata, 1, (" . + $dbh->sql_position(q{':'}, 'eventdata') . "- 1)) = ?) + OR (tokentype = 'emailnew' + AND SUBSTRING(eventdata, (" . + $dbh->sql_position(q{':'}, 'eventdata') . "+ 1)) = ?)", + undef, ($username, $username)); + + if ($eventdata) { # Allow thru owner of token if($old_username && ($eventdata eq "$old_username:$username")) { return 1; @@ -1459,7 +1461,7 @@ Bugzilla::User - Object for a Bugzilla user $user->get_selectable_classifications; # Class Functions - $password = insert_new_user($username, $realname, $password, $disabledtext); + $user = insert_new_user($username, $realname, $password, $disabledtext); =head1 DESCRIPTION @@ -1815,8 +1817,7 @@ Params: $username (scalar, string) - The login name for the new user. be sent depending on the user's email preferences. -Returns: The password for this user, in plain text, so it can be included - in an e-mail sent to the user. +Returns: The Bugzilla::User object representing the new user account. =item C -- cgit v1.2.3-24-g4f1b