github.com/cs3org/reva/v2@v2.27.7/pkg/errtypes/errtypes.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 errtypes contains definitions for common errors.
    20  // It would have nice to call this package errors, err or error
    21  // but errors clashes with github.com/pkg/errors, err is used for any error variable
    22  // and error is a reserved word :)
    23  package errtypes
    24  
    25  import (
    26  	"net/http"
    27  	"strings"
    28  
    29  	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
    30  )
    31  
    32  // NotFound is the error to use when a something is not found.
    33  type NotFound string
    34  
    35  func (e NotFound) Error() string { return "error: not found: " + string(e) }
    36  
    37  // IsNotFound implements the IsNotFound interface.
    38  func (e NotFound) IsNotFound() {}
    39  
    40  // InternalError is the error to use when we really don't know what happened. Use with care
    41  type InternalError string
    42  
    43  func (e InternalError) Error() string { return "internal error: " + string(e) }
    44  
    45  // IsInternalError implements the IsInternalError interface.
    46  func (e InternalError) IsInternalError() {}
    47  
    48  // PermissionDenied is the error to use when a resource cannot be access because of missing permissions.
    49  type PermissionDenied string
    50  
    51  func (e PermissionDenied) Error() string { return "error: permission denied: " + string(e) }
    52  
    53  // IsPermissionDenied implements the IsPermissionDenied interface.
    54  func (e PermissionDenied) IsPermissionDenied() {}
    55  
    56  // Locked is the error to use when a resource cannot be modified because of a lock.
    57  type Locked string
    58  
    59  func (e Locked) Error() string { return "error: locked by " + string(e) }
    60  
    61  // LockID returns the lock ID that caused this error
    62  func (e Locked) LockID() string {
    63  	return string(e)
    64  }
    65  
    66  // IsLocked implements the IsLocked interface.
    67  func (e Locked) IsLocked() {}
    68  
    69  // Aborted is the error to use when a client should retry at a higher level
    70  // (e.g., when a client-specified test-and-set fails, indicating the
    71  // client should restart a read-modify-write sequence) request fails
    72  // because a requested etag or lock ID mismatches.
    73  //
    74  // HTTP Mapping: 412 Precondition Failed
    75  type Aborted string
    76  
    77  func (e Aborted) Error() string { return "error: aborted: " + string(e) }
    78  
    79  // IsAborted implements the IsAborted interface.
    80  func (e Aborted) IsAborted() {}
    81  
    82  // PreconditionFailed is the error to use when a client should not retry until
    83  // the system state has been explicitly fixed.  E.g., if an "rmdir"
    84  // fails because the directory is non-empty, PreconditionFailed
    85  // should be returned since the client should not retry unless
    86  // the files are deleted from the directory. PreconditionFailed should also be
    87  // returned when an intermediate directory for an MKCOL or PUT is missing.
    88  //
    89  // # FIXME rename to FailedPrecondition to make it less confusable with the http status Precondition Failed
    90  //
    91  // HTTP Mapping: 400 Bad Request, 405 Method Not Allowed, 409 Conflict
    92  type PreconditionFailed string
    93  
    94  func (e PreconditionFailed) Error() string { return "error: precondition failed: " + string(e) }
    95  
    96  // IsPreconditionFailed implements the IsPreconditionFailed interface.
    97  func (e PreconditionFailed) IsPreconditionFailed() {}
    98  
    99  // AlreadyExists is the error to use when a resource something is not found.
   100  type AlreadyExists string
   101  
   102  func (e AlreadyExists) Error() string { return "error: already exists: " + string(e) }
   103  
   104  // IsAlreadyExists implements the IsAlreadyExists interface.
   105  func (e AlreadyExists) IsAlreadyExists() {}
   106  
   107  // UserRequired represents an error when a resource is not found.
   108  type UserRequired string
   109  
   110  func (e UserRequired) Error() string { return "error: user required: " + string(e) }
   111  
   112  // IsUserRequired implements the IsUserRequired interface.
   113  func (e UserRequired) IsUserRequired() {}
   114  
   115  // InvalidCredentials is the error to use when receiving invalid credentials.
   116  type InvalidCredentials string
   117  
   118  func (e InvalidCredentials) Error() string { return "error: invalid credentials: " + string(e) }
   119  
   120  // IsInvalidCredentials implements the IsInvalidCredentials interface.
   121  func (e InvalidCredentials) IsInvalidCredentials() {}
   122  
   123  // NotSupported is the error to use when an action is not supported.
   124  type NotSupported string
   125  
   126  func (e NotSupported) Error() string { return "error: not supported: " + string(e) }
   127  
   128  // IsNotSupported implements the IsNotSupported interface.
   129  func (e NotSupported) IsNotSupported() {}
   130  
   131  // PartialContent is the error to use when the client request has partial data.
   132  type PartialContent string
   133  
   134  func (e PartialContent) Error() string { return "error: partial content: " + string(e) }
   135  
   136  // IsPartialContent implements the IsPartialContent interface.
   137  func (e PartialContent) IsPartialContent() {}
   138  
   139  // BadRequest is the error to use when the server cannot or will not process the request (due to a client error). Reauthenticating won't help.
   140  type BadRequest string
   141  
   142  func (e BadRequest) Error() string { return "error: bad request: " + string(e) }
   143  
   144  // IsBadRequest implements the IsBadRequest interface.
   145  func (e BadRequest) IsBadRequest() {}
   146  
   147  // ChecksumMismatch is the error to use when the transmitted hash does not match the calculated hash.
   148  type ChecksumMismatch string
   149  
   150  func (e ChecksumMismatch) Error() string { return "error: checksum mismatch: " + string(e) }
   151  
   152  // IsChecksumMismatch implements the IsChecksumMismatch interface.
   153  func (e ChecksumMismatch) IsChecksumMismatch() {}
   154  
   155  // StatusChecksumMismatch 419 is an unofficial http status code in an unassigned range that is used for checksum mismatches
   156  // Proposed by https://stackoverflow.com/a/35665694
   157  // Official HTTP status code registry: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
   158  // Note: TUS uses unassigned 460 Checksum-Mismatch
   159  // RFC proposal for checksum digest uses a `Want-Digest` header: https://tools.ietf.org/html/rfc3230
   160  // oc clienst issue: https://github.com/owncloud/core/issues/22711
   161  const StatusChecksumMismatch = 419
   162  
   163  // InsufficientStorage is the error to use when there is insufficient storage.
   164  type InsufficientStorage string
   165  
   166  func (e InsufficientStorage) Error() string { return "error: insufficient storage: " + string(e) }
   167  
   168  // IsInsufficientStorage implements the IsInsufficientStorage interface.
   169  func (e InsufficientStorage) IsInsufficientStorage() {}
   170  
   171  // StatusCode returns StatusInsufficientStorage, this implementation is needed to allow TUS to cast the correct http errors.
   172  func (e InsufficientStorage) StatusCode() int {
   173  	return StatusInsufficientStorage
   174  }
   175  
   176  // NotModified is the error to use when a resource was not modified, e.g. the requested etag did not change.
   177  type NotModified string
   178  
   179  func (e NotModified) Error() string { return "error: not modified: " + string(e) }
   180  
   181  // IsNotModified implements the IsNotModified interface.
   182  func (e NotModified) IsNotModified() {}
   183  
   184  // StatusCode returns StatusInsufficientStorage, this implementation is needed to allow TUS to cast the correct http errors.
   185  func (e NotModified) StatusCode() int {
   186  	return http.StatusNotModified
   187  }
   188  
   189  // Body returns the error body. This implementation is needed to allow TUS to cast the correct http errors
   190  func (e InsufficientStorage) Body() []byte {
   191  	return []byte(e.Error())
   192  }
   193  
   194  // StatusInsufficientStorage 507 is an official HTTP status code to indicate that there is insufficient storage
   195  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/507
   196  const StatusInsufficientStorage = 507
   197  
   198  // TooEarly is the error to use when some are not finished job over resource is still in process.
   199  type TooEarly string
   200  
   201  func (e TooEarly) Error() string { return "error: too early: " + string(e) }
   202  
   203  // IsTooEarly implements the IsTooEarly interface.
   204  func (e TooEarly) IsTooEarly() {}
   205  
   206  // IsNotFound is the interface to implement
   207  // to specify that a resource is not found.
   208  type IsNotFound interface {
   209  	IsNotFound()
   210  }
   211  
   212  // IsAlreadyExists is the interface to implement
   213  // to specify that a resource already exists.
   214  type IsAlreadyExists interface {
   215  	IsAlreadyExists()
   216  }
   217  
   218  // IsInternalError is the interface to implement
   219  // to specify that there was some internal error
   220  type IsInternalError interface {
   221  	IsInternalError()
   222  }
   223  
   224  // IsUserRequired is the interface to implement
   225  // to specify that a user is required.
   226  type IsUserRequired interface {
   227  	IsUserRequired()
   228  }
   229  
   230  // IsInvalidCredentials is the interface to implement
   231  // to specify that credentials were wrong.
   232  type IsInvalidCredentials interface {
   233  	IsInvalidCredentials()
   234  }
   235  
   236  // IsNotSupported is the interface to implement
   237  // to specify that an action is not supported.
   238  type IsNotSupported interface {
   239  	IsNotSupported()
   240  }
   241  
   242  // IsPermissionDenied is the interface to implement
   243  // to specify that an action is denied.
   244  type IsPermissionDenied interface {
   245  	IsPermissionDenied()
   246  }
   247  
   248  // IsLocked is the interface to implement
   249  // to specify that a resource is locked.
   250  type IsLocked interface {
   251  	IsLocked()
   252  }
   253  
   254  // IsAborted is the interface to implement
   255  // to specify that a request was aborted.
   256  type IsAborted interface {
   257  	IsAborted()
   258  }
   259  
   260  // IsPreconditionFailed is the interface to implement
   261  // to specify that a precondition failed.
   262  type IsPreconditionFailed interface {
   263  	IsPreconditionFailed()
   264  }
   265  
   266  // IsPartialContent is the interface to implement
   267  // to specify that the client request has partial data.
   268  type IsPartialContent interface {
   269  	IsPartialContent()
   270  }
   271  
   272  // IsBadRequest is the interface to implement
   273  // to specify that the server cannot or will not process the request.
   274  type IsBadRequest interface {
   275  	IsBadRequest()
   276  }
   277  
   278  // IsChecksumMismatch is the interface to implement
   279  // to specify that a checksum does not match.
   280  type IsChecksumMismatch interface {
   281  	IsChecksumMismatch()
   282  }
   283  
   284  // IsInsufficientStorage is the interface to implement
   285  // to specify that there is insufficient storage.
   286  type IsInsufficientStorage interface {
   287  	IsInsufficientStorage()
   288  }
   289  
   290  // IsTooEarly is the interface to implement
   291  // to specify that there is some not finished job over resource is still in process.
   292  type IsTooEarly interface {
   293  	IsTooEarly()
   294  }
   295  
   296  // NewErrtypeFromStatus maps a rpc status to an errtype
   297  func NewErrtypeFromStatus(status *rpc.Status) error {
   298  	switch status.Code {
   299  	case rpc.Code_CODE_OK:
   300  		return nil
   301  	case rpc.Code_CODE_NOT_FOUND:
   302  		return NotFound(status.Message)
   303  	case rpc.Code_CODE_ALREADY_EXISTS:
   304  		return AlreadyExists(status.Message)
   305  		// case rpc.Code_CODE_FAILED_PRECONDITION: ?
   306  		// return UserRequired(status.Message)
   307  		// case rpc.Code_CODE_PERMISSION_DENIED: ?
   308  		// IsInvalidCredentials
   309  	case rpc.Code_CODE_UNIMPLEMENTED:
   310  		return NotSupported(status.Message)
   311  	case rpc.Code_CODE_PERMISSION_DENIED:
   312  		return PermissionDenied(status.Message)
   313  	case rpc.Code_CODE_LOCKED:
   314  		// FIXME make something better for that
   315  		msg := strings.Split(status.Message, "error: locked by ")
   316  		if len(msg) > 1 {
   317  			return Locked(msg[len(msg)-1])
   318  		}
   319  		return Locked(status.Message)
   320  	// case rpc.Code_CODE_DATA_LOSS: ?
   321  	//	IsPartialContent
   322  	case rpc.Code_CODE_ABORTED:
   323  		return Aborted(status.Message)
   324  	case rpc.Code_CODE_FAILED_PRECONDITION:
   325  		return PreconditionFailed(status.Message)
   326  	case rpc.Code_CODE_INSUFFICIENT_STORAGE:
   327  		return InsufficientStorage(status.Message)
   328  	case rpc.Code_CODE_INVALID_ARGUMENT, rpc.Code_CODE_OUT_OF_RANGE:
   329  		return BadRequest(status.Message)
   330  	case rpc.Code_CODE_TOO_EARLY:
   331  		return TooEarly(status.Message)
   332  	default:
   333  		return InternalError(status.Message)
   334  	}
   335  }
   336  
   337  // NewErrtypeFromHTTPStatusCode maps a http status to an errtype
   338  func NewErrtypeFromHTTPStatusCode(code int, message string) error {
   339  	switch code {
   340  	case http.StatusOK:
   341  		return nil
   342  	case http.StatusNotFound:
   343  		return NotFound(message)
   344  	case http.StatusConflict:
   345  		return AlreadyExists(message)
   346  	case http.StatusNotImplemented:
   347  		return NotSupported(message)
   348  	case http.StatusNotModified:
   349  		return NotModified(message)
   350  	case http.StatusForbidden:
   351  		return PermissionDenied(message)
   352  	case http.StatusLocked:
   353  		return Locked(message)
   354  	case http.StatusPreconditionFailed:
   355  		return Aborted(message)
   356  	case http.StatusMethodNotAllowed:
   357  		return PreconditionFailed(message)
   358  	case http.StatusInsufficientStorage:
   359  		return InsufficientStorage(message)
   360  	case http.StatusBadRequest:
   361  		return BadRequest(message)
   362  	case http.StatusPartialContent:
   363  		return PartialContent(message)
   364  	case http.StatusTooEarly:
   365  		return TooEarly(message)
   366  	case StatusChecksumMismatch:
   367  		return ChecksumMismatch(message)
   368  	default:
   369  		return InternalError(message)
   370  	}
   371  }
   372  
   373  // NewHTTPStatusCodeFromErrtype maps an errtype to a http status
   374  func NewHTTPStatusCodeFromErrtype(err error) int {
   375  	switch err.(type) {
   376  	case NotFound:
   377  		return http.StatusNotFound
   378  	case AlreadyExists:
   379  		return http.StatusConflict
   380  	case NotSupported:
   381  		return http.StatusNotImplemented
   382  	case NotModified:
   383  		return http.StatusNotModified
   384  	case InvalidCredentials:
   385  		return http.StatusUnauthorized
   386  	case PermissionDenied:
   387  		return http.StatusForbidden
   388  	case Locked:
   389  		return http.StatusLocked
   390  	case Aborted:
   391  		return http.StatusPreconditionFailed
   392  	case PreconditionFailed:
   393  		return http.StatusMethodNotAllowed
   394  	case InsufficientStorage:
   395  		return http.StatusInsufficientStorage
   396  	case BadRequest:
   397  		return http.StatusBadRequest
   398  	case PartialContent:
   399  		return http.StatusPartialContent
   400  	case TooEarly:
   401  		return http.StatusTooEarly
   402  	case ChecksumMismatch:
   403  		return StatusChecksumMismatch
   404  	default:
   405  		return http.StatusInternalServerError
   406  	}
   407  }