zuora_connect 2.0.31 → 2.0.32

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.
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