package core import ( "crypto/md5" "encoding/hex" "encoding/json" "errors" "fmt" "io" "net/http" "strconv" "strings" "time" "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" "repositories.action2quare.com/ayo/gocommon/session" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo/options" ) type blockinfo struct { Start primitive.DateTime `bson:"start" json:"start"` End primitive.DateTime `bson:"_ts" json:"_ts"` Accid primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"` Meta primitive.M `bson:"meta,omitempty" json:"meta,omitempty"` } type whitelistmember struct { Id primitive.ObjectID `bson:"_id" json:"_id"` Alias string `bson:"alias" json:"alias"` Platform string `bson:"platform" json:"platform"` Desc string `bson:"desc" json:"desc"` ExpiredAt primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"` } func (wh *whitelistmember) Key() string { return wh.Alias } func (wh *whitelistmember) Expired() bool { // 얘는 Expired가 있기만 하면 제거된 상태 return wh.ExpiredAt != 0 } func (bi *blockinfo) Key() primitive.ObjectID { return bi.Accid } func (bi *blockinfo) Expired() bool { return bi.End.Time().Unix() < time.Now().UTC().Unix() } type usertokeninfo struct { platform string userid string token string //refreshtoken secret string brinfo string accesstoken string // microsoft only accesstoken_expire_time int64 // microsoft only } type DivisionStateName string const ( DivisionState_Closed = DivisionStateName("closed") DivisionState_Maintenance = DivisionStateName("maintenance") DivisionState_RestrictedOpen = DivisionStateName("restricted") DivisionState_FullOpen = DivisionStateName("open") ) type Maintenance struct { Notice string `bson:"notice" json:"notice"` StartTimeUTC int64 `bson:"start_unixtime_utc" json:"start_unixtime_utc"` link string } type DivisionForUser struct { Priority int `bson:"priority" json:"priority"` State DivisionStateName `bson:"state" json:"state"` Maintenance *Maintenance `bson:"maintenance,omitempty" json:"maintenance,omitempty"` } type Division struct { DivisionForUser `bson:",inline" json:",inline"` Url_Deprecated string `bson:"url" json:"url"` Urls bson.M `bson:"urls" json:"urls"` urlsSerialized []byte } type serviceDescription struct { Id primitive.ObjectID `bson:"_id" json:"_id"` Divisions map[string]*Division `bson:"divisions" json:"divisions"` ServerApiTokens []primitive.ObjectID `bson:"api_tokens" json:"api_tokens"` MaximumNumLinkAccount int64 VersionSplits map[string]string `bson:"version_splits" json:"version_splits"` sessionProvider session.Provider wl *memberContainerPtr[string, *whitelistmember] bl *memberContainerPtr[primitive.ObjectID, *blockinfo] mongoClient gocommon.MongoClient sessionTTL time.Duration getUserBrowserInfo func(r *http.Request) (string, error) getUserTokenWithCheck func(platform string, userid string, brinfo string) (usertokeninfo, error) updateUserinfo func(info usertokeninfo) (bool, string, string) getProviderInfo func(platform string, uid string) (string, string, error) divisionsSerialized []byte serviceSerialized []byte divisionsSplits map[string][]byte mustUseChecksum bool } func (sh *serviceDescription) isValidToken(apiToken primitive.ObjectID) bool { if *devflag { return true } 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() if s != nil { logger.Error("readProfile failed :", authtype, id, s) if errt, ok := s.(error); ok { err = errt } else { err = errors.New(fmt.Sprint(s)) } } }() userinfo, err := sh.getUserTokenWithCheck(authtype, id, binfo) if err != nil { return "", err } if len(userinfo.token) == 0 { return "", errors.New("refreshtoken token not found") } //-- 토큰으로 모두 확인이 끝났으면 갱신한다. ok, _, email := sh.updateUserinfo(userinfo) if !ok { return "", errors.New("updateUserinfo failed") } return email, nil } func (sh *serviceDescription) prepare(mg *Maingate) error { divs := sh.Divisions divsForUsers := make(map[string]*DivisionForUser) defaultDivNames := make(map[string]bool) for dn, div := range divs { if div.State != DivisionState_Closed { defaultDivNames[dn] = true } if len(div.Url_Deprecated) > 0 && len(div.Urls) == 0 { div.Urls = bson.M{"warehouse": div.Url_Deprecated} } div.urlsSerialized, _ = json.Marshal(bson.M{ "service": div.Urls, }) divsForUsers[dn] = &div.DivisionForUser if len(div.State) == 0 { div.State = DivisionState_FullOpen } if div.State != DivisionState_FullOpen { if div.Maintenance == nil { div.Maintenance = &Maintenance{} } if len(div.Maintenance.link) == 0 { if len(div.Maintenance.Notice) == 0 { div.Maintenance.link = "https://www.action2quare.com" } else if strings.HasPrefix(div.Maintenance.Notice, "http") { div.Maintenance.link = div.Maintenance.Notice } else { var fd FileDocumentDesc if err := mg.mongoClient.FindOneAs(CollectionFile, bson.M{ "key": div.Maintenance.Notice, }, &fd, options.FindOne().SetProjection(bson.M{"link": 1})); err != nil { logger.Println(err) return err } div.Maintenance.link = fd.Link logger.Println("div.Maintenance.link :", fd.Link) } } } else { div.Maintenance = nil } } sh.divisionsSerialized, _ = json.Marshal(divs) sh.divisionsSplits = make(map[string][]byte) for ver, divnamesT := range sh.VersionSplits { if ver == "default" { continue } divnames := strings.Split(divnamesT, ",") split := make(map[string]*DivisionForUser) for _, divname := range divnames { split[divname] = divsForUsers[divname] // 스플릿 된 버전은 default에서 제거해야 한다. delete(defaultDivNames, divname) } splitMarshaled, _ := json.Marshal(split) sh.divisionsSplits[ver] = splitMarshaled } defaultsDivs := make(map[string]*DivisionForUser) for divname := range defaultDivNames { defaultsDivs[divname] = divsForUsers[divname] } defaultMarshaled, _ := json.Marshal(defaultsDivs) sh.divisionsSplits["default"] = defaultMarshaled sh.MaximumNumLinkAccount = config.MaximumNumLinkAccount sh.mongoClient = mg.mongoClient sh.sessionProvider = mg.sessionProvider sh.sessionTTL = time.Duration(config.SessionTTL * int64(time.Second)) sh.getUserBrowserInfo = mg.GetUserBrowserInfo sh.getUserTokenWithCheck = mg.getUserTokenWithCheck sh.updateUserinfo = mg.updateUserinfo sh.getProviderInfo = mg.getProviderInfo if globalApiToken, err := primitive.ObjectIDFromHex(config.GlobalMaingateToken); err == nil { if !globalApiToken.IsZero() { f := func() bool { for _, t := range sh.ServerApiTokens { if t == globalApiToken { return true } } return false }() if !f { sh.ServerApiTokens = append(sh.ServerApiTokens, globalApiToken) } } } sh.wl = &mg.wl sh.bl = &mg.bl sh.serviceSerialized, _ = json.Marshal(sh) return nil } func (sh *serviceDescription) link(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() //oldToken := queryvals.Get("otoken") oldType := queryvals.Get("otype") oldId := queryvals.Get("oid") sk := queryvals.Get("sk") //newToken := queryvals.Get("ntoken") newType := queryvals.Get("ntype") newId := queryvals.Get("nid") oldAuth, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } bfinfo, err := sh.getUserBrowserInfo(r) if err != nil { logger.Error("getUserBrowserInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } guestlink := (oldAuth.Platform == "guest") if !guestlink { _, err = sh.readProfile(oldType, oldId, bfinfo) if err != nil { logger.Error("readProfile(old) failed :", err) w.WriteHeader(http.StatusBadRequest) return } } else { logger.Println("from guest acc to real acc link : ", oldId, bfinfo, newType, newId, bfinfo) } oldType, oldId, err = sh.getProviderInfo(oldType, oldId) if err != nil { logger.Error("getProviderInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } // fmt.Println("=================") // fmt.Println(oldType) // fmt.Println(oldId) // fmt.Println("=================") // fmt.Println(newType) // fmt.Println(newId) // fmt.Println("=================") // fmt.Println(oldAuth.Platform) // fmt.Println(oldAuth.Uid) // fmt.Println("=================") //if oldAuth.Token != oldToken || oldAuth.Uid != oldId || oldAuth.Platform != oldType { if oldAuth.Uid != oldId || oldAuth.Platform != oldType { logger.Println("link failed. session key is not correct :", oldAuth, queryvals) w.WriteHeader(http.StatusBadRequest) return } //email, err := sh.readProfile(newType, newId, bfinfo) _, err = sh.readProfile(newType, newId, bfinfo) if err != nil { logger.Error("readProfile(new) failed :", err) w.WriteHeader(http.StatusBadRequest) return } // if len(email) == 0 { // logger.Println("link failed. email is missing :", r.URL.Query()) // w.WriteHeader(http.StatusBadRequest) // return // } newType, newId, err = sh.getProviderInfo(newType, newId) if err != nil { logger.Error("getProviderInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } found, err := sh.mongoClient.FindOne(CollectionLink, bson.M{"platform": newType, "uid": newId}, options.FindOne()) if err != nil { logger.Error("link failed. FindOne err:", err) w.WriteHeader(http.StatusInternalServerError) return } if found != nil { logger.Println("link failed. already have service account: ", r.URL.Query()) w.Write([]byte(`{"alreadylink":true}`)) return } createtime := primitive.NewDateTimeFromTime(time.Now().UTC()) link, err := sh.mongoClient.FindOneAndUpdate(CollectionLink, bson.M{ "platform": newType, "uid": newId, }, bson.M{ "$setOnInsert": bson.M{ "create": createtime, //"email": email, }, }, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(true).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.Update(CollectionAccount, bson.M{ "_id": link["_id"].(primitive.ObjectID), }, bson.M{ "$setOnInsert": bson.M{ "accid": oldAuth.Account, "create": createtime, }, }, options.Update().SetUpsert(true)) if err != nil { logger.Error("link failed. Update ServiceName err :", err) w.WriteHeader(http.StatusBadRequest) return } // newid가 있어야 한다. 그래야 기존 서비스 계정이 없는 상태이다. if newid == nil { // 이미 계정이 있네? logger.Println("link failed. already have service account :", r.URL.Query()) w.WriteHeader(http.StatusBadRequest) return } if guestlink { //기존 게스트 링크 삭제 link, err = sh.mongoClient.FindOneAndDelete(CollectionLink, bson.M{ "platform": oldType, "uid": oldId, }, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1})) if err == nil { sh.mongoClient.Delete(CollectionAccount, bson.M{ "_id": link["_id"].(primitive.ObjectID), }) } } 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") targetType := queryvals.Get("ttype") authInfo, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } // fmt.Println("=================") // fmt.Println(sType) // fmt.Println(sId) // fmt.Println("=================") // fmt.Println(authInfo.Platform) // fmt.Println(authInfo.Uid) // fmt.Println("=================") sType, sId, err = sh.getProviderInfo(sType, sId) if err != nil { logger.Println("getProviderInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } if authInfo.Uid != sId || authInfo.Platform != sType { logger.Println("unlink failed. session key is not correct :", authInfo, queryvals) w.WriteHeader(http.StatusBadRequest) return } accDocs, err := sh.mongoClient.FindAll(CollectionAccount, bson.M{ "accid": authInfo.Account, }, options.Find().SetProjection(bson.M{ "_id": 1, })) if err != nil { logger.Error("unlink failed, fail to count accounts :", err) w.WriteHeader(http.StatusBadRequest) } if len(accDocs) <= 1 { logger.Println("unlink failed. At least one link must be maintained. :", r.URL.Query()) w.WriteHeader(http.StatusBadRequest) return } var ids primitive.A for _, accDoc := range accDocs { ids = append(ids, accDoc["_id"].(primitive.ObjectID)) } link, err := sh.mongoClient.FindOneAndDelete(CollectionLink, bson.M{ "platform": targetType, "_id": bson.M{"$in": ids}, }, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1})) if err != nil { logger.Error("link failed. FindOneAndUpdate link err:", err) w.WriteHeader(http.StatusInternalServerError) return } preid, 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 } if preid == nil { logger.Println("unlink failed. service account not found:", r.URL.Query()) w.WriteHeader(http.StatusBadRequest) return } logger.Println("unlink success :", r.URL.Query()) } // == 연결된 계정 정보(platform) 전달하는 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, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } // fmt.Println("=================") // fmt.Println(sType) // fmt.Println(sId) // fmt.Println("=================") // fmt.Println(authInfo.Platform) // fmt.Println(authInfo.Uid) // fmt.Println("=================") sType, sId, err = sh.getProviderInfo(sType, sId) if err != nil { logger.Error("getProviderInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } //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 } platformName := "platform" accDocs, err := sh.mongoClient.FindAll(CollectionAccount, bson.M{"accid": authInfo.Account}, options.Find().SetLimit(sh.MaximumNumLinkAccount).SetProjection(bson.M{ "_id": 1, })) if err != nil { logger.Error("linkinfo failed. CountDocuments err :", err) w.WriteHeader(http.StatusInternalServerError) return } var ids primitive.A for _, accDoc := range accDocs { ids = append(ids, accDoc["_id"].(primitive.ObjectID)) } links, err := sh.mongoClient.FindAll(CollectionLink, bson.M{ "_id": bson.M{"$in": ids}, }, options.Find().SetLimit(sh.MaximumNumLinkAccount).SetProjection(bson.M{ platformName: 1, })) if err != nil { logger.Error("linkinfo failed. FindAll returns err :", err) w.WriteHeader(http.StatusInternalServerError) return } var linkstrs []string for _, link := range links { linkstrs = append(linkstrs, link[platformName].(string)) } linkbytes, err := json.Marshal(linkstrs) if err != nil { logger.Error("linkinfo failed. json marshal fail :", err) w.WriteHeader(http.StatusInternalServerError) return } logger.Println("linkinfo :", linkstrs) w.Write(linkbytes) } // == 계정 이메일 조회 func (sh *serviceDescription) emailinfo(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() reqauthtype := queryvals.Get("type") uid := queryvals.Get("id") bfinfo, err := sh.getUserBrowserInfo(r) if err != nil { logger.Error("getUserBrowserInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } email, err := sh.readProfile(reqauthtype, uid, bfinfo) if err != nil { logger.Error("readProfile(new) failed :", err) w.WriteHeader(http.StatusBadRequest) return } if strings.HasPrefix(email, "__dummy_") && strings.HasSuffix(email, "temp__") { email = "" } if strings.HasSuffix(email, "@noauth.flag") || strings.HasSuffix(email, "@guest.flag") { email = "" } // fmt.Println("=================") // fmt.Println(email) // fmt.Println("=================") //logger.Println("Email :", email) w.Write([]byte(fmt.Sprintf(`{"email":"%s"}`, email))) } func (sh *serviceDescription) authorize_dev(w http.ResponseWriter, r *http.Request) { if r.Method == "DELETE" { sk := r.Header.Get("AS-X-SESSION") if authinfo, err := sh.sessionProvider.Query(sk); err == nil { bt := r.Header.Get("AS-X-BLOCK") if len(bt) > 0 { dur, _ := strconv.ParseInt(bt, 10, 0) sh.bl.add(&blockinfo{ Start: primitive.NewDateTimeFromTime(time.Now().UTC()), End: primitive.NewDateTimeFromTime(time.Now().UTC().Add(time.Second * time.Duration(dur))), Accid: authinfo.Account, }) } sh.sessionProvider.RevokeAll(authinfo.Account) } return } sh.authorize(w, r) } func (sh *serviceDescription) authorize(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() reqauthtype := queryvals.Get("type") uid := queryvals.Get("id") checksum := r.Header.Get("AS-X-CHECKSUM") if len(checksum) > 0 || sh.mustUseChecksum { nonce := queryvals.Get("nonce") cookie := r.Header.Get("Cookie") h := md5.New() h.Write([]byte(cookie + nonce)) if checksum != hex.EncodeToString(h.Sum(nil)) { w.WriteHeader(http.StatusBadRequest) return } } var email string if !*noauth && (*authtype == "on" || *authtype == "both") { if len(reqauthtype) > 0 { //email, err := sh.readProfile(authtype, uid, accesstoken) bfinfo, err := sh.getUserBrowserInfo(r) if err != nil { logger.Error("getUserBrowserInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } email, err = sh.readProfile(reqauthtype, uid, bfinfo) if err != nil { logger.Error("readProfile failed :", err) w.WriteHeader(http.StatusBadRequest) return } newType, newId, err := sh.getProviderInfo(reqauthtype, uid) if err != nil { logger.Error("getProviderInfo failed :", err) w.WriteHeader(http.StatusBadRequest) return } if reqauthtype != newType || uid != newId { reqauthtype = newType uid = newId } } else if *authtype == "both" { email = fmt.Sprintf("%s@guest.flag", uid) } else { // authtype이 없으면 입장 불가 logger.Error("authorize failed. 'type' query parameter is missing") w.WriteHeader(http.StatusBadRequest) return } } else { email = fmt.Sprintf("%s@noauth.flag", uid) } // platform + id -> account id createtime := primitive.NewDateTimeFromTime(time.Now().UTC()) link, err := sh.mongoClient.FindOneAndUpdate(CollectionLink, bson.M{ "platform": reqauthtype, "uid": uid, }, bson.M{ "$setOnInsert": bson.M{ "create": createtime, //"email": email, }, }, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(true).SetProjection(bson.M{"_id": 1, "_ts": 1})) if err != nil { logger.Error("authorize failed :", err) w.WriteHeader(http.StatusInternalServerError) return } linkid := link["_id"].(primitive.ObjectID) newaccid := primitive.NewObjectID() account, err := sh.mongoClient.FindOneAndUpdate(CollectionAccount, bson.M{ "_id": linkid, }, bson.M{ "$setOnInsert": bson.M{ "accid": newaccid, "create": createtime, }, }, options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(true).SetProjection(bson.M{"accid": 1, "create": 1})) if err != nil { logger.Error("authorize failed. Update sh.ServiceName err:", err) w.WriteHeader(http.StatusInternalServerError) return } accid := account["accid"].(primitive.ObjectID) oldcreate := account["create"].(primitive.DateTime) newaccount := oldcreate == createtime if bi, ok := sh.bl.get(accid); ok { // 블럭된 계정. 블락 정보를 알려준다. w.Header().Add("MG-ACCOUNTBLOCK-START", strconv.FormatInt(bi.Start.Time().Unix(), 10)) w.Header().Add("MG-ACCOUNTBLOCK-END", strconv.FormatInt(bi.End.Time().Unix(), 10)) w.WriteHeader(http.StatusUnauthorized) return } alias := r.Header.Get("AS-X-ALIAS") if len(alias) == 0 { alias = email } sk, err := sh.sessionProvider.New(&session.Authorization{ Account: accid, Platform: reqauthtype, Uid: uid, Alias: alias, }) if err != nil { logger.Error("authorize failed. sessionProvider.New err:", err) w.WriteHeader(http.StatusInternalServerError) return } output := map[string]any{ "sk": sk, "expirein": sh.sessionTTL.Seconds(), "newAccount": newaccount, "accid": accid.Hex(), } if len(reqauthtype) == 0 { output["noauth"] = true } if link["_ts"] != nil { delts := link["_ts"].(primitive.DateTime) if !delts.Time().IsZero() { // 삭제된 계정. 삭제 되었다고 알려주자 w.Header().Add("MG-ACCOUNT-DELETED", "TRUE") } } json.NewEncoder(w).Encode(output) } func (sh *serviceDescription) findVersionSplit(version string) []byte { if len(version) > 0 { for k, v := range sh.divisionsSplits { if strings.HasPrefix(version, k) { if version == k || version[len(k)] == '.' { return v } } } } return sh.divisionsSplits["default"] } func (sh *serviceDescription) delacc(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") cancel := queryvals.Has("cancel") authInfo, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } originAuthType := sType if !*devflag || len(sType) != 0 { sType, sId, err = sh.getProviderInfo(sType, sId) if err != nil { logger.Error("delacc failed. getProviderInfo err :", err) w.WriteHeader(http.StatusBadRequest) return } } if authInfo.Uid != sId || authInfo.Platform != sType { logger.Println("delacc failed. session key is not correct :", authInfo, queryvals) w.WriteHeader(http.StatusBadRequest) return } linkidMap, err := sh.mongoClient.FindAll(CollectionAccount, bson.M{"accid": authInfo.Account}, options.Find().SetProjection(bson.M{"_id": 1})) if err != nil { logger.Error("delacc failed. FindAll account err :", err) w.WriteHeader(http.StatusInternalServerError) return } var linkidAry primitive.A for _, linkid := range linkidMap { linkidAry = append(linkidAry, linkid["_id"].(primitive.ObjectID)) } delfilter := primitive.M{"_id": bson.M{"$in": linkidAry}} var delop primitive.M if !cancel { curtime := time.Now().UTC() if *devflag && len(sType) == 0 { curtime = curtime.Add(time.Second * time.Duration(10-config.AccDelTTL)) } delop = primitive.M{"$set": primitive.M{"_ts": primitive.NewDateTimeFromTime(curtime)}} if originAuthType == AuthPlatformFirebaseAuth { if sType == "guest" { logger.Error("delacc failed. cannot delete a guest account.") w.WriteHeader(http.StatusBadRequest) return } sh.mongoClient.Delete(CollectionFirebaseUserInfo, bson.M{"firebaseuserid": sId}) } } else { delfilter["platform"] = sType targetLinkId, err := sh.mongoClient.FindAll(CollectionLink, delfilter, options.Find().SetProjection(bson.M{"_id": 1})) if len(targetLinkId) != 1 { logger.Error("delacc failed. FindAll link err :", err) w.WriteHeader(http.StatusInternalServerError) return } delfilter = primitive.M{"_id": targetLinkId[0]["_id"].(primitive.ObjectID)} delop = primitive.M{"$unset": primitive.M{"_ts": true}} } updated, _, err := sh.mongoClient.Update(CollectionAccount, delfilter, delop, options.Update().SetUpsert(false)) if !updated || err != nil { logger.Error("delacc failed. Update CollectionAccount timestamp err :", err) w.WriteHeader(http.StatusInternalServerError) return } updated, _, err = sh.mongoClient.Update(CollectionLink, delfilter, delop, options.Update().SetUpsert(false)) if !updated || err != nil { logger.Error("delacc failed. Update CollectionLink timestamp err :", err) w.WriteHeader(http.StatusInternalServerError) return } logger.Println("delacc success :", linkidMap) } func (sh *serviceDescription) serveHTTP_dev(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/auth") { sh.authorize_dev(w, r) } else { sh.serveHTTP(w, r) } } func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request) { defer func() { s := recover() if s != nil { logger.Error(s) } }() defer func() { io.Copy(io.Discard, r.Body) r.Body.Close() }() if strings.HasSuffix(r.URL.Path, "/auth") { 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, "/emailinfo") { sh.emailinfo(w, r) } else if strings.HasSuffix(r.URL.Path, "/delacc") { sh.delacc(w, r) } else if strings.HasSuffix(r.URL.Path, "/divs") { // TODO : 세션키와 authtoken을 헤더로 받아서 accid 조회 queryvals := r.URL.Query() sk := queryvals.Get("sk") //if len(token) == 0 || len(sk) == 0 { if len(sk) == 0 { w.WriteHeader(http.StatusBadRequest) return } // TODO : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의 // 일단 서버 종류만 내려보내자 // 세션키가 있는지 확인 authInfo, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } if authInfo.Account.IsZero() { logger.Println("sessionkey is not valid :", sk) w.WriteHeader(http.StatusUnauthorized) return } version := queryvals.Get("version") w.Write(sh.findVersionSplit(version)) } else if strings.HasSuffix(r.URL.Path, "/addr") { queryvals := r.URL.Query() sk := queryvals.Get("sk") //if len(token) == 0 || len(sk) == 0 { if len(sk) == 0 { w.WriteHeader(http.StatusBadRequest) return } // TODO : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의 // 일단 서버 종류만 내려보내자 // 세션키가 있는지 확인 authInfo, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } if authInfo.Account.IsZero() { logger.Println("sessionkey is not valid :", sk) w.WriteHeader(http.StatusUnauthorized) return } divname := queryvals.Get("div") divname = strings.Trim(divname, `"`) div, ok := sh.Divisions[divname] if !ok { div = sh.Divisions["default"] } var addrresp []byte if div != nil { logger.Println("/addr :", divname, div.State) switch div.State { case DivisionState_FullOpen: addrresp = div.urlsSerialized case DivisionState_RestrictedOpen: // 점검중이면 whitelist만 입장 가능 authInfo, err := sh.sessionProvider.Query(sk) if err != nil { logger.Println("sessionProvider.Query return err :", err) w.WriteHeader(http.StatusInternalServerError) return } wm := &whitelistmember{Alias: authInfo.Alias, Platform: authInfo.Platform} if _, ok := sh.wl.get(wm.Key()); ok { // qa 권한이면 입장 가능 addrresp = div.urlsSerialized } else if div.Maintenance != nil { // 권한이 없으므로 공지 addrresp = []byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)) } else { logger.Println("div.Maintenance is nil :", divname) } case DivisionState_Maintenance: // 점검중. 아무도 못들어감 if div.Maintenance != nil { logger.Println("/addr :", divname, div.State, *div.Maintenance) addrresp = []byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)) } else { logger.Println("div.Maintenance is nil :", divname) } } logger.Println("/addr resp :", string(addrresp)) w.Write(addrresp) } else { logger.Println("div is not found :", divname, sh.Divisions) logger.Println("check maingate database 'service.divisions' :", config.Mongo) w.WriteHeader(http.StatusBadRequest) } } }