Skip to content

Commit

Permalink
add lock implementation, refactor error handling
Browse files Browse the repository at this point in the history
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
  • Loading branch information
butonic committed Jan 21, 2022
1 parent 09950db commit f8938c4
Show file tree
Hide file tree
Showing 18 changed files with 807 additions and 156 deletions.
16 changes: 8 additions & 8 deletions internal/http/services/owncloud/ocdav/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s *svc) executePathCopy(ctx context.Context, client gateway.GatewayAPIClie
if createRes.Status.Code == rpc.Code_CODE_PERMISSION_DENIED {
w.WriteHeader(http.StatusForbidden)
m := fmt.Sprintf("Permission denied to create %v", createReq.Ref.Path)
b, err := errors.Marshal(errors.SabredavPermissionDenied, m, "")
b, err := errors.Marshal(http.StatusForbidden, m, "")
errors.HandleWebdavError(log, w, b, err)
}
return nil
Expand Down Expand Up @@ -218,7 +218,7 @@ func (s *svc) executePathCopy(ctx context.Context, client gateway.GatewayAPIClie
if uRes.Status.Code == rpc.Code_CODE_PERMISSION_DENIED {
w.WriteHeader(http.StatusForbidden)
m := fmt.Sprintf("Permissions denied to create %v", uReq.Ref.Path)
b, err := errors.Marshal(errors.SabredavPermissionDenied, m, "")
b, err := errors.Marshal(http.StatusForbidden, m, "")
errors.HandleWebdavError(log, w, b, err)
return nil
}
Expand Down Expand Up @@ -350,7 +350,7 @@ func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, clie
w.WriteHeader(http.StatusForbidden)
// TODO path could be empty or relative...
m := fmt.Sprintf("Permission denied to create %v", createReq.Ref.Path)
b, err := errors.Marshal(errors.SabredavPermissionDenied, m, "")
b, err := errors.Marshal(http.StatusForbidden, m, "")
errors.HandleWebdavError(log, w, b, err)
}
return nil
Expand Down Expand Up @@ -426,7 +426,7 @@ func (s *svc) executeSpacesCopy(ctx context.Context, w http.ResponseWriter, clie
w.WriteHeader(http.StatusForbidden)
// TODO path can be empty or relative
m := fmt.Sprintf("Permissions denied to create %v", uReq.Ref.Path)
b, err := errors.Marshal(errors.SabredavPermissionDenied, m, "")
b, err := errors.Marshal(http.StatusForbidden, m, "")
errors.HandleWebdavError(log, w, b, err)
return nil
}
Expand Down Expand Up @@ -484,15 +484,15 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re
if err != nil {
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Overwrite header is set to incorrect value %v", overwrite)
b, err := errors.Marshal(errors.SabredavBadRequest, m, "")
b, err := errors.Marshal(http.StatusBadRequest, m, "")
errors.HandleWebdavError(log, w, b, err)
return nil
}
depth, err := extractDepth(w, r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Depth header is set to incorrect value %v", depth)
b, err := errors.Marshal(errors.SabredavBadRequest, m, "")
b, err := errors.Marshal(http.StatusBadRequest, m, "")
errors.HandleWebdavError(log, w, b, err)
return nil
}
Expand All @@ -518,7 +518,7 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re
if srcStatRes.Status.Code == rpc.Code_CODE_NOT_FOUND {
w.WriteHeader(http.StatusNotFound)
m := fmt.Sprintf("Resource %v not found", srcStatReq.Ref.Path)
b, err := errors.Marshal(errors.SabredavNotFound, m, "")
b, err := errors.Marshal(http.StatusNotFound, m, "")
errors.HandleWebdavError(log, w, b, err)
}
errors.HandleErrorStatus(log, w, srcStatRes.Status)
Expand All @@ -545,7 +545,7 @@ func (s *svc) prepareCopy(ctx context.Context, w http.ResponseWriter, r *http.Re
log.Warn().Str("overwrite", overwrite).Msg("dst already exists")
w.WriteHeader(http.StatusPreconditionFailed)
m := fmt.Sprintf("Could not overwrite Resource %v", dstRef.Path)
b, err := errors.Marshal(errors.SabredavPreconditionFailed, m, "")
b, err := errors.Marshal(http.StatusPreconditionFailed, m, "")
errors.HandleWebdavError(log, w, b, err) // 412, see https://tools.ietf.org/html/rfc4918#section-9.8.5
return nil
}
Expand Down
8 changes: 4 additions & 4 deletions internal/http/services/owncloud/ocdav/dav.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler {

if r.Header.Get("Depth") == "" {
w.WriteHeader(http.StatusMethodNotAllowed)
b, err := errors.Marshal(errors.SabredavMethodNotAllowed, "Listing members of this collection is disabled", "")
b, err := errors.Marshal(http.StatusMethodNotAllowed, "Listing members of this collection is disabled", "")
if err != nil {
log.Error().Msgf("error marshaling xml response: %s", b)
w.WriteHeader(http.StatusInternalServerError)
Expand Down Expand Up @@ -208,11 +208,11 @@ func (h *DavHandler) Handler(s *svc) http.Handler {
case res.Status.Code == rpcv1beta1.Code_CODE_UNAUTHENTICATED:
w.WriteHeader(http.StatusUnauthorized)
if hasValidBasicAuthHeader {
b, err := errors.Marshal(errors.SabredavNotAuthenticated, "Username or password was incorrect", "")
b, err := errors.Marshal(http.StatusUnauthorized, "Username or password was incorrect", "")
errors.HandleWebdavError(log, w, b, err)
return
}
b, err := errors.Marshal(errors.SabredavNotAuthenticated, "No 'Authorization: Basic' header found", "")
b, err := errors.Marshal(http.StatusUnauthorized, "No 'Authorization: Basic' header found", "")
errors.HandleWebdavError(log, w, b, err)
return
case res.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND:
Expand Down Expand Up @@ -263,7 +263,7 @@ func (h *DavHandler) Handler(s *svc) http.Handler {

default:
w.WriteHeader(http.StatusNotFound)
b, err := errors.Marshal(errors.SabredavNotFound, "File not found in root", "")
b, err := errors.Marshal(http.StatusNotFound, "File not found in root", "")
errors.HandleWebdavError(log, w, b, err)
}
})
Expand Down
6 changes: 3 additions & 3 deletions internal/http/services/owncloud/ocdav/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,19 @@ func (s *svc) handleDelete(ctx context.Context, w http.ResponseWriter, r *http.R
w.WriteHeader(http.StatusNotFound)
// TODO path might be empty or relative...
m := fmt.Sprintf("Resource %v not found", ref.Path)
b, err := errors.Marshal(errors.SabredavNotFound, m, "")
b, err := errors.Marshal(http.StatusNotFound, m, "")
errors.HandleWebdavError(&log, w, b, err)
}
if res.Status.Code == rpc.Code_CODE_PERMISSION_DENIED {
w.WriteHeader(http.StatusForbidden)
// TODO path might be empty or relative...
m := fmt.Sprintf("Permission denied to delete %v", ref.Path)
b, err := errors.Marshal(errors.SabredavPermissionDenied, m, "")
b, err := errors.Marshal(http.StatusForbidden, m, "")
errors.HandleWebdavError(&log, w, b, err)
}
if res.Status.Code == rpc.Code_CODE_INTERNAL && res.Status.Message == "can't delete mount path" {
w.WriteHeader(http.StatusForbidden)
b, err := errors.Marshal(errors.SabredavPermissionDenied, res.Status.Message, "")
b, err := errors.Marshal(http.StatusForbidden, res.Status.Message, "")
errors.HandleWebdavError(&log, w, b, err)
}

Expand Down
125 changes: 89 additions & 36 deletions internal/http/services/owncloud/ocdav/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,51 +27,79 @@ import (
"github.com/rs/zerolog"
)

type code int
var sabreException = map[int]string{

const (
//http.StatusMultipleChoices: "Multiple Choices",
//http.StatusMovedPermanently: "Moved Permanently",
//http.StatusFound: "Found",
//http.StatusSeeOther: "See Other",
//http.StatusNotModified: "Not Modified",
//http.StatusUseProxy: "Use Proxy",
//http.StatusTemporaryRedirect: "Temporary Redirect",
//http.StatusPermanentRedirect: "Permanent Redirect",

// SabredavBadRequest maps to HTTP 400
SabredavBadRequest code = iota
// SabredavMethodNotAllowed maps to HTTP 405
SabredavMethodNotAllowed
// SabredavNotAuthenticated maps to HTTP 401
SabredavNotAuthenticated
// SabredavPreconditionFailed maps to HTTP 412
SabredavPreconditionFailed
// SabredavPermissionDenied maps to HTTP 403
SabredavPermissionDenied
// SabredavNotFound maps to HTTP 404
SabredavNotFound
// SabredavConflict maps to HTTP 409
SabredavConflict
)
http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest",
http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated",
http.StatusPaymentRequired: "Sabre\\DAV\\Exception\\PaymentRequired",
http.StatusForbidden: "Sabre\\DAV\\Exception\\Forbidden", // InvalidResourceType, InvalidSyncToken, TooManyMatches
http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound",
http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed",
//http.StatusNotAcceptable: "Not Acceptable",
//http.StatusProxyAuthRequired: "Proxy Authentication Required",
//http.StatusRequestTimeout: "Request Timeout",
http.StatusConflict: "Sabre\\DAV\\Exception\\Conflict", // LockTokenMatchesRequestUri
//http.StatusGone: "Gone",
http.StatusLengthRequired: "Sabre\\DAV\\Exception\\LengthRequired",
http.StatusPreconditionFailed: "Sabre\\DAV\\Exception\\PreconditionFailed",
//http.StatusRequestEntityTooLarge: "Request Entity Too Large",
//http.StatusRequestURITooLong: "Request URI Too Long",
http.StatusUnsupportedMediaType: "Sabre\\DAV\\Exception\\UnsupportedMediaType", // ReportNotSupported
http.StatusRequestedRangeNotSatisfiable: "Sabre\\DAV\\Exception\\RequestedRangeNotSatisfiable",
//http.StatusExpectationFailed: "Expectation Failed",
//http.StatusTeapot: "I'm a teapot",
//http.StatusMisdirectedRequest: "Misdirected Request",
//http.StatusUnprocessableEntity: "Unprocessable Entity",
http.StatusLocked: "Sabre\\DAV\\Exception\\Locked", // ConflictingLock
//http.StatusFailedDependency: "Failed Dependency",
//http.StatusTooEarly: "Too Early",
//http.StatusUpgradeRequired: "Upgrade Required",
//http.StatusPreconditionRequired: "Precondition Required",
//http.StatusTooManyRequests: "Too Many Requests",
//http.StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large",
//http.StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons",

var (
codesEnum = []string{
"Sabre\\DAV\\Exception\\BadRequest",
"Sabre\\DAV\\Exception\\MethodNotAllowed",
"Sabre\\DAV\\Exception\\NotAuthenticated",
"Sabre\\DAV\\Exception\\PreconditionFailed",
"Sabre\\DAV\\Exception\\PermissionDenied",
"Sabre\\DAV\\Exception\\NotFound",
"Sabre\\DAV\\Exception\\Conflict",
}
)
//http.StatusInternalServerError: "Internal Server Error",
http.StatusNotImplemented: "Sabre\\DAV\\Exception\\NotImplemented",
//http.StatusBadGateway: "Bad Gateway",
http.StatusServiceUnavailable: "Sabre\\DAV\\Exception\\ServiceUnavailable",
//http.StatusGatewayTimeout: "Gateway Timeout",
//http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
//http.StatusVariantAlsoNegotiates: "Variant Also Negotiates",
http.StatusInsufficientStorage: "Sabre\\DAV\\Exception\\InsufficientStorage",
//http.StatusLoopDetected: "Loop Detected",
//http.StatusNotExtended: "Not Extended",
//http.StatusNetworkAuthenticationRequired: "Network Authentication Required",
}

// SabreException returns a sabre exception text for the HTTP status code. It returns the empty
// string if the code is unknown.
func SabreException(code int) string {
return sabreException[code]
}

// Exception represents a ocdav exception
type Exception struct {
Code code
Code int
Message string
Header string
}

// Marshal just calls the xml marshaller for a given exception.
func Marshal(code code, message string, header string) ([]byte, error) {
func Marshal(code int, message string, header string) ([]byte, error) {
xmlstring, err := xml.Marshal(&ErrorXML{
Xmlnsd: "DAV",
Xmlnss: "http://sabredav.org/ns",
Exception: codesEnum[code],
Exception: sabreException[code],
Message: message,
Header: header,
})
Expand All @@ -94,11 +122,36 @@ type ErrorXML struct {
Header string `xml:"s:header,omitempty"`
}

// ErrorInvalidPropfind is an invalid propfind error
var ErrorInvalidPropfind = errors.New("webdav: invalid propfind")

// ErrInvalidProppatch is an invalid proppatch error
var ErrInvalidProppatch = errors.New("webdav: invalid proppatch")
var (
// ErrInvalidDepth is an invalid depth header error
ErrInvalidDepth = errors.New("webdav: invalid depth")
// ErrInvalidPropfind is an invalid propfind error
ErrInvalidPropfind = errors.New("webdav: invalid propfind")
// ErrInvalidProppatch is an invalid proppatch error
ErrInvalidProppatch = errors.New("webdav: invalid proppatch")
// ErrUnsupportedLockInfo is an invalid lock error
ErrInvalidLockInfo = errors.New("webdav: invalid lock info")
// ErrUnsupportedLockInfo is an unsupported lock error
ErrUnsupportedLockInfo = errors.New("webdav: unsupported lock info")
// ErrInvalidTimeout is an invalid timeout error
ErrInvalidTimeout = errors.New("webdav: invalid timeout")
// ErrInvalidIfHeader is an invalid if header error
ErrInvalidIfHeader = errors.New("webdav: invalid If header")
// ErrUnsupportedMethod is an unsupported method error
ErrUnsupportedMethod = errors.New("webdav: unsupported method")
// ErrInvalidLockToken is an invalid lock token error
ErrInvalidLockToken = errors.New("webdav: invalid lock token")
// ErrConfirmationFailed is returned by a LockSystem's Confirm method.
ErrConfirmationFailed = errors.New("webdav: confirmation failed")
// ErrForbidden is returned by a LockSystem's Unlock method.
ErrForbidden = errors.New("webdav: forbidden")
// ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods.
ErrLocked = errors.New("webdav: locked")
// ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods.
ErrNoSuchLock = errors.New("webdav: no such lock")
// ErrNotImplemented is returned when hitting not implemented code paths
ErrNotImplemented = errors.New("webdav: not implemented")
)

// HandleErrorStatus checks the status code, logs a Debug or Error level message
// and writes an appropriate http status
Expand Down
Loading

0 comments on commit f8938c4

Please sign in to comment.