From e4c87483d663f813588a699200b7e3a60f9df4dd Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Mon, 19 Dec 2022 17:34:57 +0800 Subject: [PATCH] feat(session): generate temp URL to copy/refresh user session --- assets | 2 +- routers/controllers/user.go | 20 ++++++++++++++++ routers/router.go | 5 ++++ service/user/login.go | 47 +++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) diff --git a/assets b/assets index f0a1835..01343d7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f0a1835ce198f0953c8b5f4ad45dba445b641164 +Subproject commit 01343d7656f839d13cc60adc6b56a238e3160465 diff --git a/routers/controllers/user.go b/routers/controllers/user.go index 7d1cc42..64dbd7b 100644 --- a/routers/controllers/user.go +++ b/routers/controllers/user.go @@ -377,3 +377,23 @@ func UserInit2FA(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// UserPrepareCopySession generates URL for copy session +func UserPrepareCopySession(c *gin.Context) { + var service user.CopySessionService + res := service.Prepare(c, CurrentUser(c)) + c.JSON(200, res) + +} + +// UserPerformCopySession copy to create new session or refresh current session +func UserPerformCopySession(c *gin.Context) { + var service user.CopySessionService + if err := c.ShouldBindUri(&service); err == nil { + res := service.Copy(c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } + +} diff --git a/routers/router.go b/routers/router.go index e6d9ba1..58baaa0 100644 --- a/routers/router.go +++ b/routers/router.go @@ -233,6 +233,8 @@ func InitMasterRouter() *gin.Engine { // 打包并下载文件 file.GET("archive/:sessionID/archive.zip", controllers.DownloadArchive) } + + sign.GET("user/session/copy/:id", controllers.UserPerformCopySession) } // 从机的 RPC 通信 @@ -523,6 +525,9 @@ func InitMasterRouter() *gin.Engine { user.GET("storage", controllers.UserStorage) // 退出登录 user.DELETE("session", controllers.UserSignOut) + // Generate temp URL for copying client-side session, used in adding accounts + // for mobile App. + user.GET("session", controllers.UserPrepareCopySession) // WebAuthn 注册相关 authn := user.Group("authn", diff --git a/service/user/login.go b/service/user/login.go index 9367ae9..22649dd 100644 --- a/service/user/login.go +++ b/service/user/login.go @@ -3,12 +3,14 @@ package user import ( "fmt" model "github.com/cloudreve/Cloudreve/v3/models" + "github.com/cloudreve/Cloudreve/v3/pkg/auth" "github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/email" "github.com/cloudreve/Cloudreve/v3/pkg/hashid" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/gin-gonic/gin" + "github.com/gofrs/uuid" "github.com/pquerna/otp/totp" "net/url" ) @@ -154,3 +156,48 @@ func (service *UserLoginService) Login(c *gin.Context) serializer.Response { return serializer.BuildUserResponse(expectedUser) } + +// CopySessionService service for copy user session +type CopySessionService struct { + ID string `uri:"id" binding:"required,uuid4"` +} + +const CopySessionTTL = 60 + +// Prepare generates the URL with short expiration duration +func (s *CopySessionService) Prepare(c *gin.Context, user *model.User) serializer.Response { + // 用户组有效期 + urlID := uuid.Must(uuid.NewV4()) + if err := cache.Set(fmt.Sprintf("copy_session_%s", urlID.String()), user.ID, CopySessionTTL); err != nil { + return serializer.Err(serializer.CodeInternalSetting, "Failed to create copy session", err) + } + + base := model.GetSiteURL() + apiBaseURI, _ := url.Parse("/api/v3/user/session/copy/" + urlID.String()) + apiURL := base.ResolveReference(apiBaseURI) + res, err := auth.SignURI(auth.General, apiURL.String(), CopySessionTTL) + if err != nil { + return serializer.Err(serializer.CodeInternalSetting, "Failed to sign temp URL", err) + } + + return serializer.Response{ + Data: res.String(), + } +} + +// Copy a new session from active session, refresh max-age +func (s *CopySessionService) Copy(c *gin.Context) serializer.Response { + // 用户组有效期 + cacheKey := fmt.Sprintf("copy_session_%s", s.ID) + uid, ok := cache.Get(cacheKey) + if !ok { + return serializer.Err(serializer.CodeNotFound, "", nil) + } + + cache.Deletes([]string{cacheKey}, "") + util.SetSession(c, map[string]interface{}{ + "user_id": uid.(uint), + }) + + return serializer.Response{} +}