Skip to content
Closed
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
1 change: 1 addition & 0 deletions app/jobs/resolution_proofing_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def perform(
ssn_is_unique = Idv::DuplicateSsnFinder.new(
ssn: applicant_pii[:ssn],
user: user,
issuer: current_sp&.issuer || nil,
).ssn_is_unique?

callback_log_data.result[:ssn_is_unique] = ssn_is_unique
Expand Down
2 changes: 1 addition & 1 deletion app/services/duplicate_profile_checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def check_for_duplicate_profiles
cacher = Pii::Cacher.new(user, user_session)

pii = cacher.fetch(profile.id)
duplicate_ssn_finder = Idv::DuplicateSsnFinder.new(user:, ssn: pii[:ssn])
duplicate_ssn_finder = Idv::DuplicateSsnFinder.new(user:, ssn: pii[:ssn], issuer: sp.issuer)
associated_profiles = duplicate_ssn_finder.associated_facial_match_profiles_with_ssn
if !duplicate_ssn_finder.ial2_profile_ssn_is_unique?
ids = associated_profiles.map(&:id)
Expand Down
19 changes: 12 additions & 7 deletions app/services/idv/duplicate_ssn_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,28 @@

module Idv
class DuplicateSsnFinder
attr_reader :ssn, :user
attr_reader :ssn, :user, :issuer

def initialize(user:, ssn:)
def initialize(user:, ssn:, issuer:)
@user = user
@ssn = ssn
@issuer = issuer
end

def ssn_is_unique?
Profile.where(ssn_signature: ssn_signatures)
.where(initiating_service_provider_issuer: sp_eligible_for_one_account)
.where.not(user_id: user.id).empty?
Profile.where(ssn_signature: ssn_signatures).where.not(user_id: user.id).empty?
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puts this method back to where it was before per
#12326 (comment)

end

def associated_facial_match_profiles_with_ssn
Profile.active.facial_match.where(ssn_signature: ssn_signatures)
.where(initiating_service_provider_issuer: sp_eligible_for_one_account)
Profile.joins('INNER JOIN identities ON identities.user_id = profiles.user_id')
.active
.facial_match
.where(ssn_signature: ssn_signatures)
.where(identities: { service_provider: sp_eligible_for_one_account })
.where(identities: { deleted_at: nil })
.where(identities: { ial: 2 })
.where.not(user_id: user.id)
.distinct
end

def ial2_profile_ssn_is_unique?
Expand Down
2 changes: 1 addition & 1 deletion lib/data_pull.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def run(args:, config:)
require 'data_requests/deployed'
ssns = args

ssn_finders = ssns.map { |ssn| Idv::DuplicateSsnFinder.new(user: nil, ssn: ssn) }
ssn_finders = ssns.map { |ssn| Idv::DuplicateSsnFinder.new(user: nil, ssn: ssn, issuer: nil) }
ssn_signatures = ssn_finders.flat_map do |ssn_finder|
ssn_finder.ssn_signatures
end
Expand Down
18 changes: 18 additions & 0 deletions spec/services/duplicate_profile_checker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,28 @@
profile.save
end

let(:identity) do
build(
:service_provider_identity,
service_provider: sp.issuer,
ial: 2,
)
end

let(:identity2) do
build(
:service_provider_identity,
service_provider: sp.issuer,
ial: 2,
)
end

before do
session[:encrypted_profiles] = {
profile.id.to_s => SessionEncryptor.new.kms_encrypt(active_pii.to_json),
}
user.identities << identity
user2.identities << identity2
end

it 'creates a new duplicate profile confirmation entry' do
Expand Down
146 changes: 138 additions & 8 deletions spec/services/idv/duplicate_ssn_finder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
describe '#ssn_is_unique?' do
let(:ssn) { '123-45-6789' }
let(:user) { create(:user) }

subject { described_class.new(ssn: ssn, user: user) }
let(:sp) { OidcAuthHelper::OIDC_FACIAL_MATCH_ISSUER }
subject { described_class.new(ssn: ssn, user: user, issuer: sp) }

before do
allow(IdentityConfig.store).to receive(:eligible_one_account_providers)
Expand Down Expand Up @@ -71,8 +71,25 @@
describe '#associated_facial_match_profiles_with_ssn' do
let(:ssn) { '123-45-6789' }
let(:user) { create(:user) }

subject { described_class.new(ssn: ssn, user: user) }
let(:user2) { create(:user) }
let(:sp) { OidcAuthHelper::OIDC_FACIAL_MATCH_ISSUER }
let(:identity) do
build(
:service_provider_identity,
service_provider: sp,
session_uuid: SecureRandom.uuid,
ial: 2,
)
end
let(:identity2) do
build(
:service_provider_identity,
service_provider: sp,
session_uuid: SecureRandom.uuid,
ial: 2,
)
end
subject { described_class.new(ssn: ssn, user: user, issuer: sp) }

before do
allow(IdentityConfig.store).to receive(:eligible_one_account_providers)
Expand All @@ -83,8 +100,10 @@
context 'when ssn is taken by different profile by and is IAL2' do
it 'returns list different profile' do
create(:profile, :facial_match_proof, pii: { ssn: ssn }, user: user, active: true)
user.identities << identity
create(:profile, :facial_match_proof, pii: { ssn: ssn }, user: user2, active: true)
user2.identities << identity2

create(:profile, :facial_match_proof, pii: { ssn: ssn }, active: true)
expect(subject.associated_facial_match_profiles_with_ssn.size).to eq(1)
end
end
Expand All @@ -110,8 +129,26 @@
describe '#ial2_profile_ssn_is_unique?' do
let(:ssn) { '123-45-6789' }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:sp) { OidcAuthHelper::OIDC_FACIAL_MATCH_ISSUER }
let(:identity) do
build(
:service_provider_identity,
service_provider: sp,
session_uuid: SecureRandom.uuid,
ial: 2,
)
end
let(:identity2) do
build(
:service_provider_identity,
service_provider: sp,
session_uuid: SecureRandom.uuid,
ial: 2,
)
end

subject { described_class.new(ssn: ssn, user: user) }
subject { described_class.new(ssn: ssn, user: user, issuer: sp) }

before do
allow(IdentityConfig.store).to receive(:eligible_one_account_providers)
Expand All @@ -121,8 +158,9 @@
context 'when ssn is taken by different profile by and is IAL2' do
it 'returns false' do
create(:profile, :facial_match_proof, pii: { ssn: ssn }, user: user, active: true)

create(:profile, :facial_match_proof, pii: { ssn: ssn }, active: true)
user.identities << identity
create(:profile, :facial_match_proof, pii: { ssn: ssn }, user: user2, active: true)
user2.identities << identity2
expect(subject.ial2_profile_ssn_is_unique?).to eq false
end
end
Expand All @@ -144,4 +182,96 @@
end
end
end

describe '#associated_facial_match_profiles_with_ssn' do
let(:ssn) { '123-45-6789' }
let(:ssn2) { '123-45-9999' }
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:user4) { create(:user) }
let(:sp) { OidcAuthHelper::OIDC_FACIAL_MATCH_ISSUER }
let(:sp2) { 'urn:gov:gsa:openidconnect:sp:test2' }
let(:identity) do
build(
:service_provider_identity,
service_provider: sp,
ial: 2,
)
end
let(:identity1a) do
build(
:service_provider_identity,
service_provider: sp2,
ial: 2,
)
end
let(:identity2) do
build(
:service_provider_identity,
service_provider: sp,
ial: 2,
)
end
let(:identity2a) do
build(
:service_provider_identity,
service_provider: sp2,
ial: 2,
)
end
let(:identity3) do
build(
:service_provider_identity,
service_provider: sp,
ial: 2,
)
end
let(:identity4) do
build(
:service_provider_identity,
service_provider: sp,
ial: 2,
)
end
let(:identity4a) do
build(
:service_provider_identity,
service_provider: sp2,
ial: 2,
)
end
subject { described_class.new(ssn: ssn, user: user, issuer: sp) }

before do
allow(IdentityConfig.store).to receive(:eligible_one_account_providers)
.and_return([
OidcAuthHelper::OIDC_FACIAL_MATCH_ISSUER,
'urn:gov:gsa:openidconnect:sp:test2',
])
create(:profile, :facial_match_proof, id: 1, pii: { ssn: ssn }, user: user, active: true)
user.identities << [identity, identity1a]
create(:profile, :facial_match_proof, id: 2, pii: { ssn: ssn }, user: user2, active: true)
user2.identities << [identity2, identity2a]
create(:profile, :facial_match_proof, id: 3, pii: { ssn: ssn }, user: user3, active: true)
user3.identities << [identity3]
create(:profile, :facial_match_proof, id: 4, pii: { ssn: ssn2 }, user: user4, active: true)
user4.identities << [identity4, identity4a]
end

context 'when ssn belongs to profiles with matching opted in sp' do
it 'returns matching profile ids' do
expect(subject.associated_facial_match_profiles_with_ssn.count).to eq(2)
expect(subject.associated_facial_match_profiles_with_ssn[0].id).to eq(2)
expect(subject.associated_facial_match_profiles_with_ssn[1].id).to eq(3)
end
end

context 'when a profile has a unique ssn but belongs to matching opted in sp' do
subject { described_class.new(ssn: ssn2, user: user4, issuer: sp) }
it 'does not return matching profile' do
expect(subject.associated_facial_match_profiles_with_ssn.last).to eq(nil)
end
end
end
end