1package logical 2 3import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 8 "github.com/hashicorp/vault/helper/wrapping" 9) 10 11const ( 12 // HTTPContentType can be specified in the Data field of a Response 13 // so that the HTTP front end can specify a custom Content-Type associated 14 // with the HTTPRawBody. This can only be used for non-secrets, and should 15 // be avoided unless absolutely necessary, such as implementing a specification. 16 // The value must be a string. 17 HTTPContentType = "http_content_type" 18 19 // HTTPRawBody is the raw content of the HTTP body that goes with the HTTPContentType. 20 // This can only be specified for non-secrets, and should should be similarly 21 // avoided like the HTTPContentType. The value must be a byte slice. 22 HTTPRawBody = "http_raw_body" 23 24 // HTTPStatusCode is the response code of the HTTP body that goes with the HTTPContentType. 25 // This can only be specified for non-secrets, and should should be similarly 26 // avoided like the HTTPContentType. The value must be an integer. 27 HTTPStatusCode = "http_status_code" 28 29 // For unwrapping we may need to know whether the value contained in the 30 // raw body is already JSON-unmarshaled. The presence of this key indicates 31 // that it has already been unmarshaled. That way we don't need to simply 32 // ignore errors. 33 HTTPRawBodyAlreadyJSONDecoded = "http_raw_body_already_json_decoded" 34) 35 36// Response is a struct that stores the response of a request. 37// It is used to abstract the details of the higher level request protocol. 38type Response struct { 39 // Secret, if not nil, denotes that this response represents a secret. 40 Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"` 41 42 // Auth, if not nil, contains the authentication information for 43 // this response. This is only checked and means something for 44 // credential backends. 45 Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"` 46 47 // Response data is an opaque map that must have string keys. For 48 // secrets, this data is sent down to the user as-is. To store internal 49 // data that you don't want the user to see, store it in 50 // Secret.InternalData. 51 Data map[string]interface{} `json:"data" structs:"data" mapstructure:"data"` 52 53 // Redirect is an HTTP URL to redirect to for further authentication. 54 // This is only valid for credential backends. This will be blanked 55 // for any logical backend and ignored. 56 Redirect string `json:"redirect" structs:"redirect" mapstructure:"redirect"` 57 58 // Warnings allow operations or backends to return warnings in response 59 // to user actions without failing the action outright. 60 Warnings []string `json:"warnings" structs:"warnings" mapstructure:"warnings"` 61 62 // Information for wrapping the response in a cubbyhole 63 WrapInfo *wrapping.ResponseWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"` 64 65 // Headers will contain the http headers from the plugin that it wishes to 66 // have as part of the output 67 Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers"` 68} 69 70// AddWarning adds a warning into the response's warning list 71func (r *Response) AddWarning(warning string) { 72 if r.Warnings == nil { 73 r.Warnings = make([]string, 0, 1) 74 } 75 r.Warnings = append(r.Warnings, warning) 76} 77 78// IsError returns true if this response seems to indicate an error. 79func (r *Response) IsError() bool { 80 return r != nil && r.Data != nil && len(r.Data) == 1 && r.Data["error"] != nil 81} 82 83func (r *Response) Error() error { 84 if !r.IsError() { 85 return nil 86 } 87 switch r.Data["error"].(type) { 88 case string: 89 return errors.New(r.Data["error"].(string)) 90 case error: 91 return r.Data["error"].(error) 92 } 93 return nil 94} 95 96// HelpResponse is used to format a help response 97func HelpResponse(text string, seeAlso []string, oapiDoc interface{}) *Response { 98 return &Response{ 99 Data: map[string]interface{}{ 100 "help": text, 101 "see_also": seeAlso, 102 "openapi": oapiDoc, 103 }, 104 } 105} 106 107// ErrorResponse is used to format an error response 108func ErrorResponse(text string, vargs ...interface{}) *Response { 109 if len(vargs) > 0 { 110 text = fmt.Sprintf(text, vargs...) 111 } 112 return &Response{ 113 Data: map[string]interface{}{ 114 "error": text, 115 }, 116 } 117} 118 119// ListResponse is used to format a response to a list operation. 120func ListResponse(keys []string) *Response { 121 resp := &Response{ 122 Data: map[string]interface{}{}, 123 } 124 if len(keys) != 0 { 125 resp.Data["keys"] = keys 126 } 127 return resp 128} 129 130// ListResponseWithInfo is used to format a response to a list operation and 131// return the keys as well as a map with corresponding key info. 132func ListResponseWithInfo(keys []string, keyInfo map[string]interface{}) *Response { 133 resp := ListResponse(keys) 134 135 keyInfoData := make(map[string]interface{}) 136 for _, key := range keys { 137 val, ok := keyInfo[key] 138 if ok { 139 keyInfoData[key] = val 140 } 141 } 142 143 if len(keyInfoData) > 0 { 144 resp.Data["key_info"] = keyInfoData 145 } 146 147 return resp 148} 149 150// RespondWithStatusCode takes a response and converts it to a raw response with 151// the provided Status Code. 152func RespondWithStatusCode(resp *Response, req *Request, code int) (*Response, error) { 153 ret := &Response{ 154 Data: map[string]interface{}{ 155 HTTPContentType: "application/json", 156 HTTPStatusCode: code, 157 }, 158 } 159 160 if resp != nil { 161 httpResp := LogicalResponseToHTTPResponse(resp) 162 163 if req != nil { 164 httpResp.RequestID = req.ID 165 } 166 167 body, err := json.Marshal(httpResp) 168 if err != nil { 169 return nil, err 170 } 171 172 // We default to string here so that the value is HMAC'd via audit. 173 // Since this function is always marshaling to JSON, this is 174 // appropriate. 175 ret.Data[HTTPRawBody] = string(body) 176 } 177 178 return ret, nil 179} 180