package apicaller import ( "encoding/json" "os" "strings" "sync" "sync/atomic" "unsafe" "repositories.action2quare.com/ayo/gocommon/flagx" ) type ApiCaller interface { HasAuthority(authPath string) bool GetMyAuthority() []string } type ApiCallerAuths interface { NewApiCaller(user string) ApiCaller NewApiCallerByServer() ApiCaller Update(newusers map[string]*map[string]bool) error Serialize() []byte } type apiCallerAuths struct { sync.Mutex serialized unsafe.Pointer // *[]byte users map[string]*map[string]bool // email -> authoriries } func (a *apiCallerAuths) Serialize() []byte { btptr := atomic.LoadPointer(&a.serialized) return *(*[]byte)(btptr) } func (a *apiCallerAuths) getAuthority(email string) []string { a.Lock() defer a.Unlock() auths := a.users[email] if auths == nil { return nil } var out []string for k, v := range *auths { if v { out = append(out, k) } } return out } func (a *apiCallerAuths) Update(newAuths map[string]*map[string]bool) error { src := map[string][]string{} for user, auths := range newAuths { for cat, has := range *auths { if has { arr := append(src[cat], user) src[cat] = arr } else if _, ok := src[cat]; !ok { src[cat] = []string{} } } } a.Lock() defer a.Unlock() file, err := os.Create(*userAuthsFileName) if err != nil { return err } defer file.Close() enc := json.NewEncoder(file) err = enc.Encode(src) if err != nil { return err } a.users = newAuths bt, _ := json.Marshal(newAuths) atomic.StorePointer(&a.serialized, unsafe.Pointer(&bt)) return nil } func (a *apiCallerAuths) hasAuthority(email string, authPath string) bool { a.Lock() defer a.Unlock() auths, ok := a.users[email] if !ok { return false } if (*auths)[authPath] { return true } for k, v := range *auths { if strings.HasPrefix(k, authPath+"/") { return v } } return false } var userAuthsFileName = flagx.String("userauth", "userauths.json", "-userauth=[json file path]") func NewApiCallerAuths() ApiCallerAuths { var out apiCallerAuths f, _ := os.Open(*userAuthsFileName) if f == nil { emptyAuths := map[string][]string{ "/admins": {"enter_first_admin_email@action2quare.com"}, } newf, _ := os.Create(*userAuthsFileName) if newf != nil { enc := json.NewEncoder(newf) enc.Encode(emptyAuths) newf.Close() f, _ = os.Open(*userAuthsFileName) } } if f != nil { defer f.Close() var src map[string][]string dec := json.NewDecoder(f) dec.Decode(&src) compiled := make(map[string]*map[string]bool) // 전체 유저 목록을 먼저 뽑고나서 for _, users := range src { for _, user := range users { if _, ok := compiled[user]; !ok { compiled[user] = &map[string]bool{} } } } // 전체 유저한테 모든 카테고리를 설정한다. for _, auths := range compiled { for cat := range src { (*auths)[cat] = false } } // 이제 유저별 권한을 설정 for category, users := range src { for _, user := range users { (*compiled[user])[category] = true } } out = apiCallerAuths{ users: compiled, } } else { out = apiCallerAuths{ users: map[string]*map[string]bool{}, } } marshaled, _ := json.Marshal(out.users) out.serialized = unsafe.Pointer(&marshaled) return &out } type apiCaller struct { userAuths *apiCallerAuths caller string } func (a *apiCallerAuths) NewApiCaller(user string) ApiCaller { if len(user) == 0 { return nil } return &apiCaller{ userAuths: a, caller: user, } } func (a *apiCallerAuths) NewApiCallerByServer() ApiCaller { return &apiCaller{ userAuths: a, caller: "", } } func (ac apiCaller) callByServer() bool { return len(ac.caller) == 0 } func (ac apiCaller) HasAuthority(authPath string) bool { if ac.callByServer() { return true } return ac.userAuths.hasAuthority(ac.caller, authPath) } func (ac apiCaller) GetMyAuthority() []string { if !ac.callByServer() { return ac.userAuths.getAuthority(ac.caller) } return nil }