connect-sdk-ruby 2.30.2 → 2.31.0

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