moesif_rack 1.4.19 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,20 +5,19 @@ require 'base64'
5
5
  require 'zlib'
6
6
  require 'stringio'
7
7
  require 'rack'
8
- require_relative './client_ip.rb'
9
- require_relative './app_config.rb'
10
- require_relative './update_user.rb'
11
- require_relative './update_company.rb'
12
- require_relative './moesif_helpers.rb'
8
+ require_relative './client_ip'
9
+ require_relative './app_config'
10
+ require_relative './update_user'
11
+ require_relative './update_company'
12
+ require_relative './moesif_helpers'
13
+ require_relative './governance_rules'
13
14
 
14
15
  module MoesifRack
15
-
16
16
  class MoesifMiddleware
17
- def initialize app, options = {}
17
+ def initialize(app, options = {})
18
18
  @app = app
19
- if not options['application_id']
20
- raise 'application_id required for Moesif Middleware'
21
- end
19
+ raise 'application_id required for Moesif Middleware' unless options['application_id']
20
+
22
21
  @api_client = MoesifApi::MoesifAPIClient.new(options['application_id'])
23
22
  @api_controller = @api_client.api
24
23
 
@@ -32,10 +31,10 @@ module MoesifRack
32
31
  @debug = options['debug']
33
32
  @app_config = AppConfig.new(@debug)
34
33
  @moesif_helpers = MoesifHelpers.new(@debug)
35
- @config = @app_config.get_config(@api_controller)
34
+
36
35
  @config_etag = nil
37
36
  @last_config_download_time = Time.now.utc
38
- @config_dict = Hash.new
37
+ @config_dict = {}
39
38
  @disable_transaction_id = options['disable_transaction_id'] || false
40
39
  @log_body = options.fetch('log_body', true)
41
40
  @batch_size = options['batch_size'] || 200
@@ -43,27 +42,29 @@ module MoesifRack
43
42
  @batch_max_time = options['batch_max_time'] || 2
44
43
  @events_queue = Queue.new
45
44
  @event_response_config_etag = nil
45
+ @governance_manager = GovernanceRules.new(@debug)
46
46
 
47
47
  # start the worker and Update the last worker run
48
48
  @last_worker_run = Time.now.utc
49
- start_worker()
49
+ start_worker
50
50
 
51
51
  begin
52
52
  new_config = @app_config.get_config(@api_controller)
53
- if !new_config.nil?
53
+ unless new_config.nil?
54
54
  @config, @config_etag, @last_config_download_time = @app_config.parse_configuration(new_config)
55
55
  end
56
- rescue => exception
56
+ @governance_manager.load_rules(@api_controller)
57
+ rescue StandardError => e
57
58
  @moesif_helpers.log_debug 'Error while parsing application configuration on initialization'
58
- @moesif_helpers.log_debug exception.to_s
59
+ @moesif_helpers.log_debug e.to_s
59
60
  end
60
61
  @capture_outoing_requests = options['capture_outoing_requests']
61
62
  @capture_outgoing_requests = options['capture_outgoing_requests']
62
- if @capture_outoing_requests || @capture_outgoing_requests
63
- @moesif_helpers.log_debug 'Start Capturing outgoing requests'
64
- require_relative '../../moesif_capture_outgoing/httplog.rb'
65
- MoesifCaptureOutgoing.start_capture_outgoing(options)
66
- end
63
+ return unless @capture_outoing_requests || @capture_outgoing_requests
64
+
65
+ @moesif_helpers.log_debug 'Start Capturing outgoing requests'
66
+ require_relative '../../moesif_capture_outgoing/httplog'
67
+ MoesifCaptureOutgoing.start_capture_outgoing(options)
67
68
  end
68
69
 
69
70
  def update_user(user_profile)
@@ -91,17 +92,17 @@ module MoesifRack
91
92
  end
92
93
 
93
94
  def transform_headers(headers)
94
- Hash[headers.map { |k, v| [k.downcase, v]}]
95
+ Hash[headers.map { |k, v| [k.downcase, v] }]
95
96
  end
96
97
 
97
98
  def base64_encode_body(body)
98
- return Base64.encode64(body), 'base64'
99
+ [Base64.encode64(body), 'base64']
99
100
  end
100
101
 
101
102
  def @moesif_helpers.log_debug(message)
102
- if @debug
103
- puts("#{Time.now.to_s} [Moesif Middleware] PID #{Process.pid} TID #{Thread.current.object_id} #{message}")
104
- end
103
+ return unless @debug
104
+
105
+ puts("#{Time.now} [Moesif Middleware] PID #{Process.pid} TID #{Thread.current.object_id} #{message}")
105
106
  end
106
107
 
107
108
  def parse_multipart(multipart_form_data, content_type)
@@ -113,9 +114,10 @@ module MoesifRack
113
114
  tempfile = Rack::Multipart::Parser::TEMPFILE_FACTORY
114
115
  bufsize = Rack::Multipart::Parser::BUFSIZE
115
116
  query_parser = Rack::Utils.default_query_parser
116
- result = Rack::Multipart::Parser.parse(io, sanitized_multipart_form_data.length, content_type, tempfile, bufsize, query_parser)
117
+ result = Rack::Multipart::Parser.parse(io, sanitized_multipart_form_data.length, content_type, tempfile, bufsize,
118
+ query_parser)
117
119
 
118
- @moesif_helpers.log_debug("multipart parse result")
120
+ @moesif_helpers.log_debug('multipart parse result')
119
121
  @moesif_helpers.log_debug(result.inspect)
120
122
 
121
123
  # this is a hash shold be treated as JSON down the road.
@@ -124,67 +126,78 @@ module MoesifRack
124
126
 
125
127
  def parse_body(body, headers)
126
128
  begin
127
- if (body.instance_of?(Hash) || body.instance_of?(Array))
129
+ if body.instance_of?(Hash) || body.instance_of?(Array)
128
130
  parsed_body = body
129
131
  transfer_encoding = 'json'
130
132
  elsif start_with_json(body)
131
133
  parsed_body = JSON.parse(body)
132
134
  transfer_encoding = 'json'
133
- elsif headers.key?('content-type') && ((headers['content-type'].downcase).include? 'multipart/form-data')
135
+ elsif headers.key?('content-type') && (headers['content-type'].downcase.include? 'multipart/form-data')
134
136
  parsed_body = parse_multipart(body, headers['content-type'])
135
137
  transfer_encoding = 'json'
136
- elsif headers.key?('content-encoding') && ((headers['content-encoding'].downcase).include? "gzip")
138
+ elsif headers.key?('content-encoding') && (headers['content-encoding'].downcase.include? 'gzip')
137
139
  uncompressed_string = decompress_body(body)
138
140
  parsed_body, transfer_encoding = base64_encode_body(uncompressed_string)
139
141
  else
140
142
  parsed_body, transfer_encoding = base64_encode_body(body)
141
143
  end
142
- rescue
144
+ rescue StandardError
143
145
  parsed_body, transfer_encoding = base64_encode_body(body)
144
146
  end
145
- return parsed_body, transfer_encoding
147
+ [parsed_body, transfer_encoding]
146
148
  end
147
149
 
148
150
  def start_worker
149
- Thread::new do
151
+ Thread.new do
150
152
  loop do
151
153
  # Update the last worker run, in case the events_queue is empty
152
154
  @last_worker_run = Time.now.utc
153
155
  begin
154
- until @events_queue.empty? do
155
- # Update the last worker run in case sending events take more than 60 seconds
156
- @last_worker_run = Time.now.utc
157
- # Populate the batch events from queue
158
- batch_events = []
159
- until batch_events.size == @batch_size || @events_queue.empty? do
160
- batch_events << @events_queue.pop
161
- end
162
- @moesif_helpers.log_debug("Sending #{batch_events.size.to_s} events to Moesif")
163
- event_api_response = @api_controller.create_events_batch(batch_events)
164
- @event_response_config_etag = event_api_response[:x_moesif_config_etag]
165
- @moesif_helpers.log_debug(event_api_response.to_s)
166
- @moesif_helpers.log_debug("Events successfully sent to Moesif")
156
+ until @events_queue.empty?
157
+ # Update the last worker run in case sending events take more than 60 seconds
158
+ @last_worker_run = Time.now.utc
159
+ # Populate the batch events from queue
160
+ batch_events = []
161
+ batch_events << @events_queue.pop until batch_events.size == @batch_size || @events_queue.empty?
162
+ @moesif_helpers.log_debug("Sending #{batch_events.size} events to Moesif")
163
+ event_api_response = @api_controller.create_events_batch(batch_events)
164
+ @event_response_config_etag = event_api_response[:x_moesif_config_etag]
165
+ @moesif_helpers.log_debug(event_api_response.to_s)
166
+ @moesif_helpers.log_debug('Events successfully sent to Moesif')
167
167
  end
168
168
 
169
- if @events_queue.empty?
170
- @moesif_helpers.log_debug("No events to read from the queue")
169
+ @moesif_helpers.log_debug('No events to read from the queue') if @events_queue.empty?
170
+ if (!@event_response_config_etag.nil? && !@config_etag.nil? && @config_etag != @event_response_config_etag) || (Time.now.utc > (@last_config_download_time + 300))
171
+ begin
172
+ @moesif_helpers.log_debug('try to reload config and rules again')
173
+ new_config = @app_config.get_config(@api_controller)
174
+ unless new_config.nil?
175
+ @config, @config_etag, @last_config_download_time = @app_config.parse_configuration(new_config)
176
+ end
177
+ # since logic to reload config is already here for every 5 minutes,
178
+ # reload rules here also.
179
+ @governance_manager.load_rules(@api_controller)
180
+ rescue StandardError => e
181
+ @moesif_helpers.log_debug 'Error while updating the application configuration'
182
+ @moesif_helpers.log_debug e.to_s
183
+ end
171
184
  end
172
185
 
173
186
  sleep @batch_max_time
174
187
  rescue MoesifApi::APIException => e
175
188
  if e.response_code.between?(401, 403)
176
- puts "Unathorized accesss sending event to Moesif. Please verify your Application Id."
189
+ puts 'Unathorized accesss sending event to Moesif. Please verify your Application Id.'
177
190
  @moesif_helpers.log_debug(e.to_s)
178
191
  end
179
- @moesif_helpers.log_debug("Error sending event to Moesif, with status code #{e.response_code.to_s}")
180
- rescue => e
192
+ @moesif_helpers.log_debug("Error sending event to Moesif, with status code #{e.response_code}")
193
+ rescue StandardError => e
181
194
  @moesif_helpers.log_debug(e.to_s)
182
195
  end
183
196
  end
184
197
  end
185
198
  end
186
199
 
187
- def call env
200
+ def call(env)
188
201
  start_time = Time.now.utc.iso8601(3)
189
202
 
190
203
  @moesif_helpers.log_debug('Calling Moesif middleware')
@@ -192,15 +205,15 @@ module MoesifRack
192
205
  status, headers, body = @app.call env
193
206
  end_time = Time.now.utc.iso8601(3)
194
207
 
195
- process_send = lambda do
208
+ make_event_model = lambda do
196
209
  req = Rack::Request.new(env)
197
210
  complex_copy = env.dup
198
211
 
199
212
  # Filter hash to only have keys of type string
200
- complex_copy = complex_copy.select { |k, v| k.is_a? String }
213
+ complex_copy = complex_copy.select { |k, _v| k.is_a? String }
201
214
 
202
215
  req_headers = {}
203
- complex_copy.select {|k,v| k.start_with?('HTTP_', 'CONTENT_') }.each do |key, val|
216
+ complex_copy.select { |k, _v| k.start_with?('HTTP_', 'CONTENT_') }.each do |key, val|
204
217
  new_key = key.sub(/^HTTP_/, '')
205
218
  new_key = new_key.sub('_', '-')
206
219
  req_headers[new_key] = val
@@ -213,172 +226,187 @@ module MoesifRack
213
226
  req_body_transfer_encoding = nil
214
227
  req_body = nil
215
228
 
216
- if @log_body
217
- if req_body_string && req_body_string.length != 0
218
- req_body, req_body_transfer_encoding = parse_body(req_body_string, transform_headers(req_headers))
219
- end
229
+ if @log_body && (req_body_string && req_body_string.length != 0)
230
+ req_body, req_body_transfer_encoding = parse_body(req_body_string,
231
+ transform_headers(req_headers))
220
232
  end
221
233
 
222
234
  rsp_headers = headers.dup
223
235
 
224
- rsp_body_string = get_response_body(body);
236
+ rsp_body_string = get_response_body(body)
225
237
  rsp_body_transfer_encoding = nil
226
238
  rsp_body = nil
227
239
 
228
- if @log_body
229
- if rsp_body_string && rsp_body_string.length != 0
230
- rsp_body, rsp_body_transfer_encoding = parse_body(rsp_body_string, transform_headers(rsp_headers))
231
- end
240
+ if @log_body && (rsp_body_string && rsp_body_string.length != 0)
241
+ rsp_body, rsp_body_transfer_encoding = parse_body(rsp_body_string,
242
+ transform_headers(rsp_headers))
232
243
  end
233
244
 
234
- event_req = MoesifApi::EventRequestModel.new()
245
+ event_req = MoesifApi::EventRequestModel.new
235
246
  event_req.time = start_time
236
247
  event_req.uri = req.url
237
248
  event_req.verb = req.request_method
238
249
 
239
- if @api_version
240
- event_req.api_version = @api_version
241
- end
250
+ event_req.api_version = @api_version if @api_version
242
251
 
243
252
  # Add Transaction Id to the Request Header
244
- if !@disable_transaction_id
245
- req_trans_id = req_headers["X-MOESIF_TRANSACTION_ID"]
253
+ unless @disable_transaction_id
254
+ req_trans_id = req_headers['X-MOESIF_TRANSACTION_ID']
246
255
  if !req_trans_id.nil?
247
256
  transaction_id = req_trans_id
248
- if transaction_id.strip.empty?
249
- transaction_id = SecureRandom.uuid
250
- end
257
+ transaction_id = SecureRandom.uuid if transaction_id.strip.empty?
251
258
  else
252
259
  transaction_id = SecureRandom.uuid
253
260
  end
254
261
  # Add Transaction Id to Request Header
255
- req_headers["X-Moesif-Transaction-Id"] = transaction_id
262
+ req_headers['X-Moesif-Transaction-Id'] = transaction_id
256
263
  # Filter out the old key as HTTP Headers case are not preserved
257
- if req_headers.key?("X-MOESIF_TRANSACTION_ID")
258
- req_headers = req_headers.except("X-MOESIF_TRANSACTION_ID")
259
- end
264
+ req_headers = req_headers.except('X-MOESIF_TRANSACTION_ID') if req_headers.key?('X-MOESIF_TRANSACTION_ID')
260
265
  end
261
266
 
262
267
  # Add Transaction Id to the Response Header
263
- if !transaction_id.nil?
264
- rsp_headers["X-Moesif-Transaction-Id"] = transaction_id
265
- end
268
+ rsp_headers['X-Moesif-Transaction-Id'] = transaction_id unless transaction_id.nil?
266
269
 
267
270
  # Add Transaction Id to the Repsonse Header sent to the client
268
- if !transaction_id.nil?
269
- headers["X-Moesif-Transaction-Id"] = transaction_id
270
- end
271
+ headers['X-Moesif-Transaction-Id'] = transaction_id unless transaction_id.nil?
271
272
 
272
273
  event_req.ip_address = get_client_address(req.env)
273
274
  event_req.headers = req_headers
274
275
  event_req.body = req_body
275
276
  event_req.transfer_encoding = req_body_transfer_encoding
276
277
 
277
- event_rsp = MoesifApi::EventResponseModel.new()
278
+ event_rsp = MoesifApi::EventResponseModel.new
278
279
  event_rsp.time = end_time
279
280
  event_rsp.status = status
280
281
  event_rsp.headers = rsp_headers
281
282
  event_rsp.body = rsp_body
282
283
  event_rsp.transfer_encoding = rsp_body_transfer_encoding
283
284
 
284
- event_model = MoesifApi::EventModel.new()
285
- event_model.request = event_req
286
- event_model.response = event_rsp
287
- event_model.direction = "Incoming"
285
+ _event_model = MoesifApi::EventModel.new
286
+ _event_model.request = event_req
287
+ _event_model.response = event_rsp
288
+ _event_model.direction = 'Incoming'
288
289
 
289
290
  if @identify_user
290
- @moesif_helpers.log_debug "calling identify user proc"
291
- event_model.user_id = @identify_user.call(env, headers, body)
291
+ @moesif_helpers.log_debug 'calling identify user proc'
292
+ _event_model.user_id = @identify_user.call(env, headers, body)
292
293
  end
293
294
 
294
295
  if @identify_company
295
- @moesif_helpers.log_debug "calling identify company proc"
296
- event_model.company_id = @identify_company.call(env, headers, body)
296
+ @moesif_helpers.log_debug 'calling identify company proc'
297
+ _event_model.company_id = @identify_company.call(env, headers, body)
297
298
  end
298
299
 
299
300
  if @get_metadata
300
- @moesif_helpers.log_debug "calling get_metadata proc"
301
- event_model.metadata = @get_metadata.call(env, headers, body)
301
+ @moesif_helpers.log_debug 'calling get_metadata proc'
302
+ _event_model.metadata = @get_metadata.call(env, headers, body)
302
303
  end
303
304
 
304
305
  if @identify_session
305
- @moesif_helpers.log_debug "calling identify session proc"
306
- event_model.session_token = @identify_session.call(env, headers, body)
306
+ @moesif_helpers.log_debug 'calling identify session proc'
307
+ _event_model.session_token = @identify_session.call(env, headers, body)
307
308
  end
308
309
  if @mask_data
309
- @moesif_helpers.log_debug "calling mask_data proc"
310
- event_model = @mask_data.call(event_model)
310
+ @moesif_helpers.log_debug 'calling mask_data proc'
311
+ _event_model = @mask_data.call(_event_model)
311
312
  end
312
313
 
313
- @moesif_helpers.log_debug "sending data to moesif"
314
- @moesif_helpers.log_debug event_model.to_json
314
+ return _event_model
315
+ rescue StandardError => e
316
+ @moesif_helpers.log_debug 'Error making event model'
317
+ @moesif_helpers.log_debug e.to_s
318
+ end
319
+
320
+ process_send = lambda do |_event_model|
321
+ @moesif_helpers.log_debug 'sending data to moesif'
322
+ @moesif_helpers.log_debug _event_model.to_json
323
+
315
324
  # Perform the API call through the SDK function
316
325
  begin
317
- random_percentage = Random.rand(0.00..100.00)
326
+ random_percentage = Random.rand(0.00..100.00)
318
327
 
319
328
  begin
320
- sampling_percentage = @app_config.get_sampling_percentage(event_model, @config, event_model.user_id, event_model.company_id)
329
+ sampling_percentage = @app_config.get_sampling_percentage(_event_model, @config, _event_model.user_id,
330
+ _event_model.company_id)
321
331
  @moesif_helpers.log_debug "Using sample rate #{sampling_percentage}"
322
- rescue => exception
332
+ rescue StandardError => e
323
333
  @moesif_helpers.log_debug 'Error while getting sampling percentage, assuming default behavior'
324
- @moesif_helpers.log_debug exception.to_s
334
+ @moesif_helpers.log_debug e.to_s
325
335
  sampling_percentage = 100
326
336
  end
327
337
 
328
338
  if sampling_percentage > random_percentage
329
- event_model.weight = @app_config.calculate_weight(sampling_percentage)
339
+ _event_model.weight = @app_config.calculate_weight(sampling_percentage)
330
340
  # Add Event to the queue
331
341
  if @events_queue.size >= @event_queue_size
332
342
  @moesif_helpers.log_debug("Skipped Event due to events_queue size [#{@events_queue.size}] is over max #{@event_queue_size} ")
333
343
  else
334
- @events_queue << event_model
335
- @moesif_helpers.log_debug("Event added to the queue ")
344
+ @events_queue << _event_model
345
+ @moesif_helpers.log_debug('Event added to the queue ')
336
346
  end
337
347
 
338
- if Time.now.utc > (@last_worker_run + 60)
339
- start_worker()
340
- end
341
-
342
- if !@event_response_config_etag.nil? && !@config_etag.nil? && @config_etag != @event_response_config_etag && Time.now.utc > (@last_config_download_time + 300)
343
- begin
344
- new_config = @app_config.get_config(@api_controller)
345
- if !new_config.nil?
346
- @config, @config_etag, @last_config_download_time = @app_config.parse_configuration(new_config)
347
- end
348
-
349
- rescue => exception
350
- @moesif_helpers.log_debug 'Error while updating the application configuration'
351
- @moesif_helpers.log_debug exception.to_s
352
- end
353
- end
348
+ start_worker if Time.now.utc > (@last_worker_run + 60)
354
349
  else
355
- @moesif_helpers.log_debug("Skipped Event due to sampling percentage: " + sampling_percentage.to_s + " and random percentage: " + random_percentage.to_s)
350
+ @moesif_helpers.log_debug('Skipped Event due to sampling percentage: ' + sampling_percentage.to_s + ' and random percentage: ' + random_percentage.to_s)
356
351
  end
357
- rescue => exception
358
- @moesif_helpers.log_debug "Error adding event to the queue "
359
- @moesif_helpers.log_debug exception.to_s
352
+ rescue StandardError => e
353
+ @moesif_helpers.log_debug 'Error adding event to the queue '
354
+ @moesif_helpers.log_debug e.to_s
360
355
  end
361
-
362
356
  end
363
357
 
364
358
  should_skip = false
365
359
 
366
- if @skip
367
- if @skip.call(env, headers, body)
368
- should_skip = true;
360
+ should_skip = true if @skip && @skip.call(env, headers, body)
361
+
362
+ should_govern = @governance_manager.has_rules
363
+
364
+ event_model = make_event_model.call if !should_skip || should_govern
365
+
366
+ if should_govern
367
+ # now we can do govern based on
368
+ # override_response = govern(env, event_model)
369
+ # return override_response if override_response
370
+ new_response = @governance_manager.govern_request(@config, env, event_model, status, headers, body)
371
+
372
+ # update the event model
373
+ if new_response
374
+ @moesif_helpers.log_debug 'new response back from govern' + new_response.to_json
375
+
376
+ # replace headers since it might be non blocking rules that adds headers
377
+ headers = new_response.fetch(:headers, headers)
378
+
379
+ # replace in event_model
380
+ event_model.response.headers = new_response.fetch(:headers, headers).dup
381
+
382
+ blocked_by = new_response.fetch(:block_rule_id, nil)
383
+
384
+ unless blocked_by.nil?
385
+ # we only replace body and status if it is blocked.
386
+ body = @moesif_helpers.format_replacement_body(new_response.fetch(:body, nil), body)
387
+ status = new_response.fetch(:status, status)
388
+
389
+ # replace the event model.
390
+ event_model.blocked_by = blocked_by
391
+ event_model.response.status = new_response.fetch(:status, status)
392
+ replaced_body = new_response.fetch(:body, event_model.response.body)
393
+ event_model.response.body = replaced_body
394
+ # replaced body is always json should not be transfer encoding needed.
395
+ event_model.response.transfer_encoding = 'json'
396
+ end
369
397
  end
370
398
  end
371
399
 
372
400
  if !should_skip
373
401
  begin
374
- process_send.call
375
- rescue => exception
402
+ process_send.call(event_model)
403
+ rescue StandardError => e
376
404
  @moesif_helpers.log_debug 'Error while logging event - '
377
- @moesif_helpers.log_debug exception.to_s
378
- @moesif_helpers.log_debug exception.backtrace
405
+ @moesif_helpers.log_debug e.to_s
406
+ @moesif_helpers.log_debug e.backtrace
379
407
  end
380
408
  else
381
- @moesif_helpers.log_debug "Skipped Event using should_skip configuration option."
409
+ @moesif_helpers.log_debug 'Skipped Event using should_skip configuration option.'
382
410
  end
383
411
 
384
412
  [status, headers, body]
@@ -386,12 +414,10 @@ module MoesifRack
386
414
 
387
415
  def get_response_body(response)
388
416
  body = response.respond_to?(:body) ? response.body : response
389
- if (body.instance_of?(Hash) || body.instance_of?(Array))
390
- return body
391
- end
392
- body = body.inject("") { |i, a| i << a } if (body.respond_to?(:each) && body.respond_to?(:inject))
417
+ return body if body.instance_of?(Hash) || body.instance_of?(Array)
418
+
419
+ body = body.inject('') { |i, a| i << a } if body.respond_to?(:each) && body.respond_to?(:inject)
393
420
  body.to_s
394
421
  end
395
-
396
422
  end
397
423
  end