Skip to content

Instantly share code, notes, and snippets.

@acoshift
Created May 20, 2020 05:14
Show Gist options
  • Save acoshift/442893609e1f5c8b6c714982212197f4 to your computer and use it in GitHub Desktop.
Save acoshift/442893609e1f5c8b6c714982212197f4 to your computer and use it in GitHub Desktop.
[Golang] Mask struct by role using struct tag
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
)
type response struct {
ID string `json:"id"`
Username string `json:"username"`
Password string `json:"password,omitempty" role:"admin"`
Info *info `json:"info,omitempty" role:"admin,officer"`
}
type info struct {
FirstName string `json:"first_name,omitempty" role:"admin,officer"`
LastName string `json:"last_name"`
}
func makeResponse() *response {
return &response{
ID: "10",
Username: "user1",
Password: "1234",
Info: &info{
FirstName: "AAA",
LastName: "BBB",
},
}
}
// $ go run main.go
// Raw struct:
// {"id":"10","username":"user1","password":"1234","info":{"first_name":"AAA","last_name":"BBB"}}
// Mask for admin:
// {"id":"10","username":"user1","password":"1234","info":{"first_name":"AAA","last_name":"BBB"}}
// Mask for marketing:
// {"id":"10","username":"user1"}
// Mask for officer:
// {"id":"10","username":"user1","info":{"first_name":"AAA","last_name":"BBB"}}
func main() {
r := makeResponse()
fmt.Println("Raw struct:")
json.NewEncoder(os.Stdout).Encode(r)
maskResponse(r, "admin")
fmt.Println("Mask for admin:")
json.NewEncoder(os.Stdout).Encode(r)
r = makeResponse()
fmt.Println("Mask for marketing:")
maskResponse(r, "marketing")
json.NewEncoder(os.Stdout).Encode(r)
r = makeResponse()
fmt.Println("Mask for officer:")
maskResponse(r, "officer")
json.NewEncoder(os.Stdout).Encode(r)
}
// v must be pointer of struct
func maskResponse(v interface{}, role string) {
rv := reflect.ValueOf(v).Elem()
rt := reflect.TypeOf(v).Elem()
for i := 0; i < rt.NumField(); i++ {
ft := rt.Field(i)
r := ft.Tag.Get("role")
// allow for all role
if r == "" {
// nest struct
if ft.Type.Kind() == reflect.Struct {
maskResponse(rv.Field(i).Addr().Interface(), role)
}
continue
}
rs := strings.Split(r, ",")
// not allow
if !stringsContain(rs, role) {
rv.Field(i).Set(reflect.New(ft.Type).Elem())
continue
}
// allow but might not allow inside nest struct
if ft.Type.Kind() == reflect.Struct {
maskResponse(rv.Field(i).Addr().Interface(), role)
}
}
}
func stringsContain(xs []string, x string) bool {
for _, p := range xs {
if p == x {
return true
}
}
return false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment