Skip to content

Commit

Permalink
creates query_mpi_profile User model method
Browse files Browse the repository at this point in the history
  • Loading branch information
bramleyjl committed Dec 10, 2024
1 parent ebb1dee commit 4459125
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 97 deletions.
50 changes: 31 additions & 19 deletions app/models/mpi_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
require 'mpi/constants'
require 'sentry_logging'

# Facade for MVI. User model delegates MVI correlation id and VA profile (golden record) methods to this class.
# Facade for MPI. User model delegates MPI correlation id and VA profile (golden record) methods to this class.
# When a profile is requested from one of the delegates it is returned from either a cached response in Redis
# or from the MVI SOAP service.
# or from the MPI SOAP service.
class MPIData < Common::RedisStore
include Common::CacheAside
include SentryLogging
Expand All @@ -30,7 +30,7 @@ class MPIData < Common::RedisStore

# Creates a new MPIData instance for a user identity.
#
# @param user [UserIdentity] the user identity to query MVI for
# @param user [UserIdentity] the user identity to query MPI for
# @return [MPIData] an instance of this class
def self.for_user(user_identity)
mvi = MPIData.new
Expand All @@ -48,7 +48,7 @@ def self.for_user(user_identity)
mvi
end

# A DOD EDIPI (Electronic Data Interchange Personal Identifier) MVI correlation ID
# A DOD EDIPI (Electronic Data Interchange Personal Identifier) MPI correlation ID
# or nil for users < LOA 3
#
# @return [String] the edipi correlation id
Expand All @@ -57,35 +57,35 @@ def self.for_user(user_identity)
# @return [Array[String]] multiple edipi correlation ids
delegate :edipis, to: :profile, allow_nil: true

# A ICN (Integration Control Number - generated by the Master Patient Index) MVI correlation ID
# A ICN (Integration Control Number - generated by the Master Patient Index) MPI correlation ID
# or nil for users < LOA 3
#
# @return [String] the icn correlation id
delegate :icn, to: :profile, allow_nil: true

# A ICN (Integration Control Number - generated by the Master Patient Index) MVI correlation ID
# A ICN (Integration Control Number - generated by the Master Patient Index) MPI correlation ID
# combined with its Assigning Authority ID. Or nil for users < LOA 3.
#
# @return [String] the icn correlation id with its assigning authority id.
# For example: '12345678901234567^NI^200M^USVHA^P'
#
delegate :icn_with_aaid, to: :profile, allow_nil: true

# A MHV (My HealtheVet) MVI correlation id
# A MHV (My HealtheVet) MPI correlation id
# or nil for users < LOA 3
#
# @return [String] the mhv correlation id
delegate :mhv_correlation_id, to: :profile, allow_nil: true

# A VBA (Veterans Benefits Administration) or participant MVI correlation id.
# A VBA (Veterans Benefits Administration) or participant MPI correlation id.
#
# @return [String] the participant id
delegate :participant_id, to: :profile, allow_nil: true

# @return [Array[String]] multiple participant ids
delegate :participant_ids, to: :profile, allow_nil: true

# A BIRLS (Beneficiary Identification and Records Locator System) MVI correlation id.
# A BIRLS (Beneficiary Identification and Records Locator System) MPI correlation id.
#
# @return [String] the birls id
delegate :birls_id, to: :profile, allow_nil: true
Expand All @@ -106,7 +106,7 @@ def self.for_user(user_identity)
# @return [String] the Vet360 id
delegate :vet360_id, to: :profile, allow_nil: true

# The search token given in the original MVI 1306 response message
# The search token given in the original MPI 1306 response message
#
# @return [String] the search token
delegate :search_token, to: :profile, allow_nil: true
Expand Down Expand Up @@ -136,25 +136,26 @@ def self.for_user(user_identity)
# @return [String] the home_phone
delegate :home_phone, to: :profile, allow_nil: true

# The profile returned from the MVI service. Either returned from cached response in Redis or the MVI service.
# The profile returned from the MPI service. Either returned from cached response in Redis or the MPI service.
# If break_cache is true a new request is made to the MPI service & the response is validated & cached.
#
# @return [MPI::Models::MviProfile] patient 'golden record' data from MVI
def profile
# @return [MPI::Models::MviProfile] patient 'golden record' data from MPI
def profile(break_cache: false)
return nil unless user_loa3

mvi_response&.profile
break_cache == true ? query_mpi_profile(user_key: get_user_key) : mvi_response&.profile
end

# The status of the last MVI response or not authorized for for users < LOA 3
# The status of the last MPI response or not authorized for for users < LOA 3
#
# @return [String] the status of the last MVI response
# @return [String] the status of the last MPI response
def status
return :not_authorized unless user_loa3

mvi_response&.status
end

# The error experienced when reaching out to the MVI service.
# The error experienced when reaching out to the MPI service.
#
# @return [Common::Exceptions::BackendServiceException]
def error
Expand All @@ -163,7 +164,7 @@ def error
mvi_response.try(:error)
end

# @return [MPI::Responses::FindProfileResponse] the response returned from MVI
# @return [MPI::Responses::FindProfileResponse] the response returned from MPI
def mvi_response(user_key: get_user_key)
@mvi_response ||= response_from_redis_or_service(user_key:)
end
Expand All @@ -172,7 +173,7 @@ def mpi_response_is_cached?(user_key: get_user_key)
cached?(key: user_key)
end

# The status of the MPI Add Person Proxy Add call. An Orchestrated MVI Search needs to be made before an
# The status of the MPI Add Person Proxy Add call. An Orchestrated MPI Search needs to be made before an
# MPI add person proxy addcall is made. The response is recached afterwards so the new ids can be accessed
# on the next call.
def add_person_proxy
Expand Down Expand Up @@ -231,6 +232,17 @@ def find_profile
end
end

def query_mpi_profile(user_key:)
do_cached_with(key: user_key, force: true) do
queried_profile = find_profile
@mvi_response = queried_profile
queried_profile
rescue ArgumentError, MPI::Errors::ArgumentError => e
log_message_to_sentry("[MPI Data] Request error: #{e.message}", :warn)
return nil
end
end

def add_ids(response)
# set new ids in the profile and recache the response
profile.birls_id = response.parsed_codes[:birls_id].presence
Expand Down
8 changes: 6 additions & 2 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ def ssn_normalized
delegate :status, to: :mpi, prefix: true
delegate :vet360_id, to: :mpi

def query_mpi_profile
mpi_profile(break_cache: true)
end

def active_mhv_ids
mpi_profile&.active_mhv_ids
end
Expand Down Expand Up @@ -478,10 +482,10 @@ def can_create_mhv_account?

private

def mpi_profile
def mpi_profile(break_cache: false)
return nil unless identity && mpi

mpi.profile
mpi.profile(break_cache:)
end

def mpi
Expand Down
11 changes: 0 additions & 11 deletions app/services/sign_in/session_refresher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def perform
validate_terms_of_use if client_config.enforced_terms.present?
anti_csrf_check if anti_csrf_enabled_client?
detect_token_theft
validate_mpi_profile
update_session! if parent_refresh_token_in_session?
create_new_tokens
end
Expand Down Expand Up @@ -60,16 +59,6 @@ def parent_refresh_token_in_session?
session.hashed_refresh_token == double_parent_refresh_token_hash
end

def validate_mpi_profile
mpi_service = MPI::Service.new
mpi_profile = mpi_service.find_profile_by_identifier(identifier: session.user_account&.icn,
identifier_type: MPI::Constants::ICN)&.profile
return unless mpi_profile

raise Errors::MPILockedAccountError.new message: 'Death Flag Detected' unless mpi_profile.deceased_date.nil?
raise Errors::MPILockedAccountError.new message: 'Theft Flag Detected' unless mpi_profile.id_theft_flag == false
end

def create_new_tokens
SessionContainer.new(session:,
refresh_token: child_refresh_token,
Expand Down
9 changes: 9 additions & 0 deletions app/services/sign_in/user_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def reload_user
current_user.session_handle = access_token.session_handle
current_user.save && user_identity.save
current_user.invalidate_mpi_cache
validate_mpi_profile
current_user.create_mhv_account_async

current_user
Expand All @@ -40,6 +41,14 @@ def validate_account_and_session
raise Errors::SessionNotFoundError.new message: 'Invalid Session Handle' unless session
end

def validate_mpi_profile
mpi_profile = current_user.query_mpi_profile
return unless mpi_profile

raise Errors::MPILockedAccountError.new message: 'Death Flag Detected' unless mpi_profile.deceased_date.nil?
raise Errors::MPILockedAccountError.new message: 'Theft Flag Detected' unless mpi_profile.id_theft_flag == false
end

def user_attributes
{
mhv_icn: user_account.icn,
Expand Down
27 changes: 1 addition & 26 deletions spec/controllers/v0/sign_in_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2277,19 +2277,8 @@
let(:enforced_terms) { nil }
let(:anti_csrf) { false }
let(:expected_error_status) { :unauthorized }
let(:identifier) { user_account.icn }
let(:deceased_date) { nil }
let(:id_theft_flag) { false }
let(:mpi_profile) { build(:mpi_profile, icn: user_account.icn, deceased_date:, id_theft_flag:) }
let(:find_profile_response) { create(:find_profile_response, profile: mpi_profile) }

before do
allow(Rails.logger).to receive(:info)
allow_any_instance_of(MPI::Service)
.to receive(:find_profile_by_identifier)
.with(identifier:, identifier_type: MPI::Constants::ICN)
.and_return(find_profile_response)
end
before { allow(Rails.logger).to receive(:info) }

shared_examples 'error response' do
let(:expected_error_json) { { 'errors' => expected_error } }
Expand Down Expand Up @@ -2457,20 +2446,6 @@
it_behaves_like 'error response'
end

context 'and the retrieved account has an MPI death flag' do
let(:deceased_date) { '20020202' }
let(:expected_error) { 'Death Flag Detected' }

it_behaves_like 'error response'
end

context 'and the account has an MPI theft flag' do
let(:id_theft_flag) { true }
let(:expected_error) { 'Theft Flag Detected' }

it_behaves_like 'error response'
end

it 'returns ok status' do
expect(subject).to have_http_status(:ok)
end
Expand Down
6 changes: 0 additions & 6 deletions spec/requests/swagger_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,6 @@
CGI.escape(SignIn::RefreshTokenEncryptor.new(refresh_token: session_container.refresh_token).perform)
end
let(:refresh_token_param) { { refresh_token: } }
let(:mpi_profile) { build(:mpi_profile, icn: user_verification.user_account.icn) }
let(:find_profile_response) { create(:find_profile_response, profile: mpi_profile) }

before do
allow_any_instance_of(MPI::Service).to receive(:find_profile_by_identifier).and_return(find_profile_response)
end

it 'refreshes the session and returns new tokens' do
expect(subject).to validate(
Expand Down
35 changes: 2 additions & 33 deletions spec/services/sign_in/session_refresher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,8 @@
let(:anti_csrf) { false }
let(:refresh_token_duration) { SignIn::Constants::RefreshToken::VALIDITY_LENGTH_SHORT_MINUTES }
let(:enforced_terms) { nil }
let(:identifier) { user_account.icn }
let(:deceased_date) { nil }
let(:id_theft_flag) { false }
let(:mpi_profile) { build(:mpi_profile, icn: user_account.icn, deceased_date:, id_theft_flag:) }
let(:find_profile_response) { create(:find_profile_response, profile: mpi_profile) }

before do
Timecop.freeze(Time.zone.now.floor)
allow_any_instance_of(MPI::Service)
.to receive(:find_profile_by_identifier)
.with(identifier:, identifier_type: MPI::Constants::ICN)
.and_return(find_profile_response)
end

before { Timecop.freeze(Time.zone.now.floor) }

after { Timecop.return }

Expand Down Expand Up @@ -289,26 +278,6 @@
expect { subject }.to raise_error(expected_error, expected_error_message)
end
end

context 'and the account has an MPI death flag' do
let(:deceased_date) { '20050215' }
let(:expected_error) { SignIn::Errors::MPILockedAccountError }
let(:expected_error_message) { 'Death Flag Detected' }

it 'raises an MPI Locked Account error' do
expect { subject }.to raise_error(expected_error, expected_error_message)
end
end

context 'and the account has an MPI theft flag' do
let(:id_theft_flag) { true }
let(:expected_error) { SignIn::Errors::MPILockedAccountError }
let(:expected_error_message) { 'Theft Flag Detected' }

it 'raises an MPI Locked Account error' do
expect { subject }.to raise_error(expected_error, expected_error_message)
end
end
end

context 'when session handle in refresh token does not match an existing oauth session' do
Expand Down

0 comments on commit 4459125

Please sign in to comment.