Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .woodpecker.star
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ config = {
},
"basic2": {
"suites": [
"apiDownloads",
"apiDepthInfinity",
"apiLocks",
"apiActivities",
"apiArchiver",
"apiContract",
"apiCors",
"apiAsyncUpload",
],
"skip": False,
},
Expand Down
27 changes: 17 additions & 10 deletions services/webdav/pkg/service/v0/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"path"
"regexp"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -36,6 +37,8 @@ const (
// TODO elementNameFilterFiles = "filter-files"
)

var spacesSearchRegex = regexp.MustCompile(`^/(?:remote\.php/)?dav/spaces/([^/]+)`)

// Search is the endpoint for retrieving search results for REPORT requests
func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
logger := g.log.SubloggerWithRequestID(r.Context())
Expand Down Expand Up @@ -76,9 +79,9 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
PageSize: int32(rep.SearchFiles.Search.Limit),
}

// Limit search to the according space when searching /dav/spaces/
if strings.HasPrefix(r.URL.Path, "/dav/spaces") {
space := strings.TrimPrefix(r.URL.Path, "/dav/spaces/")
// Limit search to the according space when searching /dav/spaces/<spaceid>
if matches := spacesSearchRegex.FindStringSubmatch(r.URL.Path); matches != nil {
space := matches[1]
rid, err := storagespace.ParseID(space)
if err != nil {
logger.Debug().Err(err).Msg("error parsing the space id for filtering")
Expand All @@ -92,6 +95,10 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
}
}
}
davPrefix := ""
if strings.HasPrefix(r.URL.Path, "/remote.php") {
davPrefix = "/remote.php"
}

rsp, err := g.searchClient.Search(ctx, req)
if err != nil {
Expand All @@ -105,12 +112,12 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
logger.Error().Err(err).Msg("could not get search results")
return
}
g.sendSearchResponse(rsp, w, r, user)
g.sendSearchResponse(davPrefix, rsp, w, r, user)
}

func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request, user *userv1beta1.User) {
func (g Webdav) sendSearchResponse(davPrefix string, rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request, user *userv1beta1.User) {
logger := g.log.SubloggerWithRequestID(r.Context())
responsesXML, err := multistatusResponse(r.Context(), g.config.OpenCloudPublicURL, rsp.Matches, user)
responsesXML, err := multistatusResponse(r.Context(), davPrefix, g.config.OpenCloudPublicURL, rsp.Matches, user)
if err != nil {
logger.Error().Err(err).Msg("error formatting propfind")
w.WriteHeader(http.StatusInternalServerError)
Expand All @@ -128,10 +135,10 @@ func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.Respons
}

// multistatusResponse converts a list of matches into a multistatus response string
func multistatusResponse(ctx context.Context, publicURL string, matches []*searchmsg.Match, user *userv1beta1.User) ([]byte, error) {
func multistatusResponse(ctx context.Context, davPrefix, publicURL string, matches []*searchmsg.Match, user *userv1beta1.User) ([]byte, error) {
responses := make([]*propfind.ResponseXML, 0, len(matches))
for i := range matches {
res, err := matchToPropResponse(ctx, publicURL, matches[i], user)
res, err := matchToPropResponse(ctx, davPrefix, publicURL, matches[i], user)
if err != nil {
return nil, err
}
Expand All @@ -147,7 +154,7 @@ func multistatusResponse(ctx context.Context, publicURL string, matches []*searc
return msg, nil
}

func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg.Match, user *userv1beta1.User) (*propfind.ResponseXML, error) {
func matchToPropResponse(ctx context.Context, davPrefix, publicURL string, match *searchmsg.Match, user *userv1beta1.User) (*propfind.ResponseXML, error) {
// unfortunately, search uses own versions of ResourceId and Ref. So we need to assert them here
var (
ref string
Expand Down Expand Up @@ -180,7 +187,7 @@ func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg
return nil, err
}
response := propfind.ResponseXML{
Href: net.EncodePath(path.Join("/remote.php/dav/spaces/", ref)),
Href: net.EncodePath(path.Join(davPrefix, "/dav/spaces/", ref)),
Propstat: []propfind.PropstatXML{},
}

Expand Down
35 changes: 35 additions & 0 deletions services/webdav/pkg/service/v0/search_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package svc

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestSearch(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Search Suite")
}

var _ = Describe("SpacesSearchRegex", func() {
DescribeTable("path matching",
func(path, expectedSpace string, shouldMatch bool) {
matches := spacesSearchRegex.FindStringSubmatch(path)
if shouldMatch {
Expect(matches).ToNot(BeNil(), "Expected path %q to match", path)
Expect(matches[1]).To(Equal(expectedSpace), "Expected space to be %q", expectedSpace)
} else {
Expect(matches).To(BeNil(), "Expected path %q not to match", path)
}
},
Entry("standard dav spaces path", "/dav/spaces/12345", "12345", true),
Entry("remote.php dav spaces path", "/remote.php/dav/spaces/12345", "12345", true),
Entry("standard dav spaces path with subpaths", "/dav/spaces/12345/some/folder", "12345", true),
Entry("remote.php dav spaces path with subpaths", "/remote.php/dav/spaces/12345/some/folder", "12345", true),
Entry("standard dav spaces path without space", "/dav/spaces/", "", false),
Entry("remote.php dav spaces path without space", "/remote.php/dav/spaces/", "", false),
Entry("prefix match only", "/dav/spaces", "", false),
Entry("unrelated path", "/dav/files/123", "", false),
)
})
Loading