diff --git a/internal/db/sharing.go b/internal/db/sharing.go index 8670b15f..9c356440 100644 --- a/internal/db/sharing.go +++ b/internal/db/sharing.go @@ -64,6 +64,14 @@ func UpdateSharing(s *model.SharingDB) error { return errors.WithStack(db.Save(s).Error) } +func UpdateSharingId(oldId, newId string) error { + // Check if new ID already exists + if err := db.Where("id = ?", newId).First(&model.SharingDB{}).Error; err == nil { + return errors.New("sharing id already exists") + } + return errors.WithStack(db.Model(&model.SharingDB{}).Where("id = ?", oldId).Update("id", newId).Error) +} + func DeleteSharingById(id string) error { s := model.SharingDB{ID: id} return errors.WithStack(db.Where(s).Delete(&s).Error) diff --git a/internal/model/sharing.go b/internal/model/sharing.go index c5dd95e9..c5014944 100644 --- a/internal/model/sharing.go +++ b/internal/model/sharing.go @@ -3,7 +3,7 @@ package model import "time" type SharingDB struct { - ID string `json:"id" gorm:"type:char(12);primaryKey"` + ID string `json:"id" gorm:"type:varchar(64);primaryKey"` FilesRaw string `json:"-" gorm:"type:text"` Expires *time.Time `json:"expires"` Pwd string `json:"pwd"` diff --git a/internal/model/user.go b/internal/model/user.go index 61252ce9..2dacd752 100644 --- a/internal/model/user.go +++ b/internal/model/user.go @@ -63,6 +63,7 @@ type User struct { // 12: can read archives // 13: can decompress archives // 14: can share + // 15: can customize share id Permission int32 `json:"permission"` OtpSecret string `json:"-"` SsoID string `json:"sso_id"` // unique by sso platform @@ -219,6 +220,14 @@ func (u *User) CanShare() bool { return CanShare(u.Permission) } +func CanCustomizeShareID(permission int32) bool { + return (permission>>15)&1 == 1 +} + +func (u *User) CanCustomizeShareID() bool { + return CanCustomizeShareID(u.Permission) +} + func (u *User) JoinPath(reqPath string) (string, error) { return utils.JoinBasePath(u.BasePath, reqPath) } diff --git a/internal/op/sharing.go b/internal/op/sharing.go index 9db51c59..5be0f8e6 100644 --- a/internal/op/sharing.go +++ b/internal/op/sharing.go @@ -133,6 +133,15 @@ func UpdateSharing(sharing *model.Sharing, skipMarshal ...bool) (err error) { return db.UpdateSharing(sharing.SharingDB) } +func UpdateSharingId(sharing *model.Sharing, newId string) error { + sharingCache.Del(sharing.ID) + if err := db.UpdateSharingId(sharing.ID, newId); err != nil { + return err + } + sharing.ID = newId + return nil +} + func DeleteSharing(sid string) error { sharingCache.Del(sid) return db.DeleteSharingById(sid) diff --git a/server/handles/sharing.go b/server/handles/sharing.go index 43f855af..6090fcbd 100644 --- a/server/handles/sharing.go +++ b/server/handles/sharing.go @@ -3,6 +3,7 @@ package handles import ( "fmt" stdpath "path" + "regexp" "strings" "time" @@ -416,6 +417,19 @@ type UpdateSharingReq struct { CreatorName string `json:"creator"` Accessed int `json:"accessed"` ID string `json:"id"` + NewID string `json:"new_id"` +} + +var validSharingID = regexp.MustCompile(`^[\w\p{Han}\-]+$`) + +func validateSharingID(id string) error { + if len([]rune(id)) > 64 { + return errors.New("share id must be at most 64 characters") + } + if !validSharingID.MatchString(id) { + return errors.New("share id can only contain letters, numbers, underscores, hyphens, and CJK characters") + } + return nil } func UpdateSharing(c *gin.Context) { @@ -471,6 +485,20 @@ func UpdateSharing(c *gin.Context) { s.Readme = req.Readme s.Remark = req.Remark s.Creator = user + if req.NewID != "" && req.NewID != req.ID { + if !reqUser.CanCustomizeShareID() { + common.ErrorStrResp(c, "permission denied", 403) + return + } + if err = validateSharingID(req.NewID); err != nil { + common.ErrorResp(c, err, 400) + return + } + if err = op.UpdateSharingId(s, req.NewID); err != nil { + common.ErrorResp(c, err, 500) + return + } + } if err = op.UpdateSharing(s); err != nil { common.ErrorResp(c, err, 500) } else { @@ -493,6 +521,12 @@ func CreateSharing(c *gin.Context) { common.ErrorStrResp(c, "must add at least 1 object", 400) return } + if req.ID != "" { + if err = validateSharingID(req.ID); err != nil { + common.ErrorResp(c, err, 400) + return + } + } var user *model.User reqUser := c.Request.Context().Value(conf.UserKey).(*model.User) if reqUser.IsAdmin() && req.CreatorName != "" { @@ -503,7 +537,7 @@ func CreateSharing(c *gin.Context) { } } else { user = reqUser - if !user.CanShare() || (!user.IsAdmin() && req.ID != "") { + if !user.CanShare() || (!user.CanCustomizeShareID() && req.ID != "") { common.ErrorStrResp(c, "permission denied", 403) return }