forked from hashicorp/vault
/
response.go
199 lines (168 loc) · 6.06 KB
/
response.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package logical
import (
"errors"
"fmt"
"reflect"
"time"
"github.com/mitchellh/copystructure"
)
const (
// HTTPContentType can be specified in the Data field of a Response
// so that the HTTP front end can specify a custom Content-Type associated
// with the HTTPRawBody. This can only be used for non-secrets, and should
// be avoided unless absolutely necessary, such as implementing a specification.
// The value must be a string.
HTTPContentType = "http_content_type"
// HTTPRawBody is the raw content of the HTTP body that goes with the HTTPContentType.
// This can only be specified for non-secrets, and should should be similarly
// avoided like the HTTPContentType. The value must be a byte slice.
HTTPRawBody = "http_raw_body"
// HTTPStatusCode is the response code of the HTTP body that goes with the HTTPContentType.
// This can only be specified for non-secrets, and should should be similarly
// avoided like the HTTPContentType. The value must be an integer.
HTTPStatusCode = "http_status_code"
)
type WrapInfo struct {
// Setting to non-zero specifies that the response should be wrapped.
// Specifies the desired TTL of the wrapping token.
TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl"`
// The token containing the wrapped response
Token string `json:"token" structs:"token" mapstructure:"token"`
// The creation time. This can be used with the TTL to figure out an
// expected expiration.
CreationTime time.Time `json:"creation_time" structs:"creation_time" mapstructure:"cration_time"`
// If the contained response is the output of a token creation call, the
// created token's accessor will be accessible here
WrappedAccessor string `json:"wrapped_accessor" structs:"wrapped_accessor" mapstructure:"wrapped_accessor"`
}
// Response is a struct that stores the response of a request.
// It is used to abstract the details of the higher level request protocol.
type Response struct {
// Secret, if not nil, denotes that this response represents a secret.
Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"`
// Auth, if not nil, contains the authentication information for
// this response. This is only checked and means something for
// credential backends.
Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"`
// Response data is an opaque map that must have string keys. For
// secrets, this data is sent down to the user as-is. To store internal
// data that you don't want the user to see, store it in
// Secret.InternalData.
Data map[string]interface{} `json:"data" structs:"data" mapstructure:"data"`
// Redirect is an HTTP URL to redirect to for further authentication.
// This is only valid for credential backends. This will be blanked
// for any logical backend and ignored.
Redirect string `json:"redirect" structs:"redirect" mapstructure:"redirect"`
// Warnings allow operations or backends to return warnings in response
// to user actions without failing the action outright.
// Making it private helps ensure that it is easy for various parts of
// Vault (backend, core, etc.) to add warnings without accidentally
// replacing what exists.
warnings []string `json:"warnings" structs:"warnings" mapstructure:"warnings"`
// Information for wrapping the response in a cubbyhole
WrapInfo *WrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"`
}
func init() {
copystructure.Copiers[reflect.TypeOf(Response{})] = func(v interface{}) (interface{}, error) {
input := v.(Response)
ret := Response{
Redirect: input.Redirect,
}
if input.Secret != nil {
retSec, err := copystructure.Copy(input.Secret)
if err != nil {
return nil, fmt.Errorf("error copying Secret: %v", err)
}
ret.Secret = retSec.(*Secret)
}
if input.Auth != nil {
retAuth, err := copystructure.Copy(input.Auth)
if err != nil {
return nil, fmt.Errorf("error copying Auth: %v", err)
}
ret.Auth = retAuth.(*Auth)
}
if input.Data != nil {
retData, err := copystructure.Copy(&input.Data)
if err != nil {
return nil, fmt.Errorf("error copying Data: %v", err)
}
ret.Data = retData.(map[string]interface{})
}
if input.Warnings() != nil {
for _, warning := range input.Warnings() {
ret.AddWarning(warning)
}
}
if input.WrapInfo != nil {
retWrapInfo, err := copystructure.Copy(input.WrapInfo)
if err != nil {
return nil, fmt.Errorf("error copying WrapInfo: %v", err)
}
ret.WrapInfo = retWrapInfo.(*WrapInfo)
}
return &ret, nil
}
}
// AddWarning adds a warning into the response's warning list
func (r *Response) AddWarning(warning string) {
if r.warnings == nil {
r.warnings = make([]string, 0, 1)
}
r.warnings = append(r.warnings, warning)
}
// Warnings returns the list of warnings set on the response
func (r *Response) Warnings() []string {
return r.warnings
}
// ClearWarnings clears the response's warning list
func (r *Response) ClearWarnings() {
r.warnings = make([]string, 0, 1)
}
// Copies the warnings from the other response to this one
func (r *Response) CloneWarnings(other *Response) {
r.warnings = other.warnings
}
// IsError returns true if this response seems to indicate an error.
func (r *Response) IsError() bool {
return r != nil && r.Data != nil && len(r.Data) == 1 && r.Data["error"] != nil
}
func (r *Response) Error() error {
if !r.IsError() {
return nil
}
switch r.Data["error"].(type) {
case string:
return errors.New(r.Data["error"].(string))
case error:
return r.Data["error"].(error)
}
return nil
}
// HelpResponse is used to format a help response
func HelpResponse(text string, seeAlso []string) *Response {
return &Response{
Data: map[string]interface{}{
"help": text,
"see_also": seeAlso,
},
}
}
// ErrorResponse is used to format an error response
func ErrorResponse(text string) *Response {
return &Response{
Data: map[string]interface{}{
"error": text,
},
}
}
// ListResponse is used to format a response to a list operation.
func ListResponse(keys []string) *Response {
resp := &Response{
Data: map[string]interface{}{},
}
if len(keys) != 0 {
resp.Data["keys"] = keys
}
return resp
}