Skip to content

Commit 1c160c8

Browse files
committed
webdav: check locks before write operations
1 parent b26616f commit 1c160c8

File tree

3 files changed

+94
-5
lines changed

3 files changed

+94
-5
lines changed

internal/internal.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,33 @@ 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+
118145
// Condition is a condition to match lock tokens and entity tags.
119146
//
120147
// 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
@@ -262,6 +262,16 @@ func (b *backend) PropPatch(r *http.Request, update *internal.PropertyUpdate) (*
262262
}
263263

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

@@ -294,6 +304,16 @@ func (b *backend) Put(w http.ResponseWriter, r *http.Request) error {
294304
}
295305

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

@@ -324,6 +344,16 @@ func (b *backend) Mkcol(r *http.Request) error {
324344
}
325345

326346
func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrite bool) (created bool, err error) {
347+
if lock := b.resourceLock(dest.Path); lock != nil {
348+
token, err := internal.ParseSubmittedToken(r.Header)
349+
if err != nil {
350+
return false, err
351+
}
352+
if token != lock.Href {
353+
return false, &internal.HTTPError{Code: http.StatusLocked}
354+
}
355+
}
356+
327357
options := CopyOptions{
328358
NoRecursive: !recursive,
329359
NoOverwrite: !overwrite,
@@ -336,6 +366,37 @@ func (b *backend) Copy(r *http.Request, dest *internal.Href, recursive, overwrit
336366
}
337367

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

0 commit comments

Comments
 (0)