增加服务鉴权机制
This commit is contained in:
parent
9485249fe9
commit
3d2499ebad
|
@ -0,0 +1,175 @@
|
||||||
|
package accesstoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/async"
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
|
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeeperEvent interface {
|
||||||
|
IsAccessTokenKeeper() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExitEvent struct {
|
||||||
|
KeeperEvent
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Keeper struct {
|
||||||
|
cfg Config
|
||||||
|
enabled bool
|
||||||
|
token cortypes.UserAccessToken
|
||||||
|
priKey ed25519.PrivateKey
|
||||||
|
lock sync.RWMutex
|
||||||
|
done chan any
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg Config, tempCli *corrpc.TempClient) (*Keeper, error) {
|
||||||
|
loginResp, cerr := tempCli.UserLogin(context.Background(), &corrpc.UserLogin{
|
||||||
|
Account: cfg.Account,
|
||||||
|
Password: cfg.Password,
|
||||||
|
})
|
||||||
|
if cerr != nil {
|
||||||
|
return nil, fmt.Errorf("login: %w", cerr.ToError())
|
||||||
|
}
|
||||||
|
|
||||||
|
priKey, err := hex.DecodeString(loginResp.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("decode private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Keeper{
|
||||||
|
cfg: cfg,
|
||||||
|
enabled: true,
|
||||||
|
token: loginResp.Token,
|
||||||
|
priKey: priKey,
|
||||||
|
done: make(chan any, 1),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDisabled() *Keeper {
|
||||||
|
return &Keeper{
|
||||||
|
done: make(chan any, 1),
|
||||||
|
enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Keeper) Start() *async.UnboundChannel[KeeperEvent] {
|
||||||
|
log := logger.WithField("Mod", "Keeper")
|
||||||
|
|
||||||
|
ch := async.NewUnboundChannel[KeeperEvent]()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if !k.enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
k.lock.RLock()
|
||||||
|
log.Infof("login success, token expires at %v", k.token.ExpiresAt)
|
||||||
|
k.lock.RUnlock()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-k.done:
|
||||||
|
break loop
|
||||||
|
|
||||||
|
case <-ticker.C:
|
||||||
|
k.lock.RLock()
|
||||||
|
token := k.token
|
||||||
|
k.lock.RUnlock()
|
||||||
|
|
||||||
|
// 当前Token已经过期,说明之前的刷新都失败了,打个日志
|
||||||
|
if time.Now().After(token.ExpiresAt) {
|
||||||
|
log.Warnf("token expired at %v !", token.ExpiresAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在Token到期前5分钟时就要开始刷新Token
|
||||||
|
|
||||||
|
tokenDeadline := token.ExpiresAt.Add(-time.Minute * 5)
|
||||||
|
if time.Now().Before(tokenDeadline) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
corCli := stgglb.CoordinatorRPCPool.Get()
|
||||||
|
refResp, cerr := corCli.UserRefreshToken(context.Background(), &corrpc.UserRefreshToken{})
|
||||||
|
if cerr != nil {
|
||||||
|
log.Warnf("refresh token: %v", cerr)
|
||||||
|
corCli.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
priKey, err := hex.DecodeString(refResp.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("decode private key: %v", err)
|
||||||
|
corCli.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("refresh token success, new token expires at %v", refResp.Token.ExpiresAt)
|
||||||
|
|
||||||
|
k.lock.Lock()
|
||||||
|
k.token = refResp.Token
|
||||||
|
k.priKey = priKey
|
||||||
|
k.lock.Unlock()
|
||||||
|
|
||||||
|
corCli.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ch.Send(ExitEvent{})
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Keeper) Stop() {
|
||||||
|
select {
|
||||||
|
case k.done <- true:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Keeper) GetAuthInfo() (rpc.AccessTokenAuthInfo, error) {
|
||||||
|
if !k.enabled {
|
||||||
|
return rpc.AccessTokenAuthInfo{}, fmt.Errorf("function disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
k.lock.RLock()
|
||||||
|
token := k.token
|
||||||
|
k.lock.RUnlock()
|
||||||
|
|
||||||
|
bytes := make([]byte, 8)
|
||||||
|
|
||||||
|
_, err := rand.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return rpc.AccessTokenAuthInfo{}, fmt.Errorf("generate nonce: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := hex.EncodeToString(bytes)
|
||||||
|
stringToSign := accesstoken.MakeStringToSign(token.UserID, token.TokenID, nonce)
|
||||||
|
|
||||||
|
signBytes := ed25519.Sign(k.priKey, []byte(stringToSign))
|
||||||
|
signature := hex.EncodeToString(signBytes)
|
||||||
|
|
||||||
|
return rpc.AccessTokenAuthInfo{
|
||||||
|
UserID: token.UserID,
|
||||||
|
AccessTokenID: token.TokenID,
|
||||||
|
Nonce: nonce,
|
||||||
|
Signature: signature,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package accesstoken
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Account string `json:"account"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
||||||
|
@ -69,8 +70,42 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stgglb.InitLocal(config.Cfg().Local)
|
stgglb.InitLocal(config.Cfg().Local)
|
||||||
stgglb.InitPools(&config.Cfg().HubRPC, &config.Cfg().CoordinatorRPC)
|
|
||||||
stgglb.StandaloneMode = opts.Standalone
|
stgglb.StandaloneMode = opts.Standalone || config.Cfg().AccessToken == nil
|
||||||
|
|
||||||
|
var accToken *accesstoken.Keeper
|
||||||
|
if !stgglb.StandaloneMode {
|
||||||
|
tempCli, err := config.Cfg().CoordinatorRPC.BuildTempClient()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build coordinator rpc temp client: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
accToken, err = accesstoken.New(*config.Cfg().AccessToken, tempCli)
|
||||||
|
tempCli.Release()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("new access token keeper: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
hubRPCCfg, err := config.Cfg().HubRPC.Build(accToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build hub rpc pool config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
corRPCCfg, err := config.Cfg().CoordinatorRPC.Build(accToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build coordinator rpc pool config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stgglb.InitPools(hubRPCCfg, corRPCCfg)
|
||||||
|
} else {
|
||||||
|
accToken = accesstoken.NewDisabled()
|
||||||
|
}
|
||||||
|
accTokenChan := accToken.Start()
|
||||||
|
defer accToken.Stop()
|
||||||
|
|
||||||
// 数据库
|
// 数据库
|
||||||
db, err := db.NewDB(&config.Cfg().DB)
|
db, err := db.NewDB(&config.Cfg().DB)
|
||||||
|
@ -162,6 +197,7 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
|
||||||
|
|
||||||
/// 开始监听各个模块的事件
|
/// 开始监听各个模块的事件
|
||||||
|
|
||||||
|
accTokenEvt := accTokenChan.Receive()
|
||||||
evtPubEvt := evtPubChan.Receive()
|
evtPubEvt := evtPubChan.Receive()
|
||||||
acStatEvt := acStatChan.Receive()
|
acStatEvt := acStatChan.Receive()
|
||||||
replEvt := replCh.Receive()
|
replEvt := replCh.Receive()
|
||||||
|
@ -171,6 +207,23 @@ func serveHTTP(configPath string, opts serveHTTPOptions) {
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case e := <-accTokenEvt.Chan():
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("receive access token event: %v", err)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := e.Value.(type) {
|
||||||
|
case accesstoken.ExitEvent:
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("access token keeper exit with error: %v", err)
|
||||||
|
} else {
|
||||||
|
logger.Info("access token keeper exited")
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
accTokenEvt = accTokenChan.Receive()
|
||||||
|
|
||||||
case e := <-evtPubEvt.Chan():
|
case e := <-evtPubEvt.Chan():
|
||||||
if e.Err != nil {
|
if e.Err != nil {
|
||||||
logger.Errorf("receive publisher event: %v", err)
|
logger.Errorf("receive publisher event: %v", err)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
|
"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
||||||
|
@ -80,7 +81,42 @@ func test(configPath string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stgglb.InitLocal(config.Cfg().Local)
|
stgglb.InitLocal(config.Cfg().Local)
|
||||||
stgglb.InitPools(&config.Cfg().HubRPC, &config.Cfg().CoordinatorRPC)
|
|
||||||
|
stgglb.StandaloneMode = config.Cfg().AccessToken == nil
|
||||||
|
|
||||||
|
var accToken *accesstoken.Keeper
|
||||||
|
if !stgglb.StandaloneMode {
|
||||||
|
tempCli, err := config.Cfg().CoordinatorRPC.BuildTempClient()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build coordinator rpc temp client: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
accToken, err = accesstoken.New(*config.Cfg().AccessToken, tempCli)
|
||||||
|
tempCli.Release()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("new access token keeper: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
hubRPCCfg, err := config.Cfg().HubRPC.Build(accToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build hub rpc pool config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
corRPCCfg, err := config.Cfg().CoordinatorRPC.Build(accToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build coordinator rpc pool config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stgglb.InitPools(hubRPCCfg, corRPCCfg)
|
||||||
|
} else {
|
||||||
|
accToken = accesstoken.NewDisabled()
|
||||||
|
}
|
||||||
|
accTokenChan := accToken.Start()
|
||||||
|
defer accToken.Stop()
|
||||||
|
|
||||||
// 数据库
|
// 数据库
|
||||||
db, err := db.NewDB(&config.Cfg().DB)
|
db, err := db.NewDB(&config.Cfg().DB)
|
||||||
|
@ -140,13 +176,30 @@ func test(configPath string) {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
/// 开始监听各个模块的事件
|
/// 开始监听各个模块的事件
|
||||||
|
accTokenEvt := accTokenChan.Receive()
|
||||||
evtPubEvt := evtPubChan.Receive()
|
evtPubEvt := evtPubChan.Receive()
|
||||||
acStatEvt := acStatChan.Receive()
|
acStatEvt := acStatChan.Receive()
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case e := <-accTokenEvt.Chan():
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("receive access token event: %v", err)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := e.Value.(type) {
|
||||||
|
case accesstoken.ExitEvent:
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("access token keeper exit with error: %v", err)
|
||||||
|
} else {
|
||||||
|
logger.Info("access token keeper exited")
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
accTokenEvt = accTokenChan.Receive()
|
||||||
|
|
||||||
case e := <-evtPubEvt.Chan():
|
case e := <-evtPubEvt.Chan():
|
||||||
if e.Err != nil {
|
if e.Err != nil {
|
||||||
logger.Errorf("receive publisher event: %v", err)
|
logger.Errorf("receive publisher event: %v", err)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accessstat"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
||||||
|
@ -60,7 +61,42 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stgglb.InitLocal(config.Cfg().Local)
|
stgglb.InitLocal(config.Cfg().Local)
|
||||||
stgglb.InitPools(&config.Cfg().HubRPC, &config.Cfg().CoordinatorRPC)
|
|
||||||
|
stgglb.StandaloneMode = opts.Standalone || config.Cfg().AccessToken == nil
|
||||||
|
|
||||||
|
var accToken *accesstoken.Keeper
|
||||||
|
if !opts.Standalone {
|
||||||
|
tempCli, err := config.Cfg().CoordinatorRPC.BuildTempClient()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build coordinator rpc temp client: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
accToken, err = accesstoken.New(*config.Cfg().AccessToken, tempCli)
|
||||||
|
tempCli.Release()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("new access token keeper: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
hubRPCCfg, err := config.Cfg().HubRPC.Build(accToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build hub rpc pool config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
corRPCCfg, err := config.Cfg().CoordinatorRPC.Build(accToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("build coordinator rpc pool config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stgglb.InitPools(hubRPCCfg, corRPCCfg)
|
||||||
|
} else {
|
||||||
|
accToken = accesstoken.NewDisabled()
|
||||||
|
}
|
||||||
|
accTokenChan := accToken.Start()
|
||||||
|
defer accToken.Stop()
|
||||||
|
|
||||||
// 数据库
|
// 数据库
|
||||||
db, err := db.NewDB(&config.Cfg().DB)
|
db, err := db.NewDB(&config.Cfg().DB)
|
||||||
|
@ -152,6 +188,7 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
|
||||||
|
|
||||||
/// 开始监听各个模块的事件
|
/// 开始监听各个模块的事件
|
||||||
|
|
||||||
|
accTokenEvt := accTokenChan.Receive()
|
||||||
evtPubEvt := evtPubChan.Receive()
|
evtPubEvt := evtPubChan.Receive()
|
||||||
acStatEvt := acStatChan.Receive()
|
acStatEvt := acStatChan.Receive()
|
||||||
httpEvt := httpChan.Receive()
|
httpEvt := httpChan.Receive()
|
||||||
|
@ -160,6 +197,23 @@ func vfsTest(configPath string, opts serveHTTPOptions) {
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case e := <-accTokenEvt.Chan():
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("receive access token event: %v", err)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := e.Value.(type) {
|
||||||
|
case accesstoken.ExitEvent:
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("access token keeper exit with error: %v", err)
|
||||||
|
} else {
|
||||||
|
logger.Info("access token keeper exited")
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
accTokenEvt = accTokenChan.Receive()
|
||||||
|
|
||||||
case e := <-evtPubEvt.Chan():
|
case e := <-evtPubEvt.Chan():
|
||||||
if e.Err != nil {
|
if e.Err != nil {
|
||||||
logger.Errorf("receive publisher event: %v", err)
|
logger.Errorf("receive publisher event: %v", err)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
"gitlink.org.cn/cloudream/common/utils/config"
|
"gitlink.org.cn/cloudream/common/utils/config"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader/strategy"
|
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader/strategy"
|
||||||
|
@ -18,8 +19,8 @@ import (
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Local stgglb.LocalMachineInfo `json:"local"`
|
Local stgglb.LocalMachineInfo `json:"local"`
|
||||||
HubRPC hubrpc.PoolConfig `json:"hubRPC"`
|
HubRPC hubrpc.PoolConfigJSON `json:"hubRPC"`
|
||||||
CoordinatorRPC corrpc.PoolConfig `json:"coordinatorRPC"`
|
CoordinatorRPC corrpc.PoolConfigJSON `json:"coordinatorRPC"`
|
||||||
Logger logger.Config `json:"logger"`
|
Logger logger.Config `json:"logger"`
|
||||||
DB db.Config `json:"db"`
|
DB db.Config `json:"db"`
|
||||||
SysEvent sysevent.Config `json:"sysEvent"`
|
SysEvent sysevent.Config `json:"sysEvent"`
|
||||||
|
@ -29,6 +30,7 @@ type Config struct {
|
||||||
TickTock ticktock.Config `json:"tickTock"`
|
TickTock ticktock.Config `json:"tickTock"`
|
||||||
HTTP *http.Config `json:"http"`
|
HTTP *http.Config `json:"http"`
|
||||||
Mount *mntcfg.Config `json:"mount"`
|
Mount *mntcfg.Config `json:"mount"`
|
||||||
|
AccessToken *accesstoken.Config `json:"accessToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg Config
|
var cfg Config
|
||||||
|
|
|
@ -5,9 +5,16 @@
|
||||||
"externalIP": "127.0.0.1",
|
"externalIP": "127.0.0.1",
|
||||||
"locationID": 1
|
"locationID": 1
|
||||||
},
|
},
|
||||||
"hubRPC": {},
|
"hubRPC": {
|
||||||
|
"rootCA": "",
|
||||||
|
"clientCert": "",
|
||||||
|
"clientKey": ""
|
||||||
|
},
|
||||||
"coordinatorRPC": {
|
"coordinatorRPC": {
|
||||||
"address": "127.0.0.1:5009"
|
"address": "127.0.0.1:5009",
|
||||||
|
"rootCA": "",
|
||||||
|
"clientCert": "",
|
||||||
|
"clientKey": ""
|
||||||
},
|
},
|
||||||
"logger": {
|
"logger": {
|
||||||
"output": "stdout",
|
"output": "stdout",
|
||||||
|
@ -62,5 +69,9 @@
|
||||||
"cacheActiveTime": "1m",
|
"cacheActiveTime": "1m",
|
||||||
"cacheExpireTime": "1m",
|
"cacheExpireTime": "1m",
|
||||||
"scanDataDirInterval": "10m"
|
"scanDataDirInterval": "10m"
|
||||||
|
},
|
||||||
|
"accessToken": {
|
||||||
|
"account": "",
|
||||||
|
"password": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,14 @@
|
||||||
"hubUnavailableTime": "20s"
|
"hubUnavailableTime": "20s"
|
||||||
},
|
},
|
||||||
"rpc": {
|
"rpc": {
|
||||||
"listen": "127.0.0.1:5009"
|
"listen": "127.0.0.1:5009",
|
||||||
|
"rootCA": "",
|
||||||
|
"serverCert": "",
|
||||||
|
"serverKey": ""
|
||||||
|
},
|
||||||
|
"hubRPC": {
|
||||||
|
"rootCA": "",
|
||||||
|
"clientCert": "",
|
||||||
|
"clientKey": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
"logger": {
|
|
||||||
"output": "file",
|
|
||||||
"outputFileName": "datamap",
|
|
||||||
"outputDirectory": "log",
|
|
||||||
"level": "debug"
|
|
||||||
},
|
|
||||||
"db": {
|
|
||||||
"address": "106.75.6.194:3306",
|
|
||||||
"account": "root",
|
|
||||||
"password": "cloudream123456",
|
|
||||||
"databaseName": "cloudream"
|
|
||||||
},
|
|
||||||
"rabbitMQ": {
|
|
||||||
"address": "106.75.6.194:5672",
|
|
||||||
"account": "cloudream",
|
|
||||||
"password": "123456",
|
|
||||||
"vhost": "/",
|
|
||||||
"param": {
|
|
||||||
"retryNum": 5,
|
|
||||||
"retryInterval": 5000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,13 +6,24 @@
|
||||||
"locationID": 1
|
"locationID": 1
|
||||||
},
|
},
|
||||||
"rpc": {
|
"rpc": {
|
||||||
"listen": "127.0.0.1:5010"
|
"listen": "127.0.0.1:5010",
|
||||||
|
"rootCA": "",
|
||||||
|
"serverCert": "",
|
||||||
|
"serverKey": ""
|
||||||
},
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"listen": "127.0.0.1:5110"
|
"listen": "127.0.0.1:5110"
|
||||||
},
|
},
|
||||||
"coordinatorRPC": {
|
"coordinatorRPC": {
|
||||||
"address": "127.0.0.1:5009"
|
"address": "127.0.0.1:5009",
|
||||||
|
"rootCA": "",
|
||||||
|
"clientCert": "",
|
||||||
|
"clientKey": ""
|
||||||
|
},
|
||||||
|
"hubRPC": {
|
||||||
|
"rootCA": "",
|
||||||
|
"clientCert": "",
|
||||||
|
"clientKey": ""
|
||||||
},
|
},
|
||||||
"logger": {
|
"logger": {
|
||||||
"output": "file",
|
"output": "file",
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
package accesstoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/async"
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CacheEvent interface {
|
||||||
|
IsAccessTokenCacheEvent() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExitEvent struct {
|
||||||
|
CacheEvent
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CacheKey struct {
|
||||||
|
UserID cortypes.UserID
|
||||||
|
TokenID cortypes.AccessTokenID
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrTokenNotFound = fmt.Errorf("token not found")
|
||||||
|
|
||||||
|
type AccessTokenLoader func(key CacheKey) (cortypes.UserAccessToken, error)
|
||||||
|
|
||||||
|
type CacheEntry struct {
|
||||||
|
IsTokenValid bool
|
||||||
|
Token cortypes.UserAccessToken
|
||||||
|
PublicKey ed25519.PublicKey
|
||||||
|
LoadedAt time.Time
|
||||||
|
LastUsedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
lock sync.Mutex
|
||||||
|
cache map[CacheKey]*CacheEntry
|
||||||
|
done chan any
|
||||||
|
loader AccessTokenLoader
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(loader AccessTokenLoader) *Cache {
|
||||||
|
return &Cache{
|
||||||
|
cache: make(map[CacheKey]*CacheEntry),
|
||||||
|
done: make(chan any, 1),
|
||||||
|
loader: loader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nc *Cache) Start() *async.UnboundChannel[CacheEvent] {
|
||||||
|
log := logger.WithField("Mod", "AccessTokenCache")
|
||||||
|
|
||||||
|
ch := async.NewUnboundChannel[CacheEvent]()
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(time.Second * 10)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-nc.done:
|
||||||
|
break loop
|
||||||
|
|
||||||
|
case <-ticker.C:
|
||||||
|
nc.lock.Lock()
|
||||||
|
for key, entry := range nc.cache {
|
||||||
|
if !entry.IsTokenValid {
|
||||||
|
// 无效Token的记录5分钟后删除
|
||||||
|
if time.Since(entry.LoadedAt) > time.Minute*5 {
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Infof("delete expired invalid token")
|
||||||
|
delete(nc.cache, key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 5分钟没有使用的Token则删除
|
||||||
|
if time.Since(entry.LastUsedAt) > time.Minute*5 {
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Infof("delete unused token")
|
||||||
|
delete(nc.cache, key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过期Token标记为无效
|
||||||
|
if time.Now().After(entry.Token.ExpiresAt) {
|
||||||
|
entry.IsTokenValid = false
|
||||||
|
entry.LastUsedAt = time.Now()
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Infof("token expired")
|
||||||
|
|
||||||
|
} else if time.Since(entry.LoadedAt) > time.Minute*5 {
|
||||||
|
// 依然有效的Token,则5分钟检查一次有效性
|
||||||
|
go nc.load(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nc.lock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.Send(&ExitEvent{})
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *Cache) Stop() {
|
||||||
|
select {
|
||||||
|
case mc.done <- true:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *Cache) Get(key CacheKey) (*CacheEntry, bool) {
|
||||||
|
var ret *CacheEntry
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
mc.lock.Lock()
|
||||||
|
entry, getOk := mc.cache[key]
|
||||||
|
|
||||||
|
if getOk {
|
||||||
|
ret = entry
|
||||||
|
ok = true
|
||||||
|
ret.LastUsedAt = time.Now()
|
||||||
|
|
||||||
|
// 如果Token已经过期,则直接设置为无效Token。因为Token是随机生成的,几乎不可能把一个过期的Token再用上
|
||||||
|
if entry.IsTokenValid && time.Now().After(entry.Token.ExpiresAt) {
|
||||||
|
entry.IsTokenValid = false
|
||||||
|
entry.LastUsedAt = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.lock.Unlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.load(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *Cache) NotifyTokenInvalid(key CacheKey) {
|
||||||
|
log := logger.WithField("Mod", "AccessTokenCache")
|
||||||
|
|
||||||
|
mc.lock.Lock()
|
||||||
|
defer mc.lock.Unlock()
|
||||||
|
|
||||||
|
entry, ok := mc.cache[key]
|
||||||
|
if !ok {
|
||||||
|
entry = &CacheEntry{
|
||||||
|
IsTokenValid: false,
|
||||||
|
LoadedAt: time.Now(),
|
||||||
|
LastUsedAt: time.Now(),
|
||||||
|
}
|
||||||
|
mc.cache[key] = entry
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.IsTokenValid = false
|
||||||
|
entry.LastUsedAt = time.Now()
|
||||||
|
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Infof("notify token invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *Cache) load(key CacheKey) {
|
||||||
|
log := logger.WithField("Mod", "AccessTokenCache")
|
||||||
|
|
||||||
|
loadToken, cerr := mc.loader(key)
|
||||||
|
|
||||||
|
mc.lock.Lock()
|
||||||
|
defer mc.lock.Unlock()
|
||||||
|
|
||||||
|
if cerr != nil {
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Warnf("load token: %v", cerr)
|
||||||
|
|
||||||
|
// 明确是无效的Token的也缓存一下,用于快速拒绝请求
|
||||||
|
if cerr == ErrTokenNotFound {
|
||||||
|
mc.cache[key] = &CacheEntry{
|
||||||
|
IsTokenValid: false,
|
||||||
|
LoadedAt: time.Now(),
|
||||||
|
LastUsedAt: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, err := hex.DecodeString(loadToken.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Warnf("invalid public key: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.cache[key] = &CacheEntry{
|
||||||
|
IsTokenValid: true,
|
||||||
|
Token: loadToken,
|
||||||
|
PublicKey: pubKey,
|
||||||
|
LoadedAt: time.Now(),
|
||||||
|
LastUsedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithField("UserID", key.UserID).WithField("TokenID", key.TokenID).Infof("load token success, expires at: %v", loadToken.ExpiresAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *Cache) Verify(authInfo rpc.AccessTokenAuthInfo) bool {
|
||||||
|
token, ok := mc.Get(CacheKey{
|
||||||
|
UserID: authInfo.UserID,
|
||||||
|
TokenID: authInfo.AccessTokenID,
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !token.IsTokenValid {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := hex.DecodeString(authInfo.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ed25519.Verify(token.PublicKey, []byte(MakeStringToSign(authInfo.UserID, authInfo.AccessTokenID, authInfo.Nonce)), []byte(sig))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeStringToSign(userID cortypes.UserID, tokenID cortypes.AccessTokenID, nonce string) string {
|
||||||
|
return fmt.Sprintf("%v.%v.%v", userID, tokenID, nonce)
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ClientAPISNIV1 = "rpc.client.jcs-pub.v1"
|
||||||
|
InternalAPISNIV1 = "rpc.internal.jcs-pub.v1"
|
||||||
|
|
||||||
|
MetaUserID = "x-jcs-user-id"
|
||||||
|
MetaAccessTokenID = "x-jcs-access-token-id"
|
||||||
|
MetaNonce = "x-jcs-nonce"
|
||||||
|
MetaSignature = "x-jcs-signature"
|
||||||
|
MetaTokenAuthInfo = "x-jcs-token-auth-info"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccessTokenAuthInfo struct {
|
||||||
|
UserID cortypes.UserID
|
||||||
|
AccessTokenID cortypes.AccessTokenID
|
||||||
|
Nonce string
|
||||||
|
Signature string
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessTokenVerifier interface {
|
||||||
|
Verify(authInfo AccessTokenAuthInfo) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessTokenProvider interface {
|
||||||
|
GetAuthInfo() (AccessTokenAuthInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerBase) tlsConfigSelector(hello *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||||
|
switch hello.ServerName {
|
||||||
|
case ClientAPISNIV1:
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{s.serverCert},
|
||||||
|
ClientAuth: tls.NoClientCert,
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
}, nil
|
||||||
|
case InternalAPISNIV1:
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{s.serverCert},
|
||||||
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
|
ClientCAs: s.rootCA,
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown server name: %s", hello.ServerName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerBase) authUnary(
|
||||||
|
ctx context.Context,
|
||||||
|
req interface{},
|
||||||
|
info *grpc.UnaryServerInfo,
|
||||||
|
handler grpc.UnaryHandler,
|
||||||
|
|
||||||
|
) (resp any, err error) {
|
||||||
|
pr, ok := peer.FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "no peer found in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsInfo, ok := pr.AuthInfo.(credentials.TLSInfo)
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "no tls info found in peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是使用interanl ServerName通过的TLS认证,则直接放行
|
||||||
|
if tlsInfo.State.ServerName == InternalAPISNIV1 {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是无需认证的API,则直接放行
|
||||||
|
if s.noAuthAPIs[info.FullMethod] {
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则要进行额外的Token认证
|
||||||
|
|
||||||
|
if !s.accessTokenAuthAPIs[info.FullMethod] {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "unauthorized access")
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "no metadata found in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDs := meta.Get(MetaUserID)
|
||||||
|
if len(userIDs) != 1 {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "missing or multiple user ids in metadata")
|
||||||
|
}
|
||||||
|
userID, err := strconv.ParseInt(userIDs[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "invalid user id in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
accessTokenIDs := meta.Get(MetaAccessTokenID)
|
||||||
|
if len(accessTokenIDs) != 1 {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "missing or multiple access token ids in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := meta.Get(MetaNonce)
|
||||||
|
if len(nonce) != 1 {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "missing or multiple nonces in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
signature := meta.Get(MetaSignature)
|
||||||
|
if len(signature) != 1 {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "missing or multiple signatures in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
authInfo := AccessTokenAuthInfo{
|
||||||
|
UserID: cortypes.UserID(userID),
|
||||||
|
AccessTokenID: cortypes.AccessTokenID(accessTokenIDs[0]),
|
||||||
|
Nonce: nonce[0],
|
||||||
|
Signature: signature[0],
|
||||||
|
}
|
||||||
|
if !s.tokenVerifier.Verify(authInfo) {
|
||||||
|
return nil, status.Error(codes.Unauthenticated, "invalid access token")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, MetaTokenAuthInfo, authInfo)
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ServerBase) authStream(
|
||||||
|
srv any,
|
||||||
|
stream grpc.ServerStream,
|
||||||
|
info *grpc.StreamServerInfo,
|
||||||
|
handler grpc.StreamHandler,
|
||||||
|
) error {
|
||||||
|
pr, ok := peer.FromContext(stream.Context())
|
||||||
|
if !ok {
|
||||||
|
return status.Error(codes.Unauthenticated, "no peer found in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsInfo, ok := pr.AuthInfo.(credentials.TLSInfo)
|
||||||
|
if !ok {
|
||||||
|
return status.Error(codes.Unauthenticated, "no tls info found in peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是使用interanl ServerName通过的TLS认证,则直接放行
|
||||||
|
if tlsInfo.State.ServerName == InternalAPISNIV1 {
|
||||||
|
return handler(srv, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是无需认证的API,则直接放行
|
||||||
|
if s.noAuthAPIs[info.FullMethod] {
|
||||||
|
return handler(srv, stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则要进行额外的Token认证
|
||||||
|
|
||||||
|
if !s.accessTokenAuthAPIs[info.FullMethod] {
|
||||||
|
return status.Error(codes.Unauthenticated, "unauthorized access")
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, ok := metadata.FromIncomingContext(stream.Context())
|
||||||
|
if !ok {
|
||||||
|
return status.Error(codes.Unauthenticated, "no metadata found in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDs := meta.Get(MetaUserID)
|
||||||
|
if len(userIDs) != 1 {
|
||||||
|
return status.Error(codes.Unauthenticated, "missing or multiple user ids in metadata")
|
||||||
|
}
|
||||||
|
userID, err := strconv.ParseInt(userIDs[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return status.Error(codes.Unauthenticated, "invalid user id in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
accessTokenIDs := meta.Get(MetaAccessTokenID)
|
||||||
|
if len(accessTokenIDs) != 1 {
|
||||||
|
return status.Error(codes.Unauthenticated, "missing or multiple access token ids in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := meta.Get(MetaNonce)
|
||||||
|
if len(nonce) != 1 {
|
||||||
|
return status.Error(codes.Unauthenticated, "missing or multiple nonces in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
signature := meta.Get(MetaSignature)
|
||||||
|
if len(signature) != 1 {
|
||||||
|
return status.Error(codes.Unauthenticated, "missing or multiple signatures in metadata")
|
||||||
|
}
|
||||||
|
|
||||||
|
authInfo := AccessTokenAuthInfo{
|
||||||
|
UserID: cortypes.UserID(userID),
|
||||||
|
AccessTokenID: cortypes.AccessTokenID(accessTokenIDs[0]),
|
||||||
|
Nonce: nonce[0],
|
||||||
|
Signature: signature[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.tokenVerifier.Verify(authInfo) {
|
||||||
|
return status.Error(codes.Unauthenticated, "invalid access token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler(srv, &serverStream{stream, context.WithValue(stream.Context(), MetaTokenAuthInfo, authInfo)})
|
||||||
|
}
|
||||||
|
|
||||||
|
type serverStream struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *serverStream) Context() context.Context {
|
||||||
|
return s.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAuthInfo(ctx context.Context) (AccessTokenAuthInfo, bool) {
|
||||||
|
val := ctx.Value(MetaTokenAuthInfo)
|
||||||
|
if val == nil {
|
||||||
|
return AccessTokenAuthInfo{}, false
|
||||||
|
}
|
||||||
|
authInfo, ok := val.(AccessTokenAuthInfo)
|
||||||
|
return authInfo, ok
|
||||||
|
}
|
|
@ -14,9 +14,17 @@ type Client struct {
|
||||||
|
|
||||||
func (c *Client) Release() {
|
func (c *Client) Release() {
|
||||||
if c.con != nil {
|
if c.con != nil {
|
||||||
c.pool.release()
|
c.pool.connPool.Release(c.pool.cfg.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TempClient struct {
|
||||||
|
Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TempClient) Release() {
|
||||||
|
c.con.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// 客户端的API要和服务端的API保持一致
|
// 客户端的API要和服务端的API保持一致
|
||||||
var _ CoordinatorAPI = (*Client)(nil)
|
var _ CoordinatorAPI = (*Client)(nil)
|
||||||
|
|
|
@ -27,7 +27,7 @@ var file_pkgs_rpc_coordinator_coordinator_proto_rawDesc = []byte{
|
||||||
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74,
|
0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74,
|
||||||
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x72, 0x72, 0x70, 0x63,
|
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x72, 0x72, 0x70, 0x63,
|
||||||
0x1a, 0x12, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x2e, 0x70,
|
0x1a, 0x12, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x32, 0xfe, 0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e,
|
0x72, 0x6f, 0x74, 0x6f, 0x32, 0xb7, 0x03, 0x0a, 0x0b, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e,
|
||||||
0x61, 0x74, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x48, 0x75, 0x62, 0x43, 0x6f,
|
0x61, 0x74, 0x6f, 0x72, 0x12, 0x2b, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x48, 0x75, 0x62, 0x43, 0x6f,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
|
@ -43,11 +43,23 @@ var file_pkgs_rpc_coordinator_coordinator_proto_rawDesc = []byte{
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x10, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x74,
|
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x10, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x74,
|
||||||
0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x75, 0x62, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52,
|
0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x75, 0x62, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73,
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x6c, 0x69, 0x6e, 0x6b,
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x6f, 0x67,
|
||||||
0x2e, 0x6f, 0x72, 0x67, 0x2e, 0x63, 0x6e, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x72, 0x65, 0x61,
|
0x69, 0x6e, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x6d, 0x2f, 0x6a, 0x63, 0x73, 0x2d, 0x70, 0x75, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||||
0x2f, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x72, 0x72, 0x70, 0x63,
|
0x2f, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f,
|
||||||
0x3b, 0x63, 0x6f, 0x72, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x6b, 0x65, 0x6e, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
|
0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
|
0x12, 0x29, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x0c,
|
||||||
|
0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72,
|
||||||
|
0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x12, 0x48,
|
||||||
|
0x75, 0x62, 0x4c, 0x6f, 0x61, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65,
|
||||||
|
0x6e, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x40,
|
||||||
|
0x5a, 0x3e, 0x67, 0x69, 0x74, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x6f, 0x72, 0x67, 0x2e, 0x63, 0x6e,
|
||||||
|
0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x72, 0x65, 0x61, 0x6d, 0x2f, 0x6a, 0x63, 0x73, 0x2d, 0x70,
|
||||||
|
0x75, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72,
|
||||||
|
0x70, 0x63, 0x2f, 0x63, 0x6f, 0x72, 0x72, 0x70, 0x63, 0x3b, 0x63, 0x6f, 0x72, 0x72, 0x70, 0x63,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_pkgs_rpc_coordinator_coordinator_proto_goTypes = []any{
|
var file_pkgs_rpc_coordinator_coordinator_proto_goTypes = []any{
|
||||||
|
@ -60,13 +72,21 @@ var file_pkgs_rpc_coordinator_coordinator_proto_depIdxs = []int32{
|
||||||
0, // 2: corrpc.Coordinator.GetHubConnectivities:input_type -> rpc.Request
|
0, // 2: corrpc.Coordinator.GetHubConnectivities:input_type -> rpc.Request
|
||||||
0, // 3: corrpc.Coordinator.ReportHubConnectivity:input_type -> rpc.Request
|
0, // 3: corrpc.Coordinator.ReportHubConnectivity:input_type -> rpc.Request
|
||||||
0, // 4: corrpc.Coordinator.SelectStorageHub:input_type -> rpc.Request
|
0, // 4: corrpc.Coordinator.SelectStorageHub:input_type -> rpc.Request
|
||||||
1, // 5: corrpc.Coordinator.GetHubConfig:output_type -> rpc.Response
|
0, // 5: corrpc.Coordinator.UserLogin:input_type -> rpc.Request
|
||||||
1, // 6: corrpc.Coordinator.GetHubs:output_type -> rpc.Response
|
0, // 6: corrpc.Coordinator.UserRefreshToken:input_type -> rpc.Request
|
||||||
1, // 7: corrpc.Coordinator.GetHubConnectivities:output_type -> rpc.Response
|
0, // 7: corrpc.Coordinator.UserLogout:input_type -> rpc.Request
|
||||||
1, // 8: corrpc.Coordinator.ReportHubConnectivity:output_type -> rpc.Response
|
0, // 8: corrpc.Coordinator.HubLoadAccessToken:input_type -> rpc.Request
|
||||||
1, // 9: corrpc.Coordinator.SelectStorageHub:output_type -> rpc.Response
|
1, // 9: corrpc.Coordinator.GetHubConfig:output_type -> rpc.Response
|
||||||
5, // [5:10] is the sub-list for method output_type
|
1, // 10: corrpc.Coordinator.GetHubs:output_type -> rpc.Response
|
||||||
0, // [0:5] is the sub-list for method input_type
|
1, // 11: corrpc.Coordinator.GetHubConnectivities:output_type -> rpc.Response
|
||||||
|
1, // 12: corrpc.Coordinator.ReportHubConnectivity:output_type -> rpc.Response
|
||||||
|
1, // 13: corrpc.Coordinator.SelectStorageHub:output_type -> rpc.Response
|
||||||
|
1, // 14: corrpc.Coordinator.UserLogin:output_type -> rpc.Response
|
||||||
|
1, // 15: corrpc.Coordinator.UserRefreshToken:output_type -> rpc.Response
|
||||||
|
1, // 16: corrpc.Coordinator.UserLogout:output_type -> rpc.Response
|
||||||
|
1, // 17: corrpc.Coordinator.HubLoadAccessToken:output_type -> rpc.Response
|
||||||
|
9, // [9:18] is the sub-list for method output_type
|
||||||
|
0, // [0:9] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
0, // [0:0] is the sub-list for field type_name
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
|
|
@ -14,4 +14,9 @@ service Coordinator {
|
||||||
rpc ReportHubConnectivity(rpc.Request) returns(rpc.Response);
|
rpc ReportHubConnectivity(rpc.Request) returns(rpc.Response);
|
||||||
|
|
||||||
rpc SelectStorageHub(rpc.Request) returns(rpc.Response);
|
rpc SelectStorageHub(rpc.Request) returns(rpc.Response);
|
||||||
|
|
||||||
|
rpc UserLogin(rpc.Request) returns(rpc.Response);
|
||||||
|
rpc UserRefreshToken(rpc.Request) returns(rpc.Response);
|
||||||
|
rpc UserLogout(rpc.Request) returns(rpc.Response);
|
||||||
|
rpc HubLoadAccessToken(rpc.Request) returns(rpc.Response);
|
||||||
}
|
}
|
|
@ -25,6 +25,10 @@ const (
|
||||||
Coordinator_GetHubConnectivities_FullMethodName = "/corrpc.Coordinator/GetHubConnectivities"
|
Coordinator_GetHubConnectivities_FullMethodName = "/corrpc.Coordinator/GetHubConnectivities"
|
||||||
Coordinator_ReportHubConnectivity_FullMethodName = "/corrpc.Coordinator/ReportHubConnectivity"
|
Coordinator_ReportHubConnectivity_FullMethodName = "/corrpc.Coordinator/ReportHubConnectivity"
|
||||||
Coordinator_SelectStorageHub_FullMethodName = "/corrpc.Coordinator/SelectStorageHub"
|
Coordinator_SelectStorageHub_FullMethodName = "/corrpc.Coordinator/SelectStorageHub"
|
||||||
|
Coordinator_UserLogin_FullMethodName = "/corrpc.Coordinator/UserLogin"
|
||||||
|
Coordinator_UserRefreshToken_FullMethodName = "/corrpc.Coordinator/UserRefreshToken"
|
||||||
|
Coordinator_UserLogout_FullMethodName = "/corrpc.Coordinator/UserLogout"
|
||||||
|
Coordinator_HubLoadAccessToken_FullMethodName = "/corrpc.Coordinator/HubLoadAccessToken"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CoordinatorClient is the client API for Coordinator service.
|
// CoordinatorClient is the client API for Coordinator service.
|
||||||
|
@ -36,6 +40,10 @@ type CoordinatorClient interface {
|
||||||
GetHubConnectivities(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
GetHubConnectivities(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
ReportHubConnectivity(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
ReportHubConnectivity(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
SelectStorageHub(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
SelectStorageHub(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
|
UserLogin(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
|
UserRefreshToken(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
|
UserLogout(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
|
HubLoadAccessToken(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type coordinatorClient struct {
|
type coordinatorClient struct {
|
||||||
|
@ -91,6 +99,42 @@ func (c *coordinatorClient) SelectStorageHub(ctx context.Context, in *rpc.Reques
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *coordinatorClient) UserLogin(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error) {
|
||||||
|
out := new(rpc.Response)
|
||||||
|
err := c.cc.Invoke(ctx, Coordinator_UserLogin_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *coordinatorClient) UserRefreshToken(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error) {
|
||||||
|
out := new(rpc.Response)
|
||||||
|
err := c.cc.Invoke(ctx, Coordinator_UserRefreshToken_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *coordinatorClient) UserLogout(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error) {
|
||||||
|
out := new(rpc.Response)
|
||||||
|
err := c.cc.Invoke(ctx, Coordinator_UserLogout_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *coordinatorClient) HubLoadAccessToken(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error) {
|
||||||
|
out := new(rpc.Response)
|
||||||
|
err := c.cc.Invoke(ctx, Coordinator_HubLoadAccessToken_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CoordinatorServer is the server API for Coordinator service.
|
// CoordinatorServer is the server API for Coordinator service.
|
||||||
// All implementations must embed UnimplementedCoordinatorServer
|
// All implementations must embed UnimplementedCoordinatorServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
|
@ -100,6 +144,10 @@ type CoordinatorServer interface {
|
||||||
GetHubConnectivities(context.Context, *rpc.Request) (*rpc.Response, error)
|
GetHubConnectivities(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
ReportHubConnectivity(context.Context, *rpc.Request) (*rpc.Response, error)
|
ReportHubConnectivity(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
SelectStorageHub(context.Context, *rpc.Request) (*rpc.Response, error)
|
SelectStorageHub(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
|
UserLogin(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
|
UserRefreshToken(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
|
UserLogout(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
|
HubLoadAccessToken(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
mustEmbedUnimplementedCoordinatorServer()
|
mustEmbedUnimplementedCoordinatorServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +170,18 @@ func (UnimplementedCoordinatorServer) ReportHubConnectivity(context.Context, *rp
|
||||||
func (UnimplementedCoordinatorServer) SelectStorageHub(context.Context, *rpc.Request) (*rpc.Response, error) {
|
func (UnimplementedCoordinatorServer) SelectStorageHub(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SelectStorageHub not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method SelectStorageHub not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedCoordinatorServer) UserLogin(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UserLogin not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedCoordinatorServer) UserRefreshToken(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UserRefreshToken not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedCoordinatorServer) UserLogout(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method UserLogout not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedCoordinatorServer) HubLoadAccessToken(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method HubLoadAccessToken not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedCoordinatorServer) mustEmbedUnimplementedCoordinatorServer() {}
|
func (UnimplementedCoordinatorServer) mustEmbedUnimplementedCoordinatorServer() {}
|
||||||
|
|
||||||
// UnsafeCoordinatorServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeCoordinatorServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
@ -225,6 +285,78 @@ func _Coordinator_SelectStorageHub_Handler(srv interface{}, ctx context.Context,
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Coordinator_UserLogin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(rpc.Request)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(CoordinatorServer).UserLogin(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Coordinator_UserLogin_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(CoordinatorServer).UserLogin(ctx, req.(*rpc.Request))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Coordinator_UserRefreshToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(rpc.Request)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(CoordinatorServer).UserRefreshToken(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Coordinator_UserRefreshToken_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(CoordinatorServer).UserRefreshToken(ctx, req.(*rpc.Request))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Coordinator_UserLogout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(rpc.Request)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(CoordinatorServer).UserLogout(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Coordinator_UserLogout_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(CoordinatorServer).UserLogout(ctx, req.(*rpc.Request))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Coordinator_HubLoadAccessToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(rpc.Request)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(CoordinatorServer).HubLoadAccessToken(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Coordinator_HubLoadAccessToken_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(CoordinatorServer).HubLoadAccessToken(ctx, req.(*rpc.Request))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// Coordinator_ServiceDesc is the grpc.ServiceDesc for Coordinator service.
|
// Coordinator_ServiceDesc is the grpc.ServiceDesc for Coordinator service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
@ -252,6 +384,22 @@ var Coordinator_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "SelectStorageHub",
|
MethodName: "SelectStorageHub",
|
||||||
Handler: _Coordinator_SelectStorageHub_Handler,
|
Handler: _Coordinator_SelectStorageHub_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "UserLogin",
|
||||||
|
Handler: _Coordinator_UserLogin_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "UserRefreshToken",
|
||||||
|
Handler: _Coordinator_UserRefreshToken_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "UserLogout",
|
||||||
|
Handler: _Coordinator_UserLogout_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "HubLoadAccessToken",
|
||||||
|
Handler: _Coordinator_HubLoadAccessToken_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "pkgs/rpc/coordinator/coordinator.proto",
|
Metadata: "pkgs/rpc/coordinator/coordinator.proto",
|
||||||
|
|
|
@ -1,100 +1,115 @@
|
||||||
package corrpc
|
package corrpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"crypto/tls"
|
||||||
"time"
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
grpc "google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PoolConfig struct {
|
type PoolConfig struct {
|
||||||
Address string `json:"address"`
|
Address string
|
||||||
|
Conn rpc.PoolConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type PoolConfigJSON struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
RootCA string `json:"rootCA"`
|
||||||
|
ClientCert string `json:"clientCert"`
|
||||||
|
ClientKey string `json:"clientKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PoolConfigJSON) Build(tokenProv rpc.AccessTokenProvider) (*PoolConfig, error) {
|
||||||
|
pc := &PoolConfig{
|
||||||
|
Address: c.Address,
|
||||||
|
}
|
||||||
|
pc.Conn.AccessTokenProvider = tokenProv
|
||||||
|
|
||||||
|
rootCA, err := os.ReadFile(c.RootCA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load root ca: %v", err)
|
||||||
|
}
|
||||||
|
pc.Conn.RootCA = x509.NewCertPool()
|
||||||
|
if !pc.Conn.RootCA.AppendCertsFromPEM(rootCA) {
|
||||||
|
return nil, fmt.Errorf("failed to parse root ca")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ClientCert != "" && c.ClientKey != "" {
|
||||||
|
cert, err := tls.LoadX509KeyPair(c.ClientCert, c.ClientKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load client cert: %v", err)
|
||||||
|
}
|
||||||
|
pc.Conn.ClientCert = &cert
|
||||||
|
} else if tokenProv == nil {
|
||||||
|
return nil, fmt.Errorf("must provide client cert or access token provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PoolConfigJSON) BuildTempClient() (*TempClient, error) {
|
||||||
|
rootCA, err := os.ReadFile(c.RootCA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load root ca: %v", err)
|
||||||
|
}
|
||||||
|
rootCAs := x509.NewCertPool()
|
||||||
|
if !rootCAs.AppendCertsFromPEM(rootCA) {
|
||||||
|
return nil, fmt.Errorf("failed to parse root ca")
|
||||||
|
}
|
||||||
|
|
||||||
|
gcon, err := grpc.NewClient(c.Address,
|
||||||
|
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||||
|
RootCAs: rootCAs,
|
||||||
|
ServerName: rpc.ClientAPISNIV1,
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TempClient{
|
||||||
|
Client: Client{
|
||||||
|
con: gcon,
|
||||||
|
cli: NewCoordinatorClient(gcon),
|
||||||
|
pool: nil,
|
||||||
|
fusedErr: nil,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
cfg PoolConfig
|
cfg PoolConfig
|
||||||
grpcCon *grpcCon
|
connPool *rpc.ConnPool
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type grpcCon struct {
|
|
||||||
grpcCon *grpc.ClientConn
|
|
||||||
refCount int
|
|
||||||
stopClosing chan any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPool(cfg PoolConfig) *Pool {
|
func NewPool(cfg PoolConfig) *Pool {
|
||||||
return &Pool{
|
return &Pool{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
connPool: rpc.NewConnPool(cfg.Conn),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) Get() *Client {
|
func (p *Pool) Get() *Client {
|
||||||
p.lock.Lock()
|
con, err := p.connPool.GetConnection(p.cfg.Address)
|
||||||
defer p.lock.Unlock()
|
if err != nil {
|
||||||
|
return &Client{
|
||||||
con := p.grpcCon
|
con: nil,
|
||||||
if con == nil {
|
cli: nil,
|
||||||
gcon, err := grpc.NewClient(p.cfg.Address, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
pool: p,
|
||||||
if err != nil {
|
fusedErr: rpc.Failed(errorcode.OperationFailed, err.Error()),
|
||||||
return &Client{
|
|
||||||
con: nil,
|
|
||||||
pool: p,
|
|
||||||
fusedErr: rpc.Failed(errorcode.OperationFailed, err.Error()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
con = &grpcCon{
|
|
||||||
grpcCon: gcon,
|
|
||||||
refCount: 0,
|
|
||||||
stopClosing: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
p.grpcCon = con
|
|
||||||
|
|
||||||
} else if con.stopClosing != nil {
|
|
||||||
close(con.stopClosing)
|
|
||||||
con.stopClosing = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
con.refCount++
|
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
con: con.grpcCon,
|
con: con,
|
||||||
cli: NewCoordinatorClient(con.grpcCon),
|
cli: NewCoordinatorClient(con),
|
||||||
pool: p,
|
pool: p,
|
||||||
}
|
fusedErr: nil,
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) release() {
|
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
grpcCon := p.grpcCon
|
|
||||||
grpcCon.refCount--
|
|
||||||
grpcCon.refCount = max(grpcCon.refCount, 0)
|
|
||||||
|
|
||||||
if grpcCon.refCount == 0 {
|
|
||||||
stopClosing := make(chan any)
|
|
||||||
grpcCon.stopClosing = stopClosing
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-stopClosing:
|
|
||||||
return
|
|
||||||
|
|
||||||
case <-time.After(time.Minute):
|
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
if p.grpcCon.refCount == 0 {
|
|
||||||
p.grpcCon.grpcCon.Close()
|
|
||||||
p.grpcCon = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
type CoordinatorAPI interface {
|
type CoordinatorAPI interface {
|
||||||
HubService
|
HubService
|
||||||
StorageService
|
StorageService
|
||||||
|
UserService
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
@ -15,12 +16,26 @@ type Server struct {
|
||||||
svrImpl CoordinatorAPI
|
svrImpl CoordinatorAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(cfg rpc.Config, impl CoordinatorAPI) *Server {
|
func NewServer(cfg rpc.Config, impl CoordinatorAPI, tokenVerifier rpc.AccessTokenVerifier) *Server {
|
||||||
svr := &Server{
|
svr := &Server{
|
||||||
svrImpl: impl,
|
svrImpl: impl,
|
||||||
}
|
}
|
||||||
svr.ServerBase = rpc.NewServerBase(cfg, svr, &Coordinator_ServiceDesc)
|
svr.ServerBase = rpc.NewServerBase(cfg, svr, &Coordinator_ServiceDesc, tokenAuthAPIs, tokenVerifier, noAuthAPIs)
|
||||||
return svr
|
return svr
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ CoordinatorServer = (*Server)(nil)
|
var _ CoordinatorServer = (*Server)(nil)
|
||||||
|
|
||||||
|
var tokenAuthAPIs []string
|
||||||
|
|
||||||
|
func TokenAuth(api string) bool {
|
||||||
|
tokenAuthAPIs = append(tokenAuthAPIs, api)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var noAuthAPIs []string
|
||||||
|
|
||||||
|
func NoAuth(api string) bool {
|
||||||
|
noAuthAPIs = append(noAuthAPIs, api)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ type SelectStorageHubResp struct {
|
||||||
Hubs []*cortypes.Hub
|
Hubs []*cortypes.Hub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Coordinator_SelectStorageHub_FullMethodName)
|
||||||
|
|
||||||
func (c *Client) SelectStorageHub(ctx context.Context, msg *SelectStorageHub) (*SelectStorageHubResp, *rpc.CodeError) {
|
func (c *Client) SelectStorageHub(ctx context.Context, msg *SelectStorageHub) (*SelectStorageHubResp, *rpc.CodeError) {
|
||||||
if c.fusedErr != nil {
|
if c.fusedErr != nil {
|
||||||
return nil, c.fusedErr
|
return nil, c.fusedErr
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package corrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserService interface {
|
||||||
|
UserLogin(ctx context.Context, msg *UserLogin) (*UserLoginResp, *rpc.CodeError)
|
||||||
|
|
||||||
|
UserLogout(ctx context.Context, msg *UserLogout) (*UserLogoutResp, *rpc.CodeError)
|
||||||
|
|
||||||
|
UserRefreshToken(ctx context.Context, msg *UserRefreshToken) (*UserRefreshTokenResp, *rpc.CodeError)
|
||||||
|
|
||||||
|
HubLoadAccessToken(ctx context.Context, msg *HubLoadAccessToken) (*HubLoadAccessTokenResp, *rpc.CodeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端登录
|
||||||
|
type UserLogin struct {
|
||||||
|
Account string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
type UserLoginResp struct {
|
||||||
|
Token cortypes.UserAccessToken
|
||||||
|
PrivateKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = NoAuth(Coordinator_UserLogin_FullMethodName)
|
||||||
|
|
||||||
|
func (c *Client) UserLogin(ctx context.Context, msg *UserLogin) (*UserLoginResp, *rpc.CodeError) {
|
||||||
|
if c.fusedErr != nil {
|
||||||
|
return nil, c.fusedErr
|
||||||
|
}
|
||||||
|
return rpc.UnaryClient[*UserLoginResp](c.cli.UserLogin, ctx, msg)
|
||||||
|
}
|
||||||
|
func (s *Server) UserLogin(ctx context.Context, req *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return rpc.UnaryServer(s.svrImpl.UserLogin, ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端刷新Token,原始Token会继续有效。
|
||||||
|
type UserRefreshToken struct{}
|
||||||
|
type UserRefreshTokenResp struct {
|
||||||
|
Token cortypes.UserAccessToken
|
||||||
|
PrivateKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Coordinator_UserLogin_FullMethodName)
|
||||||
|
|
||||||
|
func (c *Client) UserRefreshToken(ctx context.Context, msg *UserRefreshToken) (*UserRefreshTokenResp, *rpc.CodeError) {
|
||||||
|
if c.fusedErr != nil {
|
||||||
|
return nil, c.fusedErr
|
||||||
|
}
|
||||||
|
return rpc.UnaryClient[*UserRefreshTokenResp](c.cli.UserRefreshToken, ctx, msg)
|
||||||
|
}
|
||||||
|
func (s *Server) UserRefreshToken(ctx context.Context, req *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return rpc.UnaryServer(s.svrImpl.UserRefreshToken, ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户端登出。会使用GRPC元数据中的TokenID和UserID来查找Token并删除。
|
||||||
|
type UserLogout struct{}
|
||||||
|
type UserLogoutResp struct{}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Coordinator_UserLogout_FullMethodName)
|
||||||
|
|
||||||
|
func (c *Client) UserLogout(ctx context.Context, msg *UserLogout) (*UserLogoutResp, *rpc.CodeError) {
|
||||||
|
if c.fusedErr != nil {
|
||||||
|
return nil, c.fusedErr
|
||||||
|
}
|
||||||
|
return rpc.UnaryClient[*UserLogoutResp](c.cli.UserLogout, ctx, msg)
|
||||||
|
}
|
||||||
|
func (s *Server) UserLogout(ctx context.Context, req *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return rpc.UnaryServer(s.svrImpl.UserLogout, ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hub服务加载AccessToken
|
||||||
|
type HubLoadAccessToken struct {
|
||||||
|
HubID cortypes.HubID
|
||||||
|
UserID cortypes.UserID
|
||||||
|
TokenID cortypes.AccessTokenID
|
||||||
|
}
|
||||||
|
type HubLoadAccessTokenResp struct {
|
||||||
|
Token cortypes.UserAccessToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) HubLoadAccessToken(ctx context.Context, msg *HubLoadAccessToken) (*HubLoadAccessTokenResp, *rpc.CodeError) {
|
||||||
|
if c.fusedErr != nil {
|
||||||
|
return nil, c.fusedErr
|
||||||
|
}
|
||||||
|
return rpc.UnaryClient[*HubLoadAccessTokenResp](c.cli.HubLoadAccessToken, ctx, msg)
|
||||||
|
}
|
||||||
|
func (s *Server) HubLoadAccessToken(ctx context.Context, req *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return rpc.UnaryServer(s.svrImpl.HubLoadAccessToken, ctx, req)
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
addr grpcAddr
|
addr string
|
||||||
con *grpc.ClientConn
|
con *grpc.ClientConn
|
||||||
cli HubClient
|
cli HubClient
|
||||||
pool *Pool
|
pool *Pool
|
||||||
|
@ -15,7 +15,7 @@ type Client struct {
|
||||||
|
|
||||||
func (c *Client) Release() {
|
func (c *Client) Release() {
|
||||||
if c.con != nil {
|
if c.con != nil {
|
||||||
c.pool.release(c.addr)
|
c.pool.connPool.Release(c.addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ var file_pkgs_rpc_hub_hub_proto_rawDesc = []byte{
|
||||||
0x0a, 0x16, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x68, 0x75, 0x62, 0x2f, 0x68,
|
0x0a, 0x16, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x68, 0x75, 0x62, 0x2f, 0x68,
|
||||||
0x75, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x68, 0x75, 0x62, 0x72, 0x70, 0x63,
|
0x75, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x68, 0x75, 0x62, 0x72, 0x70, 0x63,
|
||||||
0x1a, 0x12, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x2e, 0x70,
|
0x1a, 0x12, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x70, 0x63, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x32, 0xb8, 0x02, 0x0a, 0x03, 0x48, 0x75, 0x62, 0x12, 0x2c, 0x0a, 0x0d,
|
0x72, 0x6f, 0x74, 0x6f, 0x32, 0xf5, 0x02, 0x0a, 0x03, 0x48, 0x75, 0x62, 0x12, 0x2c, 0x0a, 0x0d,
|
||||||
0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x49, 0x4f, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x0c, 0x2e,
|
0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x49, 0x4f, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x0c, 0x2e,
|
||||||
0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70,
|
0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70,
|
||||||
0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x0c, 0x53, 0x65,
|
0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x0c, 0x53, 0x65,
|
||||||
|
@ -45,12 +45,16 @@ var file_pkgs_rpc_hub_hub_proto_rawDesc = []byte{
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65,
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65,
|
||||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
|
||||||
0x74, 0x65, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
0x74, 0x65, 0x12, 0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
|
0x1a, 0x0d, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||||
0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x6f, 0x72, 0x67, 0x2e, 0x63,
|
0x3b, 0x0a, 0x1c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63,
|
||||||
0x6e, 0x2f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x72, 0x65, 0x61, 0x6d, 0x2f, 0x6a, 0x63, 0x73, 0x2d,
|
0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12,
|
||||||
0x70, 0x75, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x73, 0x2f,
|
0x0c, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e,
|
||||||
0x72, 0x70, 0x63, 0x2f, 0x68, 0x75, 0x62, 0x72, 0x70, 0x63, 0x3b, 0x68, 0x75, 0x62, 0x72, 0x70,
|
0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x40, 0x5a, 0x3e,
|
||||||
0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x67, 0x69, 0x74, 0x6c, 0x69, 0x6e, 0x6b, 0x2e, 0x6f, 0x72, 0x67, 0x2e, 0x63, 0x6e, 0x2f, 0x63,
|
||||||
|
0x6c, 0x6f, 0x75, 0x64, 0x72, 0x65, 0x61, 0x6d, 0x2f, 0x6a, 0x63, 0x73, 0x2d, 0x70, 0x75, 0x62,
|
||||||
|
0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x73, 0x2f, 0x72, 0x70, 0x63,
|
||||||
|
0x2f, 0x68, 0x75, 0x62, 0x72, 0x70, 0x63, 0x3b, 0x68, 0x75, 0x62, 0x72, 0x70, 0x63, 0x62, 0x06,
|
||||||
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_pkgs_rpc_hub_hub_proto_goTypes = []any{
|
var file_pkgs_rpc_hub_hub_proto_goTypes = []any{
|
||||||
|
@ -66,15 +70,17 @@ var file_pkgs_rpc_hub_hub_proto_depIdxs = []int32{
|
||||||
0, // 4: hubrpc.Hub.GetIOVar:input_type -> rpc.Request
|
0, // 4: hubrpc.Hub.GetIOVar:input_type -> rpc.Request
|
||||||
0, // 5: hubrpc.Hub.Ping:input_type -> rpc.Request
|
0, // 5: hubrpc.Hub.Ping:input_type -> rpc.Request
|
||||||
0, // 6: hubrpc.Hub.GetState:input_type -> rpc.Request
|
0, // 6: hubrpc.Hub.GetState:input_type -> rpc.Request
|
||||||
2, // 7: hubrpc.Hub.ExecuteIOPlan:output_type -> rpc.Response
|
0, // 7: hubrpc.Hub.NotifyUserAccessTokenInvalid:input_type -> rpc.Request
|
||||||
2, // 8: hubrpc.Hub.SendIOStream:output_type -> rpc.Response
|
2, // 8: hubrpc.Hub.ExecuteIOPlan:output_type -> rpc.Response
|
||||||
1, // 9: hubrpc.Hub.GetIOStream:output_type -> rpc.ChunkedData
|
2, // 9: hubrpc.Hub.SendIOStream:output_type -> rpc.Response
|
||||||
2, // 10: hubrpc.Hub.SendIOVar:output_type -> rpc.Response
|
1, // 10: hubrpc.Hub.GetIOStream:output_type -> rpc.ChunkedData
|
||||||
2, // 11: hubrpc.Hub.GetIOVar:output_type -> rpc.Response
|
2, // 11: hubrpc.Hub.SendIOVar:output_type -> rpc.Response
|
||||||
2, // 12: hubrpc.Hub.Ping:output_type -> rpc.Response
|
2, // 12: hubrpc.Hub.GetIOVar:output_type -> rpc.Response
|
||||||
2, // 13: hubrpc.Hub.GetState:output_type -> rpc.Response
|
2, // 13: hubrpc.Hub.Ping:output_type -> rpc.Response
|
||||||
7, // [7:14] is the sub-list for method output_type
|
2, // 14: hubrpc.Hub.GetState:output_type -> rpc.Response
|
||||||
0, // [0:7] is the sub-list for method input_type
|
2, // 15: hubrpc.Hub.NotifyUserAccessTokenInvalid:output_type -> rpc.Response
|
||||||
|
8, // [8:16] is the sub-list for method output_type
|
||||||
|
0, // [0:8] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
0, // [0:0] is the sub-list for field type_name
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
|
|
@ -16,4 +16,6 @@ service Hub {
|
||||||
|
|
||||||
rpc Ping(rpc.Request) returns(rpc.Response);
|
rpc Ping(rpc.Request) returns(rpc.Response);
|
||||||
rpc GetState(rpc.Request) returns(rpc.Response);
|
rpc GetState(rpc.Request) returns(rpc.Response);
|
||||||
|
|
||||||
|
rpc NotifyUserAccessTokenInvalid(rpc.Request) returns(rpc.Response);
|
||||||
}
|
}
|
|
@ -20,13 +20,14 @@ import (
|
||||||
const _ = grpc.SupportPackageIsVersion7
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Hub_ExecuteIOPlan_FullMethodName = "/hubrpc.Hub/ExecuteIOPlan"
|
Hub_ExecuteIOPlan_FullMethodName = "/hubrpc.Hub/ExecuteIOPlan"
|
||||||
Hub_SendIOStream_FullMethodName = "/hubrpc.Hub/SendIOStream"
|
Hub_SendIOStream_FullMethodName = "/hubrpc.Hub/SendIOStream"
|
||||||
Hub_GetIOStream_FullMethodName = "/hubrpc.Hub/GetIOStream"
|
Hub_GetIOStream_FullMethodName = "/hubrpc.Hub/GetIOStream"
|
||||||
Hub_SendIOVar_FullMethodName = "/hubrpc.Hub/SendIOVar"
|
Hub_SendIOVar_FullMethodName = "/hubrpc.Hub/SendIOVar"
|
||||||
Hub_GetIOVar_FullMethodName = "/hubrpc.Hub/GetIOVar"
|
Hub_GetIOVar_FullMethodName = "/hubrpc.Hub/GetIOVar"
|
||||||
Hub_Ping_FullMethodName = "/hubrpc.Hub/Ping"
|
Hub_Ping_FullMethodName = "/hubrpc.Hub/Ping"
|
||||||
Hub_GetState_FullMethodName = "/hubrpc.Hub/GetState"
|
Hub_GetState_FullMethodName = "/hubrpc.Hub/GetState"
|
||||||
|
Hub_NotifyUserAccessTokenInvalid_FullMethodName = "/hubrpc.Hub/NotifyUserAccessTokenInvalid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HubClient is the client API for Hub service.
|
// HubClient is the client API for Hub service.
|
||||||
|
@ -40,6 +41,7 @@ type HubClient interface {
|
||||||
GetIOVar(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
GetIOVar(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
Ping(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
Ping(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
GetState(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
GetState(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
|
NotifyUserAccessTokenInvalid(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type hubClient struct {
|
type hubClient struct {
|
||||||
|
@ -161,6 +163,15 @@ func (c *hubClient) GetState(ctx context.Context, in *rpc.Request, opts ...grpc.
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *hubClient) NotifyUserAccessTokenInvalid(ctx context.Context, in *rpc.Request, opts ...grpc.CallOption) (*rpc.Response, error) {
|
||||||
|
out := new(rpc.Response)
|
||||||
|
err := c.cc.Invoke(ctx, Hub_NotifyUserAccessTokenInvalid_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
// HubServer is the server API for Hub service.
|
// HubServer is the server API for Hub service.
|
||||||
// All implementations must embed UnimplementedHubServer
|
// All implementations must embed UnimplementedHubServer
|
||||||
// for forward compatibility
|
// for forward compatibility
|
||||||
|
@ -172,6 +183,7 @@ type HubServer interface {
|
||||||
GetIOVar(context.Context, *rpc.Request) (*rpc.Response, error)
|
GetIOVar(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
Ping(context.Context, *rpc.Request) (*rpc.Response, error)
|
Ping(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
GetState(context.Context, *rpc.Request) (*rpc.Response, error)
|
GetState(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
|
NotifyUserAccessTokenInvalid(context.Context, *rpc.Request) (*rpc.Response, error)
|
||||||
mustEmbedUnimplementedHubServer()
|
mustEmbedUnimplementedHubServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +212,9 @@ func (UnimplementedHubServer) Ping(context.Context, *rpc.Request) (*rpc.Response
|
||||||
func (UnimplementedHubServer) GetState(context.Context, *rpc.Request) (*rpc.Response, error) {
|
func (UnimplementedHubServer) GetState(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetState not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetState not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedHubServer) NotifyUserAccessTokenInvalid(context.Context, *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method NotifyUserAccessTokenInvalid not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {}
|
func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {}
|
||||||
|
|
||||||
// UnsafeHubServer may be embedded to opt out of forward compatibility for this service.
|
// UnsafeHubServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
@ -350,6 +365,24 @@ func _Hub_GetState_Handler(srv interface{}, ctx context.Context, dec func(interf
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Hub_NotifyUserAccessTokenInvalid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(rpc.Request)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(HubServer).NotifyUserAccessTokenInvalid(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Hub_NotifyUserAccessTokenInvalid_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(HubServer).NotifyUserAccessTokenInvalid(ctx, req.(*rpc.Request))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// Hub_ServiceDesc is the grpc.ServiceDesc for Hub service.
|
// Hub_ServiceDesc is the grpc.ServiceDesc for Hub service.
|
||||||
// It's only intended for direct use with grpc.RegisterService,
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
// and not to be introspected or modified (even as a copy)
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
@ -377,6 +410,10 @@ var Hub_ServiceDesc = grpc.ServiceDesc{
|
||||||
MethodName: "GetState",
|
MethodName: "GetState",
|
||||||
Handler: _Hub_GetState_Handler,
|
Handler: _Hub_GetState_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "NotifyUserAccessTokenInvalid",
|
||||||
|
Handler: _Hub_NotifyUserAccessTokenInvalid_Handler,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{
|
Streams: []grpc.StreamDesc{
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,8 @@ type ExecuteIOPlan struct {
|
||||||
}
|
}
|
||||||
type ExecuteIOPlanResp struct{}
|
type ExecuteIOPlanResp struct{}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Hub_ExecuteIOPlan_FullMethodName)
|
||||||
|
|
||||||
func (c *Client) ExecuteIOPlan(ctx context.Context, req *ExecuteIOPlan) (*ExecuteIOPlanResp, *rpc.CodeError) {
|
func (c *Client) ExecuteIOPlan(ctx context.Context, req *ExecuteIOPlan) (*ExecuteIOPlanResp, *rpc.CodeError) {
|
||||||
if c.fusedErr != nil {
|
if c.fusedErr != nil {
|
||||||
return nil, c.fusedErr
|
return nil, c.fusedErr
|
||||||
|
@ -49,6 +51,8 @@ func (s *SendIOStream) SetStream(str io.Reader) {
|
||||||
|
|
||||||
type SendIOStreamResp struct{}
|
type SendIOStreamResp struct{}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Hub_SendIOStream_FullMethodName)
|
||||||
|
|
||||||
func (c *Client) SendIOStream(ctx context.Context, req *SendIOStream) (*SendIOStreamResp, *rpc.CodeError) {
|
func (c *Client) SendIOStream(ctx context.Context, req *SendIOStream) (*SendIOStreamResp, *rpc.CodeError) {
|
||||||
if c.fusedErr != nil {
|
if c.fusedErr != nil {
|
||||||
return nil, c.fusedErr
|
return nil, c.fusedErr
|
||||||
|
@ -71,6 +75,8 @@ type GetIOStreamResp struct {
|
||||||
Stream io.ReadCloser `json:"-"`
|
Stream io.ReadCloser `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Hub_GetIOStream_FullMethodName)
|
||||||
|
|
||||||
func (r *GetIOStreamResp) GetStream() io.ReadCloser {
|
func (r *GetIOStreamResp) GetStream() io.ReadCloser {
|
||||||
return r.Stream
|
return r.Stream
|
||||||
}
|
}
|
||||||
|
@ -97,6 +103,8 @@ type SendIOVar struct {
|
||||||
}
|
}
|
||||||
type SendIOVarResp struct{}
|
type SendIOVarResp struct{}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Hub_SendIOVar_FullMethodName)
|
||||||
|
|
||||||
func (c *Client) SendIOVar(ctx context.Context, req *SendIOVar) (*SendIOVarResp, *rpc.CodeError) {
|
func (c *Client) SendIOVar(ctx context.Context, req *SendIOVar) (*SendIOVarResp, *rpc.CodeError) {
|
||||||
if c.fusedErr != nil {
|
if c.fusedErr != nil {
|
||||||
return nil, c.fusedErr
|
return nil, c.fusedErr
|
||||||
|
@ -119,6 +127,8 @@ type GetIOVarResp struct {
|
||||||
Value exec.VarValue
|
Value exec.VarValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Hub_GetIOVar_FullMethodName)
|
||||||
|
|
||||||
func (c *Client) GetIOVar(ctx context.Context, req *GetIOVar) (*GetIOVarResp, *rpc.CodeError) {
|
func (c *Client) GetIOVar(ctx context.Context, req *GetIOVar) (*GetIOVarResp, *rpc.CodeError) {
|
||||||
if c.fusedErr != nil {
|
if c.fusedErr != nil {
|
||||||
return nil, c.fusedErr
|
return nil, c.fusedErr
|
||||||
|
|
|
@ -15,6 +15,8 @@ type MicsSvc interface {
|
||||||
type Ping struct{}
|
type Ping struct{}
|
||||||
type PingResp struct{}
|
type PingResp struct{}
|
||||||
|
|
||||||
|
var _ = TokenAuth(Hub_Ping_FullMethodName)
|
||||||
|
|
||||||
func (c *Client) Ping(ctx context.Context, req *Ping) (*PingResp, *rpc.CodeError) {
|
func (c *Client) Ping(ctx context.Context, req *Ping) (*PingResp, *rpc.CodeError) {
|
||||||
if c.fusedErr != nil {
|
if c.fusedErr != nil {
|
||||||
return nil, c.fusedErr
|
return nil, c.fusedErr
|
||||||
|
|
|
@ -1,114 +1,77 @@
|
||||||
package hubrpc
|
package hubrpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
grpc "google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PoolConfig struct{}
|
type PoolConfig struct {
|
||||||
|
Conn rpc.PoolConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type PoolConfigJSON struct {
|
||||||
|
RootCA string `json:"rootCA"`
|
||||||
|
ClientCert string `json:"clientCert"`
|
||||||
|
ClientKey string `json:"clientKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PoolConfigJSON) Build(tokenProv rpc.AccessTokenProvider) (*PoolConfig, error) {
|
||||||
|
pc := &PoolConfig{}
|
||||||
|
pc.Conn.AccessTokenProvider = tokenProv
|
||||||
|
|
||||||
|
rootCA, err := os.ReadFile(c.RootCA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load root ca: %v", err)
|
||||||
|
}
|
||||||
|
pc.Conn.RootCA = x509.NewCertPool()
|
||||||
|
if !pc.Conn.RootCA.AppendCertsFromPEM(rootCA) {
|
||||||
|
return nil, fmt.Errorf("failed to parse root ca")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ClientCert != "" && c.ClientKey != "" {
|
||||||
|
cert, err := tls.LoadX509KeyPair(c.ClientCert, c.ClientKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("load client cert: %v", err)
|
||||||
|
}
|
||||||
|
pc.Conn.ClientCert = &cert
|
||||||
|
} else if tokenProv == nil {
|
||||||
|
return nil, fmt.Errorf("must provide client cert or access token provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pc, nil
|
||||||
|
}
|
||||||
|
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
grpcCons map[grpcAddr]*grpcCon
|
connPool *rpc.ConnPool
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type grpcAddr struct {
|
|
||||||
IP string
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
|
|
||||||
type grpcCon struct {
|
|
||||||
grpcCon *grpc.ClientConn
|
|
||||||
refCount int
|
|
||||||
stopClosing chan any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPool(cfg PoolConfig) *Pool {
|
func NewPool(cfg PoolConfig) *Pool {
|
||||||
return &Pool{
|
return &Pool{
|
||||||
grpcCons: make(map[grpcAddr]*grpcCon),
|
connPool: rpc.NewConnPool(cfg.Conn),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) Get(ip string, port int) *Client {
|
func (p *Pool) Get(ip string, port int) *Client {
|
||||||
p.lock.Lock()
|
addr := fmt.Sprintf("%s:%d", ip, port)
|
||||||
defer p.lock.Unlock()
|
con, err := p.connPool.GetConnection(addr)
|
||||||
|
if err != nil {
|
||||||
ga := grpcAddr{IP: ip, Port: port}
|
return &Client{
|
||||||
con := p.grpcCons[ga]
|
addr: addr,
|
||||||
if con == nil {
|
con: nil,
|
||||||
gcon, err := grpc.NewClient(fmt.Sprintf("%v:%v", ip, port), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
pool: p,
|
||||||
if err != nil {
|
fusedErr: rpc.Failed(errorcode.OperationFailed, err.Error()),
|
||||||
return &Client{
|
|
||||||
addr: ga,
|
|
||||||
con: nil,
|
|
||||||
pool: p,
|
|
||||||
fusedErr: rpc.Failed(errorcode.OperationFailed, err.Error()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
con = &grpcCon{
|
|
||||||
grpcCon: gcon,
|
|
||||||
refCount: 0,
|
|
||||||
stopClosing: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
p.grpcCons[ga] = con
|
|
||||||
} else if con.stopClosing != nil {
|
|
||||||
close(con.stopClosing)
|
|
||||||
con.stopClosing = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
con.refCount++
|
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
addr: ga,
|
addr: addr,
|
||||||
con: con.grpcCon,
|
con: con,
|
||||||
cli: NewHubClient(con.grpcCon),
|
cli: NewHubClient(con),
|
||||||
pool: p,
|
pool: p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) release(addr grpcAddr) {
|
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
grpcCon := p.grpcCons[addr]
|
|
||||||
if grpcCon == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
grpcCon.refCount--
|
|
||||||
grpcCon.refCount = max(grpcCon.refCount, 0)
|
|
||||||
|
|
||||||
if grpcCon.refCount == 0 {
|
|
||||||
stopClosing := make(chan any)
|
|
||||||
grpcCon.stopClosing = stopClosing
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-stopClosing:
|
|
||||||
return
|
|
||||||
|
|
||||||
case <-time.After(time.Minute):
|
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
grpcCon := p.grpcCons[addr]
|
|
||||||
if grpcCon == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if grpcCon.refCount == 0 {
|
|
||||||
grpcCon.grpcCon.Close()
|
|
||||||
delete(p.grpcCons, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ type HubAPI interface {
|
||||||
// CacheSvc
|
// CacheSvc
|
||||||
IOSwitchSvc
|
IOSwitchSvc
|
||||||
MicsSvc
|
MicsSvc
|
||||||
// UserSpaceSvc
|
UserSvc
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
@ -17,12 +17,26 @@ type Server struct {
|
||||||
svrImpl HubAPI
|
svrImpl HubAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(cfg rpc.Config, impl HubAPI) *Server {
|
func NewServer(cfg rpc.Config, impl HubAPI, tokenVerifier rpc.AccessTokenVerifier) *Server {
|
||||||
svr := &Server{
|
svr := &Server{
|
||||||
svrImpl: impl,
|
svrImpl: impl,
|
||||||
}
|
}
|
||||||
svr.ServerBase = rpc.NewServerBase(cfg, svr, &Hub_ServiceDesc)
|
svr.ServerBase = rpc.NewServerBase(cfg, svr, &Hub_ServiceDesc, tokenAuthAPIs, tokenVerifier, noAuthAPIs)
|
||||||
return svr
|
return svr
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ HubServer = (*Server)(nil)
|
var _ HubServer = (*Server)(nil)
|
||||||
|
|
||||||
|
var tokenAuthAPIs []string
|
||||||
|
|
||||||
|
func TokenAuth(api string) bool {
|
||||||
|
tokenAuthAPIs = append(tokenAuthAPIs, api)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var noAuthAPIs []string
|
||||||
|
|
||||||
|
func NoAuth(api string) bool {
|
||||||
|
noAuthAPIs = append(noAuthAPIs, api)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package hubrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserSvc interface {
|
||||||
|
NotifyUserAccessTokenInvalid(ctx context.Context, req *NotifyUserAccessTokenInvalid) (*NotifyUserAccessTokenInvalidResp, *rpc.CodeError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知用户的Token登出
|
||||||
|
type NotifyUserAccessTokenInvalid struct {
|
||||||
|
UserID cortypes.UserID
|
||||||
|
TokenID cortypes.AccessTokenID
|
||||||
|
}
|
||||||
|
type NotifyUserAccessTokenInvalidResp struct{}
|
||||||
|
|
||||||
|
func (c *Client) NotifyUserAccessTokenInvalid(ctx context.Context, req *NotifyUserAccessTokenInvalid) (*NotifyUserAccessTokenInvalidResp, *rpc.CodeError) {
|
||||||
|
if c.fusedErr != nil {
|
||||||
|
return nil, c.fusedErr
|
||||||
|
}
|
||||||
|
return rpc.UnaryClient[*NotifyUserAccessTokenInvalidResp](c.cli.NotifyUserAccessTokenInvalid, ctx, req)
|
||||||
|
}
|
||||||
|
func (s *Server) NotifyUserAccessTokenInvalid(ctx context.Context, req *rpc.Request) (*rpc.Response, error) {
|
||||||
|
return rpc.UnaryServer(s.svrImpl.NotifyUserAccessTokenInvalid, ctx, req)
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PoolConfig struct {
|
||||||
|
RootCA *x509.CertPool
|
||||||
|
// 客户端证书,与AccessTokenProvider二选一
|
||||||
|
ClientCert *tls.Certificate
|
||||||
|
// AccessTokenProvider,与ClientCert二选一
|
||||||
|
AccessTokenProvider AccessTokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnPool struct {
|
||||||
|
cfg PoolConfig
|
||||||
|
grpcCons map[string]*grpcCon
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type grpcCon struct {
|
||||||
|
grpcCon *grpc.ClientConn
|
||||||
|
refCount int
|
||||||
|
stopClosing chan any
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnPool(cfg PoolConfig) *ConnPool {
|
||||||
|
return &ConnPool{
|
||||||
|
cfg: cfg,
|
||||||
|
grpcCons: make(map[string]*grpcCon),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) GetConnection(addr string) (*grpc.ClientConn, error) {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
con := p.grpcCons[addr]
|
||||||
|
if con == nil {
|
||||||
|
gcon, err := p.connecting(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
con = &grpcCon{
|
||||||
|
grpcCon: gcon,
|
||||||
|
refCount: 0,
|
||||||
|
stopClosing: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.grpcCons[addr] = con
|
||||||
|
} else if con.stopClosing != nil {
|
||||||
|
close(con.stopClosing)
|
||||||
|
con.stopClosing = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
con.refCount++
|
||||||
|
|
||||||
|
return con.grpcCon, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) connecting(addr string) (*grpc.ClientConn, error) {
|
||||||
|
if p.cfg.ClientCert != nil {
|
||||||
|
gcon, err := grpc.NewClient(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||||
|
RootCAs: p.cfg.RootCA,
|
||||||
|
Certificates: []tls.Certificate{*p.cfg.ClientCert},
|
||||||
|
ServerName: InternalAPISNIV1,
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
})))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gcon, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.cfg.AccessTokenProvider == nil {
|
||||||
|
return nil, fmt.Errorf("no client cert or access token provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
gcon, err := grpc.NewClient(addr,
|
||||||
|
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||||
|
RootCAs: p.cfg.RootCA,
|
||||||
|
ServerName: ClientAPISNIV1,
|
||||||
|
NextProtos: []string{"h2"},
|
||||||
|
})),
|
||||||
|
grpc.WithUnaryInterceptor(p.populateAccessTokenUnary),
|
||||||
|
grpc.WithStreamInterceptor(p.populateAccessTokenStream),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gcon, nil
|
||||||
|
}
|
||||||
|
func (p *ConnPool) Release(addr string) {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
grpcCon := p.grpcCons[addr]
|
||||||
|
if grpcCon == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
grpcCon.refCount--
|
||||||
|
grpcCon.refCount = max(grpcCon.refCount, 0)
|
||||||
|
|
||||||
|
if grpcCon.refCount == 0 {
|
||||||
|
stopClosing := make(chan any)
|
||||||
|
grpcCon.stopClosing = stopClosing
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-stopClosing:
|
||||||
|
return
|
||||||
|
|
||||||
|
case <-time.After(time.Minute):
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
|
|
||||||
|
grpcCon := p.grpcCons[addr]
|
||||||
|
if grpcCon == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if grpcCon.refCount == 0 {
|
||||||
|
grpcCon.grpcCon.Close()
|
||||||
|
delete(p.grpcCons, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) populateAccessTokenUnary(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||||
|
authInfo, err := p.cfg.AccessTokenProvider.GetAuthInfo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
md := metadata.Pairs(
|
||||||
|
MetaUserID, fmt.Sprintf("%v", authInfo.UserID),
|
||||||
|
MetaAccessTokenID, fmt.Sprintf("%v", authInfo.AccessTokenID),
|
||||||
|
MetaNonce, authInfo.Nonce,
|
||||||
|
MetaSignature, authInfo.Signature,
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||||
|
return invoker(ctx, method, req, reply, cc, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) populateAccessTokenStream(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||||
|
authInfo, err := p.cfg.AccessTokenProvider.GetAuthInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
md := metadata.Pairs(
|
||||||
|
MetaUserID, fmt.Sprintf("%v", authInfo.UserID),
|
||||||
|
MetaAccessTokenID, fmt.Sprintf("%v", authInfo.AccessTokenID),
|
||||||
|
MetaNonce, authInfo.Nonce,
|
||||||
|
MetaSignature, authInfo.Signature,
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||||
|
return streamer(ctx, desc, cc, method, opts...)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package rpc
|
|
@ -1,11 +1,16 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/async"
|
"gitlink.org.cn/cloudream/common/pkgs/async"
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerEventChan = async.UnboundChannel[RPCServerEvent]
|
type ServerEventChan = async.UnboundChannel[RPCServerEvent]
|
||||||
|
@ -20,34 +25,84 @@ type ExitEvent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Listen string `json:"listen"`
|
Listen string `json:"listen"`
|
||||||
|
RootCA string `json:"rootCA"`
|
||||||
|
ServerCert string `json:"serverCert"`
|
||||||
|
ServerKey string `json:"serverKey"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerBase struct {
|
type ServerBase struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
grpcSvr *grpc.Server
|
grpcSvr *grpc.Server
|
||||||
srvImpl any
|
srvImpl any
|
||||||
svcDesc *grpc.ServiceDesc
|
svcDesc *grpc.ServiceDesc
|
||||||
|
rootCA *x509.CertPool
|
||||||
|
serverCert tls.Certificate
|
||||||
|
accessTokenAuthAPIs map[string]bool
|
||||||
|
tokenVerifier AccessTokenVerifier
|
||||||
|
noAuthAPIs map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServerBase(cfg Config, srvImpl any, svcDesc *grpc.ServiceDesc) *ServerBase {
|
func NewServerBase(cfg Config, srvImpl any, svcDesc *grpc.ServiceDesc, accessTokenAuthAPIs []string, tokenVerifier AccessTokenVerifier, noAuthAPIs []string) *ServerBase {
|
||||||
|
tokenAuthAPIs := make(map[string]bool)
|
||||||
|
for _, api := range accessTokenAuthAPIs {
|
||||||
|
tokenAuthAPIs[api] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
noAuths := make(map[string]bool)
|
||||||
|
for _, api := range noAuthAPIs {
|
||||||
|
noAuths[api] = true
|
||||||
|
}
|
||||||
|
|
||||||
return &ServerBase{
|
return &ServerBase{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
srvImpl: srvImpl,
|
srvImpl: srvImpl,
|
||||||
svcDesc: svcDesc,
|
svcDesc: svcDesc,
|
||||||
|
accessTokenAuthAPIs: tokenAuthAPIs,
|
||||||
|
tokenVerifier: tokenVerifier,
|
||||||
|
noAuthAPIs: noAuths,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerBase) Start() *ServerEventChan {
|
func (s *ServerBase) Start() *ServerEventChan {
|
||||||
ch := async.NewUnboundChannel[RPCServerEvent]()
|
ch := async.NewUnboundChannel[RPCServerEvent]()
|
||||||
go func() {
|
go func() {
|
||||||
|
svrCert, err := tls.LoadX509KeyPair(s.cfg.ServerCert, s.cfg.ServerKey)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("load server cert: %v", err)
|
||||||
|
ch.Send(&ExitEvent{Err: err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.serverCert = svrCert
|
||||||
|
|
||||||
|
rootCA, err := os.ReadFile(s.cfg.RootCA)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warnf("load root ca: %v", err)
|
||||||
|
ch.Send(&ExitEvent{Err: err})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.rootCA = x509.NewCertPool()
|
||||||
|
if !s.rootCA.AppendCertsFromPEM(rootCA) {
|
||||||
|
logger.Warnf("load root ca: failed to parse root ca")
|
||||||
|
ch.Send(&ExitEvent{Err: fmt.Errorf("failed to parse root ca")})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.Infof("start serving rpc at: %v", s.cfg.Listen)
|
logger.Infof("start serving rpc at: %v", s.cfg.Listen)
|
||||||
lis, err := net.Listen("tcp", s.cfg.Listen)
|
lis, err := net.Listen("tcp", s.cfg.Listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ch.Send(&ExitEvent{Err: err})
|
ch.Send(&ExitEvent{Err: err})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.grpcSvr = grpc.NewServer()
|
|
||||||
|
s.grpcSvr = grpc.NewServer(
|
||||||
|
grpc.Creds(credentials.NewTLS(&tls.Config{
|
||||||
|
GetConfigForClient: s.tlsConfigSelector,
|
||||||
|
})),
|
||||||
|
grpc.UnaryInterceptor(s.authUnary),
|
||||||
|
grpc.StreamInterceptor(s.authStream),
|
||||||
|
)
|
||||||
s.grpcSvr.RegisterService(s.svcDesc, s.srvImpl)
|
s.grpcSvr.RegisterService(s.svcDesc, s.srvImpl)
|
||||||
err = s.grpcSvr.Serve(lis)
|
err = s.grpcSvr.Serve(lis)
|
||||||
ch.Send(&ExitEvent{Err: err})
|
ch.Send(&ExitEvent{Err: err})
|
||||||
|
|
|
@ -38,17 +38,17 @@ func UnaryClient[Resp, Req any](apiFn func(context.Context, *Request, ...grpc.Ca
|
||||||
func UnaryServer[Resp, Req any](apiFn func(context.Context, Req) (Resp, *CodeError), ctx context.Context, req *Request) (*Response, error) {
|
func UnaryServer[Resp, Req any](apiFn func(context.Context, Req) (Resp, *CodeError), ctx context.Context, req *Request) (*Response, error) {
|
||||||
rreq, err := serder.JSONToObjectEx[Req](req.Payload)
|
rreq, err := serder.JSONToObjectEx[Req](req.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, makeCodeError(errorcode.OperationFailed, err.Error())
|
return nil, MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, cerr := apiFn(ctx, rreq)
|
ret, cerr := apiFn(ctx, rreq)
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
return nil, wrapCodeError(cerr)
|
return nil, WrapCodeError(cerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := serder.ObjectToJSONEx(ret)
|
data, err := serder.ObjectToJSONEx(ret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, makeCodeError(errorcode.OperationFailed, err.Error())
|
return nil, MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Response{
|
return &Response{
|
||||||
|
@ -120,33 +120,33 @@ func UploadStreamServer[Resp any, Req UploadStreamReq, APIRet UploadStreamAPISer
|
||||||
cr := NewChunkedReader(req)
|
cr := NewChunkedReader(req)
|
||||||
_, data, err := cr.NextDataPart()
|
_, data, err := cr.NextDataPart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
_, pr, err := cr.NextPart()
|
_, pr, err := cr.NextPart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
rreq, err := serder.JSONToObjectEx[Req](data)
|
rreq, err := serder.JSONToObjectEx[Req](data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
rreq.SetStream(pr)
|
rreq.SetStream(pr)
|
||||||
|
|
||||||
resp, cerr := apiFn(req.Context(), rreq)
|
resp, cerr := apiFn(req.Context(), rreq)
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
return wrapCodeError(cerr)
|
return WrapCodeError(cerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
respData, err := serder.ObjectToJSONEx(resp)
|
respData, err := serder.ObjectToJSONEx(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = req.SendAndClose(&Response{Payload: respData})
|
err = req.SendAndClose(&Response{Payload: respData})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -211,33 +211,33 @@ func DownloadStreamClient[Resp DownloadStreamResp, Req any, APIRet DownloadStrea
|
||||||
func DownloadStreamServer[Resp DownloadStreamResp, Req any, APIRet DownloadStreamAPIServer](apiFn func(context.Context, Req) (Resp, *CodeError), req *Request, ret APIRet) error {
|
func DownloadStreamServer[Resp DownloadStreamResp, Req any, APIRet DownloadStreamAPIServer](apiFn func(context.Context, Req) (Resp, *CodeError), req *Request, ret APIRet) error {
|
||||||
rreq, err := serder.JSONToObjectEx[Req](req.Payload)
|
rreq, err := serder.JSONToObjectEx[Req](req.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, cerr := apiFn(ret.Context(), rreq)
|
resp, cerr := apiFn(ret.Context(), rreq)
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
return wrapCodeError(cerr)
|
return WrapCodeError(cerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
cw := NewChunkedWriter(ret)
|
cw := NewChunkedWriter(ret)
|
||||||
data, err := serder.ObjectToJSONEx(resp)
|
data, err := serder.ObjectToJSONEx(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cw.WriteDataPart("", data)
|
err = cw.WriteDataPart("", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = cw.WriteStreamPart("", resp.GetStream())
|
_, err = cw.WriteStreamPart("", resp.GetStream())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cw.Finish()
|
err = cw.Finish()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return makeCodeError(errorcode.OperationFailed, err.Error())
|
return MakeCodeError(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -282,12 +282,12 @@ func getCodeError(err error) *CodeError {
|
||||||
return Failed(errorcode.OperationFailed, err.Error())
|
return Failed(errorcode.OperationFailed, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeCodeError(code string, msg string) error {
|
func MakeCodeError(code string, msg string) error {
|
||||||
ce, _ := status.New(codes.Unknown, "custom error").WithDetails(Failed(code, msg))
|
ce, _ := status.New(codes.Unknown, "custom error").WithDetails(Failed(code, msg))
|
||||||
return ce.Err()
|
return ce.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapCodeError(ce *CodeError) error {
|
func WrapCodeError(ce *CodeError) error {
|
||||||
e, _ := status.New(codes.Unknown, "custom error").WithDetails(ce)
|
e, _ := status.New(codes.Unknown, "custom error").WithDetails(ce)
|
||||||
return e.Err()
|
return e.Err()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package accesstoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExitEvent = accesstoken.ExitEvent
|
||||||
|
|
||||||
|
type CacheKey = accesstoken.CacheKey
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
*accesstoken.Cache
|
||||||
|
db *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db *db.DB) *Cache {
|
||||||
|
c := &Cache{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
c.Cache = accesstoken.New(c.load)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) load(key accesstoken.CacheKey) (cortypes.UserAccessToken, error) {
|
||||||
|
token, err := c.db.UserAccessToken().GetByID(c.db.DefCtx(), key.UserID, key.TokenID)
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return cortypes.UserAccessToken{}, accesstoken.ErrTokenNotFound
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return cortypes.UserAccessToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
certCmd := cobra.Command{
|
||||||
|
Use: "cert",
|
||||||
|
}
|
||||||
|
RootCmd.AddCommand(&certCmd)
|
||||||
|
|
||||||
|
certRoot := cobra.Command{
|
||||||
|
Use: "root [outputDir]",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
certRoot(args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
certCmd.AddCommand(&certRoot)
|
||||||
|
|
||||||
|
var certFilePath string
|
||||||
|
var keyFilePath string
|
||||||
|
|
||||||
|
certServer := cobra.Command{
|
||||||
|
Use: "server [outputDir]",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
certServer(certFilePath, keyFilePath, args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
certServer.Flags().StringVar(&certFilePath, "cert", "", "CA certificate file path")
|
||||||
|
certServer.Flags().StringVar(&keyFilePath, "key", "", "CA key file path")
|
||||||
|
certCmd.AddCommand(&certServer)
|
||||||
|
|
||||||
|
certClient := cobra.Command{
|
||||||
|
Use: "client [outputDir]",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
certClient(certFilePath, keyFilePath, args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
certClient.Flags().StringVar(&certFilePath, "cert", "", "CA certificate file path")
|
||||||
|
certClient.Flags().StringVar(&keyFilePath, "key", "", "CA key file path")
|
||||||
|
certCmd.AddCommand(&certClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func certRoot(output string) {
|
||||||
|
caPriv, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
|
||||||
|
// 创建 CA 证书模板
|
||||||
|
caTemplate := &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
Organization: []string{"JCS"},
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().AddDate(10, 0, 0), // 有效期10年
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自签名 CA 证书
|
||||||
|
caCertDER, _ := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caPriv.PublicKey, caPriv)
|
||||||
|
|
||||||
|
// 保存 CA 证书和私钥
|
||||||
|
writePem(filepath.Join(output, "ca_cert.pem"), "CERTIFICATE", caCertDER)
|
||||||
|
writePem(filepath.Join(output, "ca_key.pem"), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(caPriv))
|
||||||
|
fmt.Println("CA certificate and key saved to", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func certServer(certFile string, keyFile string, output string) {
|
||||||
|
// 读取 CA 证书和私钥
|
||||||
|
caCertPEM, err := os.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to read CA certificate:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caKeyPEM, err := os.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to read CA key:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caCertPEMBlock, _ := pem.Decode(caCertPEM)
|
||||||
|
if caCertPEMBlock == nil {
|
||||||
|
fmt.Println("Failed to decode CA certificate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caKeyPEMBlock, _ := pem.Decode(caKeyPEM)
|
||||||
|
if caKeyPEMBlock == nil {
|
||||||
|
fmt.Println("Failed to decode CA key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caCert, err := x509.ParseCertificate(caCertPEMBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to parse CA certificate:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caKey, err := x509.ParsePKCS1PrivateKey(caKeyPEMBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to parse CA key:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成服务端私钥
|
||||||
|
serverPriv, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
|
||||||
|
// 服务端证书模板
|
||||||
|
serverTemplate := &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(2),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "localhost",
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().AddDate(1, 0, 0), // 有效期1年
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加主机名/IP 到证书
|
||||||
|
serverTemplate.DNSNames = []string{rpc.ClientAPISNIV1, rpc.InternalAPISNIV1}
|
||||||
|
|
||||||
|
// 用 CA 签发服务端证书
|
||||||
|
serverCertDER, _ := x509.CreateCertificate(rand.Reader, serverTemplate, caCert, &serverPriv.PublicKey, caKey)
|
||||||
|
|
||||||
|
// 保存服务端证书和私钥
|
||||||
|
writePem(filepath.Join(output, "server_cert.pem"), "CERTIFICATE", serverCertDER)
|
||||||
|
writePem(filepath.Join(output, "server_key.pem"), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(serverPriv))
|
||||||
|
fmt.Println("Server certificate and key saved to", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func certClient(certFile string, keyFile string, output string) {
|
||||||
|
// 读取 CA 证书和私钥
|
||||||
|
caCertPEM, err := os.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to read CA certificate:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caKeyPEM, err := os.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to read CA key:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caCertPEMBlock, _ := pem.Decode(caCertPEM)
|
||||||
|
if caCertPEMBlock == nil {
|
||||||
|
fmt.Println("Failed to decode CA certificate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caKeyPEMBlock, _ := pem.Decode(caKeyPEM)
|
||||||
|
if caKeyPEMBlock == nil {
|
||||||
|
fmt.Println("Failed to decode CA key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caCert, err := x509.ParseCertificate(caCertPEMBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to parse CA certificate:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caKey, err := x509.ParsePKCS1PrivateKey(caKeyPEMBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to parse CA key:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成客户端私钥
|
||||||
|
clientPriv, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
|
||||||
|
// 客户端证书模板
|
||||||
|
clientTemplate := &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(3),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "client",
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().AddDate(1, 0, 0), // 有效期1年
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用 CA 签发客户端证书
|
||||||
|
clientCertDER, _ := x509.CreateCertificate(rand.Reader, clientTemplate, caCert, &clientPriv.PublicKey, caKey)
|
||||||
|
|
||||||
|
// 保存客户端证书和私钥
|
||||||
|
writePem(filepath.Join(output, "client_cert.pem"), "CERTIFICATE", clientCertDER)
|
||||||
|
writePem(filepath.Join(output, "client_key.pem"), "RSA PRIVATE KEY", x509.MarshalPKCS1PrivateKey(clientPriv))
|
||||||
|
fmt.Println("Client certificate and key saved to", output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePem(filename, pemType string, bytes []byte) {
|
||||||
|
f, _ := os.Create(filename)
|
||||||
|
pem.Encode(f, &pem.Block{Type: pemType, Bytes: bytes})
|
||||||
|
f.Close()
|
||||||
|
}
|
|
@ -42,6 +42,8 @@ func migrate(configPath string) {
|
||||||
migrateOne(db, cortypes.Hub{})
|
migrateOne(db, cortypes.Hub{})
|
||||||
migrateOne(db, cortypes.HubLocation{})
|
migrateOne(db, cortypes.HubLocation{})
|
||||||
migrateOne(db, cortypes.User{})
|
migrateOne(db, cortypes.User{})
|
||||||
|
migrateOne(db, cortypes.UserAccessToken{})
|
||||||
|
migrateOne(db, cortypes.LoadedAccessToken{})
|
||||||
|
|
||||||
fmt.Println("migrate success")
|
fmt.Println("migrate success")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||||
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/config"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/config"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/repl"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/repl"
|
||||||
|
@ -44,7 +44,13 @@ func serve(configPath string) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
stgglb.InitPools(&hubrpc.PoolConfig{}, nil)
|
hubRPCCfg, err := config.Cfg().HubRPC.Build(nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("build hub rpc config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stgglb.InitPools(hubRPCCfg, nil)
|
||||||
|
|
||||||
db2, err := db.NewDB(&config.Cfg().DB)
|
db2, err := db.NewDB(&config.Cfg().DB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -59,13 +65,18 @@ func serve(configPath string) {
|
||||||
// }
|
// }
|
||||||
// go servePublisher(evtPub)
|
// go servePublisher(evtPub)
|
||||||
|
|
||||||
|
// 客户端访问令牌缓存
|
||||||
|
accToken := accesstoken.New(db2)
|
||||||
|
accTokenChan := accToken.Start()
|
||||||
|
defer accToken.Stop()
|
||||||
|
|
||||||
// RPC服务
|
// RPC服务
|
||||||
rpcSvr := corrpc.NewServer(config.Cfg().RPC, myrpc.NewService(db2))
|
rpcSvr := corrpc.NewServer(config.Cfg().RPC, myrpc.NewService(db2, accToken), accToken)
|
||||||
rpcSvrChan := rpcSvr.Start()
|
rpcSvrChan := rpcSvr.Start()
|
||||||
defer rpcSvr.Stop()
|
defer rpcSvr.Stop()
|
||||||
|
|
||||||
// 定时任务
|
// 定时任务
|
||||||
tktk := ticktock.New(config.Cfg().TickTock, db2)
|
tktk := ticktock.New(config.Cfg().TickTock, db2, accToken)
|
||||||
tktk.Start()
|
tktk.Start()
|
||||||
defer tktk.Stop()
|
defer tktk.Stop()
|
||||||
|
|
||||||
|
@ -74,11 +85,29 @@ func serve(configPath string) {
|
||||||
replCh := rep.Start()
|
replCh := rep.Start()
|
||||||
|
|
||||||
/// 开始监听各个模块的事件
|
/// 开始监听各个模块的事件
|
||||||
|
accTokenEvt := accTokenChan.Receive()
|
||||||
replEvt := replCh.Receive()
|
replEvt := replCh.Receive()
|
||||||
rpcEvt := rpcSvrChan.Receive()
|
rpcEvt := rpcSvrChan.Receive()
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case e := <-accTokenEvt.Chan():
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("receive access token event: %v", e.Err)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := e.Value.(type) {
|
||||||
|
case accesstoken.ExitEvent:
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("access token cache exited with error: %v", e.Err)
|
||||||
|
} else {
|
||||||
|
logger.Info("access token cache exited")
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
accTokenEvt = accTokenChan.Receive()
|
||||||
|
|
||||||
case e := <-replEvt.Chan():
|
case e := <-replEvt.Chan():
|
||||||
if e.Err != nil {
|
if e.Err != nil {
|
||||||
logger.Errorf("receive repl event: %v", err)
|
logger.Errorf("receive repl event: %v", err)
|
||||||
|
|
|
@ -4,15 +4,17 @@ import (
|
||||||
log "gitlink.org.cn/cloudream/common/pkgs/logger"
|
log "gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
c "gitlink.org.cn/cloudream/common/utils/config"
|
c "gitlink.org.cn/cloudream/common/utils/config"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/ticktock"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/ticktock"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Logger log.Config `json:"logger"`
|
Logger log.Config `json:"logger"`
|
||||||
DB db.Config `json:"db"`
|
DB db.Config `json:"db"`
|
||||||
TickTock ticktock.Config `json:"tickTock"`
|
TickTock ticktock.Config `json:"tickTock"`
|
||||||
RPC rpc.Config `json:"rpc"`
|
RPC rpc.Config `json:"rpc"`
|
||||||
|
HubRPC hubrpc.PoolConfigJSON `json:"hubRPC"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg Config
|
var cfg Config
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoadedAccessTokenDB struct {
|
||||||
|
*DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) LoadedAccessToken() *LoadedAccessTokenDB {
|
||||||
|
return &LoadedAccessTokenDB{DB: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LoadedAccessTokenDB) GetByUserIDAndTokenID(ctx SQLContext, userID cortypes.UserID, tokenID cortypes.AccessTokenID) ([]cortypes.LoadedAccessToken, error) {
|
||||||
|
var ret []cortypes.LoadedAccessToken
|
||||||
|
err := ctx.Table("LoadedAccessToken").Where("UserID = ? AND TokenID = ?", userID, tokenID).Find(&ret).Error
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadedAccessTokenDB) CreateOrUpdate(ctx SQLContext, token cortypes.LoadedAccessToken) error {
|
||||||
|
return ctx.Clauses(clause.OnConflict{
|
||||||
|
Columns: []clause.Column{{Name: "UserID"}, {Name: "TokenID"}, {Name: "HubID"}},
|
||||||
|
DoUpdates: clause.AssignmentColumns([]string{"LoadedAt"}),
|
||||||
|
}).Create(token).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadedAccessTokenDB) GetExpired(ctx SQLContext, expireAt time.Time) ([]cortypes.LoadedAccessToken, error) {
|
||||||
|
var ret []cortypes.LoadedAccessToken
|
||||||
|
err := ctx.Table("LoadedAccessToken").
|
||||||
|
Select("LoadedAccessToken.*").
|
||||||
|
Joins("join UserAccessToken on UserAccessToken.UserID = LoadedAccessToken.UserID and UserAccessToken.TokenID = LoadedAccessToken.TokenID").
|
||||||
|
Where("UserAccessToken.ExpiresAt < ?", expireAt).
|
||||||
|
Find(&ret).Error
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LoadedAccessTokenDB) DeleteExpired(ctx SQLContext, expireAt time.Time) error {
|
||||||
|
return ctx.Table("LoadedAccessToken").
|
||||||
|
Where("UserID in (select UserID from UserAccessToken where ExpiresAt < ?)", expireAt).
|
||||||
|
Delete(&cortypes.LoadedAccessToken{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *LoadedAccessTokenDB) DeleteAllByUserIDAndTokenID(ctx SQLContext, userID cortypes.UserID, tokenID cortypes.AccessTokenID) error {
|
||||||
|
return ctx.Table("LoadedAccessToken").Where("UserID = ? AND TokenID = ?", userID, tokenID).Delete(&cortypes.LoadedAccessToken{}).Error
|
||||||
|
}
|
|
@ -19,14 +19,14 @@ func (db *UserDB) GetByID(ctx SQLContext, userID cortypes.UserID) (cortypes.User
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *UserDB) GetByName(ctx SQLContext, name string) (cortypes.User, error) {
|
func (db *UserDB) GetByAccount(ctx SQLContext, account string) (cortypes.User, error) {
|
||||||
var ret cortypes.User
|
var ret cortypes.User
|
||||||
err := ctx.Table("User").Where("Name = ?", name).First(&ret).Error
|
err := ctx.Table("User").Where("Account = ?", account).First(&ret).Error
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *UserDB) Create(ctx SQLContext, name string) (cortypes.User, error) {
|
func (db *UserDB) Create(ctx SQLContext, account string, password string, nickName string) (cortypes.User, error) {
|
||||||
_, err := db.GetByName(ctx, name)
|
_, err := db.GetByAccount(ctx, account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return cortypes.User{}, gorm.ErrDuplicatedKey
|
return cortypes.User{}, gorm.ErrDuplicatedKey
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ func (db *UserDB) Create(ctx SQLContext, name string) (cortypes.User, error) {
|
||||||
return cortypes.User{}, err
|
return cortypes.User{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
user := cortypes.User{Name: name}
|
user := cortypes.User{NickName: nickName, Account: account, Password: password}
|
||||||
err = ctx.Table("User").Create(&user).Error
|
err = ctx.Table("User").Create(&user).Error
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserAccessTokenDB struct {
|
||||||
|
*DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) UserAccessToken() *UserAccessTokenDB {
|
||||||
|
return &UserAccessTokenDB{DB: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *UserAccessTokenDB) GetByID(ctx SQLContext, userID cortypes.UserID, tokenID cortypes.AccessTokenID) (cortypes.UserAccessToken, error) {
|
||||||
|
var ret cortypes.UserAccessToken
|
||||||
|
err := ctx.Table("UserAccessToken").Where("UserID = ? AND TokenID = ?", userID, tokenID).First(&ret).Error
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UserAccessTokenDB) Create(ctx SQLContext, token *cortypes.UserAccessToken) error {
|
||||||
|
return ctx.Table("UserAccessToken").Create(token).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *UserAccessTokenDB) DeleteByID(ctx SQLContext, userID cortypes.UserID, tokenID cortypes.AccessTokenID) error {
|
||||||
|
return ctx.Table("UserAccessToken").Where("UserID = ? AND TokenID = ?", userID, tokenID).Delete(&cortypes.UserAccessToken{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UserAccessTokenDB) DeleteExpired(ctx SQLContext, expireTime time.Time) error {
|
||||||
|
return ctx.Table("UserAccessToken").Where("ExpiresAt < ?", expireTime).Delete(&cortypes.UserAccessToken{}).Error
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package repl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
userCmd := &cobra.Command{
|
||||||
|
Use: "user",
|
||||||
|
Short: "user command",
|
||||||
|
}
|
||||||
|
RootCmd.AddCommand(userCmd)
|
||||||
|
|
||||||
|
createCmd := &cobra.Command{
|
||||||
|
Use: "create [account] [nickName]",
|
||||||
|
Short: "create a new user account",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
userCreate(GetCmdCtx(cmd), args[0], args[1])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
userCmd.AddCommand(createCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func userCreate(ctx *CommandContext, account string, nickName string) {
|
||||||
|
_, err := ctx.repl.db.User().GetByAccount(ctx.repl.db.DefCtx(), account)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("user %s already exists\n", account)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("input account password: ")
|
||||||
|
pass, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error reading password:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
passHash, err := bcrypt.GenerateFromPassword(pass, bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error hashing password:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := db.DoTx02(ctx.repl.db, func(tx db.SQLContext) (cortypes.User, error) {
|
||||||
|
return ctx.repl.db.User().Create(tx, account, hex.EncodeToString(passHash), nickName)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error creating user:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("user %s created\n", user.Account)
|
||||||
|
}
|
|
@ -1,15 +1,18 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
db *db.DB
|
db *db.DB
|
||||||
|
accessToken *accesstoken.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(db *db.DB) *Service {
|
func NewService(db *db.DB, accessToken *accesstoken.Cache) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
db: db,
|
db: db,
|
||||||
|
accessToken: accessToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
|
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||||
|
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (svc *Service) UserLogin(ctx context.Context, msg *corrpc.UserLogin) (*corrpc.UserLoginResp, *rpc.CodeError) {
|
||||||
|
log := logger.WithField("Account", msg.Account)
|
||||||
|
|
||||||
|
user, err := svc.db.User().GetByAccount(svc.db.DefCtx(), msg.Account)
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
log.Warnf("account not found")
|
||||||
|
return nil, rpc.Failed(errorcode.DataNotFound, "account not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Warnf("getting account: %v", err)
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "getting account: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbPass, err := hex.DecodeString(user.Password)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("decoding password: %v", err)
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "decoding password: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bcrypt.CompareHashAndPassword(dbPass, []byte(msg.Password)) != nil {
|
||||||
|
log.Warnf("password not match")
|
||||||
|
return nil, rpc.Failed(errorcode.Unauthorized, "password not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, priKey, err := ed25519.GenerateKey(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("generating key: %v", err)
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "generating key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyStr := hex.EncodeToString(pubKey)
|
||||||
|
nowTime := time.Now()
|
||||||
|
token := cortypes.UserAccessToken{
|
||||||
|
UserID: user.UserID,
|
||||||
|
TokenID: cortypes.AccessTokenID(uuid.NewString()),
|
||||||
|
PublicKey: pubKeyStr,
|
||||||
|
ExpiresAt: nowTime.Add(time.Hour),
|
||||||
|
CreatedAt: nowTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = svc.db.UserAccessToken().Create(svc.db.DefCtx(), &token)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("creating token: %v", err)
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "creating token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("login success, token expires at %v", token.ExpiresAt)
|
||||||
|
|
||||||
|
return &corrpc.UserLoginResp{
|
||||||
|
Token: token,
|
||||||
|
PrivateKey: hex.EncodeToString(priKey),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) UserRefreshToken(ctx context.Context, msg *corrpc.UserRefreshToken) (*corrpc.UserRefreshTokenResp, *rpc.CodeError) {
|
||||||
|
authInfo, ok := rpc.GetAuthInfo(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, rpc.Failed(errorcode.Unauthorized, "unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logger.WithField("UserID", authInfo.UserID).WithField("TokenID", authInfo.AccessTokenID)
|
||||||
|
|
||||||
|
pubKey, priKey, err := ed25519.GenerateKey(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("generating key: %v", err)
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "generating key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyStr := hex.EncodeToString(pubKey)
|
||||||
|
nowTime := time.Now()
|
||||||
|
token := cortypes.UserAccessToken{
|
||||||
|
UserID: authInfo.UserID,
|
||||||
|
TokenID: cortypes.AccessTokenID(uuid.NewString()),
|
||||||
|
PublicKey: pubKeyStr,
|
||||||
|
ExpiresAt: nowTime.Add(time.Hour),
|
||||||
|
CreatedAt: nowTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = svc.db.UserAccessToken().Create(svc.db.DefCtx(), &token)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("creating token: %v", err)
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "creating token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("refresh token success, new token expires at %v", token.ExpiresAt)
|
||||||
|
|
||||||
|
return &corrpc.UserRefreshTokenResp{
|
||||||
|
Token: token,
|
||||||
|
PrivateKey: hex.EncodeToString(priKey),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) UserLogout(ctx context.Context, msg *corrpc.UserLogout) (*corrpc.UserLogoutResp, *rpc.CodeError) {
|
||||||
|
authInfo, ok := rpc.GetAuthInfo(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, rpc.Failed(errorcode.Unauthorized, "unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
log := logger.WithField("UserID", authInfo.UserID).WithField("TokenID", authInfo.AccessTokenID)
|
||||||
|
|
||||||
|
loaded, err := db.DoTx02(svc.db, func(tx db.SQLContext) ([]cortypes.LoadedAccessToken, error) {
|
||||||
|
token, err := svc.db.UserAccessToken().GetByID(tx, authInfo.UserID, authInfo.AccessTokenID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = svc.db.UserAccessToken().DeleteByID(tx, token.UserID, token.TokenID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded, err := svc.db.LoadedAccessToken().GetByUserIDAndTokenID(tx, token.UserID, token.TokenID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = svc.db.LoadedAccessToken().DeleteAllByUserIDAndTokenID(tx, token.UserID, token.TokenID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaded, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("delete access token: %v", err)
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, rpc.Failed(errorcode.DataNotFound, "token not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "delete access token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.accessToken.NotifyTokenInvalid(accesstoken.CacheKey{
|
||||||
|
UserID: authInfo.UserID,
|
||||||
|
TokenID: authInfo.AccessTokenID,
|
||||||
|
})
|
||||||
|
|
||||||
|
var loadedHubIDs []cortypes.HubID
|
||||||
|
for _, l := range loaded {
|
||||||
|
loadedHubIDs = append(loadedHubIDs, l.HubID)
|
||||||
|
}
|
||||||
|
|
||||||
|
svc.notifyLoadedHubs(authInfo.UserID, authInfo.AccessTokenID, loadedHubIDs)
|
||||||
|
|
||||||
|
return &corrpc.UserLogoutResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) notifyLoadedHubs(userID cortypes.UserID, tokenID cortypes.AccessTokenID, loadedHubIDs []cortypes.HubID) {
|
||||||
|
log := logger.WithField("UserID", userID).WithField("TokenID", tokenID)
|
||||||
|
|
||||||
|
loadedHubs, err := svc.db.Hub().BatchGetByID(svc.db.DefCtx(), loadedHubIDs)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("getting hubs: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range loadedHubs {
|
||||||
|
addr, ok := l.Address.(*cortypes.GRPCAddressInfo)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := stgglb.HubRPCPool.Get(addr.ExternalIP, addr.ExternalGRPCPort)
|
||||||
|
// 不关心返回值
|
||||||
|
cli.NotifyUserAccessTokenInvalid(context.Background(), &hubrpc.NotifyUserAccessTokenInvalid{
|
||||||
|
UserID: userID,
|
||||||
|
TokenID: tokenID,
|
||||||
|
})
|
||||||
|
cli.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (svc *Service) HubLoadAccessToken(ctx context.Context, msg *corrpc.HubLoadAccessToken) (*corrpc.HubLoadAccessTokenResp, *rpc.CodeError) {
|
||||||
|
token, err := db.DoTx02(svc.db, func(tx db.SQLContext) (cortypes.UserAccessToken, error) {
|
||||||
|
token, err := svc.db.UserAccessToken().GetByID(tx, msg.UserID, msg.TokenID)
|
||||||
|
if err != nil {
|
||||||
|
return cortypes.UserAccessToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = svc.db.LoadedAccessToken().CreateOrUpdate(tx, cortypes.LoadedAccessToken{
|
||||||
|
UserID: msg.UserID,
|
||||||
|
TokenID: msg.TokenID,
|
||||||
|
HubID: msg.HubID,
|
||||||
|
LoadedAt: time.Now(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return cortypes.UserAccessToken{}, fmt.Errorf("creating access token loaded record: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
return nil, rpc.Failed(errorcode.DataNotFound, "token not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, rpc.Failed(errorcode.OperationFailed, "loading access token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &corrpc.HubLoadAccessTokenResp{
|
||||||
|
Token: token,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package ticktock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
|
"gitlink.org.cn/cloudream/common/utils/reflect2"
|
||||||
|
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
|
||||||
|
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
|
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClearExpiredAccessToken struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *ClearExpiredAccessToken) Name() string {
|
||||||
|
return reflect2.TypeNameOf[ClearExpiredAccessToken]()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *ClearExpiredAccessToken) Execute(t *TickTock) {
|
||||||
|
log := logger.WithType[ClearExpiredAccessToken]("TickTock")
|
||||||
|
log.Infof("job start")
|
||||||
|
startTime := time.Now()
|
||||||
|
defer func() {
|
||||||
|
log.Infof("job end, time: %v", time.Since(startTime))
|
||||||
|
}()
|
||||||
|
|
||||||
|
expired, err := db.DoTx02(t.db, func(tx db.SQLContext) ([]cortypes.LoadedAccessToken, error) {
|
||||||
|
nowTime := time.Now()
|
||||||
|
expired, err := t.db.LoadedAccessToken().GetExpired(tx, nowTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get expired access token load record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.db.LoadedAccessToken().DeleteExpired(tx, nowTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("delete expired access token load record: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.db.UserAccessToken().DeleteExpired(tx, nowTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("delete expired user access token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return expired, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uniToken := make(map[accesstoken.CacheKey]bool)
|
||||||
|
for _, t := range expired {
|
||||||
|
uniToken[accesstoken.CacheKey{
|
||||||
|
UserID: t.UserID,
|
||||||
|
TokenID: t.TokenID,
|
||||||
|
}] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("%v expired access token cleared", len(uniToken))
|
||||||
|
|
||||||
|
// 通知本服务的AccessToken缓存失效
|
||||||
|
for k := range uniToken {
|
||||||
|
t.accessToken.NotifyTokenInvalid(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通知所有加载了失效Token的Hub
|
||||||
|
|
||||||
|
var loadedHubIDs []cortypes.HubID
|
||||||
|
for _, e := range expired {
|
||||||
|
loadedHubIDs = append(loadedHubIDs, e.HubID)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedHubs, err := t.db.Hub().BatchGetByID(t.db.DefCtx(), loadedHubIDs)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("getting hubs: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hubMap := make(map[cortypes.HubID]cortypes.Hub)
|
||||||
|
for _, h := range loadedHubs {
|
||||||
|
hubMap[h.HubID] = h
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range expired {
|
||||||
|
h, ok := hubMap[e.HubID]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr, ok := h.Address.(*cortypes.GRPCAddressInfo)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cli := stgglb.HubRPCPool.Get(addr.ExternalIP, addr.ExternalGRPCPort)
|
||||||
|
// 不关心返回值
|
||||||
|
_, err := cli.NotifyUserAccessTokenInvalid(context.Background(), &hubrpc.NotifyUserAccessTokenInvalid{
|
||||||
|
UserID: e.UserID,
|
||||||
|
TokenID: e.TokenID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("notify hub %v: %v", h.HubID, err)
|
||||||
|
}
|
||||||
|
cli.Release()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-co-op/gocron/v2"
|
"github.com/go-co-op/gocron/v2"
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
"gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,19 +21,21 @@ type cronJob struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type TickTock struct {
|
type TickTock struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
sch gocron.Scheduler
|
sch gocron.Scheduler
|
||||||
jobs map[string]cronJob
|
jobs map[string]cronJob
|
||||||
db *db.DB
|
db *db.DB
|
||||||
|
accessToken *accesstoken.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg Config, db *db.DB) *TickTock {
|
func New(cfg Config, db *db.DB, accessToken *accesstoken.Cache) *TickTock {
|
||||||
sch, _ := gocron.NewScheduler()
|
sch, _ := gocron.NewScheduler()
|
||||||
t := &TickTock{
|
t := &TickTock{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
sch: sch,
|
sch: sch,
|
||||||
jobs: map[string]cronJob{},
|
jobs: map[string]cronJob{},
|
||||||
db: db,
|
db: db,
|
||||||
|
accessToken: accessToken,
|
||||||
}
|
}
|
||||||
t.initJobs()
|
t.initJobs()
|
||||||
return t
|
return t
|
||||||
|
@ -70,4 +73,6 @@ func (t *TickTock) addJob(job Job, duration gocron.JobDefinition) {
|
||||||
|
|
||||||
func (t *TickTock) initJobs() {
|
func (t *TickTock) initJobs() {
|
||||||
t.addJob(&CheckHubState{}, gocron.DurationJob(time.Minute*5))
|
t.addJob(&CheckHubState{}, gocron.DurationJob(time.Minute*5))
|
||||||
|
|
||||||
|
t.addJob(&ClearExpiredAccessToken{}, gocron.DurationJob(time.Minute*5))
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,15 +68,6 @@ func (HubConnectivity) TableName() string {
|
||||||
return "HubConnectivity"
|
return "HubConnectivity"
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
|
||||||
UserID UserID `gorm:"column:UserID; primaryKey; type:bigint; autoIncrement" json:"userID"`
|
|
||||||
Name string `gorm:"column:Name; type:varchar(255); not null" json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (User) TableName() string {
|
|
||||||
return "User"
|
|
||||||
}
|
|
||||||
|
|
||||||
type HubLocation struct {
|
type HubLocation struct {
|
||||||
HubID HubID `gorm:"column:HubID; type:bigint" json:"hubID"`
|
HubID HubID `gorm:"column:HubID; type:bigint" json:"hubID"`
|
||||||
StorageName string `gorm:"column:StorageName; type:varchar(255); not null" json:"storageName"`
|
StorageName string `gorm:"column:StorageName; type:varchar(255); not null" json:"storageName"`
|
||||||
|
@ -86,3 +77,40 @@ type HubLocation struct {
|
||||||
func (HubLocation) TableName() string {
|
func (HubLocation) TableName() string {
|
||||||
return "HubLocation"
|
return "HubLocation"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
UserID UserID `gorm:"column:UserID; primaryKey; type:bigint; autoIncrement" json:"userID"`
|
||||||
|
NickName string `gorm:"column:NickName; type:varchar(255); not null" json:"nickName"`
|
||||||
|
Account string `gorm:"column:Account; type:varchar(255); not null" json:"account"`
|
||||||
|
// bcrypt哈希过的密码,带有盐值
|
||||||
|
Password string `gorm:"column:Password; type:varchar(255); not null" json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (User) TableName() string {
|
||||||
|
return "User"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessTokenID string
|
||||||
|
|
||||||
|
type UserAccessToken struct {
|
||||||
|
UserID UserID `gorm:"column:UserID; primaryKey; type:bigint" json:"userID"`
|
||||||
|
TokenID AccessTokenID `gorm:"column:TokenID; primaryKey; type:char(36); not null" json:"tokenID"`
|
||||||
|
PublicKey string `gorm:"column:PublicKey; type:char(64); not null" json:"publicKey"`
|
||||||
|
ExpiresAt time.Time `gorm:"column:ExpiresAt; type:datetime" json:"expiresAt"`
|
||||||
|
CreatedAt time.Time `gorm:"column:CreatedAt; type:datetime" json:"createdAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UserAccessToken) TableName() string {
|
||||||
|
return "UserAccessToken"
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadedAccessToken struct {
|
||||||
|
UserID UserID `gorm:"column:UserID; primaryKey; type:bigint" json:"userID"`
|
||||||
|
TokenID AccessTokenID `gorm:"column:TokenID; primaryKey; type:char(36); not null" json:"tokenID"`
|
||||||
|
HubID HubID `gorm:"column:HubID; primaryKey; type:bigint" json:"hubID"`
|
||||||
|
LoadedAt time.Time `gorm:"column:LoadedAt; type:datetime" json:"loadedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (LoadedAccessToken) TableName() string {
|
||||||
|
return "LoadedAccessToken"
|
||||||
|
}
|
||||||
|
|
10
go.mod
10
go.mod
|
@ -28,10 +28,11 @@ require (
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
gitlink.org.cn/cloudream/common v0.0.0
|
gitlink.org.cn/cloudream/common v0.0.0
|
||||||
|
golang.org/x/crypto v0.38.0
|
||||||
golang.org/x/net v0.35.0
|
golang.org/x/net v0.35.0
|
||||||
golang.org/x/sync v0.13.0
|
golang.org/x/sync v0.14.0
|
||||||
golang.org/x/sys v0.32.0
|
golang.org/x/sys v0.33.0
|
||||||
golang.org/x/term v0.31.0
|
golang.org/x/term v0.32.0
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.67.1
|
||||||
google.golang.org/protobuf v1.36.6
|
google.golang.org/protobuf v1.36.6
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
|
@ -70,9 +71,8 @@ require (
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
go.mongodb.org/mongo-driver v1.12.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||||
golang.org/x/text v0.24.0 // indirect
|
golang.org/x/text v0.25.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|
20
go.sum
20
go.sum
|
@ -243,8 +243,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||||
|
@ -275,8 +275,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -300,16 +300,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
@ -319,8 +319,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package accesstoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/common/consts/errorcode"
|
||||||
|
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
|
||||||
|
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||||
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExitEvent = accesstoken.ExitEvent
|
||||||
|
|
||||||
|
type CacheKey = accesstoken.CacheKey
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
localHubID cortypes.HubID
|
||||||
|
*accesstoken.Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(localHubID cortypes.HubID) *Cache {
|
||||||
|
c := &Cache{
|
||||||
|
localHubID: localHubID,
|
||||||
|
}
|
||||||
|
c.Cache = accesstoken.New(c.load)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) load(key accesstoken.CacheKey) (cortypes.UserAccessToken, error) {
|
||||||
|
corCli := stgglb.CoordinatorRPCPool.Get()
|
||||||
|
defer corCli.Release()
|
||||||
|
|
||||||
|
tokenResp, cerr := corCli.HubLoadAccessToken(context.Background(), &corrpc.HubLoadAccessToken{
|
||||||
|
UserID: key.UserID,
|
||||||
|
TokenID: key.TokenID,
|
||||||
|
HubID: c.localHubID,
|
||||||
|
})
|
||||||
|
if cerr != nil {
|
||||||
|
if cerr.Code == errorcode.DataNotFound {
|
||||||
|
return cortypes.UserAccessToken{}, accesstoken.ErrTokenNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
return cortypes.UserAccessToken{}, cerr.ToError()
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenResp.Token, nil
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/accesstoken"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/http"
|
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/http"
|
||||||
myrpc "gitlink.org.cn/cloudream/jcs-pub/hub/internal/rpc"
|
myrpc "gitlink.org.cn/cloudream/jcs-pub/hub/internal/rpc"
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ import (
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/config"
|
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/config"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/ticktock"
|
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/ticktock"
|
||||||
|
|
||||||
coormq "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -47,22 +48,36 @@ type serveOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func serve(configPath string, opts serveOptions) {
|
func serve(configPath string, opts serveOptions) {
|
||||||
|
// 加载服务配置
|
||||||
err := config.Init(configPath)
|
err := config.Init(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("init config failed, err: %s", err.Error())
|
fmt.Printf("init config failed, err: %s", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化日志
|
||||||
err = logger.Init(&config.Cfg().Logger)
|
err = logger.Init(&config.Cfg().Logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("init logger failed, err: %s", err.Error())
|
fmt.Printf("init logger failed, err: %s", err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化全局变量
|
||||||
stgglb.InitLocal(config.Cfg().Local)
|
stgglb.InitLocal(config.Cfg().Local)
|
||||||
stgglb.InitPools(&hubrpc.PoolConfig{}, &config.Cfg().CoordinatorRPC)
|
|
||||||
// stgglb.Stats.SetupHubStorageTransfer(*config.Cfg().Local.HubID)
|
// 初始化各服务客户端的连接池
|
||||||
// stgglb.Stats.SetupHubTransfer(*config.Cfg().Local.HubID)
|
corRPCCfg, err := config.Cfg().CoordinatorRPC.Build(nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("building coordinator rpc config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
hubRPCCfg, err := config.Cfg().HubRPC.Build(nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("building hub rpc config: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
stgglb.InitPools(hubRPCCfg, corRPCCfg)
|
||||||
|
|
||||||
// 获取Hub配置
|
// 获取Hub配置
|
||||||
hubCfg := downloadHubConfig()
|
hubCfg := downloadHubConfig()
|
||||||
|
|
||||||
|
@ -109,13 +124,19 @@ func serve(configPath string, opts serveOptions) {
|
||||||
tktk.Start()
|
tktk.Start()
|
||||||
defer tktk.Stop()
|
defer tktk.Stop()
|
||||||
|
|
||||||
|
// 客户端访问令牌管理器
|
||||||
|
accToken := accesstoken.New(config.Cfg().ID)
|
||||||
|
accTokenChan := accToken.Start()
|
||||||
|
defer accToken.Stop()
|
||||||
|
|
||||||
// RPC服务
|
// RPC服务
|
||||||
rpcSvr := hubrpc.NewServer(config.Cfg().RPC, myrpc.NewService(&worker, stgPool))
|
rpcSvr := hubrpc.NewServer(config.Cfg().RPC, myrpc.NewService(&worker, stgPool, accToken), accToken)
|
||||||
rpcSvrChan := rpcSvr.Start()
|
rpcSvrChan := rpcSvr.Start()
|
||||||
defer rpcSvr.Stop()
|
defer rpcSvr.Stop()
|
||||||
|
|
||||||
/// 开始监听各个模块的事件
|
/// 开始监听各个模块的事件
|
||||||
evtPubEvt := evtPubChan.Receive()
|
evtPubEvt := evtPubChan.Receive()
|
||||||
|
accTokenEvt := accTokenChan.Receive()
|
||||||
rpcEvt := rpcSvrChan.Receive()
|
rpcEvt := rpcSvrChan.Receive()
|
||||||
httpEvt := httpChan.Receive()
|
httpEvt := httpChan.Receive()
|
||||||
|
|
||||||
|
@ -145,6 +166,23 @@ loop:
|
||||||
}
|
}
|
||||||
evtPubEvt = evtPubChan.Receive()
|
evtPubEvt = evtPubChan.Receive()
|
||||||
|
|
||||||
|
case e := <-accTokenEvt.Chan():
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("receive access token event: %v", err)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e := e.Value.(type) {
|
||||||
|
case accesstoken.ExitEvent:
|
||||||
|
if e.Err != nil {
|
||||||
|
logger.Errorf("access token manager exited with error: %v", e.Err)
|
||||||
|
} else {
|
||||||
|
logger.Info("access token manager exited")
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
accTokenEvt = accTokenChan.Receive()
|
||||||
|
|
||||||
case e := <-rpcEvt.Chan():
|
case e := <-rpcEvt.Chan():
|
||||||
if e.Err != nil {
|
if e.Err != nil {
|
||||||
logger.Errorf("receive rpc event: %v", e.Err)
|
logger.Errorf("receive rpc event: %v", e.Err)
|
||||||
|
@ -179,14 +217,14 @@ loop:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadHubConfig() coormq.GetHubConfigResp {
|
func downloadHubConfig() corrpc.GetHubConfigResp {
|
||||||
coorCli := stgglb.CoordinatorRPCPool.Get()
|
coorCli := stgglb.CoordinatorRPCPool.Get()
|
||||||
defer coorCli.Release()
|
defer coorCli.Release()
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
cfgResp, cerr := coorCli.GetHubConfig(ctx, coormq.ReqGetHubConfig(cortypes.HubID(config.Cfg().ID)))
|
cfgResp, cerr := coorCli.GetHubConfig(ctx, corrpc.ReqGetHubConfig(cortypes.HubID(config.Cfg().ID)))
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
logger.Errorf("getting hub config: %v", cerr)
|
logger.Errorf("getting hub config: %v", cerr)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||||
|
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/sysevent"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/sysevent"
|
||||||
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/http"
|
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/http"
|
||||||
|
@ -17,7 +18,8 @@ type Config struct {
|
||||||
Local stgglb.LocalMachineInfo `json:"local"`
|
Local stgglb.LocalMachineInfo `json:"local"`
|
||||||
RPC rpc.Config `json:"rpc"`
|
RPC rpc.Config `json:"rpc"`
|
||||||
HTTP *http.Config `json:"http"`
|
HTTP *http.Config `json:"http"`
|
||||||
CoordinatorRPC corrpc.PoolConfig `json:"coordinatorRPC"`
|
CoordinatorRPC corrpc.PoolConfigJSON `json:"coordinatorRPC"`
|
||||||
|
HubRPC hubrpc.PoolConfigJSON `json:"hubRPC"`
|
||||||
Logger log.Config `json:"logger"`
|
Logger log.Config `json:"logger"`
|
||||||
SysEvent sysevent.Config `json:"sysEvent"`
|
SysEvent sysevent.Config `json:"sysEvent"`
|
||||||
TickTock ticktock.Config `json:"tickTock"`
|
TickTock ticktock.Config `json:"tickTock"`
|
||||||
|
|
|
@ -4,17 +4,20 @@ import (
|
||||||
"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
|
"gitlink.org.cn/cloudream/common/pkgs/ioswitch/exec"
|
||||||
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/hub/internal/accesstoken"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
swWorker *exec.Worker
|
swWorker *exec.Worker
|
||||||
stgPool *pool.Pool
|
stgPool *pool.Pool
|
||||||
|
accessToken *accesstoken.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(swWorker *exec.Worker, stgPool *pool.Pool) *Service {
|
func NewService(swWorker *exec.Worker, stgPool *pool.Pool, accessToken *accesstoken.Cache) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
swWorker: swWorker,
|
swWorker: swWorker,
|
||||||
stgPool: stgPool,
|
stgPool: stgPool,
|
||||||
|
accessToken: accessToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
|
||||||
|
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
|
||||||
|
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) NotifyUserAccessTokenInvalid(ctx context.Context, msg *hubrpc.NotifyUserAccessTokenInvalid) (*hubrpc.NotifyUserAccessTokenInvalidResp, *rpc.CodeError) {
|
||||||
|
s.accessToken.NotifyTokenInvalid(accesstoken.CacheKey{
|
||||||
|
UserID: msg.UserID,
|
||||||
|
TokenID: msg.TokenID,
|
||||||
|
})
|
||||||
|
logger.WithField("UserID", msg.UserID).WithField("TokenID", msg.TokenID).Infof("user access token invalid")
|
||||||
|
return &hubrpc.NotifyUserAccessTokenInvalidResp{}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue