maingate는 admin 계정으로 통일 또는 ApiToken

This commit is contained in:
2023-06-19 21:19:45 +09:00
parent 4bb25a1eff
commit 09328575ad
3 changed files with 91 additions and 113 deletions

View File

@ -86,7 +86,7 @@ func (caller apiCaller) isGlobalAdmin() bool {
return false return false
} }
if _, ok := caller.admins[email.(string)]; ok { if _, ok := caller.globalAdmins[email.(string)]; ok {
return true return true
} }
@ -125,7 +125,7 @@ func (caller apiCaller) getAccessableServices() ([]*serviceDescription, []string
} }
email = v.(string) email = v.(string)
_, admin = caller.admins[email] _, admin = caller.globalAdmins[email]
} }
var output []*serviceDescription var output []*serviceDescription
@ -134,11 +134,9 @@ func (caller apiCaller) getAccessableServices() ([]*serviceDescription, []string
if admin { if admin {
output = append(output, desc) output = append(output, desc)
editable = append(editable, desc.ServiceName) editable = append(editable, desc.ServiceName)
} else if desc.isValidAPIUser("*", email) { } else if caller.isAdminOrValidToken(email) {
output = append(output, desc) output = append(output, desc)
if desc.isValidAPIUser("service", email) { editable = append(editable, desc.ServiceName)
editable = append(editable, desc.ServiceName)
}
} }
} }
@ -149,60 +147,49 @@ func (caller apiCaller) getAccessableServices() ([]*serviceDescription, []string
return output, editable return output, editable
} }
func (caller apiCaller) isValidUser(service any, category string) (valid bool, admin bool) { func (caller apiCaller) isAdmin(service any) bool {
if *noauth { if *noauth {
return true, true return true
} }
v, ok := caller.userinfo["email"] v, ok := caller.userinfo["email"]
if !ok { if !ok {
logger.Println("isVaidUser failed. email is missing :", caller.userinfo) logger.Println("isVaidUser failed. email is missing :", caller.userinfo)
return false, false return false
} }
email := v.(string) email := v.(string)
if _, ok := caller.admins[email]; ok { if _, ok := caller.globalAdmins[email]; ok {
return true, true return true
} }
svcdesc := caller.mg.services.get(service) svcdesc := caller.mg.services.get(service)
if svcdesc == nil { if svcdesc == nil {
logger.Println("isVaidUser failed. service is missing :", service) logger.Println("isVaidUser failed. service is missing :", service)
return false, false return false
} }
return svcdesc.isValidAPIUser(category, email), false return svcdesc.isAdmin(email)
}
func (caller apiCaller) isAdminOrValidToken(service any) bool {
if caller.isAdmin(service) {
return true
}
sh := caller.mg.services.get(service)
if sh == nil {
return false
}
return sh.isValidToken(caller.apiToken)
} }
func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error { func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
if r.Method == "GET" { if r.Method == "GET" {
hasAuth := caller.isGlobalAdmin()
var email string
if !*noauth {
v, ok := caller.userinfo["email"]
if !ok {
return nil
}
email = v.(string)
_, hasAuth = caller.admins[email]
}
servicename := r.FormValue("service") servicename := r.FormValue("service")
sh := caller.mg.services.get(servicename) if !caller.isAdminOrValidToken(servicename) {
if sh == nil { w.WriteHeader(http.StatusUnauthorized)
w.WriteHeader(http.StatusBadRequest)
return nil
}
if !hasAuth {
if hasAuth = sh.isValidAPIUser("maintenance", email); !hasAuth {
hasAuth = sh.isValidAPIUser("service", email)
}
}
if !hasAuth {
w.WriteHeader(http.StatusBadRequest)
return nil return nil
} }
@ -228,6 +215,11 @@ func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
return nil return nil
} }
if !caller.isAdminOrValidToken(servicename) {
w.WriteHeader(http.StatusUnauthorized)
return nil
}
_, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{ _, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{
"service": servicename, "service": servicename,
"key": key, "key": key,
@ -309,14 +301,12 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
queryvals := r.URL.Query() queryvals := r.URL.Query()
if r.Method == "GET" { if r.Method == "GET" {
service := queryvals.Get("service") service := queryvals.Get("service")
if valid, _ := caller.isValidUser(service, "whitelist"); !valid {
logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest)
return nil
}
if len(service) > 0 { if len(service) > 0 {
if !caller.isAdminOrValidToken(service) {
logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusUnauthorized)
return nil
}
all, err := mg.mongoClient.FindAll(CollectionWhitelist, bson.M{ all, err := mg.mongoClient.FindAll(CollectionWhitelist, bson.M{
"service": service, "service": service,
@ -344,9 +334,10 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
if err := json.Unmarshal(body, &member); err != nil { if err := json.Unmarshal(body, &member); err != nil {
return err return err
} }
if valid, _ := caller.isValidUser(member.Service, "whitelist"); !valid {
if !caller.isAdminOrValidToken(member.Service) {
logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo) logger.Println("whitelistAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusUnauthorized)
return nil return nil
} }
@ -391,16 +382,13 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
if r.Method == "GET" { if r.Method == "GET" {
name := queryvals.Get("name") name := queryvals.Get("name")
if len(name) > 0 { if len(name) > 0 {
if valid, _ := caller.isValidUser(name, "*"); !valid { if !caller.isAdminOrValidToken(name) {
logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo) logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return nil return nil
} }
if valid, admin := caller.isValidUser(name, "service"); valid || admin { w.Header().Add("MG-X-SERVICE-EDITABLE", name)
w.Header().Add("MG-X-SERVICE-EDITABLE", name)
}
serptr := atomic.LoadPointer(&mg.services.get(name).serviceSerialized) serptr := atomic.LoadPointer(&mg.services.get(name).serviceSerialized)
w.Write(*(*[]byte)(serptr)) w.Write(*(*[]byte)(serptr))
} else { } else {
@ -421,7 +409,7 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return nil return nil
} }
} else if valid, _ := caller.isValidUser(service.Id, "service"); !valid { } else if !caller.isAdminOrValidToken(service.Id) {
logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo) logger.Println("serviceAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return nil return nil
@ -461,15 +449,11 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
if r.Method == "GET" { if r.Method == "GET" {
name := queryvals.Get("name") name := queryvals.Get("name")
if len(name) > 0 { if len(name) > 0 {
if valid, _ := caller.isValidUser(name, "*"); !valid { if !caller.isAdminOrValidToken(name) {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusUnauthorized)
return nil return nil
} }
w.Header().Add("MG-X-SERVICE-EDITABLE", name)
if valid, admin := caller.isValidUser(name, "maintenance"); valid || admin {
w.Header().Add("MG-X-SERVICE-EDITABLE", name)
}
serptr := atomic.LoadPointer(&mg.services.get(name).divisionsSerialized) serptr := atomic.LoadPointer(&mg.services.get(name).divisionsSerialized)
w.Write(*(*[]byte)(serptr)) w.Write(*(*[]byte)(serptr))
} else { } else {
@ -477,7 +461,7 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
} }
} else if r.Method == "POST" { } else if r.Method == "POST" {
servicename := queryvals.Get("name") servicename := queryvals.Get("name")
if valid, _ := caller.isValidUser(servicename, "service"); !valid { if !caller.isAdminOrValidToken(servicename) {
logger.Println("maintenanceAPI failed. not vaild user :", r.Method, caller.userinfo) logger.Println("maintenanceAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return nil return nil
@ -514,9 +498,9 @@ func (caller apiCaller) accountAPI(w http.ResponseWriter, r *http.Request) error
return nil return nil
} }
if valid, _ := caller.isValidUser(service, "account"); !valid { if !caller.isAdminOrValidToken(service) {
logger.Println("accountAPI failed. not vaild user :", r.Method, caller.userinfo) logger.Println("accountAPI failed. not vaild user :", r.Method, caller.userinfo)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusUnauthorized)
return nil return nil
} }
@ -641,9 +625,10 @@ func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error
var noauth = flag.Bool("noauth", false, "") var noauth = flag.Bool("noauth", false, "")
type apiCaller struct { type apiCaller struct {
userinfo map[string]any userinfo map[string]any
admins map[string]bool globalAdmins map[string]bool
mg *Maingate mg *Maingate
apiToken primitive.ObjectID
} }
func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) { func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
@ -704,11 +689,24 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
} }
} }
apiToken := r.Header.Get("MG-X-API-TOKEN")
var apiTokenObj primitive.ObjectID
if len(apiToken) > 0 {
obj, err := primitive.ObjectIDFromHex(apiToken)
if err != nil {
logger.Error(err)
w.WriteHeader(http.StatusBadRequest)
return
}
apiTokenObj = obj
}
logger.Println("api call :", r.URL.Path, r.Method, r.URL.Query(), userinfo) logger.Println("api call :", r.URL.Path, r.Method, r.URL.Query(), userinfo)
caller := apiCaller{ caller := apiCaller{
userinfo: userinfo, userinfo: userinfo,
admins: adminsptr.emails, globalAdmins: adminsptr.emails,
mg: mg, mg: mg,
apiToken: apiTokenObj,
} }
var err error var err error

View File

@ -138,7 +138,7 @@ type serviceDescription struct {
ServiceDescriptionSummary `bson:",inline" json:",inline"` ServiceDescriptionSummary `bson:",inline" json:",inline"`
Divisions map[string]*Division `bson:"divisions" json:"divisions"` Divisions map[string]*Division `bson:"divisions" json:"divisions"`
ServerApiTokens []primitive.ObjectID `bson:"api_tokens" json:"api_tokens"` ServerApiTokens []primitive.ObjectID `bson:"api_tokens" json:"api_tokens"`
ApiUsers map[string][]string `bson:"api_users" json:"api_users"` Admins []string `bson:"admins" json:"admins"`
auths *common.AuthCollection auths *common.AuthCollection
wl whitelist wl whitelist
@ -150,13 +150,26 @@ type serviceDescription struct {
updateUserinfo func(info usertokeninfo) (bool, string, string) updateUserinfo func(info usertokeninfo) (bool, string, string)
getProviderInfo func(platform string, uid string) (string, string, error) getProviderInfo func(platform string, uid string) (string, string, error)
apiUsers unsafe.Pointer admins unsafe.Pointer
divisionsForUsersSerialized unsafe.Pointer divisionsForUsersSerialized unsafe.Pointer
divisionsSerialized unsafe.Pointer divisionsSerialized unsafe.Pointer
serviceSerialized unsafe.Pointer serviceSerialized unsafe.Pointer
serviceSummarySerialized unsafe.Pointer serviceSummarySerialized unsafe.Pointer
} }
func (sh *serviceDescription) isValidToken(apiToken primitive.ObjectID) bool {
if apiToken.IsZero() {
return false
}
for _, test := range sh.ServerApiTokens {
if test == apiToken {
return true
}
}
return false
}
func (sh *serviceDescription) readProfile(authtype string, id string, binfo string) (email string, err error) { func (sh *serviceDescription) readProfile(authtype string, id string, binfo string) (email string, err error) {
defer func() { defer func() {
s := recover() s := recover()
@ -254,25 +267,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
} }
sh.wl.init(whites) sh.wl.init(whites)
sh.admins = unsafe.Pointer(&sh.Admins)
if len(sh.ApiUsers) == 0 {
sh.ApiUsers = map[string][]string{
"service": {},
"whitelist": {},
"account": {},
"maintenance": {},
}
}
parsedUsers := make(map[string]map[string]bool)
for cat, users := range sh.ApiUsers {
catusers := make(map[string]bool)
for _, user := range users {
catusers[user] = true
}
parsedUsers[cat] = catusers
}
sh.apiUsers = unsafe.Pointer(&parsedUsers)
for _, keyid := range sh.ServerApiTokens { for _, keyid := range sh.ServerApiTokens {
mg.apiTokenToService.add(keyid.Hex(), sh.ServiceCode) mg.apiTokenToService.add(keyid.Hex(), sh.ServiceCode)
} }
@ -283,7 +278,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
btsum, _ := json.Marshal(sh.ServiceDescriptionSummary) btsum, _ := json.Marshal(sh.ServiceDescriptionSummary)
atomic.StorePointer(&sh.serviceSummarySerialized, unsafe.Pointer(&btsum)) atomic.StorePointer(&sh.serviceSummarySerialized, unsafe.Pointer(&btsum))
logger.Println("service is ready :", sh.ServiceName, sh.ServiceCode, sh.ApiUsers, string(divmarshaled)) logger.Println("service is ready :", sh.ServiceName, sh.ServiceCode, sh.Admins, string(divmarshaled))
return nil return nil
} }
@ -410,30 +405,15 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
logger.Println("link success :", r.URL.Query()) logger.Println("link success :", r.URL.Query())
} }
func (sh *serviceDescription) isValidAPIUser(category string, email string) bool { func (sh *serviceDescription) isAdmin(email string) bool {
ptr := atomic.LoadPointer(&sh.apiUsers) ptr := atomic.LoadPointer(&sh.admins)
catusers := *(*map[string]map[string]bool)(ptr) admins := *(*[]string)(ptr)
if category == "*" { for _, a := range admins {
for _, users := range catusers { if a == email {
if _, ok := users[email]; ok {
return true
}
}
logger.Println("isValidAPIUser failed. email is not allowed :", category, email, catusers)
return false
}
if users, ok := catusers[category]; ok {
if _, ok := users[email]; ok {
return true return true
} }
logger.Println("isValidAPIUser failed. email is not allowed :", category, email, users)
return false
} }
logger.Println("isValidAPIUser failed. category is missing :", category)
return false return false
} }

View File

@ -303,7 +303,7 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
if old := mg.services.get(data.Service.ServiceName); old != nil { if old := mg.services.get(data.Service.ServiceName); old != nil {
atomic.SwapPointer(&old.divisionsForUsersSerialized, data.Service.divisionsForUsersSerialized) atomic.SwapPointer(&old.divisionsForUsersSerialized, data.Service.divisionsForUsersSerialized)
atomic.SwapPointer(&old.divisionsSerialized, data.Service.divisionsSerialized) atomic.SwapPointer(&old.divisionsSerialized, data.Service.divisionsSerialized)
atomic.SwapPointer(&old.apiUsers, data.Service.apiUsers) atomic.SwapPointer(&old.admins, data.Service.admins)
atomic.SwapPointer(&old.serviceSerialized, data.Service.serviceSerialized) atomic.SwapPointer(&old.serviceSerialized, data.Service.serviceSerialized)
atomic.SwapPointer(&old.serviceSummarySerialized, data.Service.serviceSummarySerialized) atomic.SwapPointer(&old.serviceSummarySerialized, data.Service.serviceSummarySerialized)