2015-03-12 06:05:16 +00:00
package http
import (
"encoding/json"
2015-04-01 04:24:20 +00:00
"fmt"
2015-08-26 14:03:33 +00:00
"io"
2018-04-17 22:52:09 +00:00
"net"
2015-03-12 06:05:16 +00:00
"net/http"
2018-04-17 22:52:09 +00:00
"net/textproto"
2015-04-19 20:18:09 +00:00
"net/url"
2018-03-27 20:23:33 +00:00
"os"
2015-04-03 05:21:33 +00:00
"strings"
2017-10-23 21:41:44 +00:00
"time"
2015-03-12 06:05:16 +00:00
2018-03-27 20:23:33 +00:00
"github.com/elazarl/go-bindata-assetfs"
2016-03-08 23:27:03 +00:00
"github.com/hashicorp/errwrap"
2017-12-16 01:19:37 +00:00
cleanhttp "github.com/hashicorp/go-cleanhttp"
2018-04-17 22:52:09 +00:00
sockaddr "github.com/hashicorp/go-sockaddr"
2017-02-16 20:15:02 +00:00
"github.com/hashicorp/vault/helper/consts"
2016-07-06 16:25:40 +00:00
"github.com/hashicorp/vault/helper/jsonutil"
2017-05-19 12:34:17 +00:00
"github.com/hashicorp/vault/helper/parseutil"
2015-03-29 23:14:54 +00:00
"github.com/hashicorp/vault/logical"
2015-03-12 06:05:16 +00:00
"github.com/hashicorp/vault/vault"
)
2016-05-02 02:39:45 +00:00
const (
// AuthHeaderName is the name of the header containing the token.
AuthHeaderName = "X-Vault-Token"
2017-01-04 21:44:03 +00:00
// WrapTTLHeaderName is the name of the header containing a directive to
// wrap the response
2016-05-02 04:08:07 +00:00
WrapTTLHeaderName = "X-Vault-Wrap-TTL"
2016-08-15 13:42:42 +00:00
2017-01-04 21:44:03 +00:00
// WrapFormatHeaderName is the name of the header containing the format to
// wrap in; has no effect if the wrap TTL is not set
WrapFormatHeaderName = "X-Vault-Wrap-Format"
2016-08-15 13:42:42 +00:00
// NoRequestForwardingHeaderName is the name of the header telling Vault
// not to use request forwarding
NoRequestForwardingHeaderName = "X-Vault-No-Request-Forwarding"
2016-11-17 20:06:43 +00:00
2017-10-23 21:39:21 +00:00
// MFAHeaderName represents the HTTP header which carries the credentials
// required to perform MFA on any path.
MFAHeaderName = "X-Vault-MFA"
// canonicalMFAHeaderName is the MFA header value's format in the request
// headers. Do not alter the casing of this string.
canonicalMFAHeaderName = "X-Vault-Mfa"
// PolicyOverrideHeaderName is the header set to request overriding
// soft-mandatory Sentinel policies.
PolicyOverrideHeaderName = "X-Vault-Policy-Override"
2016-11-17 20:06:43 +00:00
// MaxRequestSize is the maximum accepted request size. This is to prevent
// a denial of service attack where no Content-Length is provided and the server
// is fed ever more data until it exhausts memory.
2016-11-17 20:15:37 +00:00
MaxRequestSize = 32 * 1024 * 1024
2016-05-02 02:39:45 +00:00
)
2015-05-11 17:56:41 +00:00
2017-10-23 21:39:21 +00:00
var (
ReplicationStaleReadTimeout = 2 * time . Second
2018-03-27 20:23:33 +00:00
// Set to false by stub_asset if the ui build tag isn't enabled
uiBuiltIn = true
2017-10-23 21:39:21 +00:00
)
2015-03-12 06:05:16 +00:00
// Handler returns an http.Handler for the API. This can be used on
// its own to mount the Vault API within another web server.
func Handler ( core * vault . Core ) http . Handler {
2015-04-03 05:21:33 +00:00
// Create the muxer to handle the actual endpoints
2015-03-12 06:05:16 +00:00
mux := http . NewServeMux ( )
2015-03-12 19:37:41 +00:00
mux . Handle ( "/v1/sys/init" , handleSysInit ( core ) )
2015-03-12 17:47:31 +00:00
mux . Handle ( "/v1/sys/seal-status" , handleSysSealStatus ( core ) )
mux . Handle ( "/v1/sys/seal" , handleSysSeal ( core ) )
2017-05-25 15:55:40 +00:00
mux . Handle ( "/v1/sys/step-down" , handleRequestForwarding ( core , handleSysStepDown ( core ) ) )
2015-03-12 17:47:31 +00:00
mux . Handle ( "/v1/sys/unseal" , handleSysUnseal ( core ) )
2015-04-20 18:59:24 +00:00
mux . Handle ( "/v1/sys/leader" , handleSysLeader ( core ) )
2015-04-23 18:53:31 +00:00
mux . Handle ( "/v1/sys/health" , handleSysHealth ( core ) )
2017-11-10 18:19:42 +00:00
mux . Handle ( "/v1/sys/generate-root/attempt" , handleRequestForwarding ( core , handleSysGenerateRootAttempt ( core , vault . GenerateStandardRootTokenStrategy ) ) )
mux . Handle ( "/v1/sys/generate-root/update" , handleRequestForwarding ( core , handleSysGenerateRootUpdate ( core , vault . GenerateStandardRootTokenStrategy ) ) )
2016-08-15 13:42:42 +00:00
mux . Handle ( "/v1/sys/rekey/init" , handleRequestForwarding ( core , handleSysRekeyInit ( core , false ) ) )
mux . Handle ( "/v1/sys/rekey/update" , handleRequestForwarding ( core , handleSysRekeyUpdate ( core , false ) ) )
mux . Handle ( "/v1/sys/rekey-recovery-key/init" , handleRequestForwarding ( core , handleSysRekeyInit ( core , true ) ) )
mux . Handle ( "/v1/sys/rekey-recovery-key/update" , handleRequestForwarding ( core , handleSysRekeyUpdate ( core , true ) ) )
2016-09-29 04:01:28 +00:00
mux . Handle ( "/v1/sys/wrapping/lookup" , handleRequestForwarding ( core , handleLogical ( core , false , wrappingVerificationFunc ) ) )
mux . Handle ( "/v1/sys/wrapping/rewrap" , handleRequestForwarding ( core , handleLogical ( core , false , wrappingVerificationFunc ) ) )
mux . Handle ( "/v1/sys/wrapping/unwrap" , handleRequestForwarding ( core , handleLogical ( core , false , wrappingVerificationFunc ) ) )
2017-10-05 15:17:50 +00:00
for _ , path := range injectDataIntoTopRoutes {
mux . Handle ( path , handleRequestForwarding ( core , handleLogical ( core , true , nil ) ) )
}
mux . Handle ( "/v1/sys/" , handleRequestForwarding ( core , handleLogical ( core , false , nil ) ) )
2016-08-15 13:42:42 +00:00
mux . Handle ( "/v1/" , handleRequestForwarding ( core , handleLogical ( core , false , nil ) ) )
2018-03-27 20:23:33 +00:00
if core . UIEnabled ( ) == true {
if uiBuiltIn {
mux . Handle ( "/ui/" , http . StripPrefix ( "/ui/" , handleUIHeaders ( core , handleUI ( http . FileServer ( & UIAssetWrapper { FileSystem : assetFS ( ) } ) ) ) ) )
} else {
mux . Handle ( "/ui/" , handleUIHeaders ( core , handleUIStub ( ) ) )
}
mux . Handle ( "/" , handleRootRedirect ( ) )
}
2015-04-03 05:21:33 +00:00
// Wrap the handler in another handler to trigger all help paths.
2016-12-15 22:53:07 +00:00
helpWrappedHandler := wrapHelpHandler ( mux , core )
2017-06-17 04:04:55 +00:00
corsWrappedHandler := wrapCORSHandler ( helpWrappedHandler , core )
2015-04-03 05:21:33 +00:00
2016-12-15 22:53:07 +00:00
// Wrap the help wrapped handler with another layer with a generic
// handler
2017-06-17 04:04:55 +00:00
genericWrappedHandler := wrapGenericHandler ( corsWrappedHandler )
2016-12-15 22:53:07 +00:00
2017-12-16 01:19:37 +00:00
// Wrap the handler with PrintablePathCheckHandler to check for non-printable
// characters in the request path.
printablePathCheckHandler := cleanhttp . PrintablePathCheckHandler ( genericWrappedHandler , nil )
return printablePathCheckHandler
2016-12-15 22:53:07 +00:00
}
// wrapGenericHandler wraps the handler with an extra layer of handler where
// tasks that should be commonly handled for all the requests and/or responses
// are performed.
func wrapGenericHandler ( h http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
// Set the Cache-Control header for all the responses returned
// by Vault
w . Header ( ) . Set ( "Cache-Control" , "no-store" )
h . ServeHTTP ( w , r )
return
} )
2015-04-03 05:21:33 +00:00
}
2018-04-17 22:52:09 +00:00
func WrapForwardedForHandler ( h http . Handler , authorizedAddrs [ ] * sockaddr . SockAddrMarshaler , rejectNotPresent , rejectNonAuthz bool , hopSkips int ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
headers , headersOK := r . Header [ textproto . CanonicalMIMEHeaderKey ( "X-Forwarded-For" ) ]
if ! headersOK || len ( headers ) == 0 {
if ! rejectNotPresent {
h . ServeHTTP ( w , r )
return
}
respondError ( w , http . StatusBadRequest , fmt . Errorf ( "missing x-forwarded-for header and configured to reject when not present" ) )
return
}
host , port , err := net . SplitHostPort ( r . RemoteAddr )
if err != nil {
// If not rejecting treat it like we just don't have a valid
// header because we can't do a comparison against an address we
// can't understand
if ! rejectNotPresent {
h . ServeHTTP ( w , r )
return
}
respondError ( w , http . StatusBadRequest , errwrap . Wrapf ( "error parsing client hostport: {{err}}" , err ) )
return
}
addr , err := sockaddr . NewIPAddr ( host )
if err != nil {
// We treat this the same as the case above
if ! rejectNotPresent {
h . ServeHTTP ( w , r )
return
}
respondError ( w , http . StatusBadRequest , errwrap . Wrapf ( "error parsing client address: {{err}}" , err ) )
return
}
var found bool
for _ , authz := range authorizedAddrs {
if authz . Contains ( addr ) {
found = true
break
}
}
if ! found {
// If we didn't find it and aren't configured to reject, simply
// don't trust it
if ! rejectNonAuthz {
h . ServeHTTP ( w , r )
return
}
respondError ( w , http . StatusBadRequest , fmt . Errorf ( "client address not authorized for x-forwarded-for and configured to reject connection" ) )
return
}
// At this point we have at least one value and it's authorized
// Split comma separated ones, which are common. This brings it in line
// to the multiple-header case.
var acc [ ] string
for _ , header := range headers {
vals := strings . Split ( header , "," )
for _ , v := range vals {
acc = append ( acc , strings . TrimSpace ( v ) )
}
}
indexToUse := len ( acc ) - 1 - hopSkips
if indexToUse < 0 {
// This is likely an error in either configuration or other
// infrastructure. We could either deny the request, or we
// could simply not trust the value. Denying the request is
// "safer" since if this logic is configured at all there may
// be an assumption it can always be trusted. Given that we can
// deny accepting the request at all if it's not from an
// authorized address, if we're at this point the address is
// authorized (or we've turned off explicit rejection) and we
// should assume that what comes in should be properly
// formatted.
respondError ( w , http . StatusBadRequest , fmt . Errorf ( "malformed x-forwarded-for configuration or request, hops to skip (%d) would skip before earliest chain link (chain length %d)" , hopSkips , len ( headers ) ) )
return
}
r . RemoteAddr = net . JoinHostPort ( acc [ indexToUse ] , port )
h . ServeHTTP ( w , r )
return
} )
}
2016-09-29 04:01:28 +00:00
// A lookup on a token that is about to expire returns nil, which means by the
// time we can validate a wrapping token lookup will return nil since it will
// be revoked after the call. So we have to do the validation here.
func wrappingVerificationFunc ( core * vault . Core , req * logical . Request ) error {
if req == nil {
2016-03-18 03:01:28 +00:00
return fmt . Errorf ( "invalid request" )
}
2016-09-29 04:01:28 +00:00
2017-01-04 21:44:03 +00:00
valid , err := core . ValidateWrappingToken ( req )
2016-09-29 04:01:28 +00:00
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "error validating wrapping token: {{err}}" , err )
2016-09-29 04:01:28 +00:00
}
if ! valid {
return fmt . Errorf ( "wrapping token is not valid or does not exist" )
}
2016-03-18 02:29:53 +00:00
return nil
}
2015-04-03 05:21:33 +00:00
// stripPrefix is a helper to strip a prefix from the path. It will
// return false from the second return value if it the prefix doesn't exist.
func stripPrefix ( prefix , path string ) ( string , bool ) {
if ! strings . HasPrefix ( path , prefix ) {
return "" , false
}
path = path [ len ( prefix ) : ]
if path == "" {
return "" , false
}
return path , true
2015-03-12 06:05:16 +00:00
}
2018-03-27 20:23:33 +00:00
func handleUIHeaders ( core * vault . Core , h http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
header := w . Header ( )
userHeaders , err := core . UIHeaders ( )
if err != nil {
respondError ( w , http . StatusInternalServerError , err )
return
}
if userHeaders != nil {
for k := range userHeaders {
v := userHeaders . Get ( k )
header . Set ( k , v )
}
}
h . ServeHTTP ( w , req )
} )
}
func handleUI ( h http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
h . ServeHTTP ( w , req )
return
} )
}
func handleUIStub ( ) http . Handler {
stubHTML := `
< ! DOCTYPE html >
< html >
< p > Vault UI is not available in this binary . To get Vault UI do one of the following : < / p >
< ul >
< li > < a href = "https://www.vaultproject.io/downloads.html" > Download an official release < / a > < / li >
< li > Run < code > make release < / code > to create your own release binaries .
< li > Run < code > make dev - ui < / code > to create a development binary with the UI .
< / ul >
< / html >
`
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
w . Write ( [ ] byte ( stubHTML ) )
} )
}
func handleRootRedirect ( ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , req * http . Request ) {
http . Redirect ( w , req , "/ui/" , 307 )
return
} )
}
type UIAssetWrapper struct {
FileSystem * assetfs . AssetFS
}
func ( fs * UIAssetWrapper ) Open ( name string ) ( http . File , error ) {
file , err := fs . FileSystem . Open ( name )
if err == nil {
return file , nil
}
// serve index.html instead of 404ing
if err == os . ErrNotExist {
return fs . FileSystem . Open ( "index.html" )
}
return nil , err
}
2016-12-01 18:59:00 +00:00
func parseRequest ( r * http . Request , w http . ResponseWriter , out interface { } ) error {
2016-11-17 20:06:43 +00:00
// Limit the maximum number of bytes to MaxRequestSize to protect
// against an indefinite amount of data being read.
2016-12-01 18:59:00 +00:00
limit := http . MaxBytesReader ( w , r . Body , MaxRequestSize )
2016-11-17 20:06:43 +00:00
err := jsonutil . DecodeJSONFromReader ( limit , out )
2015-08-26 14:03:33 +00:00
if err != nil && err != io . EOF {
2016-12-01 18:59:00 +00:00
return errwrap . Wrapf ( "failed to parse JSON input: {{err}}" , err )
2015-08-26 14:03:33 +00:00
}
return err
2015-03-12 06:05:16 +00:00
}
2016-08-15 13:42:42 +00:00
// handleRequestForwarding determines whether to forward a request or not,
// falling back on the older behavior of redirecting the client
func handleRequestForwarding ( core * vault . Core , handler http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
if r . Header . Get ( vault . IntNoForwardingHeaderName ) != "" {
handler . ServeHTTP ( w , r )
return
}
if r . Header . Get ( NoRequestForwardingHeaderName ) != "" {
// Forwarding explicitly disabled, fall back to previous behavior
2018-04-03 00:46:59 +00:00
core . Logger ( ) . Debug ( "handleRequestForwarding: forwarding disabled by client request" )
2016-08-15 13:42:42 +00:00
handler . ServeHTTP ( w , r )
return
}
// Note: in an HA setup, this call will also ensure that connections to
// the leader are set up, as that happens once the advertised cluster
// values are read during this function
2017-07-31 22:25:27 +00:00
isLeader , leaderAddr , _ , err := core . Leader ( )
2016-08-15 13:42:42 +00:00
if err != nil {
if err == vault . ErrHANotEnabled {
// Standalone node, serve request normally
handler . ServeHTTP ( w , r )
return
}
// Some internal error occurred
respondError ( w , http . StatusInternalServerError , err )
return
}
if isLeader {
// No forwarding needed, we're leader
handler . ServeHTTP ( w , r )
return
}
if leaderAddr == "" {
2017-07-31 22:25:27 +00:00
respondError ( w , http . StatusInternalServerError , fmt . Errorf ( "local node not active but active cluster node not found" ) )
2016-08-15 13:42:42 +00:00
return
}
// Attempt forwarding the request. If we cannot forward -- perhaps it's
// been disabled on the active node -- this will return with an
// ErrCannotForward and we simply fall back
2016-08-26 21:53:47 +00:00
statusCode , header , retBytes , err := core . ForwardRequest ( r )
2016-08-15 13:42:42 +00:00
if err != nil {
if err == vault . ErrCannotForward {
2018-04-03 00:46:59 +00:00
core . Logger ( ) . Debug ( "handleRequestForwarding: cannot forward (possibly disabled on active node), falling back" )
2016-08-15 13:42:42 +00:00
} else {
2018-04-03 00:46:59 +00:00
core . Logger ( ) . Error ( "handleRequestForwarding: error forwarding request" , "error" , err )
2016-08-15 13:42:42 +00:00
}
// Fall back to redirection
handler . ServeHTTP ( w , r )
return
}
2016-08-26 21:53:47 +00:00
if header != nil {
for k , v := range header {
2017-11-02 12:31:50 +00:00
w . Header ( ) [ k ] = v
2016-08-26 21:53:47 +00:00
}
}
2016-08-19 15:03:53 +00:00
w . WriteHeader ( statusCode )
w . Write ( retBytes )
2016-08-15 13:42:42 +00:00
return
} )
}
2015-04-08 18:19:03 +00:00
// request is a helper to perform a request and properly exit in the
// case of an error.
2015-04-19 21:36:50 +00:00
func request ( core * vault . Core , w http . ResponseWriter , rawReq * http . Request , r * logical . Request ) ( * logical . Response , bool ) {
2015-04-08 18:19:03 +00:00
resp , err := core . HandleRequest ( r )
2017-02-16 20:15:02 +00:00
if errwrap . Contains ( err , consts . ErrStandby . Error ( ) ) {
2015-04-19 21:36:50 +00:00
respondStandby ( core , w , rawReq . URL )
2015-04-19 20:18:09 +00:00
return resp , false
}
2017-02-16 20:15:02 +00:00
if respondErrorCommon ( w , r , resp , err ) {
2015-04-08 18:19:03 +00:00
return resp , false
}
return resp , true
}
2015-04-19 20:18:09 +00:00
// respondStandby is used to trigger a redirect in the case that this Vault is currently a hot standby
func respondStandby ( core * vault . Core , w http . ResponseWriter , reqURL * url . URL ) {
// Request the leader address
2017-07-31 22:25:27 +00:00
_ , redirectAddr , _ , err := core . Leader ( )
2015-04-19 20:18:09 +00:00
if err != nil {
respondError ( w , http . StatusInternalServerError , err )
return
}
// If there is no leader, generate a 503 error
2016-08-15 13:42:42 +00:00
if redirectAddr == "" {
2015-04-19 20:18:09 +00:00
err = fmt . Errorf ( "no active Vault instance found" )
respondError ( w , http . StatusServiceUnavailable , err )
return
}
2016-08-15 13:42:42 +00:00
// Parse the redirect location
redirectURL , err := url . Parse ( redirectAddr )
2015-04-19 20:18:09 +00:00
if err != nil {
respondError ( w , http . StatusInternalServerError , err )
return
}
// Generate a redirect URL
2016-08-15 13:42:42 +00:00
finalURL := url . URL {
Scheme : redirectURL . Scheme ,
Host : redirectURL . Host ,
2015-04-19 20:18:09 +00:00
Path : reqURL . Path ,
RawQuery : reqURL . RawQuery ,
}
// Ensure there is a scheme, default to https
2016-08-15 13:42:42 +00:00
if finalURL . Scheme == "" {
finalURL . Scheme = "https"
2015-04-19 20:18:09 +00:00
}
// If we have an address, redirect! We use a 307 code
// because we don't actually know if its permanent and
// the request method should be preserved.
2016-08-15 13:42:42 +00:00
w . Header ( ) . Set ( "Location" , finalURL . String ( ) )
2015-04-19 20:18:09 +00:00
w . WriteHeader ( 307 )
}
2015-03-29 23:14:54 +00:00
// requestAuth adds the token to the logical.Request if it exists.
2016-10-29 21:01:49 +00:00
func requestAuth ( core * vault . Core , r * http . Request , req * logical . Request ) * logical . Request {
2015-05-11 17:56:41 +00:00
// Attach the header value if we have it
if v := r . Header . Get ( AuthHeaderName ) ; v != "" {
req . ClientToken = v
2016-10-29 21:01:49 +00:00
// Also attach the accessor if we have it. This doesn't fail if it
// doesn't exist because the request may be to an unauthenticated
// endpoint/login endpoint where a bad current token doesn't matter, or
// a token from a Vault version pre-accessors.
te , err := core . LookupToken ( v )
if err == nil && te != nil {
req . ClientTokenAccessor = te . Accessor
2017-03-08 22:36:50 +00:00
req . ClientTokenRemainingUses = te . NumUses
2016-10-29 21:01:49 +00:00
}
2015-05-11 17:56:41 +00:00
}
2015-03-29 23:14:54 +00:00
return req
}
2017-01-04 21:44:03 +00:00
// requestWrapInfo adds the WrapInfo value to the logical.Request if wrap info exists
func requestWrapInfo ( r * http . Request , req * logical . Request ) ( * logical . Request , error ) {
2016-05-02 02:39:45 +00:00
// First try for the header value
2016-05-02 04:08:07 +00:00
wrapTTL := r . Header . Get ( WrapTTLHeaderName )
if wrapTTL == "" {
2016-05-02 02:39:45 +00:00
return req , nil
}
// If it has an allowed suffix parse as a duration string
2017-03-07 16:21:22 +00:00
dur , err := parseutil . ParseDurationSecond ( wrapTTL )
2016-07-11 18:19:35 +00:00
if err != nil {
return req , err
2016-05-02 02:39:45 +00:00
}
2016-07-11 18:19:35 +00:00
if int64 ( dur ) < 0 {
2016-06-07 19:00:35 +00:00
return req , fmt . Errorf ( "requested wrap ttl cannot be negative" )
}
2017-01-04 21:44:03 +00:00
req . WrapInfo = & logical . RequestWrapInfo {
TTL : dur ,
}
wrapFormat := r . Header . Get ( WrapFormatHeaderName )
switch wrapFormat {
case "jwt" :
req . WrapInfo . Format = "jwt"
}
2016-05-02 02:39:45 +00:00
return req , nil
}
2015-03-12 06:05:16 +00:00
func respondError ( w http . ResponseWriter , status int , err error ) {
2017-02-16 20:15:02 +00:00
logical . AdjustErrorStatusCode ( & status , err )
2015-08-10 17:27:25 +00:00
2017-11-02 12:31:50 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-03-12 06:05:16 +00:00
w . WriteHeader ( status )
resp := & ErrorResponse { Errors : make ( [ ] string , 0 , 1 ) }
if err != nil {
resp . Errors = append ( resp . Errors , err . Error ( ) )
}
enc := json . NewEncoder ( w )
enc . Encode ( resp )
}
2017-02-16 20:15:02 +00:00
func respondErrorCommon ( w http . ResponseWriter , req * logical . Request , resp * logical . Response , err error ) bool {
statusCode , newErr := logical . RespondErrorCommon ( req , resp , err )
if newErr == nil && statusCode == 0 {
2015-04-01 04:29:53 +00:00
return false
}
2017-02-16 20:15:02 +00:00
respondError ( w , statusCode , newErr )
2016-06-22 21:47:05 +00:00
return true
2015-04-01 04:24:20 +00:00
}
2015-03-12 06:05:16 +00:00
func respondOk ( w http . ResponseWriter , body interface { } ) {
2017-11-02 12:31:50 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2015-03-12 06:05:16 +00:00
if body == nil {
w . WriteHeader ( http . StatusNoContent )
} else {
w . WriteHeader ( http . StatusOK )
enc := json . NewEncoder ( w )
enc . Encode ( body )
}
}
type ErrorResponse struct {
Errors [ ] string ` json:"errors" `
}
2017-10-05 15:17:50 +00:00
var injectDataIntoTopRoutes = [ ] string {
"/v1/sys/audit" ,
"/v1/sys/audit/" ,
"/v1/sys/audit-hash/" ,
"/v1/sys/auth" ,
"/v1/sys/auth/" ,
"/v1/sys/config/cors" ,
"/v1/sys/config/auditing/request-headers/" ,
"/v1/sys/config/auditing/request-headers" ,
"/v1/sys/capabilities" ,
"/v1/sys/capabilities-accessor" ,
"/v1/sys/capabilities-self" ,
"/v1/sys/key-status" ,
"/v1/sys/mounts" ,
"/v1/sys/mounts/" ,
"/v1/sys/policy" ,
"/v1/sys/policy/" ,
"/v1/sys/rekey/backup" ,
"/v1/sys/rekey/recovery-key-backup" ,
"/v1/sys/remount" ,
"/v1/sys/rotate" ,
"/v1/sys/wrapping/wrap" ,
}