Skip to content

Commit c4595e5

Browse files
committed
Fix share access accounting and validation
1 parent 4f60c31 commit c4595e5

3 files changed

Lines changed: 66 additions & 28 deletions

File tree

internal/db/share.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,20 @@ func GetShareByCreatorAndShareID(creatorID uint, shareID string) (*model.Share,
2424
return &share, nil
2525
}
2626

27-
func ShareIDExists(shareID string) bool {
27+
func ShareIDExists(shareID string) (bool, error) {
2828
var count int64
2929
if err := db.Model(&model.Share{}).Where("share_id = ?", shareID).Count(&count).Error; err != nil {
30-
return false
30+
return false, err
3131
}
32-
return count > 0
32+
return count > 0, nil
3333
}
3434

35-
func ShareIDExistsExceptID(shareID string, id uint) bool {
35+
func ShareIDExistsExceptID(shareID string, id uint) (bool, error) {
3636
var count int64
3737
if err := db.Model(&model.Share{}).Where("share_id = ? AND id <> ?", shareID, id).Count(&count).Error; err != nil {
38-
return false
38+
return false, err
3939
}
40-
return count > 0
40+
return count > 0, nil
4141
}
4242

4343
func CreateShare(share *model.Share) error {

server/handles/share.go

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package handles
22

33
import (
44
"crypto/subtle"
5+
"errors"
56
"fmt"
7+
"net/http"
68
"net/url"
79
stdpath "path"
810
"regexp"
@@ -24,6 +26,11 @@ const shareAccessTokenLifetime = 24 * time.Hour
2426

2527
var shareIDPattern = regexp.MustCompile(`^[A-Za-z0-9_-]{1,32}$`)
2628

29+
var (
30+
errShareIDInvalid = errors.New("share_id must be 1-32 characters of letters, numbers, underscore or hyphen")
31+
errShareIDExists = errors.New("share link already exists")
32+
)
33+
2734
type CreateShareReq struct {
2835
Path string `json:"path" binding:"required"`
2936
ShareID string `json:"share_id"`
@@ -184,7 +191,11 @@ func normalizeOptionalShareName(name, fallback string) string {
184191
func generateShareID() (string, error) {
185192
for range 10 {
186193
shareID := random.String(8)
187-
if !db.ShareIDExists(shareID) {
194+
exists, err := db.ShareIDExists(shareID)
195+
if err != nil {
196+
return "", err
197+
}
198+
if !exists {
188199
return shareID, nil
189200
}
190201
}
@@ -200,7 +211,7 @@ func validateCustomShareID(shareID string) error {
200211
return nil
201212
}
202213
if !shareIDPattern.MatchString(shareID) {
203-
return fmt.Errorf("share_id must be 1-32 characters of letters, numbers, underscore or hyphen")
214+
return errShareIDInvalid
204215
}
205216
return nil
206217
}
@@ -217,13 +228,21 @@ func resolveRequestedShareID(rawShareID, fallback string, excludeID uint) (strin
217228
return "", err
218229
}
219230
if excludeID == 0 {
220-
if db.ShareIDExists(shareID) {
221-
return "", fmt.Errorf("share link already exists")
231+
exists, err := db.ShareIDExists(shareID)
232+
if err != nil {
233+
return "", fmt.Errorf("check share id availability: %w", err)
234+
}
235+
if exists {
236+
return "", errShareIDExists
222237
}
223238
return shareID, nil
224239
}
225-
if db.ShareIDExistsExceptID(shareID, excludeID) {
226-
return "", fmt.Errorf("share link already exists")
240+
exists, err := db.ShareIDExistsExceptID(shareID, excludeID)
241+
if err != nil {
242+
return "", fmt.Errorf("check share id availability: %w", err)
243+
}
244+
if exists {
245+
return "", errShareIDExists
227246
}
228247
return shareID, nil
229248
}
@@ -334,6 +353,10 @@ func ensureShareAccess(c *gin.Context, share *model.Share, token string) bool {
334353
return true
335354
}
336355

356+
func shouldTrackShareContentAccess(c *gin.Context) bool {
357+
return c.Request.Method != http.MethodHead
358+
}
359+
337360
func resolveShareTarget(share *model.Share, rawRelPath string) (string, string, error) {
338361
cleanRelPath := utils.FixAndCleanPath(rawRelPath)
339362
if !share.IsDir && cleanRelPath != "/" {
@@ -349,6 +372,14 @@ func resolveShareTarget(share *model.Share, rawRelPath string) (string, string,
349372
return target, cleanRelPath, nil
350373
}
351374

375+
func resolveShareWildcardTarget(share *model.Share, rawPath string) (string, string, error) {
376+
path, err := url.PathUnescape(rawPath)
377+
if err != nil {
378+
return "", "", err
379+
}
380+
return resolveShareTarget(share, strings.TrimPrefix(path, "/"))
381+
}
382+
352383
func buildPublicShareAssetURL(c *gin.Context, prefix, shareID, relPath, token string, preview bool) string {
353384
base := common.GetApiUrl(c.Request) + prefix + shareID
354385
cleanPath := utils.FixAndCleanPath(relPath)
@@ -423,7 +454,11 @@ func CreateShare(c *gin.Context) {
423454
}
424455
shareID, err := resolveRequestedShareID(req.ShareID, "", 0)
425456
if err != nil {
426-
common.ErrorResp(c, err, 400)
457+
if errors.Is(err, errShareIDInvalid) || errors.Is(err, errShareIDExists) {
458+
common.ErrorResp(c, err, 400)
459+
return
460+
}
461+
common.ErrorResp(c, err, 500, true)
427462
return
428463
}
429464
allowPreview := true
@@ -483,7 +518,11 @@ func UpdateShare(c *gin.Context) {
483518

484519
shareID, err := resolveRequestedShareID(req.NewShareID, share.ShareID, share.ID)
485520
if err != nil {
486-
common.ErrorResp(c, err, 400)
521+
if errors.Is(err, errShareIDInvalid) || errors.Is(err, errShareIDExists) {
522+
common.ErrorResp(c, err, 400)
523+
return
524+
}
525+
common.ErrorResp(c, err, 500, true)
487526
return
488527
}
489528

server/handles/share_public.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package handles
22

33
import (
44
stdpath "path"
5-
"strings"
65
"time"
76

87
"github.com/alist-org/alist/v3/internal/db"
@@ -143,10 +142,6 @@ func ListPublicShare(c *gin.Context) {
143142
}
144143
content = append(content, toPublicShareObjResp(c, share, item, itemTargetPath, itemRelPath, token))
145144
}
146-
if err := recordShareAccess(share); err != nil {
147-
common.ErrorResp(c, err, 500, true)
148-
return
149-
}
150145
common.SuccessResp(c, PublicShareListResp{
151146
Content: content,
152147
Total: int64(total),
@@ -213,7 +208,7 @@ func ShareDown(c *gin.Context) {
213208
if !ensureShareAccess(c, share, token) {
214209
return
215210
}
216-
targetPath, _, err := resolveShareTarget(share, strings.TrimPrefix(c.Param("path"), "/"))
211+
targetPath, _, err := resolveShareWildcardTarget(share, c.Param("path"))
217212
if err != nil {
218213
common.ErrorResp(c, err, 400)
219214
return
@@ -227,10 +222,12 @@ func ShareDown(c *gin.Context) {
227222
common.ErrorStrResp(c, "directory download is not supported", 400)
228223
return
229224
}
230-
_ = db.TouchShareDownload(share.ShareID)
231-
if err := recordShareAccess(share); err != nil {
232-
common.ErrorResp(c, err, 500, true)
233-
return
225+
if shouldTrackShareContentAccess(c) {
226+
_ = db.TouchShareDownload(share.ShareID)
227+
if err := recordShareAccess(share); err != nil {
228+
common.ErrorResp(c, err, 500, true)
229+
return
230+
}
234231
}
235232
c.Set("path", targetPath)
236233
Down(c)
@@ -253,7 +250,7 @@ func ShareProxy(c *gin.Context) {
253250
if !ensureShareAccess(c, share, token) {
254251
return
255252
}
256-
targetPath, _, err := resolveShareTarget(share, strings.TrimPrefix(c.Param("path"), "/"))
253+
targetPath, _, err := resolveShareWildcardTarget(share, c.Param("path"))
257254
if err != nil {
258255
common.ErrorResp(c, err, 400)
259256
return
@@ -267,9 +264,11 @@ func ShareProxy(c *gin.Context) {
267264
common.ErrorStrResp(c, "directory preview is not supported", 400)
268265
return
269266
}
270-
if err := recordShareAccess(share); err != nil {
271-
common.ErrorResp(c, err, 500, true)
272-
return
267+
if shouldTrackShareContentAccess(c) {
268+
if err := recordShareAccess(share); err != nil {
269+
common.ErrorResp(c, err, 500, true)
270+
return
271+
}
273272
}
274273
c.Set("path", targetPath)
275274
Proxy(c)

0 commit comments

Comments
 (0)