2023-05-24 12:16:03 +09:00
|
|
|
package core
|
|
|
|
|
|
|
|
|
|
import (
|
2023-06-07 11:56:52 +09:00
|
|
|
"crypto/md5"
|
2023-05-24 12:16:03 +09:00
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
2023-05-28 22:13:10 +09:00
|
|
|
"path"
|
2023-05-24 12:16:03 +09:00
|
|
|
"strings"
|
|
|
|
|
"sync/atomic"
|
|
|
|
|
"time"
|
|
|
|
|
"unsafe"
|
|
|
|
|
|
2023-05-24 15:31:01 +09:00
|
|
|
common "repositories.action2quare.com/ayo/gocommon"
|
|
|
|
|
"repositories.action2quare.com/ayo/gocommon/logger"
|
2023-05-24 12:16:03 +09:00
|
|
|
|
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type blockinfo struct {
|
2023-06-07 11:56:52 +09:00
|
|
|
Start primitive.DateTime `bson:"start" json:"start"`
|
2023-05-24 12:16:03 +09:00
|
|
|
End primitive.DateTime `bson:"_ts"`
|
2023-06-07 11:56:52 +09:00
|
|
|
Reason string `bson:"reason" json:"reason"`
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
2023-06-19 14:31:34 +09:00
|
|
|
type whitelistMemberTag = string
|
2023-05-28 22:13:10 +09:00
|
|
|
|
2023-05-24 12:16:03 +09:00
|
|
|
type whitelistmember struct {
|
2023-06-19 14:31:34 +09:00
|
|
|
Email string `bson:"email" json:"email"`
|
|
|
|
|
Platform string `bson:"platform" json:"platform"`
|
|
|
|
|
Desc string `bson:"desc" json:"desc"`
|
|
|
|
|
Expired primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"`
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type whitelist struct {
|
|
|
|
|
emailptr unsafe.Pointer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type usertokeninfo struct {
|
|
|
|
|
platform string
|
|
|
|
|
userid string
|
|
|
|
|
token string //refreshtoken
|
|
|
|
|
secret string
|
|
|
|
|
brinfo string
|
|
|
|
|
accesstoken string // microsoft only
|
|
|
|
|
accesstoken_expire_time int64 // microsoft only
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (wl *whitelist) init(total []whitelistmember) {
|
2023-06-19 14:31:34 +09:00
|
|
|
all := make(map[string]*whitelistmember)
|
2023-05-24 12:16:03 +09:00
|
|
|
for _, member := range total {
|
2023-05-28 22:13:10 +09:00
|
|
|
all[whitelistKey(member.Email)] = &member
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
2023-05-28 22:13:10 +09:00
|
|
|
atomic.StorePointer(&wl.emailptr, unsafe.Pointer(&all))
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
2023-05-28 22:13:10 +09:00
|
|
|
func addToUnsafePointer(to *unsafe.Pointer, m *whitelistmember) {
|
|
|
|
|
ptr := atomic.LoadPointer(to)
|
2023-05-24 12:16:03 +09:00
|
|
|
src := (*map[string]*whitelistmember)(ptr)
|
|
|
|
|
|
|
|
|
|
next := map[string]*whitelistmember{}
|
|
|
|
|
for k, v := range *src {
|
|
|
|
|
next[k] = v
|
|
|
|
|
}
|
|
|
|
|
next[whitelistKey(m.Email)] = m
|
2023-05-28 22:13:10 +09:00
|
|
|
atomic.StorePointer(to, unsafe.Pointer(&next))
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
2023-05-28 22:13:10 +09:00
|
|
|
func removeFromUnsafePointer(from *unsafe.Pointer, email string) {
|
|
|
|
|
ptr := atomic.LoadPointer(from)
|
2023-05-24 12:16:03 +09:00
|
|
|
src := (*map[string]*whitelistmember)(ptr)
|
|
|
|
|
|
|
|
|
|
next := make(map[string]*whitelistmember)
|
|
|
|
|
for k, v := range *src {
|
|
|
|
|
next[k] = v
|
|
|
|
|
}
|
|
|
|
|
delete(next, whitelistKey(email))
|
2023-05-28 22:13:10 +09:00
|
|
|
atomic.StorePointer(from, unsafe.Pointer(&next))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (wl *whitelist) add(m *whitelistmember) {
|
|
|
|
|
addToUnsafePointer(&wl.emailptr, m)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (wl *whitelist) remove(email string) {
|
|
|
|
|
removeFromUnsafePointer(&wl.emailptr, email)
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (wl *whitelist) isMember(email string, platform string) bool {
|
|
|
|
|
ptr := atomic.LoadPointer(&wl.emailptr)
|
|
|
|
|
src := *(*map[string]*whitelistmember)(ptr)
|
|
|
|
|
|
|
|
|
|
if member, exists := src[whitelistKey(email)]; exists {
|
|
|
|
|
return member.Platform == platform
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-05 17:58:46 +09:00
|
|
|
type DivisionStateName string
|
2023-05-28 22:13:10 +09:00
|
|
|
|
2023-05-24 15:31:01 +09:00
|
|
|
const (
|
2023-06-05 17:58:46 +09:00
|
|
|
DivisionState_Closed = DivisionStateName("closed")
|
|
|
|
|
DivisionState_Maintenance = DivisionStateName("maintenance")
|
|
|
|
|
DivisionState_RestrictedOpen = DivisionStateName("restricted")
|
|
|
|
|
DivisionState_FullOpen = DivisionStateName("open")
|
2023-05-24 15:31:01 +09:00
|
|
|
)
|
|
|
|
|
|
2023-06-05 17:58:46 +09:00
|
|
|
type Maintenance struct {
|
2023-06-05 17:22:48 +09:00
|
|
|
Notice string `bson:"notice" json:"notice"`
|
2023-05-28 22:13:10 +09:00
|
|
|
StartTimeUTC int64 `bson:"start_unixtime_utc" json:"start_unixtime_utc"`
|
|
|
|
|
link string
|
2023-05-24 15:31:01 +09:00
|
|
|
}
|
|
|
|
|
|
2023-06-07 11:56:52 +09:00
|
|
|
type DivisionForUser struct {
|
|
|
|
|
Priority int `bson:"priority" json:"priority"`
|
|
|
|
|
State DivisionStateName `bson:"state" json:"state"`
|
2023-06-08 12:14:21 +09:00
|
|
|
Maintenance *Maintenance `bson:"maintenance,omitempty" json:"maintenance,omitempty"`
|
2023-06-07 11:56:52 +09:00
|
|
|
}
|
|
|
|
|
|
2023-06-05 18:01:09 +09:00
|
|
|
type Division struct {
|
2023-06-07 11:56:52 +09:00
|
|
|
DivisionForUser `bson:",inline" json:",inline"`
|
|
|
|
|
Url string `bson:"url" json:"url"`
|
2023-05-24 15:31:01 +09:00
|
|
|
}
|
|
|
|
|
|
2023-06-05 11:56:34 +09:00
|
|
|
type ServiceDescriptionSummary struct {
|
2023-06-19 14:56:47 +09:00
|
|
|
Id primitive.ObjectID `bson:"_id" json:"_id"`
|
|
|
|
|
ServiceCode string `bson:"code" json:"code"`
|
2023-06-05 11:56:34 +09:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 12:16:03 +09:00
|
|
|
type serviceDescription struct {
|
2023-06-07 11:56:52 +09:00
|
|
|
ServiceDescriptionSummary `bson:",inline" json:",inline"`
|
|
|
|
|
Divisions map[string]*Division `bson:"divisions" json:"divisions"`
|
|
|
|
|
ServerApiTokens []primitive.ObjectID `bson:"api_tokens" json:"api_tokens"`
|
2023-06-19 21:19:45 +09:00
|
|
|
Admins []string `bson:"admins" json:"admins"`
|
2023-05-24 12:16:03 +09:00
|
|
|
|
|
|
|
|
auths *common.AuthCollection
|
2023-06-20 11:18:32 +09:00
|
|
|
wl *whitelist
|
2023-05-24 12:16:03 +09:00
|
|
|
mongoClient common.MongoClient
|
|
|
|
|
sessionTTL time.Duration
|
|
|
|
|
serviceCodeBytes []byte
|
|
|
|
|
getUserBrowserInfo func(r *http.Request) (string, error)
|
|
|
|
|
getUserTokenWithCheck func(platform string, userid string, brinfo string) (usertokeninfo, error)
|
|
|
|
|
updateUserinfo func(info usertokeninfo) (bool, string, string)
|
2023-05-24 15:31:01 +09:00
|
|
|
getProviderInfo func(platform string, uid string) (string, string, error)
|
2023-05-24 12:16:03 +09:00
|
|
|
|
2023-06-19 21:19:45 +09:00
|
|
|
admins unsafe.Pointer
|
2023-06-07 11:56:52 +09:00
|
|
|
divisionsForUsersSerialized unsafe.Pointer
|
|
|
|
|
divisionsSerialized unsafe.Pointer
|
|
|
|
|
serviceSerialized unsafe.Pointer
|
|
|
|
|
serviceSummarySerialized unsafe.Pointer
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
2023-06-19 21:19:45 +09:00
|
|
|
func (sh *serviceDescription) isValidToken(apiToken primitive.ObjectID) bool {
|
2023-06-23 17:58:41 +09:00
|
|
|
if *devflag {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-19 21:19:45 +09:00
|
|
|
if apiToken.IsZero() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, test := range sh.ServerApiTokens {
|
|
|
|
|
if test == apiToken {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 12:16:03 +09:00
|
|
|
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 userinfo.token == "" {
|
|
|
|
|
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 {
|
2023-05-28 22:13:10 +09:00
|
|
|
divs := sh.Divisions
|
2023-05-24 12:16:03 +09:00
|
|
|
if len(sh.ServiceCode) == 0 {
|
|
|
|
|
sh.ServiceCode = hex.EncodeToString(sh.Id[6:])
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 17:34:30 +09:00
|
|
|
if *noauth {
|
|
|
|
|
sh.ServiceCode = "000000000000"
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-07 11:56:52 +09:00
|
|
|
divsForUsers := make(map[string]*DivisionForUser)
|
2023-05-28 22:13:10 +09:00
|
|
|
for dn, div := range divs {
|
|
|
|
|
if div.State == DivisionState_Closed {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-07 11:56:52 +09:00
|
|
|
divsForUsers[dn] = &div.DivisionForUser
|
2023-05-28 22:13:10 +09:00
|
|
|
if len(div.State) == 0 {
|
|
|
|
|
div.State = DivisionState_FullOpen
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if div.State != DivisionState_FullOpen {
|
|
|
|
|
if div.Maintenance == nil {
|
2023-06-05 17:58:46 +09:00
|
|
|
div.Maintenance = &Maintenance{}
|
2023-05-28 22:13:10 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2023-06-07 11:56:52 +09:00
|
|
|
hasher := md5.New()
|
2023-06-20 11:07:53 +09:00
|
|
|
hasher.Write(sh.Id[:])
|
2023-06-07 11:56:52 +09:00
|
|
|
subfolder := hex.EncodeToString(hasher.Sum(nil))[:8]
|
|
|
|
|
|
|
|
|
|
div.Maintenance.link = path.Join("static", subfolder, div.Maintenance.Notice)
|
2023-05-28 22:13:10 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
div.Maintenance = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
divmarshaled, _ := json.Marshal(divs)
|
2023-05-24 12:16:03 +09:00
|
|
|
devstr := string(divmarshaled)
|
|
|
|
|
sh.divisionsSerialized = unsafe.Pointer(&devstr)
|
|
|
|
|
|
2023-06-07 11:56:52 +09:00
|
|
|
divmarshaled2, _ := json.Marshal(divsForUsers)
|
|
|
|
|
devstr2 := string(divmarshaled2)
|
|
|
|
|
sh.divisionsForUsersSerialized = unsafe.Pointer(&devstr2)
|
|
|
|
|
|
2023-05-24 12:16:03 +09:00
|
|
|
sh.mongoClient = mg.mongoClient
|
|
|
|
|
sh.auths = mg.auths
|
|
|
|
|
sh.sessionTTL = time.Duration(mg.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
|
2023-06-20 11:29:17 +09:00
|
|
|
|
|
|
|
|
if sh.Admins == nil {
|
|
|
|
|
sh.Admins = []string{}
|
|
|
|
|
}
|
2023-06-19 21:19:45 +09:00
|
|
|
sh.admins = unsafe.Pointer(&sh.Admins)
|
2023-06-20 11:29:17 +09:00
|
|
|
|
2023-06-20 11:18:32 +09:00
|
|
|
sh.wl = &mg.wl
|
2023-06-07 11:16:53 +09:00
|
|
|
bt, _ := json.Marshal(sh)
|
2023-05-24 12:16:03 +09:00
|
|
|
atomic.StorePointer(&sh.serviceSerialized, unsafe.Pointer(&bt))
|
|
|
|
|
|
2023-06-07 11:16:53 +09:00
|
|
|
btsum, _ := json.Marshal(sh.ServiceDescriptionSummary)
|
|
|
|
|
atomic.StorePointer(&sh.serviceSummarySerialized, unsafe.Pointer(&btsum))
|
|
|
|
|
|
2023-06-20 11:07:53 +09:00
|
|
|
logger.Println("service is ready :", sh.ServiceCode, sh.Admins, string(divmarshaled))
|
2023-05-24 12:16:03 +09:00
|
|
|
|
|
|
|
|
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 := sh.auths.Find(sk)
|
|
|
|
|
if oldAuth == nil {
|
|
|
|
|
// 잘못된 세션
|
|
|
|
|
logger.Println("link failed. session key is not valid :", sk)
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bfinfo, err := sh.getUserBrowserInfo(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("getUserBrowserInfo failed :", err)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = sh.readProfile(oldType, oldId, bfinfo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("readProfile(old) failed :", err)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
email, 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
|
|
|
|
|
// }
|
|
|
|
|
|
2023-05-24 15:31:01 +09:00
|
|
|
newType, newId, err = sh.getProviderInfo(newType, newId)
|
2023-05-24 12:16:03 +09:00
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("getProviderInfo failed :", err)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-20 12:02:29 +09:00
|
|
|
_, newid, err := sh.mongoClient.Update(CollectionAccount, bson.M{
|
2023-05-24 12:16:03 +09:00
|
|
|
"_id": link["_id"].(primitive.ObjectID),
|
|
|
|
|
}, bson.M{
|
|
|
|
|
"$setOnInsert": bson.M{
|
|
|
|
|
"accid": oldAuth.Accid,
|
|
|
|
|
"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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Println("link success :", r.URL.Query())
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-19 21:19:45 +09:00
|
|
|
func (sh *serviceDescription) isAdmin(email string) bool {
|
|
|
|
|
ptr := atomic.LoadPointer(&sh.admins)
|
|
|
|
|
admins := *(*[]string)(ptr)
|
2023-05-24 12:16:03 +09:00
|
|
|
|
2023-06-19 21:19:45 +09:00
|
|
|
for _, a := range admins {
|
|
|
|
|
if a == email {
|
2023-05-24 12:16:03 +09:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
authtype := queryvals.Get("type")
|
|
|
|
|
uid := queryvals.Get("id")
|
|
|
|
|
//accesstoken := queryvals.Get("token") //-- 이거 이제 받지마라
|
|
|
|
|
session := queryvals.Get("sk")
|
2023-06-20 15:50:18 +09:00
|
|
|
var email string
|
|
|
|
|
|
2023-06-21 14:33:29 +09:00
|
|
|
if !*noauth {
|
2023-06-20 15:50:18 +09:00
|
|
|
//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
|
|
|
|
|
}
|
2023-05-24 12:16:03 +09:00
|
|
|
|
2023-06-20 15:50:18 +09:00
|
|
|
email, err = sh.readProfile(authtype, uid, bfinfo)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("readProfile failed :", err)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-05-24 12:16:03 +09:00
|
|
|
|
2023-06-20 15:50:18 +09:00
|
|
|
logger.Println("auth success :", authtype, uid, email, session)
|
2023-05-24 12:16:03 +09:00
|
|
|
|
2023-06-20 15:50:18 +09:00
|
|
|
newType, newId, err := sh.getProviderInfo(authtype, uid)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("getProviderInfo failed :", err)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
|
}
|
2023-05-24 12:16:03 +09:00
|
|
|
|
2023-06-20 15:50:18 +09:00
|
|
|
if authtype != newType || uid != newId {
|
|
|
|
|
authtype = newType
|
|
|
|
|
uid = newId
|
|
|
|
|
logger.Println("auth success ( redirect ) :", authtype, uid, email, session)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
email = fmt.Sprintf("%s@noauth.flag", uid)
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//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}))
|
|
|
|
|
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]
|
|
|
|
|
}
|
2023-06-20 12:02:29 +09:00
|
|
|
account, err := sh.mongoClient.FindOneAndUpdate(CollectionAccount, bson.M{
|
2023-05-24 12:16:03 +09:00
|
|
|
"_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 err := sh.mongoClient.FindOneAs(CollectionBlock, bson.M{
|
|
|
|
|
"code": sh.ServiceCode,
|
|
|
|
|
"accid": accid,
|
|
|
|
|
}, &bi); err != nil {
|
|
|
|
|
logger.Error("authorize failed. find blockinfo in CollectionBlock err:", err)
|
|
|
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !bi.Start.Time().IsZero() {
|
|
|
|
|
now := time.Now().UTC()
|
|
|
|
|
if bi.Start.Time().Before(now) && bi.End.Time().After(now) {
|
|
|
|
|
// block됐네?
|
|
|
|
|
// status는 정상이고 reason을 넘겨주자
|
|
|
|
|
json.NewEncoder(w).Encode(map[string]any{
|
|
|
|
|
"blocked": bi,
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newsession := primitive.NewObjectID()
|
|
|
|
|
expired := primitive.NewDateTimeFromTime(time.Now().UTC().Add(sh.sessionTTL))
|
|
|
|
|
newauth := common.Authinfo{
|
|
|
|
|
Accid: accid,
|
|
|
|
|
ServiceCode: sh.ServiceCode,
|
|
|
|
|
Platform: authtype,
|
|
|
|
|
Uid: uid,
|
2023-05-28 22:13:10 +09:00
|
|
|
Email: email,
|
|
|
|
|
Sk: newsession,
|
|
|
|
|
Expired: expired,
|
2023-05-24 12:16:03 +09:00
|
|
|
//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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output := map[string]any{
|
|
|
|
|
"sk": newsession.Hex(),
|
|
|
|
|
"expirein": sh.sessionTTL.Seconds(),
|
|
|
|
|
"newAccount": newaccount,
|
|
|
|
|
"accid": newauth.Accid.Hex(),
|
|
|
|
|
}
|
|
|
|
|
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")
|
|
|
|
|
logger.Error(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(),
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
2023-06-23 16:43:20 +09:00
|
|
|
} else if strings.HasSuffix(r.URL.Path, "/divs") {
|
|
|
|
|
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 : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의
|
|
|
|
|
// 일단 서버 종류만 내려보내자
|
|
|
|
|
// 세션키가 있는지 확인
|
|
|
|
|
if _, ok := sh.auths.IsValid(sk, ""); !ok {
|
|
|
|
|
logger.Println("sessionkey is not valid :", sk)
|
|
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
divstrptr := atomic.LoadPointer(&sh.divisionsForUsersSerialized)
|
|
|
|
|
divstr := *(*string)(divstrptr)
|
|
|
|
|
w.Write([]byte(divstr))
|
|
|
|
|
} else if strings.HasSuffix(r.URL.Path, "/addr") {
|
2023-05-24 12:16:03 +09:00
|
|
|
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 : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의
|
|
|
|
|
// 일단 서버 종류만 내려보내자
|
|
|
|
|
// 세션키가 있는지 확인
|
2023-05-28 22:13:10 +09:00
|
|
|
if _, ok := sh.auths.IsValid(sk, ""); !ok {
|
|
|
|
|
logger.Println("sessionkey is not valid :", sk)
|
2023-06-19 14:31:34 +09:00
|
|
|
w.WriteHeader(http.StatusUnauthorized)
|
2023-05-24 12:16:03 +09:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-23 16:43:20 +09:00
|
|
|
divname := queryvals.Get("div")
|
|
|
|
|
divname = strings.Trim(divname, `"`)
|
|
|
|
|
div := sh.Divisions[divname]
|
|
|
|
|
if div != nil {
|
|
|
|
|
switch div.State {
|
|
|
|
|
case DivisionState_FullOpen:
|
|
|
|
|
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)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if sh.wl.isMember(cell.ToAuthinfo().Email, cell.ToAuthinfo().Platform) {
|
|
|
|
|
// qa 권한이면 입장 가능
|
2023-05-28 22:13:10 +09:00
|
|
|
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
|
2023-06-23 16:43:20 +09:00
|
|
|
} else if div.Maintenance != nil {
|
|
|
|
|
// 권한이 없으므로 공지
|
|
|
|
|
w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
|
|
|
|
|
} else {
|
|
|
|
|
logger.Println("div.Maintenance is nil :", divname)
|
|
|
|
|
}
|
2023-05-28 22:13:10 +09:00
|
|
|
|
2023-06-23 16:43:20 +09:00
|
|
|
case DivisionState_Maintenance:
|
|
|
|
|
// 점검중. 아무도 못들어감
|
|
|
|
|
if div.Maintenance != nil {
|
|
|
|
|
w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
|
|
|
|
|
} else {
|
|
|
|
|
logger.Println("div.Maintenance is nil :", divname)
|
2023-05-28 22:13:10 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2023-06-23 16:43:20 +09:00
|
|
|
logger.Println("div is not found :", divname)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2023-05-28 22:13:10 +09:00
|
|
|
}
|
2023-06-23 16:43:20 +09:00
|
|
|
} else {
|
|
|
|
|
logger.Println("??? :", r.URL.Path)
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
2023-05-24 12:16:03 +09:00
|
|
|
}
|
|
|
|
|
}
|