From 8ff63313fdda84c9e1e7d90fae5d37ca5e22b1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A5=A5=E8=B6=85?= Date: Wed, 20 Jan 2021 10:08:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E7=94=A8=E6=88=B7=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E4=B8=8A=E4=BC=A0=E7=8A=B6=E6=80=81=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/db/models/user_node_dao.go | 22 ++++- internal/rpc/services/service_base.go | 105 +++++++++++++++++++++ internal/rpc/services/service_user_node.go | 26 +++++ 3 files changed, 151 insertions(+), 2 deletions(-) diff --git a/internal/db/models/user_node_dao.go b/internal/db/models/user_node_dao.go index 56dc1b39..8a478060 100644 --- a/internal/db/models/user_node_dao.go +++ b/internal/db/models/user_node_dao.go @@ -136,7 +136,7 @@ func (this *UserNodeDAO) FindEnabledUserNodeIdWithAddr(tx *dbs.Tx, protocol stri // 创建用户节点 func (this *UserNodeDAO) CreateUserNode(tx *dbs.Tx, name string, description string, httpJSON []byte, httpsJSON []byte, accessAddrsJSON []byte, isOn bool) (nodeId int64, err error) { - uniqueId, err := this.genUniqueId(tx) + uniqueId, err := this.GenUniqueId(tx) if err != nil { return 0, err } @@ -216,8 +216,17 @@ func (this *UserNodeDAO) FindEnabledUserNodeWithUniqueId(tx *dbs.Tx, uniqueId st return result.(*UserNode), err } +// 根据唯一ID获取节点ID +func (this *UserNodeDAO) FindEnabledUserNodeIdWithUniqueId(tx *dbs.Tx, uniqueId string) (int64, error) { + return this.Query(tx). + Attr("uniqueId", uniqueId). + Attr("state", UserNodeStateEnabled). + ResultPk(). + FindInt64Col(0) +} + // 生成唯一ID -func (this *UserNodeDAO) genUniqueId(tx *dbs.Tx) (string, error) { +func (this *UserNodeDAO) GenUniqueId(tx *dbs.Tx) (string, error) { for { uniqueId := rands.HexString(32) ok, err := this.Query(tx). @@ -232,3 +241,12 @@ func (this *UserNodeDAO) genUniqueId(tx *dbs.Tx) (string, error) { return uniqueId, nil } } + +// 更改节点状态 +func (this *UserNodeDAO) UpdateNodeStatus(tx *dbs.Tx, nodeId int64, statusJSON []byte) error { + _, err := this.Query(tx). + Pk(nodeId). + Set("status", string(statusJSON)). + Update() + return err +} diff --git a/internal/rpc/services/service_base.go b/internal/rpc/services/service_base.go index 3d3f7887..0af3d2a6 100644 --- a/internal/rpc/services/service_base.go +++ b/internal/rpc/services/service_base.go @@ -2,10 +2,20 @@ package services import ( "context" + "encoding/base64" + "encoding/json" + teaconst "github.com/TeaOSLab/EdgeAPI/internal/const" + "github.com/TeaOSLab/EdgeAPI/internal/db/models" + "github.com/TeaOSLab/EdgeAPI/internal/encrypt" "github.com/TeaOSLab/EdgeAPI/internal/errors" rpcutils "github.com/TeaOSLab/EdgeAPI/internal/rpc/utils" + "github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/dbs" + "github.com/iwind/TeaGo/lists" + "github.com/iwind/TeaGo/maps" + "google.golang.org/grpc/metadata" + "time" ) type BaseService struct { @@ -72,6 +82,101 @@ func (this *BaseService) ValidateUser(ctx context.Context) (userId int64, err er return } +// 获取节点ID +func (this *BaseService) ValidateNodeId(ctx context.Context, roles ...rpcutils.UserType) (role rpcutils.UserType, nodeIntId int64, err error) { + if ctx == nil { + err = errors.New("context should not be nil") + role = rpcutils.UserTypeNone + return + } + + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return rpcutils.UserTypeNone, 0, errors.New("context: need 'nodeId'") + } + nodeIds := md.Get("nodeid") + if len(nodeIds) == 0 || len(nodeIds[0]) == 0 { + return rpcutils.UserTypeNone, 0, errors.New("context: need 'nodeId'") + } + nodeId := nodeIds[0] + + // 获取角色Node信息 + // TODO 缓存节点ID相关信息 + apiToken, err := models.SharedApiTokenDAO.FindEnabledTokenWithNode(nil, nodeId) + if err != nil { + return rpcutils.UserTypeNone, 0, err + } + if apiToken == nil { + return rpcutils.UserTypeNone, 0, errors.New("context: can not find api token for node '" + nodeId + "'") + } + if !lists.ContainsString(roles, apiToken.Role) { + return rpcutils.UserTypeNone, 0, errors.New("context: unsupported role '" + apiToken.Role + "'") + } + + tokens := md.Get("token") + if len(tokens) == 0 || len(tokens[0]) == 0 { + return rpcutils.UserTypeNone, 0, errors.New("context: need 'token'") + } + token := tokens[0] + + data, err := base64.StdEncoding.DecodeString(token) + if err != nil { + return rpcutils.UserTypeNone, 0, err + } + + method, err := encrypt.NewMethodInstance(teaconst.EncryptMethod, apiToken.Secret, nodeId) + if err != nil { + utils.PrintError(err) + return rpcutils.UserTypeNone, 0, err + } + data, err = method.Decrypt(data) + if err != nil { + return rpcutils.UserTypeNone, 0, err + } + if len(data) == 0 { + return rpcutils.UserTypeNone, 0, errors.New("invalid token") + } + + m := maps.Map{} + err = json.Unmarshal(data, &m) + if err != nil { + return rpcutils.UserTypeNone, 0, errors.New("decode token error: " + err.Error()) + } + + timestamp := m.GetInt64("timestamp") + if time.Now().Unix()-timestamp > 600 { + // 请求超过10分钟认为超时 + return rpcutils.UserTypeNone, 0, errors.New("authenticate timeout") + } + + switch apiToken.Role { + case rpcutils.UserTypeNode: + nodeIntId, err = models.SharedNodeDAO.FindEnabledNodeIdWithUniqueId(nil, nodeId) + if err != nil { + return rpcutils.UserTypeNode, 0, errors.New("context: " + err.Error()) + } + if nodeIntId <= 0 { + return rpcutils.UserTypeNode, 0, errors.New("context: not found node with id '" + nodeId + "'") + } + case rpcutils.UserTypeCluster: + nodeIntId, err = models.SharedNodeClusterDAO.FindEnabledClusterIdWithUniqueId(nil, nodeId) + if err != nil { + return rpcutils.UserTypeCluster, 0, errors.New("context: " + err.Error()) + } + if nodeIntId <= 0 { + return rpcutils.UserTypeCluster, 0, errors.New("context: not found cluster with id '" + nodeId + "'") + } + case rpcutils.UserTypeUser: + nodeIntId, err = models.SharedUserNodeDAO.FindEnabledUserNodeIdWithUniqueId(nil, nodeId) + case rpcutils.UserTypeAdmin: + nodeIntId = 0 + default: + err = errors.New("unsupported user role '" + apiToken.Role + "'") + } + + return +} + // 返回成功 func (this *BaseService) Success() (*pb.RPCSuccess, error) { return &pb.RPCSuccess{}, nil diff --git a/internal/rpc/services/service_user_node.go b/internal/rpc/services/service_user_node.go index 00c9ff21..2fee4df9 100644 --- a/internal/rpc/services/service_user_node.go +++ b/internal/rpc/services/service_user_node.go @@ -151,6 +151,7 @@ func (this *UserNodeService) ListEnabledUserNodes(ctx context.Context, req *pb.L HttpsJSON: []byte(node.Https), AccessAddrsJSON: []byte(node.AccessAddrs), AccessAddrs: accessAddrs, + StatusJSON: []byte(node.Status), }) } @@ -241,3 +242,28 @@ func (this *UserNodeService) FindCurrentUserNode(ctx context.Context, req *pb.Fi } return &pb.FindCurrentUserNodeResponse{Node: result}, nil } + +// 更新节点状态 +func (this *UserNodeService) UpdateUserNodeStatus(ctx context.Context, req *pb.UpdateUserNodeStatusRequest) (*pb.RPCSuccess, error) { + // 校验节点 + _, nodeId, err := this.ValidateNodeId(ctx, rpcutils.UserTypeUser) + if err != nil { + return nil, err + } + + if req.NodeId > 0 { + nodeId = req.NodeId + } + + if nodeId <= 0 { + return nil, errors.New("'nodeId' should be greater than 0") + } + + tx := this.NullTx() + + err = models.SharedUserNodeDAO.UpdateNodeStatus(tx, nodeId, req.StatusJSON) + if err != nil { + return nil, err + } + return this.Success() +}