fix(upload): restrict cross-user private file access (UPLOAD-1)

- Add access_mode column to w_uploads table (0 = private, 1 = public) and initialize data in a single migration script
- Enforce strict ownership check for private files during download
- Allow public files to follow whitelisted public-access rules
- Default access_mode to public for avatars and private for generic uploads
- Update frontend service to support optional accessMode parameter
This commit is contained in:
ryan
2026-06-13 10:13:41 +08:00
parent 5412c385dc
commit 7b379863b4
11 changed files with 190 additions and 12 deletions
+32 -6
View File
@@ -23,6 +23,7 @@ import (
"github.com/Rain-kl/Wavelet/internal/logger"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/Rain-kl/Wavelet/internal/storage"
"github.com/Rain-kl/Wavelet/internal/util"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
@@ -56,7 +57,7 @@ func ServeFileByID(c *gin.Context) {
}
// 校验业务白名单与访问权限
if err := checkFileAccessPermission(c, upload.Type); err != nil {
if err := checkFileAccessPermission(c, upload); err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error_msg": common.UnAuthorized, "data": nil})
return
}
@@ -309,13 +310,38 @@ func isFilePublic(ctx context.Context, uploadType string) bool {
return false
}
// checkFileAccessPermission 校验文件是否可以被当前请求访问
func checkFileAccessPermission(c *gin.Context, uploadType string) error {
if !isFilePublic(c.Request.Context(), uploadType) {
// 必须进行鉴权
if _, err := oauth.GetUserFromRequest(c); err != nil {
func checkPrivateFileOwner(c *gin.Context, ownerID uint64) error {
var currUser *model.User
var err error
if u, ok := util.GetFromContext[*model.User](c, oauth.UserObjKey); ok && u != nil {
currUser = u
} else {
currUser, err = oauth.GetUserFromRequest(c)
if err != nil {
return err
}
}
if currUser.ID != ownerID {
return errors.New("forbidden: cross-user access denied")
}
return nil
}
// checkFileAccessPermission 校验文件是否可以被当前请求访问
func checkFileAccessPermission(c *gin.Context, upload *model.Upload) error {
// 1. 私有文件校验(优先级高于当前白名单逻辑)
if upload.AccessMode == 0 {
return checkPrivateFileOwner(c, upload.UserID)
}
// 2. 如果类型为公开的则再进行校验白名单
if !isFilePublic(c.Request.Context(), upload.Type) {
// 必须进行鉴权
if _, ok := util.GetFromContext[*model.User](c, oauth.UserObjKey); !ok {
if _, err := oauth.GetUserFromRequest(c); err != nil {
return err
}
}
}
return nil
}