Skip to content

[Bug 1963773][Harmony] login-names separate from email addresses #147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 111 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
5b19a7d
Adding test change
topunix Mar 15, 2025
9126b93
Adding new auth, and new_login to Account page
topunix Apr 2, 2025
e9cdb7a
Added new user errors and save new_login to userprefs.cgi
topunix Apr 2, 2025
b4e10d8
Added error check, login_change_during_email_change
topunix Apr 2, 2025
43a5566
updated small login field
topunix Apr 4, 2025
880d9ae
changed loginname to login in placeholder
topunix Apr 4, 2025
3ceeed9
Updated login input
topunix Apr 4, 2025
1ba09fa
Added login_too_long error
topunix Apr 4, 2025
9a54d04
Improved error checking in check_login_name_for_creation
topunix Apr 4, 2025
4205bd7
removed email change logic
topunix Apr 4, 2025
6a0758f
Change current_tab to email
topunix Apr 10, 2025
f638667
Change current_tab to email
topunix Apr 10, 2025
a3add95
Adding hook file for email tab
topunix Apr 10, 2025
c785e57
Adding hook file for email tab
topunix Apr 10, 2025
de4807e
Removing account-field
topunix Apr 13, 2025
24f7283
Removing account-field
topunix Apr 13, 2025
5e52941
Rename email-field file to account-field, reversing change
topunix Apr 14, 2025
67674b8
Removing email-field.html.tmpl, reversing change
topunix Apr 14, 2025
a4be2f2
Rename email-field file to account-field, reversing change
topunix Apr 14, 2025
574d95b
Removing email-field.html.tmpl, reversing change
topunix Apr 14, 2025
d92a37a
Reversed tab check to account
topunix Apr 14, 2025
bb4399f
Reversed tab check to account
topunix Apr 14, 2025
edf8b6d
Returning email address field
topunix Apr 14, 2025
6b1e064
Revert changes to userprefs.cgi from 4205bd7d00c61ef7a9bbf7aec0a8a198…
topunix Apr 14, 2025
0ccb69d
Adding secondary email placeholder
topunix Apr 16, 2025
03a91d6
Adding profiles_emails table
topunix Apr 16, 2025
8676091
Change column name
topunix Apr 18, 2025
d3fded1
Added new column
topunix Apr 20, 2025
e49fe87
Adding more test fields for emails
topunix Apr 20, 2025
40df3f2
Added login name to answers
topunix Apr 21, 2025
3bc163b
Adding Bugzilla::User::Email
topunix Apr 23, 2025
26c7dda
Adding creation of email account to Bugzilla::User::create()
topunix Apr 23, 2025
3256359
Removed unneeded columns
topunix Apr 25, 2025
ca84658
Added validators and columns
topunix Apr 28, 2025
0b8dadd
Adding email widget
topunix Apr 28, 2025
ba60296
Enhanced email widget
topunix Apr 28, 2025
24c1366
Added mutators, accessors, update, and check_user_id validator
topunix Apr 30, 2025
f54b3cf
Added check email validator
topunix Apr 30, 2025
884c21f
Added remove_from_db
topunix Apr 30, 2025
503010f
Updated button styling
topunix Apr 30, 2025
3d53c25
Adding display_order column
topunix May 4, 2025
35ddd05
Added get_user_emails subroutine
topunix May 4, 2025
d54ed69
added display_order to update_columns
topunix May 4, 2025
bb53674
Change to remove_from_db
topunix May 4, 2025
393eb9d
Added reIndexEmailFields() and inline comments
topunix May 4, 2025
0bd16d3
Updated input attributes for primary email
topunix May 5, 2025
50b6e71
Added display_order constants
topunix May 5, 2025
2a3a8a3
Fixed check_email validator
topunix May 5, 2025
0a34409
Added find_user_by_email()
topunix May 5, 2025
177fc27
Renamed subroutine
topunix May 5, 2025
e4ca9a4
Renamed subroutine for parity
topunix May 5, 2025
8f4e0ab
Converted to methods
topunix May 6, 2025
ec0ffab
Added new(), validator, and name_field
topunix May 7, 2025
dcc01f6
Added install_admin_get_login_name
topunix May 9, 2025
c6ff94d
Added email_exists
topunix May 9, 2025
f4847f8
Improved validator
topunix May 9, 2025
567860a
Added Bugzilla::Error
topunix May 9, 2025
5cd3cb6
Added email to admin install process
topunix May 9, 2025
adf2201
Fixed bug
topunix May 11, 2025
58bde1b
Fixed comment
topunix May 11, 2025
4b0e7dc
removed comment
topunix May 11, 2025
0d7d7fb
Added get_primary_email_of_user()
topunix May 11, 2025
7c60d17
Updated email accessor to use Bugzilla::User::Email
topunix May 11, 2025
5e2c5f7
Replaced email with login_name
topunix May 11, 2025
13ace61
Revert account.html.tmpl to version from 07ece2b
topunix May 12, 2025
c8c2e96
Added login_name and new_email fields
topunix May 12, 2025
1f32d29
Added new_login_name and new_email
topunix May 12, 2025
01400e4
Added email_changed
topunix May 12, 2025
528f4c1
Updated email_exists
topunix May 12, 2025
410cecc
Updated account_exists
topunix May 12, 2025
d6a216a
Replaced key name in user error
topunix May 12, 2025
6202e37
Updated changeEmail() and confirm_create_account()
topunix May 12, 2025
91f7d0d
Added email creation
topunix May 12, 2025
ca3054c
Replaced username with login, added email to create_or_update_user()
topunix May 12, 2025
27a5c4a
Added set_email()
topunix May 12, 2025
816bdf7
Added email to user create
topunix May 13, 2025
dbf8e7d
Added login_name fallback to set_email
topunix May 13, 2025
4e82f4c
Expand token data unpacking
topunix May 13, 2025
dd7e441
Added email param to issue_new_user_account_token()
topunix May 13, 2025
e452a75
Added email param
topunix May 13, 2025
9b01505
Added email var
topunix May 13, 2025
c705a05
Added email string
topunix May 13, 2025
0f705a9
Updated template to use email in form
topunix May 13, 2025
4e5d7e9
Added email param to check_and_send_account_creation_confirmation()
topunix May 13, 2025
c400a97
Added email to user error
topunix May 13, 2025
e93b725
Replace emailaddress with login for User
topunix May 14, 2025
320ae8a
Added userid check for emailaddress and login vars
topunix May 14, 2025
1c8af66
Fallback to user login if primary email is undefined
topunix May 15, 2025
565af33
Update password reset error message to require email address
topunix May 16, 2025
ac3abe2
Added ACCOUNT_CHANGE_INTERVAL constant
topunix May 16, 2025
13eca3d
Fix: Update password change error message and condition
topunix May 16, 2025
f4f2070
Fixed error name
topunix May 16, 2025
f951015
Refactor: Standardize on email variable for password requests and upd…
topunix May 16, 2025
077be0c
Removed SEND_NOW
topunix May 16, 2025
da852f6
Refactor: Implement user lookup when 'name' parameter is present
topunix May 16, 2025
6262b97
Added invalid_email error
topunix May 16, 2025
7d6638b
Refactor: Simplify user instantiation and rely on constructor for val…
topunix May 16, 2025
b4e4a2e
Added email syntax check to password reset
topunix May 16, 2025
01f6477
Removed email syntax check in constructor to allow non-email logins
topunix May 16, 2025
1e69387
Added email address to search options
topunix May 16, 2025
02779c7
Added email column
topunix May 17, 2025
bae60f8
feat: Add email to user profile query
topunix May 17, 2025
9d3824b
Added email to user account
topunix May 20, 2025
43e65a4
Prevented error with unrecognized column (email)
topunix May 20, 2025
8621cfa
Replace user with otheruser
topunix May 20, 2025
0363a1f
Docs: Update login name and email field help text
topunix May 21, 2025
a6ee967
Added _copy_valid_emails_to_profiles_emails()
topunix May 22, 2025
93457d1
Fix: Handle undefined email address in validation
topunix May 22, 2025
87be5dd
Fix: Improve email population and validation in DB install
topunix May 22, 2025
c2852ab
Refactor: Handle optional 'email' column in profiles table during DB …
topunix May 22, 2025
e33ec45
Fix: Log 'unknown' if remote IP is not available during user creation…
topunix May 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Bugzilla/Auth.pm
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,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;
}
Expand Down Expand Up @@ -474,6 +478,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
Expand Down
44 changes: 23 additions & 21 deletions Bugzilla/Auth/Verify.pm
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,16 @@ 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 = $params->{email};
my $password = $params->{password} || '*';
my $real_name = $params->{realname} || '';
my $user_id = $params->{user_id};

# 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) {
$extern_user_id = $dbh->selectrow_array(
Expand All @@ -55,51 +56,52 @@ sub create_or_update_user {

# If we have both a valid extern_id and a valid username, and they are
# not the same id, then we have a conflict.
if ( $username_user_id
if ( $login_user_id
&& $extern_user_id
&& $username_user_id ne $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}
{extern_id => $extern_id, extern_user => $extern_name, username => $login}
};
}

# If we have a valid username, 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 {
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 => $login}
};

# external authentication
# systems might follow different standards than ours. So in this

# 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, cryptpassword => $password, realname => $real_name
});
$username_user_id = $user->id;
my $user = Bugzilla::User->create({
login_name => $login,
email => $email,
cryptpassword => $password,
realname => $real_name});
$login_user_id = $user->id;
}

# If we have a valid username 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
Expand All @@ -117,13 +119,13 @@ sub create_or_update_user {
# Now that we have a valid User, we need to see if any data has to be
# updated.
my $user_updated = 0;
if ($username && lc($user->login) ne lc($username)) {
validate_email_syntax($username) || return {
if ($email && lc($user->email) ne lc($email)) {
validate_email_syntax($email) || return {
failure => AUTH_ERROR,
error => 'auth_invalid_email',
details => {addr => $username}
details => {addr => $email}
};
$user->set_login($username);
$user->set_email($email);
$user_updated = 1;
}
if ($real_name && $user->name ne $real_name) {
Expand Down
2 changes: 1 addition & 1 deletion Bugzilla/Config/Common.pm
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ sub check_regexp {
sub check_email {
my ($value) = @_;
my ($address) = Email::Address::XS->parse($value);
if (!$address->is_valid) {
if (defined $address && !$address->is_valid) {
return "must be a valid email address.";
}
return "";
Expand Down
5 changes: 5 additions & 0 deletions Bugzilla/Constants.pm
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ use Memoize;
MAX_SUDO_TOKEN_AGE
MAX_LOGIN_ATTEMPTS
LOGIN_LOCKOUT_INTERVAL
ACCOUNT_CHANGE_INTERVAL
MAX_STS_AGE

SAFE_PROTOCOLS
Expand Down Expand Up @@ -473,6 +474,10 @@ use constant MAX_LOGIN_ATTEMPTS => 5;
# account is locked.
use constant LOGIN_LOCKOUT_INTERVAL => 30;

# The time in minutes a user must wait before they can request another email to
# create a new account or change their password.
use constant ACCOUNT_CHANGE_INTERVAL => 10;

# The maximum number of seconds the Strict-Transport-Security header
# will remain valid. BMO uses one year.
use constant MAX_STS_AGE => 31536000;
Expand Down
18 changes: 18 additions & 0 deletions Bugzilla/DB/Schema.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,24 @@ use constant ABSTRACT_SCHEMA => {
],
},

profiles_emails => {
FIELDS => [
id => {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
user_id => {
TYPE => 'INT3',
NOTNULL => 1,
REFERENCES => {TABLE => 'profiles', COLUMN => 'userid', DELETE => 'CASCADE'}
},
email => {TYPE => 'varchar(255)', NOTNULL => 1},
is_primary_email => {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'},
display_order => {TYPE => 'INT1', NOTNULL => 1, DEFAULT => 1},
],
INDEXES => [
profiles_emails_userid_idx => ['user_id'],
profiles_emails_email_idx => {FIELDS => ['email'], TYPE => 'UNIQUE'},
],
},

profile_mfa => {
FIELDS => [
id => {TYPE => 'INTSERIAL', NOTNULL => 1, PRIMARYKEY => 1},
Expand Down
23 changes: 19 additions & 4 deletions Bugzilla/Install.pm
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ use Bugzilla::Error;
use Bugzilla::Group;
use Bugzilla::Product;
use Bugzilla::User;
use Bugzilla::User::Email;
use Bugzilla::User::Setting;
use Bugzilla::Util qw(get_text);
use Bugzilla::Version;


use constant STATUS_WORKFLOW => (
[undef, 'UNCONFIRMED'],
[undef, 'CONFIRMED'],
Expand Down Expand Up @@ -420,16 +422,17 @@ sub create_admin {
return if $admin_count;

my %answer = %{Bugzilla->installation_answers};
my $login = $answer{'ADMIN_EMAIL'};
my $login = $answer{'ADMIN_LOGIN_NAME'};
my $email = $answer{'ADMIN_EMAIL'};
my $password = $answer{'ADMIN_PASSWORD'};
my $full_name = $answer{'ADMIN_REALNAME'};

if (!$login || !$password || !$full_name) {
if (!$login || !$email || !$password || !$full_name) {
print "\n" . get_text('install_admin_setup') . "\n\n";
}

while (!$login) {
print get_text('install_admin_get_email') . ' ';
print get_text('install_admin_get_login_name') . ' ';
$login = <STDIN>;
chomp $login;
eval { Bugzilla::User->check_login_name_for_creation($login); };
Expand All @@ -439,6 +442,17 @@ sub create_admin {
}
}

while (!$email) {
print get_text('install_admin_get_email') . ' ';
$email = <STDIN>;
chomp $email;
eval { Bugzilla::User::Email->check_email_for_creation($email); };
if ($@) {
print $@ . "\n";
undef $email;
}
}

while (!defined $full_name) {
print get_text('install_admin_get_name') . ' ';
$full_name = <STDIN>;
Expand All @@ -451,8 +465,9 @@ sub create_admin {

my $admin
= Bugzilla::User->create({
login_name => $login, realname => $full_name, cryptpassword => $password
login_name => $login, email => $email, realname => $full_name, cryptpassword => $password
});

make_admin($admin);
}

Expand Down
61 changes: 60 additions & 1 deletion Bugzilla/Install/DB.pm
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,8 @@ sub update_table_definitions {
$dbh->bz_alter_column('user_request_log', 'attach_id', {TYPE => 'INT5', NOTNULL => 0});
_populate_attachment_storage_class();


# Bug 1963773 - [email protected]
_copy_valid_emails_to_profiles_emails();
################################################################
# New --TABLE-- changes should go *** A B O V E *** this point #
################################################################
Expand Down Expand Up @@ -4393,6 +4394,64 @@ sub _populate_attachment_storage_class {
}
}

sub _copy_valid_emails_to_profiles_emails {
my $dbh = Bugzilla->dbh;

my ($total) = $dbh->selectrow_array("SELECT COUNT(*) FROM profiles");
unless ($total) {
print "Skipping profiles_emails population: no profiles to process.\n";
return;
}

# Check if 'email' column exists in 'profiles'
my $columns = $dbh->selectcol_arrayref(
"SHOW COLUMNS FROM profiles LIKE 'email'"
);
my $has_email_column = scalar(@$columns) > 0;

# Build SELECT statement dynamically
my $select_sql = 'SELECT userid, login_name';
$select_sql .= ', email' if $has_email_column;
$select_sql .= ' FROM profiles';

my $select_sth = $dbh->prepare($select_sql);
my $check_sth = $dbh->prepare('SELECT 1 FROM profiles_emails WHERE user_id = ?');
my $insert_sth = $dbh->prepare('
INSERT INTO profiles_emails (user_id, email, is_primary_email, display_order)
VALUES (?, ?, 1, 1)
');

$select_sth->execute();

while (my $row = $select_sth->fetchrow_hashref) {
my $user_id = $row->{userid};

# Skip if the user already has an entry
$check_sth->execute($user_id);
next if $check_sth->fetchrow_array;

my $email = $has_email_column ? $row->{email} : undef;
my $login = $row->{login_name};

my $valid_email;
if (defined $email && $email ne '' && validate_email_syntax($email)) {
$valid_email = $email;
} elsif (defined $login && $login ne '' && validate_email_syntax($login)) {
$valid_email = $login;
}

next unless defined $valid_email;

eval {
$insert_sth->execute($user_id, $valid_email);
};
warn "Failed to insert email for user $user_id: $@" if $@;
}

$select_sth->finish;
$check_sth->finish;
$insert_sth->finish;
}

1;

Expand Down
Loading