Skip to content

Commit

Permalink
Feat: process upload callback sent from slave node
Browse files Browse the repository at this point in the history
  • Loading branch information
HFO4 committed Mar 3, 2022
1 parent 4925a35 commit e0714fd
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 236 deletions.
287 changes: 147 additions & 140 deletions middleware/auth.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
package middleware

import (
"bytes"
"context"
"crypto/md5"
"fmt"
"io/ioutil"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem"
"net/http"

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/filesystem/driver/onedrive"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/oss"
"github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/upyun"
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
"github.com/cloudreve/Cloudreve/v3/pkg/util"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/qiniu/api.v7/v7/auth/qbox"
)

const (
CallbackFailedStatusCode = http.StatusUnauthorized
)

// SignRequired 验证请求签名
Expand Down Expand Up @@ -117,48 +112,60 @@ func WebDAVAuth() gin.HandlerFunc {
}
}

// 对上传会话进行验证
func UseUploadSession(policyType string) gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp := uploadCallbackCheck(c, policyType)
if resp.Code != 0 {
c.JSON(CallbackFailedStatusCode, resp)
c.Abort()
return
}

c.Next()
}
}

// uploadCallbackCheck 对上传回调请求的 callback key 进行验证,如果成功则返回上传用户
func uploadCallbackCheck(c *gin.Context) (serializer.Response, *model.User) {
func uploadCallbackCheck(c *gin.Context, policyType string) serializer.Response {
// 验证 Callback Key
callbackKey := c.Param("key")
if callbackKey == "" {
return serializer.ParamErr("Callback Key 不能为空", nil), nil
sessionID := c.Param("sessionID")
if sessionID == "" {
return serializer.ParamErr("Session ID 不能为空", nil)
}
callbackSessionRaw, exist := cache.Get("callback_" + callbackKey)

callbackSessionRaw, exist := cache.Get(filesystem.UploadSessionCachePrefix + sessionID)
if !exist {
return serializer.ParamErr("回调会话不存在或已过期", nil), nil
return serializer.ParamErr("上传会话不存在或已过期", nil)
}

callbackSession := callbackSessionRaw.(serializer.UploadSession)
c.Set("callbackSession", &callbackSession)
c.Set(filesystem.UploadSessionCtx, &callbackSession)
if callbackSession.Policy.Type != policyType {
return serializer.Err(serializer.CodePolicyNotAllowed, "Policy not supported", nil)
}

// 清理回调会话
_ = cache.Deletes([]string{callbackKey}, "callback_")
_ = cache.Deletes([]string{sessionID}, filesystem.UploadSessionCachePrefix)

// 查找用户
user, err := model.GetActiveUserByID(callbackSession.UID)
if err != nil {
return serializer.Err(serializer.CodeCheckLogin, "找不到用户", err), nil
return serializer.Err(serializer.CodeCheckLogin, "找不到用户", err)
}
c.Set("user", &user)

return serializer.Response{}, &user
c.Set(filesystem.UserCtx, &user)
return serializer.Response{}
}

// RemoteCallbackAuth 远程回调签名验证
func RemoteCallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, user := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(200, resp)
c.Abort()
return
}

// 验证签名
authInstance := auth.HMACAuth{SecretKey: []byte(user.Policy.SecretKey)}
session := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession)
authInstance := auth.HMACAuth{SecretKey: []byte(session.Policy.SecretKey)}
if err := auth.CheckRequest(authInstance, c.Request); err != nil {
c.JSON(200, serializer.Err(serializer.CodeCheckLogin, err.Error(), err))
c.JSON(CallbackFailedStatusCode, serializer.Err(serializer.CodeCredentialInvalid, err.Error(), err))
c.Abort()
return
}
Expand All @@ -171,28 +178,28 @@ func RemoteCallbackAuth() gin.HandlerFunc {
// QiniuCallbackAuth 七牛回调签名验证
func QiniuCallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, user := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
c.Abort()
return
}

// 验证回调是否来自qiniu
mac := qbox.NewMac(user.Policy.AccessKey, user.Policy.SecretKey)
ok, err := mac.VerifyCallback(c.Request)
if err != nil {
util.Log().Debug("无法验证回调请求,%s", err)
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "无法验证回调请求"})
c.Abort()
return
}
if !ok {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "回调签名无效"})
c.Abort()
return
}
//// 验证key并查找用户
//resp, user := uploadCallbackCheck(c)
//if resp.Code != 0 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
// c.Abort()
// return
//}
//
//// 验证回调是否来自qiniu
//mac := qbox.NewMac(user.Policy.AccessKey, user.Policy.SecretKey)
//ok, err := mac.VerifyCallback(c.Request)
//if err != nil {
// util.Log().Debug("无法验证回调请求,%s", err)
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "无法验证回调请求"})
// c.Abort()
// return
//}
//if !ok {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "回调签名无效"})
// c.Abort()
// return
//}

c.Next()
}
Expand All @@ -201,21 +208,21 @@ func QiniuCallbackAuth() gin.HandlerFunc {
// OSSCallbackAuth 阿里云OSS回调签名验证
func OSSCallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, _ := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
c.Abort()
return
}

err := oss.VerifyCallbackSignature(c.Request)
if err != nil {
util.Log().Debug("回调签名验证失败,%s", err)
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "回调签名验证失败"})
c.Abort()
return
}
//// 验证key并查找用户
//resp, _ := uploadCallbackCheck(c)
//if resp.Code != 0 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
// c.Abort()
// return
//}
//
//err := oss.VerifyCallbackSignature(c.Request)
//if err != nil {
// util.Log().Debug("回调签名验证失败,%s", err)
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "回调签名验证失败"})
// c.Abort()
// return
//}

c.Next()
}
Expand All @@ -224,53 +231,53 @@ func OSSCallbackAuth() gin.HandlerFunc {
// UpyunCallbackAuth 又拍云回调签名验证
func UpyunCallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, user := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
c.Abort()
return
}

// 获取请求正文
body, err := ioutil.ReadAll(c.Request.Body)
c.Request.Body.Close()
if err != nil {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: err.Error()})
c.Abort()
return
}

c.Request.Body = ioutil.NopCloser(bytes.NewReader(body))

// 准备验证Upyun回调签名
handler := upyun.Driver{Policy: &user.Policy}
contentMD5 := c.Request.Header.Get("Content-Md5")
date := c.Request.Header.Get("Date")
actualSignature := c.Request.Header.Get("Authorization")

// 计算正文MD5
actualContentMD5 := fmt.Sprintf("%x", md5.Sum(body))
if actualContentMD5 != contentMD5 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "MD5不一致"})
c.Abort()
return
}

// 计算理论签名
signature := handler.Sign(context.Background(), []string{
"POST",
c.Request.URL.Path,
date,
contentMD5,
})

// 对比签名
if signature != actualSignature {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "鉴权失败"})
c.Abort()
return
}
//// 验证key并查找用户
//resp, user := uploadCallbackCheck(c)
//if resp.Code != 0 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
// c.Abort()
// return
//}
//
//// 获取请求正文
//body, err := ioutil.ReadAll(c.Request.Body)
//c.Request.Body.Close()
//if err != nil {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: err.Error()})
// c.Abort()
// return
//}
//
//c.Request.Body = ioutil.NopCloser(bytes.NewReader(body))
//
//// 准备验证Upyun回调签名
//handler := upyun.Driver{Policy: &user.Policy}
//contentMD5 := c.Request.Header.Get("Content-Md5")
//date := c.Request.Header.Get("Date")
//actualSignature := c.Request.Header.Get("Authorization")
//
//// 计算正文MD5
//actualContentMD5 := fmt.Sprintf("%x", md5.Sum(body))
//if actualContentMD5 != contentMD5 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "MD5不一致"})
// c.Abort()
// return
//}
//
//// 计算理论签名
//signature := handler.Sign(context.Background(), []string{
// "POST",
// c.Request.URL.Path,
// date,
// contentMD5,
//})
//
//// 对比签名
//if signature != actualSignature {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "鉴权失败"})
// c.Abort()
// return
//}

c.Next()
}
Expand All @@ -280,16 +287,16 @@ func UpyunCallbackAuth() gin.HandlerFunc {
// TODO 解耦
func OneDriveCallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, _ := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
c.Abort()
return
}

// 发送回调结束信号
onedrive.FinishCallback(c.Param("key"))
//// 验证key并查找用户
//resp, _ := uploadCallbackCheck(c)
//if resp.Code != 0 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
// c.Abort()
// return
//}
//
//// 发送回调结束信号
//onedrive.FinishCallback(c.Param("key"))

c.Next()
}
Expand All @@ -299,13 +306,13 @@ func OneDriveCallbackAuth() gin.HandlerFunc {
// TODO 解耦 测试
func COSCallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, _ := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
c.Abort()
return
}
//// 验证key并查找用户
//resp, _ := uploadCallbackCheck(c)
//if resp.Code != 0 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
// c.Abort()
// return
//}

c.Next()
}
Expand All @@ -314,13 +321,13 @@ func COSCallbackAuth() gin.HandlerFunc {
// S3CallbackAuth Amazon S3回调签名验证
func S3CallbackAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证key并查找用户
resp, _ := uploadCallbackCheck(c)
if resp.Code != 0 {
c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
c.Abort()
return
}
//// 验证key并查找用户
//resp, _ := uploadCallbackCheck(c)
//if resp.Code != 0 {
// c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: resp.Msg})
// c.Abort()
// return
//}

c.Next()
}
Expand Down
3 changes: 2 additions & 1 deletion models/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (file *File) UpdateSourceName(value string) error {
return DB.Model(&file).Set("gorm:association_autoupdate", false).Update("source_name", value).Error
}

func (file *File) PopChunkToFile(lastModified *time.Time) error {
func (file *File) PopChunkToFile(lastModified *time.Time, picInfo string) error {
file.UploadSessionID = nil
if lastModified != nil {
file.UpdatedAt = *lastModified
Expand All @@ -308,6 +308,7 @@ func (file *File) PopChunkToFile(lastModified *time.Time) error {
return DB.Model(file).UpdateColumns(map[string]interface{}{
"upload_session_id": file.UploadSessionID,
"updated_at": file.UpdatedAt,
"pic_info": picInfo,
}).Error
}

Expand Down
Loading

0 comments on commit e0714fd

Please sign in to comment.