Skip to content

Commit af8136a

Browse files
authored
Merge d69e596 into 6cb1f28
2 parents 6cb1f28 + d69e596 commit af8136a

File tree

7 files changed

+222
-92
lines changed

7 files changed

+222
-92
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
19.0.4
1+
19.0.5

connect/logger/Logger.hx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class Logger extends Base {
3535
this.level = Std.int(Math.min(Math.max(config.level_, LEVEL_ERROR), LEVEL_DEBUG));
3636
this.handlers = config.handlers_.copy();
3737
this.sections = [];
38+
this.maskedFields = config.maskedFields_;
39+
this.maskedFields.push("apiKey");
3840
}
3941

4042

@@ -184,6 +186,13 @@ class Logger extends Base {
184186
}
185187
}
186188

189+
/**
190+
* Returns a list of fields which should be masked in http requests or responses
191+
**/
192+
public function getMaskedFields(){
193+
return this.maskedFields;
194+
}
195+
187196

188197
@:dox(hide)
189198
public function log(message: String): Void {
@@ -253,6 +262,7 @@ class Logger extends Base {
253262
private final level: Int;
254263
private final handlers: Collection<LoggerHandler>;
255264
private final sections: Array<LoggerSection>;
265+
private final maskedFields: Collection<String>;
256266

257267

258268
private function writeSections(): Void {

connect/logger/LoggerConfig.hx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ class LoggerConfig extends Base {
5151
return this;
5252
}
5353

54+
/**
55+
* Sets the fields which should be masked in the logs,
56+
* by default only connect api credentials are masked
57+
* @param maskedFields Collection of field names (string).
58+
* @return LoggerConfig
59+
*/
60+
public function maskedFields(maskedFields: Collection<String>):LoggerConfig{
61+
this.maskedFields_ = maskedFields;
62+
return this;
63+
}
5464

5565
public function new() {
5666
this.path_ = 'logs';
@@ -65,4 +75,5 @@ class LoggerConfig extends Base {
6575
public var path_(default, null): String;
6676
public var level_(default, null): Int;
6777
public var handlers_(default, null): Collection<LoggerHandler>;
78+
public var maskedFields_(default,null): Collection<String>;
6879
}

connect/util/Util.hx

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
Copyright (c) 2019 Ingram Micro. All Rights Reserved.
44
*/
55
package connect.util;
6-
7-
6+
import connect.Env;
87
@:dox(hide)
98
class Util {
109
/*
@@ -13,10 +12,10 @@ class Util {
1312
contains a JSON string representation, only the id is returned or a string with all the
1413
fields if it does not have an id.
1514
*/
16-
public static function beautify(text: String, compact: Bool): String {
15+
public static function beautify(text:String, compact:Bool):String {
1716
try {
1817
return beautifyObject(haxe.Json.parse(text), compact);
19-
} catch (ex: Dynamic) {
18+
} catch (ex:Dynamic) {
2019
return text;
2120
}
2221
}
@@ -27,38 +26,49 @@ class Util {
2726
If `compact` is `true` and the text contains a JSON string representation, only the id
2827
is returned or a string with all the fields if it does not have an id.
2928
*/
30-
public static function beautifyObject(obj: Dynamic, compact: Bool): String {
29+
public static function beautifyObject(obj:Dynamic, compact:Bool):String {
3130
if (compact) {
32-
if (Type.typeof(obj) == TObject) {
33-
// Json contains an object
34-
if (Reflect.hasField(obj, 'id')) {
35-
return obj.id;
36-
} else {
37-
return '{ ' + Reflect.fields(obj).join(', ') + ' }';
38-
}
39-
} else {
40-
// Json contains an array
41-
final arr: Array<Dynamic> = obj;
42-
final mapped = arr.map(function(el) {
43-
return Reflect.hasField(el, 'id')
44-
? el.id
45-
: (Type.typeof(el) == TObject)
46-
? '{ ' + Reflect.fields(el).join(', ') + ' }'
47-
: Std.string(el);
48-
});
49-
return haxe.Json.stringify(mapped, null, ' ');
50-
}
31+
return haxe.Json.stringify(maskFields(obj), null, ' ');
32+
5133
} else {
5234
return haxe.Json.stringify(obj, null, ' ');
5335
}
5436
}
5537

38+
public static function maskFields(obj:Dynamic): Dynamic {
39+
final maskedFields= Env.getLogger().getMaskedFields();
40+
if (Type.typeof(obj) == TObject) {
41+
for(fieldName in Reflect.fields(obj)){
42+
if(maskedFields.indexOf(fieldName) != - 1){
43+
if (Type.typeof(Reflect.field(obj, fieldName)) == TObject){
44+
if (Reflect.hasField(Reflect.field(obj, fieldName), 'id')) {
45+
Reflect.setField(obj, fieldName, Reflect.field(obj, fieldName).id);
46+
} else {
47+
Reflect.setField(obj, fieldName, '{object}');
48+
}
49+
}else{
50+
Reflect.setField(obj, fieldName, 'HIDDEN FIELD');
51+
}
52+
}else if (Type.typeof(obj) == TObject || Std.is(obj,connect.util.Collection) || Std.is(obj, Array)){
53+
Reflect.setField(obj, fieldName, maskFields(Reflect.field(obj, fieldName)));
54+
}
55+
}
56+
return obj;
57+
}else if (Std.is(obj, Array)){
58+
final arr: Array<Dynamic> = obj;
59+
return arr.map(function(el) {
60+
return maskFields(el);
61+
});
62+
}
63+
return obj;
64+
}
65+
5666

5767
/**
5868
@returns Whether the text seems to contain a JSON object or array.
5969
NOTE: This function could return `true` even if the JSON string is malformed.
6070
**/
61-
public static function isJson(text: String): Bool {
71+
public static function isJson(text:String):Bool {
6272
return isJsonObject(text) || isJsonArray(text);
6373
}
6474

@@ -67,7 +77,7 @@ class Util {
6777
@returns Whether the text seems to contain a JSON object.
6878
NOTE: This function could return `true` even if the JSON string is malformed.
6979
**/
70-
public static function isJsonObject(text: String): Bool {
80+
public static function isJsonObject(text:String):Bool {
7181
return StringTools.trim(text).charAt(0) == '{';
7282
}
7383

@@ -76,19 +86,19 @@ class Util {
7686
@returns Whether the text seems to contain a JSON array.
7787
NOTE: This function could return `true` even if the JSON string is malformed.
7888
**/
79-
public static function isJsonArray(text: String): Bool {
89+
public static function isJsonArray(text:String):Bool {
8090
return StringTools.trim(text).charAt(0) == '[';
8191
}
8292

8393

8494
/** @return Whether the passed object is an array. **/
85-
public static function isArray(value: Dynamic): Bool {
95+
public static function isArray(value: Dynamic):Bool {
8696
return Std.is(value, Array);
8797
}
8898

8999

90100
/** @return Whether the passed object is a dynamic object. **/
91-
public static function isStruct(value: Dynamic): Bool {
101+
public static function isStruct(value:Dynamic):Bool {
92102
return Type.typeof(value) == TObject;
93103
}
94104

@@ -101,7 +111,7 @@ class Util {
101111
* @param previous The object prior to the updates.
102112
* @return The object with only the differences.
103113
*/
104-
public static function createObjectDiff(object: Dynamic, previous: Dynamic): Dynamic {
114+
public static function createObjectDiff(object:Dynamic, previous:Dynamic):Dynamic {
105115
return Util.addIdsToObject(
106116
new Diff(previous, object).apply({id: object.id}),
107117
previous);
@@ -116,28 +126,28 @@ class Util {
116126
* This method is used for example when updating a request, to send only the modified data.
117127
* @return A new dynamic object with the updated data.
118128
*/
119-
private static function addIdsToObject(object: Dynamic, original: Dynamic): Dynamic {
129+
private static function addIdsToObject(object:Dynamic, original:Dynamic):Dynamic {
120130
final out = {};
121131
final id = 'id';
122132
if (!Reflect.hasField(object, id) && Reflect.hasField(original, id)) {
123-
Reflect.setField(out, id, Reflect.field(original, id));
133+
Reflect.setField(out, id, Reflect.field(original, id));
124134
}
125135
Lambda.iter(Reflect.fields(object), function(field) {
126-
final value = Reflect.field(object, field);
127-
if (Reflect.hasField(original, field)) {
128-
final originalValue = Reflect.field(original, field);
129-
if (isStruct(value) && isStruct(originalValue)) {
130-
Reflect.setField(out, field, addIdsToObject(value, originalValue));
131-
} else if (isArray(value) && isArray(originalValue)) {
132-
final valueArr = cast(value, Array<Dynamic>);
133-
final originalValueArr = cast(originalValue, Array<Dynamic>);
134-
Reflect.setField(out, field, compactArray(valueArr, originalValueArr));
135-
} else {
136-
Reflect.setField(out, field, value);
137-
}
138-
} else {
139-
Reflect.setField(out, field, value);
140-
}
136+
final value = Reflect.field(object, field);
137+
if (Reflect.hasField(original, field)) {
138+
final originalValue = Reflect.field(original, field);
139+
if (isStruct(value) && isStruct(originalValue)) {
140+
Reflect.setField(out, field, addIdsToObject(value, originalValue));
141+
} else if (isArray(value) && isArray(originalValue)) {
142+
final valueArr = cast(value, Array<Dynamic>);
143+
final originalValueArr = cast(originalValue, Array<Dynamic>);
144+
Reflect.setField(out, field, compactArray(valueArr, originalValueArr));
145+
} else {
146+
Reflect.setField(out, field, value);
147+
}
148+
} else {
149+
Reflect.setField(out, field, value);
150+
}
141151
});
142152
return out;
143153
}
@@ -150,21 +160,21 @@ class Util {
150160
* the value of the same object in the `second` array.
151161
* @return A new array containing only the modified elements.
152162
*/
153-
private static function compactArray(array: Array<Dynamic>, second: Array<Dynamic>)
154-
: Array<Dynamic> {
155-
final out: Array<Dynamic> = [];
163+
private static function compactArray(array:Array<Dynamic>, second:Array<Dynamic>)
164+
:Array<Dynamic> {
165+
final out: Array<Dynamic>= [];
156166
for (i in 0...array.length) {
157-
final value = array[i];
158-
final secondValue = second[i];
159-
if (isStruct(value) && isStruct(secondValue)) {
160-
out.push(addIdsToObject(value, secondValue));
161-
} else if (isArray(value) && isArray(secondValue)) {
162-
final valueArr = cast(value, Array<Dynamic>);
163-
final secondValueArr = cast(secondValue, Array<Dynamic>);
164-
out.push(compactArray(valueArr, secondValueArr));
165-
} else if (value != null) {
166-
out.push(value);
167-
}
167+
final value = array[i];
168+
final secondValue = second[i];
169+
if (isStruct(value) && isStruct(secondValue)) {
170+
out.push(addIdsToObject(value, secondValue));
171+
} else if (isArray(value) && isArray(secondValue)) {
172+
final valueArr = cast(value, Array<Dynamic>);
173+
final secondValueArr = cast(secondValue, Array<Dynamic>);
174+
out.push(compactArray(valueArr, secondValueArr));
175+
} else if (value != null) {
176+
out.push(value);
177+
}
168178
}
169179
return out;
170180
}

test/unit/LoggerTest.hx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
This file is part of the Ingram Micro CloudBlue Connect SDK.
3+
Copyright (c) 2019 Ingram Micro. All Rights Reserved.
4+
*/
5+
import test.util.ArrayLoggerWriter;
6+
import connect.logger.MarkdownLoggerFormatter;
7+
import connect.logger.LoggerHandler;
8+
import connect.logger.LoggerConfig;
9+
import connect.Env;
10+
import connect.util.Collection;
11+
import connect.util.Util;
12+
import massive.munit.Assert;
13+
import haxe.Json;
14+
15+
class LoggerTest {
16+
17+
private static final dataTestMaskDataInObj:String = '{"apiKey":"123456","non_important_data": 1,"some_list":["a","b",{"username":"1"}],"smtp": {"user":"loguser","password":"pass"},"the_obj":{"a":"test"},"id_obj":{"id":"the id"}}';
18+
private static final resultTestMaskDataInObj:String = '{"apiKey":"HIDDEN FIELD","non_important_data": 1,"some_list":["a","b",{"username":"HIDDEN FIELD"}],"smtp": {"user":"loguser","password":"HIDDEN FIELD"},"the_obj":"{object}","id_obj":"the id"}';
19+
20+
private static final dataTestMaskDataInList:String = '[{"apiKey":"123456","non_important_data": 1,"some_list":["a","b",{"username":"1"}],"smtp": {"user":"loguser","password":"pass"},"the_obj":{"a":"test"},"id_obj":{"id":"the id"}},{"apiKey":"123456","non_important_data": 1,"some_list":["a","b",{"username":"1"}],"smtp": {"user":"loguser","password":"pass"},"the_obj":{"a":"test"},"id_obj":{"id":"the id"}}]';
21+
private static final resultTestMaskDataInList:String = '[{"apiKey":"HIDDEN FIELD","non_important_data": 1,"some_list":["a","b",{"username":"HIDDEN FIELD"}],"smtp": {"user":"loguser","password":"HIDDEN FIELD"},"the_obj":"{object}","id_obj":"the id"},{"apiKey":"HIDDEN FIELD","non_important_data": 1,"some_list":["a","b",{"username":"HIDDEN FIELD"}],"smtp": {"user":"loguser","password":"HIDDEN FIELD"},"the_obj":"{object}","id_obj":"the id"}]';
22+
23+
24+
@Before
25+
public function setup() {
26+
Env._reset();
27+
var maskedFields:Collection<String> = new Collection().push("apiKey").push("username").push("password").push("smtpUsername").push("id_obj").push("the_obj");
28+
Env.initLogger(new LoggerConfig().handlers(new Collection<LoggerHandler>().push(new LoggerHandler(new MarkdownLoggerFormatter(),
29+
new ArrayLoggerWriter())))
30+
.maskedFields(maskedFields));
31+
}
32+
33+
@Test
34+
public function testMaskDataInObj() {
35+
final maskedInfo = Util.beautify(dataTestMaskDataInObj, true);
36+
final result = Helper.sortObject(Json.parse(maskedInfo));
37+
final expected = Helper.sortObject(Json.parse(resultTestMaskDataInObj));
38+
Assert.areEqual(Json.stringify(expected), Json.stringify(result));
39+
}
40+
41+
@Test
42+
public function testMaskDataInList() {
43+
final maskedInfo = Util.beautify(dataTestMaskDataInList, true);
44+
final result = Helper.sortObject(Json.parse(maskedInfo));
45+
final expected = Helper.sortObject(Json.parse(resultTestMaskDataInList));
46+
Assert.areEqual(Json.stringify(expected), Json.stringify(result));
47+
}
48+
49+
}

0 commit comments

Comments
 (0)