diff options
author | Frédéric Buclin <LpSolit@gmail.com> | 2016-04-27 18:50:13 +0200 |
---|---|---|
committer | Frédéric Buclin <LpSolit@gmail.com> | 2016-04-27 18:50:13 +0200 |
commit | 3891b63a1eb52076337885487f251a10580a4a85 (patch) | |
tree | db1463894b756a6bb5114644feeec704ec886eb5 | |
parent | c44470a368465adfe329fcfc32492829a21878da (diff) | |
download | bugzilla-3891b63a1eb52076337885487f251a10580a4a85.tar.gz bugzilla-3891b63a1eb52076337885487f251a10580a4a85.tar.xz |
Bug 218917 - Allow the login name to be different from the email address
Original patch by Gervase Markham
r=gerv a=dkl
78 files changed, 1014 insertions, 598 deletions
diff --git a/Bugzilla/API/1_0/Resource/Bug.pm b/Bugzilla/API/1_0/Resource/Bug.pm index 781ac3176..b0182a5e3 100644 --- a/Bugzilla/API/1_0/Resource/Bug.pm +++ b/Bugzilla/API/1_0/Resource/Bug.pm @@ -509,7 +509,7 @@ sub _translate_comment { my $comment_hash = { id => as_int($comment->id), bug_id => as_int($comment->bug_id), - creator => as_email($comment->author->login), + creator => as_login($comment->author->login), time => as_datetime($comment->creation_ts), creation_time => as_datetime($comment->creation_ts), is_private => as_boolean($comment->is_private), @@ -1431,7 +1431,7 @@ sub _bug_to_hash { $item{alias} = as_string_array($bug->alias); } if (filter_wants $params, 'assigned_to') { - $item{'assigned_to'} = as_email($bug->assigned_to->login); + $item{'assigned_to'} = as_login($bug->assigned_to->login); $item{'assigned_to_detail'} = $self->_user_to_hash($bug->assigned_to, $params, undef, 'assigned_to'); } if (filter_wants $params, 'blocks') { @@ -1444,14 +1444,14 @@ sub _bug_to_hash { $item{component} = as_string($bug->component); } if (filter_wants $params, 'cc') { - $item{'cc'} = as_email_array($bug->cc); + $item{'cc'} = as_login_array($bug->cc); $item{'cc_detail'} = [ map { $self->_user_to_hash($_, $params, undef, 'cc') } @{ $bug->cc_users } ]; } if (filter_wants $params, 'creation_time') { $item{'creation_time'} = as_datetime($bug->creation_ts); } if (filter_wants $params, 'creator') { - $item{'creator'} = as_email($bug->reporter->login); + $item{'creator'} = as_login($bug->reporter->login); $item{'creator_detail'} = $self->_user_to_hash($bug->reporter, $params, undef, 'creator'); } if (filter_wants $params, 'depends_on') { @@ -1477,7 +1477,7 @@ sub _bug_to_hash { } if (filter_wants $params, 'qa_contact') { my $qa_login = $bug->qa_contact ? $bug->qa_contact->login : ''; - $item{'qa_contact'} = as_email($qa_login); + $item{'qa_contact'} = as_login($qa_login); if ($bug->qa_contact) { $item{'qa_contact_detail'} = $self->_user_to_hash($bug->qa_contact, $params, undef, 'qa_contact'); } @@ -1546,8 +1546,7 @@ sub _user_to_hash { my $item = filter $filters, { id => as_int($user->id), real_name => as_string($user->name), - name => as_email($user->login), - email => as_email($user->email), + name => as_login($user->login), }, $types, $prefix; return $item; } @@ -1571,7 +1570,7 @@ sub _attachment_to_hash { # creator requires an extra lookup, so we only send them if # the filter wants them. if (filter_wants $filters, 'creator', $types, $prefix) { - $item->{'creator'} = as_email($attach->attacher->login); + $item->{'creator'} = as_login($attach->attacher->login); } if (filter_wants $filters, 'data', $types, $prefix) { @@ -1603,7 +1602,7 @@ sub _flag_to_hash { foreach my $field (qw(setter requestee)) { my $field_id = $field . "_id"; - $item->{$field} = as_email($flag->$field->login) + $item->{$field} = as_login($flag->$field->login) if $flag->$field_id; } @@ -2672,10 +2671,6 @@ C<string> The 'real' name for this user, if any. C<string> The user's Bugzilla login. -=item C<email> - -C<string> The user's email address. Currently this is the same value as the name. - =back =back diff --git a/Bugzilla/API/1_0/Resource/Bugzilla.pm b/Bugzilla/API/1_0/Resource/Bugzilla.pm index f5bb2f417..fc2e15e90 100644 --- a/Bugzilla/API/1_0/Resource/Bugzilla.pm +++ b/Bugzilla/API/1_0/Resource/Bugzilla.pm @@ -69,7 +69,6 @@ use constant PARAMETERS_LOGGED_IN => qw( defaultseverity duplicate_or_move_bug_status emailregexpdesc - emailsuffix letsubmitterchoosemilestone letsubmitterchoosepriority mailfrom @@ -82,6 +81,7 @@ use constant PARAMETERS_LOGGED_IN => qw( requirelogin search_allow_no_criteria urlbase + use_email_as_login use_see_also useclassification usemenuforusers @@ -449,7 +449,6 @@ A logged-in user can access the following parameters (listed alphabetically): C<defaultseverity>, C<duplicate_or_move_bug_status>, C<emailregexpdesc>, - C<emailsuffix>, C<letsubmitterchoosemilestone>, C<letsubmitterchoosepriority>, C<mailfrom>, @@ -462,6 +461,7 @@ A logged-in user can access the following parameters (listed alphabetically): C<requirelogin>, C<search_allow_no_criteria>, C<urlbase>, + C<use_email_as_login>, C<use_see_also>, C<useclassification>, C<usemenuforusers>, diff --git a/Bugzilla/API/1_0/Resource/Group.pm b/Bugzilla/API/1_0/Resource/Group.pm index 43a2521fb..f3b55b3fd 100644 --- a/Bugzilla/API/1_0/Resource/Group.pm +++ b/Bugzilla/API/1_0/Resource/Group.pm @@ -247,8 +247,8 @@ sub _get_group_membership { map {{ id => as_int($_->id), real_name => as_string($_->name), - name => as_string($_->login), - email => as_string($_->email), + name => as_login($_->login), + email => as_email($_->email), can_login => as_boolean($_->is_enabled), email_enabled => as_boolean($_->email_enabled), login_denied_text => as_string($_->disabledtext), @@ -571,12 +571,12 @@ C<string> The actual name of the user. =item email -C<string> The email address of the user. +C<string> If you are in the editusers group, returns the email address of the +user, else returns nothing. =item name -C<string> The login name of the user. Note that in some situations this is -different than their email. +C<string> The login name of the user. =item can_login diff --git a/Bugzilla/API/1_0/Resource/Product.pm b/Bugzilla/API/1_0/Resource/Product.pm index d3576a734..02581b0ed 100644 --- a/Bugzilla/API/1_0/Resource/Product.pm +++ b/Bugzilla/API/1_0/Resource/Product.pm @@ -322,9 +322,9 @@ sub _component_to_hash { name => as_string($component->name), description => as_string($component->description), default_assigned_to => - as_email($component->default_assignee->login), + as_login($component->default_assignee->login), default_qa_contact => - as_email($component->default_qa_contact ? + as_login($component->default_qa_contact ? $component->default_qa_contact->login : ""), sort_key => 0, # sort_key is returned to match Bug.fields is_active => as_boolean($component->is_active), diff --git a/Bugzilla/API/1_0/Resource/User.pm b/Bugzilla/API/1_0/Resource/User.pm index a214bd81f..101a70529 100644 --- a/Bugzilla/API/1_0/Resource/User.pm +++ b/Bugzilla/API/1_0/Resource/User.pm @@ -53,13 +53,11 @@ use constant PUBLIC_METHODS => qw( ); use constant MAPPED_FIELDS => { - email => 'login', full_name => 'name', login_denied_text => 'disabledtext', }; use constant MAPPED_RETURNS => { - login_name => 'email', realname => 'full_name', disabledtext => 'login_denied_text', }; @@ -158,8 +156,11 @@ sub offer_account_by_email { my $email = trim($params->{email}) || ThrowCodeError('param_required', { param => 'email' }); + my $login = Bugzilla->params->{use_email_as_login} ? $email : trim($params->{login}); + $login or ThrowCodeError('param_required', { param => 'login' }); + Bugzilla->user->check_account_creation_enabled; - Bugzilla->user->check_and_send_account_creation_confirmation($email); + Bugzilla->user->check_and_send_account_creation_confirmation($login, $email); return undef; } @@ -174,11 +175,16 @@ sub create { my $email = trim($params->{email}) || ThrowCodeError('param_required', { param => 'email' }); + + my $login = Bugzilla->params->{use_email_as_login} ? $email : trim($params->{login}); + $login or ThrowCodeError('param_required', { param => 'login' }); + my $realname = trim($params->{full_name}); my $password = trim($params->{password}) || '*'; my $user = Bugzilla::User->create({ - login_name => $email, + login_name => $login, + email => $email, realname => $realname, cryptpassword => $password }); @@ -225,7 +231,7 @@ sub get { @users = map { filter $params, { id => as_int($_->id), real_name => as_string($_->name), - name => as_email($_->login), + name => as_login($_->login), } } @$in_group; return { users => \@users }; @@ -277,12 +283,12 @@ sub get { my $user_info = filter $params, { id => as_int($user->id), real_name => as_string($user->name), - name => as_email($user->login), - email => as_email($user->email), + name => as_login($user->login), can_login => as_boolean($user->is_enabled ? 1 : 0), }; if (Bugzilla->user->in_group('editusers')) { + $user_info->{email} = as_email($user->email), $user_info->{email_enabled} = as_boolean($user->email_enabled); $user_info->{login_denied_text} = as_string($user->disabledtext); } @@ -651,6 +657,8 @@ This is the recommended way to create a Bugzilla account. =item C<email> (string) - the email to send the offer to. +=item C<login> (string) - the login of the user account. + =back =item B<Returns> (nothing) @@ -699,6 +707,11 @@ are the same as below. =item C<email> (string) - The email address for the new user. +=item C<login> (string) - The login name for the new user. +If the installation has the C<use_email_as_login> parameter switched on, then +this parameter is ignored, and the value of the C<email> parameter will +be used as the login name for the new account. + =item C<full_name> (string) B<Optional> - The user's full name. Will be set to empty if not specified. @@ -768,7 +781,7 @@ C<array> Contains ids of user to update. =item C<names> -C<array> Contains email/login of user to update. +C<array> Contains login of user to update. =item C<full_name> @@ -996,8 +1009,7 @@ C<string> The email address of the user. =item name -C<string> The login name of the user. Note that in some situations this is -different than their email. +C<string> The login name of the user. =item can_login @@ -1083,7 +1095,7 @@ C<string> The CGI parameters for the saved report. B<Note>: If you are not logged in to Bugzilla when you call this function, you will only be returned the C<id>, C<name>, and C<real_name> items. If you are logged in and not in editusers group, you will only be returned the C<id>, C<name>, -C<real_name>, C<email>, C<can_login>, and C<groups> items. The groups returned are +C<real_name>, C<can_login>, and C<groups> items. The groups returned are filtered based on your permission to bless each group. The C<saved_searches> and C<saved_reports> items are only returned if you are querying your own account, even if you are in the editusers group. diff --git a/Bugzilla/API/1_0/Util.pm b/Bugzilla/API/1_0/Util.pm index ce4487c1f..13c3eebac 100644 --- a/Bugzilla/API/1_0/Util.pm +++ b/Bugzilla/API/1_0/Util.pm @@ -33,9 +33,10 @@ our @EXPORT = qw( as_datetime as_double as_email - as_email_array as_int as_int_array + as_login + as_login_array as_name_array as_string as_string_array @@ -367,8 +368,8 @@ sub as_string { defined $_[0] ? $_[0] . '' : JSON::null } # array types -sub as_email_array { [ map { as_email($_) } @{ $_[0] // [] } ] } sub as_int_array { [ map { as_int($_) } @{ $_[0] // [] } ] } +sub as_login_array { [ map { as_login($_) } @{ $_[0] // [] } ] } sub as_name_array { [ map { as_string($_->name) } @{ $_[0] // [] } ] } sub as_string_array { [ map { as_string($_) } @{ $_[0] // [] } ] } @@ -380,10 +381,14 @@ sub as_datetime { : JSON::null; } -sub as_email { +sub as_login { defined $_[0] - ? ( Bugzilla->params->{webservice_email_filter} ? email_filter($_[0]) : $_[0] . '' ) - : JSON::null + ? ( Bugzilla->params->{use_email_as_login} ? email_filter($_[0]) : $_[0] . '' ) + : JSON::null; +} + +sub as_email { + defined($_[0]) && Bugzilla->user->in_group('editusers') ? $_[0] . '' : JSON::null; } sub as_base64 { @@ -496,13 +501,9 @@ double value. If parameter is undefined, returns JSON::null. =head2 as_email -Takes an email address as a parameter if filters it if C<webservice_email_filter> is -enabled in the system settings. If parameter is undefined, returns JSON::null. - -=head2 as_email_array - -Similar to C<as_email>, but takes an array reference to a list of values and -returns an array reference with the converted values. +Takes an email address as a parameter. If the user is in the editusers group, +it returns the email address, unchanged. If the parameter is undefined or the +user is not in the editusers group, it returns JSON::null. =head2 as_int @@ -514,6 +515,18 @@ value. If parameter is undefined, returns JSON::null. Similar to C<as_int>, but takes an array reference to a list of values and returns an array reference with the converted values. +=head2 as_login + +Takes a login name as a parameter. If C<use_email_as_login> is enabled and the +user is logged out, it returns the local part of the email address (the part +before '@'). Else it returns the full login name. If parameter is undefined, +returns JSON::null. + +=head2 as_login_array + +Similar to C<as_login>, but takes an array reference to a list of values and +returns an array reference with the converted values. + =head2 as_name_array Takes a list of L<Bugzilla::Object> values and returns an array of new values diff --git a/Bugzilla/Auth.pm b/Bugzilla/Auth.pm index 399f68d24..d693bf12d 100644 --- a/Bugzilla/Auth.pm +++ b/Bugzilla/Auth.pm @@ -136,6 +136,10 @@ sub extern_id_used { || $self->{_verifier}->extern_id_used; } +sub can_change_login { + return $_[0]->user_can_create_account; +} + sub can_change_email { return $_[0]->user_can_create_account; } @@ -412,6 +416,14 @@ Returns: C<true> if users are allowed to create new Bugzilla accounts, Description: Whether or not current login system uses extern_id. +=item C<can_change_login> + +Description: Whether or not the current login system allows users to + change their own login. +Params: None +Returns: C<true> if users can change their own login, + C<false> otherwise. + =item C<can_change_email> Description: Whether or not the current login system allows users to diff --git a/Bugzilla/Auth/Verify.pm b/Bugzilla/Auth/Verify.pm index ef5b749b1..20318b3a5 100644 --- a/Bugzilla/Auth/Verify.pm +++ b/Bugzilla/Auth/Verify.pm @@ -36,7 +36,8 @@ sub create_or_update_user { my $dbh = Bugzilla->dbh; my $extern_id = $params->{extern_id}; - my $username = $params->{bz_username} || $params->{username}; + my $login = $params->{bz_username} || $params->{username}; + my $email = Bugzilla->params->{use_email_as_login} ? $login : $params->{email}; my $password = $params->{password} || '*'; my $real_name = $params->{realname} || ''; my $user_id = $params->{user_id}; @@ -44,7 +45,7 @@ sub create_or_update_user { # A passed-in user_id always overrides anything else, for determining # what account we should return. if (!$user_id) { - my $username_user_id = login_to_id($username || ''); + my $login_user_id = login_to_id($login || ''); my $extern_user_id; if ($extern_id) { trick_taint($extern_id); @@ -52,26 +53,26 @@ sub create_or_update_user { FROM profiles WHERE extern_id = ?', undef, $extern_id); } - # If we have both a valid extern_id and a valid username, and they are + # If we have both a valid extern_id and a valid login, and they are # not the same id, then we have a conflict. - if ($username_user_id && $extern_user_id - && $username_user_id ne $extern_user_id) + if ($login_user_id && $extern_user_id + && $login_user_id ne $extern_user_id) { my $extern_name = Bugzilla::User->new($extern_user_id)->login; return { failure => AUTH_ERROR, error => "extern_id_conflict", details => {extern_id => $extern_id, extern_user => $extern_name, - username => $username} }; + username => $login} }; } - # If we have a valid username, but no valid id, + # If we have a valid login, but no valid id, # then we have to create the user. This happens when we're - # passed only a username, and that username doesn't exist already. - if ($username && !$username_user_id && !$extern_user_id) { - validate_email_syntax($username) - || return { failure => AUTH_ERROR, + # passed only a login, and that login doesn't exist already. + if ($login && !$login_user_id && !$extern_user_id) { + validate_email_syntax($email) + || return { failure => AUTH_ERROR, error => 'auth_invalid_email', - details => {addr => $username} }; + details => {addr => $email} }; # Usually we'd call validate_password, but external authentication # systems might follow different standards than ours. So in this # place here, we call trick_taint without checks. @@ -79,23 +80,24 @@ sub create_or_update_user { # XXX Theoretically this could fail with an error, but the fix for # that is too involved to be done right now. - my $user = Bugzilla::User->create({ - login_name => $username, + my $user = Bugzilla::User->create({ + login_name => $login, + email => $email, cryptpassword => $password, realname => $real_name}); - $username_user_id = $user->id; + $login_user_id = $user->id; } - # If we have a valid username id and an extern_id, but no valid + # If we have a valid login id and an extern_id, but no valid # extern_user_id, then we have to set the user's extern_id. - if ($extern_id && $username_user_id && !$extern_user_id) { + if ($extern_id && $login_user_id && !$extern_user_id) { $dbh->do('UPDATE profiles SET extern_id = ? WHERE userid = ?', - undef, $extern_id, $username_user_id); - Bugzilla->memcached->clear({ table => 'profiles', id => $username_user_id }); + undef, $extern_id, $login_user_id); + Bugzilla->memcached->clear({ table => 'profiles', id => $login_user_id }); } # Finally, at this point, one of these will give us a valid user id. - $user_id = $extern_user_id || $username_user_id; + $user_id = $extern_user_id || $login_user_id; } # If we still don't have a valid user_id, then we weren't passed @@ -109,11 +111,15 @@ sub create_or_update_user { # Now that we have a valid User, we need to see if any data has to be updated. my $changed = 0; - if ($username && lc($user->login) ne lc($username)) { - validate_email_syntax($username) + if ($email && lc($user->email) ne lc($email)) { + validate_email_syntax($email) || return { failure => AUTH_ERROR, error => 'auth_invalid_email', - details => {addr => $username} }; - $user->set_login($username); + details => {addr => $email} }; + $user->set_email($email); + $changed = 1; + } + if ($login && lc($user->login) ne lc($login)) { + $user->set_login($login); $changed = 1; } if ($real_name && $user->name ne $real_name) { diff --git a/Bugzilla/Auth/Verify/LDAP.pm b/Bugzilla/Auth/Verify/LDAP.pm index cd2e64370..2ff38a217 100644 --- a/Bugzilla/Auth/Verify/LDAP.pm +++ b/Bugzilla/Auth/Verify/LDAP.pm @@ -99,23 +99,21 @@ sub check_credentials { my @emails = $user_entry->get_value($mail_attr); # Default to the first email address returned. - $params->{bz_username} = $emails[0]; + $params->{email} = $emails[0]; if (@emails > 1) { # Cycle through the adresses and check if they're Bugzilla logins. # Use the first one that returns a valid id. foreach my $email (@emails) { - if ( login_to_id($email) ) { - $params->{bz_username} = $email; + if ( email_to_id($email) ) { + $params->{email} = $email; last; } } } - - } else { - $params->{bz_username} = $username; } + $params->{bz_username} = $username; $params->{realname} ||= $user_entry->get_value("displayName"); $params->{realname} ||= $user_entry->get_value("cn"); diff --git a/Bugzilla/Auth/Verify/RADIUS.pm b/Bugzilla/Auth/Verify/RADIUS.pm index 60be52a07..163058c54 100644 --- a/Bugzilla/Auth/Verify/RADIUS.pm +++ b/Bugzilla/Auth/Verify/RADIUS.pm @@ -46,8 +46,10 @@ sub check_credentials { Bugzilla->params->{'RADIUS_NAS_IP'} || undef) || return { failure => AUTH_LOGINFAILED }; + $params->{bz_username} = $username; + # Build the user account's e-mail address. - $params->{bz_username} = $username . $address_suffix; + $params->{email} = $username . $address_suffix; return $params; } diff --git a/Bugzilla/Config.pm b/Bugzilla/Config.pm index 7a7ba3a3e..79b468f0c 100644 --- a/Bugzilla/Config.pm +++ b/Bugzilla/Config.pm @@ -29,7 +29,10 @@ use File::Basename; # when it shouldn't %Bugzilla::Config::EXPORT_TAGS = ( - admin => [qw(update_params SetParam write_params)], + admin => [qw(update_params + SetParam + call_param_onchange_handlers + write_params)], ); Exporter::export_ok_tags('admin'); @@ -78,7 +81,7 @@ sub param_panels { sub SetParam { my ($name, $value) = @_; - _load_params unless %params; + _load_params() unless %params; die "Unknown param $name" unless (exists $params{$name}); my $entry = $params{$name}; @@ -96,6 +99,19 @@ sub SetParam { Bugzilla->params->{$name} = $value; } +sub call_param_onchange_handlers { + my ($changes) = @_; + + _load_params() unless %params; + + foreach my $name (@$changes) { + my $param = $params{$name}; + if (exists $param->{'onchange'}) { + $param->{'onchange'}->(Bugzilla->params->{$name}); + } + } +} + sub update_params { my ($params) = @_; my $answer = Bugzilla->installation_answers; @@ -211,7 +227,7 @@ sub update_params { # --- DEFAULTS FOR NEW PARAMS --- - _load_params unless %params; + _load_params() unless %params; foreach my $name (keys %params) { my $item = $params{$name}; unless (exists $param->{$name}) { @@ -228,8 +244,10 @@ sub update_params { } } - # Bug 452525: OR based groups are on by default for new installations - $param->{'or_groups'} = 1 if $new_install; + if ($new_install) { + $param->{'or_groups'} = 1; + $param->{'use_email_as_login'} = 0; + } # --- REMOVE OLD PARAMS --- @@ -341,6 +359,7 @@ Bugzilla::Config - Configuration parameters for Bugzilla update_params(); SetParam($param, $value); + call_param_onchange_handlers(\@changes); write_params(); =head1 DESCRIPTION @@ -371,6 +390,16 @@ Prints out information about what it's doing, if it makes any changes. May prompt the user for input, if certain required parameters are not specified. +=item C<call_param_onchange_handlers(\@changes)> + +Expects a list of parameter names. +For each parameter, checks whether there is a change handler defined, +and if so, calls it. + +Params: C<\@changes> (required) - A list of parameter names. + +Returns: nothing + =item C<write_params($params)> Description: Writes the parameters to disk. diff --git a/Bugzilla/Config/Auth.pm b/Bugzilla/Config/Auth.pm index 219e834ee..326c4cd3f 100644 --- a/Bugzilla/Config/Auth.pm +++ b/Bugzilla/Config/Auth.pm @@ -75,12 +75,6 @@ sub get_param_list { }, { - name => 'webservice_email_filter', - type => 'b', - default => 0 - }, - - { name => 'emailregexp', type => 't', default => q:^[\\w\\.\\+\\-=']+@[\\w\\.\\-]+\\.[\\w\\-]+$:, @@ -95,9 +89,10 @@ sub get_param_list { }, { - name => 'emailsuffix', - type => 't', - default => '' + name => 'use_email_as_login', + type => 'b', + default => '1', + onchange => \&change_use_email_as_login }, { diff --git a/Bugzilla/Config/BugChange.pm b/Bugzilla/Config/BugChange.pm index b8a80dd94..b6cd7ae43 100644 --- a/Bugzilla/Config/BugChange.pm +++ b/Bugzilla/Config/BugChange.pm @@ -38,7 +38,8 @@ sub get_param_list { type => 's', choices => \@closed_bug_statuses, default => $closed_bug_statuses[0], - checker => \&check_bug_status + checker => \&check_bug_status, + onchange => \&change_duplicate_or_move_bug_status }, { diff --git a/Bugzilla/Config/Common.pm b/Bugzilla/Config/Common.pm index 6e89fdcae..e6e0d4a23 100644 --- a/Bugzilla/Config/Common.pm +++ b/Bugzilla/Config/Common.pm @@ -29,6 +29,8 @@ use parent qw(Exporter); check_bug_status check_smtp_auth check_theschwartz_available check_maxattachmentsize check_email check_smtp_ssl check_comment_taggers_group check_smtp_server check_resolution + + change_use_email_as_login change_duplicate_or_move_bug_status ); # Checking functions for the various values @@ -362,18 +364,36 @@ sub check_comment_taggers_group { return check_group($group_name); } +# Change handler functions for various parameters + +# If use_email_as_login is turned on, update all login names to be email +# addresses. +sub change_use_email_as_login { + my $newvalue = shift; + if ($newvalue) { + Bugzilla->dbh->do('UPDATE profiles SET login_name = email'); + } +} + +sub change_duplicate_or_move_bug_status { + my $newvalue = shift; + Bugzilla::Status::add_missing_bug_status_transitions($newvalue); +} + # OK, here are the parameter definitions themselves. # # Each definition is a hash with keys: # -# name - name of the param -# desc - description of the param (for editparams.cgi) -# type - see below -# choices - (optional) see below -# default - default value for the param -# checker - (optional) checking function for validating parameter entry -# It is called with the value of the param as the first arg and a -# reference to the param's hash as the second argument +# name - name of the param +# desc - description of the param (for editparams.cgi) +# type - see below +# choices - (optional) see below +# default - default value for the param +# checker - (optional) checking function for validating parameter entry. +# It is called with the value of the param as the first arg +# and a reference to the param's hash as the second argument. +# onchange - (optional) handling function for parameter changes. +# The argument is the new value of the param. # # The type value can be one of the following: # @@ -467,6 +487,17 @@ Checks that the value is a valid regexp Checks that the required modules for comment tagging are installed, and that a valid group is provided. +=item C<change_use_email_as_login> + +Change handler for "use_email_as_login" parameter - if param is changed to +true, updates login_name field to be the value of the email field for all +users. + +=item C<change_duplicate_or_move_bug_status> + +Change handler for "duplicate_or_move_bug_status" parameter - if param is +changed, insert all missing transitions to the new bug status. + =back =head1 B<Methods in need of POD> diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index ca3839ca6..76ea3c0c8 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -930,6 +930,7 @@ use constant ABSTRACT_SCHEMA => { userid => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1}, login_name => {TYPE => 'varchar(255)', NOTNULL => 1}, + email => {TYPE => 'varchar(255)', NOTNULL => 1}, cryptpassword => {TYPE => 'varchar(128)'}, realname => {TYPE => 'varchar(255)', NOTNULL => 1, DEFAULT => "''"}, @@ -948,7 +949,9 @@ use constant ABSTRACT_SCHEMA => { profiles_login_name_idx => {FIELDS => ['login_name'], TYPE => 'UNIQUE'}, profiles_extern_id_idx => {FIELDS => ['extern_id'], - TYPE => 'UNIQUE'} + TYPE => 'UNIQUE'}, + profiles_email_idx => {FIELDS => ['email'], + TYPE => 'UNIQUE'} ], }, diff --git a/Bugzilla/FlagType.pm b/Bugzilla/FlagType.pm index cbf831bc5..644a02e7f 100644 --- a/Bugzilla/FlagType.pm +++ b/Bugzilla/FlagType.pm @@ -301,8 +301,8 @@ sub _check_cc_list { my @addresses = split(/[,\s]+/, $cc_list); my $addr_spec = $Email::Address::addr_spec; - # We do not call check_email_syntax() because these addresses do not - # require to match 'emailregexp' and do not depend on 'emailsuffix'. + # We do not call check_email_syntax() because these addresses are not + # required to match 'emailregexp'. foreach my $address (@addresses) { ($address !~ /\P{ASCII}/ && $address =~ /^$addr_spec$/) || ThrowUserError('illegal_email_address', diff --git a/Bugzilla/Group.pm b/Bugzilla/Group.pm index 9ea851c20..61c085c0e 100644 --- a/Bugzilla/Group.pm +++ b/Bugzilla/Group.pm @@ -322,7 +322,7 @@ sub _rederive_regexp { my ($self) = @_; my $dbh = Bugzilla->dbh; - my $sth = $dbh->prepare("SELECT userid, login_name, group_id + my $sth = $dbh->prepare("SELECT userid, email, group_id FROM profiles LEFT JOIN user_group_map ON user_group_map.user_id = profiles.userid @@ -337,8 +337,8 @@ sub _rederive_regexp { AND grant_type = ? and isbless = 0"); $sth->execute($self->id, GRANT_REGEXP); my $regexp = $self->user_regexp; - while (my ($uid, $login, $present) = $sth->fetchrow_array) { - if ($regexp ne '' and $login =~ /$regexp/i) { + while (my ($uid, $email, $present) = $sth->fetchrow_array) { + if ($regexp ne '' and $email =~ /$regexp/i) { $sthadd->execute($uid, $self->id, GRANT_REGEXP) unless $present; } else { $sthdel->execute($uid, $self->id, GRANT_REGEXP) if $present; diff --git a/Bugzilla/Hook.pm b/Bugzilla/Hook.pm index 8d0976d9c..3f902cab0 100644 --- a/Bugzilla/Hook.pm +++ b/Bugzilla/Hook.pm @@ -1631,10 +1631,14 @@ Params: =over +=item C<email> + +The email address of the new account. + =item C<login> -The login of the new account. This is usually an email address, unless the -C<emailsuffix> parameter is not empty. +The login of the new account. This will be the same as the email address if +the "use_email_as_login" parameter is set, otherwise it may be different. =back diff --git a/Bugzilla/Install.pm b/Bugzilla/Install.pm index e1ed65c36..37c87a2b1 100644 --- a/Bugzilla/Install.pm +++ b/Bugzilla/Install.pm @@ -308,27 +308,47 @@ sub create_admin { my $template = Bugzilla->template; my $admin_group = new Bugzilla::Group({ name => 'admin' }); - my $admin_inheritors = + my $admin_inheritors = Bugzilla::Group->flatten_group_membership($admin_group->id); my $admin_group_ids = join(',', @$admin_inheritors); my ($admin_count) = $dbh->selectrow_array( - "SELECT COUNT(*) FROM user_group_map + "SELECT COUNT(*) FROM user_group_map WHERE group_id IN ($admin_group_ids)"); return if $admin_count; my %answer = %{Bugzilla->installation_answers}; - my $login = $answer{'ADMIN_EMAIL'}; + my $login = $answer{'ADMIN_LOGIN'}; + my $email = $answer{'ADMIN_EMAIL'}; my $password = $answer{'ADMIN_PASSWORD'}; my $full_name = $answer{'ADMIN_REALNAME'}; - if (!$login || !$password || !$full_name) { + if (!($login || Bugzilla->params->{'use_email_as_login'}) + || !$email + || !$password) + { say "\n" . get_text('install_admin_setup') . "\n"; } - while (!$login) { + while (!$email) { print get_text('install_admin_get_email') . ' '; + $email = <STDIN>; + chomp $email; + eval { Bugzilla::User->check_email($email); }; + if ($@) { + say $@; + undef $email; + } + } + + # Make sure the email address is used as login when required. + if (Bugzilla->params->{'use_email_as_login'}) { + $login = $email; + } + + while (!$login) { + print get_text('install_admin_get_login') . ' '; $login = <STDIN>; chomp $login; eval { Bugzilla::User->check_login_name($login); }; @@ -349,7 +369,8 @@ sub create_admin { get_text('install_admin_get_password')); } - my $admin = Bugzilla::User->create({ login_name => $login, + my $admin = Bugzilla::User->create({ login_name => $login, + email => $email, realname => $full_name, cryptpassword => $password }); make_admin($admin); diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index a6ca1e90f..b7ca7c657 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -748,6 +748,10 @@ sub update_table_definitions { # 2015-12-16 LpSolit@gmail.com - Bug 1232578 _sanitize_audit_log_table(); + # 2016-04-27 wurblzap@gmail.com and gerv@gerv.net - Bug 218917 + # Split login_name into login_name and email columns + _split_login_and_email($old_params); + ################################################################ # New --TABLE-- changes should go *** A B O V E *** this point # ################################################################ @@ -3957,6 +3961,27 @@ sub _sanitize_audit_log_table { } } +sub _split_login_and_email { + my ($old_params) = (@_); + + my $dbh = Bugzilla->dbh; + $dbh->bz_add_column('profiles', 'email', + {TYPE => 'varchar(255)', NOTNULL => 1}, ''); + $dbh->do('UPDATE profiles SET email = login_name'); + + # This change obsoletes the 'emailsuffix' parameter. If it is in use, + # append it to all the values in the 'email' column. + my $suffix = $old_params->{'emailsuffix'}; + if ($suffix) { + $dbh->do('UPDATE profiles SET email = ' . $dbh->sql_string_concat('email', '?'), + undef, $suffix); + } + + $dbh->bz_add_index('profiles', 'profiles_email_idx', + {TYPE => 'UNIQUE', FIELDS => ['email']}); +} + + 1; __END__ diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 618d80cfa..dea207f21 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -1170,7 +1170,7 @@ sub create { my $cache = Bugzilla->request_cache; return $cache->{login_not_email} //= - ($params->{emailsuffix} + (!$params->{use_email_as_login} || ($params->{user_verify_class} =~ /LDAP/ && $params->{LDAPmailattribute}) || ($params->{user_verify_class} =~ /RADIUS/ && $params->{RADIUS_email_suffix})) ? 1 : 0; diff --git a/Bugzilla/Token.pm b/Bugzilla/Token.pm index b6f07468e..4f522bda9 100644 --- a/Bugzilla/Token.pm +++ b/Bugzilla/Token.pm @@ -89,30 +89,32 @@ sub check_auth_delegation_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 ($login, $email) = @_; 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 + # Is there already a pending request for this email? 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 $regexp = "^$email:"; my $pending_requests = $dbh->selectrow_array( 'SELECT COUNT(*) FROM tokens WHERE tokentype = ? - AND ' . $dbh->sql_istrcmp('eventdata', '?') . ' + AND ' . $dbh->sql_regexp('eventdata', $dbh->quote($regexp)) . ' AND issuedate > ' . $dbh->sql_date_math('NOW()', '-', ACCOUNT_CHANGE_INTERVAL, 'MINUTE'), - undef, ('account', $login_name)); + undef, 'account'); 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', "$email:$login"); - $vars->{'email'} = $login_name . Bugzilla->params->{'emailsuffix'}; + $vars->{'login'} = $login; + $vars->{'email'} = $email; $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400); $vars->{'token'} = $token; @@ -131,15 +133,15 @@ sub IssueEmailChangeToken { 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->email . ":$new_email"); + my $newtoken = _create_token($user->id, 'emailnew', $user->email . ":$new_email"); # Mail the user the token along with instructions for using it. my $template = Bugzilla->template_inner($user->setting('lang')); my $vars = {}; - $vars->{'newemailaddress'} = $new_email . Bugzilla->params->{'emailsuffix'}; + $vars->{'newemailaddress'} = $new_email; $vars->{'expiration_ts'} = ctime($token_ts + MAX_TOKEN_AGE * 86400); # First send an email to the new address. If this one doesn't exist, @@ -335,7 +337,17 @@ sub Cancel { # is no entry in the 'profiles' table. my $user = new Bugzilla::User($userid); - $vars->{'emailaddress'} = $userid ? $user->email : $eventdata; + if ($userid) { + $vars->{'emailaddress'} = $user->email; + $vars->{'login'} = $user->login; + } + else { + # Be careful! Some logins may contain ":" in them. + my ($email, $login) = split(':', $eventdata, 2); + $vars->{'emailaddress'} = $email; + $vars->{'login'} = $login; + } + $vars->{'remoteaddress'} = remote_ip(); $vars->{'token'} = $token; $vars->{'tokentype'} = $tokentype; @@ -511,7 +523,7 @@ Bugzilla::Token - Provides different routines to manage tokens. use Bugzilla::Token; - Bugzilla::Token::issue_new_user_account_token($login_name); + Bugzilla::Token::issue_new_user_account_token($login, $email); Bugzilla::Token::IssueEmailChangeToken($user, $new_email); Bugzilla::Token::IssuePasswordToken($user); Bugzilla::Token::DeletePasswordTokens($user_id, $reason); @@ -539,14 +551,15 @@ Bugzilla::Token - Provides different routines to manage tokens. Returns: The token. -=item C<issue_new_user_account_token($login_name)> +=item C<issue_new_user_account_token($login, $email)> Description: Creates and sends a token per email to the email address requesting a new user account. It doesn't check whether the user account already exists. The user will have to use this token to confirm the creation of their user account. - Params: $login_name - The new login name requested by the user. + Params: $login - The new login name requested by the user. + $email - The email address to be associated with the account. Returns: Nothing. It throws an error if the same user made the same request in the last few minutes. diff --git a/Bugzilla/User.pm b/Bugzilla/User.pm index 91defebb4..c1e3adee3 100644 --- a/Bugzilla/User.pm +++ b/Bugzilla/User.pm @@ -32,7 +32,7 @@ use URI; use URI::QueryParam; use parent qw(Bugzilla::Object Exporter); -@Bugzilla::User::EXPORT = qw(is_available_username +@Bugzilla::User::EXPORT = qw(is_available_email email_to_id login_to_id validate_password validate_password_check USER_MATCH_MULTIPLE USER_MATCH_FAILED USER_MATCH_SUCCESS MATCH_SKIP_CONFIRM @@ -50,12 +50,13 @@ use constant MATCH_SKIP_CONFIRM => 1; use constant DEFAULT_USER => { 'userid' => 0, - 'realname' => '', 'login_name' => '', + 'email' => '', + 'realname' => '', 'showmybugslink' => 0, 'disabledtext' => '', 'disable_mail' => 0, - 'is_enabled' => 1, + 'is_enabled' => 1, }; use constant DB_TABLE => 'profiles'; @@ -69,6 +70,7 @@ sub DB_COLUMNS { return ( 'profiles.userid', 'profiles.login_name', + 'profiles.email', 'profiles.realname', 'profiles.mybugslink AS showmybugslink', 'profiles.disabledtext', @@ -88,9 +90,10 @@ use constant VALIDATORS => { disable_mail => \&_check_disable_mail, disabledtext => \&_check_disabledtext, login_name => \&check_login_name, + email => \&check_email, realname => \&_check_realname, extern_id => \&_check_extern_id, - is_enabled => \&_check_is_enabled, + is_enabled => \&_check_is_enabled, }; sub UPDATE_COLUMNS { @@ -99,6 +102,7 @@ sub UPDATE_COLUMNS { disable_mail disabledtext login_name + email realname extern_id is_enabled @@ -108,7 +112,8 @@ sub UPDATE_COLUMNS { }; use constant VALIDATOR_DEPENDENCIES => { - is_enabled => ['disabledtext'], + is_enabled => ['disabledtext'], + login_name => ['email'], }; use constant EXTRA_REQUIRED_FIELDS => qw(is_enabled); @@ -222,7 +227,7 @@ sub update { my $dbh = Bugzilla->dbh; $self->_update_groups($group_changes, $changes); - if (exists $changes->{login_name}) { + if (exists $changes->{email}) { # Delete all the tokens related to the userid $dbh->do('DELETE FROM tokens WHERE userid = ?', undef, $self->id) unless $options->{keep_tokens}; @@ -231,15 +236,16 @@ sub update { } # Logout the user if necessary. - Bugzilla->logout_user($self) + Bugzilla->logout_user($self) if (!$options->{keep_session} && (exists $changes->{login_name} + || exists $changes->{email} || exists $changes->{disabledtext} || exists $changes->{cryptpassword})); # XXX Can update profiles_activity here as soon as it understands # field names like login_name. - + return $changes; } @@ -266,23 +272,53 @@ sub _check_extern_id { return $extern_id; } -# This is public since createaccount.cgi needs to use it before issuing -# a token for account creation. sub check_login_name { - my ($invocant, $name) = @_; - $name = trim($name); - $name || ThrowUserError('user_login_required'); - check_email_syntax($name); + my ($invocant, $login, undef, $data) = @_; + + # No control characters + $login = clean_text($login); + $login || ThrowUserError('user_login_required'); + # No whitespace + $login !~ /\s/ || ThrowUserError('login_illegal_character'); + + # No @ sign unless login is email (VALIDATOR_DEPENDENCIES means + # this will be set already) + if ($login =~ /@/) { + my $email = ref($invocant) ? $invocant->email : $data->{email}; + # We should really use fc() instead of lc(), but this requires Perl 5.16. + ThrowUserError('login_at_sign_disallowed') unless lc($login) eq lc($email); + } + # We set the max length to 127 to ensure logins aren't truncated when + # inserted into the tokens.eventdata field. + length($login) <= 127 or ThrowUserError('login_too_long'); + + trick_taint($login); + + # Check the login name if it's a new user, or if we're changing the login name. + if (!ref($invocant) || lc($invocant->login) ne lc($login)) { + if (login_to_id($login)) { + ThrowUserError('account_exists', { login => $login }); + } + } - # Check the name if it's a new user, or if we're changing the name. - if (!ref($invocant) || lc($invocant->login) ne lc($name)) { - my @params = ($name); - push(@params, $invocant->login) if ref($invocant); - is_available_username(@params) - || ThrowUserError('account_exists', { email => $name }); + return $login; +} + +sub check_email { + my ($invocant, $email) = @_; + $email = clean_text($email); + $email || ThrowUserError('email_required'); + + check_email_syntax($email); + + # Check the email if it's a new user, or if we're changing the email. + my $old_email = ref($invocant) ? $invocant->email : undef; + if (!defined($old_email) || lc($old_email) ne lc($email)) { + is_available_email($email, $old_email) + || ThrowUserError('account_exists', { email => $email }); } - return $name; + return $email; } sub _check_password { @@ -325,6 +361,12 @@ sub set_login { delete $self->{nick}; } +sub set_email { + my ($self, $email) = @_; + $self->set('email', $email); + $self->set_login($email) if Bugzilla->params->{'use_email_as_login'}; +} + sub set_name { my ($self, $name) = @_; $self->set('realname', $name); @@ -470,7 +512,7 @@ sub update_last_seen_date { sub name { $_[0]->{realname}; } sub login { $_[0]->{login_name}; } sub extern_id { $_[0]->{extern_id}; } -sub email { $_[0]->login . Bugzilla->params->{'emailsuffix'}; } +sub email { $_[0]->{email}; } sub disabledtext { $_[0]->{'disabledtext'}; } sub is_enabled { $_[0]->{'is_enabled'} ? 1 : 0; } sub showmybugslink { $_[0]->{showmybugslink}; } @@ -502,14 +544,17 @@ sub authorizer { # Generate a string to identify the user by name + login if the user # has a name or by login only if they don't. +# +# See also get_userlist(), which constructs pseudo-Bugzilla::Users, including +# the 'identity' value. sub identity { my $self = shift; return "" unless $self->id; if (!defined $self->{identity}) { - $self->{identity} = - $self->name ? $self->name . " <" . $self->login. ">" : $self->login; + $self->{identity} = + $self->name ? $self->name . " (" . $self->login. ")" : $self->login; } return $self->{identity}; @@ -521,6 +566,7 @@ sub nick { return "" unless $self->id; if (!defined $self->{nick}) { + # This has the correct result even if the login does not contain an @. $self->{nick} = (split(/@/, $self->login, 2))[0]; } @@ -1730,8 +1776,8 @@ sub can_bless { } sub match { - # Generates a list of users whose login name (email address) or real name - # matches a substring or wildcard. + # Generates a list of users whose login name or real name matches a + # substring or wildcard. # This is also called if matches are disabled (for error checking), but # in this case only the exact match code will end up running. @@ -1780,8 +1826,10 @@ sub match { @users = @{Bugzilla::User->new_from_list($user_ids)}; } else { # try an exact match - # Exact matches don't care if a user is disabled. + # Exact matches don't care if a user is disabled, and match + # login names only. trick_taint($str); + my $user_id = $dbh->selectrow_array('SELECT userid FROM profiles WHERE ' . $dbh->sql_istrcmp('login_name', '?'), undef, $str); @@ -2253,7 +2301,7 @@ sub get_userlist { while (my($login, $name, $visible) = $sth->fetchrow_array) { push @userlist, { login => $login, - identity => $name ? "$name <$login>" : $login, + identity => $name ? "$name ($login)" : $login, visible => $visible, }; } @@ -2375,17 +2423,15 @@ sub check_current_password { # Subroutines # ############### -sub is_available_username { - my ($username, $old_username) = @_; +sub is_available_email { + my ($email, $old_email) = @_; - if(login_to_id($username) != 0) { - return 0; - } + return 0 if email_to_id($email); my $dbh = Bugzilla->dbh; - # $username is safe because it is only used in SELECT placeholders. - trick_taint($username); - # Reject if the new login is part of an email change which is + # $email is safe because it is only used in SELECT placeholders. + trick_taint($email); + # Reject if the new email is part of an email change which is # still in progress # # substring/locate stuff: bug 165221; this used to use regexes, but that @@ -2401,13 +2447,13 @@ sub is_available_username { OR (tokentype = 'emailnew' AND SUBSTRING(eventdata, (" . $dbh->sql_position(q{':'}, 'eventdata') . "+ 1), LENGTH(eventdata)) = ?)", - undef, ($username, $username)); + undef, ($email, $email)); if ($eventdata) { # Allow thru owner of token - if ($old_username - && (($tokentype eq 'emailnew' && $eventdata eq "$old_username:$username") - || ($tokentype eq 'emailold' && $eventdata eq "$username:$old_username"))) + if ($old_email + && (($tokentype eq 'emailnew' && $eventdata eq "$old_email:$email") + || ($tokentype eq 'emailold' && $eventdata eq "$email:$old_email"))) { return 1; } @@ -2429,24 +2475,27 @@ sub check_account_creation_enabled { } sub check_and_send_account_creation_confirmation { - my ($self, $login) = @_; + my ($self, $login, $email) = @_; + my $class = ref($self) || $self; my $dbh = Bugzilla->dbh; $dbh->bz_start_transaction; - $login = $self->check_login_name($login); + $email = $class->check_email($email); + $login = $class->check_login_name($login, undef, { email => $email }); my $creation_regexp = Bugzilla->params->{'createemailregexp'}; - if ($login !~ /$creation_regexp/i) { + if ($email !~ /$creation_regexp/i) { ThrowUserError('account_creation_restricted'); } # Allow extensions to do extra checks. - Bugzilla::Hook::process('user_check_account_creation', { login => $login }); + Bugzilla::Hook::process('user_check_account_creation', + { login => $login, email => $email }); # Create and send a token for this new account. require Bugzilla::Token; - Bugzilla::Token::issue_new_user_account_token($login); + Bugzilla::Token::issue_new_user_account_token($login, $email); $dbh->bz_commit_transaction; } @@ -2458,20 +2507,17 @@ sub login_to_id { my $dbh = Bugzilla->dbh; my $cache = Bugzilla->request_cache->{user_login_to_id} ||= {}; - # We cache lookups because this function showed up as taking up a + # We cache lookups because this function showed up as taking up a # significant amount of time in profiles of xt/search.t. However, - # for users that don't exist, we re-do the check every time, because - # otherwise we break is_available_username. + # for users that don't exist, we re-do the check every time. my $user_id; if (defined $cache->{$login}) { $user_id = $cache->{$login}; } else { - # No need to validate $login -- it will be used by the following SELECT - # statement only, so it's safe to simply trick_taint. trick_taint($login); $user_id = $dbh->selectrow_array( - "SELECT userid FROM profiles + "SELECT userid FROM profiles WHERE " . $dbh->sql_istrcmp('login_name', '?'), undef, $login); $cache->{$login} = $user_id; } @@ -2485,6 +2531,24 @@ sub login_to_id { } } +sub email_to_id { + my ($email, $throw_error) = @_; + my $dbh = Bugzilla->dbh; + trick_taint($email); + my $user_id = $dbh->selectrow_array("SELECT userid FROM profiles WHERE " . + $dbh->sql_istrcmp('email', '?'), + undef, $email); + if ($user_id) { + return $user_id; + } + elsif ($throw_error) { + ThrowUserError('invalid_email', { email => $email }); + } + else { + return 0; + } +} + sub validate_password { my $check = validate_password_check(@_); ThrowUserError($check) if $check; @@ -2532,14 +2596,15 @@ Bugzilla::User - Object for a Bugzilla user my $user = new Bugzilla::User($id); - my @get_selectable_classifications = + my @get_selectable_classifications = $user->get_selectable_classifications; # Class Functions - $user = Bugzilla::User->create({ - login_name => $username, - realname => $realname, - cryptpassword => $plaintext_password, + $user = Bugzilla::User->create({ + login_name => $username, + email => $email, + realname => $realname, + cryptpassword => $plaintext_password, disabledtext => $disabledtext, disable_mail => 0}); @@ -2595,6 +2660,27 @@ database changes and so on. =back +=head2 Validators + +=over + +=item C<check_email> + +Returns the sanitized email address if that email address is well formatted +and not already used by another user account. Else an error is thrown. + +=item C<check_login_name> + +Returns the sanitized login name if: + +1) it is not already used by another user account; and +2) it contains no forbidden characters, which means no whitespace characters; and +3) it contains no @ character (unless it exactly matches the email address of the account). + +Else an error is thrown. + +=back + =head2 Saved and Shared Queries =over @@ -2734,8 +2820,7 @@ Returns the login name for this user. =item C<email> -Returns the user's email address. Currently this is the same value as the -login. +Returns the user's email address. =item C<name> @@ -2754,10 +2839,10 @@ otherwise. =item C<nick> -Returns a user "nickname" -- i.e. a shorter, not-necessarily-unique name by -which to identify the user. Currently the part of the user's email address -before the at sign (@), but that could change, especially if we implement -usernames not dependent on email address. +Usually, this is an alias for C<login>. +If email addresses are used as logins, though, then this is the part of the +user's email address before the at sign (@). In this case, nicks are not +unique. =item C<authorizer> @@ -3141,7 +3226,8 @@ called "statically," just like a normal procedural function. The same as L<Bugzilla::Object/create>. Params: login_name - B<Required> The login name for the new user. - realname - The full name for the new user. + email - B<Required> The email address of the new user. + realname - The full name of the new user. cryptpassword - B<Required> The password for the new user. Even though the name says "crypt", you should just specify a plain-text password. If you specify '*', the user will not @@ -3162,30 +3248,29 @@ user with that username. Returns a C<Bugzilla::User> object. Checks that users can create new user accounts, and throws an error if user creation is disabled. -=item C<check_and_send_account_creation_confirmation($login)> +=item C<check_and_send_account_creation_confirmation($login, $email)> If the user request for a new account passes validation checks, an email is sent to this user for confirmation. Otherwise an error is thrown indicating why the request has been rejected. -=item C<is_available_username> +=item C<is_available_email> -Returns a boolean indicating whether or not the supplied username is +Returns a boolean indicating whether or not the supplied email address is already taken in Bugzilla. -Params: $username (scalar, string) - The full login name of the username - that you are checking. - $old_username (scalar, string) - If you are checking an email-change - token, insert the "old" username that the user is changing from, - here. Then, as long as it's the right user for that token, they - can change their username to $username. (That is, this function +Params: $email (scalar, string) - The email address that you are checking. + $old_email (scalar, string) - If you are checking an email-change + token, insert the "old" address that the user is changing from, + here. Then, as long as it's the right user for that token, he + can change his email to $email. (That is, this function will return a boolean true value). =item C<login_to_id($login, $throw_error)> Takes a login name of a Bugzilla user and changes that into a numeric ID for that user. This ID can then be passed to Bugzilla::User::new to -create a new user. +create a new user object. If no valid user exists with that login name, then the function returns 0. However, if $throw_error is set, the function will throw a user error @@ -3197,6 +3282,11 @@ of a user, but you don't want the full weight of Bugzilla::User. However, consider using a Bugzilla::User object instead of this function if you need more information about the user than just their ID. +=item C<email_to_id($email, $throw_error)> + +Same as C<login_to_id>, but operates on an email address instead of a login +name. + =item C<validate_password($passwd1, $passwd2)> Returns true if a password is valid (i.e. meets Bugzilla's @@ -3282,8 +3372,6 @@ L<Bugzilla|Bugzilla> =item groups_with_icon -=item check_login_name - =item set_extern_id =item mail_settings @@ -3300,6 +3388,8 @@ L<Bugzilla|Bugzilla> =item set_login +=item set_email + =item set_password =item last_seen_date diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index 06022ce7c..e673a920e 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -690,9 +690,8 @@ sub generate_random_password { } sub validate_email_syntax { - my ($addr) = @_; + my ($email) = @_; my $match = Bugzilla->params->{'emailregexp'}; - my $email = $addr . Bugzilla->params->{'emailsuffix'}; # This regexp follows RFC 2822 section 3.4.1. my $addr_spec = $Email::Address::addr_spec; # RFC 2822 section 2.1 specifies that email addresses must @@ -700,7 +699,7 @@ sub validate_email_syntax { # Email::Address::addr_spec doesn't enforce this. # We set the max length to 127 to ensure addresses aren't truncated when # inserted into the tokens.eventdata field. - if ($addr =~ /$match/ + if ($email =~ /$match/ && $email !~ /\P{ASCII}/ && $email =~ /^$addr_spec$/ && length($email) <= 127) @@ -713,11 +712,10 @@ sub validate_email_syntax { } sub check_email_syntax { - my ($addr) = @_; + my ($email) = @_; unless (validate_email_syntax(@_)) { - my $email = $addr . Bugzilla->params->{'emailsuffix'}; - ThrowUserError('illegal_email_address', { addr => $email }); + ThrowUserError('illegal_email_address', { email => $email }); } } diff --git a/Bugzilla/WebService/Bug.pm b/Bugzilla/WebService/Bug.pm index 2279395a1..d137000a9 100644 --- a/Bugzilla/WebService/Bug.pm +++ b/Bugzilla/WebService/Bug.pm @@ -366,7 +366,7 @@ sub _translate_comment { my $comment_hash = { id => $self->type('int', $comment->id), bug_id => $self->type('int', $comment->bug_id), - creator => $self->type('email', $comment->author->login), + creator => $self->type('login', $comment->author->login), time => $self->type('dateTime', $comment->creation_ts), creation_time => $self->type('dateTime', $comment->creation_ts), is_private => $self->type('boolean', $comment->is_private), @@ -1308,7 +1308,7 @@ sub _bug_to_hash { $item{alias} = [ map { $self->type('string', $_) } @{ $bug->alias } ]; } if (filter_wants $params, 'assigned_to') { - $item{'assigned_to'} = $self->type('email', $bug->assigned_to->login); + $item{'assigned_to'} = $self->type('login', $bug->assigned_to->login); $item{'assigned_to_detail'} = $self->_user_to_hash($bug->assigned_to, $params, undef, 'assigned_to'); } if (filter_wants $params, 'blocks') { @@ -1322,7 +1322,7 @@ sub _bug_to_hash { $item{component} = $self->type('string', $bug->component); } if (filter_wants $params, 'cc') { - my @cc = map { $self->type('email', $_) } @{ $bug->cc }; + my @cc = map { $self->type('login', $_) } @{ $bug->cc }; $item{'cc'} = \@cc; $item{'cc_detail'} = [ map { $self->_user_to_hash($_, $params, undef, 'cc') } @{ $bug->cc_users } ]; } @@ -1330,7 +1330,7 @@ sub _bug_to_hash { $item{'creation_time'} = $self->type('dateTime', $bug->creation_ts); } if (filter_wants $params, 'creator') { - $item{'creator'} = $self->type('email', $bug->reporter->login); + $item{'creator'} = $self->type('login', $bug->reporter->login); $item{'creator_detail'} = $self->_user_to_hash($bug->reporter, $params, undef, 'creator'); } if (filter_wants $params, 'depends_on') { @@ -1361,7 +1361,7 @@ sub _bug_to_hash { } if (filter_wants $params, 'qa_contact') { my $qa_login = $bug->qa_contact ? $bug->qa_contact->login : ''; - $item{'qa_contact'} = $self->type('email', $qa_login); + $item{'qa_contact'} = $self->type('login', $qa_login); if ($bug->qa_contact) { $item{'qa_contact_detail'} = $self->_user_to_hash($bug->qa_contact, $params, undef, 'qa_contact'); } @@ -1433,8 +1433,7 @@ sub _user_to_hash { my $item = filter $filters, { id => $self->type('int', $user->id), real_name => $self->type('string', $user->name), - name => $self->type('email', $user->login), - email => $self->type('email', $user->email), + name => $self->type('login', $user->login), }, $types, $prefix; return $item; } @@ -1455,10 +1454,10 @@ sub _attachment_to_hash { is_patch => $self->type('int', $attach->ispatch), }, $types, $prefix; - # creator requires an extra lookup, so we only send them if - # the filter wants them. + # creator requires an extra lookup, so we only send it if + # the filter wants it. if (filter_wants $filters, 'creator', $types, $prefix) { - $item->{'creator'} = $self->type('email', $attach->attacher->login); + $item->{'creator'} = $self->type('login', $attach->attacher->login); } if (filter_wants $filters, 'data', $types, $prefix) { @@ -1490,7 +1489,7 @@ sub _flag_to_hash { foreach my $field (qw(setter requestee)) { my $field_id = $field . "_id"; - $item->{$field} = $self->type('email', $flag->$field->login) + $item->{$field} = $self->type('login', $flag->$field->login) if $flag->$field_id; } @@ -2575,10 +2574,6 @@ C<string> The 'real' name for this user, if any. C<string> The user's Bugzilla login. -=item C<email> - -C<string> The user's email address. Currently this is the same value as the name. - =back =back @@ -2698,7 +2693,11 @@ in Bugzilla B<4.4>. =item REST API call added in Bugzilla B<5.0>. -=item In Bugzilla B<5.0>, the following items were added to the bugs return value: C<assigned_to_detail>, C<creator_detail>, C<qa_contact_detail>. +=item In Bugzilla B<5.0>, the following items were added to the bugs return +value: C<assigned_to_detail>, C<creator_detail>, C<qa_contact_detail>. + +=item Since Bugzilla B<6.0>, the email address of users is private and is no +longer returned as part of the user detail hash. =back diff --git a/Bugzilla/WebService/Bugzilla.pm b/Bugzilla/WebService/Bugzilla.pm index 67e40a15f..c0df4754f 100644 --- a/Bugzilla/WebService/Bugzilla.pm +++ b/Bugzilla/WebService/Bugzilla.pm @@ -62,7 +62,6 @@ use constant PARAMETERS_LOGGED_IN => qw( defaultseverity duplicate_or_move_bug_status emailregexpdesc - emailsuffix letsubmitterchoosemilestone letsubmitterchoosepriority mailfrom @@ -75,6 +74,7 @@ use constant PARAMETERS_LOGGED_IN => qw( resolution_forbidden_with_open_blockers search_allow_no_criteria urlbase + use_email_as_login use_see_also useclassification usemenuforusers @@ -417,7 +417,6 @@ A logged-in user can access the following parameters (listed alphabetically): C<defaultseverity>, C<duplicate_or_move_bug_status>, C<emailregexpdesc>, - C<emailsuffix>, C<letsubmitterchoosemilestone>, C<letsubmitterchoosepriority>, C<mailfrom>, @@ -430,6 +429,7 @@ A logged-in user can access the following parameters (listed alphabetically): C<resolution_forbidden_with_open_blockers>, C<search_allow_no_criteria>, C<urlbase>, + C<use_email_as_login>, C<use_see_also>, C<useclassification>, C<usemenuforusers>, diff --git a/Bugzilla/WebService/Group.pm b/Bugzilla/WebService/Group.pm index ec09604c4..a9539f83d 100644 --- a/Bugzilla/WebService/Group.pm +++ b/Bugzilla/WebService/Group.pm @@ -210,8 +210,8 @@ sub _get_group_membership { map {{ id => $self->type('int', $_->id), real_name => $self->type('string', $_->name), - name => $self->type('string', $_->login), - email => $self->type('string', $_->email), + name => $self->type('login', $_->login), + email => $self->type('email', $_->email), can_login => $self->type('boolean', $_->is_enabled), email_enabled => $self->type('boolean', $_->email_enabled), login_denied_text => $self->type('string', $_->disabledtext), @@ -282,7 +282,7 @@ name of the group. =item C<user_regexp> -C<string> A regular expression. Any user whose Bugzilla username matches +C<string> A regular expression. Any user whose Bugzilla email address matches this regular expression will automatically be granted membership in this group. =item C<is_active> @@ -553,8 +553,7 @@ C<string> The email address of the user. =item name -C<string> The login name of the user. Note that in some situations this is -different than their email. +C<string> The login name of the user. =item can_login diff --git a/Bugzilla/WebService/Product.pm b/Bugzilla/WebService/Product.pm index 879d8eac1..66bd32808 100644 --- a/Bugzilla/WebService/Product.pm +++ b/Bugzilla/WebService/Product.pm @@ -271,9 +271,9 @@ sub _component_to_hash { description => $self->type('string' , $component->description), default_assigned_to => - $self->type('email', $component->default_assignee->login), + $self->type('login', $component->default_assignee->login), default_qa_contact => - $self->type('email', $component->default_qa_contact ? + $self->type('login', $component->default_qa_contact ? $component->default_qa_contact->login : ""), sort_key => # sort_key is returned to match Bug.fields 0, diff --git a/Bugzilla/WebService/Server/JSONRPC.pm b/Bugzilla/WebService/Server/JSONRPC.pm index 93d315fde..b488c8de5 100644 --- a/Bugzilla/WebService/Server/JSONRPC.pm +++ b/Bugzilla/WebService/Server/JSONRPC.pm @@ -222,8 +222,11 @@ sub type { utf8::encode($value) if utf8::is_utf8($value); $retval = encode_base64($value, ''); } - elsif ($type eq 'email' && Bugzilla->params->{'webservice_email_filter'}) { - $retval = email_filter($value); + elsif ($type eq 'login') { + $retval = Bugzilla->params->{'use_email_as_login'} ? email_filter($retval) : "$retval"; + } + elsif ($type eq 'email') { + $retval = Bugzilla->user->in_group('editusers') ? "$retval" : ''; } return $retval; diff --git a/Bugzilla/WebService/Server/XMLRPC.pm b/Bugzilla/WebService/Server/XMLRPC.pm index f697f0927..c6461abd6 100644 --- a/Bugzilla/WebService/Server/XMLRPC.pm +++ b/Bugzilla/WebService/Server/XMLRPC.pm @@ -35,11 +35,13 @@ BEGIN { $value = Bugzilla::WebService::Server->datetime_format_outbound($value); $value =~ s/-//g; } + elsif ($type eq 'login') { + $type = 'string'; + $value = email_filter($value) if Bugzilla->params->{'use_email_as_login'}; + } elsif ($type eq 'email') { $type = 'string'; - if (Bugzilla->params->{'webservice_email_filter'}) { - $value = email_filter($value); - } + $value = '' unless Bugzilla->user->in_group('editusers'); } return XMLRPC::Data->type($type)->value($value); }; diff --git a/Bugzilla/WebService/User.pm b/Bugzilla/WebService/User.pm index 079e0782e..d69df5056 100644 --- a/Bugzilla/WebService/User.pm +++ b/Bugzilla/WebService/User.pm @@ -43,13 +43,11 @@ use constant PUBLIC_METHODS => qw( ); use constant MAPPED_FIELDS => { - email => 'login', full_name => 'name', login_denied_text => 'disabledtext', }; use constant MAPPED_RETURNS => { - login_name => 'email', realname => 'full_name', disabledtext => 'login_denied_text', }; @@ -67,7 +65,7 @@ sub login { return $self->_login_to_hash($user); } - # Username and password params are required + # Login name and password params are required foreach my $param ("login", "password") { (defined $params->{$param} || defined $params->{'Bugzilla_' . $param}) || ThrowCodeError('param_required', { param => $param }); @@ -103,8 +101,11 @@ sub offer_account_by_email { my $email = trim($params->{email}) || ThrowCodeError('param_required', { param => 'email' }); + my $login = Bugzilla->params->{use_email_as_login} ? $email : trim($params->{login}); + $login or ThrowCodeError('param_required', { param => 'login' }); + Bugzilla->user->check_account_creation_enabled; - Bugzilla->user->check_and_send_account_creation_confirmation($email); + Bugzilla->user->check_and_send_account_creation_confirmation($login, $email); return undef; } @@ -119,11 +120,16 @@ sub create { my $email = trim($params->{email}) || ThrowCodeError('param_required', { param => 'email' }); + + my $login = Bugzilla->params->{use_email_as_login} ? $email : trim($params->{login}); + $login or ThrowCodeError('param_required', { param => 'login' }); + my $realname = trim($params->{full_name}); my $password = trim($params->{password}) || '*'; my $user = Bugzilla::User->create({ - login_name => $email, + login_name => $login, + email => $email, realname => $realname, cryptpassword => $password }); @@ -170,7 +176,7 @@ sub get { @users = map { filter $params, { id => $self->type('int', $_->id), real_name => $self->type('string', $_->name), - name => $self->type('email', $_->login), + name => $self->type('login', $_->login), } } @$in_group; return { users => \@users }; @@ -222,12 +228,12 @@ sub get { my $user_info = filter $params, { id => $self->type('int', $user->id), real_name => $self->type('string', $user->name), - name => $self->type('email', $user->login), - email => $self->type('email', $user->email), + name => $self->type('login', $user->login), can_login => $self->type('boolean', $user->is_enabled ? 1 : 0), }; if (Bugzilla->user->in_group('editusers')) { + $user_info->{email} = $self->type('email', $user->email), $user_info->{email_enabled} = $self->type('boolean', $user->email_enabled); $user_info->{login_denied_text} = $self->type('string', $user->disabledtext); } @@ -605,10 +611,15 @@ and real name. This is the recommended way to create a Bugzilla account. -=item B<Param> +=item B<Params> =over +=item C<login> (string) - the login name for the new account. +If the installation has the C<use_email_as_login> parameter switched on, then +this parameter is ignored, and the value of the C<email> parameter will +be used as the login name for the new account. + =item C<email> (string) - the email to send the offer to. =back @@ -659,7 +670,12 @@ are the same as below. =over -=item C<email> (string) - The email address for the new user. +=item C<login> (string) - the login name for the new account. +If the installation has the C<use_email_as_login> parameter switched on, then +this parameter is ignored, and the value of the C<email> parameter will +be used as the login name for the new account. + +=item C<email> (string) - The email address for the new account's user. =item C<full_name> (string) B<Optional> - The user's full name. Will be set to empty if not specified. @@ -675,7 +691,7 @@ resetting their password) or by the administrator. =item B<Returns> -A hash containing one item, C<id>, the numeric id of the user that was +A hash containing one item, C<id>, the numeric id of the user account that was created. =item B<Errors> @@ -737,7 +753,7 @@ C<array> Contains ids of user to update. =item C<names> -C<array> Contains email/login of user to update. +C<array> Contains login name of user to update. =item C<full_name> @@ -745,8 +761,10 @@ C<string> The new name of the user. =item C<email> -C<string> The email of the user. Note that email used to login to bugzilla. -Also note that you can only update one user at a time when changing the +C<string> The email address of the user. It may be required that this is the +same as the login name. If you send different values in that case, the results +are undefined. +Note that you can only update one user at a time when changing the login name / email. (An error will be thrown if you try to update this field for multiple users at once.) @@ -967,8 +985,7 @@ C<string> The email address of the user. =item name -C<string> The login name of the user. Note that in some situations this is -different than their email. +C<string> The login name of the user. =item can_login @@ -1054,7 +1071,7 @@ C<string> The CGI parameters for the saved report. B<Note>: If you are not logged in to Bugzilla when you call this function, you will only be returned the C<id>, C<name>, and C<real_name> items. If you are logged in and not in editusers group, you will only be returned the C<id>, C<name>, -C<real_name>, C<email>, C<can_login>, and C<groups> items. The groups returned are +C<real_name>, C<can_login>, and C<groups> items. The groups returned are filtered based on your permission to bless each group. The C<saved_searches> and C<saved_reports> items are only returned if you are querying your own account, even if you are in the editusers group. diff --git a/buglist.cgi b/buglist.cgi index 941ec6ed4..69069ba82 100755 --- a/buglist.cgi +++ b/buglist.cgi @@ -909,11 +909,11 @@ if (!$user->in_group('editbugs')) { } my @bugowners = keys %$bugowners; -if (scalar(@bugowners) > 1 && $user->in_group('editbugs')) { - my $suffix = Bugzilla->params->{'emailsuffix'}; - map(s/$/$suffix/, @bugowners) if $suffix; - my $bugowners = join(",", @bugowners); - $vars->{'bugowners'} = $bugowners; +if (scalar(@bugowners) > 1 + && $user->in_group('editbugs') + && Bugzilla->params->{'use_email_as_login'}) +{ + $vars->{'bugowners'} = join(",", @bugowners); } # Whether or not to split the column titles across two rows to make diff --git a/checksetup.pl b/checksetup.pl index aacdf41be..05d98f9e1 100755 --- a/checksetup.pl +++ b/checksetup.pl @@ -461,6 +461,7 @@ The format of that file is as follows: (Any localconfig variable or parameter can be specified as above.) + $answer{'ADMIN_LOGIN'} = 'myadmin'; $answer{'ADMIN_EMAIL'} = 'myadmin@mydomain.net'; $answer{'ADMIN_PASSWORD'} = 'fooey'; $answer{'ADMIN_REALNAME'} = 'Joel Peshkin'; diff --git a/createaccount.cgi b/createaccount.cgi index 754b1b552..3b3dd7c1f 100755 --- a/createaccount.cgi +++ b/createaccount.cgi @@ -27,22 +27,25 @@ my $vars = { doc_section => 'using/creating-an-account.html' }; print $cgi->header(); -my $login = $cgi->param('login'); +my $email = $cgi->param('email'); +my $login = Bugzilla->params->{'use_email_as_login'} ? $email : $cgi->param('login'); + my $request_new_password = $cgi->param('request_new_password'); if ($request_new_password) { $template->process('account/request-new-password.html.tmpl', $vars) || ThrowTemplateError($template->error()); } -elsif (defined($login)) { +elsif ($email && $login) { $user->check_account_creation_enabled; # Check the hash token to make sure this user actually submitted # the create account form. my $token = $cgi->param('token'); check_hash_token($token, ['create_account']); - $user->check_and_send_account_creation_confirmation($login); + $user->check_and_send_account_creation_confirmation($login, $email); $vars->{'login'} = $login; + $vars->{'email'} = $email; $template->process("account/created.html.tmpl", $vars) || ThrowTemplateError($template->error()); diff --git a/docs/en/rst/administering/parameters.rst b/docs/en/rst/administering/parameters.rst index 0d2d18029..5835d654f 100644 --- a/docs/en/rst/administering/parameters.rst +++ b/docs/en/rst/administering/parameters.rst @@ -169,12 +169,6 @@ requirelogin If this option is set, all access to the system beyond the front page will require a login. No anonymous users will be permitted. -webservice_email_filter - Filter email addresses returned by the WebService API depending on if the - user is logged in or not. This works similarly to how the web UI currently - filters email addresses. If requirelogin is enabled, then this parameter - has no effect as users must be logged in to use Bugzilla anyway. - emailregexp Defines the regular expression used to validate email addresses used for login names. The default attempts to match fully qualified email addresses @@ -186,12 +180,6 @@ emailregexpdesc This description is shown to the user to explain which email addresses are allowed by the :param:`emailregexp` param. -emailsuffix - This is a string to append to any email addresses when actually sending mail - to that address. It is useful if you have changed the :param:`emailregexp` - param to only allow local usernames, but you want the mail to be delivered - to username\@my.local.hostname. - createemailregexp This defines the (case-insensitive) regexp to use for email addresses that are permitted to self-register. The default (:paramval:`.*`) permits any @@ -499,9 +487,8 @@ address. The LDAP authentication builds on top of this scheme, rather than replacing it. The initial log-in is done with a username and password for the LDAP directory. Bugzilla tries to bind to LDAP using those credentials and, if successful, tries to map this account to a -Bugzilla account. If an LDAP mail attribute is defined, the value of this -attribute is used; otherwise, the :param:`emailsuffix` parameter is appended to -the LDAP username to form a full email address. If an account for this address +Bugzilla account. An LDAP mail attribute must be defined. +If an account for this address already exists in the Bugzilla installation, it will log in to that account. If no account for that email address exists, one is created at the time of login. (In this case, Bugzilla will attempt to use the "displayName" diff --git a/docs/en/rst/administering/users.rst b/docs/en/rst/administering/users.rst index e32db2f05..7d5b33399 100644 --- a/docs/en/rst/administering/users.rst +++ b/docs/en/rst/administering/users.rst @@ -27,9 +27,7 @@ will appear in the Administration page. The first screen is a search form to search for existing user accounts. You can run searches based either on the user ID, real -name or login name (i.e. the email address, or just the first part -of the email address if the :param:`emailsuffix` parameter is set). -The search can be conducted +name, login name or email address. The search can be conducted in different ways using the listbox to the right of the text entry box. You can match by case-insensitive substring (the default), regular expression, a *reverse* regular expression @@ -55,13 +53,15 @@ Modifying Users Once you have found your user, you can change the following fields: -- *Login Name*: - This is generally the user's full email address. However, if you - have are using the :param:`emailsuffix` parameter, this may - just be the user's login name. Unless you turn off the +- *Email Address*: + This is the user's full email address. Unless you turn off the :param:`allowemailchange` parameter, users can change their login names themselves (to any valid email address). +- *Login Name*: + This is the user's login name, if it is (or can be) different from their email + address (i.e. if the "use_email_as_login" parameter is switched off). + - *Real Name*: The user's real name. Note that Bugzilla does not require this to create an account. diff --git a/docs/en/rst/api/core/v1/bug.rst b/docs/en/rst/api/core/v1/bug.rst index e13b17129..8b253a32c 100644 --- a/docs/en/rst/api/core/v1/bug.rst +++ b/docs/en/rst/api/core/v1/bug.rst @@ -251,8 +251,6 @@ name type description id int The user ID for this user. real_name string The 'real' name for this user, if any. name string The user's Bugzilla login. -email string The user's email address. Currently this is the same value as - the name. ========= ====== ============================================================== Flag object: @@ -291,6 +289,11 @@ field name in ``include_fields``. * 102 (Access Denied) You do not have access to the bug_id you specified. +**History** + +Since Bugzilla 6.0, the email address of users is private and is no longer +returned as part of the user object. + .. _rest_history: Bug History diff --git a/docs/en/rst/api/core/v1/bugzilla.rst b/docs/en/rst/api/core/v1/bugzilla.rst index 5aca556ec..f0ba58e65 100644 --- a/docs/en/rst/api/core/v1/bugzilla.rst +++ b/docs/en/rst/api/core/v1/bugzilla.rst @@ -178,7 +178,6 @@ Example response for authenticated user: "defaultseverity" : "normal", "duplicate_or_move_bug_status" : "RESOLVED", "emailregexp" : "^[\\w\\.\\+\\-=']+@[\\w\\.\\-]+\\.[\\w\\-]+$", - "emailsuffix" : "", "letsubmitterchoosemilestone" : "1", "letsubmitterchoosepriority" : "1", "mailfrom" : "bugzilla-daemon@example.com", @@ -214,7 +213,6 @@ A logged-in user can access the following parameters (listed alphabetically): * defaultseverity * duplicate_or_move_bug_status * emailregexpdesc -* emailsuffix * letsubmitterchoosemilestone * letsubmitterchoosepriority * mailfrom diff --git a/docs/en/rst/api/core/v1/user.rst b/docs/en/rst/api/core/v1/user.rst index 0638ef758..90728287e 100644 --- a/docs/en/rst/api/core/v1/user.rst +++ b/docs/en/rst/api/core/v1/user.rst @@ -142,6 +142,8 @@ to perform this action. ========== ====== ============================================================= name type description ========== ====== ============================================================= +**login** string The login name for the new user. Ignored if the + *use_email_as_login* parameter is true. **email** string The email address for the new user. full_name string The user's full name. Will be set to empty if not specified. password string The password for the new user account, in plain text. It @@ -173,6 +175,10 @@ id int The numeric ID of the user that was created. The password specified is too short. (Usually, this means the password is under three characters.) +**History** + +The *login* parameter has been added in Bugzilla 6.0. + .. _rest_user_update: Update User @@ -203,8 +209,7 @@ login names using the ``ids`` or ``names`` parameters respectively. **ids** array Additional IDs of users to update. **names** array Additional login names of users to update. full_name string The new name of the user. -email string The email of the user. Note that email used to - login to bugzilla. Also note that you can only +email string The email of the user. Also note that you can only update one user at a time when changing the login name / email. (An error will be thrown if you try to update this field for multiple users at once.) @@ -357,8 +362,7 @@ id int The unique integer ID that Bugzilla uses to represen this will not change. real_name string The actual name of the user. May be blank. email string The email address of the user. -name string The login name of the user. Note that in some - situations this is different than their email. +name string The login name of the user. can_login boolean A boolean value to indicate if the user can login into bugzilla. email_enabled boolean A boolean value to indicate if bug-related mail will @@ -404,7 +408,7 @@ query string The CGI parameters for the saved report. If you are not authenticated when you call this function, you will only be returned the ``id``, ``name``, and ``real_name`` items. If you are authenticated and not in 'editusers' group, you will only be returned the ``id``, ``name``, -``real_name``, ``email``, ``can_login``, and ``groups`` items. The groups +``real_name``, ``can_login``, and ``groups`` items. The groups returned are filtered based on your permission to bless each group. The ``saved_searches`` and ``saved_reports`` items are only returned if you are querying your own account, even if you are in the editusers group. diff --git a/editgroups.cgi b/editgroups.cgi index 49bebb04b..36c262fde 100755 --- a/editgroups.cgi +++ b/editgroups.cgi @@ -303,7 +303,7 @@ if ($action eq 'remove_regexp') { my @deleted; foreach my $member (@$users) { - if ($regexp eq '' || $member->login =~ m/$regexp/i) { + if ($regexp eq '' || $member->email =~ m/$regexp/i) { $sth_delete->execute($member->id, $group->id); push(@deleted, $member); } diff --git a/editparams.cgi b/editparams.cgi index 502d2a564..796d80d41 100755 --- a/editparams.cgi +++ b/editparams.cgi @@ -135,12 +135,11 @@ if ($action eq 'save' && $current_module) { if (($name eq "shutdownhtml") && ($value ne "")) { $vars->{'shutdown_is_active'} = 1; } - if ($name eq 'duplicate_or_move_bug_status') { - Bugzilla::Status::add_missing_bug_status_transitions($value); - } } } + call_param_onchange_handlers(\@changes); + $vars->{'message'} = 'parameters_updated'; $vars->{'param_changed'} = \@changes; diff --git a/editusers.cgi b/editusers.cgi index 9d89d2efa..3bc648509 100755 --- a/editusers.cgi +++ b/editusers.cgi @@ -72,7 +72,7 @@ if ($action eq 'search') { my $grouprestrict = $cgi->param('grouprestrict') || '0'; # 0 = disabled only, 1 = enabled only, 2 = everyone my $is_enabled = $cgi->param('is_enabled') // 2; - my $query = 'SELECT DISTINCT userid, login_name, realname, is_enabled, ' . + my $query = 'SELECT DISTINCT userid, login_name, email, realname, is_enabled, ' . $dbh->sql_date_format('last_seen_date', '%Y-%m-%d') . ' AS last_seen_date ' . 'FROM profiles'; my @bindValues; @@ -98,7 +98,8 @@ if ($action eq 'search') { }; $nextCondition = 'AND'; } - } else { + } + else { $visibleGroups = 1; if ($grouprestrict eq '1') { $query .= qq{, user_group_map AS ugm @@ -116,10 +117,10 @@ if ($action eq 'search') { $vars->{'users'} = {}; } else { - # Handle selection by login name, real name, or userid. + # Handle selection by login name, email, real name, or userid. if (defined($matchtype)) { $query .= " $nextCondition "; - my $expr = ""; + my $expr = ''; if ($matchvalue eq 'userid') { if ($matchstr) { my $stored_matchstr = $matchstr; @@ -127,6 +128,8 @@ if ($action eq 'search') { || ThrowUserError('illegal_user_id', {userid => $stored_matchstr}); } $expr = "profiles.userid"; + } elsif ($matchvalue eq 'email') { + $expr = 'profiles.email'; } elsif ($matchvalue eq 'realname') { $expr = "profiles.realname"; } else { @@ -182,7 +185,8 @@ if ($action eq 'search') { my $match_user_id = $vars->{'users'}[0]->{'userid'}; my $match_user = check_user($match_user_id); edit_processing($match_user); - } else { + } + else { $template->process('admin/users/list.html.tmpl', $vars) || ThrowTemplateError($template->error()); } @@ -211,8 +215,14 @@ if ($action eq 'search') { my $password = $cgi->param('password'); $password = '*' if !defined $password; + my $login_name = $cgi->param('login'); + my $email = $cgi->param('email'); + + $login_name = $email if Bugzilla->params->{use_email_as_login}; + my $new_user = Bugzilla::User->create({ - login_name => scalar $cgi->param('login'), + login_name => $login_name, + email => $email, cryptpassword => $password, realname => scalar $cgi->param('name'), disabledtext => scalar $cgi->param('disabledtext'), @@ -268,7 +278,9 @@ if ($action eq 'search') { # is not authorized. my $changes = {}; if ($editusers) { - $otherUser->set_login(scalar $cgi->param('login')); + $otherUser->set_login(scalar $cgi->param('login')) + unless Bugzilla->params->{use_email_as_login}; + $otherUser->set_email(scalar $cgi->param('email')); $otherUser->set_name(scalar $cgi->param('name')); $otherUser->set_password(scalar $cgi->param('password')) if $cgi->param('password'); diff --git a/email_in.pl b/email_in.pl index c34d2fc24..cb3c4883e 100755 --- a/email_in.pl +++ b/email_in.pl @@ -508,10 +508,6 @@ Bugzilla::Hook::process('email_in_after_parse', { fields => $mail_fields }); my $attachments = delete $mail_fields->{'attachments'}; my $username = $mail_fields->{'reporter'}; -# If emailsuffix is in use, we have to remove it from the email address. -if (my $suffix = Bugzilla->params->{'emailsuffix'}) { - $username =~ s/\Q$suffix\E$//i; -} my $user = Bugzilla::User->check($username); Bugzilla->set_user($user); diff --git a/extensions/Voting/Extension.pm b/extensions/Voting/Extension.pm index a0ed8f3fd..3de790d83 100644 --- a/extensions/Voting/Extension.pm +++ b/extensions/Voting/Extension.pm @@ -746,7 +746,7 @@ sub _remove_votes { my $whopart = ($who) ? " AND votes.who = $who" : ""; - my $sth = $dbh->prepare("SELECT profiles.login_name, " . + my $sth = $dbh->prepare("SELECT profiles.email, " . "profiles.userid, votes.vote_count, " . "products.votesperuser, products.maxvotesperbug " . "FROM profiles " . @@ -756,8 +756,8 @@ sub _remove_votes { "WHERE votes.bug_id = ? " . $whopart); $sth->execute($id); my @list; - while (my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = $sth->fetchrow_array()) { - push(@list, [$name, $userid, $oldvotes, $votesperuser, $maxvotesperbug]); + while (my ($email, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = $sth->fetchrow_array()) { + push(@list, [$email, $userid, $oldvotes, $votesperuser, $maxvotesperbug]); } # @messages stores all emails which have to be sent, if any. @@ -766,7 +766,7 @@ sub _remove_votes { if (scalar(@list)) { foreach my $ref (@list) { - my ($name, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref); + my ($email, $userid, $oldvotes, $votesperuser, $maxvotesperbug) = (@$ref); $maxvotesperbug = min($votesperuser, $maxvotesperbug); @@ -797,7 +797,7 @@ sub _remove_votes { # Now lets send the e-mail to alert the user to the fact that their votes have # been reduced or removed. my $vars = { - 'to' => $name . Bugzilla->params->{'emailsuffix'}, + 'to' => $email, 'bugid' => $id, 'reason' => $reason, diff --git a/sanitycheck.cgi b/sanitycheck.cgi index 85f73bc6d..4c29f90fe 100755 --- a/sanitycheck.cgi +++ b/sanitycheck.cgi @@ -645,7 +645,7 @@ DoubleCrossCheck("milestones", "product_id", "value", Status('profile_login_start'); -my $sth = $dbh->prepare(q{SELECT userid, login_name FROM profiles}); +my $sth = $dbh->prepare(q{SELECT userid, email FROM profiles}); $sth->execute; while (my ($id, $email) = $sth->fetchrow_array) { diff --git a/skins/standard/global.css b/skins/standard/global.css index f85976516..90cd1743d 100644 --- a/skins/standard/global.css +++ b/skins/standard/global.css @@ -530,6 +530,24 @@ input.requestee { font-weight: bold; } +form.table_layout div { + margin-bottom: 0.4em; +} + +form.table_layout label { + font-weight: bold; + width: 8em; + display: inline-block; + float: left; + text-align: right; + padding-right: 0.5em; +} + +.flex { + display: flex; + flex-wrap: wrap; +} + .throw_error a:visited { color: darkblue ; } diff --git a/t/007util.t b/t/007util.t index 8f54e828e..5861c2d35 100644 --- a/t/007util.t +++ b/t/007util.t @@ -66,7 +66,6 @@ foreach my $input (keys %email_strings) { # validate_email_syntax. We need to override some parameters. my $params = Bugzilla->params; $params->{emailregexp} = '.*'; -$params->{emailsuffix} = ''; my $ascii_email = 'admin@company.com'; # U+0430 returns the Cyrillic "а", which looks similar to the ASCII "a". my $utf8_email = "\N{U+0430}dmin\@company.com"; diff --git a/t/008filter.t b/t/008filter.t index cd0af0735..0da18c38a 100644 --- a/t/008filter.t +++ b/t/008filter.t @@ -161,7 +161,7 @@ sub directive_ok { # Directives return 1 if $directive =~ /^(IF|END|UNLESS|FOREACH|PROCESS|INCLUDE| - BLOCK|USE|ELSE|NEXT|LAST|DEFAULT| + BLOCK|USE|ELSE|NEXT|LAST|DEFAULT|CALL| ELSIF|SET|SWITCH|CASE|WHILE|RETURN|STOP| TRY|CATCH|FINAL|THROW|CLEAR|MACRO|FILTER)/x; diff --git a/template/en/default/account/auth/login-small.html.tmpl b/template/en/default/account/auth/login-small.html.tmpl index 97d96a5af..5536d306a 100644 --- a/template/en/default/account/auth/login-small.html.tmpl +++ b/template/en/default/account/auth/login-small.html.tmpl @@ -65,12 +65,8 @@ <form action="token.cgi" method="post" id="forgot_form[% qs_suffix %]" class="mini_forgot bz_default_hidden"> <label for="login[% qs_suffix FILTER html %]">Login:</label> - <input name="loginname" size="20" id="login[% qs_suffix FILTER html %]" required - [% IF login_not_email %] - placeholder="Your Login" - [% ELSE %] - type="email" placeholder="Your Email Address" - [% END %]> + <input name="email" size="20" id="login[% qs_suffix FILTER html %]" required + type="email" placeholder="Your Email Address"> <input id="forgot_button[% qs_suffix %]" value="Reset Password" type="submit"> <input type="hidden" name="a" value="reqpw"> diff --git a/template/en/default/account/auth/login.html.tmpl b/template/en/default/account/auth/login.html.tmpl index afa77df7f..ac0fbcb99 100644 --- a/template/en/default/account/auth/login.html.tmpl +++ b/template/en/default/account/auth/login.html.tmpl @@ -40,7 +40,6 @@ <td> <input id="Bugzilla_login" name="Bugzilla_login" [%- ' type="email"' UNLESS login_not_email %] autofocus required> - [% Param('emailsuffix') FILTER html %] </td> </tr> diff --git a/template/en/default/account/cancel-token.txt.tmpl b/template/en/default/account/cancel-token.txt.tmpl index 384f2cfe0..46e058435 100644 --- a/template/en/default/account/cancel-token.txt.tmpl +++ b/template/en/default/account/cancel-token.txt.tmpl @@ -21,7 +21,7 @@ to [% Param('maintainer') %] if you suspect foul play. Token: [% token %] Token Type: [% tokentype %] - User: [% emailaddress %] + User: [% login %] Issue Date: [% issuedate FILTER time("%Y-%m-%d %H:%M:%S %Z", timezone) %] Event Data: [% eventdata %] Canceled Because: [% PROCESS cancelactionmessage %] diff --git a/template/en/default/account/create.html.tmpl b/template/en/default/account/create.html.tmpl index 5711a726f..e67848beb 100644 --- a/template/en/default/account/create.html.tmpl +++ b/template/en/default/account/create.html.tmpl @@ -6,25 +6,16 @@ # defined by the Mozilla Public License, v. 2.0. #%] -[%# INTERFACE - # none - # - # Param("maintainer") is used to display the maintainer's email. - # Param("emailsuffix") is used to pre-fill the email field. - #%] - [% PROCESS global/header.html.tmpl title = "Create a new $terms.Bugzilla account" %] <p> To create a [% terms.Bugzilla %] account, all you need to do is to enter -[% IF Param('emailsuffix') == '' %] + [% IF !Param('use_email_as_login') %] + a login name of your choice and + [% END %] a legitimate email address. -[% ELSE %] - an account name which when combined with [% Param('emailsuffix') %] - corresponds to an address where you receive email. -[% END %] You will receive an email at this address to confirm the creation of your account. <b>You will not be able to log in until you receive the email.</b> If it doesn't arrive within a reasonable amount of time, you may contact @@ -32,50 +23,66 @@ at <a href="mailto:[% Param("maintainer") %]">[% Param("maintainer") %]</a>. </p> -[% IF Param('allowemailchange') %] <p> If you already have an account and want to change your - [% IF Param('emailsuffix') == '' %] - email address, - [% ELSE %] - login name, + [% IF Param('allowemailchange') %] + email address or [% END %] - you can change it from the Preferences page after logging in. + login name, you can change it from the Preferences page after logging in. </p> -[% END %] <p> A user account is required to report new [% terms.bugs %] or to comment into existing ones, as you may be contacted for more information if needed. This also lets other users clearly identify who is the author of comments or changes made into [% terms.bugs %]. <b>Note that your email address will - <em>never</em> be displayed to logged out users. Only registered users will be - able to see it.</b> + <em>never</em> be displayed to + [% IF Param('use_email_as_login') %] + logged out users. Only registered users will be able to see it.</b> + [% ELSE %] + other users, unless you decide to use your email address as login. Only + your login is visible to other users.</b> + [% END %] </p> -[% IF Param('createemailregexp') == '.*' && Param('emailsuffix') == '' %] +[% IF Param('createemailregexp') == '.*' %] <p> <b>PRIVACY NOTICE:</b> [% terms.Bugzilla %] is an open [% terms.bug %] - tracking system. Activity on most [% terms.bugs %], including email - addresses, will be visible to registered users. We <b>recommend</b> using a - secondary account or free web email service (such as Gmail, Yahoo, - Hotmail, or similar) to avoid receiving spam at your primary email address. + tracking system. Activity on most [% terms.bugs %] will be visible to + registered users. + [% IF Param('use_email_as_login') %] + That includes email addresses. We <b>recommend</b> using a + secondary account or free web email service (such as Gmail, Yahoo, + Hotmail, or similar) to avoid receiving spam at your primary email address. + [% END %] </p> [% END %] -<form id="account_creation_form" method="get" action="createaccount.cgi"> - <span class="label"> - [% IF Param('emailsuffix') %] - Login: - [% ELSE %] - Email address: - [% END %] - </span> - <input size="35" id="login" name="login" autofocus - [%- ' type="email"' UNLESS Param('emailsuffix') %] required> - [% Param('emailsuffix') FILTER html %] - <input type="hidden" id="token" name="token" value="[% issue_hash_token(['create_account']) FILTER html %]"> - <input type="submit" id="send" value="Send"> +<form id="account_creation_form" action="createaccount.cgi" class="table_layout"> + [% IF !Param('use_email_as_login') %] + <div> + <label for="login">Login</label> + <div class="flex"> + <input size="35" id="login" name="login" autofocus required> + <span class="bz_info"> + (no whitespaces and no @ character, unless it matches your email address) + </span> + </div> + </div> + [% END %] + + <div> + <label for="email">Email Address</label> + <input size="35" id="email" name="email" type="email" + [%~ " autofocus" IF Param('use_email_as_login') %] required> + </div> + + <div> + <label> </label> + <input type="hidden" id="token" name="token" + value="[% issue_hash_token(['create_account']) FILTER html %]"> + <input type="submit" id="send" value="Send"> + </div> </form> [% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/account/created.html.tmpl b/template/en/default/account/created.html.tmpl index f35deca83..f87571936 100644 --- a/template/en/default/account/created.html.tmpl +++ b/template/en/default/account/created.html.tmpl @@ -7,7 +7,8 @@ #%] [%# INTERFACE: - # login: string. The user's Bugzilla login email address. + # login: string. The user's Bugzilla login. + # email: string. The user's Bugzilla email address. #%] [% title = BLOCK %] @@ -17,9 +18,9 @@ [% PROCESS global/header.html.tmpl title = title %] <p> - A confirmation email has been sent containing a link to continue - creating an account. The link will expire if an account is not - created within [% constants.MAX_TOKEN_AGE FILTER html %] days. + A confirmation email has been sent to <em>[% email FILTER html %]</em> containing + a link to continue creating an account. The link will expire if an account is + not created within [% constants.MAX_TOKEN_AGE FILTER html %] days. </p> [% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/account/email/confirm-new.html.tmpl b/template/en/default/account/email/confirm-new.html.tmpl index 3b9866004..87a1c6440 100644 --- a/template/en/default/account/email/confirm-new.html.tmpl +++ b/template/en/default/account/email/confirm-new.html.tmpl @@ -9,26 +9,40 @@ [%# INTERFACE: # token: string. The token to be used in the user account creation. # email: email address of the user account. + # login: login of the user account. # expiration_ts: expiration date of the token. #%] -[% title = BLOCK %]Create a new user account for '[% email FILTER html %]'[% END %] -[% PROCESS "global/header.html.tmpl" - title = title -%] +[% title = BLOCK %]Create a new user account for '[% login FILTER html %]'[% END %] +[% PROCESS "global/header.html.tmpl" title = title %] [% password_complexity = Param('password_complexity') %] <p> To create your account, you must enter a password in the form below. - Your email address and Real Name (if provided) will be shown with - changes you make. + Your real name is optional, but recommended. </p> <form id="confirm_account_form" method="post" action="token.cgi"> <input type="hidden" name="t" value="[% token FILTER html %]"> <input type="hidden" name="a" value="confirm_new_account"> <table> + [% IF !Param('use_email_as_login') %] + <tr> + <th>Login:</th> + <td> + [% IF login_already_in_use %] + <input id="login" name="login" required value="[% login FILTER html %]"> + <span class="warning"> + The login '[% login FILTER html %]' has been taken by another + user since you requested your account. Please choose another login. + </span> + [% ELSE %] + [% login FILTER html %] + [% END %] + </td> + </tr> + [% END %] <tr> <th>Email Address:</th> <td>[% email FILTER html %]</td> diff --git a/template/en/default/account/email/request-new.txt.tmpl b/template/en/default/account/email/request-new.txt.tmpl index 8ca0ff122..b56bf4f79 100644 --- a/template/en/default/account/email/request-new.txt.tmpl +++ b/template/en/default/account/email/request-new.txt.tmpl @@ -10,6 +10,7 @@ # token: random string used to authenticate the transaction. # expiration_ts: expiration date of the token. # email: email address of the new account. + # login: login name of the new account. #%] From: [% Param('mailfrom') %] @@ -18,7 +19,7 @@ Subject: [% terms.Bugzilla %]: confirm account creation X-Bugzilla-Type: admin [%+ terms.Bugzilla %] has received a request to create a user account -using your email address ([% email %]). +[%+ "($login)" IF !Param('use_email_as_login') %] using your email address ([% email %]). To continue creating an account using this email address, visit the following link by [% expiration_ts FILTER time("%B %e, %Y at %H:%M %Z") %]: @@ -31,11 +32,14 @@ again by going to: [%+ urlbase %]createaccount.cgi -[% IF Param('createemailregexp') == '.*' && Param('emailsuffix') == '' %] -PRIVACY NOTICE: [% terms.Bugzilla %] is an open [% terms.bug %] tracking system. Activity on most -[%+ terms.bugs %], including email addresses, will be visible to the public. We recommend -using a secondary account or free web email service (such as Gmail, Yahoo, -Hotmail, or similar) to avoid receiving spam at your primary email address. +[% IF Param('createemailregexp') == '.*' %] +PRIVACY NOTICE: [% terms.Bugzilla %] is an open [% terms.bug %] tracking +system. Activity on most [%+ terms.bugs %] will be visible to the public. +[% IF Param('use_email_as_login') %] +This includes email addresses. We recommend using a secondary account or free +web email service (such as Gmail, Yahoo, Hotmail, or similar) to avoid +receiving spam at your primary email address. +[% END %] [% END %] If you do not wish to create an account, or if this request was made in diff --git a/template/en/default/account/prefs/account.html.tmpl b/template/en/default/account/prefs/account.html.tmpl index ddfc79aaf..81eb61f60 100644 --- a/template/en/default/account/prefs/account.html.tmpl +++ b/template/en/default/account/prefs/account.html.tmpl @@ -7,17 +7,17 @@ #%] [%# INTERFACE: - # realname: string. The user's real name, if any. - # login_change_date: string. The date the email change will be complete. (optional) - # new_login_name: string. The user's new Bugzilla login whilst not confirmed. (optional) + # email_change_date: string. The date the email change will be complete. (optional) + # new_email: string. The user's new email address whilst not confirmed. (optional) #%] <table> [% IF user.authorizer.can_change_password - || (user.authorizer.can_change_email && Param('allowemailchange')) %] + || (user.authorizer.can_change_email && Param('allowemailchange')) + || user.authorizer.can_change_login %] <tr> - <td colspan="3"> - Please enter your existing password to confirm account changes. + <td colspan="2"> + Please enter your current password to confirm any changes in this section. </td> </tr> <tr> @@ -27,9 +27,6 @@ <input type="password" name="old_password" id="old_password"> </td> </tr> - <tr> - <td colspan="2"><hr></td> - </tr> [% END %] [% IF user.authorizer.can_change_password %] @@ -37,6 +34,9 @@ <th><label for="new_password1">New password:</label></th> <td> <input type="password" name="new_password1" id="new_password1"> + <span class="bz_info"> + (Leave empty to keep current password) + </span> </td> </tr> @@ -48,55 +48,75 @@ </tr> [% END %] - <tr> - <th> - <label for="realname">Your real name (optional, but encouraged):</label> - </th> - <td> - <input size="35" name="realname" id="realname" value="[% realname FILTER html %]"> - </td> - </tr> + [% UNLESS Param('use_email_as_login') %] + <tr> + <th><label for="new_login">Your login name:</label></th> + <td> + [% IF user.authorizer.can_change_login %] + <input size="35" name="new_login" id="new_login" + value="[% user.login FILTER html %]"> + [% ELSE %] + [% user.login FILTER html %] + [% END %] + </td> + </tr> + [% END %] - [% IF user.authorizer.can_change_email && Param('allowemailchange') %] - [% IF login_change_date %] - [% IF new_login_name %] - <tr> - <th>Pending email address:</th> - <td>[% new_login_name FILTER html %]</td> - </tr> - <tr> - <th>Change request expires:</th> - <td>[% login_change_date FILTER time %]</td> - </tr> - [% ELSE %] - <tr> - <th>Confirmed email address:</th> - <td>[% user.login FILTER html %]</td> - </tr> - <tr> - <th>Completion date:</th> - <td>[% login_change_date FILTER time %]</td> - </tr> - [% END %] + [% IF user.authorizer.can_change_email + && Param('allowemailchange') + && email_change_date %] + [% IF new_email %] + <tr> + <th><label>Pending email address:</label></th> + <td>[% new_email FILTER html %]</td> + </tr> + <tr> + <th><label>Change request expires:</label></th> + <td>[% email_change_date FILTER time %]</td> + </tr> [% ELSE %] <tr> - <th> - <label for="new_login_name"> - [% IF Param('emailsuffix') %] - New login: - [% ELSE %] - New email address: - [% END %] - </label> - </th> - <td> - <input size="35" id="new_login_name" name="new_login_name" - [%- ' type="email"' UNLESS Param('emailsuffix') %]> - </td> + <th><label>Confirmed email address:</label></th> + <td>[% user.email FILTER html %]</td> + </tr> + <tr> + <th><label>Completion date:</label></th> + <td>[% email_change_date FILTER time %]</td> </tr> [% END %] + [% ELSE %] + <tr> + <th> + <label for="new_email"> + Your email address[% "/login" IF Param('use_email_as_login') %]: + </label> + </th> + <td> + [% IF user.authorizer.can_change_email && Param('allowemailchange') %] + <input size="35" name="new_email" id="new_email" + value="[% user.email FILTER html %]" + [%~ ' type="email"' IF Param('use_email_as_login') %]> + [% ELSE %] + [% user.email FILTER html %] + [% END %] + </td> + </tr> [% END %] + <tr> + <td colspan="2"><hr></td> + </tr> + + <tr> + <th> + <label for="realname">Your real name (optional, but encouraged):</label> + </th> + <td> + <input size="35" name="realname" id="realname" + value="[% user.name FILTER html %]"> + </td> + </tr> + [% Hook.process('field') %] </table> diff --git a/template/en/default/account/request-new-password.html.tmpl b/template/en/default/account/request-new-password.html.tmpl index a94b3a114..73caa6dcb 100644 --- a/template/en/default/account/request-new-password.html.tmpl +++ b/template/en/default/account/request-new-password.html.tmpl @@ -10,20 +10,14 @@ [% IF user.authorizer.can_change_password %] <p> - If you have an account, but have forgotten your password, enter your - [% IF Param('emailsuffix') %] - login name - [% ELSE %] - email address - [% END %] - below and submit a request to change your password. An email with details - on how to reset your password will be sent. + If you have an account, but have forgotten your password, enter your email + address below and submit a request to change your password. An email with + details on how to reset your password will be sent. </p> <form id="forgot_password" method="get" action="token.cgi"> <input type="hidden" name="a" value="reqpw"> - <input id="loginname" [% IF !Param('emailsuffix') %]type="email"[% END %] - name="loginname" autofocus required> + <input id="email" type="email" name="email" autofocus required> <input type="hidden" id="token" name="token" value="[% issue_hash_token(['reqpw']) FILTER html %]"> <input type="submit" id="request" value="Reset Password"> diff --git a/template/en/default/admin/flag-type/edit.html.tmpl b/template/en/default/admin/flag-type/edit.html.tmpl index 7505ebd8c..1fccea4a5 100644 --- a/template/en/default/admin/flag-type/edit.html.tmpl +++ b/template/en/default/admin/flag-type/edit.html.tmpl @@ -161,11 +161,7 @@ if requestable, who should get carbon copied on email notification of requests. This is a comma-separated list of full e-mail addresses which do not need to be [% terms.Bugzilla %] logins. - [% IF Param('emailsuffix') %] - Note that the configured emailsuffix - <kbd>[% Param('emailsuffix') %]</kbd> will <em>not</em> be appended - to these addresses, so you should add it explicitly if so desired. - [% END %]<br> + <br> <input type="text" name="cc_list" value="[% type.cc_list FILTER html %]" size="80" maxlength="200" [%- ' disabled="disabled"' UNLESS can_fully_edit %]> </td> diff --git a/template/en/default/admin/groups/confirm-remove.html.tmpl b/template/en/default/admin/groups/confirm-remove.html.tmpl index 3249ee384..ef3e44a91 100644 --- a/template/en/default/admin/groups/confirm-remove.html.tmpl +++ b/template/en/default/admin/groups/confirm-remove.html.tmpl @@ -12,7 +12,7 @@ #%] [% IF regexp %] - [% title = "Confirm: Remove Explicit Members in the Regular Expression?" %] + [% title = "Confirm: Remove Explicit Members Using A Regular Expression?" %] [% ELSE %] [% title = "Confirm: Remove All Explicit Members?" %] [% END %] @@ -24,10 +24,10 @@ [% IF regexp %] <p>This option will remove all users from '[% group.name FILTER html %]' - whose login names match the regular expression: + whose email addresses match the regular expression: '[% regexp FILTER html %]'</p> [% ELSE %] - <p>This option will remove all explicitly defined users + <p>This option will remove all explicitly-added users from '[% group.name FILTER html %].'</p> [% END %] diff --git a/template/en/default/admin/groups/edit.html.tmpl b/template/en/default/admin/groups/edit.html.tmpl index 3e64fa7c4..5a20ef36e 100644 --- a/template/en/default/admin/groups/edit.html.tmpl +++ b/template/en/default/admin/groups/edit.html.tmpl @@ -165,7 +165,7 @@ <form method="post" action="editgroups.cgi"> <fieldset id="mass-remove"> - <legend>Remove all explicit memberships from users whose login names + <legend>Remove all explicit memberships from users whose email addresses match the following regular expression:</legend> <input type="text" size="20" name="regexp"> <input type="submit" id="remove-membership" value="Remove Memberships"> diff --git a/template/en/default/admin/params/auth.html.tmpl b/template/en/default/admin/params/auth.html.tmpl index 06f85ed26..79fa8123b 100644 --- a/template/en/default/admin/params/auth.html.tmpl +++ b/template/en/default/admin/params/auth.html.tmpl @@ -93,29 +93,16 @@ "front page will require a login. No anonymous users will " _ "be permitted.", - webservice_email_filter => - "Filter email addresses returned by the WebService API depending on " _ - "if the user is logged in or not. This works similarly to how the " _ - "web UI currently filters email addresses. If <var>requirelogin</var> " _ - "is enabled, then this parameter has no effect as users must be logged " _ - "in to use Bugzilla.", - emailregexp => "This defines the regular expression to use for legal email addresses. " _ "The default tries to match fully qualified email addresses. " _ "Use <kbd>.*</kbd> to accept any email address following the " _ "<a href=\"http://tools.ietf.org/html/rfc2822#section-3.4.1\">RFC 2822</a> " _ - "specification. Another popular value to put here is <kbd>^[^@]+$</kbd>, " _ - "which means 'local usernames, no @ allowed.'", + "specification.", emailregexpdesc => "This description explains valid addresses that " _ "are allowed by the <var>emailregexp</var> param.", - emailsuffix => "This is a string to append to any email addresses when actually " _ - "sending mail to that address. It is useful if you have changed " _ - "the <var>emailregexp</var> param to only allow local usernames, " _ - "but you want the mail to be delivered to username@my.local.hostname.", - createemailregexp => "This defines the (case-insensitive) regexp to use for email addresses that are " _ "permitted to self-register using a 'New Account' feature. The " _ "default (.*) permits any account matching the emailregexp " _ @@ -123,6 +110,13 @@ "will be permitted to create their own accounts and all accounts " _ "will have to be created by an administrator.", + use_email_as_login => "If switched on, users will log in with the email " _ + "address of the account. If switched off, they will " _ + "log in with a separate identifier.<br>" _ + "<strong>Switching this from off to on results in " _ + "the destructive act of the login name for all users " _ + "being set to their email address!</strong>", + password_complexity => "Set the complexity required for passwords. In all cases must the passwords " _ "be at least ${constants.USER_PASSWORD_MIN_LENGTH} characters long." _ diff --git a/template/en/default/admin/params/ldap.html.tmpl b/template/en/default/admin/params/ldap.html.tmpl index ab3521f27..d31aad825 100644 --- a/template/en/default/admin/params/ldap.html.tmpl +++ b/template/en/default/admin/params/ldap.html.tmpl @@ -35,10 +35,7 @@ LDAPmailattribute => "The name of the attribute of a user in your " _ "directory that contains the email address, to be " _ - "used as Bugzilla username. If this parameter " _ - "is empty, Bugzilla will use the LDAP username"_ - " as the Bugzilla username. You may also want" _ - " to set the \"emailsuffix\" parameter, in this case.", + "used to send the user email.", LDAPfilter => "LDAP filter to AND with the <var>LDAPuidattribute</var> for " _ "filtering the list of valid users." } diff --git a/template/en/default/admin/users/confirm-delete.html.tmpl b/template/en/default/admin/users/confirm-delete.html.tmpl index 4dab8b471..07b6ccb25 100644 --- a/template/en/default/admin/users/confirm-delete.html.tmpl +++ b/template/en/default/admin/users/confirm-delete.html.tmpl @@ -51,9 +51,15 @@ <th>Field</th> <th>Value</th> </tr> + [% IF !Param('use_email_as_login') %] + <tr> + <th>Login:</th> + <td>[% otheruser.login FILTER html %]</td> + </tr> + [% END %] <tr> - <th>Login name:</th> - <td>[% otheruser.login FILTER html %]</td> + <th>Email address:</th> + <td>[% otheruser.email FILTER html %]</td> </tr> <tr> <th>Real name:</th> diff --git a/template/en/default/admin/users/list.html.tmpl b/template/en/default/admin/users/list.html.tmpl index f90996882..168e6dd90 100644 --- a/template/en/default/admin/users/list.html.tmpl +++ b/template/en/default/admin/users/list.html.tmpl @@ -27,35 +27,44 @@ [% listselectionurlparams = INCLUDE listselectionurlparams %] [% columns = - [{name => 'login_name' - heading => 'Edit user...' - contentlink => 'editusers.cgi?action=edit&userid=%%userid%%' _ - listselectionurlparams - } - {name => 'realname' - heading => 'Real name' - } - {name => 'last_seen_date' - heading => 'Last Login' - } - {heading => 'Account History' - content => 'View' - contentlink => 'editusers.cgi?action=activity' _ - '&userid=%%userid%%' _ - listselectionurlparams + [{name => 'login_name' + heading => 'Edit user...' + contentlink => 'editusers.cgi?action=edit&userid=%%userid%%' _ + listselectionurlparams + }, + {name => 'email' + heading => 'Email address' + }, + {name => 'realname' + heading => 'Real name' + }, + {name => 'last_seen_date' + heading => 'Last Login' + }, + {name => 'view_history_link', + heading => 'Account History' + content => 'View' + contentlink => 'editusers.cgi?action=activity' _ + '&userid=%%userid%%' _ + listselectionurlparams + }, + {name => 'action_link', + heading => 'Action' + content => 'Delete' + contentlink => 'editusers.cgi?action=del' _ + '&userid=%%userid%%' _ + listselectionurlparams } ] -%] + %] + +[%# Eliminate inappropriate columns, starting at the end %] +[% IF NOT (Param('allowuserdeletion') && editusers) %] + [% CALL columns.splice(5, 1) %] +[% END %] -[% IF Param('allowuserdeletion') && editusers %] - [% columns.push({heading => 'Action' - content => 'Delete' - contentlink => 'editusers.cgi?action=del' _ - '&userid=%%userid%%' _ - listselectionurlparams - } - ) - %] +[% IF Param('use_email_as_login') %] + [% CALL columns.splice(1, 1) %] [% END %] [%# Disabled users are crossed out. Missing realnames are noticed in red. %] diff --git a/template/en/default/admin/users/search.html.tmpl b/template/en/default/admin/users/search.html.tmpl index 17477a012..f71589480 100644 --- a/template/en/default/admin/users/search.html.tmpl +++ b/template/en/default/admin/users/search.html.tmpl @@ -30,6 +30,7 @@ <p><label for="matchvalue">List users with</label> <select id="matchvalue" name="matchvalue"> <option value="login_name">login name</option> + <option value="email">email address</option> <option value="realname">real name</option> <option value="userid">user id</option> </select> diff --git a/template/en/default/admin/users/userdata.html.tmpl b/template/en/default/admin/users/userdata.html.tmpl index c08cd0018..8f925e69a 100644 --- a/template/en/default/admin/users/userdata.html.tmpl +++ b/template/en/default/admin/users/userdata.html.tmpl @@ -17,13 +17,31 @@ var disable_mail_manually_set = [% (otheruser.email_disabled ? 1 : 0) FILTER js %]; </script> +[% IF NOT Param('use_email_as_login') %] + <tr> + <th><label for="login">Login:</label></th> + <td> + [% IF editusers %] + <input size="64" maxlength="255" id="login" name="login" + value="[% otheruser.login FILTER html %]" + [%- " autofocus" UNLESS editform %] required> + [% ELSE %] + [% otheruser.login FILTER html %] + [% END %] + </td> + </tr> +[% END %] <tr> - <th><label for="login">Login name:</label></th> + <th> + <label for="email"> + Email address [% ' (login)' IF Param('use_email_as_login') %]: + </label> + </th> <td> [% IF editusers %] - <input size="64" maxlength="255" id="login" name="login" - value="[% otheruser.login FILTER html %]" - [%- " autofocus" UNLESS editform %] required> + <input size="64" maxlength="255" id="email" name="email" + value="[% otheruser.email FILTER html %]" + [%- " autofocus" IF Param('use_email_as_login') AND NOT editform %] required> [% IF editform %] [% IF !otheruser.in_group('bz_sudo_protect') %] <br> @@ -32,7 +50,7 @@ [% END %] [% END %] [% ELSE %] - [% otheruser.login FILTER html %] + [% otheruser.email FILTER html %] [% END %] </td> </tr> diff --git a/template/en/default/bug/edit.html.tmpl b/template/en/default/bug/edit.html.tmpl index 7f2b741f6..d10053702 100644 --- a/template/en/default/bug/edit.html.tmpl +++ b/template/en/default/bug/edit.html.tmpl @@ -765,7 +765,7 @@ users [% END %] [% IF user.id %] - [% IF bug.cc.contains( user.email ) %] + [% IF bug.cc.contains( user.login ) %] including you [% END %] [% END %] diff --git a/template/en/default/bug/show.xml.tmpl b/template/en/default/bug/show.xml.tmpl index 2b2d32d5a..fe5f4f6f4 100644 --- a/template/en/default/bug/show.xml.tmpl +++ b/template/en/default/bug/show.xml.tmpl @@ -18,7 +18,7 @@ %] maintainer="[% Param('maintainer') FILTER xml %]" [% IF user.id %] - exporter="[% user.email FILTER email FILTER xml %]" + exporter="[% user.login FILTER email FILTER xml %]" [% END %] > @@ -76,7 +76,7 @@ <type>[% a.contenttype FILTER xml %]</type> <size>[% a.datasize FILTER xml %]</size> <attacher[% IF a.attacher.name != '' %] name="[% a.attacher.name FILTER xml %]"[% END -%]> - [% a.attacher.email FILTER email FILTER xml %]</attacher> + [% a.attacher.login FILTER email FILTER xml %]</attacher> [%# This is here so automated clients can still use attachment.cgi %] [% IF displayfields.token && user.id %] <token>[% issue_hash_token([a.id, a.modification_time]) FILTER xml %]</token> @@ -113,7 +113,7 @@ [% IF field == 'reporter' OR field == 'assigned_to' OR field == 'qa_contact' %] [% name = val.name %] - [% val = val.email FILTER email %] + [% val = val.login FILTER email %] [% ELSIF field == 'cc' %] [% val = val FILTER email %] [% ELSIF field == 'creation_ts' OR field == 'delta_ts' %] @@ -134,9 +134,9 @@ id="[% flag.id FILTER xml %]" type_id="[% flag.type_id FILTER xml %]" status="[% flag.status FILTER xml %]" - setter="[% flag.setter.email FILTER email FILTER xml %]" + setter="[% flag.setter.login FILTER email FILTER xml %]" [% IF flag.status == "?" && flag.requestee %] - requestee="[% flag.requestee.email FILTER email FILTER xml %]" + requestee="[% flag.requestee.login FILTER email FILTER xml %]" [% END %] /> [% END %] @@ -150,7 +150,7 @@ [% IF c.is_about_attachment %] <attachid>[% c.extra_data FILTER xml %]</attachid> [% END %] - <who name="[% c.author.name FILTER xml %]">[% c.author.email FILTER email FILTER xml %]</who> + <who name="[% c.author.name FILTER xml %]">[% c.author.login FILTER email FILTER xml %]</who> <bug_when>[% c.creation_ts FILTER time("%Y-%m-%d %T %z") FILTER xml %]</bug_when> [% IF user.is_timetracker && (c.work_time - 0 != 0) %] <work_time>[% PROCESS formattimeunit time_unit = c.work_time FILTER xml %]</work_time> diff --git a/template/en/default/email/whine.txt.tmpl b/template/en/default/email/whine.txt.tmpl index 106a94a5e..5844c92f3 100644 --- a/template/en/default/email/whine.txt.tmpl +++ b/template/en/default/email/whine.txt.tmpl @@ -7,7 +7,7 @@ #%] From: [% Param("mailfrom") %] -To: [% email %][% Param("emailsuffix") %] +To: [% email %] Subject: Your [% terms.Bugzilla %] [%+ terms.bug %] list needs attention. X-Bugzilla-Type: whine diff --git a/template/en/default/global/messages.html.tmpl b/template/en/default/global/messages.html.tmpl index 77b13859c..9f6464601 100644 --- a/template/en/default/global/messages.html.tmpl +++ b/template/en/default/global/messages.html.tmpl @@ -22,7 +22,7 @@ [% ELSIF message_tag == "account_creation_canceled" %] [% title = "User Account Creation Canceled" %] - The creation of the user account [% account FILTER html %] has been + The creation of the user account '[% login FILTER html %]' has been canceled. [% ELSIF message_tag == "account_updated" %] @@ -35,6 +35,8 @@ <li> [% IF field == 'login_name' %] The login is now [% otheruser.login FILTER html %]. + [% ELSIF field == 'email' %] + The email is now [% otheruser.email FILTER html %]. [% ELSIF field == 'realname' %] The real name has been updated. [% ELSIF field == 'cryptpassword' %] @@ -89,8 +91,8 @@ successfully. [% ELSIF message_tag == "account_disabled" %] - Logging in with this user account [% account FILTER html %] is disabled, so - you cannot change its password. + Logging in with the user account with email address [% email FILTER html %] + is disabled, so you cannot change its password. [% ELSIF message_tag == "attachment_creation_failed" %] The [% terms.bug %] was created successfully, but attachment creation @@ -496,7 +498,7 @@ [%+ terms.Bugzilla %]... [% ELSIF message_tag == "migrate_user_created" %] - User created: [% created.email FILTER html %] + User created: [% created.login FILTER html %] [% IF password %] Password: [% password FILTER html %][% END %] [% ELSIF message_tag == "migrate_value_created" %] @@ -506,6 +508,10 @@ [%+ field_descs.${field.name} FILTER html %] value created: [% value FILTER html %] + [% ELSIF message_tag == "email_changed" %] + [% title = "Email Changed" %] + Your email address has been updated. + [% ELSIF message_tag == "milestone_created" %] [% title = "Milestone Created" %] The milestone <em>[% milestone.name FILTER html %]</em> has been created. @@ -561,7 +567,7 @@ [% ELSIF message_tag == "password_change_request" %] [% title = "Request to Change Password" %] A token for changing your password has been emailed to - <em>[% login_name FILTER html %]</em>. + <em>[% email FILTER html %]</em>. Follow the instructions in that email to change your password. [% ELSIF message_tag == "password_changed" %] @@ -639,6 +645,9 @@ [% title = BLOCK %]Flag Type '[% flag_type.name FILTER html %]' Deactivated[% END %] The flag type <em>[% flag_type.name FILTER html %]</em> has been deactivated. + [% ELSIF message_tag == "install_admin_get_login" %] + Enter the login name the administrator will log in with: + [% ELSIF message_tag == "install_admin_get_email" %] Enter the e-mail address of the administrator: diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index bd8c54f2d..bdb90a2a2 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -55,10 +55,12 @@ [% ELSIF error == "account_exists" %] [% title = "Account Already Exists" %] There is already an account with - [% IF email %] - the login name [% email FILTER html %]. + [% IF login %] + the login name [% login FILTER html %]. + [% ELSIF email %] + the email address [% email FILTER html %]. [% ELSE %] - that login name. + that login name or email address. [% END %] [% ELSIF error == "account_locked" %] @@ -523,9 +525,18 @@ [% title = "Email Change Already In Progress" %] Email change already in progress; please check your email. + [% ELSIF error == "email_needed_for_password_change" %] + [% title = "Email Address Required" %] + You must enter an email address when requesting to change your password. + [% ELSIF error == "email_no_body" %] Your message did not contain any text, as far as we could tell. + [% ELSIF error == "email_required" %] + [% title = "Email Address Required" %] + [% admindocslinks = {'useradmin.html' => 'User administration'} %] + You must enter an email address for the new user. + [% ELSIF error == "empty_group_description" %] [% title = "The group description cannot be empty" %] You must enter a description for the group. @@ -969,10 +980,10 @@ [% IF format %] Please use the format '<code>[% format FILTER html %]</code>'. [% END %] - + [% ELSIF error == "illegal_email_address" %] [% title = "Invalid Email Address" %] - The e-mail address you entered (<b>[% addr FILTER html %]</b>) + The e-mail address you entered (<b>[% email FILTER html %]</b>) didn't pass our syntax checking for a legal email address. [% IF default %] A legal address must contain exactly one '@', @@ -1076,6 +1087,12 @@ [% title = "Invalid Dimensions" %] The width or height specified is not a positive integer. + [% ELSIF error == "invalid_email" %] + [% title = "Invalid Email Address" %] + The address <tt>[% email FILTER html %]</tt> is not a valid email address. + Either you misspelled it, or the person has not + registered for a [% terms.Bugzilla %] account. + [% ELSIF error == "invalid_format" %] [% title = "Invalid Format" %] The format "[% format FILTER html %]" is invalid (must be one of @@ -1208,18 +1225,28 @@ [% ELSIF error == "keyword_blank_description" %] [% title = "Blank Keyword Description Not Allowed" %] You must enter a non-blank description for the keyword. - + [% ELSIF error == "keyword_blank_name" %] [% title = "Blank Keyword Name Not Allowed" %] You must enter a non-blank name for the keyword. - + [% ELSIF error == "keyword_invalid_name" %] [% title = "Invalid Keyword Name" %] You may not use commas or whitespace in a keyword name. - - [% ELSIF error == "login_needed_for_password_change" %] - [% title = "Login Name Required" %] - You must enter a login name when requesting to change your password. + + [% ELSIF error == "login_at_sign_disallowed" %] + [% title = "Illegal Character in Login" %] + Login names may not contain the "@" sign unless you are setting your + login name to be identical to your email address. + + [% ELSIF error == "login_change_during_email_change" %] + [% title = "Login Change Not Permitted" %] + You may not change your login name while you are in the middle of the + process of changing your email address. + + [% ELSIF error == "login_illegal_character" %] + [% title = "Illegal Character In Login" %] + Login names are not allowed to contain space characters. [% ELSIF error == "login_required_for_pronoun" %] [% title = "Login Name Required" %] @@ -1230,6 +1257,10 @@ [%# Used for non-web-based LOGIN_REQUIRED situations. %] You must log in before using this part of [% terms.Bugzilla %]. + [% ELSIF error == "login_too_long" %] + [% title = "Login Too Long" %] + Your login name is too long. It cannot exceed 127 characters. + [% ELSIF error == "markdown_disabled" %] Markdown feature is not enabled. @@ -1495,8 +1526,8 @@ You did not enter your current password correctly. [% ELSIF error == "current_password_required" %] - [% title = "Old Password Required" %] - You must enter your old password to change your email address. + [% title = "Current Password Required" %] + You must enter your current password to edit your account. [% ELSIF error == "password_change_requests_not_allowed" %] [% title = "Password Change Requests Not Allowed" %] @@ -1511,7 +1542,7 @@ The password must be at least [%+ constants.USER_PASSWORD_MIN_LENGTH FILTER html %] characters long. [% IF locked_user %] - You must <a href="token.cgi?a=reqpw&loginname=[% locked_user.email FILTER uri %]&token=[% issue_hash_token(['reqpw']) FILTER uri %]"> + You must <a href="token.cgi?a=reqpw&email=[% locked_user.email FILTER uri %]&token=[% issue_hash_token(['reqpw']) FILTER uri %]"> request a new password</a> in order to log in again. [% END %] @@ -1532,7 +1563,7 @@ [% END %] </ul> [% IF locked_user %] - You must <a href="token.cgi?a=reqpw&loginname=[% locked_user.email FILTER uri %]&token=[% issue_hash_token(['reqpw']) FILTER uri %]"> + You must <a href="token.cgi?a=reqpw&email=[% locked_user.email FILTER uri %]&token=[% issue_hash_token(['reqpw']) FILTER uri %]"> request a new password</a> in order to log in again. [% END %] diff --git a/template/en/default/global/user.html.tmpl b/template/en/default/global/user.html.tmpl index 7050c6d37..0ee303fe9 100644 --- a/template/en/default/global/user.html.tmpl +++ b/template/en/default/global/user.html.tmpl @@ -12,15 +12,19 @@ <span class="vcard"> [% FILTER collapse %] - [% IF user.id %] - <a class="email" href="mailto:[% who.email FILTER html %]" - title="[% who.identity FILTER html %]"> - [%- END -%] - [% IF who.name %] - <span class="fn">[% who.name FILTER html %]</span> + [% IF Param("use_email_as_login") %] + [% IF user.id %] + <a class="email" href="mailto:[% who.email FILTER html %]" + title="[% who.identity FILTER html %]"> + [%- END -%] + [% IF who.name %] + <span class="fn">[% who.name FILTER html %]</span> + [% ELSE %] + [% who.login FILTER email FILTER html %] + [% END %] + [% '</a>' IF user.id %] [% ELSE %] - [% who.login FILTER email FILTER html %] + [% who.identity FILTER html %] [% END %] - [% '</a>' IF user.id %] [% END %] </span> diff --git a/template/en/default/reports/delete-series.html.tmpl b/template/en/default/reports/delete-series.html.tmpl index d19178ce3..bb440c2b4 100644 --- a/template/en/default/reports/delete-series.html.tmpl +++ b/template/en/default/reports/delete-series.html.tmpl @@ -21,8 +21,7 @@ </p> <p> [% IF series.creator %] - This series has been created by <a href="mailto:[% series.creator.email FILTER html %]"> - [% series.creator.email FILTER html %]</a> + This series has been created by [% series.creator.login FILTER html %] [% ELSE %] This series has been automatically created by Bugzilla [% END %] diff --git a/template/en/default/reports/edit-series.html.tmpl b/template/en/default/reports/edit-series.html.tmpl index ad6bb32f4..8cf1616c0 100644 --- a/template/en/default/reports/edit-series.html.tmpl +++ b/template/en/default/reports/edit-series.html.tmpl @@ -33,8 +33,7 @@ <p> <b>Creator</b>: [% IF default.creator %] - <a href="mailto:[% default.creator.email FILTER html %]"> - [% default.creator.email FILTER html %]</a> + [% default.creator.login FILTER html %] [% ELSE %] (automatically created by Bugzilla) [% END %] @@ -122,22 +122,22 @@ sub requestChangePassword { my $token = $cgi->param('token'); check_hash_token($token, ['reqpw']); - my $login_name = $cgi->param('loginname') - or ThrowUserError("login_needed_for_password_change"); + my $email = $cgi->param('email') + or ThrowUserError("email_needed_for_password_change"); - check_email_syntax($login_name); - my $user = new Bugzilla::User({ name => $login_name }); + my $userid = email_to_id($email, 'throw_error_if_not_exist'); + my $user = new Bugzilla::User($userid); # Make sure the user account is active. - if ($user && !$user->is_enabled) { + if (!$user->is_enabled) { ThrowUserError('account_disabled', - {disabled_reason => get_text('account_disabled', {account => $login_name})}); + {disabled_reason => get_text('account_disabled', {email => $email})}); } - Bugzilla::Token::IssuePasswordToken($user) if $user; + Bugzilla::Token::IssuePasswordToken($user); $vars->{'message'} = "password_change_request"; - $vars->{'login_name'} = $login_name; + $vars->{'email'} = $email; print $cgi->header(); $template->process("global/message.html.tmpl", $vars) @@ -208,7 +208,7 @@ sub changeEmail { my ($old_email, $new_email) = split(/:/,$eventdata); $dbh->bz_start_transaction(); - + my $user = Bugzilla::User->check({ id => $userid }); my $cgipassword = $cgi->param('password'); @@ -218,14 +218,14 @@ sub changeEmail { # 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)) { + if (!is_available_email($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); + # Update the user's email address in the profiles table. + $user->set_email($new_email); $user->update({ keep_session => 1, keep_tokens => 1 }); delete_token($token); $dbh->do(q{DELETE FROM tokens WHERE userid = ? @@ -256,8 +256,8 @@ sub cancelChangeEmail { my $user = Bugzilla::User->check({ id => $userid }); # check to see if it has been altered - if ($user->login ne $old_email) { - $user->set_login($old_email); + if ($user->email ne $old_email) { + $user->set_email($old_email); $user->update({ keep_tokens => 1 }); $vars->{'message'} = "email_change_canceled_reinstated"; @@ -285,13 +285,20 @@ sub cancelChangeEmail { } sub request_create_account { - my ($date, $login_name, $token) = @_; + my ($date, $data, $token) = @_; Bugzilla->user->check_account_creation_enabled; - $vars->{'token'} = $token; - $vars->{'email'} = $login_name . Bugzilla->params->{'emailsuffix'}; - $vars->{'expiration_ts'} = ctime(str2time($date) + MAX_TOKEN_AGE * 86400); + # Be careful! Some logins may contain ":" in them. + my ($email, $login) = split(':', $data, 2); + $vars = { + token => $token, + login => $login, + email => $email, + # Make sure nobody else chose this login meanwhile. + login_already_in_use => login_to_id($login) ? 1 : 0, + expiration_ts => ctime(str2time($date) + MAX_TOKEN_AGE * 86400) + }; print $cgi->header(); $template->process('account/email/confirm-new.html.tmpl', $vars) @@ -299,7 +306,7 @@ sub request_create_account { } sub confirm_create_account { - my ($login_name, $token) = @_; + my ($data, $token) = @_; Bugzilla->user->check_account_creation_enabled; @@ -308,8 +315,13 @@ sub confirm_create_account { # Make sure that these never show up anywhere in the UI. $cgi->delete('passwd1', 'passwd2'); + # Be careful! Some logins may contain ":" in them. + my ($email, $login) = split(':', $data, 2); + $login = $cgi->param('login') if login_to_id($login); + my $otheruser = Bugzilla::User->create({ - login_name => $login_name, + login_name => $login, + email => $email, realname => scalar $cgi->param('realname'), cryptpassword => $password}); @@ -332,10 +344,11 @@ sub confirm_create_account { } sub cancel_create_account { - my ($login_name, $token) = @_; + my ($data, $token) = @_; $vars->{'message'} = 'account_creation_canceled'; - $vars->{'account'} = $login_name; + # Be careful! Some logins may contain ":" in them. + ($vars->{'email'}, $vars->{'login'}) = split(':', $data, 2); Bugzilla::Token::Cancel($token, $vars->{'message'}); print $cgi->header(); diff --git a/userprefs.cgi b/userprefs.cgi index c94c63f5f..54cad3c51 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -37,8 +37,6 @@ sub DoAccount { my $dbh = Bugzilla->dbh; my $user = Bugzilla->user; - $vars->{'realname'} = $user->name; - if (Bugzilla->params->{'allowemailchange'} && $user->authorizer->can_change_email) { @@ -55,11 +53,11 @@ sub DoAccount { ORDER BY tokentype ASC " . $dbh->sql_limit(1), undef, $user->id); if (scalar(@token) > 0) { my ($tokentype, $change_date, $eventdata) = @token; - $vars->{'login_change_date'} = $change_date; + $vars->{'email_change_date'} = $change_date; if($tokentype eq 'emailnew') { my ($oldemail,$newemail) = split(/:/,$eventdata); - $vars->{'new_login_name'} = $newemail; + $vars->{'new_email'} = $newemail; } } } @@ -77,7 +75,8 @@ sub SaveAccount { my $verified_password; my $pwd1 = $cgi->param('new_password1'); my $pwd2 = $cgi->param('new_password2'); - my $new_login_name = trim(scalar $cgi->param('new_login_name')); + my $new_login = clean_text(scalar $cgi->param('new_login')); + my $new_email = clean_text(scalar $cgi->param('new_email')); if ($user->authorizer->can_change_password && ($pwd1 ne "" || $pwd2 ne "")) @@ -95,28 +94,47 @@ sub SaveAccount { } } + # This is used only if email and login are separate + if ($user->authorizer->can_change_login + && !Bugzilla->params->{"use_email_as_login"} + && $new_login + && $user->login ne $new_login) + { + $verified_password || $user->check_current_password($oldpassword); + + if ($new_login =~ /@/ && + $new_login ne $user->email) + { + ThrowUserError("login_at_sign_disallowed"); + } + + if (Bugzilla::Token::HasEmailChangeToken($user->id)) { + ThrowUserError("login_change_during_email_change"); + } + + $user->set_login($new_login); + } + + # This is used for the single value if use_email_as_login is true, or for + # the email address otherwise. if ($user->authorizer->can_change_email && Bugzilla->params->{"allowemailchange"} - && $new_login_name) + && $new_email + && $user->email ne $new_email) { - if ($user->login ne $new_login_name) { - $verified_password || $user->check_current_password($oldpassword); + $verified_password || $user->check_current_password($oldpassword); - # Block multiple email changes for the same user. - if (Bugzilla::Token::HasEmailChangeToken($user->id)) { - ThrowUserError("email_change_in_progress"); - } + # Block multiple email changes for the same user. + if (Bugzilla::Token::HasEmailChangeToken($user->id)) { + ThrowUserError("email_change_in_progress"); + } - # Before changing an email address, confirm one does not exist. - check_email_syntax($new_login_name); - is_available_username($new_login_name) - || ThrowUserError("account_exists", {email => $new_login_name}); + # Before changing to an email address, confirm it does not exist. + $user->check_email($new_email); - $vars->{'email_token'} = Bugzilla::Token::IssueEmailChangeToken($new_login_name); - $vars->{'email_changes_saved'} = 1; - } + $vars->{'email_token'} = Bugzilla::Token::IssueEmailChangeToken($new_email); + $vars->{'email_changes_saved'} = 1; } - $user->set_name(scalar $cgi->param('realname')); $user->update({ keep_session => 1, keep_tokens => 1 }); $dbh->bz_commit_transaction; diff --git a/whineatnews.pl b/whineatnews.pl index 660cc617f..2113bb819 100755 --- a/whineatnews.pl +++ b/whineatnews.pl @@ -47,28 +47,28 @@ my $slt_bugs = $dbh->selectall_arrayref($query, undef, 'CONFIRMED', 'NEW', 'REOPENED'); foreach my $bug (@$slt_bugs) { - my ($id, $desc, $email) = @$bug; - if (!defined $bugs{$email}) { - $bugs{$email} = []; + my ($id, $desc, $login) = @$bug; + if (!defined $bugs{$login}) { + $bugs{$login} = []; } - if (!defined $desc{$email}) { - $desc{$email} = []; + if (!defined $desc{$login}) { + $desc{$login} = []; } - push @{$bugs{$email}}, $id; - push @{$desc{$email}}, $desc; + push @{$bugs{$login}}, $id; + push @{$desc{$login}}, $desc; } -foreach my $email (sort (keys %bugs)) { - my $user = new Bugzilla::User({name => $email}); +foreach my $login (sort (keys %bugs)) { + my $user = new Bugzilla::User({name => $login}); next if $user->email_disabled; - my $vars = {'email' => $email}; + my $vars = {'email' => $user->email}; my @bugs = (); - foreach my $i (@{$bugs{$email}}) { + foreach my $i (@{$bugs{$login}}) { my $bug = {}; - $bug->{'summary'} = shift(@{$desc{$email}}); + $bug->{'summary'} = shift(@{$desc{$login}}); $bug->{'id'} = $i; push @bugs, $bug; } @@ -81,5 +81,5 @@ foreach my $email (sort (keys %bugs)) { MessageToMTA($msg); - say "$email " . join(" ", @{$bugs{$email}}); + say $user->email . " " . join(" ", @{$bugs{$login}}); } diff --git a/xt/selenium/password_complexity.t b/xt/selenium/password_complexity.t index 0e75e6b9d..fe6159b2c 100644 --- a/xt/selenium/password_complexity.t +++ b/xt/selenium/password_complexity.t @@ -20,8 +20,7 @@ my ($sel, $config) = get_selenium(); log_in($sel, $config, 'admin'); set_parameters($sel, {"Administrative Policies" => {"allowuserdeletion-on" => undef}, - "User Authentication" => {"createemailregexp" => {type => "text", value => '.*'}, - "emailsuffix" => {type => "text", value => ''}} }); + "User Authentication" => {"createemailregexp" => {type => "text", value => '.*'}} }); # Set the password complexity to MIXED LETTERS. # Password must contain at least one UPPER and one lowercase letter. |