Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 14 additions & 2 deletions dpc-portal/app/controllers/login_dot_gov_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def maybe_update_user(user, data)
user&.update(given_name: data.given_name, family_name: data.family_name)
end

def update_email(csp_user, new_emails)
def update_email(csp_user, new_emails, primary_email)
return unless csp_user

existing_emails = csp_user.user_emails
Expand All @@ -87,6 +87,10 @@ def update_email(csp_user, new_emails)
ActiveRecord::Base.transaction do
add_or_activate_new_email(csp_user, new_emails, existing_emails)
deactivate_old_email(new_emails, existing_emails)

# Set their primary email, which should now exist in the user_emails table
primary_email = csp_user.user_emails.find_by(email: primary_email)
primary_email.update(primary: true) if primary_email && !primary_email.primary?
end
end

Expand Down Expand Up @@ -154,7 +158,15 @@ def csp

def post_signin_actions(user, csp_user, auth)
ial_2_actions(user, auth)
update_email(csp_user, auth.extra.raw_info.all_emails)
update_email(csp_user, all_emails(auth), primary_email(auth))
end

def primary_email(auth)
auth.info.email
end

def all_emails(auth)
auth.extra.raw_info.all_emails
end
end
# rubocop:enable Metrics/ClassLength
13 changes: 13 additions & 0 deletions dpc-portal/app/models/user_email.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,17 @@
class UserEmail < ApplicationRecord
belongs_to :csp_user
has_one :user, through: :csp_users

# If we update this email to primary, make sure the others aren't.
before_save :ensure_only_one_primary, if: -> { primary? && primary_changed? }

private

# Sets all of the user's other emails to not primary.
def ensure_only_one_primary
UserEmail.where(csp_user_id: csp_user_id)
.where(primary: true)
.where.not(id: id)
.update_all(primary: false)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class AddPrimaryToUserEmails < ActiveRecord::Migration[8.0]
def change
add_column :user_emails, :primary, :boolean, default: false, null: false

# Make sure each csp_user can only have one primary email.
add_index :user_emails, :csp_user_id,
unique: true,
where: '"primary" = true',
name: 'index_unique_primary_email_per_csp_user'
end
end
4 changes: 3 additions & 1 deletion dpc-portal/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2026_04_24_194005) do
ActiveRecord::Schema[8.0].define(version: 2026_05_27_181236) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"

Expand Down Expand Up @@ -252,7 +252,9 @@
t.datetime "reactivated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "primary", default: false, null: false
t.index ["csp_user_id", "email"], name: "index_user_emails_on_csp_user_id_and_email", unique: true
t.index ["csp_user_id"], name: "index_unique_primary_email_per_csp_user", unique: true, where: "(\"primary\" = true)"
t.index ["csp_user_id"], name: "index_user_emails_on_csp_user_id"
end

Expand Down
31 changes: 31 additions & 0 deletions dpc-portal/spec/models/user_email_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,35 @@
user_email = create(:user_email, csp_user:)
expect(user_email.csp_user).to eq csp_user
end

it 'ensures that creating a new primary email unsets others' do
user = create(:user)
csp = create(:csp)
csp_user = create(:csp_user, user:, csp:)

email_old_primary = create(:user_email, csp_user:, primary: true, email: 'old@email.com')
email_new_primary = create(:user_email, csp_user:, primary: true, email: 'new@email.com')

expect(email_old_primary.reload.primary).to eq false
expect(email_new_primary.reload.primary).to eq true
end

it 'ensures that setting an email to primary unsets others' do
user = create(:user)
csp = create(:csp)
csp_user = create(:csp_user, user:, csp:)

email_old_primary = create(:user_email, csp_user:, primary: true, email: 'old@email.com')
email_new_primary = create(:user_email, csp_user:, primary: false, email: 'new@email.com')
expect(email_old_primary.primary).to eq true
expect(email_new_primary.primary).to eq false

# Should set email1 to not primary
email_new_primary.update!(primary: true)
email_old_primary.reload
email_new_primary.reload

expect(email_old_primary.primary).to eq false
expect(email_new_primary.primary).to eq true
end
end
3 changes: 3 additions & 0 deletions dpc-portal/spec/requests/login_dot_gov_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@
emails = UserEmail.last(2).pluck(:email)
expect(emails).to match_array(%w[email1@example.com email2@example.com])
expect(UserEmail.pluck(:active)).to all(be true)
expect(UserEmail.find(&:primary?).email).to eq 'email1@example.com'
expect(UserEmail.count(&:primary?)).to eq 1
end
end

Expand Down Expand Up @@ -270,6 +272,7 @@
expect(email.active).to eq true
expect(email.deactivated_at).to be_nil
expect(email.reactivated_at).to_not be_nil
expect(email.primary).to eq true
end
end
end
Expand Down
Loading