zuora_connect 2.0.31 → 2.0.32

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4c353323b9470a1ddfa8b1a59c7a2ecc16ccaba034a04afc3a4debc3c33a012
4
- data.tar.gz: 9f24e15e3da5bd757a7ae5f9bb61b5e6d5a817e3e0ec23d2c7de129ff283ed21
3
+ metadata.gz: 935b69f5db037f12613d00742bdbff89ebe6b2e412a15144abe9df3a5523a86b
4
+ data.tar.gz: 584df5dda94a7b1401fae7f7928edfad7787aad46bef34a16a649919b9a9d151
5
5
  SHA512:
6
- metadata.gz: be6512b18a580dcc897598062fd12e7104f31f8cb611cc3a9c9c7fbc252ff867ea4f58de5e645679e711a7746ed4e9baae5e2254f0efb608d3028fbc233b73fe
7
- data.tar.gz: 0d3f8a0af8cb7c0aaf863f2641b59c6d7f252262fab6fc9812da525a64bb7dd248adff8230c203fa2990d2a164c68ac24d7194f2b8b3b2d9bf26957b39e1a322
6
+ metadata.gz: 4d0bb0a6e44d720c8e835a71c54cd74e03c282504088c7672a71ca868a82185d0aac5adf658a049d97040621225cd8def662d712a84ea28f802728184eaab7ba
7
+ data.tar.gz: 149ceb2eb52f955054e54c4ffe4ef6cd81e0026eab0bc9da5780072675c739640f604c36fe3fa9a75d27a8a181758ef0fe0f70994ddc89d1970cbb7c7a5f235e
@@ -256,75 +256,73 @@ module ZuoraConnect
256
256
  end
257
257
  end
258
258
 
259
- def refresh(session: {}, session_fallback: false)
259
+ def refresh(session: {})
260
260
  refresh_count ||= 0
261
261
  skip_connect ||= false
262
- #Check how app was deployed
263
- if self.id < 25000000 && !skip_connect
264
- start = Time.now
265
- response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
266
- response_time = Time.now - start
267
-
268
- ZuoraConnect.logger.debug("REFRESH TASK - Connect Task Info Request Time #{response_time.round(2).to_s}", self.default_ougai_items)
269
- if response.code == 200
270
- begin
271
- parsed_json = JSON.parse(response.body)
272
- rescue JSON::ParserError => ex
273
- raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("JSON parse error", response.body, response.code)
274
- end
275
-
276
- self.build_task(task_data: parsed_json, session: session)
277
- if self.kms_key.present?
262
+ begin
263
+ #Check how app was deployed
264
+ if self.id < 25000000 && !skip_connect
265
+ self.check_oauth_state
266
+ start = Time.now
267
+ response = HTTParty.get(ZuoraConnect.configuration.url + "/api/#{self.api_version}/tools/tasks/#{self.id}.json",:body => {:access_token => self.access_token})
268
+ response_time = Time.now - start
269
+
270
+ ZuoraConnect.logger.debug("REFRESH TASK - Connect Task Info Request Time #{response_time.round(2).to_s}", self.default_ougai_items)
271
+ if response.code == 200
278
272
  begin
279
- parsed_json.delete('applications')
280
- parsed_json.delete('tokens')
281
- self.zuora_logins = parsed_json
282
- self.save(:validate => false)
283
- rescue Aws::KMS::Errors::ValidationException, *AWS_AUTH_ERRORS => ex
284
- Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
285
- rescue => ex
286
- Rails.logger.error(AWS_AUTH_ERRORS_MSG, ex)
273
+ parsed_json = JSON.parse(response.body)
274
+ rescue JSON::ParserError => ex
275
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("JSON parse error", response.body, response.code)
287
276
  end
288
- end
277
+
278
+ self.build_task(task_data: parsed_json, session: session)
279
+ if self.kms_key.present?
280
+ begin
281
+ self.zuora_logins = self.strip_cache_data(object: parsed_json.dup, keys: ['applications', 'tokens', 'user_settings'])
282
+ self.save(:validate => false)
283
+ rescue Aws::KMS::Errors::ValidationException, *AWS_AUTH_ERRORS => ex
284
+ Rails.logger.warn(AWS_AUTH_ERRORS_MSG, ex)
285
+ rescue => ex
286
+ Rails.logger.error(AWS_AUTH_ERRORS_MSG, ex)
287
+ end
288
+ end
289
+ else
290
+ raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
291
+ end
289
292
  else
290
- raise ZuoraConnect::Exceptions::ConnectCommunicationError.new("Error Communicating with Connect", response.body, response.code)
293
+ self.build_task(task_data: self.zuora_logins, session: session)
291
294
  end
292
- else
293
- self.build_task(task_data: self.zuora_logins, session: session)
294
- end
295
- self.last_refresh = Time.now.to_i
296
- self.cache_app_instance
297
- self.reset_mark_for_refresh
298
- rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
299
- refresh_count += 1
300
- if refresh_count < 3
301
- sleep(10)
302
- ZuoraConnect.logger.debug("REFRESH TASK - Connection Failure Retrying(#{refresh_count})", ex, self.default_ougai_items)
303
- retry
304
- elsif refresh_count < 4 && self['zuora_logins'].present?
305
- ZuoraConnect.logger.warn("REFRESH TASK - Fallback to local encrypted store", ex, self.default_ougai_items)
306
- skip_connect = true
307
- retry
308
- else
309
- ZuoraConnect.logger.fatal("REFRESH TASK - Connection Failed", ex, self.default_ougai_items)
310
- raise
311
- end
312
- rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
313
- refresh_count += 1
314
- if refresh_count < 3
315
- ZuoraConnect.logger.debug("REFRESH TASK - Communication Failure Retrying(#{refresh_count})", ex, self.default_ougai_items)
316
- if ex.code == 401
317
- self.refresh_oauth
295
+ self.last_refresh = Time.now.to_i
296
+ self.cache_app_instance
297
+ self.reset_mark_for_refresh
298
+ rescue *(ZuoraAPI::Login::CONNECTION_EXCEPTIONS + ZuoraAPI::Login::CONNECTION_READ_EXCEPTIONS) => ex
299
+ refresh_count += 1
300
+ if refresh_count < 3
301
+ sleep(10)
302
+ ZuoraConnect.logger.debug("REFRESH TASK - Connection Failure Retrying(#{refresh_count})", ex, self.default_ougai_items)
303
+ retry
304
+ else
305
+ ZuoraConnect.logger.fatal("REFRESH TASK - Connection Failed", ex, self.default_ougai_items)
306
+ raise
318
307
  end
319
- retry
320
- elsif refresh_count < 4 && self['zuora_logins'].present?
308
+ rescue ZuoraConnect::Exceptions::ConnectCommunicationError => ex
309
+ refresh_count += 1
310
+ if refresh_count < 3
311
+ ZuoraConnect.logger.debug("REFRESH TASK - Communication Failure Retrying(#{refresh_count})", ex, self.default_ougai_items)
312
+ self.refresh_oauth if ex.code == 401
313
+ retry
314
+ else
315
+ ZuoraConnect.logger.fatal("REFRESH TASK - Communication Failed #{ex.code}", ex, self.default_ougai_items)
316
+ raise
317
+ end
318
+ end
319
+ rescue => ex
320
+ if self['zuora_logins'].present?
321
321
  ZuoraConnect.logger.warn("REFRESH TASK - Fallback to local encrypted store", ex, self.default_ougai_items)
322
322
  skip_connect = true
323
323
  retry
324
- else
325
- ZuoraConnect.logger.fatal("REFRESH TASK - Communication Failed #{ex.code}", ex, self.default_ougai_items)
326
- raise
327
324
  end
325
+ raise
328
326
  end
329
327
 
330
328
  #### START KMS ENCRYPTION Methods ####
@@ -510,7 +508,7 @@ module ZuoraConnect
510
508
  rescue ZuoraConnect::Exceptions::InvalidCredentialSet => ex
511
509
  raise
512
510
  rescue => ex
513
- ZuoraConnect.logger.error(ex)
511
+ ZuoraConnect.logger.error("Build Task Error", ex)
514
512
  ZuoraConnect.logger.error("Task Data: #{task_data}") if task_data.present?
515
513
  if session.present?
516
514
  ZuoraConnect.logger.error("Task Session: #{session.to_h}") if session.methods.include?(:to_h)
@@ -588,7 +586,7 @@ module ZuoraConnect
588
586
  #### END Task Methods ####
589
587
 
590
588
  #### START Connect OAUTH Methods ####
591
- def check_oauth_state(method)
589
+ def check_oauth_state(method=nil)
592
590
  #Refresh token if already expired
593
591
  if self.oauth_expired?
594
592
  ZuoraConnect.logger.debug("Before '#{method}' method, Oauth expired", self.default_ougai_items)
@@ -738,13 +736,7 @@ module ZuoraConnect
738
736
 
739
737
  #Redis is not defined strip out old data
740
738
  if !defined?(Redis.current)
741
- session["#{self.id}::task_data"].delete('applications')
742
- session["#{self.id}::task_data"].delete('tokens')
743
- session["#{self.id}::task_data"].delete('tenant_ids')
744
- session["#{self.id}::task_data"].delete('organizations')
745
- session["#{self.id}::task_data"].select {|k,v| k.include?('login') && v['tenant_type'] == 'Zuora'}.each do |login_key, login_data|
746
- session["#{self.id}::task_data"][login_key]['entities'] = (login_data.dig('entities') || []).map {|entity| entity.slice('id', 'tenantId')}
747
- end
739
+ strip_cache_data(object: session["#{self.id}::task_data"])
748
740
  end
749
741
 
750
742
  session["#{self.id}::last_refresh"] = self.last_refresh
@@ -752,6 +744,14 @@ module ZuoraConnect
752
744
  return session
753
745
  end
754
746
 
747
+ def strip_cache_data(object: {}, keys: ['applications', 'tokens','tenant_ids', 'organizations','user_settings'] )
748
+ keys.each {|key| object.delete(key) }
749
+ object.select {|k,v| k.include?('login') && v['tenant_type'] == 'Zuora'}.each do |login_key, login_data|
750
+ object[login_key]['entities'] = login_data.fetch('entities',[]).map {|entity| entity.slice('id', 'tenantId', 'entityId')}
751
+ end
752
+ return object
753
+ end
754
+
755
755
  def encryptor
756
756
  # Default values for Rails 4 apps
757
757
  key_iter_num, key_size, salt, signed_salt = [1000, 64, "encrypted cookie", "signed encrypted cookie"]
@@ -1148,7 +1148,7 @@ module ZuoraConnect
1148
1148
  super
1149
1149
  end
1150
1150
 
1151
- method_hook :refresh, :updateOption, :update_logins, :before => :check_oauth_state
1151
+ method_hook :updateOption, :update_logins, :before => :check_oauth_state
1152
1152
  method_hook :new_session, :refresh, :build_task, :after => :apartment_switch
1153
1153
  end
1154
1154
  end
@@ -96,239 +96,7 @@ module ZuoraConnect
96
96
  start_time = Time.now
97
97
 
98
98
  if ZuoraConnect.configuration.mode == "Production"
99
- zuora_entity_id = request.headers['ZuoraCurrentEntity'] || cookies['ZuoraCurrentEntity']
100
-
101
- if zuora_entity_id.present?
102
- zuora_tenant_id = cookies['Zuora-Tenant-Id']
103
- zuora_user_id = cookies['Zuora-User-Id']
104
- zuora_host = request.headers["HTTP_X_FORWARDED_HOST"] || "apisandbox.zuora.com"
105
-
106
- zuora_details = {'host' => zuora_host, 'user_id' => zuora_user_id, 'tenant_id' => zuora_tenant_id, 'entity_id' => zuora_entity_id}
107
-
108
- #Do we need to refresh session identity
109
- if request.headers["Zuora-Auth-Token"].present?
110
- zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", bearer_token: request.headers["Zuora-Auth-Token"], oauth_session_expires_at: Time.now + 5.minutes )
111
- elsif cookies['ZSession'].present?
112
- zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'])
113
- else
114
- render "zuora_connect/static/error_handled", :locals => {
115
- :title => "Missing Authorization Token",
116
- :message => "Zuora 'Zuora-Auth-Token' header and 'ZSession' cookie not present."
117
- }, :layout => false
118
- return
119
- end
120
-
121
- begin
122
- zuora_instance_id = params[:sidebar_launch].to_s.to_bool ? nil : (params[:app_instance_id] || session["appInstance"])
123
-
124
- #Identity blank or current entity different
125
- different_zsession = session["ZSession"] != cookies['ZSession']
126
- missmatched_entity = session["ZuoraCurrentEntity"] != zuora_entity_id
127
- missing_identity = session["ZuoraCurrentIdentity"].blank?
128
-
129
- if (missing_identity || missmatched_entity || different_zsession)
130
- zuora_details.merge!({'identity' => {'different_zsession' => different_zsession, 'missing_identity' => missing_identity, 'missmatched_entity' => missmatched_entity}})
131
- identity, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("identity"))
132
- session["ZuoraCurrentIdentity"] = identity
133
- session["ZuoraCurrentEntity"] = identity['entityId']
134
- session["ZSession"] = cookies['ZSession']
135
- zuora_instance_id = nil
136
- zuora_details["identity"]["entityId"] = identity['entityId']
137
-
138
- 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)}"})
139
- session["ZuoraCurrentUserInfo"] = client_describe
140
-
141
- raise ZuoraConnect::Exceptions::Error.new("Header entity id does not match identity call entity id.") if zuora_entity_id != identity['entityId']
142
- end
143
-
144
- #Find matching app instances.
145
- if zuora_instance_id.present?
146
- 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)
147
- else
148
- #if app_instance_ids is present then permissions still controlled by connect
149
- if params[:app_instance_ids].present?
150
- navbar, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("navigation"))
151
- urls = navbar['menus'].map {|x| x['url']}
152
- app_env = ENV["DEIS_APP"] || "xyz123"
153
- url = urls.compact.select {|url| File.basename(url).start_with?(app_env + '?')}.first
154
- begin
155
- task_ids = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(url).query)["app_instance_ids"][0]))
156
- rescue URI::InvalidURIError => ex
157
- raise ZuoraConnect::Exceptions::APIError.new(message: "Failure in parsing the navbar urls.", response: response)
158
- end
159
- appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name)
160
- else
161
- 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)
162
- end
163
- end
164
-
165
- zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId']
166
-
167
- #One deployed instance
168
- if appinstances.size == 1
169
- ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
170
- @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first)
171
-
172
- #Add user/update
173
- begin
174
- @zuora_user = ZuoraConnect::ZuoraUser.where(:zuora_user_id => zuora_user_id).first
175
- rescue ActiveRecord::StatementInvalid => ex
176
- if ex.message.include?("PG::UndefinedTable") && ex.message.include?("zuora_users")
177
- self.apartment_switch(nil,true)
178
- retry
179
- else
180
- raise
181
- end
182
- end
183
- if @zuora_user.present?
184
- ZuoraConnect.logger.debug("Current zuora user #{zuora_user_id}")
185
- if @zuora_user.updated_at < Time.now - 1.day
186
- @zuora_user.zuora_identity_response[zuora_entity_id] = session["ZuoraCurrentIdentity"]
187
- @zuora_user.save!
188
- end
189
- else
190
- ZuoraConnect.logger.debug("New zuora user object for #{zuora_user_id}")
191
- @zuora_user = ZuoraConnect::ZuoraUser.create!(:zuora_user_id => zuora_user_id, :zuora_identity_response => {zuora_entity_id => session["ZuoraCurrentIdentity"]})
192
- end
193
- @zuora_user.session = session
194
- session["#{@appinstance.id}::user::localUserId"] = @zuora_user.id
195
- session["#{@appinstance.id}::user::email"] = session['ZuoraCurrentIdentity']["username"]
196
- session["#{@appinstance.id}::user::timezone"] = session['ZuoraCurrentIdentity']["timeZone"]
197
- session["#{@appinstance.id}::user::locale"] = session['ZuoraCurrentIdentity']["language"]
198
- session["appInstance"] = @appinstance.id
199
-
200
- #We have multiple, user must pick
201
- elsif appinstances.size > 1
202
- ZuoraConnect.logger.debug("User must select instance. #{@names}")
203
- render "zuora_connect/static/launch", :locals => {:names => appinstances.to_h}, :layout => false
204
- return
205
-
206
- #We have no deployed instance for this tenant
207
- else
208
- #Ensure user can access oauth creation API
209
- if session["ZuoraCurrentIdentity"]['platformRole'] != 'ADMIN'
210
- Thread.current[:appinstance] = nil
211
- session["appInstance"] = nil
212
- render "zuora_connect/static/error_handled", :locals => {
213
- :title => "Application can only complete its initial setup via platform administrator",
214
- :message => "Please contact admin of tenant and have them click on link again to launch application."
215
- }, :layout => false
216
- return
217
- end
218
- Apartment::Tenant.switch!("public")
219
- ActiveRecord::Base.transaction do
220
- ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE')
221
- 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)
222
-
223
- if appinstances.size > 0
224
- redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
225
- return
226
- end
227
-
228
- next_id = (ZuoraConnect::AppInstance.all.where('id > 24999999').order(id: :desc).limit(1).pluck(:id).first || 24999999) + 1
229
- user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ')
230
- body = {
231
- 'userId' => zuora_user_id,
232
- 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
233
- 'customAuthorities' => [],
234
- 'additionalInformation' => {
235
- 'description' => "This user is for #{user} application.",
236
- 'name' => "#{user} API User #{next_id}"
237
- }
238
- }
239
-
240
- 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)}"})
241
-
242
- new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
243
- if session["ZuoraCurrentUserInfo"].blank?
244
- client_describe, response = new_zuora_client.rest_call(url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
245
- else
246
- client_describe = session["ZuoraCurrentUserInfo"]
247
- end
248
-
249
- available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id}
250
- task_data = {
251
- "id": next_id,
252
- "name": client_describe["tenantName"],
253
- "mode": "Collections",
254
- "status": "Running",
255
- ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => {
256
- "tenant_type": "Zuora",
257
- "username": session["ZuoraCurrentIdentity"]["username"],
258
- "url": new_zuora_client.url,
259
- "status": "Active",
260
- "oauth_client_id": oauth_response['clientId'],
261
- "oauth_secret": oauth_response['clientSecret'],
262
- "authentication_type": "OAUTH",
263
- "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})}
264
- },
265
- "tenant_ids": available_entities.map{|e| e['entityId']}.uniq,
266
- }
267
- 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]}
268
- @appinstance = ZuoraConnect::AppInstance.new(mapped_values)
269
- retry_count = 0
270
- begin
271
- @appinstance.save(:validate => false)
272
- rescue ActiveRecord::RecordNotUnique => ex
273
- if (retry_count += 1) < 3
274
- @appinstance.assign_attributes({:api_token => rand(36**64).to_s(36), :token => rand(36**64).to_s(36)})
275
- retry
276
- else
277
- Thread.current[:appinstance] = nil
278
- session["appInstance"] = nil
279
- render "zuora_connect/static/error_handled", :locals => {
280
- :title => "Application could not create unique tokens.",
281
- :message => "Please contact support or retry launching application."
282
- }, :layout => false
283
- return
284
- end
285
- end
286
- end
287
-
288
- Apartment::Tenant.switch!("public")
289
- begin
290
- Apartment::Tenant.create(@appinstance.id.to_s)
291
- rescue Apartment::TenantExists => ex
292
- ZuoraConnect.logger.debug("Tenant Already Exists")
293
- end
294
- @appinstance.refresh
295
- session["appInstance"] = @appinstance.id
296
- end
297
-
298
- rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
299
- output_xml, input_xml, response = zuora_client.soap_call(errors: [], z_session: false) do |xml|
300
- xml['api'].getUserInfo
301
- end
302
- final_error = output_xml.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
303
- session.clear
304
- if final_error.blank?
305
- ZuoraConnect.logger.warn("UI Authorization Error", ex, zuora: zuora_details.merge({:error => response.body}))
306
- elsif final_error != "INVALID_SESSION"
307
- ZuoraConnect.logger.warn("UI Authorization Error", ex, zuora: zuora_details.merge({:error => final_error}))
308
- end
309
- redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
310
- return
311
- rescue => ex
312
- if defined?(ex.response) && ex.response.present? && defined?(ex.response.body)
313
- zuora_details.merge!({:error => ex.response.body})
314
- end
315
- ZuoraConnect.logger.error("UI Authorization Error", ex, zuora: zuora_details)
316
- render "zuora_connect/static/error_unhandled", :locals => {:exception => ex}, :layout => false
317
- return
318
- end
319
- 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)
320
- setup_instance_via_data
321
- else
322
- if session["appInstance"].present?
323
- @appinstance = ZuoraConnect::AppInstance.where(:id => session["appInstance"]).first
324
- else
325
- render "zuora_connect/static/error_handled", :locals => {
326
- :title => "Application state could not be verified",
327
- :message => "Please relaunch application."
328
- }, :layout => false
329
- return
330
- end
331
- end
99
+ setup_instance_via_prod_mode
332
100
  else
333
101
  setup_instance_via_dev_mode
334
102
  end
@@ -376,6 +144,24 @@ module ZuoraConnect
376
144
  deployment and create new with Zuora Basic or OAuth credentials."
377
145
  }, :layout => false
378
146
  return
147
+ rescue ZuoraConnect::Exceptions::AccessDenied => ex
148
+ respond_to do |format|
149
+ format.html {
150
+ render "zuora_connect/static/error_handled", :locals => {
151
+ :title => "Application State Error",
152
+ :message => ex.message
153
+ }, status: 401
154
+ }
155
+ format.js {
156
+ render "zuora_connect/static/error_handled", :locals => {
157
+ :title => "Application State Error",
158
+ :message => ex.message
159
+ }, status: 401
160
+ }
161
+ format.json { render json: {'errors' => ex.message}, status: 401 }
162
+ format.all { render json: ex.message, status: 401 }
163
+ end
164
+ return
379
165
  rescue => ex
380
166
  ZuoraConnect.logger.error("UI Authorization Error", ex)
381
167
  respond_to do |format|
@@ -395,8 +181,28 @@ module ZuoraConnect
395
181
  end
396
182
  end
397
183
 
398
- def check_connect_admin!
399
- raise ZuoraConnect::Exceptions::AccessDenied.new("User is not an authorized admin for this application") if !session["#{@appinstance.id}::admin"]
184
+ def check_connect_admin!(raise_error: false)
185
+ if !session["#{@appinstance.id}::admin"]
186
+ raise ZuoraConnect::Exceptions::AccessDenied.new("User is not an authorized admin for this application") if raise_error
187
+
188
+ respond_to do |format|
189
+ format.html {
190
+ render "zuora_connect/static/error_handled", :locals => {
191
+ :title => "Unauthorized",
192
+ :message => "User is not an authorized admin for this application"
193
+ }, status: 401
194
+ }
195
+ format.js {
196
+ render "zuora_connect/static/error_handled", :locals => {
197
+ :title => "Unauthorized",
198
+ :message => "User is not an authorized admin for this application"
199
+ }, status: 401
200
+ }
201
+ format.json { render json: {'errors' => ex.message}, status: 401 }
202
+ format.all { render json: ex.message, status: 401 }
203
+ end
204
+ return
205
+ end
400
206
  end
401
207
 
402
208
  def check_connect_admin
@@ -412,46 +218,272 @@ module ZuoraConnect
412
218
  end
413
219
 
414
220
  private
415
- def setup_instance_via_data
416
- session.clear
417
- values = JSON.parse(ZuoraConnect::AppInstance.decrypt_response(Base64.urlsafe_decode64(request["data"])))
418
- if values["param_data"]
419
- values["param_data"].each do |k ,v|
420
- params[k] = v
421
- end
422
- end
423
- session["#{values["appInstance"]}::destroy"] = values["destroy"]
424
- session["appInstance"] = values["appInstance"]
425
- if values["current_user"]
426
- session["#{values["appInstance"]}::admin"] = values["current_user"]["admin"] ? values["current_user"]["admin"] : false
427
- session["#{values["appInstance"]}::user::timezone"] = values["current_user"]["timezone"]
428
- session["#{values["appInstance"]}::user::locale"] = values["current_user"]["locale"]
429
- session["#{values["appInstance"]}::user::email"] = values["current_user"]["email"]
430
- end
221
+ def setup_instance_via_prod_mode
222
+ zuora_entity_id = request.headers['ZuoraCurrentEntity'] || cookies['ZuoraCurrentEntity']
431
223
 
432
- ZuoraConnect.logger.debug({msg: 'Setup values', connect: values}) if Rails.env != "production"
224
+ if zuora_entity_id.present?
225
+ zuora_tenant_id = cookies['Zuora-Tenant-Id']
226
+ zuora_user_id = cookies['Zuora-User-Id']
227
+ zuora_host = request.headers["HTTP_X_FORWARDED_HOST"] || "apisandbox.zuora.com"
433
228
 
434
- @appinstance = ZuoraConnect::AppInstance.where(:id => values["appInstance"].to_i).first
229
+ zuora_details = {'host' => zuora_host, 'user_id' => zuora_user_id, 'tenant_id' => zuora_tenant_id, 'entity_id' => zuora_entity_id}
230
+
231
+ #Do we need to refresh session identity
232
+ if request.headers["Zuora-Auth-Token"].present?
233
+ zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", bearer_token: request.headers["Zuora-Auth-Token"], oauth_session_expires_at: Time.now + 5.minutes )
234
+ elsif cookies['ZSession'].present?
235
+ zuora_client = ZuoraAPI::Basic.new(url: "https://#{zuora_host}", session: cookies['ZSession'])
236
+ else
237
+ render "zuora_connect/static/error_handled", :locals => {
238
+ :title => "Missing Authorization Token",
239
+ :message => "Zuora 'Zuora-Auth-Token' header and 'ZSession' cookie not present."
240
+ }, :layout => false
241
+ return
242
+ end
435
243
 
436
- if @appinstance.blank?
437
- Apartment::Tenant.switch!("public")
438
244
  begin
439
- Apartment::Tenant.create(values["appInstance"].to_s)
440
- rescue Apartment::TenantExists => ex
441
- ZuoraConnect.logger.debug("Tenant Already Exists")
245
+ zuora_instance_id = params[:sidebar_launch].to_s.to_bool ? nil : (params[:app_instance_id] || session["appInstance"])
246
+
247
+ #Identity blank or current entity different
248
+ different_zsession = session["ZSession"] != cookies['ZSession']
249
+ missmatched_entity = session["ZuoraCurrentEntity"] != zuora_entity_id
250
+ missing_identity = session["ZuoraCurrentIdentity"].blank?
251
+
252
+ if (missing_identity || missmatched_entity || different_zsession)
253
+ zuora_details.merge!({'identity' => {'different_zsession' => different_zsession, 'missing_identity' => missing_identity, 'missmatched_entity' => missmatched_entity}})
254
+ identity, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("identity"))
255
+ session["ZuoraCurrentIdentity"] = identity
256
+ session["ZuoraCurrentEntity"] = identity['entityId']
257
+ session["ZSession"] = cookies['ZSession']
258
+ zuora_instance_id = nil
259
+ zuora_details["identity"]["entityId"] = identity['entityId']
260
+
261
+ 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)}"})
262
+ session["ZuoraCurrentUserInfo"] = client_describe
263
+
264
+ raise ZuoraConnect::Exceptions::Error.new("Header entity id does not match identity call entity id.") if zuora_entity_id != identity['entityId']
265
+ end
266
+
267
+ #Find matching app instances.
268
+ if zuora_instance_id.present?
269
+ 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)
270
+ else
271
+ #if app_instance_ids is present then permissions still controlled by connect
272
+ if params[:app_instance_ids].present?
273
+ navbar, response = zuora_client.rest_call(url: zuora_client.rest_endpoint("navigation"))
274
+ urls = navbar['menus'].map {|x| x['url']}
275
+ app_env = ENV["DEIS_APP"] || "xyz123"
276
+ url = urls.compact.select {|url| File.basename(url).start_with?(app_env + '?')}.first
277
+ begin
278
+ task_ids = JSON.parse(Base64.urlsafe_decode64(CGI.parse(URI.parse(url).query)["app_instance_ids"][0]))
279
+ rescue URI::InvalidURIError => ex
280
+ raise ZuoraConnect::Exceptions::APIError.new(message: "Failure in parsing the navbar urls.", response: response)
281
+ end
282
+ appinstances = ZuoraConnect::AppInstance.where(:id => task_ids).pluck(:id, :name)
283
+ else
284
+ 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)
285
+ end
286
+ end
287
+
288
+ zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId']
289
+
290
+ #One deployed instance
291
+ if appinstances.size == 1
292
+ ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
293
+ @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first)
294
+
295
+ #Add user/update
296
+ begin
297
+ @zuora_user = ZuoraConnect::ZuoraUser.where(:zuora_user_id => zuora_user_id).first
298
+ rescue ActiveRecord::StatementInvalid => ex
299
+ if ex.message.include?("PG::UndefinedTable") && ex.message.include?("zuora_users")
300
+ @appinstance.apartment_switch(nil,true)
301
+ retry
302
+ else
303
+ raise
304
+ end
305
+ end
306
+ if @zuora_user.present?
307
+ ZuoraConnect.logger.debug("Current zuora user #{zuora_user_id}")
308
+ if @zuora_user.updated_at < Time.now - 1.day
309
+ @zuora_user.zuora_identity_response[zuora_entity_id] = session["ZuoraCurrentIdentity"]
310
+ @zuora_user.save!
311
+ end
312
+ else
313
+ ZuoraConnect.logger.debug("New zuora user object for #{zuora_user_id}")
314
+ @zuora_user = ZuoraConnect::ZuoraUser.create!(:zuora_user_id => zuora_user_id, :zuora_identity_response => {zuora_entity_id => session["ZuoraCurrentIdentity"]})
315
+ end
316
+ @zuora_user.session = session
317
+ session["#{@appinstance.id}::user::localUserId"] = @zuora_user.id
318
+ session["#{@appinstance.id}::user::email"] = session['ZuoraCurrentIdentity']["username"]
319
+ session["#{@appinstance.id}::user::timezone"] = session['ZuoraCurrentIdentity']["timeZone"]
320
+ session["#{@appinstance.id}::user::locale"] = session['ZuoraCurrentIdentity']["language"]
321
+ session["appInstance"] = @appinstance.id
322
+
323
+ #We have multiple, user must pick
324
+ elsif appinstances.size > 1
325
+ ZuoraConnect.logger.debug("User must select instance. #{@names}")
326
+ render "zuora_connect/static/launch", :locals => {:names => appinstances.to_h}, :layout => false
327
+ return
328
+
329
+ #We have no deployed instance for this tenant
330
+ else
331
+ #Ensure user can access oauth creation API
332
+ if session["ZuoraCurrentIdentity"]['platformRole'] != 'ADMIN'
333
+ Thread.current[:appinstance] = nil
334
+ session["appInstance"] = nil
335
+ render "zuora_connect/static/error_handled", :locals => {
336
+ :title => "Application can only complete its initial setup via platform administrator",
337
+ :message => "Please contact admin of tenant and have them click on link again to launch application."
338
+ }, :layout => false
339
+ return
340
+ end
341
+ Apartment::Tenant.switch!("public")
342
+ ActiveRecord::Base.transaction do
343
+ ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE')
344
+ 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)
345
+
346
+ if appinstances.size > 0
347
+ redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
348
+ return
349
+ end
350
+
351
+ next_id = (ZuoraConnect::AppInstance.all.where('id > 24999999').order(id: :desc).limit(1).pluck(:id).first || 24999999) + 1
352
+ user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ')
353
+ body = {
354
+ 'userId' => zuora_user_id,
355
+ 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
356
+ 'customAuthorities' => [],
357
+ 'additionalInformation' => {
358
+ 'description' => "This user is for #{user} application.",
359
+ 'name' => "#{user} API User #{next_id}"
360
+ }
361
+ }
362
+
363
+ 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)}"})
364
+
365
+ new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
366
+ if session["ZuoraCurrentUserInfo"].blank?
367
+ client_describe, response = new_zuora_client.rest_call(url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
368
+ else
369
+ client_describe = session["ZuoraCurrentUserInfo"]
370
+ end
371
+
372
+ available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id}
373
+ task_data = {
374
+ "id": next_id,
375
+ "name": client_describe["tenantName"],
376
+ "mode": "Collections",
377
+ "status": "Running",
378
+ ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => {
379
+ "tenant_type": "Zuora",
380
+ "username": session["ZuoraCurrentIdentity"]["username"],
381
+ "url": new_zuora_client.url,
382
+ "status": "Active",
383
+ "oauth_client_id": oauth_response['clientId'],
384
+ "oauth_secret": oauth_response['clientSecret'],
385
+ "authentication_type": "OAUTH",
386
+ "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})}
387
+ },
388
+ "tenant_ids": available_entities.map{|e| e['entityId']}.uniq,
389
+ }
390
+ 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]}
391
+ @appinstance = ZuoraConnect::AppInstance.new(mapped_values)
392
+ retry_count = 0
393
+ begin
394
+ @appinstance.save(:validate => false)
395
+ rescue ActiveRecord::RecordNotUnique => ex
396
+ if (retry_count += 1) < 3
397
+ @appinstance.assign_attributes({:api_token => rand(36**64).to_s(36), :token => rand(36**64).to_s(36)})
398
+ retry
399
+ else
400
+ Thread.current[:appinstance] = nil
401
+ session["appInstance"] = nil
402
+ render "zuora_connect/static/error_handled", :locals => {
403
+ :title => "Application could not create unique tokens.",
404
+ :message => "Please contact support or retry launching application."
405
+ }, :layout => false
406
+ return
407
+ end
408
+ end
409
+ end
410
+
411
+ Apartment::Tenant.switch!("public")
412
+ begin
413
+ Apartment::Tenant.create(@appinstance.id.to_s)
414
+ rescue Apartment::TenantExists => ex
415
+ ZuoraConnect.logger.debug("Tenant Already Exists")
416
+ end
417
+ @appinstance.refresh
418
+ session["appInstance"] = @appinstance.id
419
+ end
420
+
421
+ rescue ZuoraAPI::Exceptions::ZuoraAPIAuthenticationTypeError => ex
422
+ output_xml, input_xml, response = zuora_client.soap_call(errors: [], z_session: false) do |xml|
423
+ xml['api'].getUserInfo
424
+ end
425
+ final_error = output_xml.xpath('//fns:FaultCode', 'fns' =>'http://fault.api.zuora.com/').text
426
+ session.clear
427
+ if final_error.blank?
428
+ ZuoraConnect.logger.warn("UI Authorization Error", ex, zuora: zuora_details.merge({:error => response.body}))
429
+ elsif final_error != "INVALID_SESSION"
430
+ ZuoraConnect.logger.warn("UI Authorization Error", ex, zuora: zuora_details.merge({:error => final_error}))
431
+ end
432
+ redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
433
+ return
434
+ rescue => ex
435
+ if defined?(ex.response) && ex.response.present? && defined?(ex.response.body)
436
+ zuora_details.merge!({:error => ex.response.body})
437
+ end
438
+ ZuoraConnect.logger.error("UI Authorization Error", ex, zuora: zuora_details)
439
+ render "zuora_connect/static/error_unhandled", :locals => {:exception => ex}, :layout => false
440
+ return
442
441
  end
443
- 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"]}
444
- @appinstance = ZuoraConnect::AppInstance.new(mapped_values.merge({:id => values["appInstance"].to_i}))
445
- @appinstance.save(:validate => false)
446
- else
447
- mapped_values = {:access_token => values["access_token"], :refresh_token => values["refresh_token"], :oauth_expires_at => values["expires"]}
448
- @appinstance.assign_attributes(mapped_values)
449
- if @appinstance.access_token_changed? && @appinstance.refresh_token_changed?
442
+ 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)
443
+ session.clear
444
+ values = JSON.parse(ZuoraConnect::AppInstance.decrypt_response(Base64.urlsafe_decode64(request["data"])))
445
+ values.fetch("param_data", {}).each do |k ,v|
446
+ params[k] = v
447
+ end
448
+ session["#{values["appInstance"]}::destroy"] = values["destroy"]
449
+ session["appInstance"] = values["appInstance"]
450
+ if values["current_user"]
451
+ session["#{values["appInstance"]}::admin"] = values["current_user"]["admin"] ? values["current_user"]["admin"] : false
452
+ session["#{values["appInstance"]}::user::timezone"] = values["current_user"]["timezone"]
453
+ session["#{values["appInstance"]}::user::locale"] = values["current_user"]["locale"]
454
+ session["#{values["appInstance"]}::user::email"] = values["current_user"]["email"]
455
+ end
456
+
457
+ ZuoraConnect.logger.debug({msg: 'Setup values', connect: values}) if Rails.env != "production"
458
+
459
+ @appinstance = ZuoraConnect::AppInstance.where(:id => values["appInstance"].to_i).first
460
+
461
+ if @appinstance.blank?
462
+ Apartment::Tenant.switch!("public")
463
+ begin
464
+ Apartment::Tenant.create(values["appInstance"].to_s)
465
+ rescue Apartment::TenantExists => ex
466
+ ZuoraConnect.logger.debug("Tenant Already Exists")
467
+ end
468
+ 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"]}
469
+ @appinstance = ZuoraConnect::AppInstance.new(mapped_values.merge({:id => values["appInstance"].to_i}))
450
470
  @appinstance.save(:validate => false)
451
471
  else
452
- raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mismatch. Possible tampering")
472
+ mapped_values = {:access_token => values["access_token"], :refresh_token => values["refresh_token"], :oauth_expires_at => values["expires"]}
473
+ @appinstance.assign_attributes(mapped_values)
474
+ if @appinstance.access_token_changed? && @appinstance.refresh_token_changed?
475
+ @appinstance.save(:validate => false)
476
+ else
477
+ raise ZuoraConnect::Exceptions::AccessDenied.new("Authorization mismatch. Possible tampering with session.")
478
+ end
479
+ end
480
+ else
481
+ if session["appInstance"].present?
482
+ @appinstance = ZuoraConnect::AppInstance.where(:id => session["appInstance"]).first
483
+ else
484
+ raise ZuoraConnect::Exceptions::AccessDenied.new("No application state or session found.")
453
485
  end
454
- end
486
+ end
455
487
  end
456
488
 
457
489
  def setup_instance_via_dev_mode
@@ -1,3 +1,3 @@
1
1
  module ZuoraConnect
2
- VERSION = "2.0.31"
2
+ VERSION = "2.0.32"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zuora_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.31
4
+ version: 2.0.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Connect Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-13 00:00:00.000000000 Z
11
+ date: 2020-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apartment