Skip to content
Merged
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
50 changes: 50 additions & 0 deletions app/services/duplicate_accounts_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

class DuplicateAccountsReport
def initialize(service_provider_arr)
@service_provider_arr = service_provider_arr
end

def self.call(service_provider_arr)
identities_sql(service_provider_arr)
end

def self.identities_sql(sp_array)
query = <<-SQL
WITH non_unique_ssn_signatures AS (
SELECT ssn_signature
FROM profiles
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This was missed out in the original story - we only need active profiles.

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.

added active: true f2ac0cc

GROUP BY ssn_signature
HAVING COUNT(*) > 1
)
SELECT
i.service_provider,
u.uuid,
i.last_authenticated_at,
sp.friendly_name,
p.activated_at,
p.ssn_signature
FROM identities i
JOIN users u ON i.user_id = u.id
JOIN service_providers sp ON i.service_provider = sp.issuer
JOIN profiles p ON u.id = p.user_id
JOIN non_unique_ssn_signatures nus ON p.ssn_signature = nus.ssn_signature
WHERE
i.service_provider IN (?)
AND i.ial = 2
AND i.last_authenticated_at BETWEEN ? AND ?
AND p.active
SQL

ActiveRecord::Base.connection.execute(
ApplicationRecord.sanitize_sql_array(
[
query,
sp_array,
Date.yesterday.beginning_of_day,
Date.yesterday.end_of_day,
],
),
)
end
end
50 changes: 50 additions & 0 deletions lib/tasks/duplicate_accounts.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

require 'csv'

namespace :duplicate_accounts do
task :report, %i[service_provider] => [:environment] do |_task, args|
ActiveRecord::Base.connection.execute('SET statement_timeout = 0')

results = DuplicateAccountsReport.call(args[:service_provider])

if results.count > 0
results_to_csv(results)
else
puts 'no results found'
end
end

def results_to_csv(results)
puts 'result to csv'
puts 'User uuid, Service Provider, Agency, Latest Activity, Profile Activated'
output_dir = './tmp/duplicate_accounts/'
FileUtils.mkdir_p(output_dir)
accounts_csv = CSV.open(File.join(output_dir, 'duplicate_accounts.csv'), 'w')

accounts_csv << %w[
user_uuid
service_provider
agency
latest_activity
profile_activated
]

results.each do |result|
accounts_csv << [
result['uuid'],
result['service_provider'],
result['friendly_name'],
result['last_authenticated_at'],
result['activated_at'],
]
result_str = "#{result['uuid']}, "\
"#{result['service_provider']}, "\
"#{result['friendly_name']}, "\
"#{result['last_authenticated_at']}, "\
"#{result['activated_at']}"
puts result_str
end
end
end
# rake "duplicate_accounts:report["urn:gov:gsa:SAML:2.0.profiles:sp:sso:localhost"]"
43 changes: 43 additions & 0 deletions spec/lib/tasks/duplicate_accounts_rake_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require 'rails_helper'
require 'rake'

RSpec.describe 'duplicate accounts rake task' do
before do
Rake.application.rake_require 'tasks/duplicate_accounts'
Rake::Task.define_task(:environment)
Rake::Task['duplicate_accounts:report'].reenable
end

describe 'duplicate_accounts:report' do
let(:service_provider) { ['test_saml_sp_requesting_signed_response_message'] }
subject(:task) { Rake.application.invoke_task('duplicate_accounts:report[service_provider]') }

context 'with an empty report array' do
it 'displays a no results found message' do
expect { task }.to \
output("no results found\n").to_stdout
end
end

context 'with a duplicate account shown in report array' do
let(:results) do
[
{
uuid: 'abc123def456',
service_provider: service_provider[0],
friendly_name: 'AAA Test SP',
latest_activity: Date.yesterday.middle_of_day,
activated_at: Date.yesterday.beginning_of_day,
},
]
end
it 'displays a result' do
allow(DuplicateAccountsReport).to receive(:call)
.and_return(results)

expect { task }.to \
output(include('result to csv')).to_stdout
end
end
end
end
77 changes: 77 additions & 0 deletions spec/services/duplicate_accounts_report_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'rails_helper'
RSpec.describe DuplicateAccountsReport do
let(:issuer) { 'test_saml_sp_requesting_signed_response_message' }

def create_user_with_ssn_signature(ssn_signature)
create(
:user,
:fully_registered,
profiles: [build(:profile, :active, :verified, ssn_signature: ssn_signature)],
)
end

def create_service_provider_identity(user, last_authenticated_at)
create(
:service_provider_identity,
ial: 2,
service_provider: issuer,
user: user,
last_authenticated_at: last_authenticated_at,
)
end

describe '#call' do
context 'when no records exist' do
it 'creates an empty results array' do
results = DuplicateAccountsReport.call(issuer)
expect(results.count).to eq(0)
end
end

context 'when no ssn_signatures match' do
it 'creates empty results' do
freeze_time do
user = create_user_with_ssn_signature('hashedssnsignaturekeytwo')
user2 = create_user_with_ssn_signature('hashedssnsignaturekeyzero')
create_service_provider_identity(user, Date.yesterday.middle_of_day)
create_service_provider_identity(user2, Date.yesterday.middle_of_day)

results = DuplicateAccountsReport.call(issuer)
expect(results.count).to eq(0)
end
end
end

context 'when duplicate accounts exist' do
it 'creates a results array with one record when two users have same SSN signature' do
freeze_time do
user1 = create_user_with_ssn_signature('hashedssnsignaturekeyone')
user2 = create_user_with_ssn_signature('hashedssnsignaturekeyone')

create_service_provider_identity(user2, Time.zone.today)
create_service_provider_identity(user1, Date.yesterday.middle_of_day)

results = DuplicateAccountsReport.call(issuer)
expect(results.count).to eq(1)
end
end

it 'creates a results array with three records when multiple users have same SSN signature' do
freeze_time do
user1 = create_user_with_ssn_signature('hashedssnsignaturekeytwo')
user2 = create_user_with_ssn_signature('hashedssnsignaturekeyone')
user3 = create_user_with_ssn_signature('hashedssnsignaturekeyone')
user4 = create_user_with_ssn_signature('hashedssnsignaturekeyone')

create_service_provider_identity(user1, Date.yesterday.middle_of_day)
create_service_provider_identity(user2, Date.yesterday.middle_of_day)
create_service_provider_identity(user3, Date.yesterday.middle_of_day)
create_service_provider_identity(user4, Date.yesterday.middle_of_day)

results = DuplicateAccountsReport.call(issuer)
expect(results.count).to eq(3)
end
end
end
end
end