module ZuoraConnect::Controllers::Helpers
def authenticate_connect_app_request
def authenticate_connect_app_request
ElasticAPM.set_tag(:trace_id, request.uuid) if defined?(ElasticAPM) && ElasticAPM.running?
Thread.current[:appinstance] = nil
if request.headers['Zuora-Auth-Token'].present?
#Do we need to refresh session identity
zuora_host = request.headers["HTTP_X_FORWARDED_HOST"] || "apisandbox.zuora.com"
if request.headers["Zuora-Auth-Token"].present?
zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", bearer_token: request.headers["Zuora-Auth-Token"], oauth_session_expires_at: Time.now + 5.minutes )
else
zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'])
end
zuora_entity_id = request.headers['ZuoraCurrentEntity']
zuora_instance_id = params[:sidebar_launch].to_bool ? nil : (params[:app_instance_id] || session["appInstance"])
#Identity blank or current entity different
if (session["ZuoraCurrentIdentity"].blank? || session["ZuoraCurrentEntity"] != zuora_entity_id)
begin
identity, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("identity"))
session["ZuoraCurrentIdentity"] = identity
session["ZuoraCurrentEntity"] = identity['entityId']
raise ZuoraConnect::Exceptions::Error.new("Header entity id, '#{zuora_entity_id}' does not match identity call entity id, '#{identity['entityId']}'.") if zuora_entity_id != identity['entityId']
rescue => ex
ZuoraConnect.logger.error(ex)
render "zuora_connect/static/invalid_launch_request", :locals => {:exception => ex}
return
end
end
#Find matching app instances.
if zuora_instance_id.present?
appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host AND id = :id", entities: [zuora_entity_id], host: zuora_client.rest_domain, id: zuora_instance_id).pluck(:id, :name)
else
#if app_instance_ids is present then permissions still controlled by connect
if params[:app_instance_ids].present?
begin
navbar, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("navigation"))
urls = navbar['menus'].map {|x| x['url']}
app_env = ENV["DEIS_APP"] || "xyz123"
url = urls.compact.select {|url| File.basename(url).start_with?(app_env + '?')}.first
task_ids = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(url).query)["app_instance_ids"][0]))
appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name)
rescue => ex
ZuoraConnect.logger.error(ex)
render "zuora_connect/static/invalid_launch_request", :locals => {:exception => ex}
return
end
else
appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host", entities: [zuora_entity_id], host: zuora_client.rest_domain).pluck(:id, :name)
end
end
zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId']
#One deployed instance
if appinstances.size == 1
ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
#Add user/update
user = ZuoraConnect::ZuoraUser.where(:zuora_user_id => zuora_user_id).first
if user.present?
ZuoraConnect.logger.debug("Current zuora user #{zuora_user_id}")
if user.updated_at < Time.now - 1.day
user.zuora_identity_response[zuora_entity_id] = session["ZuoraCurrentIdentity"]
user.save!
end
else
ZuoraConnect.logger.debug("New zuora user object for #{zuora_user_id}")
user = ZuoraConnect::ZuoraUser.create!(:zuora_user_id => zuora_user_id, :zuora_identity_response => {zuora_entity_id => session["ZuoraCurrentIdentity"]})
end
#Update access if admin in tenant
if session["ZuoraCurrentIdentity"]['platformRole'] == 'ADMIN' && !user.app_permissions['access'].to_bool
user.app_permissions['access'] = true
user.save!
end
#If user has has access to application
if user.app_permissions['access'].to_bool || !ZuoraConnect.configuration.app_access_permissions
session["appInstance"] = appinstances.to_h.keys.first
else
Thread.current[:appinstance] = nil
session["appInstance"] = nil
admin_users = ZuoraConnect::ZuoraUser.select("zuora_identity_response #>> '{#{zuora_entity_id},username}' as username").where("zuora_identity_response #>> :selector = 'ADMIN' ", :selector => "{#{zuora_entity_id},platformRole}")
render "zuora_connect/static/permission_error", :locals => {:admins => admin_users}
return
end
#We have multiple, user must pick
elsif appinstances.size > 1
ZuoraConnect.logger.debug("User must select instance. #{@names}")
render "zuora_connect/static/launch", :locals => {:names => appinstances.to_h}
return
else
begin
#Ensure user can access oauth creation API
if session["ZuoraCurrentIdentity"]['platformRole'] != 'ADMIN'
raise ZuoraConnect::Exceptions::Error.new("User is not admin, workflow cannot be deployed.")
end
body = {
'clientId' => SecureRandom.uuid,
'clientSecret' => SecureRandom.hex(10),
'userId' => zuora_user_id,
'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
'customAuthorities' => [],
'additionalInformation' => {
'description' => 'This user is for workflow application.',
'name' => 'Workflow API User'
}
}
oauth_response, response = zuora_client.rest_call(method: :post, body: body.to_json, url: zuora_client.rest_endpoint("genesis/clients").gsub('v1/', ''), session_type: :bearer)
new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
client_describe, response = new_zuora_client.rest_call(body: body.to_json, url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
Apartment::Tenant.switch!("public")
next_id = (ZuoraConnect::AppInstance.all.where(:access_token => nil).order(id: :desc).limit(1).pluck(:id).first || 24999999) + 1
begin
Apartment::Tenant.create(next_id.to_s)
rescue Apartment::TenantExists => ex
ZuoraConnect.logger.debug("Tenant Already Exists")
end
task_data = {
"id": next_id,
"name": client_describe["tenantName"],
"mode": "Collections",
"status": "Running",
"target_login": {
"tenant_type": "Zuora",
"username": session["ZuoraCurrentIdentity"]["username"],
"url": new_zuora_client.url,
"status": "Active",
"oauth_client_id": body['clientId'],
"oauth_secret": body['clientSecret'],
"authentication_type": "OAUTH",
"entities": client_describe["accessibleEntities"].map {|e| e.merge({'displayName' => client_describe["tenantName"]})} #needs work
},
"tenant_ids": client_describe["accessibleEntities"].map{|e| e['entityId'] }.push(client_describe["tenantId"]).uniq,
}
appinstance = ZuoraConnect::AppInstance.new(:id => next_id, :zuora_logins => task_data.to_json, :oauth_expires_at => Time.now + 1000.years)
appinstance.save(:validate => false)
session["appInstance"] = appinstance.id
rescue => ex
ZuoraConnect.logger.error(ex)
render "zuora_connect/static/invalid_launch_request", :locals => {:exception => ex}
return
end
end
end
start_time = Time.now
if ZuoraConnect.configuration.mode == "Production"
if request["data"] && /^([A-Za-z0-9+\/\-\_]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/.match(request["data"].to_s)
setup_instance_via_data
else
setup_instance_via_session
end
else
setup_instance_via_dev_mode
end
#Call .data_lookup with the current session to retrieve session. In some cases session may be stored/cache in redis
#so data lookup provides a model method that can be overriden per app.
if params[:controller] != 'zuora_connect/api/v1/app_instance' && params[:action] != 'drop'
if @appinstance.new_session_for_ui_requests(:params => params)
@appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
end
end
if session["#{@appinstance.id}::user::email"].present?
ElasticAPM.set_user(session["#{@appinstance.id}::user::email"]) if defined?(ElasticAPM) && ElasticAPM.running?
PaperTrail.whodunnit = session["#{@appinstance.id}::user::email"] if defined?(PaperTrail)
end
begin
I18n.locale = session["#{@appinstance.id}::user::locale"] ? session["#{@appinstance.id}::user::locale"] : @appinstance.locale
rescue I18n::InvalidLocale => ex
ZuoraConnect.logger.error(ex) if !ZuoraConnect::AppInstance::IGNORED_LOCALS.include?(ex.locale.to_s.downcase)
end
Time.zone = session["#{@appinstance.id}::user::timezone"] ? session["#{@appinstance.id}::user::timezone"] : @appinstance.timezone
ZuoraConnect.logger.debug("[#{@appinstance.blank? ? "N/A" : @appinstance.id}] Authenticate App Request Completed In - #{(Time.now - start_time).round(2)}s")
end