maingate는 admin 계정으로 통일 또는 ApiToken
This commit is contained in:
120
core/api.go
120
core/api.go
@ -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,13 +134,11 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(output, func(i, j int) bool {
|
sort.Slice(output, func(i, j int) bool {
|
||||||
return output[i].ServiceName < output[j].ServiceName
|
return output[i].ServiceName < output[j].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,15 +301,13 @@ 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 len(service) > 0 {
|
||||||
if valid, _ := caller.isValidUser(service, "whitelist"); !valid {
|
if !caller.isAdminOrValidToken(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
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(service) > 0 {
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,8 +626,9 @@ 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
|
||||||
|
|||||||
@ -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
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user