-
Notifications
You must be signed in to change notification settings - Fork 1
/
anonymizer.go
125 lines (105 loc) · 2.73 KB
/
anonymizer.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package pii
import (
"context"
"reflect"
)
const (
anonymizeTag = "anonymize"
)
// StringAnonymizer anonymizes and deanonymizes strings.
// K is the type of key used to anonymize the string.
type StringAnonymizer[K any] interface {
AnonymizeString(ctx context.Context, key K, value string) (string, error)
DeanonymizeString(ctx context.Context, key K, value string) (string, error)
}
// StructAnonymizer anonymizes and deanonymizes structs.
// K is the type of key used to anonymize the struct.
// T is the type of struct to be anonymized.
type StructAnonymizer[K any, T any] struct {
stringAnonymizer StringAnonymizer[K]
}
func NewStructAnonymizer[K any, T any](
stringAnonymizer StringAnonymizer[K],
) StructAnonymizer[K, T] {
return StructAnonymizer[K, T]{
stringAnonymizer: stringAnonymizer,
}
}
func (a StructAnonymizer[K, T]) Anonymize(ctx context.Context, key K, data T) (T, error) {
t := reflect.TypeOf(data)
cp := reflect.New(t).Elem()
cp.Set(reflect.ValueOf(data))
err := a.anonymize(ctx, key, cp)
if err != nil {
var empty T
return empty, err
}
return cp.Interface().(T), nil
}
func (a StructAnonymizer[K, T]) anonymize(ctx context.Context, key K, v reflect.Value) error {
tv := v
t := v.Type()
for tv.Kind() == reflect.Ptr {
tv = v.Elem()
t = tv.Type()
}
for i := 0; i < t.NumField(); i++ {
field := tv.Field(i)
fieldType := t.Field(i)
if field.Kind() == reflect.String {
_, ok := fieldType.Tag.Lookup(anonymizeTag)
if ok {
anonymized, err := a.stringAnonymizer.AnonymizeString(ctx, key, field.String())
if err != nil {
return err
}
field.SetString(anonymized)
}
} else if field.Kind() == reflect.Struct {
err := a.anonymize(ctx, key, field)
if err != nil {
return err
}
}
}
return nil
}
func (a StructAnonymizer[K, T]) Deanonymize(ctx context.Context, key K, data T) (T, error) {
t := reflect.TypeOf(data)
cp := reflect.New(t).Elem()
cp.Set(reflect.ValueOf(data))
err := a.deanonymize(ctx, key, cp)
if err != nil {
var empty T
return empty, err
}
return cp.Interface().(T), nil
}
func (a StructAnonymizer[K, T]) deanonymize(ctx context.Context, key K, v reflect.Value) error {
tv := v
t := v.Type()
for tv.Kind() == reflect.Ptr {
tv = v.Elem()
t = tv.Type()
}
for i := 0; i < t.NumField(); i++ {
field := tv.Field(i)
fieldType := t.Field(i)
if field.Kind() == reflect.String {
_, ok := fieldType.Tag.Lookup(anonymizeTag)
if ok {
deanonymized, err := a.stringAnonymizer.DeanonymizeString(ctx, key, field.String())
if err != nil {
return err
}
field.SetString(deanonymized)
}
} else if field.Kind() == reflect.Struct {
err := a.deanonymize(ctx, key, field)
if err != nil {
return err
}
}
}
return nil
}