github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/middleware/auth.go (about) 1 package middleware 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/md5" 7 "fmt" 8 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" 9 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/oss" 10 "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver/upyun" 11 "github.com/cloudreve/Cloudreve/v3/pkg/mq" 12 "github.com/cloudreve/Cloudreve/v3/pkg/util" 13 "github.com/qiniu/go-sdk/v7/auth/qbox" 14 "io/ioutil" 15 "net/http" 16 17 model "github.com/cloudreve/Cloudreve/v3/models" 18 "github.com/cloudreve/Cloudreve/v3/pkg/auth" 19 "github.com/cloudreve/Cloudreve/v3/pkg/cache" 20 "github.com/cloudreve/Cloudreve/v3/pkg/serializer" 21 "github.com/gin-contrib/sessions" 22 "github.com/gin-gonic/gin" 23 ) 24 25 const ( 26 CallbackFailedStatusCode = http.StatusUnauthorized 27 ) 28 29 // SignRequired 验证请求签名 30 func SignRequired(authInstance auth.Auth) gin.HandlerFunc { 31 return func(c *gin.Context) { 32 var err error 33 switch c.Request.Method { 34 case "PUT", "POST", "PATCH": 35 err = auth.CheckRequest(authInstance, c.Request) 36 default: 37 err = auth.CheckURI(authInstance, c.Request.URL) 38 } 39 40 if err != nil { 41 c.JSON(200, serializer.Err(serializer.CodeCredentialInvalid, err.Error(), err)) 42 c.Abort() 43 return 44 } 45 46 c.Next() 47 } 48 } 49 50 // CurrentUser 获取登录用户 51 func CurrentUser() gin.HandlerFunc { 52 return func(c *gin.Context) { 53 session := sessions.Default(c) 54 uid := session.Get("user_id") 55 if uid != nil { 56 user, err := model.GetActiveUserByID(uid) 57 if err == nil { 58 c.Set("user", &user) 59 } 60 } 61 c.Next() 62 } 63 } 64 65 // AuthRequired 需要登录 66 func AuthRequired() gin.HandlerFunc { 67 return func(c *gin.Context) { 68 if user, _ := c.Get("user"); user != nil { 69 if _, ok := user.(*model.User); ok { 70 c.Next() 71 return 72 } 73 } 74 75 c.JSON(200, serializer.CheckLogin()) 76 c.Abort() 77 } 78 } 79 80 // WebDAVAuth 验证WebDAV登录及权限 81 func WebDAVAuth() gin.HandlerFunc { 82 return func(c *gin.Context) { 83 // OPTIONS 请求不需要鉴权,否则Windows10下无法保存文档 84 if c.Request.Method == "OPTIONS" { 85 c.Next() 86 return 87 } 88 89 username, password, ok := c.Request.BasicAuth() 90 if !ok { 91 c.Writer.Header()["WWW-Authenticate"] = []string{`Basic realm="cloudreve"`} 92 c.Status(http.StatusUnauthorized) 93 c.Abort() 94 return 95 } 96 97 expectedUser, err := model.GetActiveUserByEmail(username) 98 if err != nil { 99 c.Status(http.StatusUnauthorized) 100 c.Abort() 101 return 102 } 103 104 // 密码正确? 105 webdav, err := model.GetWebdavByPassword(password, expectedUser.ID) 106 if err != nil { 107 c.Status(http.StatusUnauthorized) 108 c.Abort() 109 return 110 } 111 112 // 用户组已启用WebDAV? 113 if !expectedUser.Group.WebDAVEnabled { 114 c.Status(http.StatusForbidden) 115 c.Abort() 116 return 117 } 118 119 // 用户组已启用WebDAV代理? 120 if !expectedUser.Group.OptionsSerialized.WebDAVProxy { 121 webdav.UseProxy = false 122 } 123 124 c.Set("user", &expectedUser) 125 c.Set("webdav", webdav) 126 c.Next() 127 } 128 } 129 130 // 对上传会话进行验证 131 func UseUploadSession(policyType string) gin.HandlerFunc { 132 return func(c *gin.Context) { 133 // 验证key并查找用户 134 resp := uploadCallbackCheck(c, policyType) 135 if resp.Code != 0 { 136 c.JSON(CallbackFailedStatusCode, resp) 137 c.Abort() 138 return 139 } 140 141 c.Next() 142 } 143 } 144 145 // uploadCallbackCheck 对上传回调请求的 callback key 进行验证,如果成功则返回上传用户 146 func uploadCallbackCheck(c *gin.Context, policyType string) serializer.Response { 147 // 验证 Callback Key 148 sessionID := c.Param("sessionID") 149 if sessionID == "" { 150 return serializer.ParamErr("Session ID cannot be empty", nil) 151 } 152 153 callbackSessionRaw, exist := cache.Get(filesystem.UploadSessionCachePrefix + sessionID) 154 if !exist { 155 return serializer.Err(serializer.CodeUploadSessionExpired, "上传会话不存在或已过期", nil) 156 } 157 158 callbackSession := callbackSessionRaw.(serializer.UploadSession) 159 c.Set(filesystem.UploadSessionCtx, &callbackSession) 160 if callbackSession.Policy.Type != policyType { 161 return serializer.Err(serializer.CodePolicyNotAllowed, "", nil) 162 } 163 164 // 清理回调会话 165 _ = cache.Deletes([]string{sessionID}, filesystem.UploadSessionCachePrefix) 166 167 // 查找用户 168 user, err := model.GetActiveUserByID(callbackSession.UID) 169 if err != nil { 170 return serializer.Err(serializer.CodeUserNotFound, "", err) 171 } 172 c.Set(filesystem.UserCtx, &user) 173 return serializer.Response{} 174 } 175 176 // RemoteCallbackAuth 远程回调签名验证 177 func RemoteCallbackAuth() gin.HandlerFunc { 178 return func(c *gin.Context) { 179 // 验证签名 180 session := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 181 authInstance := auth.HMACAuth{SecretKey: []byte(session.Policy.SecretKey)} 182 if err := auth.CheckRequest(authInstance, c.Request); err != nil { 183 c.JSON(CallbackFailedStatusCode, serializer.Err(serializer.CodeCredentialInvalid, err.Error(), err)) 184 c.Abort() 185 return 186 } 187 188 c.Next() 189 190 } 191 } 192 193 // QiniuCallbackAuth 七牛回调签名验证 194 func QiniuCallbackAuth() gin.HandlerFunc { 195 return func(c *gin.Context) { 196 session := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 197 198 // 验证回调是否来自qiniu 199 mac := qbox.NewMac(session.Policy.AccessKey, session.Policy.SecretKey) 200 ok, err := mac.VerifyCallback(c.Request) 201 if err != nil { 202 util.Log().Debug("Failed to verify callback request: %s", err) 203 c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Failed to verify callback request."}) 204 c.Abort() 205 return 206 } 207 208 if !ok { 209 c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Invalid signature."}) 210 c.Abort() 211 return 212 } 213 214 c.Next() 215 } 216 } 217 218 // OSSCallbackAuth 阿里云OSS回调签名验证 219 func OSSCallbackAuth() gin.HandlerFunc { 220 return func(c *gin.Context) { 221 err := oss.VerifyCallbackSignature(c.Request) 222 if err != nil { 223 util.Log().Debug("Failed to verify callback request: %s", err) 224 c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Failed to verify callback request."}) 225 c.Abort() 226 return 227 } 228 229 c.Next() 230 } 231 } 232 233 // UpyunCallbackAuth 又拍云回调签名验证 234 func UpyunCallbackAuth() gin.HandlerFunc { 235 return func(c *gin.Context) { 236 session := c.MustGet(filesystem.UploadSessionCtx).(*serializer.UploadSession) 237 238 // 获取请求正文 239 body, err := ioutil.ReadAll(c.Request.Body) 240 c.Request.Body.Close() 241 if err != nil { 242 c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: err.Error()}) 243 c.Abort() 244 return 245 } 246 247 c.Request.Body = ioutil.NopCloser(bytes.NewReader(body)) 248 249 // 准备验证Upyun回调签名 250 handler := upyun.Driver{Policy: &session.Policy} 251 contentMD5 := c.Request.Header.Get("Content-Md5") 252 date := c.Request.Header.Get("Date") 253 actualSignature := c.Request.Header.Get("Authorization") 254 255 // 计算正文MD5 256 actualContentMD5 := fmt.Sprintf("%x", md5.Sum(body)) 257 if actualContentMD5 != contentMD5 { 258 c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "MD5 mismatch."}) 259 c.Abort() 260 return 261 } 262 263 // 计算理论签名 264 signature := handler.Sign(context.Background(), []string{ 265 "POST", 266 c.Request.URL.Path, 267 date, 268 contentMD5, 269 }) 270 271 // 对比签名 272 if signature != actualSignature { 273 c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Signature not match"}) 274 c.Abort() 275 return 276 } 277 278 c.Next() 279 } 280 } 281 282 // OneDriveCallbackAuth OneDrive回调签名验证 283 func OneDriveCallbackAuth() gin.HandlerFunc { 284 return func(c *gin.Context) { 285 // 发送回调结束信号 286 mq.GlobalMQ.Publish(c.Param("sessionID"), mq.Message{}) 287 288 c.Next() 289 } 290 } 291 292 // IsAdmin 必须为管理员用户组 293 func IsAdmin() gin.HandlerFunc { 294 return func(c *gin.Context) { 295 user, _ := c.Get("user") 296 if user.(*model.User).Group.ID != 1 && user.(*model.User).ID != 1 { 297 c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "", nil)) 298 c.Abort() 299 return 300 } 301 302 c.Next() 303 } 304 }