vault/sdk/logical/error.go
Steven Clark b7dff9777d
Allow backends to extract credentials from payloads and trigger an authentication workflow (#23924)
* wip

* Work on the tuneable allowance and some bugs

* Call handleCancellableRequest instead, which gets the audit order more correct and includes the preauth response

* Get rid of no longer needed operation

* Phew, this wasn't necessary

* Add auth error handling by the backend, and fix a bug with handleInvalidCredentials

* Cleanup req/resp naming

* Use the new form, and data

* Discovered that tokens werent really being checked because isLoginRequest returns true for the re-request into the backend, when it shouldnt

* Add a few more checks in the delegated request handler for bad inputs

 - Protect the delegated handler from bad inputs from the backend such
   as an empty accessor, a path that isn't registered as a login request
 - Add similar protections for bad auth results as we do in the normal
   login request paths. Technically not 100% needed but if somehow the
   handleCancelableRequest doesn't use the handleLoginRequest code path
   we could get into trouble in the future
 - Add delegated-auth-accessors flag to the secrets tune command and
   api-docs

* Unit tests and some small fixes

* Remove transit preauth test, rely on unit tests

* Cleanup and add a little more commentary in tests

* Fix typos, add another failure use-case which we reference a disabled auth mount

* PR Feedback

 - Use router to lookup mount instead of defining a new lookup method
 - Enforce auth table types and namespace when mount is found
 - Define a type alias for the handleInvalidCreds
 - Fix typos/grammar
 - Clean up globals in test

* Additional PR feedback

 - Add test for delegated auth handler
 - Force batch token usage
 - Add a test to validate failures if a non-batch token is used
 - Check for Data member being nil in test cases

* Update failure error message around requiring batch tokens

* Trap MFA requests

* Reword some error messages

* Add test and fixes for delegated response wrapping

* Move MFA test to dedicated mount

 - If the delegated auth tests were running in parallel, the MFA test
   case might influence the other tests, so move the MFA to a dedicated
   mount

* PR feedback: use textproto.CanonicalMIMEHeaderKey

 - Change the X-Vault-Wrap-Ttl constant to X-Vault-Wrap-TTL
   and use textproto.CanonicalMIMEHeaderKey to format it
   within the delete call.
 - This protects the code around changes of the constant typing

* PR feedback

 - Append Error to RequestDelegatedAuth
 - Force error interface impl through explicit nil var assignment on
   RequestDelegatedAuthError
 - Clean up test factory and leverage NewTestSoloCluster
 - Leverage newer maps.Clone as this is 1.16 only

---------

Co-authored-by: Scott G. Miller <smiller@hashicorp.com>
2023-11-21 14:36:49 -05:00

170 lines
5.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package logical
import (
"context"
"errors"
)
var (
// ErrUnsupportedOperation is returned if the operation is not supported
// by the logical backend.
ErrUnsupportedOperation = errors.New("unsupported operation")
// ErrUnsupportedPath is returned if the path is not supported
// by the logical backend.
ErrUnsupportedPath = errors.New("unsupported path")
// ErrInvalidRequest is returned if the request is invalid
ErrInvalidRequest = errors.New("invalid request")
// ErrPermissionDenied is returned if the client is not authorized
ErrPermissionDenied = errors.New("permission denied")
// ErrInvalidCredentials is returned when the provided credentials are incorrect
// This is used internally for user lockout purposes. This is not seen externally.
// The status code returned does not change because of this error
ErrInvalidCredentials = errors.New("invalid credentials")
// ErrMultiAuthzPending is returned if the the request needs more
// authorizations
ErrMultiAuthzPending = errors.New("request needs further approval")
// ErrUpstreamRateLimited is returned when Vault receives a rate limited
// response from an upstream
ErrUpstreamRateLimited = errors.New("upstream rate limited")
// ErrPerfStandbyForward is returned when Vault is in a state such that a
// perf standby cannot satisfy a request
ErrPerfStandbyPleaseForward = errors.New("please forward to the active node")
// ErrLeaseCountQuotaExceeded is returned when a request is rejected due to a lease
// count quota being exceeded.
ErrLeaseCountQuotaExceeded = errors.New("lease count quota exceeded")
// ErrRateLimitQuotaExceeded is returned when a request is rejected due to a
// rate limit quota being exceeded.
ErrRateLimitQuotaExceeded = errors.New("rate limit quota exceeded")
// ErrUnrecoverable is returned when a request fails due to something that
// is likely to require manual intervention. This is a generic form of an
// unrecoverable error.
// e.g.: misconfigured or disconnected storage backend.
ErrUnrecoverable = errors.New("unrecoverable error")
// ErrMissingRequiredState is returned when a request can't be satisfied
// with the data in the local node's storage, based on the provided
// X-Vault-Index request header.
ErrMissingRequiredState = errors.New("required index state not present")
// Error indicating that the requested path used to serve a purpose in older
// versions, but the functionality has now been removed
ErrPathFunctionalityRemoved = errors.New("functionality on this path has been removed")
)
type DelegatedAuthErrorHandler func(ctx context.Context, initiatingRequest, authRequest *Request, authResponse *Response, err error) (*Response, error)
var _ error = &RequestDelegatedAuthError{}
// RequestDelegatedAuthError Special error indicating the backend wants to delegate authentication elsewhere
type RequestDelegatedAuthError struct {
mountAccessor string
path string
data map[string]interface{}
errHandler DelegatedAuthErrorHandler
}
func NewDelegatedAuthenticationRequest(mountAccessor, path string, data map[string]interface{}, errHandler DelegatedAuthErrorHandler) *RequestDelegatedAuthError {
return &RequestDelegatedAuthError{
mountAccessor: mountAccessor,
path: path,
data: data,
errHandler: errHandler,
}
}
func (d *RequestDelegatedAuthError) Error() string {
return "authentication delegation requested"
}
func (d *RequestDelegatedAuthError) MountAccessor() string {
return d.mountAccessor
}
func (d *RequestDelegatedAuthError) Path() string {
return d.path
}
func (d *RequestDelegatedAuthError) Data() map[string]interface{} {
return d.data
}
func (d *RequestDelegatedAuthError) AuthErrorHandler() DelegatedAuthErrorHandler {
return d.errHandler
}
type HTTPCodedError interface {
Error() string
Code() int
}
func CodedError(status int, msg string) HTTPCodedError {
return &codedError{
Status: status,
Message: msg,
}
}
var _ HTTPCodedError = (*codedError)(nil)
type codedError struct {
Status int
Message string
}
func (e *codedError) Error() string {
return e.Message
}
func (e *codedError) Code() int {
return e.Status
}
// Struct to identify user input errors. This is helpful in responding the
// appropriate status codes to clients from the HTTP endpoints.
type StatusBadRequest struct {
Err string
}
// Implementing error interface
func (s *StatusBadRequest) Error() string {
return s.Err
}
// This is a new type declared to not cause potential compatibility problems if
// the logic around the CodedError changes; in particular for logical request
// paths it is basically ignored, and changing that behavior might cause
// unforeseen issues.
type ReplicationCodedError struct {
Msg string
Code int
}
func (r *ReplicationCodedError) Error() string {
return r.Msg
}
type KeyNotFoundError struct {
Err error
}
func (e *KeyNotFoundError) WrappedErrors() []error {
return []error{e.Err}
}
func (e *KeyNotFoundError) Error() string {
return e.Err.Error()
}