From cb08ecb53a4d472919f24baaf4e8069412c2870c Mon Sep 17 00:00:00 2001 From: rehjinh Date: Wed, 28 Jun 2023 16:10:28 +0900 Subject: [PATCH] =?UTF-8?q?maingate=20-=20=EC=97=B0=EA=B2=B0=EB=90=9C=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=20=EC=88=AB=EC=9E=90=20=EC=A1=B0=ED=9A=8C,?= =?UTF-8?q?=20=EC=97=B0=EA=B2=B0=20=EB=81=8A=EB=8A=94=20API=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config_template.json | 1 + core/maingate.go | 7 ++ core/service.go | 158 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+) diff --git a/config_template.json b/config_template.json index 9bb0857..5ba3080 100644 --- a/config_template.json +++ b/config_template.json @@ -1,6 +1,7 @@ { "maingate_mongodb_url": "mongodb://...", "autologin_ttl": 604800, + "maximum_num_link_account": 10, "redirect_base_url": "", "google_client_id" : "", "google_client_secret" : "", diff --git a/core/maingate.go b/core/maingate.go index e57657f..948cfbb 100644 --- a/core/maingate.go +++ b/core/maingate.go @@ -121,6 +121,7 @@ type maingateConfig struct { Mongo string `json:"maingate_mongodb_url"` SessionTTL int64 `json:"maingate_session_ttl"` Autologin_ttl int64 `json:"autologin_ttl"` + MaximumNumLinkAccount int64 `json:"maximum_num_link_account"` RedirectBaseUrl string `json:"redirect_base_url"` GoogleClientId string `json:"google_client_id"` GoogleClientSecret string `json:"google_client_secret"` @@ -326,6 +327,12 @@ func (mg *Maingate) prepare(context context.Context) (err error) { return err } + if err = mg.mongoClient.MakeIndices(CollectionAccount, map[string]bson.D{ + "accid": {{Key: "accid", Value: 1}}, + }); err != nil { + return err + } + if err = mg.mongoClient.MakeUniqueIndices(CollectionFile, map[string]bson.D{ "sk": {{Key: "service", Value: 1}, {Key: "key", Value: 1}}, }); err != nil { diff --git a/core/service.go b/core/service.go index 559ca40..9c51699 100644 --- a/core/service.go +++ b/core/service.go @@ -1,6 +1,7 @@ package core import ( + "context" "crypto/md5" "encoding/hex" "encoding/json" @@ -142,6 +143,8 @@ type serviceDescription struct { wl *whitelist mongoClient common.MongoClient sessionTTL time.Duration + MaximumNumLinkAccount int64 + serviceCodeBytes []byte getUserBrowserInfo func(r *http.Request) (string, error) getUserTokenWithCheck func(platform string, userid string, brinfo string) (usertokeninfo, error) @@ -255,6 +258,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error { devstr2 := string(divmarshaled2) sh.divisionsForUsersSerialized = unsafe.Pointer(&devstr2) + sh.MaximumNumLinkAccount = mg.maingateConfig.MaximumNumLinkAccount sh.mongoClient = mg.mongoClient sh.auths = mg.auths sh.sessionTTL = time.Duration(mg.SessionTTL * int64(time.Second)) @@ -403,6 +407,155 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) { logger.Println("link success :", r.URL.Query()) } +// == link된 계정을 해제 한다. but, 최소 1개 계정은 연결되어 있어야 한다. +func (sh *serviceDescription) unlink(w http.ResponseWriter, r *http.Request) { + defer func() { + s := recover() + if s != nil { + logger.Error(s) + } + }() + + if r.Method != "GET" { + w.WriteHeader(http.StatusBadRequest) + return + } + + queryvals := r.URL.Query() + sType := queryvals.Get("stype") + sId := queryvals.Get("sid") + sk := queryvals.Get("sk") + + authInfo := sh.auths.Find(sk) + if authInfo == nil { + // 잘못된 세션 + logger.Println("linkinfo failed. session key is not valid :", sk) + w.WriteHeader(http.StatusBadRequest) + return + } + + // fmt.Println("=================") + // fmt.Println(sType) + // fmt.Println(sId) + // fmt.Println("=================") + // fmt.Println(authInfo.Platform) + // fmt.Println(authInfo.Uid) + // fmt.Println("=================") + + if authInfo.Uid != sId || authInfo.Platform != sType { + logger.Println("unlink failed. session key is not correct :", *authInfo, queryvals) + w.WriteHeader(http.StatusBadRequest) + return + } + + numRecord, err := sh.mongoClient.Collection(CollectionAccount).CountDocuments(context.Background(), bson.M{ + "accid": authInfo.Accid, + }, options.Count().SetLimit(2)) + + if err != nil { + logger.Error("unlink failed, fail to count accounts :", err) + w.WriteHeader(http.StatusBadRequest) + } + + if numRecord <= 1 { + logger.Println("unlink failed. At least one link must be maintained. :", r.URL.Query()) + w.WriteHeader(http.StatusBadRequest) + return + } + + sType, sId, err = sh.getProviderInfo(sType, sId) + if err != nil { + logger.Error("getProviderInfo failed :", err) + w.WriteHeader(http.StatusBadRequest) + } + + link, err := sh.mongoClient.FindOne(CollectionLink, bson.M{ + "platform": sType, + "uid": sId, + }, options.FindOne().SetProjection(bson.M{"_id": 1})) + if err != nil { + logger.Error("link failed. FindOneAndUpdate link err:", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + newid, err := sh.mongoClient.FindOneAndDelete(CollectionAccount, bson.M{ + "_id": link["_id"].(primitive.ObjectID), + }, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1})) + if err != nil { + logger.Error("unlink failed. Delete ServiceName err :", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + // newid가 있어야 한다. 그래야 기존 서비스 계정이 없는 상태이다. + if newid == nil { + // 이미 계정이 있네? + logger.Println("unlink failed. service account not found:", r.URL.Query()) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Println("unlink success :", r.URL.Query()) +} + +// == 연결된 계정 정보(숫자) 전달하는 API +func (sh *serviceDescription) linkinfo(w http.ResponseWriter, r *http.Request) { + defer func() { + s := recover() + if s != nil { + logger.Error(s) + } + }() + + if r.Method != "GET" { + w.WriteHeader(http.StatusBadRequest) + return + } + + queryvals := r.URL.Query() + sType := queryvals.Get("stype") + sId := queryvals.Get("sid") + sk := queryvals.Get("sk") + + authInfo := sh.auths.Find(sk) + if authInfo == nil { + // 잘못된 세션 + logger.Println("linkinfo failed. session key is not valid :", sk) + w.WriteHeader(http.StatusBadRequest) + return + } + + // fmt.Println("=================") + // fmt.Println(sType) + // fmt.Println(sId) + // fmt.Println("=================") + // fmt.Println(authInfo.Platform) + // fmt.Println(authInfo.Uid) + // fmt.Println("=================") + + //if oldAuth.Token != oldToken || oldAuth.Uid != oldId || oldAuth.Platform != oldType { + if authInfo.Uid != sId || authInfo.Platform != sType { + logger.Println("linkinfo failed. session key is not correct :", *authInfo, queryvals) + w.WriteHeader(http.StatusBadRequest) + return + } + + numRecord, err := sh.mongoClient.Collection(CollectionAccount).CountDocuments(context.Background(), bson.M{ + "accid": authInfo.Accid, + }, options.Count().SetLimit(sh.MaximumNumLinkAccount)) + + if err != nil { + logger.Error("linkinfo failed. CountDocuments err :", err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Println("linkinfo :", numRecord) + w.Write([]byte(fmt.Sprintf(`{"num_linked_account":"%d"}`, numRecord))) + +} + func (sh *serviceDescription) isAdmin(email string) bool { ptr := atomic.LoadPointer(&sh.admins) admins := *(*[]string)(ptr) @@ -618,7 +771,12 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request) sh.authorize(w, r) } else if strings.HasSuffix(r.URL.Path, "/link") { sh.link(w, r) + } else if strings.HasSuffix(r.URL.Path, "/unlink") { + sh.unlink(w, r) + } else if strings.HasSuffix(r.URL.Path, "/linkinfo") { + sh.linkinfo(w, r) } else if strings.HasSuffix(r.URL.Path, "/divs") { + // TODO : 세션키와 authtoken을 헤더로 받아서 accid 조회 queryvals := r.URL.Query() sk := queryvals.Get("sk")