zuora_connect 0 → 3.2.11

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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +219 -0
  4. data/Rakefile +1 -1
  5. data/app/assets/javascripts/hallway_wrapper/after.js +22 -3
  6. data/app/controllers/concerns/zuora_connect/authenticate.rb +39 -0
  7. data/app/controllers/zuora_connect/api/v1/app_instance_controller.rb +5 -0
  8. data/app/controllers/zuora_connect/application_controller.rb +37 -2
  9. data/app/controllers/zuora_connect/static_controller.rb +142 -27
  10. data/app/helpers/zuora_connect/LDAP/adapter.rb +16 -0
  11. data/app/helpers/zuora_connect/LDAP/connection.rb +123 -0
  12. data/app/helpers/zuora_connect/application_helper.rb +10 -0
  13. data/app/models/concerns/zuora_connect/auditable.rb +29 -0
  14. data/app/models/zuora_connect/app_instance_base.rb +652 -180
  15. data/app/models/zuora_connect/login.rb +24 -11
  16. data/app/models/zuora_connect/telegraf.rb +18 -38
  17. data/app/models/zuora_connect/zuora_user.rb +35 -0
  18. data/app/views/sql/refresh_aggregate_table.txt +12 -10
  19. data/app/views/zuora_connect/application/ldap_login.html.erb +195 -0
  20. data/app/views/zuora_connect/static/error_handled.html.erb +76 -0
  21. data/app/views/zuora_connect/static/error_handled.js.erb +1 -0
  22. data/app/views/zuora_connect/static/error_unhandled.erb +85 -0
  23. data/app/views/zuora_connect/static/error_unhandled.js.erb +1 -0
  24. data/app/views/zuora_connect/static/launch.html.erb +71 -76
  25. data/config/initializers/object_method_hooks.rb +2 -2
  26. data/config/initializers/patches.rb +9 -0
  27. data/config/initializers/postgresql_adapter.rb +119 -1
  28. data/config/initializers/prometheus.rb +57 -23
  29. data/config/initializers/redis.rb +52 -5
  30. data/config/initializers/resque.rb +5 -1
  31. data/config/initializers/unicorn.rb +30 -2
  32. data/config/initializers/zuora_observability.rb +24 -0
  33. data/config/routes.rb +8 -3
  34. data/db/migrate/20100718151733_create_connect_app_instances.rb +1 -1
  35. data/db/migrate/20101024162319_add_tokens_to_app_instance.rb +1 -1
  36. data/db/migrate/20101024220705_add_token_to_app_instance.rb +1 -1
  37. data/db/migrate/20110131211919_add_sessions_table.rb +1 -1
  38. data/db/migrate/20110411200303_add_expiration_to_app_instance.rb +1 -1
  39. data/db/migrate/20110413191512_add_new_api_token.rb +1 -1
  40. data/db/migrate/20110503003602_add_catalog_data_to_app_instance.rb +1 -1
  41. data/db/migrate/20110503003603_add_catalog_mappings_to_app_instance.rb +1 -1
  42. data/db/migrate/20110503003604_catalog_default.rb +1 -1
  43. data/db/migrate/20180301052853_add_catalog_attempted_at.rb +1 -1
  44. data/db/migrate/20181206162339_add_fields_to_instance.rb +1 -1
  45. data/db/migrate/20190520232221_add_zuora_user_table_and_alter_app_instance_id_table.rb +18 -0
  46. data/db/migrate/20190520232222_add_unique_index.rb +6 -0
  47. data/db/migrate/20190520232223_add_provisioning_fields.rb +6 -0
  48. data/db/migrate/20190520232224_add_environment_fields.rb +16 -0
  49. data/lib/metrics/net.rb +3 -3
  50. data/lib/middleware/json_parse_errors.rb +33 -0
  51. data/lib/middleware/metrics_middleware.rb +62 -68
  52. data/lib/middleware/request_id_middleware.rb +17 -0
  53. data/lib/resque/dynamic_queues.rb +35 -13
  54. data/lib/resque/plugins/app_instance_job.rb +63 -0
  55. data/lib/resque/plugins/custom_logger.rb +12 -27
  56. data/lib/tasks/zuora_connect_tasks.rake +0 -5
  57. data/lib/zuora_connect/configuration.rb +8 -4
  58. data/lib/zuora_connect/controllers/helpers.rb +640 -189
  59. data/lib/zuora_connect/engine.rb +12 -9
  60. data/lib/zuora_connect/exceptions.rb +18 -2
  61. data/lib/zuora_connect/middleware/hallway.rb +34 -0
  62. data/lib/zuora_connect/railtie.rb +16 -39
  63. data/lib/zuora_connect/version.rb +3 -1
  64. data/lib/zuora_connect.rb +69 -5
  65. metadata +146 -126
  66. data/app/views/zuora_connect/static/invalid_app_instance_error.html.erb +0 -65
  67. data/app/views/zuora_connect/static/invalid_launch_request.html +0 -65
  68. data/app/views/zuora_connect/static/session_error.html.erb +0 -63
  69. data/config/initializers/elastic_apm.rb +0 -25
  70. data/lib/zuora_connect/views/helpers.rb +0 -9
  71. data/test/controllers/zuora_connect/api/v1/app_instance_controller_test.rb +0 -13
  72. data/test/dummy/README.rdoc +0 -28
  73. data/test/dummy/Rakefile +0 -6
  74. data/test/dummy/app/assets/javascripts/application.js +0 -13
  75. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  76. data/test/dummy/app/controllers/application_controller.rb +0 -5
  77. data/test/dummy/app/helpers/application_helper.rb +0 -2
  78. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  79. data/test/dummy/bin/bundle +0 -3
  80. data/test/dummy/bin/rails +0 -4
  81. data/test/dummy/bin/rake +0 -4
  82. data/test/dummy/bin/setup +0 -29
  83. data/test/dummy/config/application.rb +0 -26
  84. data/test/dummy/config/boot.rb +0 -5
  85. data/test/dummy/config/database.yml +0 -25
  86. data/test/dummy/config/environment.rb +0 -5
  87. data/test/dummy/config/environments/development.rb +0 -41
  88. data/test/dummy/config/environments/production.rb +0 -79
  89. data/test/dummy/config/environments/test.rb +0 -42
  90. data/test/dummy/config/initializers/assets.rb +0 -11
  91. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  92. data/test/dummy/config/initializers/cookies_serializer.rb +0 -3
  93. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  94. data/test/dummy/config/initializers/inflections.rb +0 -16
  95. data/test/dummy/config/initializers/mime_types.rb +0 -4
  96. data/test/dummy/config/initializers/session_store.rb +0 -3
  97. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  98. data/test/dummy/config/locales/en.yml +0 -23
  99. data/test/dummy/config/routes.rb +0 -4
  100. data/test/dummy/config/secrets.yml +0 -22
  101. data/test/dummy/config.ru +0 -4
  102. data/test/dummy/public/404.html +0 -67
  103. data/test/dummy/public/422.html +0 -67
  104. data/test/dummy/public/500.html +0 -66
  105. data/test/dummy/public/favicon.ico +0 -0
  106. data/test/fixtures/zuora_connect/app_instances.yml +0 -11
  107. data/test/integration/navigation_test.rb +0 -8
  108. data/test/lib/generators/zuora_connect/datatable_generator_test.rb +0 -16
  109. data/test/models/zuora_connect/app_instance_test.rb +0 -9
  110. data/test/test_helper.rb +0 -21
  111. data/test/zuora_connect_test.rb +0 -7
@@ -5,147 +5,190 @@ module ZuoraConnect
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  def authenticate_app_api_request
8
- #Skip session for api requests
9
- Thread.current[:appinstance] = nil
10
- request.session_options[:skip] = true
11
- ElasticAPM.set_tag(:trace_id, request.uuid) if defined?(ElasticAPM)
12
-
13
- start_time = Time.now
14
- if request.headers["API-Token"].present?
15
- @appinstance = ZuoraConnect::AppInstance.where(:api_token => request.headers["API-Token"]).first
16
- Rails.logger.debug("[#{@appinstance.id}] API REQUEST - API token") if @appinstance.present?
17
- check_instance
18
- else
19
- authenticate_or_request_with_http_basic do |username, password|
20
- @appinstance = ZuoraConnect::AppInstance.where(:token => password).first
21
- @appinstance ||= ZuoraConnect::AppInstance.where(:api_token => password).first
22
- Rails.logger.debug("[#{@appinstance.id}] API REQUEST - Basic Auth") if @appinstance.present?
23
- check_instance
24
- end
25
- end
26
- if @appinstance.present?
27
- Rails.logger.debug("[#{@appinstance.id}] Authenticate App API Request Completed In - #{(Time.now - start_time).round(2)}s")
28
- end
29
- end
8
+ ZuoraConnect::AppInstance.read_master_db do
9
+ #Skip session for api requests
10
+ request.session_options[:skip] = true
30
11
 
31
- def verify_with_navbar
32
- if !session[params[:app_instance_ids]].present?
33
- host = request.headers["HTTP_X_FORWARDED_HOST"]
34
- zuora_client = ZuoraAPI::Login.new(url: "https://#{host}")
35
- menus = zuora_client.get_full_nav(cookies.to_h)["menus"]
36
- app = menus.select do |item|
37
- matches = /(?<=.com\/services\/)(.*?)(?=\?|$)/.match(item["url"])
38
- if !matches.blank?
39
- matches[0].split("?").first == ENV["DEIS_APP"]
12
+ Thread.current[:appinstance] = nil
13
+ if ZuoraConnect.logger.is_a?(Ougai::Logger); ZuoraConnect.logger.with_fields = {}; end
14
+ if Rails.logger.is_a?(Ougai::Logger); Rails.logger.with_fields = {}; end
15
+ if defined?(ElasticAPM) && ElasticAPM.running?
16
+ if ElasticAPM.respond_to?(:set_label)
17
+ ElasticAPM.set_label(:trace_id, request.uuid) if defined?(ElasticAPM) && ElasticAPM.running?
18
+ else
19
+ ElasticAPM.set_label(:trace_id, request.uuid) if defined?(ElasticAPM) && ElasticAPM.running?
40
20
  end
41
21
  end
42
22
 
43
- session[params[:app_instance_ids]] = app[0]
44
- return app[0]
45
- else
46
- return session[params[:app_instance_ids]]
47
- end
48
- end
23
+ ZuoraConnect::ZuoraUser.current_user_id = request.headers["Zuora-User-Id"]
49
24
 
50
- def select_instance
51
- begin
52
- app = verify_with_navbar
25
+ if request.headers["API-Token"].present?
26
+ @appinstance = ZuoraConnect::AppInstance.find_by(:api_token => request.headers["API-Token"])
27
+ ZuoraConnect.logger.debug("API REQUEST - API token") if @appinstance.present?
28
+ check_instance
29
+ elsif ZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST", nil)) && request.headers['zuora-host'].present?
30
+ zuora_host, zuora_entity_id, zuora_instance_id = [request.headers['zuora-host'], (request.headers['zuora-entity-ids'] || "").gsub('-',''), request.headers['zuora-instance-id']]
31
+ zuora_host_mapping = {'origin-gateway.sbx.auw2.zuora.com' => 'rest.apisandbox.zuora.com', 'origin-gateway.prod.auw2.zuora.com' => 'rest.zuora.com'}
32
+ zuora_host = zuora_host_mapping[zuora_host] if zuora_host_mapping.keys.include?(zuora_host)
53
33
 
54
- url_tasks = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(app["url"]).query)["app_instance_ids"][0]))
55
- @app_instance_ids = JSON.parse(Base64.urlsafe_decode64(params[:app_instance_ids]))
34
+ #Validate entity-ids present
35
+ if zuora_entity_id.blank?
36
+ render json: {"status": 401, "message": "zuora-entity-ids header was not supplied."}, status: :unauthorized
37
+ return
38
+ end
39
+ #Select with instance id if present. Used where mulitple deployments are done.
40
+ if zuora_instance_id.present?
41
+ 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)
42
+ else
43
+ appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host", entities: [zuora_entity_id], host: zuora_host)
44
+ end
56
45
 
57
- if (url_tasks & @app_instance_ids).size == @app_instance_ids.size
58
- sql = "select name,id from zuora_connect_app_instances where id = ANY(ARRAY#{@app_instance_ids})"
59
- result = ActiveRecord::Base.connection.execute(sql)
60
- @names = {}
61
- result.each do |x|
62
- @names[x["id"].to_i] = x["name"]
46
+ if appinstances.size == 0
47
+ render json: {"status": 404, "message": "Missing mapping or no deployment for '#{zuora_host}-#{zuora_entity_id}' ."}, status: 404
48
+ return
49
+ elsif appinstances.size > 1
50
+ 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
51
+ return
52
+ else
53
+ @appinstance = appinstances.first
54
+ check_instance
55
+ end
56
+
57
+ elsif request.headers.fetch("Authorization", "").include?("Basic ")
58
+ authenticate_or_request_with_http_basic do |username, password|
59
+ @appinstance = ZuoraConnect::AppInstance.find_by(:token => password)
60
+ @appinstance ||= ZuoraConnect::AppInstance.find_by(:api_token => password)
61
+ ZuoraConnect.logger.debug("API REQUEST - Basic Auth") if @appinstance.present?
62
+ check_instance
63
63
  end
64
- render "zuora_connect/static/launch"
65
64
  else
66
- render "zuora_connect/static/invalid_launch_request"
65
+ check_instance
67
66
  end
68
- rescue => ex
69
- Rails.logger.debug("Error parsing Instance ID's: #{ex.message}")
70
- render "zuora_connect/static/invalid_launch_request"
67
+
68
+ @zuora_user = ZuoraConnect::ZuoraUser.find_by(zuora_user_id: ZuoraConnect::ZuoraUser.current_user_id)
69
+ zuora_org_ids = request.headers["Zuora-Org-Ids"]
70
+ ZuoraConnect::ZuoraUser.current_org_ids = []
71
+ ZuoraConnect::ZuoraUser.current_org_ids = zuora_org_ids.split(',') if zuora_org_ids
72
+
73
+ end
74
+ rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
75
+ render json: {"status": 400, "message": ex.message}, status: 400
76
+ return
77
+ rescue ZuoraAPI::Exceptions::ZuoraAPIInternalServerError => ex
78
+ render json: {"status": 500, "message": ex.message}, status: 500
79
+ return
80
+ end
81
+
82
+ #API ONLY
83
+ def check_instance
84
+ if defined?(@appinstance) && @appinstance.present?
85
+ if @appinstance.new_session_for_api_requests(:params => params)
86
+ @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
87
+ end
88
+ Thread.current[:appinstance] = @appinstance
89
+ PaperTrail.whodunnit = "API User" if defined?(PaperTrail)
90
+ ElasticAPM.set_user("API User") if defined?(ElasticAPM) && ElasticAPM.running?
91
+ return true
92
+ else
93
+ response.set_header('WWW-Authenticate', "Basic realm=\"Application\"")
94
+ render json: {"status": 401, "message": "Access Denied"}, status: :unauthorized
95
+ return false
71
96
  end
72
97
  end
73
98
 
74
99
  def authenticate_connect_app_request
75
- ElasticAPM.set_tag(:trace_id, request.uuid) if defined?(ElasticAPM)
76
- Thread.current[:appinstance] = nil
77
- if params[:app_instance_ids].present? && !params[:app_instance_id].present?
78
- begin
79
- app_instance_ids = JSON.parse(Base64.urlsafe_decode64(params[:app_instance_ids]))
80
- if app_instance_ids.length == 1
81
- verify_with_navbar
82
- instances = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(session[params[:app_instance_ids]]["url"]).query)["app_instance_ids"][0]))
83
- if instances.include?(app_instance_ids[0])
84
- @appinstance = ZuoraConnect::AppInstance.find(app_instance_ids[0])
85
- @appinstance.new_session(session: {})
86
- @appinstance.cache_app_instance
87
- session["appInstance"] = app_instance_ids[0]
88
- else
89
- Rails.logger.fatal("Launch Error: Param Instance didnt match session data")
90
- render "zuora_connect/static/invalid_launch_request"
91
- return
92
- end
100
+ ZuoraConnect::AppInstance.read_master_db do
101
+ Thread.current[:appinstance] = nil
102
+ if ZuoraConnect.logger.is_a?(Ougai::Logger); ZuoraConnect.logger.with_fields = {}; end
103
+ if Rails.logger.is_a?(Ougai::Logger); Rails.logger.with_fields = {}; end
104
+ if defined?(ElasticAPM) && ElasticAPM.running?
105
+ if ElasticAPM.respond_to?(:set_label)
106
+ ElasticAPM.set_label(:trace_id, request.uuid)
93
107
  else
94
- select_instance
95
- return
108
+ ElasticAPM.set_label(:trace_id, request.uuid)
96
109
  end
97
- rescue => ex
98
- Rails.logger.fatal("Launch Error: #{ex.message}")
99
- render "zuora_connect/static/invalid_launch_request"
110
+ end
111
+
112
+ if ZuoraConnect.configuration.mode == "Production"
113
+ setup_instance_via_prod_mode
114
+ else
115
+ setup_instance_via_dev_mode
116
+ end
117
+
118
+ return if performed?
119
+
120
+ if !defined?(@appinstance) || @appinstance.blank?
121
+ render "zuora_connect/static/error_handled", :locals => {
122
+ :title => "Application state could not be found.",
123
+ :message => "Please relaunch application."
124
+ }, :layout => false
100
125
  return
101
126
  end
102
-
103
- elsif params[:app_instance_ids].present? && params[:app_instance_id].present?
104
- begin
105
- instances = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(session[params[:app_instance_ids]]["url"]).query)["app_instance_ids"][0]))
106
- if instances.include?(params[:app_instance_id].to_i)
107
- @appinstance = ZuoraConnect::AppInstance.find(params[:app_instance_id].to_i)
108
- @appinstance.new_session(session: {})
109
- @appinstance.cache_app_instance
110
- session["appInstance"] = params[:app_instance_id].to_i
111
- else
112
- render "zuora_connect/static/invalid_launch_request"
113
- return
127
+ #Call .data_lookup with the current session to retrieve session. In some cases session may be stored/cache in redis
128
+ #so data lookup provides a model method that can be overriden per app.
129
+ if params[:controller] != 'zuora_connect/api/v1/app_instance' && params[:action] != 'drop'
130
+ if @appinstance.new_session_for_ui_requests(:params => params)
131
+ @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
114
132
  end
115
- rescue => ex
116
- Rails.logger.fatal("Launch Error: #{ex.message}")
117
- render "zuora_connect/static/invalid_launch_request"
118
- return
119
133
  end
120
- end
121
- start_time = Time.now
122
- if ZuoraConnect.configuration.mode == "Production"
123
- 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)
124
- setup_instance_via_data
125
- else
126
- setup_instance_via_session
134
+
135
+ if session["#{@appinstance.id}::user::email"].present?
136
+ ElasticAPM.set_user(session["#{@appinstance.id}::user::email"]) if defined?(ElasticAPM) && ElasticAPM.running?
137
+ PaperTrail.whodunnit = session["#{@appinstance.id}::user::email"] if defined?(PaperTrail)
127
138
  end
128
- else
129
- setup_instance_via_dev_mode
130
- end
131
- #Call .data_lookup with the current session to retrieve session. In some cases session may be stored/cache in redis
132
- #so data lookup provides a model method that can be overriden per app.
133
- if params[:controller] != 'zuora_connect/api/v1/app_instance' && params[:action] != 'drop'
134
- if @appinstance.new_session_for_ui_requests(:params => params)
135
- @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
139
+
140
+ locale = (session["#{@appinstance.id}::user::locale"] || "").gsub("_", "-")
141
+ begin
142
+ I18n.locale = locale.present? ? locale : @appinstance.locale
143
+ rescue I18n::InvalidLocale => ex
144
+ if locale.include?("-")
145
+ locale = locale.split("-").first
146
+ retry
147
+ elsif locale != session["#{@appinstance.id}::user::language"]
148
+ locale = session["#{@appinstance.id}::user::language"]
149
+ retry
150
+ end
151
+ ZuoraConnect.logger.error(ex) if !ZuoraConnect::AppInstance::IGNORED_LOCALS.include?(ex.locale.to_s.downcase)
152
+ end
153
+ if @appinstance.user_timezone.blank?
154
+ @appinstance.set_timezone(timezone: session["#{@appinstance.id}::user::timezone"], type: :default)
136
155
  end
137
156
  end
138
- if session["#{@appinstance.id}::user::email"].present?
139
- ElasticAPM.set_user(session["#{@appinstance.id}::user::email"]) if defined?(ElasticAPM)
140
- PaperTrail.whodunnit = session["#{@appinstance.id}::user::email"] if defined?(PaperTrail)
157
+ rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex
158
+ id = @appinstance.id
159
+ ZuoraConnect::AppInstance.destroy(id)
160
+ Apartment::Tenant.drop(id)
161
+ render "zuora_connect/static/error_handled", :locals => {
162
+ :title => "Application Setup Error",
163
+ :message => "Application cannot be run using Zuora Session. Delete old application \
164
+ deployment and create new with Zuora Basic or OAuth credentials."
165
+ }, :layout => false
166
+ return
167
+ rescue ZuoraConnect::Exceptions::AccessDenied => ex
168
+ respond_to do |format|
169
+ format.html {
170
+ render "zuora_connect/static/error_handled", :locals => {
171
+ :title => "Application State Error",
172
+ :message => ex.message
173
+ }, status: 401, layout: false
174
+ }
175
+ format.js {
176
+ render "zuora_connect/static/error_handled", :locals => {
177
+ :title => "Application State Error",
178
+ :message => ex.message
179
+ }, status: 401, layout: false
180
+ }
181
+ format.json { render json: {'errors' => ex.message}, status: 401 }
182
+ format.all { render json: ex.message, status: 401 }
141
183
  end
142
- begin
143
- I18n.locale = session["#{@appinstance.id}::user::locale"] ? session["#{@appinstance.id}::user::locale"] : @appinstance.locale
144
- rescue I18n::InvalidLocale => ex
145
- Rails.logger.error("Invalid Locale: #{ex.message}")
184
+ return
185
+ rescue => ex
186
+ ZuoraConnect.logger.error("UI Authorization Error", ex)
187
+ respond_to do |format|
188
+ format.html { render 'zuora_connect/static/error_unhandled', :locals => {:exception => ex, :skip_exception => true}, status: 500, layout: false }
189
+ format.js { render 'zuora_connect/static/error_unhandled', :locals => {:exception => ex, :skip_exception => true}, status: 500, layout: false}
146
190
  end
147
- Time.zone = session["#{@appinstance.id}::user::timezone"] ? session["#{@appinstance.id}::user::timezone"] : @appinstance.timezone
148
- Rails.logger.debug("[#{@appinstance.blank? ? "N/A" : @appinstance.id}] Authenticate App Request Completed In - #{(Time.now - start_time).round(2)}s")
191
+ return
149
192
  end
150
193
 
151
194
  def persist_connect_app_session
@@ -158,103 +201,511 @@ module ZuoraConnect
158
201
  end
159
202
  end
160
203
 
161
- def check_connect_admin!
162
- raise ZuoraConnect::Exceptions::AccessDenied.new("User is not an authorized admin for this application") if !session["#{@appinstance.id}::admin"]
204
+ def check_connect_admin!(raise_error: false)
205
+ if !(session["#{@appinstance.id}::admin"] || @appinstance.zuora_tenant_ids.include?("9"))
206
+ raise ZuoraConnect::Exceptions::AccessDenied.new("User is not an authorized admin for this application") if raise_error
207
+
208
+ respond_to do |format|
209
+ format.html {
210
+ render "zuora_connect/static/error_handled", :locals => {
211
+ :title => "Unauthorized",
212
+ :message => "User is not an authorized admin for this application"
213
+ }, status: 401, :layout => false
214
+ }
215
+ format.js {
216
+ render "zuora_connect/static/error_handled", :locals => {
217
+ :title => "Unauthorized",
218
+ :message => "User is not an authorized admin for this application"
219
+ }, status: 401, :layout => false
220
+ }
221
+ format.json { render json: {'errors' => ex.message}, status: 401 }
222
+ format.all { render json: ex.message, status: 401 }
223
+ end
224
+ return
225
+ end
163
226
  end
164
227
 
165
228
  def check_connect_admin
166
229
  return session["#{@appinstance.id}::admin"]
167
230
  end
168
231
 
169
- private
170
- def setup_instance_via_data
171
- session.clear
172
- values = JSON.parse(ZuoraConnect::AppInstance.decrypt_response(Base64.urlsafe_decode64(request["data"])))
173
- Rails.logger.debug("Data: #{values.to_json}")
174
- if values["param_data"]
175
- values["param_data"].each do |k ,v|
176
- params[k] = v
232
+ def zuora_user
233
+ return @zuora_user
234
+ end
235
+
236
+ def hallway_integration?
237
+ return (request.headers['ZuoraCurrentEntity'].present? || cookies['ZuoraCurrentEntity'].present?)
238
+ end
239
+ def create_new_instance
240
+ ZuoraConnect::AppInstance.read_master_db do
241
+ Thread.current[:appinstance] = nil
242
+ ZuoraConnect.logger.with_fields = {} if ZuoraConnect.logger.is_a?(Ougai::Logger)
243
+ Rails.logger.with_fields = {} if Rails.logger.is_a?(Ougai::Logger)
244
+
245
+ if defined?(ElasticAPM) && ElasticAPM.running? && ElasticAPM.respond_to?(:set_label)
246
+ ElasticAPM.set_label(:trace_id, request.uuid)
177
247
  end
178
- end
179
- session["#{values["appInstance"]}::destroy"] = values["destroy"]
180
- session["appInstance"] = values["appInstance"]
181
- if values["current_user"]
182
- session["#{values["appInstance"]}::admin"] = values["current_user"]["admin"] ? values["current_user"]["admin"] : false
183
- session["#{values["appInstance"]}::user::timezone"] = values["current_user"]["timezone"]
184
- session["#{values["appInstance"]}::user::locale"] = values["current_user"]["locale"]
185
- session["#{values["appInstance"]}::user::email"] = values["current_user"]["email"]
186
- end
187
248
 
188
- Rails.logger.debug("App Params: #{values.to_json}}") if Rails.env != "production"
249
+ zuora_host = request.headers['zuora-host']
250
+ zuora_entity_id = (request.headers['zuora-entity-ids'] || '').gsub(
251
+ '-',
252
+ ''
253
+ ).split(',').first
189
254
 
190
- @appinstance = ZuoraConnect::AppInstance.where(:id => values["appInstance"].to_i).first
191
- if @appinstance.blank?
192
- Apartment::Tenant.switch!("public")
193
- begin
194
- Apartment::Tenant.create(values["appInstance"].to_s)
195
- rescue Apartment::TenantExists => ex
196
- Rails.logger.debug("Tenant Already Exists")
255
+ # Validate host present
256
+ if zuora_host.blank?
257
+ render json: {
258
+ status: 401,
259
+ message: 'zuora-host header was not supplied.'
260
+ }, status: :unauthorized
261
+ return
197
262
  end
198
- @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"])
199
- @appinstance.save(:validate => false)
200
- else
201
- @appinstance.access_token = values["access_token"] if !values["access_token"].blank? && @appinstance.access_token != values["access_token"]
202
- @appinstance.refresh_token = values["refresh_token"] if !values["refresh_token"].blank? && @appinstance.refresh_token != values["refresh_token"]
203
- @appinstance.oauth_expires_at = values["expires"] if !values["expires"].blank?
204
- @appinstance.api_token = values["api_token"] if !values["api_token"].blank? && @appinstance.api_token != values["api_token"]
205
- if @appinstance.access_token_changed? && @appinstance.refresh_token_changed?
206
- @appinstance.save(:validate => false)
263
+
264
+ # Validate entity-ids present
265
+ if zuora_entity_id.blank?
266
+ render json: {
267
+ status: 401,
268
+ message: 'zuora-entity-ids header was not supplied.'
269
+ }, status: :unauthorized
270
+ return
271
+ end
272
+
273
+ rest_domain = ZuoraAPI::Login.new(url: "https://#{zuora_host}").rest_domain
274
+ app_instance_id = ZuoraConnect::AppInstance.where(
275
+ 'zuora_entity_ids ?& array[:entities] AND zuora_domain = :host',
276
+ entities: [zuora_entity_id],
277
+ host: rest_domain
278
+ ).pluck(:id).first
279
+
280
+ if app_instance_id.present?
281
+ render json: {
282
+ status: 409,
283
+ message: 'Instance already exists.',
284
+ app_instance_id: app_instance_id
285
+ }, status: 409
207
286
  else
208
- raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mistmatch. Possible tampering")
287
+ Apartment::Tenant.switch!("public")
288
+ retry_count = 3
289
+ begin
290
+ @appinstance = new_instance(
291
+ next_instance_id,
292
+ zuora_entity_id,
293
+ rest_domain,
294
+ tenant_id: request.headers['zuora-tenant-id'],
295
+ retry_count: retry_count
296
+ )
297
+ rescue ActiveRecord::RecordNotUnique
298
+ retry if (retry_count -= 1).positive?
299
+ return
300
+ end
301
+
302
+ app_instance_id = @appinstance.id
209
303
  end
210
- end
211
- end
212
304
 
213
- def setup_instance_via_session
214
- if session["appInstance"].present?
215
- @appinstance = ZuoraConnect::AppInstance.where(:id => session["appInstance"]).first
216
- else
217
- raise ZuoraConnect::Exceptions::SessionInvalid.new("Session Blank -- Relaunch Application")
305
+ begin
306
+ Apartment::Tenant.switch!('public')
307
+ Apartment::Tenant.create(app_instance_id.to_s)
308
+ rescue Apartment::TenantExists
309
+ ZuoraConnect.logger.debug('Tenant Already Exists')
310
+ end
218
311
  end
219
312
  end
220
313
 
221
- def setup_instance_via_dev_mode
222
- session["appInstance"] = ZuoraConnect.configuration.dev_mode_appinstance
223
- user = ZuoraConnect.configuration.dev_mode_user
224
- key = ZuoraConnect.configuration.dev_mode_pass
225
- values = {:user => user , :key => key, :appinstance => session["appInstance"]}
226
- @appinstance = ZuoraConnect::AppInstance.where(:id => values[:appinstance].to_i).first
227
- if @appinstance.blank?
228
- Apartment::Tenant.switch!("public")
314
+ private
315
+ def setup_instance_via_prod_mode
316
+ zuora_entity_id = request.headers['ZuoraCurrentEntity'] || cookies['ZuoraCurrentEntity']
317
+ ZuoraConnect::ZuoraUser.current_user_id = '3'
318
+
319
+ if zuora_entity_id.present?
320
+ zuora_tenant_id = cookies['Zuora-Tenant-Id']
321
+ zuora_user_id = cookies['Zuora-User-Id']
322
+ zuora_host = request.headers['HTTP_X_FORWARDED_HOST'] || request.headers['HTTP_ZUORA_HOST'] || 'apisandbox.zuora.com'
323
+
324
+ zuora_details = {'host' => zuora_host, 'user_id' => zuora_user_id, 'tenant_id' => zuora_tenant_id, 'entity_id' => zuora_entity_id}
325
+ auth_headers = {}
326
+ #Do we need to refresh session identity
327
+ if request.headers["Zuora-Auth-Token"].present?
328
+ 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 )
329
+ elsif cookies['ZSession'].present?
330
+ zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'], entity_id: zuora_entity_id)
331
+ auth_headers.merge!({'Authorization' => "ZSession-a3N2w #{zuora_client.get_session(prefix: false, auth_type: :basic)}"})
332
+ else
333
+ render "zuora_connect/static/error_handled", :locals => {
334
+ :title => "Missing Authorization Token",
335
+ :message => "Zuora 'Zuora-Auth-Token' header and 'ZSession' cookie not present."
336
+ }, :layout => false
337
+ return
338
+ end
339
+
229
340
  begin
230
- Apartment::Tenant.create(values[:appinstance].to_s)
231
- rescue Apartment::TenantExists => ex
232
- Apartment::Tenant.drop(values[:appinstance].to_s)
233
- retry
341
+ zuora_instance_id = params[:sidebar_launch].to_s.to_bool ? nil : (params[:app_instance_id] || session["appInstance"])
342
+
343
+ #Identity blank or current entity different
344
+ different_zsession = session["ZSession"] != cookies['ZSession']
345
+ missmatched_entity = session["ZuoraCurrentEntity"] != zuora_entity_id
346
+ missing_identity = session["ZuoraCurrentIdentity"].blank?
347
+
348
+ if (missing_identity || missmatched_entity || different_zsession)
349
+ zuora_details.merge!({'identity' => {'different_zsession' => different_zsession, 'missing_identity' => missing_identity, 'missmatched_entity' => missmatched_entity}})
350
+ identity, response = zuora_client.rest_call(
351
+ url: zuora_client.rest_endpoint("identity"),
352
+ zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id
353
+ )
354
+
355
+ if zuora_entity_id != identity['entityId']
356
+ if zuora_tenant_id.to_s == "10548"
357
+ session.clear
358
+ render "zuora_connect/static/error_handled", :locals => {
359
+ :title => "Security Testing",
360
+ :message => "Ya we know it you"
361
+ }, :layout => false
362
+ return
363
+ else
364
+ raise ZuoraConnect::Exceptions::Error.new("Header entity id does not match identity call entity id.")
365
+ end
366
+ end
367
+
368
+ ##
369
+ # If the ZSession was refreshed, but it's still the same user and they aren't launching from the side bar,
370
+ # we don't need to continue
371
+ is_different_user = identity.slice("entityId", "tenantId", "userId", "userProfileId") != (session["ZuoraCurrentIdentity"] || {}).slice("entityId", "tenantId", "userId", "userProfileId")
372
+ zuora_details["identity"]["entityId"] = identity['entityId']
373
+ session["ZuoraCurrentIdentity"] = identity
374
+ session["ZuoraCurrentEntity"] = identity['entityId']
375
+ session["ZSession"] = cookies['ZSession']
376
+ if is_different_user || params[:sidebar_launch].to_s.to_bool
377
+ zuora_instance_id = nil
378
+ ZuoraConnect.logger.debug("UI Authorization", zuora: zuora_details)
379
+ client_describe, response = zuora_client.rest_call(
380
+ url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''),
381
+ session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic,
382
+ headers: auth_headers,
383
+ zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id
384
+ )
385
+ session["ZuoraCurrentUserInfo"] = client_describe
386
+ end
387
+ end
388
+
389
+ if zuora_instance_id.present?
390
+ 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)
391
+ else
392
+ #if app_instance_ids is present then permissions still controlled by connect
393
+ if params[:app_instance_ids].present?
394
+ navbar, response = zuora_client.rest_call(
395
+ url: zuora_client.rest_endpoint("navigation"),
396
+ zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id
397
+ )
398
+ urls = navbar['menus'].map {|x| x['url']}
399
+ app_env = ENV["DEIS_APP"] || "xyz123"
400
+ url = urls.compact.select {|url| File.basename(url).start_with?(app_env + '?')}.first
401
+ if url.blank?
402
+ if navbar['menus'].map {|x| x['label']}.include?('Link Connect Account')
403
+ render "zuora_connect/static/error_handled", :locals => {
404
+ :title => "Link Account",
405
+ :message => "Link Connect account to gain access to application."
406
+ }, :layout => false
407
+ return
408
+ end
409
+ raise ZuoraConnect::Exceptions::APIError.new(message: "#{app_env} navbar url was blank", response: response)
410
+ else
411
+ query_params = CGI.parse(URI.parse(url).query)
412
+ app_instance_ids = query_params["app_instance_ids"][0]
413
+ if app_instance_ids.present?
414
+ begin
415
+ task_ids = JSON.parse(Base64.urlsafe_decode64(app_instance_ids))
416
+
417
+ appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name)
418
+ rescue => ex
419
+ raise ZuoraConnect::Exceptions::APIError.new(message: "Failure in parsing the navbar urls.", response: response)
420
+ end
421
+ end
422
+ end
423
+ end
424
+ 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)
425
+ end
426
+
427
+ zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId'] || request.headers["Zuora-User-Id"]
428
+
429
+ if appinstances.size == 1
430
+ ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
431
+ @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first)
432
+ end
433
+
434
+ # One deployed instance with credentials
435
+ if defined?(@appinstance) && !@appinstance['zuora_logins'].nil?
436
+ @zuora_user = ZuoraConnect::ZuoraUser.update_id_response(
437
+ zuora_user_id, zuora_entity_id, session["ZuoraCurrentIdentity"],
438
+ @appinstance,
439
+ session['ZuoraCurrentUserInfo']['permissions']
440
+ )
441
+ @zuora_user.session = session
442
+ ZuoraConnect::ZuoraUser.current_user_id = zuora_user_id
443
+ session["#{@appinstance.id}::user::localUserId"] = @zuora_user.id
444
+ session["#{@appinstance.id}::user::email"] = session['ZuoraCurrentIdentity']["username"]
445
+ session["#{@appinstance.id}::user::timezone"] = session['ZuoraCurrentIdentity']["timeZone"]
446
+ session["#{@appinstance.id}::user::language"] = session['ZuoraCurrentIdentity']["language"]
447
+ session["#{@appinstance.id}::user::locale"] = session['ZuoraCurrentIdentity']["locale"]
448
+ session["appInstance"] = @appinstance.id
449
+
450
+ #We have multiple, user must pick
451
+ elsif appinstances.size > 1
452
+ ZuoraConnect.logger.debug("User must select instance. #{@names}")
453
+ render "zuora_connect/static/launch", :locals => {:names => appinstances.to_h}, :layout => false
454
+ return
455
+
456
+ #We have no deployed instance for this tenant
457
+ else
458
+ if ZuoraConnect.configuration.disable_provisioning
459
+ raise ZuoraConnect::Exceptions::AccessDenied.new("Provisioning is suspended")
460
+ end
461
+
462
+ #Ensure user can access oauth creation API
463
+ if !session["ZuoraCurrentUserInfo"]['permissions'].include?("permission.userManagement")
464
+ Thread.current[:appinstance] = nil
465
+ session["appInstance"] = nil
466
+ render "zuora_connect/static/error_handled", :locals => {
467
+ :title => "Application can only complete its initial setup via platform administrator",
468
+ :message => "Please contact admin who has user managment permissions in tenant and have them click and finish setup."
469
+ }, :layout => false
470
+ return
471
+ end
472
+ Apartment::Tenant.switch!("public")
473
+ retry_count = 3
474
+ task_data = {}
475
+ begin
476
+ ActiveRecord::Base.transaction do
477
+ ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE')
478
+
479
+ unless defined?(@appinstance)
480
+ 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)
481
+
482
+ if appinstances.size > 0
483
+ redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}&pos=0"
484
+ return
485
+ end
486
+ end
487
+
488
+ next_id = defined?(@appinstance) ? @appinstance.id : next_instance_id
489
+ if task_data.blank?
490
+ user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ')
491
+ body = {
492
+ 'userId' => zuora_user_id,
493
+ 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
494
+ 'customAuthorities' => [],
495
+ 'additionalInformation' => {
496
+ 'description' => "This user is for #{user} application.",
497
+ 'name' => "#{user} API User #{next_id}"
498
+ }
499
+ }
500
+
501
+ oauth_response, response = zuora_client.rest_call(
502
+ method: :post,
503
+ body: body.to_json,
504
+ url: zuora_client.rest_endpoint("genesis/clients").gsub('v1/', ''),
505
+ session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic,
506
+ headers: auth_headers,
507
+ zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id
508
+ )
509
+
510
+ new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
511
+ if session["ZuoraCurrentUserInfo"].blank?
512
+ client_describe, response = new_zuora_client.rest_call(
513
+ url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''),
514
+ session_type: :bearer,
515
+ zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id
516
+ )
517
+ else
518
+ client_describe = session["ZuoraCurrentUserInfo"]
519
+ end
520
+
521
+ available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id}
522
+ task_data = {
523
+ "id": next_id,
524
+ "name": client_describe["tenantName"],
525
+ "mode": "Collections",
526
+ "status": "Running",
527
+ ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => {
528
+ "tenant_type": "Zuora",
529
+ "username": session["ZuoraCurrentIdentity"]["username"],
530
+ "url": new_zuora_client.url,
531
+ "status": "Active",
532
+ "oauth_client_id": oauth_response['clientId'],
533
+ "oauth_secret": oauth_response['clientSecret'],
534
+ "authentication_type": "OAUTH",
535
+ "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})}
536
+ },
537
+ "tenant_ids": available_entities.map{|e| e['entityId']}.uniq,
538
+ }
539
+ end
540
+
541
+ if defined?(@appinstance)
542
+ @appinstance.zuora_logins = task_data
543
+ @appinstance.save(:validate => false)
544
+ else
545
+ @appinstance = new_instance(
546
+ next_id,
547
+ zuora_entity_id,
548
+ zuora_client.rest_domain,
549
+ task_data: task_data,
550
+ retry_count: retry_count
551
+ )
552
+ end
553
+ end
554
+ rescue ActiveRecord::RecordNotUnique
555
+ retry if (retry_count -= 1).positive?
556
+ return
557
+ end
558
+
559
+ Apartment::Tenant.switch!("public")
560
+ begin
561
+ Apartment::Tenant.create(@appinstance.id.to_s)
562
+ rescue Apartment::TenantExists => ex
563
+ ZuoraConnect.logger.debug("Tenant Already Exists")
564
+ end
565
+ @appinstance.refresh
566
+ session["appInstance"] = @appinstance.id
567
+ end
568
+
569
+ zuora_org_ids = cookies['Zuora-Org-Ids'] || request.headers['Zuora-Org-Ids']
570
+ ZuoraConnect::ZuoraUser.current_org_ids = []
571
+ ZuoraConnect::ZuoraUser.current_org_ids = zuora_org_ids.split('|') if zuora_org_ids
572
+
573
+ rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
574
+ output_xml, input_xml, response = zuora_client.soap_call(errors: [], z_session: false, zuora_track_id: ZuoraConnect::RequestIdMiddleware.zuora_request_id) do |xml|
575
+ xml['api'].getUserInfo
576
+ end
577
+ final_error = output_xml.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
578
+ session.clear
579
+ if final_error.blank?
580
+ ZuoraConnect.logger.warn("UI Authorization Error", ex, response: { params: response.body })
581
+ elsif final_error != "INVALID_SESSION"
582
+ ZuoraConnect.logger.warn("UI Authorization Error", ex, response: { params: final_error })
583
+ else
584
+ ZuoraConnect.logger.info("UI Authorization Error", ex, response: { params: final_error })
585
+ end
586
+ redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}&pos=1"
587
+ return
588
+
589
+ rescue ZuoraAPI::Exceptions::ZuoraAPIError, Exception => ex
590
+ if ex.message.include?("Referenced User resource(s) not found") && ex.class == ZuoraAPI::Exceptions::ZuoraAPIError
591
+ locals = {title: "Provisioning Error", message: "New tenants need to be provisioned by API Gateway('#{ex.message}'). Please contact support."}
592
+ render "zuora_connect/static/error_handled", locals: locals, status: 200, layout: false
593
+ else
594
+ session.clear
595
+ if defined?(ex.response) && ex.response.present? && defined?(ex.response.body)
596
+ zuora_details.merge!({:error => ex.response.body})
597
+ end
598
+ ZuoraConnect.logger.error("UI Authorization Error", ex, zuora: zuora_details)
599
+
600
+ respond_to do |format|
601
+ format.html {
602
+ render "zuora_connect/static/error_unhandled", locals: {exception: ex, skip_exception: true}, layout: false, status: 500
603
+ }
604
+ format.js {
605
+ render "zuora_connect/static/error_unhandled", locals: {exception: ex, skip_exception: true}, layout: false, status: 500
606
+ }
607
+ end
608
+ end
609
+ return
610
+ end
611
+ 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))
612
+ session.clear
613
+ values = JSON.parse(ZuoraConnect::AppInstance.decrypt_response(Base64.urlsafe_decode64(request["data"])))
614
+ values.fetch("param_data", {}).each do |k ,v|
615
+ params[k] = v
616
+ end
617
+ session["#{values["appInstance"]}::destroy"] = values["destroy"]
618
+ session["appInstance"] = values["appInstance"]
619
+ if values["current_user"]
620
+ session["#{values["appInstance"]}::admin"] = values["current_user"]["admin"] ? values["current_user"]["admin"] : false
621
+ session["#{values["appInstance"]}::user::timezone"] = values["current_user"]["timezone"]
622
+ session["#{values["appInstance"]}::user::locale"] = values["current_user"]["locale"]
623
+ session["#{values["appInstance"]}::user::email"] = values["current_user"]["email"]
234
624
  end
235
625
 
236
- @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]}")
237
- @appinstance.save(:validate => false)
238
- end
239
- if @appinstance.access_token.blank? || @appinstance.refresh_token.blank? || @appinstance.token.blank? || @appinstance.api_token.blank?
240
- @appinstance.update_attributes!(:access_token => values["user"], :refresh_token => values["key"], :token => "#{values[:key]}#{values[:key]}", :api_token => "#{values[:key]}#{values[:key]}")
626
+ @appinstance = ZuoraConnect::AppInstance.find_by(:id => values["appInstance"].to_i)
627
+
628
+ if @appinstance.blank?
629
+ if ZuoraConnect.configuration.disable_provisioning
630
+ raise ZuoraConnect::Exceptions::AccessDenied.new("Provisioning is suspended")
631
+ end
632
+
633
+ Apartment::Tenant.switch!("public")
634
+ begin
635
+ Apartment::Tenant.create(values["appInstance"].to_s)
636
+ rescue Apartment::TenantExists => ex
637
+ ZuoraConnect.logger.debug("Tenant Already Exists")
638
+ end
639
+ 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"]}
640
+ @appinstance = ZuoraConnect::AppInstance.new(mapped_values.merge({:id => values["appInstance"].to_i}))
641
+ @appinstance.save(:validate => false)
642
+ else
643
+ mapped_values = {:access_token => values["access_token"], :refresh_token => values["refresh_token"], :oauth_expires_at => values["expires"]}
644
+ @appinstance.assign_attributes(mapped_values)
645
+ if @appinstance.access_token_changed? && @appinstance.refresh_token_changed?
646
+ @appinstance.save(:validate => false)
647
+ else
648
+ raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mismatch. Possible tampering with session.")
649
+ end
650
+ end
651
+ else
652
+ if session["appInstance"].present?
653
+ @appinstance = ZuoraConnect::AppInstance.find_by(:id => session["appInstance"])
654
+ else
655
+ if ZuoraConnect::AppInstance::INTERNAL_HOSTS.include?(request.headers.fetch("HOST", nil))
656
+ render "zuora_connect/application/ldap_login", :layout => false
657
+ return
658
+ else
659
+ raise ZuoraConnect::Exceptions::AccessDenied.new("No application state or session found.")
660
+ end
661
+ end
241
662
  end
242
- session["#{@appinstance.id}::admin"] = ZuoraConnect.configuration.dev_mode_admin
243
663
  end
244
664
 
245
- #API ONLY
246
- def check_instance
247
- if @appinstance.present?
248
- if @appinstance.new_session_for_api_requests(:params => params)
249
- @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
250
- end
251
- Thread.current[:appinstance] = @appinstance
252
- PaperTrail.whodunnit = "API User" if defined?(PaperTrail)
253
- ElasticAPM.set_user("API User") if defined?(ElasticAPM)
254
- return true
665
+ def next_instance_id
666
+ min_instance_id = 24_999_999
667
+ (ZuoraConnect::AppInstance.all.where("id > #{min_instance_id}").order(id: :desc).limit(1).pluck(:id).first || min_instance_id) + 1
668
+ end
669
+
670
+ def new_instance(id, zuora_entity_id, rest_domain, tenant_id: nil, task_data: nil, retry_count: 0)
671
+ app_instance = ZuoraConnect::AppInstance.new(
672
+ :id => id,
673
+ :api_token => generate_token,
674
+ :token => generate_token,
675
+ :oauth_expires_at => Time.now + 1000.years,
676
+ :zuora_domain => rest_domain,
677
+ :zuora_entity_ids => [zuora_entity_id]
678
+ )
679
+
680
+ app_instance[:zuora_tenant_ids] = [tenant_id.to_s] if tenant_id.present?
681
+
682
+ if task_data.nil?
683
+ # no encryption
684
+ app_instance['zuora_logins'] = task_data
255
685
  else
256
- render text: "Access Denied", status: :unauthorized
686
+ # kms encrypt
687
+ app_instance.zuora_logins = task_data
257
688
  end
689
+
690
+ begin
691
+ app_instance.save(:validate => false)
692
+ rescue ActiveRecord::RecordNotUnique
693
+ raise if retry_count > 1
694
+
695
+ Thread.current[:appinstance] = nil
696
+ session['appInstance'] = nil
697
+ render 'zuora_connect/static/error_handled', :locals => {
698
+ :title => 'Application could not create unique tokens.',
699
+ :message => 'Please contact support or retry launching application.'
700
+ }, :layout => false
701
+ return
702
+ end
703
+
704
+ app_instance
705
+ end
706
+
707
+ def generate_token
708
+ rand(36**64).to_s(36)
258
709
  end
259
710
  end
260
711
  end