maingate는 admin 계정으로 통일 또는 ApiToken
This commit is contained in:
138
core/api.go
138
core/api.go
@ -86,7 +86,7 @@ func (caller apiCaller) isGlobalAdmin() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok := caller.admins[email.(string)]; ok {
|
||||
if _, ok := caller.globalAdmins[email.(string)]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ func (caller apiCaller) getAccessableServices() ([]*serviceDescription, []string
|
||||
}
|
||||
|
||||
email = v.(string)
|
||||
_, admin = caller.admins[email]
|
||||
_, admin = caller.globalAdmins[email]
|
||||
}
|
||||
|
||||
var output []*serviceDescription
|
||||
@ -134,11 +134,9 @@ func (caller apiCaller) getAccessableServices() ([]*serviceDescription, []string
|
||||
if admin {
|
||||
output = append(output, desc)
|
||||
editable = append(editable, desc.ServiceName)
|
||||
} else if desc.isValidAPIUser("*", email) {
|
||||
} else if caller.isAdminOrValidToken(email) {
|
||||
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
|
||||
}
|
||||
|
||||
func (caller apiCaller) isValidUser(service any, category string) (valid bool, admin bool) {
|
||||
func (caller apiCaller) isAdmin(service any) bool {
|
||||
if *noauth {
|
||||
return true, true
|
||||
return true
|
||||
}
|
||||
|
||||
v, ok := caller.userinfo["email"]
|
||||
if !ok {
|
||||
logger.Println("isVaidUser failed. email is missing :", caller.userinfo)
|
||||
return false, false
|
||||
return false
|
||||
}
|
||||
|
||||
email := v.(string)
|
||||
if _, ok := caller.admins[email]; ok {
|
||||
return true, true
|
||||
if _, ok := caller.globalAdmins[email]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
svcdesc := caller.mg.services.get(service)
|
||||
if svcdesc == nil {
|
||||
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 {
|
||||
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")
|
||||
sh := caller.mg.services.get(servicename)
|
||||
if sh == nil {
|
||||
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)
|
||||
if !caller.isAdminOrValidToken(servicename) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -228,6 +215,11 @@ func (caller apiCaller) filesAPI(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !caller.isAdminOrValidToken(servicename) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := caller.mg.mongoClient.Delete(CollectionFile, bson.M{
|
||||
"service": servicename,
|
||||
"key": key,
|
||||
@ -309,14 +301,12 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
|
||||
queryvals := r.URL.Query()
|
||||
if r.Method == "GET" {
|
||||
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 !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{
|
||||
"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 {
|
||||
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)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -391,16 +382,13 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
|
||||
if r.Method == "GET" {
|
||||
name := queryvals.Get("name")
|
||||
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)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
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)
|
||||
w.Write(*(*[]byte)(serptr))
|
||||
} else {
|
||||
@ -421,7 +409,7 @@ func (caller apiCaller) serviceAPI(w http.ResponseWriter, r *http.Request) error
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
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)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
@ -461,15 +449,11 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
|
||||
if r.Method == "GET" {
|
||||
name := queryvals.Get("name")
|
||||
if len(name) > 0 {
|
||||
if valid, _ := caller.isValidUser(name, "*"); !valid {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
if !caller.isAdminOrValidToken(name) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return nil
|
||||
}
|
||||
|
||||
if valid, admin := caller.isValidUser(name, "maintenance"); 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).divisionsSerialized)
|
||||
w.Write(*(*[]byte)(serptr))
|
||||
} else {
|
||||
@ -477,7 +461,7 @@ func (caller apiCaller) maintenanceAPI(w http.ResponseWriter, r *http.Request) e
|
||||
}
|
||||
} else if r.Method == "POST" {
|
||||
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)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return nil
|
||||
@ -514,9 +498,9 @@ func (caller apiCaller) accountAPI(w http.ResponseWriter, r *http.Request) error
|
||||
return nil
|
||||
}
|
||||
|
||||
if valid, _ := caller.isValidUser(service, "account"); !valid {
|
||||
if !caller.isAdminOrValidToken(service) {
|
||||
logger.Println("accountAPI failed. not vaild user :", r.Method, caller.userinfo)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -641,9 +625,10 @@ func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error
|
||||
var noauth = flag.Bool("noauth", false, "")
|
||||
|
||||
type apiCaller struct {
|
||||
userinfo map[string]any
|
||||
admins map[string]bool
|
||||
mg *Maingate
|
||||
userinfo map[string]any
|
||||
globalAdmins map[string]bool
|
||||
mg *Maingate
|
||||
apiToken primitive.ObjectID
|
||||
}
|
||||
|
||||
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)
|
||||
caller := apiCaller{
|
||||
userinfo: userinfo,
|
||||
admins: adminsptr.emails,
|
||||
mg: mg,
|
||||
userinfo: userinfo,
|
||||
globalAdmins: adminsptr.emails,
|
||||
mg: mg,
|
||||
apiToken: apiTokenObj,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
@ -138,7 +138,7 @@ type serviceDescription struct {
|
||||
ServiceDescriptionSummary `bson:",inline" json:",inline"`
|
||||
Divisions map[string]*Division `bson:"divisions" json:"divisions"`
|
||||
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
|
||||
wl whitelist
|
||||
@ -150,13 +150,26 @@ type serviceDescription struct {
|
||||
updateUserinfo func(info usertokeninfo) (bool, string, string)
|
||||
getProviderInfo func(platform string, uid string) (string, string, error)
|
||||
|
||||
apiUsers unsafe.Pointer
|
||||
admins unsafe.Pointer
|
||||
divisionsForUsersSerialized unsafe.Pointer
|
||||
divisionsSerialized unsafe.Pointer
|
||||
serviceSerialized 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) {
|
||||
defer func() {
|
||||
s := recover()
|
||||
@ -254,25 +267,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
}
|
||||
|
||||
sh.wl.init(whites)
|
||||
|
||||
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)
|
||||
sh.admins = unsafe.Pointer(&sh.Admins)
|
||||
for _, keyid := range sh.ServerApiTokens {
|
||||
mg.apiTokenToService.add(keyid.Hex(), sh.ServiceCode)
|
||||
}
|
||||
@ -283,7 +278,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
|
||||
btsum, _ := json.Marshal(sh.ServiceDescriptionSummary)
|
||||
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
|
||||
}
|
||||
@ -410,30 +405,15 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Println("link success :", r.URL.Query())
|
||||
}
|
||||
|
||||
func (sh *serviceDescription) isValidAPIUser(category string, email string) bool {
|
||||
ptr := atomic.LoadPointer(&sh.apiUsers)
|
||||
catusers := *(*map[string]map[string]bool)(ptr)
|
||||
func (sh *serviceDescription) isAdmin(email string) bool {
|
||||
ptr := atomic.LoadPointer(&sh.admins)
|
||||
admins := *(*[]string)(ptr)
|
||||
|
||||
if category == "*" {
|
||||
for _, users := range catusers {
|
||||
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 {
|
||||
for _, a := range admins {
|
||||
if a == email {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@ -303,7 +303,7 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux *
|
||||
if old := mg.services.get(data.Service.ServiceName); old != nil {
|
||||
atomic.SwapPointer(&old.divisionsForUsersSerialized, data.Service.divisionsForUsersSerialized)
|
||||
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.serviceSummarySerialized, data.Service.serviceSummarySerialized)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user