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 +4 -4
- data/README.md +2 -1
- data/connect-sdk-ruby.gemspec +3 -2
- data/lib/ingenico/connect/sdk/client.rb +14 -0
- data/lib/ingenico/connect/sdk/communicator.rb +15 -0
- data/lib/ingenico/connect/sdk/defaultimpl/default_connection.rb +27 -3
- data/lib/ingenico/connect/sdk/logging/log_message_builder.rb +15 -7
- data/lib/ingenico/connect/sdk/logging/logging_util.rb +49 -36
- data/lib/ingenico/connect/sdk/logging/obfuscation/body_obfuscator.rb +98 -0
- data/lib/ingenico/connect/sdk/logging/obfuscation/header_obfuscator.rb +52 -0
- data/lib/ingenico/connect/sdk/logging/obfuscation/obfuscation_capable.rb +18 -0
- data/lib/ingenico/connect/sdk/logging/obfuscation/obfuscation_rule.rb +45 -0
- data/lib/ingenico/connect/sdk/logging/request_log_message_builder.rb +6 -4
- data/lib/ingenico/connect/sdk/logging/response_log_message_builder.rb +4 -2
- data/lib/ingenico/connect/sdk/logging.rb +4 -0
- data/lib/ingenico/connect/sdk/meta_data_provider.rb +1 -1
- data/lib/ingenico/connect/sdk/modules.rb +4 -0
- data/spec/fixtures/resources/logging/bodyWithCardCustomObfuscated.json +21 -0
- data/spec/lib/logging/header_obfuscator_spec.rb +7 -7
- data/spec/lib/logging/legacy_logging_spec.rb +178 -0
- data/spec/lib/logging/obfuscation/body_obfuscator_spec.rb +93 -0
- data/spec/lib/logging/obfuscation/header_obfuscator_spec.rb +166 -0
- data/spec/lib/logging/value_obfuscator_spec.rb +3 -3
- metadata +30 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f9cc67b441796020e9aa26d52bdd4d27e4151ad7ee0011929f623e10eb83ab5
|
4
|
+
data.tar.gz: f67e1575cafad14f2ffebae9544d76bd0e2c5ced65f9ba0ba0903ad56d231abd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
|
data/connect-sdk-ruby.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = 'connect-sdk-ruby'
|
3
|
-
spec.version = '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
|
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, '
|
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 +=
|
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 =
|
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
|
-
|
60
|
+
super.to_s
|
53
61
|
else
|
54
|
-
|
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
|
-
|
74
|
+
false
|
67
75
|
else
|
68
76
|
content_type = content_type.downcase
|
69
|
-
|
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
|
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
|
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
|
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
|
-
@
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
309
|
+
return Obfuscation::HeaderObfuscator.default_obfuscator.obfuscate_header(name, value)
|
297
310
|
end
|
298
|
-
end
|
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
|
-
|
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
|
-
|
36
|
+
@uri.path
|
35
37
|
else
|
36
|
-
|
38
|
+
"#{@uri.path}?#{@uri.query}" unless @uri.query.nil?
|
37
39
|
end
|
38
40
|
# @uri.path + '?' + empty_if_null(@uri.query)
|
39
41
|
end
|