require 'apartment/migrator' module ZuoraConnect module Controllers module Helpers extend ActiveSupport::Concern def authenticate_app_api_request ZuoraConnect::AppInstance.read_master_db do #Skip session for api requests request.session_options[:skip] = true Thread.current[:appinstance] = nil if ZuoraConnect.logger.is_a?(Ougai::Logger); ZuoraConnect.logger.with_fields = {}; end if Rails.logger.is_a?(Ougai::Logger); Rails.logger.with_fields = {}; end if defined?(ElasticAPM) && ElasticAPM.running? if ElasticAPM.respond_to?(:set_label) ElasticAPM.set_label(:trace_id, request.uuid) if defined?(ElasticAPM) && ElasticAPM.running? else ElasticAPM.set_label(:trace_id, request.uuid) if defined?(ElasticAPM) && ElasticAPM.running? end end ZuoraConnect::ZuoraUser.current_user_id = request.headers["Zuora-User-Id"] if request.headers["API-Token"].present? @appinstance = ZuoraConnect::AppInstance.find_by(:api_token => request.headers["API-Token"]) ZuoraConnect.logger.debug("API REQUEST - API token") if @appinstance.present? check_instance elsif ZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST", nil)) && request.headers['zuora-host'].present? zuora_host, zuora_entity_id, zuora_instance_id = [request.headers['zuora-host'], (request.headers['zuora-entity-ids'] || "").gsub('-',''), request.headers['zuora-instance-id']] zuora_host_mapping = {'origin-gateway.sbx.auw2.zuora.com' => 'rest.apisandbox.zuora.com', 'origin-gateway.prod.auw2.zuora.com' => 'rest.zuora.com'} zuora_host = zuora_host_mapping[zuora_host] if zuora_host_mapping.keys.include?(zuora_host) #Validate entity-ids present if zuora_entity_id.blank? render json: {"status": 401, "message": "zuora-entity-ids header was not supplied."}, status: :unauthorized return end #Select with instance id if present. Used where mulitple deployments are done. 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_host, id: zuora_instance_id.to_i) else appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host", entities: [zuora_entity_id], host: zuora_host) end if appinstances.size == 0 render json: {"status": 401, "message": "Missing mapping or no deployment for '#{zuora_host}-#{zuora_entity_id}' ."}, status: :unauthorized return elsif appinstances.size > 1 render json: {"status": 401, "message": "More than one app instance binded to host and entity ids. Please indicate correct instance via 'zuora-instance-id' header", "instances": appinstances.map {|instance| instance.id }.sort }, status: :unauthorized return else @appinstance = appinstances.first check_instance end elsif request.headers.fetch("Authorization", "").include?("Basic ") authenticate_or_request_with_http_basic do |username, password| @appinstance = ZuoraConnect::AppInstance.find_by(:token => password) @appinstance ||= ZuoraConnect::AppInstance.find_by(:api_token => password) ZuoraConnect.logger.debug("API REQUEST - Basic Auth") if @appinstance.present? check_instance end else check_instance end @zuora_user = ZuoraConnect::ZuoraUser.find_by(zuora_user_id: ZuoraConnect::ZuoraUser.current_user_id) end end #API ONLY def check_instance if defined?(@appinstance) && @appinstance.present? if @appinstance.new_session_for_api_requests(:params => params) @appinstance.new_session(:session => @appinstance.data_lookup(:session => session)) end Thread.current[:appinstance] = @appinstance PaperTrail.whodunnit = "API User" if defined?(PaperTrail) ElasticAPM.set_user("API User") if defined?(ElasticAPM) && ElasticAPM.running? return true else response.set_header('WWW-Authenticate', "Basic realm=\"Application\"") render json: {"status": 401, "message": "Access Denied"}, status: :unauthorized return false end end def authenticate_connect_app_request ZuoraConnect::AppInstance.read_master_db do Thread.current[:appinstance] = nil if ZuoraConnect.logger.is_a?(Ougai::Logger); ZuoraConnect.logger.with_fields = {}; end if Rails.logger.is_a?(Ougai::Logger); Rails.logger.with_fields = {}; end if defined?(ElasticAPM) && ElasticAPM.running? if ElasticAPM.respond_to?(:set_label) ElasticAPM.set_label(:trace_id, request.uuid) else ElasticAPM.set_label(:trace_id, request.uuid) end end if ZuoraConnect.configuration.mode == "Production" setup_instance_via_prod_mode else setup_instance_via_dev_mode end return if performed? if !defined?(@appinstance) || @appinstance.blank? render "zuora_connect/static/error_handled", :locals => { :title => "Application state could not be found.", :message => "Please relaunch application." }, :layout => false return 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 locale = (session["#{@appinstance.id}::user::locale"] || "").gsub("_", "-") begin I18n.locale = locale.present? ? locale : @appinstance.locale rescue I18n::InvalidLocale => ex if locale.include?("-") locale = locale.split("-").first retry elsif locale != session["#{@appinstance.id}::user::language"] locale = session["#{@appinstance.id}::user::language"] retry end ZuoraConnect.logger.error(ex) if !ZuoraConnect::AppInstance::IGNORED_LOCALS.include?(ex.locale.to_s.downcase) end if @appinstance.user_timezone.blank? @appinstance.set_timezone(timezone: session["#{@appinstance.id}::user::timezone"], type: :default) end end rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex id = @appinstance.id ZuoraConnect::AppInstance.destroy(id) Apartment::Tenant.drop(id) render "zuora_connect/static/error_handled", :locals => { :title => "Application Setup Error", :message => "Application cannot be run using Zuora Session. Delete old application \ deployment and create new with Zuora Basic or OAuth credentials." }, :layout => false return rescue ZuoraConnect::Exceptions::AccessDenied => ex respond_to do |format| format.html { render "zuora_connect/static/error_handled", :locals => { :title => "Application State Error", :message => ex.message }, status: 401, layout: false } format.js { render "zuora_connect/static/error_handled", :locals => { :title => "Application State Error", :message => ex.message }, status: 401, layout: false } format.json { render json: {'errors' => ex.message}, status: 401 } format.all { render json: ex.message, status: 401 } end return rescue => ex ZuoraConnect.logger.error("UI Authorization Error", ex) respond_to do |format| format.html { render 'zuora_connect/static/error_unhandled', :locals => {:exception => ex, :skip_exception => true}, status: 500, layout: false } format.js { render 'zuora_connect/static/error_unhandled', :locals => {:exception => ex, :skip_exception => true}, status: 500, layout: false} end return end def persist_connect_app_session if @appinstance.present? if defined?(Redis.current) @appinstance.cache_app_instance else session.merge!(@appinstance.save_data) end end end def check_connect_admin!(raise_error: false) if !(session["#{@appinstance.id}::admin"] || @appinstance.zuora_tenant_ids.include?("9")) raise ZuoraConnect::Exceptions::AccessDenied.new("User is not an authorized admin for this application") if raise_error respond_to do |format| format.html { render "zuora_connect/static/error_handled", :locals => { :title => "Unauthorized", :message => "User is not an authorized admin for this application" }, status: 401, :layout => false } format.js { render "zuora_connect/static/error_handled", :locals => { :title => "Unauthorized", :message => "User is not an authorized admin for this application" }, status: 401, :layout => false } format.json { render json: {'errors' => ex.message}, status: 401 } format.all { render json: ex.message, status: 401 } end return end end def check_connect_admin return session["#{@appinstance.id}::admin"] end def zuora_user return @zuora_user end def hallway_integration? return (request.headers['ZuoraCurrentEntity'].present? || cookies['ZuoraCurrentEntity'].present?) end def create_new_instance ZuoraConnect::AppInstance.read_master_db do Thread.current[:appinstance] = nil ZuoraConnect.logger.with_fields = {} if ZuoraConnect.logger.is_a?(Ougai::Logger) Rails.logger.with_fields = {} if Rails.logger.is_a?(Ougai::Logger) if defined?(ElasticAPM) && ElasticAPM.running? && ElasticAPM.respond_to?(:set_label) ElasticAPM.set_label(:trace_id, request.uuid) end zuora_host = request.headers['zuora-host'] zuora_entity_id = (request.headers['zuora-entity-ids'] || '').gsub( '-', '' ).split(',').first # Validate host present if zuora_host.blank? render json: { status: 401, message: 'zuora-host header was not supplied.' }, status: :unauthorized return end # Validate entity-ids present if zuora_entity_id.blank? render json: { status: 401, message: 'zuora-entity-ids header was not supplied.' }, status: :unauthorized return end rest_domain = ZuoraAPI::Login.new(url: "https://#{zuora_host}").rest_domain app_instance_id = ZuoraConnect::AppInstance.where( 'zuora_entity_ids ?& array[:entities] AND zuora_domain = :host', entities: [zuora_entity_id], host: rest_domain ).pluck(:id).first if app_instance_id.present? render json: { status: 409, message: 'Instance already exists.', app_instance_id: app_instance_id }, status: 409 else Apartment::Tenant.switch!("public") retry_count = 3 begin @appinstance = new_instance( next_instance_id, zuora_entity_id, rest_domain, tenant_id: request.headers['zuora-tenant-id'], retry_count: retry_count ) rescue ActiveRecord::RecordNotUnique retry if (retry_count -= 1).positive? return end app_instance_id = @appinstance.id end begin Apartment::Tenant.switch!('public') Apartment::Tenant.create(app_instance_id.to_s) rescue Apartment::TenantExists ZuoraConnect.logger.debug('Tenant Already Exists') end end end private def setup_instance_via_prod_mode zuora_entity_id = request.headers['ZuoraCurrentEntity'] || cookies['ZuoraCurrentEntity'] ZuoraConnect::ZuoraUser.current_user_id = '3' if zuora_entity_id.present? zuora_tenant_id = cookies['Zuora-Tenant-Id'] zuora_user_id = cookies['Zuora-User-Id'] zuora_host = request.headers['HTTP_X_FORWARDED_HOST'] || request.headers['HTTP_ZUORA_HOST'] || 'apisandbox.zuora.com' zuora_details = {'host' => zuora_host, 'user_id' => zuora_user_id, 'tenant_id' => zuora_tenant_id, 'entity_id' => zuora_entity_id} auth_headers = {} #Do we need to refresh session identity if request.headers["Zuora-Auth-Token"].present? zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", bearer_token: request.headers["Zuora-Auth-Token"], entity_id: zuora_entity_id, oauth_session_expires_at: Time.now + 5.minutes ) elsif cookies['ZSession'].present? zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'], entity_id: zuora_entity_id) auth_headers.merge!({'Authorization' => "ZSession-a3N2w #{zuora_client.get_session(prefix: false, auth_type: :basic)}"}) else render "zuora_connect/static/error_handled", :locals => { :title => "Missing Authorization Token", :message => "Zuora 'Zuora-Auth-Token' header and 'ZSession' cookie not present." }, :layout => false return end begin zuora_instance_id = params[:sidebar_launch].to_s.to_bool ? nil : (params[:app_instance_id] || session["appInstance"]) #Identity blank or current entity different different_zsession = session["ZSession"] != cookies['ZSession'] missmatched_entity = session["ZuoraCurrentEntity"] != zuora_entity_id missing_identity = session["ZuoraCurrentIdentity"].blank? if (missing_identity || missmatched_entity || different_zsession) zuora_details.merge!({'identity' => {'different_zsession' => different_zsession, 'missing_identity' => missing_identity, 'missmatched_entity' => missmatched_entity}}) identity, response = zuora_client.rest_call( url: zuora_client.rest_endpoint("identity"), zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id ) if zuora_entity_id != identity['entityId'] if zuora_tenant_id.to_s == "10548" session.clear render "zuora_connect/static/error_handled", :locals => { :title => "Security Testing", :message => "Ya we know it you" }, :layout => false return else raise ZuoraConnect::Exceptions::Error.new("Header entity id does not match identity call entity id.") end end ## # If the ZSession was refreshed, but it's still the same user and they aren't launching from the side bar, # we don't need to continue is_different_user = identity.slice("entityId", "tenantId", "userId", "userProfileId") != (session["ZuoraCurrentIdentity"] || {}).slice("entityId", "tenantId", "userId", "userProfileId") zuora_details["identity"]["entityId"] = identity['entityId'] session["ZuoraCurrentIdentity"] = identity session["ZuoraCurrentEntity"] = identity['entityId'] session["ZSession"] = cookies['ZSession'] if is_different_user || params[:sidebar_launch].to_s.to_bool zuora_instance_id = nil ZuoraConnect.logger.debug("UI Authorization", zuora: zuora_details) client_describe, response = zuora_client.rest_call( url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic, headers: auth_headers, zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id ) session["ZuoraCurrentUserInfo"] = client_describe end end 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.to_i).pluck(:id, :name) else #if app_instance_ids is present then permissions still controlled by connect if params[:app_instance_ids].present? navbar, response = zuora_client.rest_call( url: zuora_client.rest_endpoint("navigation"), zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id ) 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 if url.blank? if navbar['menus'].map {|x| x['label']}.include?('Link Connect Account') render "zuora_connect/static/error_handled", :locals => { :title => "Link Account", :message => "Link Connect account to gain access to application." }, :layout => false return end raise ZuoraConnect::Exceptions::APIError.new(message: "#{app_env} navbar url was blank", response: response) else query_params = CGI.parse(URI.parse(url).query) app_instance_ids = query_params["app_instance_ids"][0] if app_instance_ids.present? begin task_ids = JSON.parse(Base64.urlsafe_decode64(app_instance_ids)) appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name) rescue => ex raise ZuoraConnect::Exceptions::APIError.new(message: "Failure in parsing the navbar urls.", response: response) end end end end 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 zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId'] || request.headers["Zuora-User-Id"] if appinstances.size == 1 ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}") @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first) end # One deployed instance with credentials if defined?(@appinstance) && !@appinstance['zuora_logins'].nil? @zuora_user = ZuoraConnect::ZuoraUser.update_id_response( zuora_user_id, zuora_entity_id, session["ZuoraCurrentIdentity"], @appinstance, session['ZuoraCurrentUserInfo']['permissions'] ) @zuora_user.session = session ZuoraConnect::ZuoraUser.current_user_id = zuora_user_id session["#{@appinstance.id}::user::localUserId"] = @zuora_user.id session["#{@appinstance.id}::user::email"] = session['ZuoraCurrentIdentity']["username"] session["#{@appinstance.id}::user::timezone"] = session['ZuoraCurrentIdentity']["timeZone"] session["#{@appinstance.id}::user::language"] = session['ZuoraCurrentIdentity']["language"] session["#{@appinstance.id}::user::locale"] = session['ZuoraCurrentIdentity']["locale"] session["appInstance"] = @appinstance.id #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}, :layout => false return #We have no deployed instance for this tenant else if ZuoraConnect.configuration.disable_provisioning raise ZuoraConnect::Exceptions::AccessDenied.new("Provisioning is suspended") end #Ensure user can access oauth creation API if !session["ZuoraCurrentUserInfo"]['permissions'].include?("permission.userManagement") Thread.current[:appinstance] = nil session["appInstance"] = nil render "zuora_connect/static/error_handled", :locals => { :title => "Application can only complete its initial setup via platform administrator", :message => "Please contact admin who has user managment permissions in tenant and have them click and finish setup." }, :layout => false return end Apartment::Tenant.switch!("public") retry_count = 3 task_data = {} begin ActiveRecord::Base.transaction do ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE') unless defined?(@appinstance) 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) if appinstances.size > 0 redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}&pos=0" return end end next_id = defined?(@appinstance) ? @appinstance.id : next_instance_id if task_data.blank? user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ') body = { 'userId' => zuora_user_id, 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')], 'customAuthorities' => [], 'additionalInformation' => { 'description' => "This user is for #{user} application.", 'name' => "#{user} API User #{next_id}" } } oauth_response, response = zuora_client.rest_call( method: :post, body: body.to_json, url: zuora_client.rest_endpoint("genesis/clients").gsub('v1/', ''), session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic, headers: auth_headers, zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id ) new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] ) if session["ZuoraCurrentUserInfo"].blank? client_describe, response = new_zuora_client.rest_call( url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer, zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id ) else client_describe = session["ZuoraCurrentUserInfo"] end available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id} task_data = { "id": next_id, "name": client_describe["tenantName"], "mode": "Collections", "status": "Running", ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => { "tenant_type": "Zuora", "username": session["ZuoraCurrentIdentity"]["username"], "url": new_zuora_client.url, "status": "Active", "oauth_client_id": oauth_response['clientId'], "oauth_secret": oauth_response['clientSecret'], "authentication_type": "OAUTH", "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})} }, "tenant_ids": available_entities.map{|e| e['entityId']}.uniq, } end if defined?(@appinstance) @appinstance.zuora_logins = task_data @appinstance.save(:validate => false) else @appinstance = new_instance( next_id, zuora_entity_id, zuora_client.rest_domain, task_data: task_data, retry_count: retry_count ) end end rescue ActiveRecord::RecordNotUnique retry if (retry_count -= 1).positive? return end Apartment::Tenant.switch!("public") begin Apartment::Tenant.create(@appinstance.id.to_s) rescue Apartment::TenantExists => ex ZuoraConnect.logger.debug("Tenant Already Exists") end @appinstance.refresh session["appInstance"] = @appinstance.id end rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex output_xml, input_xml, response = zuora_client.soap_call(errors: [], z_session: false, zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id) do |xml| xml['api'].getUserInfo end final_error = output_xml.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text session.clear if final_error.blank? ZuoraConnect.logger.warn("UI Authorization Error", ex, response: { params: response.body }) elsif final_error != "INVALID_SESSION" ZuoraConnect.logger.warn("UI Authorization Error", ex, response: { params: final_error }) else ZuoraConnect.logger.info("UI Authorization Error", ex, response: { params: final_error }) end redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}&pos=1" return rescue ZuoraAPI::Exceptions::ZuoraAPIError, Exception => ex if ex.message.include?("Referenced User resource(s) not found") && ex.class == ZuoraAPI::Exceptions::ZuoraAPIError locals = {title: "Provisioning Error", message: "New tenants need to be provisioned by API Gateway('#{ex.message}'). Please contact support."} render "zuora_connect/static/error_handled", locals: locals, status: 200, layout: false else session.clear if defined?(ex.response) && ex.response.present? && defined?(ex.response.body) zuora_details.merge!({:error => ex.response.body}) end ZuoraConnect.logger.error("UI Authorization Error", ex, zuora: zuora_details) respond_to do |format| format.html { render "zuora_connect/static/error_unhandled", locals: {exception: ex, skip_exception: true}, layout: false, status: 500 } format.js { render "zuora_connect/static/error_unhandled", locals: {exception: ex, skip_exception: true}, layout: false, status: 500 } end end return end elsif request["data"].present? && (request["connectInstanceId"].present? || /^([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)) session.clear values = JSON.parse(ZuoraConnect::AppInstance.decrypt_response(Base64.urlsafe_decode64(request["data"]))) values.fetch("param_data", {}).each do |k ,v| params[k] = v end session["#{values["appInstance"]}::destroy"] = values["destroy"] session["appInstance"] = values["appInstance"] if values["current_user"] session["#{values["appInstance"]}::admin"] = values["current_user"]["admin"] ? values["current_user"]["admin"] : false session["#{values["appInstance"]}::user::timezone"] = values["current_user"]["timezone"] session["#{values["appInstance"]}::user::locale"] = values["current_user"]["locale"] session["#{values["appInstance"]}::user::email"] = values["current_user"]["email"] end @appinstance = ZuoraConnect::AppInstance.find_by(:id => values["appInstance"].to_i) if @appinstance.blank? if ZuoraConnect.configuration.disable_provisioning raise ZuoraConnect::Exceptions::AccessDenied.new("Provisioning is suspended") end Apartment::Tenant.switch!("public") begin Apartment::Tenant.create(values["appInstance"].to_s) rescue Apartment::TenantExists => ex ZuoraConnect.logger.debug("Tenant Already Exists") end mapped_values = {:api_token => values['api_token'], :token => values['api_token'], :access_token => values["access_token"], :refresh_token => values["refresh_token"], :oauth_expires_at => values["expires"]} @appinstance = ZuoraConnect::AppInstance.new(mapped_values.merge({:id => values["appInstance"].to_i})) @appinstance.save(:validate => false) else mapped_values = {:access_token => values["access_token"], :refresh_token => values["refresh_token"], :oauth_expires_at => values["expires"]} @appinstance.assign_attributes(mapped_values) if @appinstance.access_token_changed? && @appinstance.refresh_token_changed? @appinstance.save(:validate => false) else raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mismatch. Possible tampering with session.") end end else if session["appInstance"].present? @appinstance = ZuoraConnect::AppInstance.find_by(:id => session["appInstance"]) else if ZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST", nil)) render "zuora_connect/application/ldap_login", :layout => false return else raise ZuoraConnect::Exceptions::AccessDenied.new("No application state or session found.") end end end end def next_instance_id min_instance_id = 24_999_999 (ZuoraConnect::AppInstance.all.where("id > #{min_instance_id}").order(id: :desc).limit(1).pluck(:id).first || min_instance_id) + 1 end def new_instance(id, zuora_entity_id, rest_domain, tenant_id: nil, task_data: nil, retry_count: 0) app_instance = ZuoraConnect::AppInstance.new( :id => id, :api_token => generate_token, :token => generate_token, :oauth_expires_at => Time.now + 1000.years, :zuora_domain => rest_domain, :zuora_entity_ids => [zuora_entity_id] ) app_instance[:zuora_tenant_ids] = [tenant_id.to_s] if tenant_id.present? if task_data.nil? # no encryption app_instance['zuora_logins'] = task_data else # kms encrypt app_instance.zuora_logins = task_data end begin app_instance.save(:validate => false) rescue ActiveRecord::RecordNotUnique raise if retry_count > 1 Thread.current[:appinstance] = nil session['appInstance'] = nil render 'zuora_connect/static/error_handled', :locals => { :title => 'Application could not create unique tokens.', :message => 'Please contact support or retry launching application.' }, :layout => false return end app_instance end def generate_token rand(36**64).to_s(36) end end end end