package middleware import ( "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/serializer" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" ) const ( CallbackFailedStatusCode = http.StatusUnauthorized ) // SignRequired 验证请求签名 func SignRequired(authInstance auth.Auth) gin.HandlerFunc { return func(c *gin.Context) { var err error switch c.Request.Method { case "PUT", "POST", "PATCH": err = auth.CheckRequest(authInstance, c.Request) default: err = auth.CheckURI(authInstance, c.Request.URL) } if err != nil { c.JSON(200, serializer.Err(serializer.CodeCredentialInvalid, err.Error(), err)) c.Abort() return } c.Next() } } // CurrentUser 获取登录用户 func CurrentUser() gin.HandlerFunc { return func(c *gin.Context) { session := sessions.Default(c) uid := session.Get("user_id") if uid != nil { user, err := model.GetActiveUserByID(uid) if err == nil { c.Set("user", &user) } } c.Next() } } // AuthRequired 需要登录 func AuthRequired() gin.HandlerFunc { return func(c *gin.Context) { if user, _ := c.Get("user"); user != nil { if _, ok := user.(*model.User); ok { c.Next() return } } c.JSON(200, serializer.CheckLogin()) c.Abort() } } // WebDAVAuth 验证WebDAV登录及权限 func WebDAVAuth() gin.HandlerFunc { return func(c *gin.Context) { // OPTIONS 请求不需要鉴权,否则Windows10下无法保存文档 if c.Request.Method == "OPTIONS" { c.Next() return } username, password, ok := c.Request.BasicAuth() if !ok { c.Writer.Header()["WWW-Authenticate"] = []string{`Basic realm="cloudreve"`} c.Status(http.StatusUnauthorized) c.Abort() return } expectedUser, err := model.GetActiveUserByEmail(username) if err != nil { c.Status(http.StatusUnauthorized) c.Abort() return } // 密码正确? webdav, err := model.GetWebdavByPassword(password, expectedUser.ID) if err != nil { c.Status(http.StatusUnauthorized) c.Abort() return } // 用户组已启用WebDAV? if !expectedUser.Group.WebDAVEnabled { c.Status(http.StatusForbidden) c.Abort() return } c.Set("user", &expectedUser) c.Set("webdav", webdav) c.Next() } } // 对上传会话进行验证 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, policyType string) serializer.Response { // 验证 Callback Key sessionID := c.Param("sessionID") if sessionID == "" { return serializer.ParamErr("Session ID 不能为空", nil) } callbackSessionRaw, exist := cache.Get(filesystem.UploadSessionCachePrefix + sessionID) if !exist { return serializer.ParamErr("上传会话不存在或已过期", nil) } callbackSession := callbackSessionRaw.(serializer.UploadSession) c.Set(filesystem.UploadSessionCtx, &callbackSession) if callbackSession.Policy.Type != policyType { return serializer.Err(serializer.CodePolicyNotAllowed, "Policy not supported", nil) } // 清理回调会话 _ = cache.Deletes([]string{sessionID}, filesystem.UploadSessionCachePrefix) // 查找用户 user, err := model.GetActiveUserByID(callbackSession.UID) if err != nil { return serializer.Err(serializer.CodeCheckLogin, "找不到用户", err) } c.Set(filesystem.UserCtx, &user) return serializer.Response{} } // RemoteCallbackAuth 远程回调签名验证 func RemoteCallbackAuth() gin.HandlerFunc { return func(c *gin.Context) { // 验证签名 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(CallbackFailedStatusCode, serializer.Err(serializer.CodeCredentialInvalid, err.Error(), err)) c.Abort() return } c.Next() } } // 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 //} c.Next() } } // 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 //} c.Next() } } // 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 //} c.Next() } } // OneDriveCallbackAuth OneDrive回调签名验证 // 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")) c.Next() } } // COSCallbackAuth 腾讯云COS回调签名验证 // 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 //} c.Next() } } // 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 //} c.Next() } } // IsAdmin 必须为管理员用户组 func IsAdmin() gin.HandlerFunc { return func(c *gin.Context) { user, _ := c.Get("user") if user.(*model.User).Group.ID != 1 && user.(*model.User).ID != 1 { c.JSON(200, serializer.Err(serializer.CodeAdminRequired, "您不是管理组成员", nil)) c.Abort() return } c.Next() } }