diff --git a/lib/handler.go b/lib/handler.go index 61814e2..bc572cc 100644 --- a/lib/handler.go +++ b/lib/handler.go @@ -19,7 +19,6 @@ type handlerUser struct { type Handler struct { noPassword bool behindProxy bool - prefix string user *handlerUser users map[string]*handlerUser } @@ -28,12 +27,12 @@ func NewHandler(c *Config) (http.Handler, error) { h := &Handler{ noPassword: c.NoPassword, behindProxy: c.BehindProxy, - prefix: c.Prefix, user: &handlerUser{ User: User{ UserPermissions: c.UserPermissions, }, Handler: webdav.Handler{ + Prefix: c.Prefix, FileSystem: Dir{ Dir: webdav.Dir(c.Directory), noSniff: c.NoSniff, @@ -48,6 +47,7 @@ func NewHandler(c *Config) (http.Handler, error) { h.users[u.Username] = &handlerUser{ User: u, Handler: webdav.Handler{ + Prefix: c.Prefix, FileSystem: Dir{ Dir: webdav.Dir(u.Directory), noSniff: c.NoSniff, @@ -116,49 +116,19 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { zap.L().Info("user authorized", zap.String("username", username), zap.String("remote_address", remoteAddr)) } - // Validate and clean destination header if it exists, by stripping out the - // prefix and only keeping the actual destination path, always prefixed by - // a forward slash to ensure that the rules can successful match the path. + // Cleanup destination header if it's present by stripping out the prefix + // and only keeping the path. if destination := r.Header.Get("Destination"); destination != "" { u, err := url.Parse(destination) - if err != nil { - http.Error(w, "Invalid Destination header", http.StatusBadRequest) - return - } - - if h.prefix != "" { - destination = strings.TrimPrefix(u.Path, h.prefix) - if len(destination) >= len(u.Path) { - http.Error(w, "Invalid URL prefix", http.StatusBadRequest) - return + if err == nil { + destination = strings.TrimPrefix(u.Path, user.Prefix) + if !strings.HasPrefix(destination, "/") { + destination = "/" + destination } - } - - if !strings.HasPrefix(destination, "/") { - destination = "/" + destination - } - - r.Header.Set("Destination", destination) - } - - // Clean up URL path by stripping out the prefix, and ensuring it always begins - // with a forward slash, so that it can match against the rules. - path := r.URL.Path - - if h.prefix != "" { - path = strings.TrimPrefix(r.URL.Path, h.prefix) - if len(path) >= len(r.URL.Path) { - http.Error(w, "Invalid URL prefix", http.StatusBadRequest) - return + r.Header.Set("Destination", destination) } } - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - - r.URL.Path = path - // Checks for user permissions relatively to this PATH. allowed := user.Allowed(r, func(filename string) bool { _, err := user.FileSystem.Stat(r.Context(), filename) diff --git a/lib/handler_test.go b/lib/handler_test.go index d805e94..1c8b133 100644 --- a/lib/handler_test.go +++ b/lib/handler_test.go @@ -292,82 +292,6 @@ users: require.ErrorContains(t, err, "403") } -func TestServerRulesPrefix(t *testing.T) { - t.Parallel() - - dir := makeTestDirectory(t, map[string][]byte{ - "foo.txt": []byte("foo"), - "bar.js": []byte("foo js"), - "a/foo.js": []byte("foo js"), - "a/foo.txt": []byte("foo txt"), - "b/foo.txt": []byte("foo b"), - "c/a.txt": []byte("b"), - "c/b.txt": []byte("b"), - "c/c.txt": []byte("b"), - }) - - srv := makeTestServer(t, fmt.Sprintf(` -directory: %s -permissions: CRUD -prefix: /prefix - -users: - - username: basic - password: basic - rules: - - regex: "^.+.js$" - permissions: R - - path: "/b/" - permissions: R - - path: "/a/foo.txt" - permissions: none - - path: "/c/" - permissions: none -`, dir)) - - client := gowebdav.NewClient(srv.URL, "basic", "basic") - - files, err := client.ReadDir("/prefix") - require.NoError(t, err) - require.Len(t, files, 5) - - err = client.Write("/prefix/foo.txt", []byte("new"), 0666) - require.NoError(t, err) - - err = client.Write("/prefix/new.txt", []byte("new"), 0666) - require.NoError(t, err) - - err = client.Copy("/prefix/bar.js", "/prefix/b/bar.js", false) - require.ErrorContains(t, err, "403") - - err = client.Copy("/prefix/bar.js", "/prefix/bar.jsx", false) - require.NoError(t, err) - - err = client.Copy("/prefix/b/foo.txt", "/prefix/foo1.txt", false) - require.NoError(t, err) - - err = client.Rename("/prefix/b/foo.txt", "/prefix/foo2.txt", false) - require.ErrorContains(t, err, "403") - - _, err = client.Read("/prefix/a/foo.txt") - require.ErrorContains(t, err, "403") - - err = client.Write("/prefix/a/foo.js", []byte("new"), 0666) - require.ErrorContains(t, err, "403") - - err = client.Write("/prefix/b/foo.txt", []byte("new"), 0666) - require.ErrorContains(t, err, "403") - - _, err = client.ReadDir("/prefix/c") - require.ErrorContains(t, err, "403") - - _, err = client.Read("/prefix/c/a.txt") - require.ErrorContains(t, err, "403") - - err = client.Write("/prefix/c/b.txt", []byte("new"), 0666) - require.ErrorContains(t, err, "403") -} - func TestServerPermissions(t *testing.T) { t.Parallel()