github.com/hashicorp/vault/sdk@v0.13.0/logical/response.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package logical 5 6 import ( 7 "bufio" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "net" 12 "net/http" 13 "strconv" 14 "sync/atomic" 15 16 "github.com/hashicorp/vault/sdk/helper/wrapping" 17 ) 18 19 const ( 20 // HTTPContentType can be specified in the Data field of a Response 21 // so that the HTTP front end can specify a custom Content-Type associated 22 // with the HTTPRawBody. This can only be used for non-secrets, and should 23 // be avoided unless absolutely necessary, such as implementing a specification. 24 // The value must be a string. 25 HTTPContentType = "http_content_type" 26 27 // HTTPRawBody is the raw content of the HTTP body that goes with the HTTPContentType. 28 // This can only be specified for non-secrets, and should should be similarly 29 // avoided like the HTTPContentType. The value must be a byte slice. 30 HTTPRawBody = "http_raw_body" 31 32 // HTTPStatusCode is the response code of the HTTP body that goes with the HTTPContentType. 33 // This can only be specified for non-secrets, and should should be similarly 34 // avoided like the HTTPContentType. The value must be an integer. 35 HTTPStatusCode = "http_status_code" 36 37 // For unwrapping we may need to know whether the value contained in the 38 // raw body is already JSON-unmarshaled. The presence of this key indicates 39 // that it has already been unmarshaled. That way we don't need to simply 40 // ignore errors. 41 HTTPRawBodyAlreadyJSONDecoded = "http_raw_body_already_json_decoded" 42 43 // If set, HTTPCacheControlHeader will replace the default Cache-Control=no-store header 44 // set by the generic wrapping handler. The value must be a string. 45 HTTPCacheControlHeader = "http_raw_cache_control" 46 47 // If set, HTTPPragmaHeader will set the Pragma response header. 48 // The value must be a string. 49 HTTPPragmaHeader = "http_raw_pragma" 50 51 // If set, HTTPWWWAuthenticateHeader will set the WWW-Authenticate response header. 52 // The value must be a string. 53 HTTPWWWAuthenticateHeader = "http_www_authenticate" 54 ) 55 56 // Response is a struct that stores the response of a request. 57 // It is used to abstract the details of the higher level request protocol. 58 type Response struct { 59 // Secret, if not nil, denotes that this response represents a secret. 60 Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"` 61 62 // Auth, if not nil, contains the authentication information for 63 // this response. This is only checked and means something for 64 // credential backends. 65 Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"` 66 67 // Response data is an opaque map that must have string keys. For 68 // secrets, this data is sent down to the user as-is. To store internal 69 // data that you don't want the user to see, store it in 70 // Secret.InternalData. 71 Data map[string]interface{} `json:"data" structs:"data" mapstructure:"data"` 72 73 // Redirect is an HTTP URL to redirect to for further authentication. 74 // This is only valid for credential backends. This will be blanked 75 // for any logical backend and ignored. 76 Redirect string `json:"redirect" structs:"redirect" mapstructure:"redirect"` 77 78 // Warnings allow operations or backends to return warnings in response 79 // to user actions without failing the action outright. 80 Warnings []string `json:"warnings" structs:"warnings" mapstructure:"warnings"` 81 82 // Information for wrapping the response in a cubbyhole 83 WrapInfo *wrapping.ResponseWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"` 84 85 // Headers will contain the http headers from the plugin that it wishes to 86 // have as part of the output 87 Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers"` 88 89 // MountType, if non-empty, provides some information about what kind 90 // of mount this secret came from. 91 MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type"` 92 } 93 94 // AddWarning adds a warning into the response's warning list 95 func (r *Response) AddWarning(warning string) { 96 if r.Warnings == nil { 97 r.Warnings = make([]string, 0, 1) 98 } 99 r.Warnings = append(r.Warnings, warning) 100 } 101 102 // IsError returns true if this response seems to indicate an error. 103 func (r *Response) IsError() bool { 104 // If the response data contains only an 'error' element, or an 'error' and a 'data' element only 105 return r != nil && r.Data != nil && r.Data["error"] != nil && (len(r.Data) == 1 || (r.Data["data"] != nil && len(r.Data) == 2)) 106 } 107 108 func (r *Response) Error() error { 109 if !r.IsError() { 110 return nil 111 } 112 switch r.Data["error"].(type) { 113 case string: 114 return errors.New(r.Data["error"].(string)) 115 case error: 116 return r.Data["error"].(error) 117 } 118 return nil 119 } 120 121 // HelpResponse is used to format a help response 122 func HelpResponse(text string, seeAlso []string, oapiDoc interface{}) *Response { 123 return &Response{ 124 Data: map[string]interface{}{ 125 "help": text, 126 "see_also": seeAlso, 127 "openapi": oapiDoc, 128 }, 129 } 130 } 131 132 // ErrorResponse is used to format an error response 133 func ErrorResponse(text string, vargs ...interface{}) *Response { 134 if len(vargs) > 0 { 135 text = fmt.Sprintf(text, vargs...) 136 } 137 return &Response{ 138 Data: map[string]interface{}{ 139 "error": text, 140 }, 141 } 142 } 143 144 // ListResponse is used to format a response to a list operation. 145 func ListResponse(keys []string) *Response { 146 resp := &Response{ 147 Data: map[string]interface{}{}, 148 } 149 if len(keys) != 0 { 150 resp.Data["keys"] = keys 151 } 152 return resp 153 } 154 155 // ListResponseWithInfo is used to format a response to a list operation and 156 // return the keys as well as a map with corresponding key info. 157 func ListResponseWithInfo(keys []string, keyInfo map[string]interface{}) *Response { 158 resp := ListResponse(keys) 159 160 keyInfoData := make(map[string]interface{}) 161 for _, key := range keys { 162 val, ok := keyInfo[key] 163 if ok { 164 keyInfoData[key] = val 165 } 166 } 167 168 if len(keyInfoData) > 0 { 169 resp.Data["key_info"] = keyInfoData 170 } 171 172 return resp 173 } 174 175 // RespondWithStatusCode takes a response and converts it to a raw response with 176 // the provided Status Code. 177 func RespondWithStatusCode(resp *Response, req *Request, code int) (*Response, error) { 178 ret := &Response{ 179 Data: map[string]interface{}{ 180 HTTPContentType: "application/json", 181 HTTPStatusCode: code, 182 }, 183 } 184 185 if resp != nil { 186 httpResp := LogicalResponseToHTTPResponse(resp) 187 188 if req != nil { 189 httpResp.RequestID = req.ID 190 } 191 192 body, err := json.Marshal(httpResp) 193 if err != nil { 194 return nil, err 195 } 196 197 // We default to string here so that the value is HMAC'd via audit. 198 // Since this function is always marshaling to JSON, this is 199 // appropriate. 200 ret.Data[HTTPRawBody] = string(body) 201 } 202 203 return ret, nil 204 } 205 206 // HTTPResponseWriter is optionally added to a request object and can be used to 207 // write directly to the HTTP response writer. 208 type HTTPResponseWriter struct { 209 http.ResponseWriter 210 written *uint32 211 } 212 213 // NewHTTPResponseWriter creates a new HTTPResponseWriter object that wraps the 214 // provided io.Writer. 215 func NewHTTPResponseWriter(w http.ResponseWriter) *HTTPResponseWriter { 216 return &HTTPResponseWriter{ 217 ResponseWriter: w, 218 written: new(uint32), 219 } 220 } 221 222 // Write will write the bytes to the underlying io.Writer. 223 func (w *HTTPResponseWriter) Write(bytes []byte) (int, error) { 224 atomic.StoreUint32(w.written, 1) 225 return w.ResponseWriter.Write(bytes) 226 } 227 228 // Written tells us if the writer has been written to yet. 229 func (w *HTTPResponseWriter) Written() bool { 230 return atomic.LoadUint32(w.written) == 1 231 } 232 233 type WrappingResponseWriter interface { 234 http.ResponseWriter 235 Wrapped() http.ResponseWriter 236 } 237 238 type StatusHeaderResponseWriter struct { 239 wrapped http.ResponseWriter 240 wroteHeader bool 241 StatusCode int 242 headers map[string][]*CustomHeader 243 } 244 245 func NewStatusHeaderResponseWriter(w http.ResponseWriter, h map[string][]*CustomHeader) *StatusHeaderResponseWriter { 246 return &StatusHeaderResponseWriter{ 247 wrapped: w, 248 wroteHeader: false, 249 StatusCode: 200, 250 headers: h, 251 } 252 } 253 254 func (w *StatusHeaderResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 255 if h, ok := w.wrapped.(http.Hijacker); ok { 256 return h.Hijack() 257 } 258 return nil, nil, fmt.Errorf("could not hijack because wrapped connection is %T and it does not implement http.Hijacker", w.wrapped) 259 } 260 261 func (w *StatusHeaderResponseWriter) Wrapped() http.ResponseWriter { 262 return w.wrapped 263 } 264 265 func (w *StatusHeaderResponseWriter) Header() http.Header { 266 return w.wrapped.Header() 267 } 268 269 func (w *StatusHeaderResponseWriter) Write(buf []byte) (int, error) { 270 // It is allowed to only call ResponseWriter.Write and skip 271 // ResponseWriter.WriteHeader. An example of such a situation is 272 // "handleUIStub". The Write function will internally set the status code 273 // 200 for the response for which that call might invoke other 274 // implementations of the WriteHeader function. So, we still need to set 275 // the custom headers. In cases where both WriteHeader and Write of 276 // statusHeaderResponseWriter struct are called the internal call to the 277 // WriterHeader invoked from inside Write method won't change the headers. 278 if !w.wroteHeader { 279 w.setCustomResponseHeaders(w.StatusCode) 280 } 281 282 return w.wrapped.Write(buf) 283 } 284 285 func (w *StatusHeaderResponseWriter) WriteHeader(statusCode int) { 286 w.setCustomResponseHeaders(statusCode) 287 w.wrapped.WriteHeader(statusCode) 288 w.StatusCode = statusCode 289 // in cases where Write is called after WriteHeader, let's prevent setting 290 // ResponseWriter headers twice 291 w.wroteHeader = true 292 } 293 294 func (w *StatusHeaderResponseWriter) setCustomResponseHeaders(status int) { 295 sch := w.headers 296 if sch == nil { 297 return 298 } 299 300 // Checking the validity of the status code 301 if status >= 600 || status < 100 { 302 return 303 } 304 305 // setter function to set the headers 306 setter := func(hvl []*CustomHeader) { 307 for _, hv := range hvl { 308 w.Header().Set(hv.Name, hv.Value) 309 } 310 } 311 312 // Setting the default headers first 313 setter(sch["default"]) 314 315 // setting the Xyy pattern first 316 d := fmt.Sprintf("%vxx", status/100) 317 if val, ok := sch[d]; ok { 318 setter(val) 319 } 320 321 // Setting the specific headers 322 if val, ok := sch[strconv.Itoa(status)]; ok { 323 setter(val) 324 } 325 326 return 327 } 328 329 var _ WrappingResponseWriter = &StatusHeaderResponseWriter{} 330 331 // ResolveRoleResponse returns a standard response to be returned by functions handling a ResolveRoleOperation 332 func ResolveRoleResponse(roleName string) (*Response, error) { 333 return &Response{ 334 Data: map[string]interface{}{ 335 "role": roleName, 336 }, 337 }, nil 338 }