파일 업로드 추가 / 화이트리스트에 권한 종류 추가

This commit is contained in:
2023-05-28 22:13:10 +09:00
parent 594b9fb700
commit 4347909aab
6 changed files with 427 additions and 88 deletions

View File

@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net/http"
"path"
"strings"
"sync/atomic"
"time"
@ -26,16 +27,25 @@ type blockinfo struct {
Reason string
}
type whitelistAuthType = string
const (
whitelistAuthType_Default = whitelistAuthType("")
whitelistAuthType_QA = whitelistAuthType("qa")
)
type whitelistmember struct {
Service string
Email string
Platform string
Desc string
Auth []whitelistAuthType
Expired primitive.DateTime `bson:"_ts,omitempty" json:"_ts,omitempty"`
}
type whitelist struct {
emailptr unsafe.Pointer
qaptr unsafe.Pointer
working int32
}
@ -50,17 +60,33 @@ type usertokeninfo struct {
}
func (wl *whitelist) init(total []whitelistmember) {
next := make(map[string]*whitelistmember)
auths := make(map[string]map[string]*whitelistmember)
for _, member := range total {
next[whitelistKey(member.Email)] = &member
}
all := auths[""]
if all == nil {
all = make(map[string]*whitelistmember)
auths[""] = all
}
all[whitelistKey(member.Email)] = &member
atomic.StorePointer(&wl.emailptr, unsafe.Pointer(&next))
atomic.StoreInt32(&wl.working, 1)
for _, auth := range member.Auth {
spec := auths[auth]
if spec == nil {
spec = make(map[string]*whitelistmember)
auths[auth] = spec
}
spec[whitelistKey(member.Email)] = &member
}
}
all := auths[whitelistAuthType_Default]
atomic.StorePointer(&wl.emailptr, unsafe.Pointer(&all))
qa := auths[whitelistAuthType_QA]
atomic.StorePointer(&wl.qaptr, unsafe.Pointer(&qa))
}
func (wl *whitelist) add(m *whitelistmember) {
ptr := atomic.LoadPointer(&wl.emailptr)
func addToUnsafePointer(to *unsafe.Pointer, m *whitelistmember) {
ptr := atomic.LoadPointer(to)
src := (*map[string]*whitelistmember)(ptr)
next := map[string]*whitelistmember{}
@ -68,11 +94,11 @@ func (wl *whitelist) add(m *whitelistmember) {
next[k] = v
}
next[whitelistKey(m.Email)] = m
atomic.StorePointer(&wl.emailptr, unsafe.Pointer(&next))
atomic.StorePointer(to, unsafe.Pointer(&next))
}
func (wl *whitelist) remove(email string) {
ptr := atomic.LoadPointer(&wl.emailptr)
func removeFromUnsafePointer(from *unsafe.Pointer, email string) {
ptr := atomic.LoadPointer(from)
src := (*map[string]*whitelistmember)(ptr)
next := make(map[string]*whitelistmember)
@ -80,7 +106,21 @@ func (wl *whitelist) remove(email string) {
next[k] = v
}
delete(next, whitelistKey(email))
atomic.StorePointer(&wl.emailptr, unsafe.Pointer(&next))
atomic.StorePointer(from, unsafe.Pointer(&next))
}
func (wl *whitelist) add(m *whitelistmember) {
addToUnsafePointer(&wl.emailptr, m)
for _, auth := range m.Auth {
if auth == whitelistAuthType_QA {
addToUnsafePointer(&wl.qaptr, m)
}
}
}
func (wl *whitelist) remove(email string) {
removeFromUnsafePointer(&wl.emailptr, email)
removeFromUnsafePointer(&wl.qaptr, email)
}
func (wl *whitelist) isMember(email string, platform string) bool {
@ -97,30 +137,46 @@ func (wl *whitelist) isMember(email string, platform string) bool {
return false
}
func (wl *whitelist) hasAuth(email string, platform string, auth whitelistAuthType) bool {
if auth == whitelistAuthType_QA {
ptr := atomic.LoadPointer(&wl.qaptr)
src := *(*map[string]*whitelistmember)(ptr)
if member, exists := src[whitelistKey(email)]; exists {
return member.Platform == platform
}
}
return false
}
type divisionStateName string
const (
DivisionState_Closed = string("closed")
DivisionState_Maintenance = string("maintenance")
DivisionState_RestrictedOpen = string("restricted")
DivisionState_FullOpen = string("open")
DivisionState_Closed = divisionStateName("closed")
DivisionState_Maintenance = divisionStateName("maintenance")
DivisionState_RestrictedOpen = divisionStateName("restricted")
DivisionState_FullOpen = divisionStateName("open")
)
type maintenance struct {
Link string
StartTime primitive.Timestamp
Notice string `bson:"notice"`
StartTimeUTC int64 `bson:"start_unixtime_utc" json:"start_unixtime_utc"`
link string
}
type division struct {
Url string `bson:"url"`
Priority int `bson:"priority"`
State string `bson:"state"`
Maintenance maintenance `bson:"maintenance"`
Url string `bson:"url"`
Priority int `bson:"priority"`
State divisionStateName `bson:"state"`
Maintenance *maintenance `bson:"maintenance"`
}
type serviceDescription struct {
// sync.Mutex
Id primitive.ObjectID `bson:"_id"`
ServiceName string `bson:"service"`
Divisions map[string]division `bson:"divisions"`
Divisions map[string]*division `bson:"divisions"`
ServiceCode string `bson:"code"`
UseWhitelist bool `bson:"use_whitelist"`
Closed bool `bson:"closed"`
@ -175,12 +231,46 @@ func (sh *serviceDescription) readProfile(authtype string, id string, binfo stri
}
func (sh *serviceDescription) prepare(mg *Maingate) error {
div := sh.Divisions
divs := sh.Divisions
if len(sh.ServiceCode) == 0 {
sh.ServiceCode = hex.EncodeToString(sh.Id[6:])
}
divmarshaled, _ := json.Marshal(div)
var closed []string
for dn, div := range divs {
if div.State == DivisionState_Closed {
closed = append(closed, dn)
continue
}
if len(div.State) == 0 {
div.State = DivisionState_FullOpen
}
if div.State != DivisionState_FullOpen {
if div.Maintenance == nil {
div.Maintenance = &maintenance{}
}
if len(div.Maintenance.link) == 0 {
if len(div.Maintenance.Notice) == 0 {
div.Maintenance.link = "https://www.action2quare.com"
} else if strings.HasPrefix(div.Maintenance.Notice, "http") {
div.Maintenance.link = div.Maintenance.Notice
} else {
div.Maintenance.link = path.Join("static", sh.ServiceCode, div.Maintenance.Notice)
}
}
} else {
div.Maintenance = nil
}
}
for _, dn := range closed {
delete(divs, dn)
}
divmarshaled, _ := json.Marshal(divs)
devstr := string(divmarshaled)
sh.divisionsSerialized = unsafe.Pointer(&devstr)
@ -200,15 +290,17 @@ func (sh *serviceDescription) prepare(mg *Maingate) error {
sh.closed = 0
}
if sh.UseWhitelist {
var whites []whitelistmember
if err := mg.mongoClient.FindAllAs(CollectionWhitelist, bson.M{
"$or": []bson.M{{"service": sh.ServiceName}, {"service": sh.ServiceCode}},
}, &whites, options.Find().SetReturnKey(false)); err != nil {
return err
}
var whites []whitelistmember
if err := mg.mongoClient.FindAllAs(CollectionWhitelist, bson.M{
"$or": []bson.M{{"service": sh.ServiceName}, {"service": sh.ServiceCode}},
}, &whites, options.Find().SetReturnKey(false)); err != nil {
return err
}
sh.wl.init(whites)
sh.wl.init(whites)
if sh.UseWhitelist {
sh.wl.working = 1
} else {
sh.wl.working = 0
}
@ -517,9 +609,9 @@ func (sh *serviceDescription) authorize(w http.ResponseWriter, r *http.Request)
ServiceCode: sh.ServiceCode,
Platform: authtype,
Uid: uid,
//Token: accesstoken,
Sk: newsession,
Expired: expired,
Email: email,
Sk: newsession,
Expired: expired,
//RefreshToken: queryvals.Get("rt"),
}
@ -604,8 +696,6 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request)
} else {
// TODO : 세션키와 authtoken을 헤더로 받아서 accid 조회
queryvals := r.URL.Query()
//token := queryvals.Get("token")
token := "" // 더이상 쓰지 않는다.
sk := queryvals.Get("sk")
//if len(token) == 0 || len(sk) == 0 {
@ -617,14 +707,53 @@ func (sh *serviceDescription) ServeHTTP(w http.ResponseWriter, r *http.Request)
// TODO : 각 서버에 있는 자산? 캐릭터 정보를 보여줘야 하나. 뭘 보여줄지는 프로젝트에 문의
// 일단 서버 종류만 내려보내자
// 세션키가 있는지 확인
if _, ok := sh.auths.IsValid(sk, token); !ok {
logger.Println("sessionkey is not valid :", sk, token)
if _, ok := sh.auths.IsValid(sk, ""); !ok {
logger.Println("sessionkey is not valid :", sk)
w.WriteHeader(http.StatusBadRequest)
return
}
divstrptr := atomic.LoadPointer(&sh.divisionsSerialized)
divstr := *(*string)(divstrptr)
w.Write([]byte(divstr))
if divname := queryvals.Get("div"); len(divname) > 0 {
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:
// 점검중인데 일부 권한을 갖고 있는 유저만 들어갈 수 있는 상태
cell := sh.auths.QuerySession(sk, "")
if cell == nil {
logger.Println("sessionkey is not valid :", sk)
w.WriteHeader(http.StatusBadRequest)
return
}
if sh.wl.hasAuth(cell.ToAuthinfo().Email, cell.ToAuthinfo().Platform, whitelistAuthType_QA) {
// qa 권한이면 입장 가능
w.Write([]byte(fmt.Sprintf(`{"service":"%s"}`, div.Url)))
} else if div.Maintenance != nil {
// 권한이 없으므로 공지
w.Write([]byte(fmt.Sprintf(`{"notice":"%s"}`, div.Maintenance.link)))
} else {
logger.Println("div.Maintenance is nil :", divname)
}
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)
}
}
}
} else {
divstrptr := atomic.LoadPointer(&sh.divisionsSerialized)
divstr := *(*string)(divstrptr)
w.Write([]byte(divstr))
}
}
}