require'apartment/migrator'moduleZuoraConnectmoduleControllersmoduleHelpersextendActiveSupport::Concerndefauthenticate_app_api_request#Skip session for api requestsThread.current[:appinstance]=nilrequest.session_options[:skip]=trueElasticAPM.set_tag(:trace_id,request.uuid)ifdefined?(ElasticAPM)&&ElasticAPM.running?start_time=Time.nowifrequest.headers["API-Token"].present?@appinstance=ZuoraConnect::AppInstance.where(:api_token=>request.headers["API-Token"]).firstZuoraConnect.logger.debug("[#{@appinstance.id}] API REQUEST - API token")if@appinstance.present?check_instanceelsifZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST",nil))zuora_host,zuora_entity_id,zuora_instance_id=[request.headers['zuora-host'],request.headers['zuora-entity-ids'].gsub('-',''),request.headers['zuora-instance-id']]#Validate host presentifzuora_host.blank?renderjson: {"status":401,"message":"zuora-host header was not supplied."},status: :unauthorizedreturnend#Validate entity-ids presentifzuora_entity_id.blank?renderjson: {"status":401,"message":"zuora-entity-ids header was not supplied."},status: :unauthorizedreturnend#Select with instance id if present. Used where mulitple deployments are done.ifzuora_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)elseappinstances=ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host",entities: [zuora_entity_id],host: zuora_host)endifappinstances.size==0renderjson: {"status":401,"message":"Missing mapping or no deployment for '#{zuora_host}-#{zuora_entity_id}' ."},status: :unauthorizedelsifappinstances.size>1renderjson: {"status":401,"message":"More than one app instance binded to host and entity ids. Please indicate correct instance via 'zuora-instance-id' header"},status: :unauthorizedelse@appinstance=appinstances.firstendelsifrequest.headers.fetch("Authorization","").include?("Basic ")authenticate_or_request_with_http_basicdo|username,password|@appinstance=ZuoraConnect::AppInstance.where(:token=>password).first@appinstance||=ZuoraConnect::AppInstance.where(:api_token=>password).firstZuoraConnect.logger.debug("[#{@appinstance.id}] API REQUEST - Basic Auth")if@appinstance.present?check_instanceendelsecheck_instanceendif@appinstance.present?ZuoraConnect.logger.debug("[#{@appinstance.id}] Authenticate App API Request Completed In - #{(Time.now-start_time).round(2)}s")endenddefverify_with_navbarif!session[params[:app_instance_ids]].present?host=request.headers["HTTP_X_FORWARDED_HOST"]zuora_client=ZuoraAPI::Login.new(url: "https://#{host}")menus=zuora_client.get_full_nav(cookies.to_h)["menus"]app=menus.selectdo|item|matches=/(?<=.com\/services\/)(.*?)(?=\?|$)/.match(item["url"])if!matches.blank?matches[0].split("?").first==ENV["DEIS_APP"]endendsession[params[:app_instance_ids]]=app[0]returnapp[0]elsereturnsession[params[:app_instance_ids]]endenddefselect_instancebeginapp=verify_with_navbarurl_tasks=JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(app["url"]).query)["app_instance_ids"][0]))@app_instance_ids=JSON.parse(Base64.urlsafe_decode64(params[:app_instance_ids]))if(url_tasks&@app_instance_ids).size==@app_instance_ids.sizesql="select name,id from zuora_connect_app_instances where id = ANY(ARRAY#{@app_instance_ids})"result=ActiveRecord::Base.connection.execute(sql)@names={}result.eachdo|x|@names[x["id"].to_i]=x["name"]endrender"zuora_connect/static/launch"elserender"zuora_connect/static/invalid_launch_request"endrescue=>exZuoraConnect.logger.debug("Error parsing Instance ID's: #{ex.message}")render"zuora_connect/static/invalid_launch_request"endenddefauthenticate_connect_app_requestElasticAPM.set_tag(:trace_id,request.uuid)ifdefined?(ElasticAPM)&&ElasticAPM.running?Thread.current[:appinstance]=nilifsession["Identity"].blank?&&request.headers['Zuora-Auth-Token'].present?putsrequest.headers['ZuoraCurrentEntity']puts"--------\n\n\n\n\n\n\n\n\n\n\n\n"endifparams[:app_instance_ids].present?&&!params[:app_instance_id].present?beginapp_instance_ids=JSON.parse(Base64.urlsafe_decode64(params[:app_instance_ids]))ifapp_instance_ids.length==1verify_with_navbarinstances=JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(session[params[:app_instance_ids]]["url"]).query)["app_instance_ids"][0]))ifinstances.include?(app_instance_ids[0])@appinstance=ZuoraConnect::AppInstance.find(app_instance_ids[0])@appinstance.new_session(session: {})@appinstance.cache_app_instancesession["appInstance"]=app_instance_ids[0]elseZuoraConnect.logger.error("Launch Error: Param Instance didnt match session data")render"zuora_connect/static/invalid_launch_request"returnendelseselect_instancereturnendrescue=>exZuoraConnect.logger.error(ex)render"zuora_connect/static/invalid_launch_request"returnendelsifparams[:app_instance_ids].present?&¶ms[:app_instance_id].present?begininstances=JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(session[params[:app_instance_ids]]["url"]).query)["app_instance_ids"][0]))ifinstances.include?(params[:app_instance_id].to_i)@appinstance=ZuoraConnect::AppInstance.find(params[:app_instance_id].to_i)@appinstance.new_session(session: {})@appinstance.cache_app_instancesession["appInstance"]=params[:app_instance_id].to_ielserender"zuora_connect/static/invalid_launch_request"returnendrescue=>exZuoraConnect.logger.error(ex)render"zuora_connect/static/invalid_launch_request"returnendendstart_time=Time.nowifZuoraConnect.configuration.mode=="Production"ifrequest["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_dataelsesetup_instance_via_sessionendelsesetup_instance_via_dev_modeend#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.ifparams[:controller]!='zuora_connect/api/v1/app_instance'&¶ms[:action]!='drop'if@appinstance.new_session_for_ui_requests(:params=>params)@appinstance.new_session(:session=>@appinstance.data_lookup(:session=>session))endendifsession["#{@appinstance.id}::user::email"].present?ElasticAPM.set_user(session["#{@appinstance.id}::user::email"])ifdefined?(ElasticAPM)&&ElasticAPM.running?PaperTrail.whodunnit=session["#{@appinstance.id}::user::email"]ifdefined?(PaperTrail)endbeginI18n.locale=session["#{@appinstance.id}::user::locale"]?session["#{@appinstance.id}::user::locale"]:@appinstance.localerescueI18n::InvalidLocale=>exZuoraConnect.logger.error(ex)if!ZuoraConnect::AppInstance::IGNORED_LOCALS.include?(ex.locale.to_s.downcase)endTime.zone=session["#{@appinstance.id}::user::timezone"]?session["#{@appinstance.id}::user::timezone"]:@appinstance.timezoneZuoraConnect.logger.debug("[#{@appinstance.blank??"N/A":@appinstance.id}] Authenticate App Request Completed In - #{(Time.now-start_time).round(2)}s")enddefpersist_connect_app_sessionif@appinstance.present?ifdefined?(Redis.current)@appinstance.cache_app_instanceelsesession.merge!(@appinstance.save_data)endendenddefcheck_connect_admin!raiseZuoraConnect::Exceptions::AccessDenied.new("User is not an authorized admin for this application")if!session["#{@appinstance.id}::admin"]enddefcheck_connect_adminreturnsession["#{@appinstance.id}::admin"]endprivatedefsetup_instance_via_datasession.clearvalues=JSON.parse(ZuoraConnect::AppInstance.decrypt_response(Base64.urlsafe_decode64(request["data"])))ifvalues["param_data"]values["param_data"].eachdo|k,v|params[k]=vendendsession["#{values["appInstance"]}::destroy"]=values["destroy"]session["appInstance"]=values["appInstance"]ifvalues["current_user"]session["#{values["appInstance"]}::admin"]=values["current_user"]["admin"]?values["current_user"]["admin"]:falsesession["#{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"]endZuoraConnect.logger.debug({msg: 'Setup values',connect: values})ifRails.env!="production"@appinstance=ZuoraConnect::AppInstance.where(:id=>values["appInstance"].to_i).firstif@appinstance.blank?Apartment::Tenant.switch!("public")beginApartment::Tenant.create(values["appInstance"].to_s)rescueApartment::TenantExists=>exZuoraConnect.logger.debug("Tenant Already Exists")end@appinstance=ZuoraConnect::AppInstance.new(:api_token=>values[:api_token],:id=>values["appInstance"].to_i,:access_token=>values["access_token"].blank??values["user"]:values["access_token"],:token=>values["refresh_token"],:refresh_token=>values["refresh_token"].blank??values["key"]:values["refresh_token"],:oauth_expires_at=>values["expires"])@appinstance.save(:validate=>false)else@appinstance.access_token=values["access_token"]if!values["access_token"].blank?&&@appinstance.access_token!=values["access_token"]@appinstance.refresh_token=values["refresh_token"]if!values["refresh_token"].blank?&&@appinstance.refresh_token!=values["refresh_token"]@appinstance.oauth_expires_at=values["expires"]if!values["expires"].blank?@appinstance.api_token=values["api_token"]if!values["api_token"].blank?&&@appinstance.api_token!=values["api_token"]if@appinstance.access_token_changed?&&@appinstance.refresh_token_changed?@appinstance.save(:validate=>false)elseraiseZuoraConnect::Exceptions::AccessDenied.new("Authorization mistmatch. Possible tampering")endendenddefsetup_instance_via_sessionifsession["appInstance"].present?@appinstance=ZuoraConnect::AppInstance.where(:id=>session["appInstance"]).firstelseraiseZuoraConnect::Exceptions::SessionInvalid.new("Session Blank -- Relaunch Application")endenddefsetup_instance_via_dev_modesession["appInstance"]=ZuoraConnect.configuration.dev_mode_appinstanceuser=ZuoraConnect.configuration.dev_mode_userkey=ZuoraConnect.configuration.dev_mode_passvalues={:user=>user,:key=>key,:appinstance=>session["appInstance"]}@appinstance=ZuoraConnect::AppInstance.where(:id=>values[:appinstance].to_i).firstif@appinstance.blank?Apartment::Tenant.switch!("public")beginApartment::Tenant.create(values[:appinstance].to_s)rescueApartment::TenantExists=>exApartment::Tenant.drop(values[:appinstance].to_s)retryend@appinstance=ZuoraConnect::AppInstance.new(:id=>values[:appinstance].to_i,:access_token=>values[:user],:refresh_token=>values[:key],:token=>"#{values[:key]}#{values[:key]}",:api_token=>"#{values[:key]}#{values[:key]}")@appinstance.save(:validate=>false)endif@appinstance.access_token.blank?||@appinstance.refresh_token.blank?||@appinstance.token.blank?||@appinstance.api_token.blank?@appinstance.update_attributes!(:access_token=>values["user"],:refresh_token=>values["key"],:token=>"#{values[:key]}#{values[:key]}",:api_token=>"#{values[:key]}#{values[:key]}")endsession["#{@appinstance.id}::admin"]=ZuoraConnect.configuration.dev_mode_adminend#API ONLYdefcheck_instanceifdefined?(@appinstance)&&@appinstance.present?if@appinstance.new_session_for_api_requests(:params=>params)@appinstance.new_session(:session=>@appinstance.data_lookup(:session=>session))endThread.current[:appinstance]=@appinstancePaperTrail.whodunnit="API User"ifdefined?(PaperTrail)ElasticAPM.set_user("API User")ifdefined?(ElasticAPM)&&ElasticAPM.running?returntrueelseresponse.set_header('WWW-Authenticate',"Basic realm=\"Application\"")#render json: {"status": 401, "message": "Access Denied"}, status: :unauthorizedrenderhtml: "HTTP Basic: Access denied.\n",status: :unauthorizedendendendendend