connect-sdk-ruby 2.30.2 → 2.31.0

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: 7db60897f47065c79b42b4f458baaa3eb9c346b66b50381bd795b6abb762fbe6
4
- data.tar.gz: 2a43fcff7d2729aefd492f3ace41f6f67dab7157da8699ea38947c3fbc53b73d
3
+ metadata.gz: 1f9cc67b441796020e9aa26d52bdd4d27e4151ad7ee0011929f623e10eb83ab5
4
+ data.tar.gz: f67e1575cafad14f2ffebae9544d76bd0e2c5ced65f9ba0ba0903ad56d231abd
5
5
  SHA512:
6
- metadata.gz: 8e9114ffa747a472929b909730f60748f97fe1409acbcbb143b290e88b2999c34624bf630827e0f3ee7a6aa52368ab100bf944d11ab48c6b07891cacc42a434a
7
- data.tar.gz: 1f9a4c4a0db466fd0ecea4fa718738b4c8c7df63e3890d95933ec8de4a853454f39aebc8ff8e9ed05fae107b90e310969183027d327743a8ccd950a79cbafa3b
6
+ metadata.gz: 8347f98939d41bab6d3b17023f81ec5813d0cb272ab938dae81541de458a5ec5210e0bd83a085b432e2d30692c81cab220f43eca8042b433e37da08591cfa302
7
+ data.tar.gz: af0b2d7c03d66496bc3335c1bc7785f59a2b3054fe8459f53d93372884af9ed29d12dfdc5dcd0697692d7c6705575020cba08443bd51ab7368b1a815bf717db4
data/README.md CHANGED
@@ -94,7 +94,8 @@ In order to run the unit and integration tests, some additional dependencies are
94
94
  * [rake](https://ruby.github.io/rake/) 12.3.3 or higher
95
95
  * [rspec](https://github.com/rspec/rspec) 3.5 or higher
96
96
  * [webmock](https://github.com/bblimke/webmock) 2.1 or higher
97
- * [sinatra](https://github.com/sinatra/sinatra) 1.4 or higher
97
+ * [sinatra](https://github.com/sinatra/sinatra) 2.1 or higher
98
+ * [webrick](https://github.com/ruby/webrick) 1.7 or higher
98
99
 
99
100
  They can be installed using the following command:
100
101
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'connect-sdk-ruby'
3
- spec.version = '2.30.2'
3
+ spec.version = '2.31.0'
4
4
  spec.authors = ['Ingenico ePayments']
5
5
  spec.email = ['github@epay.ingenico.com']
6
6
  spec.summary = %q{SDK to communicate with the Ingenico ePayments platform using the Ingenico Connect Server API}
@@ -23,7 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency 'yard', '~> 0.9'
24
24
  spec.add_development_dependency 'rspec', '~> 3.5'
25
25
  spec.add_development_dependency 'webmock', '~> 2.1'
26
- spec.add_development_dependency 'sinatra', '~> 1.4'
26
+ spec.add_development_dependency 'sinatra', '~> 2.1'
27
+ spec.add_development_dependency 'webrick', '~> 1.7'
27
28
  spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
28
29
  # spec.metadata['yard.run'] = 'yri' # compiles yard doc on install
29
30
  end
@@ -5,6 +5,7 @@
5
5
  require 'ingenico/connect/sdk/api_resource'
6
6
  require 'ingenico/connect/sdk/data_object'
7
7
  require 'ingenico/connect/sdk/logging/logging_capable'
8
+ require 'ingenico/connect/sdk/logging/obfuscation/obfuscation_capable'
8
9
  require 'ingenico/connect/sdk/merchant/merchant_client'
9
10
  require 'base64'
10
11
 
@@ -21,6 +22,7 @@ module Ingenico
21
22
  # Thread safe.
22
23
  class Client < ApiResource
23
24
  include Logging::LoggingCapable
25
+ include Logging::Obfuscation::ObfuscationCapable
24
26
 
25
27
  # @return [String]
26
28
  def self.API_VERSION
@@ -63,6 +65,18 @@ module Ingenico
63
65
  @communicator.close_expired_connections
64
66
  end
65
67
 
68
+ # Sets the current body obfuscator to use.
69
+ # @param body_obfuscator [Ingenico::Connect::SDK::Logging::Obfuscation::BodyObfuscator]
70
+ def set_body_obfuscator(body_obfuscator)
71
+ @communicator.set_body_obfuscator(body_obfuscator)
72
+ end
73
+
74
+ # Sets the current header obfuscator to use.
75
+ # @param header_obfuscator [Ingenico::Connect::SDK::Logging::Obfuscation::HeaderObfuscator]
76
+ def set_header_obfuscator(header_obfuscator)
77
+ @communicator.set_header_obfuscator(header_obfuscator)
78
+ end
79
+
66
80
  # Turns on logging using the given communicator logger.
67
81
  # @param communicator_logger [Ingenico::Connect::SDK::Logging::CommunicatorLogger]
68
82
  def enable_logging(communicator_logger)
@@ -11,6 +11,7 @@ module Ingenico::Connect::SDK
11
11
  #
12
12
  class Communicator
13
13
  include Logging::LoggingCapable
14
+ include Logging::Obfuscation::ObfuscationCapable
14
15
 
15
16
  # Creates a new Communicator based on a session and marshaller.
16
17
  #
@@ -363,6 +364,20 @@ module Ingenico::Connect::SDK
363
364
  connection.close_expired_connections if connection.is_a? PooledConnection
364
365
  end
365
366
 
367
+ # Sets the current body obfuscator to use.
368
+ # @param body_obfuscator [Ingenico::Connect::SDK::Logging::Obfuscation::BodyObfuscator]
369
+ def set_body_obfuscator(body_obfuscator)
370
+ connection = @session.connection
371
+ connection.set_body_obfuscator(body_obfuscator) if connection.is_a? Logging::Obfuscation::ObfuscationCapable
372
+ end
373
+
374
+ # Sets the current header obfuscator to use.
375
+ # @param header_obfuscator [Ingenico::Connect::SDK::Logging::Obfuscation::HeaderObfuscator]
376
+ def set_header_obfuscator(header_obfuscator)
377
+ connection = @session.connection
378
+ connection.set_header_obfuscator(header_obfuscator) if connection.is_a? Logging::Obfuscation::ObfuscationCapable
379
+ end
380
+
366
381
  # Enables logging outgoing requests and incoming responses by registering the _communicator_logger_.
367
382
  # Note that only one logger can be registered at a time and calling _enable_logging_
368
383
  # a second time will override the old logger instance with the new one.
@@ -16,6 +16,7 @@ end
16
16
  module Ingenico::Connect::SDK
17
17
  module DefaultImpl
18
18
  class DefaultConnection < PooledConnection
19
+ include Ingenico::Connect::SDK::Logging::Obfuscation::ObfuscationCapable
19
20
  using RefineHTTPClient
20
21
 
21
22
  CONTENT_TYPE = 'Content-Type'.freeze
@@ -50,6 +51,9 @@ module Ingenico::Connect::SDK
50
51
  @http_client.connect_timeout = @connect_timeout
51
52
  @http_client.send_timeout = @socket_timeout
52
53
  @http_client.receive_timeout = @socket_timeout
54
+
55
+ @body_obfuscator = Ingenico::Connect::SDK::Logging::Obfuscation::BodyObfuscator.default_obfuscator
56
+ @header_obfuscator = Ingenico::Connect::SDK::Logging::Obfuscation::HeaderObfuscator.default_obfuscator
53
57
  end
54
58
 
55
59
  private
@@ -202,13 +206,29 @@ module Ingenico::Connect::SDK
202
206
 
203
207
  # logging code
204
208
 
209
+ # Sets the current body obfuscator to use.
210
+ # @param body_obfuscator [Ingenico::Connect::SDK::Logging::Obfuscation::BodyObfuscator]
211
+ def set_body_obfuscator(body_obfuscator)
212
+ raise ArgumentError, 'body_obfuscator is required' unless body_obfuscator
213
+
214
+ @body_obfuscator = body_obfuscator
215
+ end
216
+
217
+ # Sets the current header obfuscator to use.
218
+ # @param header_obfuscator [Ingenico::Connect::SDK::Logging::Obfuscation::HeaderObfuscator]
219
+ def set_header_obfuscator(header_obfuscator)
220
+ raise ArgumentError, 'header_obfuscator is required' unless header_obfuscator
221
+
222
+ @header_obfuscator = header_obfuscator
223
+ end
224
+
205
225
  # Enables logging outgoing requests and incoming responses by registering the _communicator_logger_.
206
226
  # Note that only one logger can be registered at a time and calling _enable_logging_
207
227
  # a second time will override the old logger instance with the new one.
208
228
  #
209
229
  # @param communicator_logger [Ingenico::Connect::SDK::Logging::CommunicatorLogger] the communicator logger the requests and responses are logged to
210
230
  def enable_logging(communicator_logger)
211
- raise ArgumentError, 'communicatorLogger is required' unless communicator_logger
231
+ raise ArgumentError, 'communicator_logger is required' unless communicator_logger
212
232
 
213
233
  @communicator_logger = communicator_logger
214
234
  end
@@ -238,7 +258,9 @@ module Ingenico::Connect::SDK
238
258
  headers = args[:headers]
239
259
  body = args[:body]
240
260
  content_type = args[:content_type]
241
- log_msg_builder = Ingenico::Connect::SDK::Logging::RequestLogMessageBuilder.new(request_id, method, uri)
261
+ log_msg_builder = Ingenico::Connect::SDK::Logging::RequestLogMessageBuilder.new(request_id, method, uri,
262
+ @body_obfuscator,
263
+ @header_obfuscator)
242
264
  headers.each { |k, v| log_msg_builder.add_headers(k, v) } if headers
243
265
 
244
266
  if binary?(headers)
@@ -264,7 +286,9 @@ module Ingenico::Connect::SDK
264
286
  body = args[:body] unless args[:body].nil?
265
287
  content_type = args[:content_type]
266
288
 
267
- log_msg_builder = Ingenico::Connect::SDK::Logging::ResponseLogMessageBuilder.new(request_id, status_code, duration)
289
+ log_msg_builder = Ingenico::Connect::SDK::Logging::ResponseLogMessageBuilder.new(request_id, status_code, duration,
290
+ @body_obfuscator,
291
+ @header_obfuscator)
268
292
  unless headers.nil?
269
293
  headers = convert_from_sdk_headers(headers)
270
294
  headers.each do |key, value|
@@ -13,19 +13,27 @@ module Ingenico::Connect::SDK
13
13
  attr_reader :headers
14
14
  attr_reader :body
15
15
  attr_reader :content_type
16
+ attr_reader :body_obfuscator
17
+ attr_reader :header_obfuscator
16
18
 
17
19
  # Create a new LogMessageBuilder
18
- def initialize(request_id)
20
+ def initialize(request_id,
21
+ body_obfuscator = Obfuscation::BodyObfuscator.default_obfuscator,
22
+ header_obfuscator = Obfuscation::HeaderObfuscator.default_obfuscator)
19
23
  raise ArgumentError if request_id.nil? or request_id.empty?
24
+ raise ArgumentError if body_obfuscator.nil?
25
+ raise ArgumentError if header_obfuscator.nil?
20
26
  @request_id = request_id
21
27
  @headers = ''
28
+ @body_obfuscator = body_obfuscator
29
+ @header_obfuscator = header_obfuscator
22
30
  end
23
31
 
24
32
  # Adds a single header to the #headers string
25
33
  def add_headers(name, value)
26
34
  @headers += ', ' if @headers.length > 0
27
35
  @headers += name + '="'
28
- @headers += LoggingUtil.obfuscate_header(name, value) unless value.nil?
36
+ @headers += @header_obfuscator.obfuscate_header(name, value) unless value.nil?
29
37
  @headers += '"'
30
38
  end
31
39
 
@@ -37,7 +45,7 @@ module Ingenico::Connect::SDK
37
45
  if is_binary(content_type)
38
46
  @body = "<binary content>"
39
47
  else
40
- @body = LoggingUtil.obfuscate_body(body)
48
+ @body = @body_obfuscator.obfuscate_body(body)
41
49
  end
42
50
  @content_type = content_type
43
51
  end
@@ -49,9 +57,9 @@ module Ingenico::Connect::SDK
49
57
 
50
58
  def to_s
51
59
  if self.class == LogMessageBuilder
52
- return super.to_s
60
+ super.to_s
53
61
  else
54
- return get_message
62
+ get_message
55
63
  end
56
64
  end
57
65
 
@@ -63,10 +71,10 @@ module Ingenico::Connect::SDK
63
71
  # Returns whether or not the content type is binary
64
72
  def is_binary(content_type)
65
73
  if content_type.nil?
66
- return false
74
+ false
67
75
  else
68
76
  content_type = content_type.downcase
69
- return !(content_type.start_with?("text/") || content_type.include?("json") || content_type.include?("xml"))
77
+ !(content_type.start_with?("text/") || content_type.include?("json") || content_type.include?("xml"))
70
78
  end
71
79
  end
72
80
  end
@@ -1,7 +1,12 @@
1
+ require 'ingenico/connect/sdk/logging/obfuscation/body_obfuscator'
2
+ require 'ingenico/connect/sdk/logging/obfuscation/header_obfuscator'
3
+
1
4
  module Ingenico::Connect::SDK
2
5
  module Logging
3
6
 
4
7
  # Class responsible for obfuscating sensitive data in a message body.
8
+ #
9
+ # @deprecated This class shouldn't have been exposed
5
10
  class ValueObfuscator
6
11
 
7
12
  class << self
@@ -71,8 +76,10 @@ module Ingenico::Connect::SDK
71
76
  def repeat_mask(count)
72
77
  @mask_character * count
73
78
  end
74
- end # end of ValueObfuscator
79
+ end
75
80
 
81
+ #
82
+ # @deprecated This class shouldn't have been exposed
76
83
  class Obfuscator
77
84
  def initialize(obfuscators, case_insensitive)
78
85
  raise ArgumentError unless obfuscators.is_a? Hash
@@ -102,7 +109,7 @@ module Ingenico::Connect::SDK
102
109
  def _insensitive(key)
103
110
  key.respond_to?(:upcase) ? key.upcase : key
104
111
  end
105
- end # end of HashClod
112
+ end
106
113
 
107
114
  def copy(obfuscators, case_insensitive)
108
115
  cp = case_insensitive ? HashClod.new(obfuscators) : obfuscators
@@ -117,6 +124,8 @@ module Ingenico::Connect::SDK
117
124
  end
118
125
 
119
126
  # A convenient wrapper to build obfuscators
127
+ #
128
+ # @deprecated This class shouldn't have been exposed
120
129
  class Builder
121
130
  attr_accessor :obfuscators
122
131
 
@@ -151,16 +160,26 @@ module Ingenico::Connect::SDK
151
160
  end # Obfuscator
152
161
 
153
162
  # Class that obfuscates headers of a message
163
+ #
164
+ # @deprecated This class shouldn't have been exposed
154
165
  class HeaderObfuscator < Obfuscator
155
166
  def initialize(obfuscators)
156
167
  # case insensitive
157
168
  super(obfuscators, true)
169
+ obfuscation_rules = obfuscators.map { |key, value| [key, ->(v) { value.obfuscate_value(v) }]}
170
+ @obfuscator = Obfuscation::HeaderObfuscator.new(obfuscation_rules)
171
+ end
172
+
173
+ def obfuscate_value(key, value)
174
+ @obfuscator.obfuscate_header(key, value)
158
175
  end
159
176
 
160
177
  def self.builder
161
178
  Builder.new
162
179
  end
163
180
 
181
+ #
182
+ # @deprecated This class shouldn't have been exposed
164
183
  class Builder < Obfuscator::Builder
165
184
  def initialize
166
185
  @obfuscators = {}
@@ -180,55 +199,39 @@ module Ingenico::Connect::SDK
180
199
  HeaderObfuscator.new(obfuscators)
181
200
  end
182
201
  end
183
- end # end of HeadObfuscator
202
+ end
184
203
 
185
204
  # Class that obfuscates properties in the JSON body of a message
205
+ #
206
+ # @deprecated This class shouldn't have been exposed
186
207
  class PropertyObfuscator < Obfuscator
187
208
  def initialize(obfuscators)
188
209
  # case sensitive
189
210
  super(obfuscators, false)
190
- @property_pattern = build_property_pattern(obfuscators.keys)
211
+ @obfuscation_rules = obfuscators.map { |key, value| [key, ->(v) { value.obfuscate_value(v) }]}
212
+ @obfuscator = Obfuscation::BodyObfuscator.new(@obfuscation_rules)
191
213
  end
192
214
 
193
215
  private
194
216
 
195
- def build_property_pattern(pn)
196
- return /$^/ if pn.empty? # no possible match
197
- # Regex to create:
198
- # (["'])(X|Y|Z)\1\s*:\s*(?:(["'])(.*?)(?<!\\)\3|([^"'\s\[\{]\S*))
199
- # Groups:
200
- # 1: opening " or ' for the property name
201
- # 2: property name
202
- # 3: opening " or ' for the value
203
- # 4: quoted value
204
- # 5: non-quoted-value
205
- # The negative lookbehind is to allow escaped quotes to be part of
206
- # the value. What this does not allow currently is having values end
207
- # with a \ (which would be escaped to \\).
208
- regex = pn.inject("([\"'])(") { |r, p| "#{r}#{Regexp.quote(p)}|"}.chop <<
209
- ")\\1\\s*:\\s*(?:([\"'])(.*?)(?<!\\\\)\\3|([^\"'\\s\\[\\{]((?!,)\\S)*))"
210
- /#{regex}/m # dotall mode
211
- end
212
-
213
217
  public
214
218
 
219
+ def obfuscate_value(key, value)
220
+ obfuscation_rule = @obfuscation_rules[property_name]
221
+ return obfuscation_rule.call(value) if obfuscation_rule
222
+ value
223
+ end
224
+
215
225
  def obfuscate(body)
216
- return nil if body.nil?
217
- return '' if body.empty?
218
-
219
- body.gsub(@property_pattern) do
220
- m = Regexp.last_match
221
- property_name = m[2]
222
- value = m[4] || m[5]
223
- # copy value 'cause it's part of m[0]
224
- m[0].sub(value, obfuscate_value(property_name, value.dup))
225
- end
226
+ @obfuscator.obfuscate_body(body)
226
227
  end
227
228
 
228
229
  def self.builder
229
230
  Builder.new
230
231
  end
231
232
 
233
+ #
234
+ # @deprecated This class shouldn't have been exposed
232
235
  class Builder < Obfuscator::Builder
233
236
  def initialize
234
237
  @obfuscators = {}
@@ -258,9 +261,11 @@ module Ingenico::Connect::SDK
258
261
  PropertyObfuscator.new(obfuscators)
259
262
  end
260
263
  end
261
- end # end of property obfuscator
264
+ end
262
265
 
263
266
  module LoggingUtil
267
+ #
268
+ # @deprecated This constant shouldn't have been exposed
264
269
  @@PROPERTY_OBFUSCATOR = PropertyObfuscator.builder
265
270
  .with_keep_end_count("cardNumber", 4)
266
271
  .with_keep_end_count("expiryDate", 2)
@@ -279,6 +284,8 @@ module Ingenico::Connect::SDK
279
284
  .with_fixed_length("encryptedCustomerInput", 8)
280
285
  .build
281
286
 
287
+ #
288
+ # @deprecated This constant shouldn't have been exposed
282
289
  @@HEADER_OBFUSCATOR = HeaderObfuscator.builder
283
290
  .with_fixed_length("Authorization", 8)
284
291
  .with_fixed_length("WWW-Authenticate", 8)
@@ -288,13 +295,19 @@ module Ingenico::Connect::SDK
288
295
  .with_fixed_length("X-GCS-CallerPassword", 8)
289
296
  .build
290
297
 
298
+ #
299
+ # @deprecated This doesn't support custom obfuscation. Use BodyObfuscator instead
300
+ # @see Obfuscation::BodyObfuscator
291
301
  def self.obfuscate_body(body)
292
- @@PROPERTY_OBFUSCATOR.obfuscate(body)
302
+ return Obfuscation::BodyObfuscator.default_obfuscator.obfuscate_body(body)
293
303
  end
294
304
 
305
+ #
306
+ # @deprecated This doesn't support custom obfuscation. Use HeaderObfuscator instead
307
+ # @see Obfuscation::HeaderObfuscator
295
308
  def self.obfuscate_header(name, value)
296
- @@HEADER_OBFUSCATOR.obfuscate_value(name, value)
309
+ return Obfuscation::HeaderObfuscator.default_obfuscator.obfuscate_header(name, value)
297
310
  end
298
- end # end of LoggingUtil
311
+ end
299
312
  end
300
313
  end
@@ -0,0 +1,98 @@
1
+ require 'ingenico/connect/sdk/logging/obfuscation/obfuscation_rule'
2
+
3
+ module Ingenico::Connect::SDK
4
+ module Logging
5
+ module Obfuscation
6
+ # A class that can be used to obfuscate properties in JSON bodies.
7
+ class BodyObfuscator
8
+
9
+ # Creates a new body obfuscator.
10
+ # This will contain some pre-defined obfuscation rules, as well as any provided custom rules
11
+ #
12
+ # @param additional_rules [Hash] An optional hash where the keys are property names and the values are
13
+ # functions that obfuscate a single value
14
+ def initialize(additional_rules=nil)
15
+ @obfuscation_rules = {
16
+ "cardNumber" => Obfuscation.obfuscate_all_but_last(4),
17
+ "expiryDate" => Obfuscation.obfuscate_all_but_last(2),
18
+ "cvv" => Obfuscation.obfuscate_all,
19
+ "iban" => Obfuscation.obfuscate_all_but_last(4),
20
+ "accountNumber" => Obfuscation.obfuscate_all_but_last(4),
21
+ "reformattedAccountNumber" => Obfuscation.obfuscate_all_but_last(4),
22
+ "bin" => Obfuscation.obfuscate_all_but_first(6),
23
+ "value" => Obfuscation.obfuscate_all,
24
+ "keyId" => Obfuscation.obfuscate_with_fixed_length(8),
25
+ "secretKey" => Obfuscation.obfuscate_with_fixed_length(8),
26
+ "publicKey" => Obfuscation.obfuscate_with_fixed_length(8),
27
+ "userAuthenticationToken" => Obfuscation.obfuscate_with_fixed_length(8),
28
+ "encryptedPayload" => Obfuscation.obfuscate_with_fixed_length(8),
29
+ "decryptedPayload" => Obfuscation.obfuscate_with_fixed_length(8),
30
+ "encryptedCustomerInput" => Obfuscation.obfuscate_with_fixed_length(8),
31
+ }
32
+ if additional_rules
33
+ additional_rules.each do |name, rule|
34
+ @obfuscation_rules[name] = rule
35
+ end
36
+ end
37
+
38
+ @property_pattern = build_property_pattern(@obfuscation_rules.keys)
39
+ end
40
+
41
+ private
42
+
43
+ def build_property_pattern(pn)
44
+ return /$^/ if pn.empty? # no possible match
45
+ # Regex to create:
46
+ # (["'])(X|Y|Z)\1\s*:\s*(?:(["'])(.*?)(?<!\\)\3|([^"'\s\[\{]\S*))
47
+ # Groups:
48
+ # 1: opening " or ' for the property name
49
+ # 2: property name
50
+ # 3: opening " or ' for the value
51
+ # 4: quoted value
52
+ # 5: non-quoted-value
53
+ # The negative lookbehind is to allow escaped quotes to be part of
54
+ # the value. What this does not allow currently is having values end
55
+ # with a \ (which would be escaped to \\).
56
+ regex = pn.inject("([\"'])(") { |r, p| "#{r}#{Regexp.quote(p)}|"}.chop <<
57
+ ")\\1\\s*:\\s*(?:([\"'])(.*?)(?<!\\\\)\\3|([^\"'\\s\\[\\{]((?!,)\\S)*))"
58
+ /#{regex}/m # dotall mode
59
+ end
60
+
61
+ def obfuscate_value(property_name, value)
62
+ obfuscation_rule = @obfuscation_rules[property_name]
63
+ return obfuscation_rule.call(value) if obfuscation_rule
64
+ value
65
+ end
66
+
67
+ public
68
+
69
+ # Obfuscates the given body as necessary.
70
+ #
71
+ # @return (String)
72
+ def obfuscate_body(body)
73
+ return nil if body.nil?
74
+ return '' if body.empty?
75
+
76
+ body.gsub(@property_pattern) do
77
+ m = Regexp.last_match
78
+ property_name = m[2]
79
+ value = m[4] || m[5]
80
+ # copy value 'cause it's part of m[0]
81
+ m[0].sub(value, obfuscate_value(property_name, value.dup))
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ @@default_obfuscator = BodyObfuscator.new
88
+
89
+ public
90
+
91
+ # @return [BodyObfuscator]
92
+ def self.default_obfuscator
93
+ @@default_obfuscator
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,52 @@
1
+ require 'ingenico/connect/sdk/logging/obfuscation/obfuscation_rule'
2
+
3
+ module Ingenico::Connect::SDK
4
+ module Logging
5
+ module Obfuscation
6
+ # A class that can be used to obfuscate headers.
7
+ class HeaderObfuscator
8
+
9
+ # Creates a new header obfuscator.
10
+ # This will contain some pre-defined obfuscation rules, as well as any provided custom rules
11
+ #
12
+ # @param additional_rules [Hash] An optional hash where the keys are header names and the values are
13
+ # functions that obfuscate a single value
14
+ def initialize(additional_rules=nil)
15
+ @obfuscation_rules = {
16
+ "authorization" => Obfuscation.obfuscate_with_fixed_length(8),
17
+ "www-authenticate" => Obfuscation.obfuscate_with_fixed_length(8),
18
+ "proxy-authenticate" => Obfuscation.obfuscate_with_fixed_length(8),
19
+ "proxy-authorization" => Obfuscation.obfuscate_with_fixed_length(8),
20
+ "x-gcs-authentication-token" => Obfuscation.obfuscate_with_fixed_length(8),
21
+ "x-gcs-callerpassword" => Obfuscation.obfuscate_with_fixed_length(8),
22
+ }
23
+ if additional_rules
24
+ additional_rules.each do |name, rule|
25
+ @obfuscation_rules[name.downcase] = rule
26
+ end
27
+ end
28
+ end
29
+
30
+ # Obfuscates the value for the given header as necessary.
31
+ #
32
+ # @return (String)
33
+ def obfuscate_header(header_name, value)
34
+ obfuscation_rule = @obfuscation_rules[header_name.downcase]
35
+ return obfuscation_rule.call(value) if obfuscation_rule
36
+ value
37
+ end
38
+
39
+ private
40
+
41
+ @@default_obfuscator = HeaderObfuscator.new
42
+
43
+ public
44
+
45
+ # @return [HeaderObfuscator]
46
+ def self.default_obfuscator
47
+ @@default_obfuscator
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,18 @@
1
+ module Ingenico::Connect::SDK
2
+ module Logging
3
+ module Obfuscation
4
+ # Abstract mixin module that allows specifying body and header obfuscators for an object.
5
+ module ObfuscationCapable
6
+ # Sets the current body obfuscator to use.
7
+ def set_body_obfuscator(body_obfuscator)
8
+ raise NotImplementedError
9
+ end
10
+
11
+ # Sets the current header obfuscator to use.
12
+ def set_header_obfuscator(header_obfuscator)
13
+ raise NotImplementedError
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module Ingenico::Connect::SDK
2
+ module Logging
3
+ module Obfuscation
4
+
5
+ # Returns an obfuscation rule (callable) that will replace all characters with *
6
+ def self.obfuscate_all
7
+ ->(value) do
8
+ return value if value.nil? or value.empty?
9
+ '*' * (value || '').length
10
+ end
11
+ end
12
+
13
+ # Returns an obfuscation rule (function) that will replace values with a fixed length string containing only *
14
+ def self.obfuscate_with_fixed_length(fixed_length)
15
+ ->(value) { '*' * fixed_length }
16
+ end
17
+
18
+ # Returns an obfuscation rule (function) that will keep a fixed number of characters at the start,
19
+ # then replaces all other characters with *
20
+ def self.obfuscate_all_but_first(count)
21
+ ->(value) do
22
+ return value if value.nil? or value.empty?
23
+ return value if value.length < count
24
+ # range describes the range of characters to replace with asterisks
25
+ range = count...value.length
26
+ value[range] = '*' * range.size
27
+ value
28
+ end
29
+ end
30
+
31
+ # Returns an obfuscation rule that will keep a fixed number of characters at the end,
32
+ # then replaces all other characters with *
33
+ def self.obfuscate_all_but_last(count)
34
+ ->(value) do
35
+ return value if value.nil? or value.empty?
36
+ return value if value.length < count
37
+ # range describes the range of characters to replace with asterisks
38
+ range = 0...(value.length - count)
39
+ value[range] = '*' * range.size
40
+ value
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -4,8 +4,10 @@ module Ingenico::Connect::SDK
4
4
  # Class that converts data about a request into a properly formatted log message.
5
5
  # Formats request id, http method, uri, headers and body into a helpful message.
6
6
  class RequestLogMessageBuilder < Ingenico::Connect::SDK::Logging::LogMessageBuilder
7
- def initialize(request_id, method, uri)
8
- super(request_id)
7
+ def initialize(request_id, method, uri,
8
+ body_obfuscator = Obfuscation::BodyObfuscator.default_obfuscator,
9
+ header_obfuscator = Obfuscation::HeaderObfuscator.default_obfuscator)
10
+ super(request_id, body_obfuscator, header_obfuscator)
9
11
  @method = method
10
12
  @uri = uri
11
13
  end
@@ -31,9 +33,9 @@ module Ingenico::Connect::SDK
31
33
  def format_uri
32
34
  '' unless @uri && @uri.path
33
35
  if @uri.query.nil?
34
- return @uri.path
36
+ @uri.path
35
37
  else
36
- return "#{@uri.path}?#{@uri.query}" unless @uri.query.nil?
38
+ "#{@uri.path}?#{@uri.query}" unless @uri.query.nil?
37
39
  end
38
40
  # @uri.path + '?' + empty_if_null(@uri.query)
39
41
  end