You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

user.go 6.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. package rpc
  2. import (
  3. "context"
  4. "crypto/ed25519"
  5. "encoding/hex"
  6. "fmt"
  7. "time"
  8. "github.com/google/uuid"
  9. "gitlink.org.cn/cloudream/common/consts/errorcode"
  10. "gitlink.org.cn/cloudream/common/pkgs/logger"
  11. stgglb "gitlink.org.cn/cloudream/jcs-pub/common/globals"
  12. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/accesstoken"
  13. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc"
  14. corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
  15. hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
  16. "gitlink.org.cn/cloudream/jcs-pub/coordinator/internal/db"
  17. cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
  18. "golang.org/x/crypto/bcrypt"
  19. "gorm.io/gorm"
  20. )
  21. func (svc *Service) UserLogin(ctx context.Context, msg *corrpc.UserLogin) (*corrpc.UserLoginResp, *rpc.CodeError) {
  22. log := logger.WithField("Account", msg.Account)
  23. user, err := svc.db.User().GetByAccount(svc.db.DefCtx(), msg.Account)
  24. if err != nil {
  25. if err == gorm.ErrRecordNotFound {
  26. log.Warnf("account not found")
  27. return nil, rpc.Failed(errorcode.DataNotFound, "account not found")
  28. }
  29. log.Warnf("getting account: %v", err)
  30. return nil, rpc.Failed(errorcode.OperationFailed, "getting account: %v", err)
  31. }
  32. dbPass, err := hex.DecodeString(user.Password)
  33. if err != nil {
  34. log.Warnf("decoding password: %v", err)
  35. return nil, rpc.Failed(errorcode.OperationFailed, "decoding password: %v", err)
  36. }
  37. if bcrypt.CompareHashAndPassword(dbPass, []byte(msg.Password)) != nil {
  38. log.Warnf("password not match")
  39. return nil, rpc.Failed(errorcode.Unauthorized, "password not match")
  40. }
  41. pubKey, priKey, err := ed25519.GenerateKey(nil)
  42. if err != nil {
  43. log.Warnf("generating key: %v", err)
  44. return nil, rpc.Failed(errorcode.OperationFailed, "generating key: %v", err)
  45. }
  46. pubKeyStr := hex.EncodeToString(pubKey)
  47. nowTime := time.Now()
  48. token := cortypes.UserAccessToken{
  49. UserID: user.UserID,
  50. TokenID: cortypes.AccessTokenID(uuid.NewString()),
  51. PublicKey: pubKeyStr,
  52. ExpiresAt: nowTime.Add(time.Hour),
  53. CreatedAt: nowTime,
  54. }
  55. err = svc.db.UserAccessToken().Create(svc.db.DefCtx(), &token)
  56. if err != nil {
  57. log.Warnf("creating token: %v", err)
  58. return nil, rpc.Failed(errorcode.OperationFailed, "creating token: %v", err)
  59. }
  60. log.Infof("login success, token expires at %v", token.ExpiresAt)
  61. return &corrpc.UserLoginResp{
  62. Token: token,
  63. PrivateKey: hex.EncodeToString(priKey),
  64. }, nil
  65. }
  66. func (svc *Service) UserRefreshToken(ctx context.Context, msg *corrpc.UserRefreshToken) (*corrpc.UserRefreshTokenResp, *rpc.CodeError) {
  67. authInfo, ok := rpc.GetAuthInfo(ctx)
  68. if !ok {
  69. return nil, rpc.Failed(errorcode.Unauthorized, "unauthorized")
  70. }
  71. log := logger.WithField("UserID", authInfo.UserID).WithField("TokenID", authInfo.AccessTokenID)
  72. pubKey, priKey, err := ed25519.GenerateKey(nil)
  73. if err != nil {
  74. log.Warnf("generating key: %v", err)
  75. return nil, rpc.Failed(errorcode.OperationFailed, "generating key: %v", err)
  76. }
  77. pubKeyStr := hex.EncodeToString(pubKey)
  78. nowTime := time.Now()
  79. token := cortypes.UserAccessToken{
  80. UserID: authInfo.UserID,
  81. TokenID: cortypes.AccessTokenID(uuid.NewString()),
  82. PublicKey: pubKeyStr,
  83. ExpiresAt: nowTime.Add(time.Hour),
  84. CreatedAt: nowTime,
  85. }
  86. err = svc.db.UserAccessToken().Create(svc.db.DefCtx(), &token)
  87. if err != nil {
  88. log.Warnf("creating token: %v", err)
  89. return nil, rpc.Failed(errorcode.OperationFailed, "creating token: %v", err)
  90. }
  91. log.Infof("refresh token success, new token expires at %v", token.ExpiresAt)
  92. return &corrpc.UserRefreshTokenResp{
  93. Token: token,
  94. PrivateKey: hex.EncodeToString(priKey),
  95. }, nil
  96. }
  97. func (svc *Service) UserLogout(ctx context.Context, msg *corrpc.UserLogout) (*corrpc.UserLogoutResp, *rpc.CodeError) {
  98. authInfo, ok := rpc.GetAuthInfo(ctx)
  99. if !ok {
  100. return nil, rpc.Failed(errorcode.Unauthorized, "unauthorized")
  101. }
  102. log := logger.WithField("UserID", authInfo.UserID).WithField("TokenID", authInfo.AccessTokenID)
  103. loaded, err := db.DoTx02(svc.db, func(tx db.SQLContext) ([]cortypes.LoadedAccessToken, error) {
  104. token, err := svc.db.UserAccessToken().GetByID(tx, authInfo.UserID, authInfo.AccessTokenID)
  105. if err != nil {
  106. return nil, err
  107. }
  108. err = svc.db.UserAccessToken().DeleteByID(tx, token.UserID, token.TokenID)
  109. if err != nil {
  110. return nil, err
  111. }
  112. loaded, err := svc.db.LoadedAccessToken().GetByUserIDAndTokenID(tx, token.UserID, token.TokenID)
  113. if err != nil {
  114. return nil, err
  115. }
  116. err = svc.db.LoadedAccessToken().DeleteAllByUserIDAndTokenID(tx, token.UserID, token.TokenID)
  117. if err != nil {
  118. return nil, err
  119. }
  120. return loaded, nil
  121. })
  122. if err != nil {
  123. log.Warnf("delete access token: %v", err)
  124. if err == gorm.ErrRecordNotFound {
  125. return nil, rpc.Failed(errorcode.DataNotFound, "token not found")
  126. }
  127. return nil, rpc.Failed(errorcode.OperationFailed, "delete access token: %v", err)
  128. }
  129. svc.accessToken.NotifyTokenInvalid(accesstoken.CacheKey{
  130. UserID: authInfo.UserID,
  131. TokenID: authInfo.AccessTokenID,
  132. })
  133. var loadedHubIDs []cortypes.HubID
  134. for _, l := range loaded {
  135. loadedHubIDs = append(loadedHubIDs, l.HubID)
  136. }
  137. svc.notifyLoadedHubs(authInfo.UserID, authInfo.AccessTokenID, loadedHubIDs)
  138. return &corrpc.UserLogoutResp{}, nil
  139. }
  140. func (svc *Service) notifyLoadedHubs(userID cortypes.UserID, tokenID cortypes.AccessTokenID, loadedHubIDs []cortypes.HubID) {
  141. log := logger.WithField("UserID", userID).WithField("TokenID", tokenID)
  142. loadedHubs, err := svc.db.Hub().BatchGetByID(svc.db.DefCtx(), loadedHubIDs)
  143. if err != nil {
  144. log.Warnf("getting hubs: %v", err)
  145. return
  146. }
  147. for _, l := range loadedHubs {
  148. addr, ok := l.Address.(*cortypes.GRPCAddressInfo)
  149. if !ok {
  150. continue
  151. }
  152. cli := stgglb.HubRPCPool.Get(addr.ExternalIP, addr.ExternalGRPCPort)
  153. // 不关心返回值
  154. cli.NotifyUserAccessTokenInvalid(context.Background(), &hubrpc.NotifyUserAccessTokenInvalid{
  155. UserID: userID,
  156. TokenID: tokenID,
  157. })
  158. cli.Release()
  159. }
  160. }
  161. func (svc *Service) HubLoadAccessToken(ctx context.Context, msg *corrpc.HubLoadAccessToken) (*corrpc.HubLoadAccessTokenResp, *rpc.CodeError) {
  162. token, err := db.DoTx02(svc.db, func(tx db.SQLContext) (cortypes.UserAccessToken, error) {
  163. token, err := svc.db.UserAccessToken().GetByID(tx, msg.UserID, msg.TokenID)
  164. if err != nil {
  165. return cortypes.UserAccessToken{}, err
  166. }
  167. err = svc.db.LoadedAccessToken().CreateOrUpdate(tx, cortypes.LoadedAccessToken{
  168. UserID: msg.UserID,
  169. TokenID: msg.TokenID,
  170. HubID: msg.HubID,
  171. LoadedAt: time.Now(),
  172. })
  173. if err != nil {
  174. return cortypes.UserAccessToken{}, fmt.Errorf("creating access token loaded record: %v", err)
  175. }
  176. return token, nil
  177. })
  178. if err != nil {
  179. if err == gorm.ErrRecordNotFound {
  180. return nil, rpc.Failed(errorcode.DataNotFound, "token not found")
  181. }
  182. return nil, rpc.Failed(errorcode.OperationFailed, "loading access token: %v", err)
  183. }
  184. return &corrpc.HubLoadAccessTokenResp{
  185. Token: token,
  186. }, nil
  187. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。