diff --git a/server/pkg/share/webdav_handler.go b/server/pkg/share/webdav_handler.go index c314abcc..7fc09106 100644 --- a/server/pkg/share/webdav_handler.go +++ b/server/pkg/share/webdav_handler.go @@ -3,12 +3,13 @@ package share import ( "net/http" "path/filepath" + "slices" "strings" . "github.com/mickael-kerjean/filestash/server/pkg/core" . "github.com/mickael-kerjean/filestash/server/pkg/kernel" - . "github.com/mickael-kerjean/filestash/server/pkg/utils" "github.com/mickael-kerjean/filestash/server/pkg/permissions" + . "github.com/mickael-kerjean/filestash/server/pkg/utils" "github.com/mickael-kerjean/net/webdav" ) @@ -19,47 +20,28 @@ func WebdavHandler(ctx *App, res http.ResponseWriter, req *http.Request) { return } - // https://github.com/golang/net/blob/master/webdav/webdav.go#L49-L68 - canRead := permissions.CanRead(ctx) - canWrite := permissions.CanEdit(ctx) - canUpload := permissions.CanUpload(ctx) + var isAllowed bool switch req.Method { - case "OPTIONS", "HEAD", "GET": - if canRead == false { - SendErrorResult(res, ErrPermissionDenied) - return - } + case "OPTIONS", "HEAD", "GET", "PROPFIND": + isAllowed = permissions.CanRead(ctx) case "MKCOL", "DELETE", "COPY", "MOVE", "PROPPATCH": - if canWrite == false { - SendErrorResult(res, ErrPermissionDenied) - return - } - case "PROPFIND": - if canRead == false { - SendErrorResult(res, ErrPermissionDenied) - return - } - case "PUT": - if canWrite == false || canUpload == false { - SendErrorResult(res, ErrPermissionDenied) - return - } - case "LOCK", "UNLOCK": - if canWrite == false || canUpload == false { - SendErrorResult(res, ErrPermissionDenied) - return - } + isAllowed = permissions.CanEdit(ctx) + case "PUT", "LOCK", "UNLOCK": + isAllowed = permissions.CanEdit(ctx) && permissions.CanUpload(ctx) default: SendErrorResult(res, ErrNotImplemented) return } + if isAllowed == false { + SendErrorResult(res, ErrPermissionDenied) + return + } - h := &webdav.Handler{ + (&webdav.Handler{ Prefix: "/s/" + ctx.Share.Id, FileSystem: NewWebdavFs(ctx.Backend, ctx.Share.Backend, ctx.Share.Path, req), LockSystem: NewWebdavLock(), - } - h.ServeHTTP(res, req) + }).ServeHTTP(res, req) } /* @@ -69,72 +51,32 @@ func WebdavHandler(ctx *App, res http.ResponseWriter, req *http.Request) { */ func WebdavBlacklist(fn HandlerFunc) HandlerFunc { return HandlerFunc(func(ctx *App, res http.ResponseWriter, req *http.Request) { - base := filepath.Base(req.URL.String()) - - if req.Method == "PUT" || req.Method == "MKCOL" { - if strings.HasPrefix(base, "._") { + name := filepath.Base(req.URL.String()) + isAppleDouble := strings.HasPrefix(name, "._") + switch req.Method { + case "PUT", "MKCOL": + if isAppleDouble || slices.Contains([]string{".DS_Store", ".localized"}, name) { res.WriteHeader(http.StatusMethodNotAllowed) - res.Write([]byte("")) - return - } else if base == ".DS_Store" { - res.WriteHeader(http.StatusMethodNotAllowed) - res.Write([]byte("")) - return - } else if base == ".localized" { - res.WriteHeader(http.StatusMethodNotAllowed) - res.Write([]byte("")) return } - } else if req.Method == "PROPFIND" { - if strings.HasPrefix(base, "._") { - res.WriteHeader(http.StatusForbidden) - return - } else if base == ".DS_Store" { - res.WriteHeader(http.StatusForbidden) - res.Write([]byte("")) - return - } else if base == ".localized" { - res.WriteHeader(http.StatusForbidden) - return - } else if base == ".ql_disablethumbnails" { - res.WriteHeader(http.StatusForbidden) - res.Write([]byte("")) - return - } else if base == ".ql_disablecache" { - res.WriteHeader(http.StatusForbidden) - return - } else if base == ".hidden" { - res.WriteHeader(http.StatusForbidden) - return - } else if base == ".Spotlight-V100" { - res.WriteHeader(http.StatusForbidden) - return - } else if base == ".metadata_never_index" { - res.WriteHeader(http.StatusForbidden) - return - } else if base == "Contents" { - res.WriteHeader(http.StatusForbidden) - return - } else if base == ".metadata_never_index_unless_rootfs" { + case "PROPFIND": + if isAppleDouble || slices.Contains([]string{ + ".DS_Store", ".localized", ".hidden", ".Spotlight-V100", + ".ql_disablethumbnails", ".ql_disablecache", + ".metadata_never_index", ".metadata_never_index_unless_rootfs", + "Contents", + }, name) { res.WriteHeader(http.StatusForbidden) return } - } else if req.Method == "GET" { - if base == ".DS_Store" { + case "GET", "DELETE": + if name == ".DS_Store" { res.WriteHeader(http.StatusForbidden) - res.Write([]byte("")) return } - } else if req.Method == "DELETE" { - if base == ".DS_Store" { - res.WriteHeader(http.StatusForbidden) - res.Write([]byte("")) - return - } - } else if req.Method == "LOCK" || req.Method == "UNLOCK" { - if base == ".DS_Store" { + case "LOCK", "UNLOCK": + if name == ".DS_Store" { res.WriteHeader(http.StatusMethodNotAllowed) - res.Write([]byte("")) return } } diff --git a/server/pkg/share/webdav_model.go b/server/pkg/share/webdav_model.go index c37e3ff9..90614f18 100644 --- a/server/pkg/share/webdav_model.go +++ b/server/pkg/share/webdav_model.go @@ -107,12 +107,6 @@ func (this *WebdavFs) Stat(ctx context.Context, name string) (os.FileInfo, error return this.webdavFile.Stat() } fullname := this.fullpath(name) - if isMicrosoftWebDAVClient(this.req) && this.req.Method == "PROPFIND" { - if name == "" { - fullname = this.chroot - } - fullname = EnforceDirectory(fullname) - } if fullname == "" { return nil, os.ErrNotExist } @@ -125,11 +119,20 @@ func (this *WebdavFs) Stat(ctx context.Context, name string) (os.FileInfo, error } func (this WebdavFs) fullpath(path string) string { - p := filepath.Join(this.chroot, path) - if strings.HasSuffix(path, "/") == true && strings.HasSuffix(p, "/") == false { - p += "/" + var ( + p = filepath.Join(this.chroot, path) + isPathAFolder = strings.HasSuffix(path, "/") + isChrootAFile = strings.HasSuffix(this.chroot, "/") == false + ) + if isChrootAFile { + if p != this.chroot { + return "" + } + return p + } else if isPathAFolder { + p = EnforceDirectory(p) } - if strings.HasPrefix(p, this.chroot) == false { + if strings.HasPrefix(EnforceDirectory(p), this.chroot) == false { return "" } return p @@ -219,6 +222,9 @@ func (this *WebdavFile) Stat() (os.FileInfo, error) { for i := range files { if files[i].Name() == baseDir { found = true + if files[i].IsDir() { + this.path = EnforceDirectory(this.path) + } break } } @@ -336,7 +342,3 @@ func NewWebdavLock() webdav.LockSystem { } return lock } - -func isMicrosoftWebDAVClient(req *http.Request) bool { - return strings.HasPrefix(req.Header.Get("User-Agent"), "Microsoft-WebDAV-MiniRedir/") -}