-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathredact.go
88 lines (73 loc) · 1.61 KB
/
redact.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package redact
import (
"errors"
"reflect"
)
const (
tagName = "redact"
nonSecret = "nonsecret"
RedactStrConst = "REDACTED"
)
type redactor func(string) string
var redactors = map[string]redactor{}
// AddRedactor allows for adding custom functionality based on tag values
func AddRedactor(key string, r redactor) {
redactors[key] = r
}
// Redact redacts all strings without the nonsecret tag
func Redact(iface interface{}) error {
ifv := reflect.ValueOf(iface)
if ifv.Kind() != reflect.Ptr {
return errors.New("Not a pointer")
}
redact(reflect.Indirect(ifv), nonSecret)
return nil
}
func redact(v reflect.Value, tag string) {
switch v.Kind() {
case reflect.Array:
for i := 0; i < v.Len(); i++ {
redact(v.Index(i), tag)
}
case reflect.Interface, reflect.Pointer:
if !v.IsNil() {
redact(v.Elem(), tag)
}
case reflect.Map:
if !v.IsNil() {
for _, key := range v.MapKeys() {
val := reflect.New(v.Type().Elem()).Elem()
val.Set(v.MapIndex(key))
redact(val, tag)
v.SetMapIndex(key, val)
}
}
case reflect.Slice:
if !v.IsNil() {
for i := 0; i < v.Len(); i++ {
redact(v.Index(i), tag)
}
}
case reflect.String:
v.SetString(transformString(v.String(), tag))
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
if v.Field(i).CanSet() {
tag, _ := v.Type().Field(i).Tag.Lookup(tagName)
redact(v.Field(i), tag)
}
}
}
}
func transformString(input, tagVal string) string {
switch tagVal {
case nonSecret:
return input
default:
redactor, ok := redactors[tagVal]
if !ok {
return RedactStrConst
}
return redactor(input)
}
}