Skip to content

Commit

Permalink
7853 Lorota Integration (#7853)
Browse files Browse the repository at this point in the history
  • Loading branch information
dillo authored Sep 15, 2021
1 parent 424cd25 commit d321b16
Show file tree
Hide file tree
Showing 30 changed files with 1,402 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

module CheckIn
class ApplicationController < ::ApplicationController
before_action :authorize
include ActionController::Cookies
include ActionController::RequestForgeryProtection

protect_from_forgery with: :exception

before_action :authorize, :set_csrf_cookie
skip_before_action :authenticate
skip_before_action :verify_authenticity_token

Expand All @@ -11,5 +16,11 @@ class ApplicationController < ::ApplicationController
def authorize
routing_error unless Flipper.enabled?('check_in_experience_enabled', params[:cookie_id])
end

private

def set_csrf_cookie
cookies['CSRF-TOKEN'] = form_authenticity_token
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,34 @@
module CheckIn
module V1
class PatientCheckInsController < CheckIn::ApplicationController
def show; end
def show
check_in = CheckIn::PatientCheckIn.build(uuid: params[:id])
resp =
if session[:jwt].present?
::V1::Lorota::Service.build(check_in: check_in).get_check_in
else
::V1::Lorota::BasicService.build(check_in: check_in).get_check_in
end

render json: resp
end

def create
check_in = CheckIn::PatientCheckIn.build(uuid: patient_check_in_params[:id])
data = ::V1::Chip::Service.build(check_in).create_check_in
check_in = CheckIn::PatientCheckIn.build(uuid: patient_check_in_params[:uuid])
resp =
if session[:jwt]
::V1::Chip::Service.build(check_in).create_check_in
else
{ data: { error: true, message: 'Check-in failed' }, status: 403 }
end

render json: data
render json: resp
end

private

def patient_check_in_params
params.require(:patient_check_ins).permit(:id)
params.require(:patient_check_ins).permit(:uuid)
end

def authorize
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

module CheckIn
module V1
class SessionsController < CheckIn::ApplicationController
def show
check_in = CheckIn::PatientCheckIn.build(uuid: params[:id])
resp =
if session[:jwt].present?
{ data:
{ permissions: 'read.full', uuid: check_in.uuid, status: 'success', jwt: session[:jwt] },
status: 200 }
else
::V1::Lorota::BasicService.build(check_in: check_in).get_or_create_token
end

render json: resp
end

def create
check_in = CheckIn::CheckInWithAuth.build(data: session_params)
resp =
if session[:jwt].present?
{ data:
{ permissions: 'read.full', uuid: check_in.uuid, status: 'success', jwt: session[:jwt] },
status: 200 }
else
data = ::V1::Lorota::Service.build(check_in: check_in).get_or_create_token

session[:jwt] = data.dig(:data, :jwt)
data
end

render json: resp
end

private

def session_params
params.require(:session).permit(:uuid, :last4, :last_name)
end

def authorize
routing_error unless Flipper.enabled?('check_in_experience_low_authentication_enabled')
end
end
end
end
30 changes: 30 additions & 0 deletions modules/check_in/app/models/check_in/check_in_with_auth.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module CheckIn
class CheckInWithAuth < PatientCheckIn
LAST_FOUR_REGEX = /^[0-9]{4}$/.freeze
LAST_NAME_REGEX = /^.{1,600}$/.freeze

attr_reader :last4, :last_name

def self.build(opts = {})
new(opts)
end

def initialize(opts)
@uuid = opts.dig(:data, :uuid)
@last4 = opts.dig(:data, :last4)
@last_name = opts.dig(:data, :last_name)
end

def valid?
UUID_REGEX.match?(uuid) && LAST_FOUR_REGEX.match?(last4) && LAST_NAME_REGEX.match?(last_name)
end

def client_error
body = { error: true, message: 'Invalid uuid, last4 or last name!' }

Faraday::Response.new(body: body, status: 400)
end
end
end
4 changes: 2 additions & 2 deletions modules/check_in/app/services/v1/chip/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ def self.build(check_in)
end

def initialize(check_in)
@settings = Settings.check_in.chip_api_v1
@check_in = check_in
@request = Request.build
@response = Response
@session = Session.build
@settings = Settings.check_in.chip_api_v1
end

def create_check_in
return handle_response(client_error) unless valid?
return response.build(response: client_error).handle unless valid?

token = session.retrieve
resp = request.post(path: "/#{base_path}/actions/check-in/#{uuid}", access_token: token)
Expand Down
52 changes: 52 additions & 0 deletions modules/check_in/app/services/v1/lorota/basic_claims_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module V1
module Lorota
class BasicClaimsToken
SIGNING_ALGORITHM = 'RS256'

extend Forwardable

attr_reader :expiration, :settings, :check_in

def_delegators :settings, :key_path

def self.build(opts = {})
new(opts)
end

def initialize(opts)
@settings = Settings.check_in.lorota_v1
@check_in = opts[:check_in]
@expiration = 1440
end

def sign_assertion
JWT.encode(claims, rsa_key, SIGNING_ALGORITHM)
end

def claims
{
aud: 'lorota',
iss: 'vets-api',
sub: check_in.uuid,
scopes: ['read.basic'],
iat: issued_at_time,
exp: expires_at_time
}
end

def rsa_key
@rsa_key ||= OpenSSL::PKey::RSA.new(File.read(key_path))
end

def issued_at_time
@issued_at_time ||= Time.zone.now.to_i
end

def expires_at_time
@expires_at_time ||= expiration.minutes.from_now.to_i
end
end
end
end
43 changes: 43 additions & 0 deletions modules/check_in/app/services/v1/lorota/basic_redis_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

module V1
module Lorota
class BasicRedisHandler
extend Forwardable

attr_reader :check_in, :settings
attr_accessor :token

def_delegators :settings, :redis_session_prefix

def self.build(opts = {})
new(opts)
end

def initialize(opts)
@settings = Settings.check_in.lorota_v1
@check_in = opts[:check_in]
end

def get
Rails.cache.read(
build_session_id_prefix,
namespace: 'check-in-lorota-v1-cache'
)
end

def save
Rails.cache.write(
build_session_id_prefix,
token.access_token,
namespace: 'check-in-lorota-v1-cache',
expires_in: 1440.minutes
)
end

def build_session_id_prefix
@build_session_id_prefix ||= "#{redis_session_prefix}_#{check_in.uuid}_read.basic"
end
end
end
end
52 changes: 52 additions & 0 deletions modules/check_in/app/services/v1/lorota/basic_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module V1
module Lorota
class BasicService
extend Forwardable

attr_reader :check_in, :response, :session, :settings, :request

def_delegators :check_in, :client_error, :valid?
def_delegators :settings, :base_path

def self.build(opts = {})
new(opts)
end

def initialize(opts)
@settings = Settings.check_in.lorota_v1
@check_in = opts[:check_in]
@session = BasicSession.build(check_in: check_in)
@request = Request.build(token: session.from_redis)
@response = Response
end

def get_or_create_token
data = session.from_redis || session.from_lorota

format_data(data)
end

def get_check_in
token = session.from_redis

if token.present?
data = request.get("/#{base_path}/data/#{check_in.uuid}")

Oj.load(data.body)
end
end

def format_data(data)
body = { permissions: permissions, uuid: check_in.uuid, status: 'success', jwt: data }

response.build(response: Faraday::Response.new(body: body, status: 200)).handle
end

def permissions
'read.basic'
end
end
end
end
29 changes: 29 additions & 0 deletions modules/check_in/app/services/v1/lorota/basic_session.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module V1
module Lorota
class BasicSession
attr_reader :check_in, :token, :redis_handler

def self.build(opts = {})
new(opts)
end

def initialize(opts)
@check_in = opts[:check_in]
@token = BasicToken.build(check_in: check_in)
@redis_handler = BasicRedisHandler.build(check_in: check_in)
end

def from_redis
redis_handler.get
end

def from_lorota
redis_handler.token = token.fetch
redis_handler.save
redis_handler.get
end
end
end
end
32 changes: 32 additions & 0 deletions modules/check_in/app/services/v1/lorota/basic_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module V1
module Lorota
class BasicToken
extend Forwardable

attr_reader :request, :claims_token, :check_in, :settings
attr_accessor :access_token

def_delegators :settings, :base_path

def self.build(opts = {})
new(opts)
end

def initialize(opts)
@settings = Settings.check_in.lorota_v1
@check_in = opts[:check_in]
@claims_token = BasicClaimsToken.build(check_in: check_in).sign_assertion
@request = Request.build(claims_token: claims_token)
end

def fetch
resp = request.post("/#{base_path}/token", {})

self.access_token = Oj.load(resp.body)&.fetch('token')
self
end
end
end
end
18 changes: 18 additions & 0 deletions modules/check_in/app/services/v1/lorota/claims_token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module V1
module Lorota
class ClaimsToken < BasicClaimsToken
def claims
{
aud: 'lorota',
iss: 'vets-api',
sub: check_in.uuid,
scopes: ['read.full'],
iat: issued_at_time,
exp: expires_at_time
}
end
end
end
end
Loading

0 comments on commit d321b16

Please sign in to comment.