Skip to content

Commit

Permalink
BTSSS/Extract Auth (#18382)
Browse files Browse the repository at this point in the history
* separate token service, update and add tests

* split services and clients, get tokens from controller

* tidying

* change return statement, remove token service from claims client

* remove mock data option from token service

* make base client for shared code

* fix funky destructuring

* simplify before for claims spec

* remove extra commented line

* add token_service specific happy path test

* consolidate claims client code

* move before block to set up for failed call tests

* re-delete client file
  • Loading branch information
liztownd committed Sep 12, 2024
1 parent abe8285 commit 911d7df
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 216 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module V0
class ClaimsController < ApplicationController
def index
begin
claims = service.get_claims(@current_user)
token_service.get_tokens(@current_user) => { veis_token:, btsss_token: }
claims = claims_service.get_claims(veis_token, btsss_token)
rescue Faraday::Error => e
TravelPay::ServiceError.raise_mapped_error(e)
end
Expand All @@ -15,8 +16,12 @@ def index

private

def service
@service ||= TravelPay::ClaimsService.new
def claims_service
@claims_service ||= TravelPay::ClaimsService.new
end

def token_service
@token_service ||= TravelPay::TokenService.new
end

def common_exception(e)
Expand Down
120 changes: 2 additions & 118 deletions modules/travel_pay/app/services/travel_pay/claims_client.rb
Original file line number Diff line number Diff line change
@@ -1,133 +1,17 @@
# frozen_string_literal: true

require 'securerandom'
require_relative 'base_client'
require_relative './base_client'

module TravelPay
class ClaimsClient < TravelPay::BaseClient
##
# HTTP POST call to the VEIS Auth endpoint to get the access token
#
# @return [Faraday::Response]
#
def request_veis_token
auth_url = Settings.travel_pay.veis.auth_url
tenant_id = Settings.travel_pay.veis.tenant_id

response = connection(server_url: auth_url).post("#{tenant_id}/oauth2/token") do |req|
req.headers[:content_type] = 'application/x-www-form-urlencoded'
req.body = URI.encode_www_form(veis_params)
end

response.body['access_token']
end

##
# HTTP POST call to the BTSSS token endpoint to get the access token
#
# @return [Faraday::Response]
#
def request_btsss_token(veis_token, sts_token)
btsss_url = Settings.travel_pay.base_url
client_number = Settings.travel_pay.client_number
correlation_id = SecureRandom.uuid
Rails.logger.debug(message: 'Correlation ID', correlation_id:)

response = connection(server_url: btsss_url).post('api/v1/Auth/access-token') do |req|
req.headers['Authorization'] = "Bearer #{veis_token}"
req.headers['BTSSS-API-Client-Number'] = client_number.to_s
req.headers['X-Correlation-ID'] = correlation_id
req.headers.merge!(claim_headers)
req.body = { authJwt: sts_token }
end

response.body['data']['accessToken']
end

##
# HTTP GET call to the BTSSS 'claims' endpoint
# API responds with travel pay claims including status
#
# @return [TravelPay::Claim]
#
def get_claims(current_user)
veis_token = request_veis_token

sts_token = request_sts_token(current_user)
btsss_token = request_btsss_token(veis_token, sts_token)

request_claims(veis_token, btsss_token)
end

def request_sts_token(user)
return nil if mock_enabled?

host_baseurl = build_host_baseurl({ ip_form: false })
private_key_file = Settings.sign_in.sts_client.key_path
private_key = OpenSSL::PKey::RSA.new(File.read(private_key_file))

assertion = build_sts_assertion(user)
jwt = JWT.encode(assertion, private_key, 'RS256')

# send to sis
response = connection(server_url: host_baseurl).post('/v0/sign_in/token') do |req|
req.params['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
req.params['assertion'] = jwt
end

response.body['data']['access_token']
end

private

def build_sts_assertion(user)
service_account_id = Settings.travel_pay.sts.service_account_id
host_baseurl = build_host_baseurl({ ip_form: false })
audience_baseurl = build_host_baseurl({ ip_form: true })
scopes = Settings.travel_pay.sts.scope.blank? ? [] : [Settings.travel_pay.sts.scope]

current_time = Time.now.to_i
jti = SecureRandom.uuid

{
'iss' => host_baseurl,
'sub' => user.email,
'aud' => "#{audience_baseurl}/v0/sign_in/token",
'iat' => current_time,
'exp' => current_time + 300,
'scopes' => scopes,
'service_account_id' => service_account_id,
'jti' => jti,
'user_attributes' => { 'icn' => user.icn }
}
end

def build_host_baseurl(config)
env = Settings.vsp_environment
host = Settings.hostname

if env == 'localhost'
if config[:ip_form]
return 'http://127.0.0.1:3000'
else
return 'http://localhost:3000'
end
end

"https://#{host}"
end

def veis_params
{
client_id: Settings.travel_pay.veis.client_id,
client_secret: Settings.travel_pay.veis.client_secret,
client_info: 1,
grant_type: 'client_credentials',
resource: Settings.travel_pay.veis.resource
}
end

def request_claims(veis_token, btsss_token)
def get_claims(veis_token, btsss_token)
btsss_url = Settings.travel_pay.base_url
correlation_id = SecureRandom.uuid
Rails.logger.debug(message: 'Correlation ID', correlation_id:)
Expand Down
4 changes: 2 additions & 2 deletions modules/travel_pay/app/services/travel_pay/claims_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

module TravelPay
class ClaimsService
def get_claims(current_user)
claims_response = client.get_claims(current_user)
def get_claims(veis_token, btsss_token)
claims_response = client.get_claims(veis_token, btsss_token)
symbolized_body = claims_response.body.deep_symbolize_keys

{
Expand Down
116 changes: 116 additions & 0 deletions modules/travel_pay/app/services/travel_pay/token_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# frozen_string_literal: true

require 'securerandom'
require_relative './base_client'

module TravelPay
class TokenClient < TravelPay::BaseClient
# HTTP POST call to the VEIS Auth endpoint to get the access token
#
# @return [Faraday::Response]
#
def request_veis_token
auth_url = Settings.travel_pay.veis.auth_url
tenant_id = Settings.travel_pay.veis.tenant_id

response = connection(server_url: auth_url).post("#{tenant_id}/oauth2/token") do |req|
req.headers[:content_type] = 'application/x-www-form-urlencoded'
req.body = URI.encode_www_form(veis_params)
end

response.body['access_token']
end

##
# HTTP POST call to the BTSSS token endpoint to get the access token
#
# @return [Faraday::Response]
#
def request_btsss_token(veis_token, user)
sts_token = request_sts_token(user)

btsss_url = Settings.travel_pay.base_url
client_number = Settings.travel_pay.client_number
correlation_id = SecureRandom.uuid
Rails.logger.debug(message: 'Correlation ID', correlation_id:)

response = connection(server_url: btsss_url).post('api/v1/Auth/access-token') do |req|
req.headers['Authorization'] = "Bearer #{veis_token}"
req.headers['BTSSS-API-Client-Number'] = client_number.to_s
req.headers['X-Correlation-ID'] = correlation_id
req.headers.merge!(claim_headers)
req.body = { authJwt: sts_token }
end

response.body['data']['accessToken']
end

def request_sts_token(user)
return nil if mock_enabled?

host_baseurl = build_host_baseurl({ ip_form: false })
private_key_file = Settings.sign_in.sts_client.key_path
private_key = OpenSSL::PKey::RSA.new(File.read(private_key_file))

assertion = build_sts_assertion(user)
jwt = JWT.encode(assertion, private_key, 'RS256')

# send to sis
response = connection(server_url: host_baseurl).post('/v0/sign_in/token') do |req|
req.params['grant_type'] = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
req.params['assertion'] = jwt
end

response.body['data']['access_token']
end

def build_sts_assertion(user)
service_account_id = Settings.travel_pay.sts.service_account_id
host_baseurl = build_host_baseurl({ ip_form: false })
audience_baseurl = build_host_baseurl({ ip_form: true })
scopes = Settings.travel_pay.sts.scope.blank? ? [] : [Settings.travel_pay.sts.scope]

current_time = Time.now.to_i
jti = SecureRandom.uuid

{
'iss' => host_baseurl,
'sub' => user.email,
'aud' => "#{audience_baseurl}/v0/sign_in/token",
'iat' => current_time,
'exp' => current_time + 300,
'scopes' => scopes,
'service_account_id' => service_account_id,
'jti' => jti,
'user_attributes' => { 'icn' => user.icn }
}
end

private

def veis_params
{
client_id: Settings.travel_pay.veis.client_id,
client_secret: Settings.travel_pay.veis.client_secret,
client_info: 1,
grant_type: 'client_credentials',
resource: Settings.travel_pay.veis.resource
}
end

def build_host_baseurl(config)
env = Settings.vsp_environment
host = Settings.hostname

if env == 'localhost'
if config[:ip_form]
return 'http://127.0.0.1:3000'
else
return 'http://localhost:3000'
end
end

"https://#{host}"
end
end
end
21 changes: 21 additions & 0 deletions modules/travel_pay/app/services/travel_pay/token_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module TravelPay
class TokenService
#
# returns a hash containing the veis_token & btsss_token
#
def get_tokens(current_user)
veis_token = token_client.request_veis_token
btsss_token = token_client.request_btsss_token(veis_token, current_user)

{ veis_token:, btsss_token: }
end

private

def token_client
TravelPay::TokenClient.new
end
end
end
Loading

0 comments on commit 911d7df

Please sign in to comment.