feat(session): generate temp URL to copy/refresh user session

pull/1567/head
HFO4 2 years ago
parent 1227f35d3c
commit e4c87483d6

@ -1 +1 @@
Subproject commit f0a1835ce198f0953c8b5f4ad45dba445b641164 Subproject commit 01343d7656f839d13cc60adc6b56a238e3160465

@ -377,3 +377,23 @@ func UserInit2FA(c *gin.Context) {
c.JSON(200, ErrorResponse(err)) 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))
}
}

@ -233,6 +233,8 @@ func InitMasterRouter() *gin.Engine {
// 打包并下载文件 // 打包并下载文件
file.GET("archive/:sessionID/archive.zip", controllers.DownloadArchive) file.GET("archive/:sessionID/archive.zip", controllers.DownloadArchive)
} }
sign.GET("user/session/copy/:id", controllers.UserPerformCopySession)
} }
// 从机的 RPC 通信 // 从机的 RPC 通信
@ -523,6 +525,9 @@ func InitMasterRouter() *gin.Engine {
user.GET("storage", controllers.UserStorage) user.GET("storage", controllers.UserStorage)
// 退出登录 // 退出登录
user.DELETE("session", controllers.UserSignOut) 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 注册相关 // WebAuthn 注册相关
authn := user.Group("authn", authn := user.Group("authn",

@ -3,12 +3,14 @@ package user
import ( import (
"fmt" "fmt"
model "github.com/cloudreve/Cloudreve/v3/models" 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/cache"
"github.com/cloudreve/Cloudreve/v3/pkg/email" "github.com/cloudreve/Cloudreve/v3/pkg/email"
"github.com/cloudreve/Cloudreve/v3/pkg/hashid" "github.com/cloudreve/Cloudreve/v3/pkg/hashid"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
"github.com/pquerna/otp/totp" "github.com/pquerna/otp/totp"
"net/url" "net/url"
) )
@ -154,3 +156,48 @@ func (service *UserLoginService) Login(c *gin.Context) serializer.Response {
return serializer.BuildUserResponse(expectedUser) 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{}
}

Loading…
Cancel
Save