zuora_connect 2.0.5zj → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/config/routes.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  ZuoraConnect::Engine.routes.draw do
2
2
  get '/health' => 'static#health'
3
3
  get '/internal/data' => 'static#metrics'
4
+ get '/invalid_session' => 'static#session_error', :as => :invalid_session
5
+ get '/invalid_instance' => "static#invalid_app_instance_error", :as => :invalid_instance
4
6
  post '/initialize_app' => 'static#initialize_app'
5
7
 
6
8
  namespace :api do
@@ -2,7 +2,7 @@ module ZuoraConnect
2
2
  class RequestIdMiddleware
3
3
  mattr_accessor :request_id
4
4
  mattr_accessor :zuora_request_id
5
- mattr_accessor :zuora_rest_domain
5
+
6
6
  def initialize(app)
7
7
  @app = app
8
8
  end
@@ -7,7 +7,7 @@ module Resque
7
7
  module Plugins
8
8
  module CustomLogger
9
9
  def before_perform(*args)
10
- Rails.logger.with_fields = { trace_id: SecureRandom.uuid, name: "RailsWorker"} if Rails.logger.class.to_s == 'Ougai::Logger' && ZuoraConnect.configuration.json_logging
10
+ Rails.logger.with_fields = { trace_id: SecureRandom.uuid, name: "RailsWorker"} if Rails.logger.class.to_s == 'Ougai::Logger'
11
11
  case args.class.to_s
12
12
  when "Array"
13
13
  if args.first.class == Hash
data/lib/zuora_connect.rb CHANGED
@@ -2,6 +2,7 @@ require 'zuora_connect/configuration'
2
2
  require "zuora_connect/engine"
3
3
  require 'zuora_connect/exceptions'
4
4
  require 'zuora_connect/controllers/helpers'
5
+ require 'zuora_connect/views/helpers'
5
6
  require 'zuora_connect/railtie'
6
7
  require 'resque/additions'
7
8
  require 'resque/dynamic_queues'
@@ -24,49 +25,30 @@ module ZuoraConnect
24
25
  else
25
26
  @logger ||= custom_logger(name: "Connect", level: Rails.logger.level)
26
27
  end
27
- end
28
+ end
28
29
 
29
30
  def custom_logger(name: "", level: Rails.logger.present? ? Rails.logger.level : MonoLogger::INFO, type: :ougai)
30
31
  #puts name + ' - ' + {Logger::WARN => 'Logger::WARN', Logger::ERROR => 'Logger::ERROR', Logger::DEBUG => 'Logger::DEBUG', Logger::INFO => 'Logger::INFO' }[level] + ' - '
31
32
  if type == :ougai
32
33
  require 'ougai'
33
- require "ougai/formatters/customizable"
34
34
  #logger = Ougai::Logger.new(MonoLogger.new(STDOUT))
35
- logger = Ougai::Logger.new(STDOUT)
35
+ logger = Ougai::Logger.new(STDOUT)
36
+ logger.formatter = Ougai::Formatters::ConnectFormatter.new(name)
36
37
  logger.level = level
37
- if ZuoraConnect.configuration.json_logging
38
- logger.formatter = Ougai::Formatters::ConnectFormatter.new(name)
39
- logger.before_log = lambda do |data|
40
- data[:trace_id] = ZuoraConnect::RequestIdMiddleware.request_id if ZuoraConnect::RequestIdMiddleware.request_id.present?
41
- data[:zuora_trace_id] = ZuoraConnect::RequestIdMiddleware.zuora_request_id if ZuoraConnect::RequestIdMiddleware.zuora_request_id.present?
42
- #data[:traces] = {amazon_id: data[:trace_id], zuora_id: data[:zuora_trace_id]}
43
- if !['ElasticAPM', 'ResqueScheduler', 'ResquePool', 'Resque', 'Makara'].include?(name)
44
- if Thread.current[:appinstance].present? && Thread.current[:appinstance].id.present?
45
- data[:app_instance_id] = Thread.current[:appinstance].id
46
- logitems = Thread.current[:appinstance].logitems
47
- if logitems.present? && logitems.class == Hash
48
- data[:tenant_ids] = logitems[:tenant_ids] if logitems[:tenant_ids].present?
49
- data[:organization] = logitems[:organization] if logitems[:organization].present?
50
- end
38
+ logger.before_log = lambda do |data|
39
+ data[:trace_id] = ZuoraConnect::RequestIdMiddleware.request_id if ZuoraConnect::RequestIdMiddleware.request_id.present?
40
+ data[:zuora_trace_id] = ZuoraConnect::RequestIdMiddleware.zuora_request_id if ZuoraConnect::RequestIdMiddleware.zuora_request_id.present?
41
+ #data[:traces] = {amazon_id: data[:trace_id], zuora_id: data[:zuora_trace_id]}
42
+ if !['ElasticAPM', 'ResqueScheduler', 'ResquePool', 'Resque', 'Makara'].include?(name)
43
+ if Thread.current[:appinstance].present?
44
+ data[:app_instance_id] = Thread.current[:appinstance].id
45
+ logitems = Thread.current[:appinstance].logitems
46
+ if logitems.present? && logitems.class == Hash
47
+ data[:tenant_ids] = logitems[:tenant_ids] if logitems[:tenant_ids].present?
48
+ data[:organization] = logitems[:organization] if logitems[:organization].present?
51
49
  end
52
50
  end
53
51
  end
54
- else
55
- logger.formatter = Ougai::Formatters::Customizable.new(
56
- format_err: proc do |data|
57
- next nil unless data.key?(:err)
58
- err = data.delete(:err)
59
- " #{err[:name]} (#{err[:message]})\n #{err[:stack]}"
60
- end,
61
- format_data: proc do |data|
62
- format('%s %s: %s', 'DATA'.ljust(6), Time.now.strftime('%FT%T.%6NZ'), "#{data.to_json}") if data.present?
63
- end,
64
- format_msg: proc do |severity, datetime, _progname, data|
65
- msg = data.delete(:msg)
66
- format('%s %s: %s', severity.ljust(6), datetime, msg)
67
- end
68
- )
69
- logger.formatter.datetime_format = '%FT%T.%6NZ'
70
52
  end
71
53
  else
72
54
  logger = MonoLogger.new(STDOUT)
@@ -76,39 +58,40 @@ module ZuoraConnect
76
58
  msg = JSON.parse(msg)
77
59
  rescue JSON::ParserError => ex
78
60
  end
79
- if ZuoraConnect.configuration.json_logging
80
- require 'json'
81
- store = {
82
- name: name,
83
- level: serverity,
84
- timestamp: datetime.strftime('%FT%T.%6NZ'),
85
- pid: Process.pid,
86
- message: name == "ActionMailer" ? msg.strip : msg
87
- }
88
- if !['ElasticAPM', 'ResqueScheduler', 'ResquePool','Resque', 'Makara'].include?(name)
89
- if Thread.current[:appinstance].present? && Thread.current[:appinstance].id.present?
90
- store[:app_instance_id] = Thread.current[:appinstance].id
91
- logitems = Thread.current[:appinstance].logitems
92
- if logitems.present? && logitems.class == Hash
93
- store[:tenant_ids] = logitems[:tenant_ids] if logitems[:tenant_ids].present?
94
- store[:organization] = logitems[:organization] if logitems[:organization].present?
95
- end
61
+
62
+ require 'json'
63
+ store = {
64
+ name: name,
65
+ level: serverity,
66
+ timestamp: datetime.strftime('%FT%T.%6NZ'),
67
+ pid: Process.pid,
68
+ message: name == "ActionMailer" ? msg.strip : msg
69
+ }
70
+ if !['ElasticAPM', 'ResqueScheduler', 'ResquePool','Resque', 'Makara'].include?(name)
71
+ if Thread.current[:appinstance].present?
72
+ store[:app_instance_id] = Thread.current[:appinstance].id
73
+ logitems = Thread.current[:appinstance].logitems
74
+ if logitems.present? && logitems.class == Hash
75
+ store[:tenant_ids] = logitems[:tenant_ids] if logitems[:tenant_ids].present?
76
+ store[:organization] = logitems[:organization] if logitems[:organization].present?
96
77
  end
97
78
  end
98
- JSON.dump(store) + "\n"
99
- else
100
- format('%s %s: %s', serverity.ljust(6), datetime, msg) + "\n"
101
79
  end
80
+ JSON.dump(store) + "\n"
102
81
  end
103
82
  end
104
83
  return logger
105
- end
84
+ end
106
85
  end
107
86
 
108
87
  module Controllers
109
88
  autoload :Helpers, 'zuora_connect/controllers/helpers'
110
89
  end
111
90
 
91
+ module Views
92
+ ActionView::Base.send(:include, Helpers)
93
+ end
94
+
112
95
  def self.configuration
113
96
  @configuration ||= Configuration.new
114
97
  end
@@ -146,23 +129,23 @@ module ZuoraConnect
146
129
  }
147
130
  when 'test'
148
131
  defaults = {
149
- active: false,
132
+ active: false,
150
133
  disable_send: true
151
134
  }
152
135
  end
153
136
 
154
137
  defaults.merge!({
155
138
  disable_start_message: true,
156
- pool_size: 1,
157
- transaction_max_spans: 500,
158
- ignore_url_patterns: ['^\/admin\/resque.*', '^\/admin\/redis.*', '^\/admin\/peek.*', '^\/peek.*'],
139
+ pool_size: 1,
140
+ transaction_max_spans: 500,
141
+ ignore_url_patterns: ['^\/admin\/resque.*', '^\/admin\/redis.*', '^\/admin\/peek.*', '^\/peek.*'],
159
142
  verify_server_cert: false,
160
143
  log_level: Logger::INFO,
161
144
  service_name: ENV['DEIS_APP'].present? ? ENV['DEIS_APP'] : Rails.application.class.parent_name,
162
145
  logger: ZuoraConnect.custom_logger(name: "ElasticAPM", level: MonoLogger::WARN)
163
146
  })
164
147
  defaults.merge!({disable_send: true}) if defined?(Rails::Console)
165
-
148
+
166
149
  return defaults
167
150
  end
168
151
  end
@@ -3,11 +3,11 @@ module ZuoraConnect
3
3
 
4
4
  attr_accessor :default_locale, :default_time_zone, :url, :mode, :delayed_job,:private_key, :additional_apartment_models
5
5
 
6
- attr_accessor :enable_metrics, :telegraf_endpoint, :telegraf_debug, :custom_prometheus_update_block, :silencer_resque_finish, :blpop_queue
6
+ attr_accessor :enable_metrics, :telegraf_endpoint, :telegraf_debug, :custom_prometheus_update_block, :silencer_resque_finish, :blpop_queue, :app_access_permissions
7
7
 
8
8
  attr_accessor :oauth_client_id, :oauth_client_secret, :oauth_client_redirect_uri
9
9
 
10
- attr_accessor :dev_mode_logins, :dev_mode_options, :dev_mode_mode, :dev_mode_appinstance, :dev_mode_user, :dev_mode_pass, :dev_mode_admin, :dev_mode_secret_access_key,:dev_mode_access_key_id,:aws_region, :s3_bucket_name, :s3_folder_name, :json_logging
10
+ attr_accessor :dev_mode_logins, :dev_mode_options, :dev_mode_mode, :dev_mode_appinstance, :dev_mode_user, :dev_mode_pass, :dev_mode_admin, :dev_mode_secret_access_key,:dev_mode_access_key_id,:aws_region, :s3_bucket_name, :s3_folder_name
11
11
 
12
12
  def initialize
13
13
  @default_locale = :en
@@ -19,6 +19,7 @@ module ZuoraConnect
19
19
  @additional_apartment_models = []
20
20
  @silencer_resque_finish = true
21
21
  @blpop_queue = false
22
+ @app_access_permissions = false
22
23
 
23
24
  # Setting the app name for telegraf write
24
25
  @enable_metrics = false
@@ -42,7 +43,6 @@ module ZuoraConnect
42
43
  @aws_region = "us-west-2"
43
44
  @s3_bucket_name = "rbm-apps"
44
45
  @s3_folder_name = Rails.application.class.parent_name
45
- @json_logging = Rails.env.to_s == 'development' ? false : true
46
46
  end
47
47
 
48
48
  def private_key
@@ -16,7 +16,7 @@ module ZuoraConnect
16
16
  ZuoraConnect.logger.debug("[#{@appinstance.id}] API REQUEST - API token") if @appinstance.present?
17
17
  check_instance
18
18
  elsif ZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST", nil))
19
- zuora_host, zuora_entity_id, zuora_instance_id = [request.headers['zuora-host'], (request.headers['zuora-entity-ids'] || "").gsub('-',''), request.headers['zuora-instance-id']]
19
+ zuora_host, zuora_entity_id, zuora_instance_id = [request.headers['zuora-host'], request.headers['zuora-entity-ids'].gsub('-',''), request.headers['zuora-instance-id']]
20
20
 
21
21
  #Validate host present
22
22
  if zuora_host.blank?
@@ -37,24 +37,21 @@ module ZuoraConnect
37
37
 
38
38
  if appinstances.size == 0
39
39
  render json: {"status": 401, "message": "Missing mapping or no deployment for '#{zuora_host}-#{zuora_entity_id}' ."}, status: :unauthorized
40
- return
41
40
  elsif appinstances.size > 1
42
41
  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"}, status: :unauthorized
43
- return
44
42
  else
45
43
  @appinstance = appinstances.first
46
- check_instance
47
44
  end
48
-
49
- elsif request.headers.fetch("Authorization", "").include?("Basic ")
45
+
46
+ else #if request.headers.fetch("Authorization", "").include?("Basic ")
50
47
  authenticate_or_request_with_http_basic do |username, password|
51
48
  @appinstance = ZuoraConnect::AppInstance.where(:token => password).first
52
49
  @appinstance ||= ZuoraConnect::AppInstance.where(:api_token => password).first
53
50
  ZuoraConnect.logger.debug("[#{@appinstance.id}] API REQUEST - Basic Auth") if @appinstance.present?
54
51
  check_instance
55
52
  end
56
- else
57
- check_instance
53
+ # else
54
+ # check_instance
58
55
  end
59
56
 
60
57
  if @appinstance.present?
@@ -62,253 +59,175 @@ module ZuoraConnect
62
59
  end
63
60
  end
64
61
 
65
- #API ONLY
66
- def check_instance
67
- if defined?(@appinstance) && @appinstance.present?
68
- if @appinstance.new_session_for_api_requests(:params => params)
69
- @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
70
- end
71
- Thread.current[:appinstance] = @appinstance
72
- PaperTrail.whodunnit = "API User" if defined?(PaperTrail)
73
- ElasticAPM.set_user("API User") if defined?(ElasticAPM) && ElasticAPM.running?
74
- return true
75
- else
76
- response.set_header('WWW-Authenticate', "Basic realm=\"Application\"")
77
- render json: {"status": 401, "message": "Access Denied"}, status: :unauthorized
78
- return false
79
- end
80
- end
81
-
82
62
  def authenticate_connect_app_request
83
63
  ElasticAPM.set_tag(:trace_id, request.uuid) if defined?(ElasticAPM) && ElasticAPM.running?
84
64
  Thread.current[:appinstance] = nil
85
- start_time = Time.now
86
65
 
87
- if ZuoraConnect.configuration.mode == "Production"
88
- zuora_entity_id = request.headers['ZuoraCurrentEntity'] || cookies['ZuoraCurrentEntity']
66
+ if request.headers['ZuoraCurrentEntity'].present?
67
+
68
+ #Do we need to refresh session identity
69
+ zuora_host = request.headers["HTTP_X_FORWARDED_HOST"] || "apisandbox.zuora.com"
70
+ if request.headers["Zuora-Auth-Token"].present?
71
+ zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", bearer_token: request.headers["Zuora-Auth-Token"], oauth_session_expires_at: Time.now + 5.minutes )
72
+ elsif cookies['ZSession'].present?
73
+ zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'])
74
+ else
75
+ raise ZuoraConnect::Exceptions::Error.new("Neither the ZSession cookie nor the Zuora-Auth-Token are present in payload.")
76
+ end
77
+ zuora_entity_id = request.headers['ZuoraCurrentEntity']
78
+ zuora_instance_id = params[:sidebar_launch].to_bool ? nil : (params[:app_instance_id] || session["appInstance"])
89
79
 
90
- if zuora_entity_id.present?
91
- zuora_tenant_id = cookies['Zuora-Tenant-Id']
92
- zuora_user_id = cookies['Zuora-User-Id']
93
- zuora_host = request.headers["HTTP_X_FORWARDED_HOST"] || "apisandbox.zuora.com"
80
+ #Identity blank or current entity different
81
+ if (session["ZuoraCurrentIdentity"].blank? || session["ZuoraCurrentEntity"] != zuora_entity_id)
82
+ begin
83
+ identity, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("identity"))
84
+ session["ZuoraCurrentIdentity"] = identity
85
+ session["ZuoraCurrentEntity"] = identity['entityId']
94
86
 
95
- zuora_details = {'host' => zuora_host, 'user_id' => zuora_user_id, 'tenant_id' => zuora_tenant_id, 'entity_id' => zuora_entity_id}
87
+ 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']
88
+ rescue => ex
89
+ ZuoraConnect.logger.error(ex)
90
+ render "zuora_connect/static/invalid_launch_request", :locals => {:exception => ex}
91
+ return
92
+ end
93
+ end
96
94
 
97
- #Do we need to refresh session identity
98
- if request.headers["Zuora-Auth-Token"].present?
99
- zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", bearer_token: request.headers["Zuora-Auth-Token"], oauth_session_expires_at: Time.now + 5.minutes )
100
- elsif cookies['ZSession'].present?
101
- zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'])
95
+ #Find matching app instances.
96
+ if zuora_instance_id.present?
97
+ 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)
98
+ else
99
+ #if app_instance_ids is present then permissions still controlled by connect
100
+ if params[:app_instance_ids].present?
101
+ begin
102
+ navbar, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("navigation"))
103
+ urls = navbar['menus'].map {|x| x['url']}
104
+ app_env = ENV["DEIS_APP"] || "xyz123"
105
+ url = urls.compact.select {|url| File.basename(url).start_with?(app_env + '?')}.first
106
+ task_ids = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(url).query)["app_instance_ids"][0]))
107
+
108
+ appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name)
109
+ rescue => ex
110
+ ZuoraConnect.logger.error(ex)
111
+ render "zuora_connect/static/invalid_launch_request", :locals => {:exception => ex}
112
+ return
113
+ end
102
114
  else
103
- render "zuora_connect/static/error_handled", :locals => {
104
- :title => "Missing Authorization Token",
105
- :message => "Zuora 'Zuora-Auth-Token' header and 'ZSession' cookie not present."
106
- }, :layout => false
107
- return
115
+ 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)
108
116
  end
117
+ end
109
118
 
110
- begin
111
- zuora_instance_id = params[:sidebar_launch].to_s.to_bool ? nil : (params[:app_instance_id] || session["appInstance"])
112
-
113
- #Identity blank or current entity different
114
- different_zsession = session["ZSession"] != cookies['ZSession']
115
- missmatched_entity = session["ZuoraCurrentEntity"] != zuora_entity_id
116
- missing_identity = session["ZuoraCurrentIdentity"].blank?
119
+ zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId']
117
120
 
118
- if (missing_identity || missmatched_entity || different_zsession)
119
- zuora_details.merge!({'identity' => {'different_zsession' => different_zsession, 'missing_identity' => missing_identity, 'missmatched_entity' => missmatched_entity}})
120
- identity, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("identity"))
121
- session["ZuoraCurrentIdentity"] = identity
122
- session["ZuoraCurrentEntity"] = identity['entityId']
123
- session["ZSession"] = cookies['ZSession']
124
- zuora_instance_id = nil
125
- zuora_details["identity"]["entityId"] = identity['entityId']
121
+ #One deployed instance
122
+ if appinstances.size == 1
123
+ ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
126
124
 
127
- 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: zuora_client.class == ZuoraAPI::Oauth ? {} : {'Authorization' => "ZSession-a3N2w #{zuora_client.get_session(prefix: false, auth_type: :basic)}"})
128
- session["ZuoraCurrentUserInfo"] = client_describe
129
-
130
- raise ZuoraConnect::Exceptions::Error.new("Header entity id does not match identity call entity id.") if zuora_entity_id != identity['entityId']
125
+ #Add user/update
126
+ @user = ZuoraConnect::ZuoraUser.where(:zuora_user_id => zuora_user_id).first
127
+ if @user.present?
128
+ ZuoraConnect.logger.debug("Current zuora user #{zuora_user_id}")
129
+ if @user.updated_at < Time.now - 1.day
130
+ @user.zuora_identity_response[zuora_entity_id] = session["ZuoraCurrentIdentity"]
131
+ @user.save!
131
132
  end
133
+ else
134
+ ZuoraConnect.logger.debug("New zuora user object for #{zuora_user_id}")
135
+ @user = ZuoraConnect::ZuoraUser.create!(:zuora_user_id => zuora_user_id, :zuora_identity_response => {zuora_entity_id => session["ZuoraCurrentIdentity"]})
136
+ end
137
+ #Update access if admin in tenant
138
+ if session["ZuoraCurrentIdentity"]['platformRole'] == 'ADMIN' && !@user.app_permissions['access'].to_bool
139
+ @user.app_permissions['access'] = true
140
+ @user.save!
141
+ end
132
142
 
133
- #Find matching app instances.
134
- if zuora_instance_id.present?
135
- 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)
136
- else
137
- #if app_instance_ids is present then permissions still controlled by connect
138
- if params[:app_instance_ids].present?
139
- navbar, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("navigation"))
140
- urls = navbar['menus'].map {|x| x['url']}
141
- app_env = ENV["DEIS_APP"] || "xyz123"
142
- url = urls.compact.select {|url| File.basename(url).start_with?(app_env + '?')}.first
143
- task_ids = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(url).query)["app_instance_ids"][0]))
144
-
145
- appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name)
146
- else
147
- 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)
148
- end
143
+ #If user has has access to application
144
+ if @user.app_permissions['access'].to_bool || !ZuoraConnect.configuration.app_access_permissions
145
+ session["appInstance"] = appinstances.to_h.keys.first
146
+ else
147
+ Thread.current[:appinstance] = nil
148
+ session["appInstance"] = nil
149
+ 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}")
150
+ render "zuora_connect/static/permission_error", :locals => {:admins => admin_users}
151
+ return
152
+ end
153
+ #We have multiple, user must pick
154
+ elsif appinstances.size > 1
155
+ ZuoraConnect.logger.debug("User must select instance. #{@names}")
156
+ render "zuora_connect/static/launch", :locals => {:names => appinstances.to_h}
157
+ return
158
+ else
159
+ begin
160
+ #Ensure user can access oauth creation API
161
+ if session["ZuoraCurrentIdentity"]['platformRole'] != 'ADMIN'
162
+ raise ZuoraConnect::Exceptions::Error.new("User is not admin, workflow cannot be deployed.")
149
163
  end
150
-
151
- zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId']
152
-
153
- #One deployed instance
154
- if appinstances.size == 1
155
- ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
156
- @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first)
157
-
158
- #Add user/update
159
- @zuora_user = ZuoraConnect::ZuoraUser.where(:zuora_user_id => zuora_user_id).first
160
- if @zuora_user.present?
161
- ZuoraConnect.logger.debug("Current zuora user #{zuora_user_id}")
162
- if @zuora_user.updated_at < Time.now - 1.day
163
- @zuora_user.zuora_identity_response[zuora_entity_id] = session["ZuoraCurrentIdentity"]
164
- @zuora_user.save!
165
- end
166
- else
167
- ZuoraConnect.logger.debug("New zuora user object for #{zuora_user_id}")
168
- @zuora_user = ZuoraConnect::ZuoraUser.create!(:zuora_user_id => zuora_user_id, :zuora_identity_response => {zuora_entity_id => session["ZuoraCurrentIdentity"]})
169
- end
170
- @zuora_user.session = session
171
- session["#{@appinstance.id}::user::email"] = session['ZuoraCurrentIdentity']["username"]
172
- session["#{@appinstance.id}::user::timezone"] = session['ZuoraCurrentIdentity']["timeZone"]
173
- session["#{@appinstance.id}::user::locale"] = session['ZuoraCurrentIdentity']["language"]
174
- session["appInstance"] = @appinstance.id
175
-
176
- #We have multiple, user must pick
177
- elsif appinstances.size > 1
178
- ZuoraConnect.logger.debug("User must select instance. #{@names}")
179
- render "zuora_connect/static/launch", :locals => {:names => appinstances.to_h}, :layout => false
180
- return
181
-
182
- #We have no deployed instance for this tenant
183
- else
184
- #Ensure user can access oauth creation API
185
- if session["ZuoraCurrentIdentity"]['platformRole'] != 'ADMIN'
186
- Thread.current[:appinstance] = nil
187
- session["appInstance"] = nil
188
- render "zuora_connect/static/error_handled", :locals => {
189
- :title => "Application can only complete its initial setup via platform administrator",
190
- :message => "Please contact admin of tenant and have them click on link again to launch application."
191
- }, :layout => false
192
- return
193
- end
194
- Apartment::Tenant.switch!("public")
195
- ActiveRecord::Base.transaction do
196
- ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE')
197
- 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)
198
-
199
- if appinstances.size > 0
200
- redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
201
- return
202
- end
203
-
204
- next_id = (ZuoraConnect::AppInstance.all.where('id > 24999999').order(id: :desc).limit(1).pluck(:id).first || 24999999) + 1
205
- user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ')
206
- body = {
207
- 'userId' => zuora_user_id,
208
- 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
209
- 'customAuthorities' => [],
210
- 'additionalInformation' => {
211
- 'description' => "This user is for #{user} application.",
212
- 'name' => "#{user} API User #{next_id}"
213
- }
214
- }
215
-
216
- 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: zuora_client.class == ZuoraAPI::Oauth ? {} : {'Authorization' => "ZSession-a3N2w #{zuora_client.get_session(prefix: false, auth_type: :basic)}"})
217
-
218
- new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
219
- if session["ZuoraCurrentUserInfo"].blank?
220
- client_describe, response = new_zuora_client.rest_call(url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
221
- else
222
- client_describe = session["ZuoraCurrentUserInfo"]
223
- end
224
-
225
- available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id}
226
- task_data = {
227
- "id": next_id,
228
- "name": client_describe["tenantName"],
229
- "mode": "Collections",
230
- "status": "Running",
231
- ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => {
232
- "tenant_type": "Zuora",
233
- "username": session["ZuoraCurrentIdentity"]["username"],
234
- "url": new_zuora_client.url,
235
- "status": "Active",
236
- "oauth_client_id": oauth_response['clientId'],
237
- "oauth_secret": oauth_response['clientSecret'],
238
- "authentication_type": "OAUTH",
239
- "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})}
240
- },
241
- "tenant_ids": available_entities.map{|e| e['entityId']}.uniq,
242
- }
243
- mapped_values = {:id => next_id, :api_token => rand(36**64).to_s(36), :token => rand(36**64).to_s(36), :zuora_logins => task_data, :oauth_expires_at => Time.now + 1000.years, :zuora_domain => zuora_client.rest_domain, :zuora_entity_ids => [zuora_entity_id]}
244
- @appinstance = ZuoraConnect::AppInstance.new(mapped_values)
245
- retry_count = 0
246
- begin
247
- @appinstance.save(:validate => false)
248
- rescue ActiveRecord::RecordNotUnique => ex
249
- if (retry_count += 1) < 3
250
- @appinstance.assign_attributes({:api_token => rand(36**64).to_s(36), :token => rand(36**64).to_s(36)})
251
- retry
252
- else
253
- Thread.current[:appinstance] = nil
254
- session["appInstance"] = nil
255
- render "zuora_connect/static/error_handled", :locals => {
256
- :title => "Application could not create unique tokens.",
257
- :message => "Please contact support or retry launching application."
258
- }, :layout => false
259
- return
260
- end
261
- end
262
- end
263
-
264
- Apartment::Tenant.switch!("public")
265
- begin
266
- Apartment::Tenant.create(@appinstance.id.to_s)
267
- rescue Apartment::TenantExists => ex
268
- ZuoraConnect.logger.debug("Tenant Already Exists")
269
- end
270
- @appinstance.refresh
271
- session["appInstance"] = @appinstance.id
164
+
165
+ body = {
166
+ 'userId' => zuora_user_id,
167
+ 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
168
+ 'customAuthorities' => [],
169
+ 'additionalInformation' => {
170
+ 'description' => 'This user is for workflow application.',
171
+ 'name' => 'Workflow API User'
172
+ }
173
+ }
174
+
175
+ 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: zuora_client.class == ZuoraAPI::Oauth ? {} : {'Authorization' => "ZSession-a3N2w #{zuora_client.get_session(prefix: false, auth_type: :basic)}"})
176
+
177
+ new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
178
+
179
+ client_describe, response = new_zuora_client.rest_call(url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
180
+
181
+ Apartment::Tenant.switch!("public")
182
+ next_id = (ZuoraConnect::AppInstance.all.where(:access_token => nil).order(id: :desc).limit(1).pluck(:id).first || 24999999) + 1
183
+ begin
184
+ Apartment::Tenant.create(next_id.to_s)
185
+ rescue Apartment::TenantExists => ex
186
+ ZuoraConnect.logger.debug("Tenant Already Exists")
272
187
  end
273
188
 
274
- rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
275
- output_xml, input_xml = zuora_client.soap_call(errors: [], z_session: false) do |xml|
276
- xml['api'].getUserInfo
277
- end
278
- final_error = output_xml.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
279
- session.clear
280
- ZuoraConnect.logger.warn(ex, zuora: zuora_details.merge({:error => final_error}))
281
- redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
282
- return
189
+ task_data = {
190
+ "id": next_id,
191
+ "name": client_describe["tenantName"],
192
+ "mode": "Collections",
193
+ "status": "Running",
194
+ "target_login": {
195
+ "tenant_type": "Zuora",
196
+ "username": session["ZuoraCurrentIdentity"]["username"],
197
+ "url": new_zuora_client.url,
198
+ "status": "Active",
199
+ "oauth_client_id": oauth_response['clientId'],
200
+ "oauth_secret": oauth_response['clientSecret'],
201
+ "authentication_type": "OAUTH",
202
+ "entities": client_describe["accessibleEntities"].map {|e| e.merge({'displayName' => client_describe["tenantName"]})} #needs work
203
+ },
204
+ "tenant_ids": client_describe["accessibleEntities"].map{|e| e['entityId'] }.push(client_describe["tenantId"]).uniq,
205
+ }
206
+
207
+ appinstance = ZuoraConnect::AppInstance.new(:id => next_id, :zuora_logins => task_data.to_json, :oauth_expires_at => Time.now + 1000.years)
208
+ appinstance.save(:validate => false)
209
+ @appinstance = ZuoraConnect::AppInstance.find(appinstance.id)
210
+ @appinstance.apartment_switch(method = nil, migrate = true)
211
+
212
+ session["appInstance"] = @appinstance.id
283
213
  rescue => ex
284
- ZuoraConnect.logger.error(ex, zuora: zuora_details)
285
- render "zuora_connect/static/error_unhandled", :locals => {:exception => ex}, :layout => false
286
- return
214
+ ZuoraConnect.logger.error(ex)
215
+ render "zuora_connect/static/invalid_launch_request", :locals => {:exception => ex}
216
+ return
287
217
  end
288
- elsif 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)
218
+ end
219
+ end
220
+
221
+ start_time = Time.now
222
+ if ZuoraConnect.configuration.mode == "Production"
223
+ 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)
289
224
  setup_instance_via_data
290
225
  else
291
- if session["appInstance"].present?
292
- @appinstance = ZuoraConnect::AppInstance.where(:id => session["appInstance"]).first
293
- else
294
- render "zuora_connect/static/error_handled", :locals => {
295
- :title => "Application state could not be verified",
296
- :message => "Please relaunch application."
297
- }, :layout => false
298
- return
299
- end
226
+ setup_instance_via_session
300
227
  end
301
228
  else
302
229
  setup_instance_via_dev_mode
303
230
  end
304
-
305
- if !defined?(@appinstance) || @appinstance.blank?
306
- render "zuora_connect/static/error_handled", :locals => {
307
- :title => "Application state could not be found.",
308
- :message => "Please relaunch application."
309
- }, :layout => false
310
- return
311
- end
312
231
  #Call .data_lookup with the current session to retrieve session. In some cases session may be stored/cache in redis
313
232
  #so data lookup provides a model method that can be overriden per app.
314
233
  if params[:controller] != 'zuora_connect/api/v1/app_instance' && params[:action] != 'drop'
@@ -316,23 +235,16 @@ module ZuoraConnect
316
235
  @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
317
236
  end
318
237
  end
319
-
320
238
  if session["#{@appinstance.id}::user::email"].present?
321
239
  ElasticAPM.set_user(session["#{@appinstance.id}::user::email"]) if defined?(ElasticAPM) && ElasticAPM.running?
322
240
  PaperTrail.whodunnit = session["#{@appinstance.id}::user::email"] if defined?(PaperTrail)
323
241
  end
324
242
  begin
325
- locale = session["#{@appinstance.id}::user::locale"]
326
- I18n.locale = locale.present? ? locale : @appinstance.locale
243
+ I18n.locale = session["#{@appinstance.id}::user::locale"] ? session["#{@appinstance.id}::user::locale"] : @appinstance.locale
327
244
  rescue I18n::InvalidLocale => ex
328
245
  ZuoraConnect.logger.error(ex) if !ZuoraConnect::AppInstance::IGNORED_LOCALS.include?(ex.locale.to_s.downcase)
329
246
  end
330
- begin
331
- Time.zone = session["#{@appinstance.id}::user::timezone"] ? session["#{@appinstance.id}::user::timezone"] : @appinstance.timezone
332
- rescue
333
- ZuoraConnect.logger.error(ex)
334
- end
335
-
247
+ Time.zone = session["#{@appinstance.id}::user::timezone"] ? session["#{@appinstance.id}::user::timezone"] : @appinstance.timezone
336
248
  ZuoraConnect.logger.debug("[#{@appinstance.blank? ? "N/A" : @appinstance.id}] Authenticate App Request Completed In - #{(Time.now - start_time).round(2)}s")
337
249
  end
338
250
 
@@ -354,14 +266,6 @@ module ZuoraConnect
354
266
  return session["#{@appinstance.id}::admin"]
355
267
  end
356
268
 
357
- def zuora_user
358
- return @zuora_user
359
- end
360
-
361
- def hallway_integration?
362
- return (request.headers['ZuoraCurrentEntity'].present? || cookies['ZuoraCurrentEntity'].present?)
363
- end
364
-
365
269
  private
366
270
  def setup_instance_via_data
367
271
  session.clear
@@ -383,7 +287,6 @@ module ZuoraConnect
383
287
  ZuoraConnect.logger.debug({msg: 'Setup values', connect: values}) if Rails.env != "production"
384
288
 
385
289
  @appinstance = ZuoraConnect::AppInstance.where(:id => values["appInstance"].to_i).first
386
-
387
290
  if @appinstance.blank?
388
291
  Apartment::Tenant.switch!("public")
389
292
  begin
@@ -391,20 +294,29 @@ module ZuoraConnect
391
294
  rescue Apartment::TenantExists => ex
392
295
  ZuoraConnect.logger.debug("Tenant Already Exists")
393
296
  end
394
- 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"]}
395
- @appinstance = ZuoraConnect::AppInstance.new(mapped_values.merge({:id => values["appInstance"].to_i}))
297
+ @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"])
396
298
  @appinstance.save(:validate => false)
397
299
  else
398
- mapped_values = {:access_token => values["access_token"], :refresh_token => values["refresh_token"], :oauth_expires_at => values["expires"]}
399
- @appinstance.assign_attributes(mapped_values)
300
+ @appinstance.access_token = values["access_token"] if !values["access_token"].blank? && @appinstance.access_token != values["access_token"]
301
+ @appinstance.refresh_token = values["refresh_token"] if !values["refresh_token"].blank? && @appinstance.refresh_token != values["refresh_token"]
302
+ @appinstance.oauth_expires_at = values["expires"] if !values["expires"].blank?
303
+ @appinstance.api_token = values["api_token"] if !values["api_token"].blank? && @appinstance.api_token != values["api_token"]
400
304
  if @appinstance.access_token_changed? && @appinstance.refresh_token_changed?
401
305
  @appinstance.save(:validate => false)
402
306
  else
403
- raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mismatch. Possible tampering")
307
+ raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mistmatch. Possible tampering")
404
308
  end
405
309
  end
406
310
  end
407
311
 
312
+ def setup_instance_via_session
313
+ if session["appInstance"].present?
314
+ @appinstance = ZuoraConnect::AppInstance.where(:id => session["appInstance"]).first
315
+ else
316
+ raise ZuoraConnect::Exceptions::SessionInvalid.new("Session Blank -- Relaunch Application")
317
+ end
318
+ end
319
+
408
320
  def setup_instance_via_dev_mode
409
321
  session["appInstance"] = ZuoraConnect.configuration.dev_mode_appinstance
410
322
  user = ZuoraConnect.configuration.dev_mode_user
@@ -428,6 +340,24 @@ module ZuoraConnect
428
340
  end
429
341
  session["#{@appinstance.id}::admin"] = ZuoraConnect.configuration.dev_mode_admin
430
342
  end
343
+
344
+ #API ONLY
345
+ def check_instance
346
+ if defined?(@appinstance) && @appinstance.present?
347
+ if @appinstance.new_session_for_api_requests(:params => params)
348
+ @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
349
+ end
350
+ Thread.current[:appinstance] = @appinstance
351
+ PaperTrail.whodunnit = "API User" if defined?(PaperTrail)
352
+ ElasticAPM.set_user("API User") if defined?(ElasticAPM) && ElasticAPM.running?
353
+ return true
354
+ else
355
+ response.set_header('WWW-Authenticate', "Basic realm=\"Application\"")
356
+ #render json: {"status": 401, "message": "Access Denied"}, status: :unauthorized
357
+ render html: "HTTP Basic: Access denied.\n", status: :unauthorized
358
+ render plain: "Access Denied", status: :unauthorized
359
+ end
360
+ end
431
361
  end
432
362
  end
433
363
  end