Skip to content

Commit 50594c3

Browse files
committed
webdav: check locks before write operations
1 parent fe4704e commit 50594c3

3 files changed

Lines changed: 95 additions & 5 deletions

File tree

internal/internal.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,34 @@ func FormatLockToken(token string) string {
115115
return fmt.Sprintf("<%v>", token)
116116
}
117117

118+
func ParseSubmittedToken(h http.Header) (string, error) {
119+
hif := h.Get("If")
120+
if hif == "" {
121+
return "", nil
122+
}
123+
124+
conditions, err := ParseConditions(hif)
125+
if err != nil {
126+
return "", &HTTPError{http.StatusBadRequest, err}
127+
}
128+
129+
if len(conditions) == 0 {
130+
return "", nil
131+
}
132+
if len(conditions) > 1 {
133+
return "", HTTPErrorf(http.StatusBadRequest, "webdav: multiple lists are not supported in the If header field")
134+
}
135+
if len(conditions[0]) == 0 {
136+
return "", nil
137+
}
138+
if len(conditions[0]) > 1 {
139+
return "", HTTPErrorf(http.StatusBadRequest, "webdav: multiple conditions are not supported in the If header field")
140+
}
141+
142+
return conditions[0][0].Token, nil
143+
}
144+
145+
118146
// Condition is a condition to match lock tokens and entity tags.
119147
//
120148
// Only one of Token or ETag is set.

internal/server.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,13 +355,14 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) error {
355355
return err
356356
}
357357

358-
conditions, err := ParseConditions(r.Header.Get("If"))
358+
var err error
359+
refreshToken, err = ParseSubmittedToken(r.Header)
359360
if err != nil {
360-
return &HTTPError{http.StatusBadRequest, err}
361-
} else if len(conditions) != 1 || len(conditions[0]) != 1 || conditions[0][0].Token == "" {
362-
return HTTPErrorf(http.StatusBadRequest, "webdav: a single lock token must be specified in the If header field")
361+
return err
362+
}
363+
if refreshToken == "" {
364+
return HTTPErrorf(http.StatusBadRequest, "webdav: a lock token must be specified in the If header field")
363365
}
364-
refreshToken = conditions[0][0].Token
365366
}
366367

367368
depth := DepthInfinity

server.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,16 @@ func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*
260260
}
261261

262262
func (b *backend) Put(w http.ResponseWriter, r *http.Request) error {
263+
if lock := b.resourceLock(r.URL.Path); lock != nil {
264+
token, err := internal.ParseSubmittedToken(r.Header)
265+
if err != nil {
266+
return err
267+
}
268+
if token != lock.Href {
269+
return &internal.HTTPError{Code: http.StatusLocked}
270+
}
271+
}
272+
263273
ifNoneMatch := ConditionalMatch(r.Header.Get("If-None-Match"))
264274
ifMatch := ConditionalMatch(r.Header.Get("If-Match"))
265275

@@ -292,6 +302,16 @@ func (b *backend) Put(w http.ResponseWriter, r *http.Request) error {
292302
}
293303

294304
func (b *backend) Delete(r *http.Request) error {
305+
if lock := b.resourceLock(r.URL.Path); lock != nil {
306+
token, err := internal.ParseSubmittedToken(r.Header)
307+
if err != nil {
308+
return err
309+
}
310+
if token != lock.Href {
311+
return &internal.HTTPError{Code: http.StatusLocked}
312+
}
313+
}
314+
295315
ifNoneMatch := ConditionalMatch(r.Header.Get("If-None-Match"))
296316
ifMatch := ConditionalMatch(r.Header.Get("If-Match"))
297317

@@ -322,6 +342,16 @@ func (b *backend) Mkcol(r *http.Request) error {
322342
}
323343

324344
func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrite bool) (created bool, err error) {
345+
if lock := b.resourceLock(dest.Path); lock != nil {
346+
token, err := internal.ParseSubmittedToken(r.Header)
347+
if err != nil {
348+
return false, err
349+
}
350+
if token != lock.Href {
351+
return false, &internal.HTTPError{Code: http.StatusLocked}
352+
}
353+
}
354+
325355
options := CopyOptions{
326356
NoRecursive: !recursive,
327357
NoOverwrite: !overwrite,
@@ -334,6 +364,37 @@ func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrit
334364
}
335365

336366
func (b *backend) Move(r *http.Request, dest *internal.Href, overwrite bool) (created bool, err error) {
367+
// Check source and destination locks
368+
var conditions [][]internal.Condition
369+
hif := r.Header.Get("If")
370+
if hif == "" {
371+
conditions = nil
372+
} else {
373+
var err error
374+
conditions, err = internal.ParseConditions(hif)
375+
if err != nil {
376+
return false, &internal.HTTPError{http.StatusBadRequest, err}
377+
}
378+
}
379+
srcLock := b.resourceLock(r.URL.Path)
380+
destLock := b.resourceLock(dest.Path)
381+
for _, conds := range conditions {
382+
if len(conds) == 0 {
383+
continue
384+
}
385+
if len(conds) > 1 {
386+
return false, internal.HTTPErrorf(http.StatusBadRequest, "webdav: multiple conditions are not supported in the If header field")
387+
}
388+
if (conds[0].Resource == "" || conds[0].Resource == r.URL.Path) && srcLock != nil && conds[0].Token == srcLock.Href {
389+
srcLock = nil
390+
} else if (conds[0].Resource == dest.Path) && destLock != nil && conds[0].Token == destLock.Href {
391+
destLock = nil
392+
}
393+
}
394+
if srcLock != nil || destLock != nil {
395+
return false, &internal.HTTPError{Code: http.StatusLocked}
396+
}
397+
337398
options := MoveOptions{
338399
NoOverwrite: !overwrite,
339400
}

0 commit comments

Comments
 (0)