Compare commits

..

50 Commits

Author SHA1 Message Date
f734ef099b 세션 무효화 적용 2024-02-21 12:23:51 +09:00
c0e1e229cd 화이트리스트 추가 오류 수정 2024-02-14 11:09:19 +09:00
b5114b5224 스팀도 readable한 email 주소 저장 2024-02-05 20:16:32 +09:00
a3b4ac47b8 maintenance 바로 반영 안되는 문제 수정 2024-02-04 15:23:33 +09:00
45e7169c3a commandcenter용 globalApiToken 추가 2024-02-02 12:45:42 +09:00
3444c17026 Merge branch 'master' of https://repositories.action2quare.com/ayo/maingate 2024-01-22 17:17:09 +09:00
98340db8df 모듈 업데이트 2024-01-22 17:17:07 +09:00
e7b3f59dd0 계정 이메일 조회 기능 추가 2024-01-10 07:37:06 +09:00
bc82cb123c Revert "Session에 email도 저장해둔다. 필요할때 꺼내줄 api만들 예정"
This reverts commit 2165a4400b.
2024-01-09 15:31:15 +09:00
2165a4400b Session에 email도 저장해둔다. 필요할때 꺼내줄 api만들 예정 2024-01-09 14:43:23 +09:00
6ca3905fed 차단 처리 2023-12-28 17:38:20 +09:00
d5708a964f block 테스트 함수 추가 2023-12-28 16:55:38 +09:00
bb9b3a9735 default division 생성 수정 2023-12-26 16:38:11 +09:00
da68071e97 dev용 serverHTTP 추가 2023-12-25 22:08:22 +09:00
2e60fac840 mg.config를 config로 변경 2023-12-06 16:35:55 +09:00
dab5a35870 모듈 업데이트 2023-11-30 14:49:01 +09:00
a35512e327 모듈 업데이트 2023-11-30 14:17:48 +09:00
8a8bd50e28 계정 제재 개선 2023-11-29 17:36:25 +09:00
db90ce931f 차단된 유저 못 가져오는 문제 수정 2023-11-29 09:30:22 +09:00
7639c749dc 모듈 업데이트 2023-11-28 22:35:01 +09:00
63461676f4 모듈 업데이트 2023-11-28 00:57:56 +09:00
eebd3fb746 모듈 업데이트 2023-11-25 22:22:33 +09:00
ba4b4eea94 로그 수정 2023-11-24 00:18:04 +09:00
e583904693 모듈 업데이트 2023-11-16 19:58:24 +09:00
a2def0af79 fba, template 폴더를 package 에 추가 2023-10-24 14:04:54 +09:00
6ccf76d1b2 Firebase-Google Analaytics Desktop 버전 연동을 위해서 JavaScript SDK( JS-SDk ) 관련 코드 추가 2023-10-23 14:28:29 +09:00
bc58249483 noauth가 아닐때 type 없으면 로그인 실패 2023-10-20 11:27:41 +09:00
95a7972835 로그 제거 2023-10-18 15:32:36 +09:00
e37aaff9cb 로그 변경 2023-10-18 15:28:14 +09:00
77cffbbe9a 로그 추가 2023-10-18 15:15:35 +09:00
d623196c10 모듈 업데이트 2023-10-12 12:05:20 +09:00
06e40853ad 화이트리스트 추가,삭제 반영 안되는 문제 수정 2023-10-05 11:11:20 +09:00
d873965d37 현재 block된 정보를 조회 2023-09-25 12:29:26 +09:00
275b9b12e3 모듈 업데이트 2023-09-19 18:52:33 +09:00
81689f7512 코드 정리 2023-09-19 18:50:45 +09:00
e3ad826826 모듈 업데이트 2023-09-11 12:48:39 +09:00
9e98b581e4 모듈 업데이트 2023-09-08 15:27:25 +09:00
41641b88e9 모듈 업데이트 2023-09-08 11:36:58 +09:00
a4d297a944 모듈 업데이트 2023-09-06 18:02:16 +09:00
418713b0c7 version split 수정 2023-09-05 17:15:15 +09:00
087743453c 모듈 업데이트 2023-09-04 14:37:58 +09:00
caed2b5925 세션 touch 리퀘스트 처리 2023-09-04 12:17:34 +09:00
e18dc74dc2 version split 수정 2023-09-04 11:15:05 +09:00
9afa1d87e7 모듈 업데이트 2023-09-04 10:24:35 +09:00
3cf9466cdb 모듈 업데이트 2023-09-01 10:49:07 +09:00
6c73e9990e 모듈 업데이트 2023-08-31 21:16:41 +09:00
e8aa6189be config로 provider 생성 2023-08-31 21:13:11 +09:00
47284a79c2 session provider생성 최신화 2023-08-31 20:44:21 +09:00
0121310941 deprecated 함수 제거 2023-08-31 17:24:21 +09:00
76a4818a66 session.provider로 교체 2023-08-30 17:04:00 +09:00
17 changed files with 457 additions and 858 deletions

View File

@ -1,7 +1,9 @@
{
"maingate_mongodb_url": "mongodb://...",
"session_storage": "",
"session_ttl" : 3600,
"autologin_ttl": 604800,
"acc_del_ttl": 7776000,
"maximum_num_link_account": 10,
"redirect_base_url": "",
"google_client_id" : "",

View File

@ -160,9 +160,12 @@ func (caller apiCaller) uploadAPI(w http.ResponseWriter, r *http.Request) error
func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error {
mg := caller.mg
logger.Println("blockAPI :", r.Method)
if r.Method == "GET" {
target, ok := gocommon.ReadObjectIDFormValue(r.Form, "accid")
if ok {
logger.Println("Get :", target, ok)
if !ok {
// 페이지네이션 해야할 듯
json.NewEncoder(w).Encode(mg.bl.all())
} else if !target.IsZero() {
if blocked, ok := mg.bl.get(target); ok && blocked != nil {
@ -170,34 +173,36 @@ func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error {
}
}
} else if r.Method == "PUT" {
body, _ := io.ReadAll(r.Body)
var bipl blockinfoWithStringId
if err := json.Unmarshal(body, &bipl); err != nil {
var targets struct {
Start primitive.DateTime
End primitive.DateTime
Accounts map[primitive.ObjectID]primitive.M // accid->meta
}
if err := gocommon.MakeDecoder(r).Decode(&targets); err != nil {
return err
}
accid, err := primitive.ObjectIDFromHex(bipl.StrId)
if err != nil {
return err
}
for accid, meta := range targets.Accounts {
bi := blockinfo{
Start: targets.Start,
End: targets.End,
Meta: meta,
}
bi := blockinfo{
Start: primitive.NewDateTimeFromTime(time.Unix(bipl.StartUnix, 0)),
End: primitive.NewDateTimeFromTime(time.Unix(bipl.EndUnix, 0)),
Reason: bipl.Reason,
}
_, _, err := mg.mongoClient.Update(CollectionBlock, bson.M{
"_id": accid,
}, bson.M{
"$set": &bi,
}, options.Update().SetUpsert(true))
if err != nil {
logger.Println("account is not blocked. err :", err)
} else {
logger.Println("account is blocked :", meta)
logger.Println("bi :", accid, bi)
_, _, err = mg.mongoClient.Update(CollectionBlock, bson.M{
"_id": accid,
}, bson.M{
"$set": &bi,
}, options.Update().SetUpsert(true))
if err != nil {
return err
bi.Accid = accid
caller.mg.bl.add(&bi)
mg.sessionProvider.Invalidate(accid)
}
}
} else if r.Method == "DELETE" {
id := r.URL.Query().Get("id")
@ -222,7 +227,7 @@ func (caller apiCaller) blockAPI(w http.ResponseWriter, r *http.Request) error {
return err
}
mg.mongoClient.Delete(CollectionAuth, bson.M{"_id": idobj})
caller.mg.bl.remove(idobj)
}
return nil
}
@ -239,9 +244,9 @@ func (caller apiCaller) whitelistAPI(w http.ResponseWriter, r *http.Request) err
return err
}
member.ExpiredAt = 0
member.Id = primitive.NilObjectID
member.Id = primitive.NewObjectID()
_, _, err := mg.mongoClient.Update(CollectionWhitelist, bson.M{
"_id": primitive.NewObjectID(),
"_id": member.Id,
}, bson.M{
"$set": &member,
}, options.Update().SetUpsert(true))
@ -403,40 +408,6 @@ func (caller apiCaller) configAPI(w http.ResponseWriter, r *http.Request) error
return nil
}
func (caller apiCaller) lockcreatecharAPI(w http.ResponseWriter, r *http.Request) error {
mg, err := caller.mg.mongoClient.FindAll(CollectionService, bson.M{})
if err != nil {
return err
}
haschr, _ := gocommon.ReadStringFormValue(r.Form, "haschr")
locked := make(map[string]any)
if haschr == "true" {
locked["lock"] = false
} else {
curregion, _ := gocommon.ReadStringFormValue(r.Form, "region")
for _, regioninfo := range mg {
region := regioninfo["divisions"].(primitive.M)
for idx, rl := range region {
if idx == curregion {
if rl.(primitive.M)["lockcreatechar"].(bool) {
locked["lock"] = true
} else {
locked["lock"] = false
}
}
}
}
}
create, _ := json.Marshal(locked)
w.Write(create)
return nil
}
type apiCaller struct {
userinfo map[string]any
globalAdmins map[string]bool
@ -543,8 +514,6 @@ func (mg *Maingate) api(w http.ResponseWriter, r *http.Request) {
err = caller.blockAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/coupon") {
err = caller.couponAPI(w, r)
} else if strings.HasSuffix(r.URL.Path, "/lockcreatechar") {
err = caller.lockcreatecharAPI(w, r)
}
if err != nil {

View File

@ -44,17 +44,17 @@ func makeCouponKey(roundnum uint32, uid []byte) string {
return fmt.Sprintf("%s-%s-%s-%s", hex.EncodeToString(final[0:2]), hex.EncodeToString(final[2:4]), hex.EncodeToString(final[4:6]), hex.EncodeToString(final[6:8]))
}
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
func makeCouponCodes(name string, count int) (string, map[string]string) {
checkunique := make(map[string]bool)
keys := make(map[string]string)
uid := make([]byte, 4)
roundHash, roundnum := coupon.MakeCouponRoundHash(name)
seed := time.Now().UnixNano()
for len(keys) < count {
rand.Seed(seed)
rand.Read(uid)
r.Read(uid)
code := makeCouponKey(roundnum, uid)
@ -62,7 +62,6 @@ func makeCouponCodes(name string, count int) (string, map[string]string) {
checkunique[code] = true
keys[hex.EncodeToString(uid)] = code
}
seed = int64(binary.BigEndian.Uint32(uid))
}
return roundHash, keys
}

View File

@ -1,39 +1,9 @@
package core
import (
"context"
"fmt"
"testing"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
"repositories.action2quare.com/ayo/gocommon"
)
func TestMakeLocalUniqueId(t *testing.T) {
ts := int64(1690815600)
start := primitive.NewDateTimeFromTime(time.Unix(ts, 0))
ts = int64(1693493999)
end := primitive.NewDateTimeFromTime(time.Unix(ts, 0))
fmt.Println(start.Time().Format(time.RFC3339))
fmt.Println(end.Time().Format(time.RFC3339))
mongoClient, err := gocommon.NewMongoClient(context.Background(), "mongodb://121.134.91.160:27018/mountain-maingate?replicaSet=rs0&retrywrites=true", "maingate")
if err != nil {
t.Error(err)
}
bi := blockinfo{
Start: start,
End: end,
Reason: "test",
}
mongoClient.Update(CollectionBlock, bson.M{
"_id": primitive.NewObjectID(),
}, bson.M{
"$set": &bi,
}, options.Update().SetUpsert(true))
}

View File

@ -9,11 +9,9 @@ import (
"fmt"
"io"
"math/big"
"math/rand"
"net"
"net/http"
"os"
"runtime/debug"
"strings"
"sync/atomic"
"text/template"
@ -23,6 +21,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"
@ -40,7 +39,6 @@ var noauth = flagx.Bool("noauth", false, "")
var (
CollectionLink = gocommon.CollectionName("link")
CollectionAuth = gocommon.CollectionName("auth")
CollectionWhitelist = gocommon.CollectionName("whitelist")
CollectionService = gocommon.CollectionName("service")
CollectionAccount = gocommon.CollectionName("account")
@ -74,57 +72,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 {
session.SessionConfig `json:",inline"`
Mongo string `json:"maingate_mongodb_url"`
SessionTTL int64 `json:"maingate_session_ttl"`
Autologin_ttl int64 `json:"autologin_ttl"`
AccDelTTL int64 `json:"acc_del_ttl"`
MaximumNumLinkAccount int64 `json:"maximum_num_link_account"`
RedirectBaseUrl string `json:"redirect_base_url"`
GoogleClientId string `json:"google_client_id"`
@ -145,6 +96,7 @@ type maingateConfig struct {
FirebaseAdminSDKCredentialFile string `json:"firebase_admin_sdk_credentialfile"`
SteamAppId string `json:"steam_app_id"`
SteamPublisherAuthKey string `json:"steam_publisher_authkey"`
GlobalMaingateToken string `json:"maingate_api_token"`
Firebase_Google_Analytics_JS_SDK_Config
}
@ -176,11 +128,9 @@ func (ga *globalAdmins) parse() {
// Maingate :
type Maingate struct {
maingateConfig
mongoClient gocommon.MongoClient
auths *gocommon.AuthCollection
sessionProvider session.Provider
//services servicelist
serviceptr unsafe.Pointer
admins unsafe.Pointer
@ -195,9 +145,10 @@ type Maingate struct {
firebaseAppContext context.Context
}
var config maingateConfig
// New :
func New(ctx context.Context) (*Maingate, error) {
var config maingateConfig
if err := gocommon.LoadConfig(&config); err != nil {
return nil, err
}
@ -207,12 +158,15 @@ func New(ctx context.Context) (*Maingate, error) {
admins.parse()
}
if len(config.SessionStorage) == 0 {
return nil, errors.New("maingate_session_storage is missing")
}
if config.SessionTTL == 0 {
config.SessionTTL = 3600
}
mg := Maingate{
maingateConfig: config,
admins: unsafe.Pointer(&admins),
tokenEndpoints: make(map[string]string),
authorizationEndpoints: make(map[string]string),
@ -226,7 +180,7 @@ func New(ctx context.Context) (*Maingate, error) {
}
if !*noauth {
opt := option.WithCredentialsFile(mg.FirebaseAdminSDKCredentialFile)
opt := option.WithCredentialsFile(config.FirebaseAdminSDKCredentialFile)
firebaseApp, err := firebase.NewApp(context.Background(), nil, opt)
if err != nil {
logger.Error("firebase admin error initializing app failed :", err)
@ -301,110 +255,96 @@ func (mg *Maingate) discoverOpenIdConfiguration(name string, url string) error {
}
func makeErrorWithStack(err error) error {
return fmt.Errorf("%s\n%s", err.Error(), string(debug.Stack()))
}
func (mg *Maingate) prepare(context context.Context) (err error) {
if err := mg.discoverOpenIdConfiguration(AuthPlatformMicrosoft, "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err := mg.discoverOpenIdConfiguration("google", "https://accounts.google.com/.well-known/openid-configuration"); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
// redis에서 env를 가져온 후에
mg.mongoClient, err = gocommon.NewMongoClient(context, mg.Mongo, "maingate")
mg.mongoClient, err = gocommon.NewMongoClient(context, config.Mongo)
if err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionCouponUse, map[string]bson.D{
"idrounds": {{Key: "_id", Value: 1}, {Key: "rounds", Value: 1}},
}); err != nil {
return err
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionAuth, map[string]bson.D{
"skonly": {{Key: "sk", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
"platformuid": {{Key: "platform", Value: 1}, {Key: "uid", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionLink, map[string]bson.D{
"emailplatform": {{Key: "email", Value: 1}, {Key: "platform", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeIndices(CollectionAccount, map[string]bson.D{
"accid": {{Key: "accid", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionFile, map[string]bson.D{
"keyonly": {{Key: "key", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
}
if err = mg.mongoClient.MakeExpireIndex(CollectionAccount, int32(mg.AccDelTTL)); err != nil {
return makeErrorWithStack(err)
}
if err = mg.mongoClient.MakeExpireIndex(CollectionLink, int32(mg.AccDelTTL)); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
// Delete대신 _ts로 expire시킴. pipeline에 삭제 알려주기 위함
if err = mg.mongoClient.MakeExpireIndex(CollectionWhitelist, 10); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeExpireIndex(CollectionAuth, int32(mg.SessionTTL+300)); err != nil {
return makeErrorWithStack(err)
if *devflag {
// 에러 체크하지 말것
mg.mongoClient.DropIndex(CollectionBlock, "codeaccid")
}
if err = mg.mongoClient.MakeExpireIndex(CollectionBlock, int32(3)); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionPlatformLoginToken, map[string]bson.D{
"platformauthtoken": {{Key: "platform", Value: 1}, {Key: "key", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeExpireIndex(CollectionPlatformLoginToken, int32(mg.SessionTTL+300)); err != nil {
return makeErrorWithStack(err)
if err = mg.mongoClient.MakeExpireIndex(CollectionPlatformLoginToken, int32(config.SessionTTL+300)); err != nil {
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionUserToken, map[string]bson.D{
"platformusertoken": {{Key: "platform", Value: 1}, {Key: "userid", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionGamepotUserInfo, map[string]bson.D{
"gamepotuserid": {{Key: "gamepotuserid", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
if err = mg.mongoClient.MakeUniqueIndices(CollectionFirebaseUserInfo, map[string]bson.D{
"firebaseuserid": {{Key: "firebaseuserid", Value: 1}},
}); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
mg.auths = makeAuthCollection(mg.mongoClient, time.Duration(mg.SessionTTL*int64(time.Second)))
mg.sessionProvider, err = session.NewProviderWithConfig(context, config.SessionConfig)
if err != nil {
return logger.ErrorWithCallStack(err)
}
var preall []struct {
Link string `bson:"link"`
@ -413,7 +353,7 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
if err = mg.mongoClient.FindAllAs(CollectionFile, nil, &preall, options.Find().SetProjection(bson.M{
"link": 1,
})); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
for _, pre := range preall {
@ -428,37 +368,40 @@ func (mg *Maingate) prepare(context context.Context) (err error) {
"_id": pre.Id,
}, &fulldoc)
if err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
err = fulldoc.Save()
if err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
}
var whites []*whitelistmember
if err := mg.mongoClient.AllAs(CollectionWhitelist, &whites, options.Find().SetReturnKey(false)); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
mg.wl.init(whites)
var blocks []*blockinfo
if err := mg.mongoClient.AllAs(CollectionBlock, &blocks); err != nil {
return makeErrorWithStack(err)
return logger.ErrorWithCallStack(err)
}
logger.Println("allblocks :", blocks)
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)
return nil
}
var portptr = flagx.Int("port", 80, "")
func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMux, prefix string) error {
var allServices []*serviceDescription
if err := mg.mongoClient.AllAs(CollectionService, &allServices, options.Find().SetReturnKey(false)); err != nil {
return err
return logger.ErrorWithCallStack(err)
}
if len(allServices) > 0 {
@ -474,10 +417,9 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
}
if *devflag {
host, _ := os.Hostname()
addrs, err := net.InterfaceAddrs()
if err != nil {
return err
return logger.ErrorWithCallStack(err)
}
ipaddr := "127.0.0.1"
for _, addr := range addrs {
@ -489,14 +431,13 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
}
empty.Divisions = map[string]*Division{
host: {
"default": {
DivisionForUser: DivisionForUser{
Priority: 0,
State: DivisionState_FullOpen,
LockCreateChar: false,
Priority: 0,
State: DivisionState_FullOpen,
},
Url: fmt.Sprintf("http://%s/warehouse", ipaddr),
Url: fmt.Sprintf("http://%s:%d/warehouse", ipaddr, *portptr),
},
}
}
@ -510,21 +451,28 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
}, options.Update().SetUpsert(true))
if err != nil {
return err
return logger.ErrorWithCallStack(err)
}
}
logger.Println("Service is registered :", mg.service().ServiceCode)
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, mg.service().ServiceCode, "/"), func(w http.ResponseWriter, r *http.Request) {
mg.service().serveHTTP(w, r)
})
if *devflag {
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, mg.service().ServiceCode, "/"), func(w http.ResponseWriter, r *http.Request) {
// mg.service()를 요청마다 불러야 함
mg.service().serveHTTP_dev(w, r)
})
} else {
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, mg.service().ServiceCode, "/"), func(w http.ResponseWriter, r *http.Request) {
// mg.service()를 요청마다 불러야 함
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)
configraw, _ := json.Marshal(config)
var convertedConfig map[string]any
if err := json.Unmarshal(configraw, &convertedConfig); err != nil {
return err
return logger.ErrorWithCallStack(err)
}
serveMux.HandleFunc(gocommon.MakeHttpHandlerPattern(prefix, "config"), func(w http.ResponseWriter, r *http.Request) {
@ -557,7 +505,7 @@ func (mg *Maingate) RegisterHandlers(ctx context.Context, serveMux *http.ServeMu
if err := os.MkdirAll("static", os.ModePerm); err != nil {
// 일반 엔드유저한테 오픈할 static 페이지
return err
return logger.ErrorWithCallStack(err)
}
cfsx := http.FileServer(http.Dir("console"))
@ -608,59 +556,11 @@ 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)
for i := range b {
b[i] = allowed[rand.Intn(len(allowed))]
b[i] = allowed[r.Intn(len(allowed))]
}
return string(b)
}
@ -674,7 +574,7 @@ func (mg *Maingate) GetUserBrowserInfo(r *http.Request) (string, error) {
cookie, err := r.Cookie("ActionSquareSessionExtraInfo")
if err != nil {
return "", err
return "", logger.ErrorWithCallStack(err)
}
//requestinfo := fmt.Sprintf("%s_%s", cookie.Value, host) //-- RemoteAddr체크는 로드밸런서 IP 찍히는 문제 때문에 제외한다.
@ -696,7 +596,7 @@ func (mg *Maingate) setUserToken(info usertokeninfo) error {
"accesstoken_expire_time": info.accesstoken_expire_time,
},
}, options.Update().SetUpsert(true))
return err
return logger.ErrorWithCallStack(err)
}
func (mg *Maingate) getUserTokenWithCheck(platform string, userid string, brinfo string) (usertokeninfo, error) {
@ -721,7 +621,7 @@ func (mg *Maingate) getUserTokenWithCheck(platform string, userid string, brinfo
updatetime, ok := found["lastupdate"].(int64)
if !ok || time.Now().Unix()-updatetime < mg.maingateConfig.Autologin_ttl {
if !ok || time.Now().Unix()-updatetime < config.Autologin_ttl {
info.platform = platform
info.userid = userid
info.brinfo = brinfo
@ -810,18 +710,13 @@ func (mg *Maingate) getProviderInfo(platform string, uid string) (string, string
if provider == "" || providerid == "" {
return "", "", errors.New("getProviderInfo - firebase info not found: " + provider + " / " + providerid)
}
case "":
//guest auth
providerid = uid
if providerid == "" {
return "", "", errors.New("getProviderInfo - guest provider id not found: " + provider + " / " + providerid)
}
default:
provider = platform
providerid = uid
if provider == "" || providerid == "" {
return "", "", errors.New("getProviderInfo - provider info not found: " + provider + " / " + providerid)
}
}
if provider == "" || providerid == "" {
return "", "", errors.New("getProviderInfo - provider info not found: " + provider + " / " + providerid)
}
return provider, providerid, nil
@ -985,25 +880,16 @@ func JWTparseCode(keyurl string, code string) (string, string, string) {
return claims["sub"].(string), email, nonce
}
func (mg *Maingate) google_analytics_html(w http.ResponseWriter, r *http.Request) {
parsedTemplate, _ := template.ParseFiles("template/track-event.html")
err := parsedTemplate.Execute(w, nil)
if err != nil {
logger.Error("Error executing template :", err)
return
}
}
func (mg *Maingate) google_analytics_js(w http.ResponseWriter, r *http.Request) {
fgaconfig := Firebase_Google_Analytics_JS_SDK_Config{
FGA_apiKey: mg.FGA_apiKey,
FGA_authDomain: mg.FGA_authDomain,
FGA_databaseURL: mg.FGA_databaseURL,
FGA_projectId: mg.FGA_projectId,
FGA_storageBucket: mg.FGA_storageBucket,
FGA_messagingSenderId: mg.FGA_messagingSenderId,
FGA_appId: mg.FGA_appId,
FGA_measurementId: mg.FGA_measurementId,
FGA_apiKey: config.FGA_apiKey,
FGA_authDomain: config.FGA_authDomain,
FGA_databaseURL: config.FGA_databaseURL,
FGA_projectId: config.FGA_projectId,
FGA_storageBucket: config.FGA_storageBucket,
FGA_messagingSenderId: config.FGA_messagingSenderId,
FGA_appId: config.FGA_appId,
FGA_measurementId: config.FGA_measurementId,
}
parsedTemplate, _ := template.ParseFiles("template/fb-ga.min.js")
err := parsedTemplate.Execute(w, fgaconfig)

View File

@ -84,24 +84,6 @@ func (p *memberContainerPtr[K, T]) all() []T {
return out
}
func (p *memberContainerPtr[K, T]) contains(key K, out *T) bool {
ptr := atomic.LoadPointer(&p.ptr)
src := (*map[K]T)(ptr)
found, exists := (*src)[key]
if exists {
if found.Expired() {
p.remove(key)
return false
}
if out != nil {
*out = found
}
return true
}
return false
}
func (p *memberContainerPtr[K, T]) watchCollection(parentctx context.Context, coll gocommon.CollectionName, mc gocommon.MongoClient) {
defer func() {
s := recover()

View File

@ -95,8 +95,8 @@ func (mg *Maingate) platform_apple_get_login_url(w http.ResponseWriter, r *http.
}
params := url.Values{}
params.Add("client_id", mg.AppleCientId)
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformApple)
params.Add("client_id", config.AppleCientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformApple)
params.Add("response_type", "code id_token")
params.Add("scope", "name email")
@ -146,7 +146,7 @@ func (mg *Maingate) platform_apple_authorize(w http.ResponseWriter, r *http.Requ
}
http.SetCookie(w, &cookie)
http.Redirect(w, r, mg.RedirectBaseUrl+"/authorize_result/"+AuthPlatformApple, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
http.Redirect(w, r, config.RedirectBaseUrl+"/authorize_result/"+AuthPlatformApple, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
}
func (mg *Maingate) platform_apple_authorize_result(w http.ResponseWriter, r *http.Request) {
@ -208,17 +208,17 @@ func (mg *Maingate) platform_apple_authorize_result(w http.ResponseWriter, r *ht
}
// Generate the client secret used to authenticate with Apple's validation servers
secret, err := generateClientSecret(mg.ApplePrivateKey, mg.AppleTeamId, mg.AppleServiceId, mg.AppleKeyId)
secret, err := generateClientSecret(config.ApplePrivateKey, config.AppleTeamId, config.AppleServiceId, config.AppleKeyId)
if err != nil {
logger.Error("error generating secret: ", err)
return
}
vReq := Apple_WebValidationTokenRequest{
ClientID: mg.AppleServiceId,
ClientID: config.AppleServiceId,
ClientSecret: secret,
Code: code,
RedirectURI: mg.RedirectBaseUrl + "/authorize/" + AuthPlatformApple, // This URL must be validated with apple in your service
RedirectURI: config.RedirectBaseUrl + "/authorize/" + AuthPlatformApple, // This URL must be validated with apple in your service
}
var resp Apple_ValidationResponse
@ -268,14 +268,14 @@ func (mg *Maingate) platform_apple_authorize_result(w http.ResponseWriter, r *ht
func (mg *Maingate) platform_apple_getuserinfo(refreshToken string) (bool, string, string) {
//=================================RefreshToken을 사용해서 정보 가져 온다. 이미 인증된 사용자의 업데이트 목적
secret, err := generateClientSecret(mg.ApplePrivateKey, mg.AppleTeamId, mg.AppleServiceId, mg.AppleKeyId)
secret, err := generateClientSecret(config.ApplePrivateKey, config.AppleTeamId, config.AppleServiceId, config.AppleKeyId)
if err != nil {
logger.Error("error generating secret: ", err)
return false, "", ""
}
vReqRefreshToken := Apple_WebRefreshTokenRequest{
ClientID: mg.AppleServiceId,
ClientID: config.AppleServiceId,
ClientSecret: secret,
RefreshToken: refreshToken,
}

View File

@ -84,9 +84,9 @@ func (mg *Maingate) platform_google_get_login_url(w http.ResponseWriter, r *http
}
params := url.Values{}
params.Add("client_id", mg.GoogleClientId)
params.Add("client_id", config.GoogleClientId)
params.Add("response_type", "code")
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformGoogle)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformGoogle)
params.Add("scope", "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email")
params.Add("access_type", "offline")
params.Add("prompt", "consent")
@ -140,7 +140,7 @@ func (mg *Maingate) platform_google_authorize(w http.ResponseWriter, r *http.Req
}
http.SetCookie(w, &cookie2)
http.Redirect(w, r, mg.RedirectBaseUrl+"/authorize_result/"+AuthPlatformGoogle, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
http.Redirect(w, r, config.RedirectBaseUrl+"/authorize_result/"+AuthPlatformGoogle, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
}
func (mg *Maingate) platform_google_authorize_result(w http.ResponseWriter, r *http.Request) {
@ -211,9 +211,9 @@ func (mg *Maingate) platform_google_authorize_result(w http.ResponseWriter, r *h
//=================
params := url.Values{}
params.Add("client_id", mg.GoogleClientId)
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformGoogle)
params.Add("client_secret", mg.GoogleClientSecret)
params.Add("client_id", config.GoogleClientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformGoogle)
params.Add("client_secret", config.GoogleClientSecret)
params.Add("code", code)
params.Add("grant_type", "authorization_code")
@ -285,9 +285,9 @@ func (mg *Maingate) platform_google_getuserinfo(info usertokeninfo) (bool, strin
if time.Now().Unix() > info.accesstoken_expire_time {
params := url.Values{}
params.Add("client_id", mg.GoogleClientId)
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformGoogle)
params.Add("client_secret", mg.GoogleClientSecret)
params.Add("client_id", config.GoogleClientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformGoogle)
params.Add("client_secret", config.GoogleClientSecret)
params.Add("scope", "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email")
params.Add("refresh_token", info.token)
params.Add("grant_type", "refresh_token")

View File

@ -83,9 +83,9 @@ func (mg *Maingate) platform_microsoft_get_login_url(w http.ResponseWriter, r *h
}
params := url.Values{}
params.Add("client_id", mg.MicrosoftClientId)
params.Add("client_id", config.MicrosoftClientId)
params.Add("response_type", "code")
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("response_mode", "query")
params.Add("scope", "openid offline_access https://graph.microsoft.com/mail.read")
@ -127,7 +127,7 @@ func (mg *Maingate) platform_microsoft_authorize(w http.ResponseWriter, r *http.
}
http.SetCookie(w, &cookie)
http.Redirect(w, r, mg.RedirectBaseUrl+"/authorize_result/"+AuthPlatformMicrosoft, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
http.Redirect(w, r, config.RedirectBaseUrl+"/authorize_result/"+AuthPlatformMicrosoft, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
}
func (mg *Maingate) platform_microsoft_authorize_result(w http.ResponseWriter, r *http.Request) {
@ -191,13 +191,13 @@ func (mg *Maingate) platform_microsoft_authorize_result(w http.ResponseWriter, r
//=================
params := url.Values{}
params.Add("client_id", mg.MicrosoftClientId)
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("client_id", config.MicrosoftClientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("code", code)
params.Add("scope", "openid offline_access https://graph.microsoft.com/mail.read")
params.Add("grant_type", "authorization_code")
params.Add("client_secret", mg.MicrosoftClientSecret)
params.Add("client_secret", config.MicrosoftClientSecret)
var respReferesh Microsoft_ValidationResponse
acceestoken_expire_time := time.Now().Unix()
@ -263,13 +263,13 @@ func (mg *Maingate) platform_microsoft_getuserinfo(info usertokeninfo) (bool, st
if time.Now().Unix() > info.accesstoken_expire_time {
params := url.Values{}
params.Add("client_id", mg.MicrosoftClientId)
params.Add("redirect_uri", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("client_id", config.MicrosoftClientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("refresh_token", info.token)
params.Add("scope", "openid offline_access https://graph.microsoft.com/mail.read")
params.Add("grant_type", "refresh_token")
params.Add("client_secret", mg.MicrosoftClientSecret)
params.Add("client_secret", config.MicrosoftClientSecret)
var respReferesh Microsoft_ValidationResponse
acceestoken_expire_time := time.Now().Unix()

View File

@ -40,7 +40,7 @@ func (mg *Maingate) platform_steamsdk_authorize(w http.ResponseWriter, r *http.R
}
if !*noauth {
err = authenticateSteamUser(mg.SteamPublisherAuthKey, mg.SteamAppId, authinfo.UserSteamId, authinfo.UserAuthToken)
err = authenticateSteamUser(config.SteamPublisherAuthKey, config.SteamAppId, authinfo.UserSteamId, authinfo.UserAuthToken)
}
if err == nil {
@ -118,9 +118,8 @@ func authenticateSteamUser(pubkey, appid, playerid, ticket string) error {
}
func (mg *Maingate) platform_steamsdk_getuserinfo(info usertokeninfo) (bool, string, string) {
// Steam은 이메일 정보를 받을수 없기 때문에 dummy임시 주소 할당하여 리턴한다.
dummyEmail := fmt.Sprintf("__dummy_%s@steamtemp__", info.userid)
// Steam은 이메일 정보를 받을수 없기 때문에 userid로 리턴한다.
dummyEmail := fmt.Sprintf("%s@steam.id", info.userid)
return true, info.userid, dummyEmail
}

View File

@ -123,7 +123,7 @@ func (mg *Maingate) platform_twitter_authorize(w http.ResponseWriter, r *http.Re
}
http.SetCookie(w, &cookie)
http.Redirect(w, r, mg.RedirectBaseUrl+"/authorize_result/"+AuthPlatformTwitter, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
http.Redirect(w, r, config.RedirectBaseUrl+"/authorize_result/"+AuthPlatformTwitter, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
}
func (mg *Maingate) platform_twitter_authorize_result(w http.ResponseWriter, r *http.Request) {
@ -249,7 +249,7 @@ func (mg *Maingate) platform_twitter_getuserinfo(token, secret string) (bool, st
}
func (mg *Maingate) CallTwitterAPI_WithAPPKey(requesturl, method, nonce string) string {
return mg.CallTwitterAPI(requesturl, method, mg.TwitterOAuthKey, mg.TwitterOAuthSecret, nonce)
return mg.CallTwitterAPI(requesturl, method, config.TwitterOAuthKey, config.TwitterOAuthSecret, nonce)
}
func (mg *Maingate) CallTwitterAPI(requesturl, method, oauth_token, oauth_secret, nonce string) string {
@ -272,8 +272,8 @@ func (mg *Maingate) CallTwitterAPI(requesturl, method, oauth_token, oauth_secret
//vals.Add("oauth_callback", "actionclient://callback")
//vals.Add("oauth_callback", "http://127.0.0.1:7770/auth")
vals.Add("oauth_callback", mg.RedirectBaseUrl+"/authorize/"+AuthPlatformTwitter)
vals.Add("oauth_consumer_key", mg.TwitterCustomerKey)
vals.Add("oauth_callback", config.RedirectBaseUrl+"/authorize/"+AuthPlatformTwitter)
vals.Add("oauth_consumer_key", config.TwitterCustomerKey)
vals.Add("oauth_token", oauth_token)
vals.Add("oauth_signature_method", "HMAC-SHA1")
vals.Add("oauth_timestamp", strconv.Itoa(int(time.Now().Unix())))
@ -282,7 +282,7 @@ func (mg *Maingate) CallTwitterAPI(requesturl, method, oauth_token, oauth_secret
parameterString := strings.Replace(vals.Encode(), "+", "%20", -1)
signatureBase := strings.ToUpper(method) + "&" + url.QueryEscape(strings.Split(requesturl, "?")[0]) + "&" + url.QueryEscape(parameterString)
signingKey := url.QueryEscape(mg.TwitterCustomerSecret) + "&" + url.QueryEscape(oauth_secret)
signingKey := url.QueryEscape(config.TwitterCustomerSecret) + "&" + url.QueryEscape(oauth_secret)
signature := calculateTwitterSignature(signatureBase, signingKey)
headerString := "OAuth oauth_callback=\"" + url.QueryEscape(vals.Get("oauth_callback")) + "\", oauth_consumer_key=\"" + url.QueryEscape(vals.Get("oauth_consumer_key")) + "\", oauth_nonce=\"" + url.QueryEscape(vals.Get("oauth_nonce")) +

View File

@ -1,6 +1,7 @@
package core
import (
"context"
"encoding/hex"
"encoding/json"
"errors"
@ -13,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"
@ -20,21 +22,14 @@ import (
)
type blockinfo struct {
Start primitive.DateTime `bson:"start" json:"start"`
End primitive.DateTime `bson:"_ts" json:"_ts"`
Reason string `bson:"reason" json:"reason"`
Accid primitive.ObjectID `bson:"_id,omitempty" json:"_id,omitempty"`
}
type blockinfoWithStringId struct {
Reason string `bson:"reason" json:"reason"`
StrId string `bson:"id" json:"id"`
StartUnix int64 `bson:"start_unix" json:"start_unix"`
EndUnix int64 `bson:"end_unix" json:"end_unix"`
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,omitempty" json:"_id,omitempty"`
Id primitive.ObjectID `bson:"_id" json:"_id"`
Email string `bson:"email" json:"email"`
Platform string `bson:"platform" json:"platform"`
Desc string `bson:"desc" json:"desc"`
@ -88,10 +83,9 @@ type Maintenance struct {
}
type DivisionForUser struct {
Priority int `bson:"priority" json:"priority"`
State DivisionStateName `bson:"state" json:"state"`
LockCreateChar bool `bson:"lockcreatechar" json:"lockcreatechar"`
Maintenance *Maintenance `bson:"maintenance,omitempty" json:"maintenance,omitempty"`
Priority int `bson:"priority" json:"priority"`
State DivisionStateName `bson:"state" json:"state"`
Maintenance *Maintenance `bson:"maintenance,omitempty" json:"maintenance,omitempty"`
}
type Division struct {
@ -111,11 +105,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)
@ -162,6 +156,7 @@ func (sh *serviceDescription) readProfile(authtype string, id string, binfo stri
if err != nil {
return "", err
}
if len(userinfo.token) == 0 {
return "", errors.New("refreshtoken token not found")
}
@ -251,16 +246,32 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
defaultMarshaled, _ := json.Marshal(defaultsDivs)
sh.divisionsSplits["default"] = defaultMarshaled
sh.MaximumNumLinkAccount = mg.maingateConfig.MaximumNumLinkAccount
sh.MaximumNumLinkAccount = config.MaximumNumLinkAccount
sh.mongoClient = mg.mongoClient
sh.auths = mg.auths
sh.sessionTTL = time.Duration(mg.SessionTTL * int64(time.Second))
sh.sessionProvider = mg.sessionProvider
sh.sessionTTL = time.Duration(config.SessionTTL * int64(time.Second))
sh.serviceCodeBytes, _ = hex.DecodeString(sh.ServiceCode)
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)
@ -292,11 +303,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
}
@ -311,6 +321,13 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
// 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
}
bfinfo, err := sh.getUserBrowserInfo(r)
if err != nil {
logger.Error("getUserBrowserInfo failed :", err)
@ -318,35 +335,16 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
return
}
guestlink := (len(oldType) == 0)
if !guestlink {
_, err = sh.readProfile(oldType, oldId, bfinfo)
if err != nil {
logger.Println("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)
_, err = sh.readProfile(oldType, oldId, bfinfo)
if err != nil {
logger.Println("getProviderInfo failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
//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)
logger.Error("readProfile(old) failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
email, err := sh.readProfile(newType, newId, bfinfo)
if err != nil {
logger.Println("readProfile(new) failed :", err)
logger.Error("readProfile(new) failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
@ -359,9 +357,8 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
newType, newId, err = sh.getProviderInfo(newType, newId)
if err != nil {
logger.Println("getProviderInfo failed :", err)
logger.Error("getProviderInfo failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
createtime := primitive.NewDateTimeFromTime(time.Now().UTC())
@ -384,13 +381,13 @@ 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))
if err != nil {
logger.Error("link failed. Update ServiceName err :", err)
w.WriteHeader(http.StatusInternalServerError)
w.WriteHeader(http.StatusBadRequest)
return
}
@ -402,20 +399,6 @@ func (sh *serviceDescription) link(w http.ResponseWriter, r *http.Request) {
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())
}
@ -437,13 +420,11 @@ func (sh *serviceDescription) unlink(w http.ResponseWriter, r *http.Request) {
sType := queryvals.Get("stype")
sId := queryvals.Get("sid")
sk := queryvals.Get("sk")
targetType := queryvals.Get("ttype")
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
}
@ -455,60 +436,55 @@ func (sh *serviceDescription) unlink(w http.ResponseWriter, r *http.Request) {
// 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)
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.Accid,
}, options.Find().SetProjection(bson.M{
"_id": 1,
}))
numRecord, err := sh.mongoClient.Collection(CollectionAccount).CountDocuments(context.Background(), bson.M{
"accid": authInfo.Account,
}, options.Count().SetLimit(2))
if err != nil {
logger.Error("unlink failed, fail to count accounts :", err)
w.WriteHeader(http.StatusInternalServerError)
w.WriteHeader(http.StatusBadRequest)
}
if len(accDocs) <= 1 {
if numRecord <= 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))
sType, sId, err = sh.getProviderInfo(sType, sId)
if err != nil {
logger.Error("getProviderInfo failed :", err)
w.WriteHeader(http.StatusBadRequest)
}
link, err := sh.mongoClient.FindOneAndDelete(CollectionLink, bson.M{
"platform": targetType,
"_id": bson.M{"$in": ids},
}, options.FindOneAndDelete().SetProjection(bson.M{"_id": 1}))
link, err := sh.mongoClient.FindOne(CollectionLink, bson.M{
"platform": sType,
"uid": sId,
}, options.FindOne().SetProjection(bson.M{"_id": 1}))
if err != nil {
logger.Error("unlink failed. FindOneAndDelete link err:", err)
logger.Error("link failed. FindOneAndUpdate link err:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
preid, err := sh.mongoClient.FindOneAndDelete(CollectionAccount, bson.M{
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.StatusInternalServerError)
w.WriteHeader(http.StatusBadRequest)
return
}
if preid == nil {
// newid가 있어야 한다. 그래야 기존 서비스 계정이 없는 상태이다.
if newid == nil {
// 이미 계정이 있네?
logger.Println("unlink failed. service account not found:", r.URL.Query())
w.WriteHeader(http.StatusBadRequest)
return
@ -536,11 +512,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
}
@ -552,62 +527,91 @@ func (sh *serviceDescription) linkinfo(w http.ResponseWriter, r *http.Request) {
// 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 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)
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.Accid,
}, options.Find().SetLimit(sh.MaximumNumLinkAccount).SetProjection(bson.M{
"_id": 1,
}))
numRecord, err := sh.mongoClient.Collection(CollectionAccount).CountDocuments(context.Background(), bson.M{
"accid": authInfo.Account,
}, options.Count().SetLimit(sh.MaximumNumLinkAccount))
if err != nil {
logger.Error("linkinfo failed. CountDocuments err :", err)
w.WriteHeader(http.StatusInternalServerError)
w.WriteHeader(http.StatusBadRequest)
return
}
var ids primitive.A
for _, accDoc := range accDocs {
ids = append(ids, accDoc["_id"].(primitive.ObjectID))
logger.Println("linkinfo :", numRecord)
w.Write([]byte(fmt.Sprintf(`{"num_linked_account":"%d"}`, numRecord)))
}
// == 계정 이메일 조회
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
}
links, err := sh.mongoClient.FindAll(CollectionLink, bson.M{
"_id": bson.M{"$in": ids},
}, options.Find().SetLimit(sh.MaximumNumLinkAccount).SetProjection(bson.M{
platformName: 1,
}))
queryvals := r.URL.Query()
sk := queryvals.Get("sk")
authInfo, err := sh.sessionProvider.Query(sk)
if err != nil {
logger.Error("linkinfo failed. FindAll returns err :", err)
logger.Println("sessionProvider.Query return err :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
var linkstrs []string
for _, link := range links {
linkstrs = append(linkstrs, link[platformName].(string))
email := authInfo.Email
if strings.HasPrefix(email, "__dummy_") && strings.HasSuffix(email, "temp__") {
email = ""
}
linkbytes, err := json.Marshal(linkstrs)
if err != nil {
logger.Error("linkinfo failed. json marshal fail :", err)
w.WriteHeader(http.StatusInternalServerError)
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.Invalidate(authinfo.Account)
}
return
}
logger.Println("linkinfo :", linkstrs)
w.Write(linkbytes)
sh.authorize(w, r)
}
func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request) {
@ -626,8 +630,26 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
queryvals := r.URL.Query()
authtype := queryvals.Get("type")
uid := queryvals.Get("id")
//accesstoken := queryvals.Get("token") //-- 이거 이제 받지마라
session := queryvals.Get("sk")
if sk := queryvals.Get("sk"); len(sk) > 0 {
success, err := sh.sessionProvider.Touch(sk)
if err != nil {
logger.Error("authorize failed. sessionProvider.Touch err:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// !success일 때 빈 body를 보내면 클라이언트는 로그아웃 된다.
if success {
json.NewEncoder(w).Encode(map[string]any{
"sk": sk,
"expirein": sh.sessionTTL.Seconds(),
})
} else {
w.WriteHeader(http.StatusUnauthorized)
}
return
}
var email string
if !*noauth {
@ -635,173 +657,112 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
//email, err := sh.readProfile(authtype, uid, accesstoken)
bfinfo, err := sh.getUserBrowserInfo(r)
if err != nil {
logger.Println("getUserBrowserInfo failed :", err)
logger.Error("getUserBrowserInfo failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
email, err = sh.readProfile(authtype, uid, bfinfo)
if err != nil {
logger.Println("readProfile failed :", err)
logger.Error("readProfile failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
newType, newId, err := sh.getProviderInfo(authtype, uid)
if err != nil {
logger.Println("getProviderInfo failed :", err)
logger.Error("getProviderInfo failed :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if authtype != newType || uid != newId {
logger.Printf("auth success ( redirect ) : %s->%s, %s->%s, %s, %s", authtype, newType, uid, newId, email, session)
authtype = newType
uid = newId
}
} else {
} else if *devflag {
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)
}
//if len(session) == 0 && len(email) > 0 {
if len(session) == 0 {
// platform + id -> account id
createtime := primitive.NewDateTimeFromTime(time.Now().UTC())
link, err := sh.mongoClient.FindOneAndUpdate(CollectionLink, bson.M{
"platform": authtype,
"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()
for i := 0; i < len(sh.serviceCodeBytes); i++ {
newaccid[i] ^= sh.serviceCodeBytes[i]
}
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
var bi *blockinfo
if sh.bl.contains(accid, &bi) {
// 블럭된 계정. 블락 정보를 알려준다.
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.Header().Add("MG-ACCOUNTBLOCK-REASON", bi.Reason)
w.WriteHeader(http.StatusUnauthorized)
return
}
newsession := primitive.NewObjectID()
expired := primitive.NewDateTimeFromTime(time.Now().UTC().Add(sh.sessionTTL))
newauth := gocommon.Authinfo{
Accid: accid,
ServiceCode: sh.ServiceCode,
Platform: authtype,
Uid: uid,
Email: email,
Sk: newsession,
Expired: expired,
//RefreshToken: queryvals.Get("rt"),
}
_, _, err = sh.mongoClient.UpsertOne(CollectionAuth, bson.M{"_id": newauth.Accid}, &newauth)
if err != nil {
logger.Error("authorize failed :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
logger.Println("session created :", accid, authtype, uid, email, newsession)
output := map[string]any{
"sk": newsession.Hex(),
"expirein": sh.sessionTTL.Seconds(),
"newAccount": newaccount,
"accid": newauth.Accid.Hex(),
}
if *noauth {
output["noauth"] = true
}
if link["_ts"] != nil {
delts := link["_ts"].(primitive.DateTime)
if !delts.Time().IsZero() {
// 삭제된 계정. 삭제 되었다고 알려주자
w.Header().Add("MG-ACCOUNT-DELETED", "TRUE")
}
}
bt, _ := json.Marshal(output)
w.Write(bt)
} else if len(session) > 0 {
sessionobj, _ := primitive.ObjectIDFromHex(session)
if !sessionobj.IsZero() {
updated, _, err := sh.mongoClient.Update(CollectionAuth,
bson.M{
"sk": sessionobj,
},
bson.M{
"$currentDate": bson.M{
"_ts": bson.M{"$type": "date"},
},
}, options.Update().SetUpsert(false))
if err != nil {
logger.Error("update auth collection failed :", err)
return
}
if !updated {
// 세션이 없네?
logger.Println("authorize failed. session not exists in database :", session)
w.WriteHeader(http.StatusUnauthorized)
return
}
output := map[string]any{
"sk": session,
"expirein": sh.sessionTTL.Seconds(),
}
logger.Println("session updated :", authtype, uid, session)
bt, _ := json.Marshal(output)
w.Write(bt)
} else {
logger.Println("authorize failed. sk is not valid hex :", session)
w.WriteHeader(http.StatusBadRequest)
return
}
} else {
logger.Println("authorize failed. id empty :", queryvals)
// platform + id -> account id
createtime := primitive.NewDateTimeFromTime(time.Now().UTC())
link, err := sh.mongoClient.FindOneAndUpdate(CollectionLink, bson.M{
"platform": authtype,
"uid": uid,
}, 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("authorize failed :", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
linkid := link["_id"].(primitive.ObjectID)
newaccid := primitive.NewObjectID()
for i := 0; i < len(sh.serviceCodeBytes); i++ {
newaccid[i] ^= sh.serviceCodeBytes[i]
}
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
}
sk, err := sh.sessionProvider.New(&session.Authorization{
Account: accid,
Platform: authtype,
Uid: uid,
Email: email,
})
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 *noauth {
output["noauth"] = true
}
json.NewEncoder(w).Encode(output)
}
func (sh *serviceDescription) findVersionSplit(version string) []byte {
@ -817,99 +778,12 @@ func (sh *serviceDescription) findVersionSplit(version string) []byte {
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 := sh.auths.Find(sk)
if authInfo == nil {
// 잘못된 세션
logger.Println("delacc failed. session key is not valid :", sk)
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.Accid,
}, 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 := primitive.NewDateTimeFromTime(time.Now().UTC())
delop = primitive.M{
"$set": primitive.M{"_ts": curtime},
}
if sType == AuthPlatformFirebaseAuth {
sh.mongoClient.Delete(CollectionFirebaseUserInfo, bson.M{
"firebaseuserid": sId,
})
}
func (sh *serviceDescription) serveHTTP_dev(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "/auth") {
sh.authorize_dev(w, r)
} 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},
}
sh.serveHTTP(w, r)
}
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(w http.ResponseWriter, r *http.Request) {
@ -933,8 +807,8 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request)
sh.unlink(w, r)
} else if strings.HasSuffix(r.URL.Path, "/linkinfo") {
sh.linkinfo(w, r)
} else if strings.HasSuffix(r.URL.Path, "/delacc") {
sh.delacc(w, r)
} else if strings.HasSuffix(r.URL.Path, "/emailinfo") {
sh.emailinfo(w, r)
} else if strings.HasSuffix(r.URL.Path, "/divs") {
// TODO : 세션키와 authtoken을 헤더로 받아서 accid 조회
queryvals := r.URL.Query()
@ -949,7 +823,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.Account.IsZero() {
logger.Println("sessionkey is not valid :", sk)
w.WriteHeader(http.StatusUnauthorized)
return
@ -970,7 +851,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.Account.IsZero() {
logger.Println("sessionkey is not valid :", sk)
w.WriteHeader(http.StatusUnauthorized)
return
@ -979,26 +867,33 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request)
divname := queryvals.Get("div")
divname = strings.Trim(divname, `"`)
div := sh.Divisions[divname]
var addrresp string
if div != nil {
logger.Println("/addr :", divname, div.State)
switch div.State {
case DivisionState_FullOpen:
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
addrresp = fmt.Sprintf(`{"service":"%s"}`, div.Url)
//w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
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}
if sh.wl.contains(wm.Key(), nil) {
wm := &whitelistmember{Email: authInfo.Email, Platform: authInfo.Platform}
if _, ok := sh.wl.get(wm.Key()); ok {
// qa 권한이면 입장 가능
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
addrresp = fmt.Sprintf(`{"service":"%s"}`, div.Url)
//w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
} else if div.Maintenance != nil {
// 권한이 없으므로 공지
w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
addrresp = fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)
//w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
} else {
logger.Println("div.Maintenance is nil :", divname)
}
@ -1006,13 +901,18 @@ func (sh *serviceDescription) serveHTTP(w http.ResponseWriter, r *http.Request)
case DivisionState_Maintenance:
// 점검중. 아무도 못들어감
if div.Maintenance != nil {
w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
logger.Println("/addr :", divname, div.State, *div.Maintenance)
addrresp = fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)
//w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
} else {
logger.Println("div.Maintenance is nil :", divname)
}
}
logger.Println("/addr resp :", addrresp)
w.Write([]byte(addrresp))
} else {
logger.Println("div is not found :", divname, sh.Divisions)
logger.Println("check maingate database 'service.divisions' :", config.Mongo)
w.WriteHeader(http.StatusBadRequest)
}
} else {

View File

@ -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)
}
}
}

10
go.mod
View File

@ -1,13 +1,13 @@
module repositories.action2quare.com/ayo/maingate
go 1.18
go 1.19
require (
firebase.google.com/go v3.13.0+incompatible
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-20230912075917-f9a146321cdb
repositories.action2quare.com/ayo/gocommon v0.0.0-20240201092859-c71a74762de7
)
require (
@ -42,7 +42,7 @@ require (
golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.9.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/sys v0.11.0 // indirect
golang.org/x/text v0.10.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
@ -51,7 +51,5 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.56.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
replace repositories.action2quare.com/ayo/maingate => ./

12
go.sum
View File

@ -192,8 +192,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -255,8 +255,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@ -268,5 +268,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb h1:Rdf6uhBIWunRLZ2LIT1hSovYXxZoOzx9mdSK5bjWpos=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230912075917-f9a146321cdb/go.mod h1:rn6NA28Mej+qgLNx/Bu2wsdGyIycmacqlNP6gUXX2a0=
repositories.action2quare.com/ayo/gocommon v0.0.0-20240201092859-c71a74762de7 h1:ikDwKNiRXJlIBueAVmp9p2To+lRN9zTzGSvVHCXgFnI=
repositories.action2quare.com/ayo/gocommon v0.0.0-20240201092859-c71a74762de7/go.mod h1:Gb418rT96M3K7L/XMPzp8IJj4UXVunq7dZzrxsMBz/8=

15
main.go
View File

@ -2,9 +2,7 @@ package main
import (
"context"
"math/rand"
"net/http"
"time"
"repositories.action2quare.com/ayo/gocommon"
"repositories.action2quare.com/ayo/gocommon/flagx"
@ -21,26 +19,29 @@ func main() {
flagx.Parse()
logger.Println("build revision =", revision)
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mg, err := core.New(ctx)
if err != nil {
logger.Error("core.New failed :", err)
panic(err)
return
}
defer mg.Destructor()
serveMux := http.NewServeMux()
if err := mg.RegisterHandlers(ctx, serveMux, *prefix); err != nil {
logger.Error("RegisterHandlers failed :", err)
panic(err)
return
}
server := gocommon.NewHTTPServer(serveMux)
logger.Println("maingate is started")
if err := server.Start(); err != nil {
logger.Error("maingate is stopped with error :", err)
}
cancel()
mg.Destructor()
logger.Println("maingate is terminated")
}

View File

@ -1,14 +0,0 @@
# $ErrorActionPreference = 'SilentlyContinue'
$CurBranch = git branch --show-current
Remove-Item maingate.zip -Force -Recurse -ErrorAction SilentlyContinue
$Env:GOOS="linux"
$Env:GOARCH="amd64"
go build -ldflags="-s -w" .
Compress-Archive -Path maingate -Update -DestinationPath maingate.zip
Compress-Archive -Path *-firebase-*.json -Update -DestinationPath maingate.zip
Compress-Archive -Path fba -Update -DestinationPath maingate.zip
Compress-Archive -Path template -Update -DestinationPath maingate.zip