github.com/cs3org/reva/v2@v2.27.7/internal/http/services/owncloud/ocdav/errors/error.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package errors 20 21 import ( 22 "bytes" 23 "encoding/xml" 24 "net/http" 25 26 rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" 27 "github.com/cs3org/reva/v2/pkg/rgrpc/status" 28 "github.com/pkg/errors" 29 "github.com/rs/zerolog" 30 ) 31 32 var sabreException = map[int]string{ 33 34 // the commented states have no corresponding exception in sabre/dav, 35 // see https://github.com/sabre-io/dav/tree/master/lib/DAV/Exception 36 37 // http.StatusMultipleChoices: "Multiple Choices", 38 // http.StatusMovedPermanently: "Moved Permanently", 39 // http.StatusFound: "Found", 40 // http.StatusSeeOther: "See Other", 41 // http.StatusNotModified: "Not Modified", 42 // http.StatusUseProxy: "Use Proxy", 43 // http.StatusTemporaryRedirect: "Temporary Redirect", 44 // http.StatusPermanentRedirect: "Permanent Redirect", 45 46 http.StatusBadRequest: "Sabre\\DAV\\Exception\\BadRequest", 47 http.StatusUnauthorized: "Sabre\\DAV\\Exception\\NotAuthenticated", 48 http.StatusPaymentRequired: "Sabre\\DAV\\Exception\\PaymentRequired", 49 http.StatusForbidden: "Sabre\\DAV\\Exception\\Forbidden", // InvalidResourceType, InvalidSyncToken, TooManyMatches 50 http.StatusNotFound: "Sabre\\DAV\\Exception\\NotFound", 51 http.StatusMethodNotAllowed: "Sabre\\DAV\\Exception\\MethodNotAllowed", 52 // http.StatusNotAcceptable: "Not Acceptable", 53 // http.StatusProxyAuthRequired: "Proxy Authentication Required", 54 // http.StatusRequestTimeout: "Request Timeout", 55 http.StatusConflict: "Sabre\\DAV\\Exception\\Conflict", // LockTokenMatchesRequestUri 56 // http.StatusGone: "Gone", 57 http.StatusLengthRequired: "Sabre\\DAV\\Exception\\LengthRequired", 58 http.StatusPreconditionFailed: "Sabre\\DAV\\Exception\\PreconditionFailed", 59 // http.StatusRequestEntityTooLarge: "Request Entity Too Large", 60 // http.StatusRequestURITooLong: "Request URI Too Long", 61 http.StatusUnsupportedMediaType: "Sabre\\DAV\\Exception\\UnsupportedMediaType", // ReportNotSupported 62 http.StatusRequestedRangeNotSatisfiable: "Sabre\\DAV\\Exception\\RequestedRangeNotSatisfiable", 63 // http.StatusExpectationFailed: "Expectation Failed", 64 // http.StatusTeapot: "I'm a teapot", 65 // http.StatusMisdirectedRequest: "Misdirected Request", 66 // http.StatusUnprocessableEntity: "Unprocessable Entity", 67 http.StatusLocked: "Sabre\\DAV\\Exception\\Locked", // ConflictingLock 68 // http.StatusFailedDependency: "Failed Dependency", 69 // http.StatusTooEarly: "Too Early", 70 // http.StatusUpgradeRequired: "Upgrade Required", 71 // http.StatusPreconditionRequired: "Precondition Required", 72 // http.StatusTooManyRequests: "Too Many Requests", 73 // http.StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large", 74 // http.StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons", 75 76 // http.StatusInternalServerError: "Internal Server Error", 77 http.StatusNotImplemented: "Sabre\\DAV\\Exception\\NotImplemented", 78 // http.StatusBadGateway: "Bad Gateway", 79 http.StatusServiceUnavailable: "Sabre\\DAV\\Exception\\ServiceUnavailable", 80 // http.StatusGatewayTimeout: "Gateway Timeout", 81 // http.StatusHTTPVersionNotSupported: "HTTP Version Not Supported", 82 // http.StatusVariantAlsoNegotiates: "Variant Also Negotiates", 83 http.StatusInsufficientStorage: "Sabre\\DAV\\Exception\\InsufficientStorage", 84 // http.StatusLoopDetected: "Loop Detected", 85 // http.StatusNotExtended: "Not Extended", 86 // http.StatusNetworkAuthenticationRequired: "Network Authentication Required", 87 } 88 89 // SabreException returns a sabre exception text for the HTTP status code. It returns the empty 90 // string if the code is unknown. 91 func SabreException(code int) string { 92 return sabreException[code] 93 } 94 95 // Exception represents a ocdav exception 96 type Exception struct { 97 Code int 98 Message string 99 Header string 100 } 101 102 // Marshal just calls the xml marshaller for a given exception. 103 func Marshal(code int, message string, header string, errorCode string) ([]byte, error) { 104 xmlstring, err := xml.Marshal(&ErrorXML{ 105 Xmlnsd: "DAV", 106 Xmlnss: "http://sabredav.org/ns", 107 Exception: sabreException[code], 108 Message: message, 109 Header: header, 110 ErrorCode: errorCode, 111 }) 112 if err != nil { 113 return nil, err 114 } 115 var buf bytes.Buffer 116 buf.WriteString(xml.Header) 117 buf.Write(xmlstring) 118 return buf.Bytes(), err 119 } 120 121 // ErrorXML holds the xml representation of an error 122 // http://www.webdav.org/specs/rfc4918.html#ELEMENT_error 123 type ErrorXML struct { 124 XMLName xml.Name `xml:"d:error"` 125 Xmlnsd string `xml:"xmlns:d,attr"` 126 Xmlnss string `xml:"xmlns:s,attr"` 127 Exception string `xml:"s:exception"` 128 Message string `xml:"s:message"` 129 ErrorCode string `xml:"s:errorcode"` 130 InnerXML []byte `xml:",innerxml"` 131 // Header is used to indicate the conflicting request header 132 Header string `xml:"s:header,omitempty"` 133 } 134 135 var ( 136 // ErrInvalidDepth is an invalid depth header error 137 ErrInvalidDepth = errors.New("webdav: invalid depth") 138 // ErrInvalidPropfind is an invalid propfind error 139 ErrInvalidPropfind = errors.New("webdav: invalid propfind") 140 // ErrInvalidProppatch is an invalid proppatch error 141 ErrInvalidProppatch = errors.New("webdav: invalid proppatch") 142 // ErrInvalidLockInfo is an invalid lock error 143 ErrInvalidLockInfo = errors.New("webdav: invalid lock info") 144 // ErrUnsupportedLockInfo is an unsupported lock error 145 ErrUnsupportedLockInfo = errors.New("webdav: unsupported lock info") 146 // ErrInvalidTimeout is an invalid timeout error 147 ErrInvalidTimeout = errors.New("webdav: invalid timeout") 148 // ErrInvalidIfHeader is an invalid if header error 149 ErrInvalidIfHeader = errors.New("webdav: invalid If header") 150 // ErrUnsupportedMethod is an unsupported method error 151 ErrUnsupportedMethod = errors.New("webdav: unsupported method") 152 // ErrInvalidLockToken is an invalid lock token error 153 ErrInvalidLockToken = errors.New("webdav: invalid lock token") 154 // ErrConfirmationFailed is returned by a LockSystem's Confirm method. 155 ErrConfirmationFailed = errors.New("webdav: confirmation failed") 156 // ErrForbidden is returned by a LockSystem's Unlock method. 157 ErrForbidden = errors.New("webdav: forbidden") 158 // ErrLocked is returned by a LockSystem's Create, Refresh and Unlock methods. 159 ErrLocked = errors.New("webdav: locked") 160 // ErrNoSuchLock is returned by a LockSystem's Refresh and Unlock methods. 161 ErrNoSuchLock = errors.New("webdav: no such lock") 162 // ErrNotImplemented is returned when hitting not implemented code paths 163 ErrNotImplemented = errors.New("webdav: not implemented") 164 // ErrTokenNotFound is returned when a token is not found 165 ErrTokenStatInfoMissing = errors.New("webdav: token stat info missing") 166 ) 167 168 // HandleErrorStatus checks the status code, logs a Debug or Error level message 169 // and writes an appropriate http status 170 func HandleErrorStatus(log *zerolog.Logger, w http.ResponseWriter, s *rpc.Status) { 171 hsc := status.HTTPStatusFromCode(s.Code) 172 if s.Code == rpc.Code_CODE_ABORTED { 173 // aborted is used for etag an lock mismatches, which translates to 412 174 // in case a real Conflict response is needed, the calling code needs to send the header 175 hsc = http.StatusPreconditionFailed 176 } 177 if hsc == http.StatusInternalServerError { 178 log.Error().Interface("status", s).Int("code", hsc).Msg(http.StatusText(hsc)) 179 } else { 180 log.Debug().Interface("status", s).Int("code", hsc).Msg(http.StatusText(hsc)) 181 } 182 w.WriteHeader(hsc) 183 } 184 185 // HandleWebdavError checks the status code, logs an error and creates a webdav response body 186 // if needed 187 func HandleWebdavError(log *zerolog.Logger, w http.ResponseWriter, b []byte, err error) { 188 if err != nil { 189 log.Error().Msgf("error marshaling xml response: %s", b) 190 w.WriteHeader(http.StatusInternalServerError) 191 return 192 } 193 _, err = w.Write(b) 194 if err != nil { 195 log.Err(err).Msg("error writing response") 196 } 197 } 198 199 func NewErrFromStatus(s *rpc.Status) error { 200 switch s.GetCode() { 201 case rpc.Code_CODE_OK: 202 return nil 203 case rpc.Code_CODE_DEADLINE_EXCEEDED: 204 return ErrInvalidTimeout 205 case rpc.Code_CODE_PERMISSION_DENIED: 206 return ErrForbidden 207 case rpc.Code_CODE_LOCKED, rpc.Code_CODE_FAILED_PRECONDITION: 208 return ErrLocked 209 case rpc.Code_CODE_UNIMPLEMENTED: 210 return ErrNotImplemented 211 default: 212 return errors.New(s.GetMessage()) 213 } 214 }