diff --git a/core/maingate.go b/core/maingate.go index 460a6d3..48870f5 100644 --- a/core/maingate.go +++ b/core/maingate.go @@ -22,6 +22,7 @@ import ( "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/flagx" "repositories.action2quare.com/ayo/gocommon/logger" + "repositories.action2quare.com/ayo/gocommon/session" "github.com/golang-jwt/jwt" "go.mongodb.org/mongo-driver/bson" @@ -73,55 +74,10 @@ func SessionTTL() time.Duration { return sessionTTL } -type mongoAuthCell struct { - src *gocommon.Authinfo -} - -func (ac *mongoAuthCell) ToAuthinfo() *gocommon.Authinfo { - if ac.src == nil { - logger.Error("mongoAuthCell ToAuthinfo failed. ac.src is nil") - } - return ac.src -} - -func (ac *mongoAuthCell) ToBytes() []byte { - bt, _ := json.Marshal(ac.src) - return bt -} - -func makeAuthCollection(mongoClient gocommon.MongoClient, sessionTTL time.Duration) *gocommon.AuthCollection { - authcoll := gocommon.MakeAuthCollection(sessionTTL) - authcoll.SessionRemoved = func(sk string) { - skid, _ := primitive.ObjectIDFromHex(sk) - mongoClient.Delete(CollectionAuth, bson.M{ - "sk": skid, - }) - } - authcoll.QuerySession = func(sk string, token string) gocommon.AuthinfoCell { - skid, _ := primitive.ObjectIDFromHex(sk) - var outcell mongoAuthCell - err := mongoClient.FindOneAs(CollectionAuth, bson.M{ - "sk": skid, - }, &outcell.src, options.FindOne().SetHint("skonly")) - - if err != nil { - logger.Error("QuerySession failed :", err) - return nil - } - - if outcell.src == nil { - return nil - } - - return &outcell - } - - return authcoll -} - type maingateConfig struct { Mongo string `json:"maingate_mongodb_url"` SessionTTL int64 `json:"maingate_session_ttl"` + SessionStorage string `json:"maingate_session_storage"` Autologin_ttl int64 `json:"autologin_ttl"` MaximumNumLinkAccount int64 `json:"maximum_num_link_account"` RedirectBaseUrl string `json:"redirect_base_url"` @@ -166,7 +122,7 @@ type Maingate struct { mongoClient gocommon.MongoClient - auths *gocommon.AuthCollection + sessionProvider session.Provider //services servicelist serviceptr unsafe.Pointer admins unsafe.Pointer @@ -387,7 +343,21 @@ func (mg *Maingate) prepare(context context.Context) (err error) { return makeErrorWithStack(err) } - mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second))) + if len(mg.SessionStorage) > 0 { + if strings.HasPrefix(mg.SessionStorage, "mongodb") { + mg.sessionProvider, err = session.NewProviderWithMongo(context, mg.SessionStorage, "maingate", time.Duration(mg.SessionTTL*int64(time.Second))) + } else if strings.HasPrefix(mg.SessionStorage, "redis") { + mg.sessionProvider, err = session.NewProviderWithRedis(context, mg.SessionStorage, time.Duration(mg.SessionTTL*int64(time.Second))) + } else { + err = fmt.Errorf("sessio storage is not valid :%s", mg.SessionStorage) + } + } else { + mg.sessionProvider, err = session.NewProviderWithMongo(context, mg.Mongo, "maingate", time.Duration(mg.SessionTTL*int64(time.Second))) + } + + if err != nil { + return makeErrorWithStack(err) + } var preall []struct { Link string `bson:"link"` @@ -431,7 +401,6 @@ func (mg *Maingate) prepare(context context.Context) (err error) { } mg.bl.init(blocks) - go watchAuthCollection(context, mg.auths, mg.mongoClient) go mg.wl.watchCollection(context, CollectionWhitelist, mg.mongoClient) go mg.bl.watchCollection(context, CollectionBlock, mg.mongoClient) @@ -501,7 +470,6 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu mg.service().serveHTTP(w, r) }) serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "api/"), mg.api) - serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "query/"), mg.query) configraw, _ := json.Marshal(mg.maingateConfig) var convertedConfig map[string]any @@ -582,54 +550,6 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu return nil } -func (mg *Maingate) query(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() - }() - - queryvals := r.URL.Query() - sk := queryvals.Get("sk") - - if len(sk) == 0 { - w.WriteHeader(http.StatusUnauthorized) - return - } - - info := mg.auths.Find(sk) - if info == nil { - logger.Println("session key is not valid :", sk) - w.WriteHeader(http.StatusUnauthorized) - return - } - - if !*devflag { - apitoken := r.Header.Get("MG-X-API-TOKEN") - if len(apitoken) == 0 { - logger.Println("MG-X-API-TOKEN is missing") - w.WriteHeader(http.StatusBadRequest) - return - } - - apitokenObj, _ := primitive.ObjectIDFromHex(apitoken) - if !mg.service().isValidToken(apitokenObj) { - logger.Println("MG-X-API-TOKEN is invalid :", apitoken) - w.WriteHeader(http.StatusBadRequest) - return - } - } - - bt, _ := json.Marshal(info) - w.Write(bt) -} - func (mg *Maingate) GeneratePlatformLoginNonceKey() string { const allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, 52) diff --git a/core/service.go b/core/service.go index e13c8bd..608141f 100644 --- a/core/service.go +++ b/core/service.go @@ -14,6 +14,7 @@ import ( "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" @@ -111,11 +112,11 @@ type serviceDescription struct { MaximumNumLinkAccount int64 VersionSplits map[string]string `bson:"version_splits" json:"version_splits"` - auths *gocommon.AuthCollection - wl memberContainerPtr[string, *whitelistmember] - bl memberContainerPtr[primitive.ObjectID, *blockinfo] - mongoClient gocommon.MongoClient - sessionTTL time.Duration + sessionProvider session.Provider + wl memberContainerPtr[string, *whitelistmember] + bl memberContainerPtr[primitive.ObjectID, *blockinfo] + mongoClient gocommon.MongoClient + sessionTTL time.Duration serviceCodeBytes []byte getUserBrowserInfo func(r *http.Request) (string, error) @@ -251,7 +252,7 @@ func (sh *serviceDescription) prepare(mg *Maingate) error { sh.MaximumNumLinkAccount = mg.maingateConfig.MaximumNumLinkAccount sh.mongoClient = mg.mongoClient - sh.auths = mg.auths + sh.sessionProvider = mg.sessionProvider sh.sessionTTL = time.Duration(mg.SessionTTL * int64(time.Second)) sh.serviceCodeBytes, _ = hex.DecodeString(sh.ServiceCode) sh.getUserBrowserInfo = mg.GetUserBrowserInfo @@ -291,11 +292,10 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) { newType := queryvals.Get("ntype") newId := queryvals.Get("nid") - oldAuth := sh.auths.Find(sk) - if oldAuth == nil { - // 잘못된 세션 - logger.Println("link failed. session key is not valid :", sk) - w.WriteHeader(http.StatusBadRequest) + oldAuth, err := sh.sessionProvider.Query(sk) + if err != nil { + logger.Println("sessionProvider.Query return err :", err) + w.WriteHeader(http.StatusInternalServerError) return } @@ -370,7 +370,7 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) { "_id": link["_id"].(primitive.ObjectID), }, bson.M{ "$setOnInsert": bson.M{ - "accid": oldAuth.Accid, + "accid": oldAuth.Account, "create": createtime, }, }, options.Update().SetUpsert(true)) @@ -410,11 +410,10 @@ func (sh *serviceDescription) unlink(w http.ResponseWriter, r *http.Request) { 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) + authInfo, err := sh.sessionProvider.Query(sk) + if err != nil { + logger.Println("sessionProvider.Query return err :", err) + w.WriteHeader(http.StatusInternalServerError) return } @@ -433,7 +432,7 @@ func (sh *serviceDescription) unlink(w http.ResponseWriter, r *http.Request) { } numRecord, err := sh.mongoClient.Collection(CollectionAccount).CountDocuments(context.Background(), bson.M{ - "accid": authInfo.Accid, + "accid": authInfo.Account, }, options.Count().SetLimit(2)) if err != nil { @@ -502,11 +501,10 @@ func (sh *serviceDescription) linkinfo(w http.ResponseWriter, r *http.Request) { 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) + authInfo, err := sh.sessionProvider.Query(sk) + if err != nil { + logger.Println("sessionProvider.Query return err :", err) + w.WriteHeader(http.StatusInternalServerError) return } @@ -526,7 +524,7 @@ func (sh *serviceDescription) linkinfo(w http.ResponseWriter, r *http.Request) { } numRecord, err := sh.mongoClient.Collection(CollectionAccount).CountDocuments(context.Background(), bson.M{ - "accid": authInfo.Accid, + "accid": authInfo.Account, }, options.Count().SetLimit(sh.MaximumNumLinkAccount)) if err != nil { @@ -767,7 +765,14 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request) // TODO : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의 // 일단 서버 종류만 내려보내자 // 세션키가 있는지 확인 - if _, ok := sh.auths.IsValid(sk, ""); !ok { + authInfo, err := sh.sessionProvider.Query(sk) + if err != nil { + logger.Println("sessionProvider.Query return err :", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if authInfo == nil { logger.Println("sessionkey is not valid :", sk) w.WriteHeader(http.StatusUnauthorized) return @@ -788,7 +793,14 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request) // TODO : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의 // 일단 서버 종류만 내려보내자 // 세션키가 있는지 확인 - if _, ok := sh.auths.IsValid(sk, ""); !ok { + authInfo, err := sh.sessionProvider.Query(sk) + if err != nil { + logger.Println("sessionProvider.Query return err :", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if authInfo == nil { logger.Println("sessionkey is not valid :", sk) w.WriteHeader(http.StatusUnauthorized) return @@ -804,13 +816,14 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request) case DivisionState_RestrictedOpen: // 점검중이면 whitelist만 입장 가능 - cell := sh.auths.QuerySession(sk, "") - if cell == nil { - logger.Println("sessionkey is not valid :", sk) - w.WriteHeader(http.StatusBadRequest) + authInfo, err := sh.sessionProvider.Query(sk) + if err != nil { + logger.Println("sessionProvider.Query return err :", err) + w.WriteHeader(http.StatusInternalServerError) return } - wm := &whitelistmember{Email: cell.ToAuthinfo().Email, Platform: cell.ToAuthinfo().Platform} + + wm := &whitelistmember{Email: authInfo.Email, Platform: authInfo.Platform} if sh.wl.contains(wm.Key(), nil) { // qa 권한이면 입장 가능 w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url))) diff --git a/core/watch.go b/core/watch.go index ba190f6..6857dea 100644 --- a/core/watch.go +++ b/core/watch.go @@ -10,7 +10,6 @@ import ( "time" "unsafe" - "repositories.action2quare.com/ayo/gocommon" "repositories.action2quare.com/ayo/gocommon/logger" "go.mongodb.org/mongo-driver/bson" @@ -19,14 +18,6 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) -type authPipelineDocument struct { - OperationType string `bson:"operationType"` - DocumentKey struct { - Id primitive.ObjectID `bson:"_id"` - } `bson:"documentKey"` - Authinfo *gocommon.Authinfo `bson:"fullDocument"` -} - type servicePipelineDocument struct { OperationType string `bson:"operationType"` DocumentKey struct { @@ -222,87 +213,3 @@ func (mg *Maingate) watchServiceCollection(parentctx context.Context, serveMux * } } } - -func watchAuthCollection(parentctx context.Context, ac *gocommon.AuthCollection, mongoClient gocommon.MongoClient) { - defer func() { - s := recover() - if s != nil { - logger.Error(s) - } - }() - - matchStage := bson.D{ - { - Key: "$match", Value: bson.D{ - {Key: "operationType", Value: bson.D{ - {Key: "$in", Value: bson.A{ - "delete", - "insert", - "update", - }}, - }}, - }, - }} - projectStage := bson.D{ - { - Key: "$project", Value: bson.D{ - {Key: "documentKey", Value: 1}, - {Key: "operationType", Value: 1}, - {Key: "fullDocument", Value: 1}, - }, - }, - } - - var stream *mongo.ChangeStream - var err error - var ctx context.Context - - for { - if stream == nil { - stream, err = mongoClient.Watch(CollectionAuth, mongo.Pipeline{matchStage, projectStage}) - if err != nil { - logger.Error("watchAuthCollection watch failed :", err) - time.Sleep(time.Minute) - continue - } - ctx = context.TODO() - } - - changed := stream.TryNext(ctx) - if ctx.Err() != nil { - logger.Error("watchAuthCollection stream.TryNext failed. process should be restarted! :", ctx.Err().Error()) - break - } - - if changed { - var data authPipelineDocument - if err := stream.Decode(&data); err == nil { - ot := data.OperationType - switch ot { - case "insert": - ac.AddRaw(&mongoAuthCell{src: data.Authinfo}) - case "update": - ac.AddRaw(&mongoAuthCell{src: data.Authinfo}) - case "delete": - ac.RemoveByAccId(data.DocumentKey.Id) - } - } else { - logger.Error("watchAuthCollection stream.Decode failed :", err) - } - } else if stream.Err() != nil || stream.ID() == 0 { - select { - case <-ctx.Done(): - logger.Println("watchAuthCollection is done") - stream.Close(ctx) - return - - case <-time.After(time.Second): - logger.Error("watchAuthCollection stream error :", stream.Err()) - stream.Close(ctx) - stream = nil - } - } else { - time.Sleep(time.Second) - } - } -} diff --git a/go.mod b/go.mod index cc5ddb5..c2d4eb3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible go.mongodb.org/mongo-driver v1.11.7 google.golang.org/api v0.128.0 - repositories.action2quare.com/ayo/gocommon v0.0.0-20230825015501-e4527aa5b3ff + repositories.action2quare.com/ayo/gocommon v0.0.0-20230830073522-021f18315726 ) require ( diff --git a/go.sum b/go.sum index ad51ef5..a407328 100644 --- a/go.sum +++ b/go.sum @@ -335,3 +335,7 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230823134414-400c7f644333 h1 repositories.action2quare.com/ayo/gocommon v0.0.0-20230823134414-400c7f644333/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230825015501-e4527aa5b3ff h1:nTOqgPSfm0EANR1SFAi+Zi/KErAAlstVcEWWOnyDT5g= repositories.action2quare.com/ayo/gocommon v0.0.0-20230825015501-e4527aa5b3ff/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230830064326-66a191f4944f h1:LlMO/RB9dHgFrCNmK2LwQA3ZDOyIF5Aw5Y5TVPXZcHw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230830064326-66a191f4944f/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230830073522-021f18315726 h1:HB13+b19K56B5Uih0hWCwCs5x4CvzHxZlq5ARtpe/CE= +repositories.action2quare.com/ayo/gocommon v0.0.0-20230830073522-021f18315726/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=