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  }