1package logical
2
3import (
4	"fmt"
5	"io"
6	"strings"
7	"time"
8)
9
10// RequestWrapInfo is a struct that stores information about desired response
11// and seal wrapping behavior
12type RequestWrapInfo struct {
13	// Setting to non-zero specifies that the response should be wrapped.
14	// Specifies the desired TTL of the wrapping token.
15	TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl" sentinel:""`
16
17	// The format to use for the wrapped response; if not specified it's a bare
18	// token
19	Format string `json:"format" structs:"format" mapstructure:"format" sentinel:""`
20
21	// A flag to conforming backends that data for a given request should be
22	// seal wrapped
23	SealWrap bool `json:"seal_wrap" structs:"seal_wrap" mapstructure:"seal_wrap" sentinel:""`
24}
25
26func (r *RequestWrapInfo) SentinelGet(key string) (interface{}, error) {
27	if r == nil {
28		return nil, nil
29	}
30	switch key {
31	case "ttl":
32		return r.TTL, nil
33	case "ttl_seconds":
34		return int64(r.TTL.Seconds()), nil
35	}
36
37	return nil, nil
38}
39
40func (r *RequestWrapInfo) SentinelKeys() []string {
41	return []string{
42		"ttl",
43		"ttl_seconds",
44	}
45}
46
47type ClientTokenSource uint32
48
49const (
50	NoClientToken ClientTokenSource = iota
51	ClientTokenFromVaultHeader
52	ClientTokenFromAuthzHeader
53)
54
55// Request is a struct that stores the parameters and context of a request
56// being made to Vault. It is used to abstract the details of the higher level
57// request protocol from the handlers.
58//
59// Note: Many of these have Sentinel disabled because they are values populated
60// by the router after policy checks; the token namespace would be the right
61// place to access them via Sentinel
62type Request struct {
63	// Id is the uuid associated with each request
64	ID string `json:"id" structs:"id" mapstructure:"id" sentinel:""`
65
66	// If set, the name given to the replication secondary where this request
67	// originated
68	ReplicationCluster string `json:"replication_cluster" structs:"replication_cluster" mapstructure:"replication_cluster" sentinel:""`
69
70	// Operation is the requested operation type
71	Operation Operation `json:"operation" structs:"operation" mapstructure:"operation"`
72
73	// Path is the part of the request path not consumed by the
74	// routing. As an example, if the original request path is "prod/aws/foo"
75	// and the AWS logical backend is mounted at "prod/aws/", then the
76	// final path is "foo" since the mount prefix is trimmed.
77	Path string `json:"path" structs:"path" mapstructure:"path" sentinel:""`
78
79	// Request data is an opaque map that must have string keys.
80	Data map[string]interface{} `json:"map" structs:"data" mapstructure:"data"`
81
82	// Storage can be used to durably store and retrieve state.
83	Storage Storage `json:"-" sentinel:""`
84
85	// Secret will be non-nil only for Revoke and Renew operations
86	// to represent the secret that was returned prior.
87	Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret" sentinel:""`
88
89	// Auth will be non-nil only for Renew operations
90	// to represent the auth that was returned prior.
91	Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth" sentinel:""`
92
93	// Headers will contain the http headers from the request. This value will
94	// be used in the audit broker to ensure we are auditing only the allowed
95	// headers.
96	Headers map[string][]string `json:"headers" structs:"headers" mapstructure:"headers" sentinel:""`
97
98	// Connection will be non-nil only for credential providers to
99	// inspect the connection information and potentially use it for
100	// authentication/protection.
101	Connection *Connection `json:"connection" structs:"connection" mapstructure:"connection"`
102
103	// ClientToken is provided to the core so that the identity
104	// can be verified and ACLs applied. This value is passed
105	// through to the logical backends but after being salted and
106	// hashed.
107	ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token" sentinel:""`
108
109	// ClientTokenAccessor is provided to the core so that the it can get
110	// logged as part of request audit logging.
111	ClientTokenAccessor string `json:"client_token_accessor" structs:"client_token_accessor" mapstructure:"client_token_accessor" sentinel:""`
112
113	// DisplayName is provided to the logical backend to help associate
114	// dynamic secrets with the source entity. This is not a sensitive
115	// name, but is useful for operators.
116	DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name" sentinel:""`
117
118	// MountPoint is provided so that a logical backend can generate
119	// paths relative to itself. The `Path` is effectively the client
120	// request path with the MountPoint trimmed off.
121	MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point" sentinel:""`
122
123	// MountType is provided so that a logical backend can make decisions
124	// based on the specific mount type (e.g., if a mount type has different
125	// aliases, generating different defaults depending on the alias)
126	MountType string `json:"mount_type" structs:"mount_type" mapstructure:"mount_type" sentinel:""`
127
128	// MountAccessor is provided so that identities returned by the authentication
129	// backends can be tied to the mount it belongs to.
130	MountAccessor string `json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor" sentinel:""`
131
132	// WrapInfo contains requested response wrapping parameters
133	WrapInfo *RequestWrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info" sentinel:""`
134
135	// ClientTokenRemainingUses represents the allowed number of uses left on the
136	// token supplied
137	ClientTokenRemainingUses int `json:"client_token_remaining_uses" structs:"client_token_remaining_uses" mapstructure:"client_token_remaining_uses"`
138
139	// EntityID is the identity of the caller extracted out of the token used
140	// to make this request
141	EntityID string `json:"entity_id" structs:"entity_id" mapstructure:"entity_id" sentinel:""`
142
143	// PolicyOverride indicates that the requestor wishes to override
144	// soft-mandatory Sentinel policies
145	PolicyOverride bool `json:"policy_override" structs:"policy_override" mapstructure:"policy_override"`
146
147	// Whether the request is unauthenticated, as in, had no client token
148	// attached. Useful in some situations where the client token is not made
149	// accessible.
150	Unauthenticated bool `json:"unauthenticated" structs:"unauthenticated" mapstructure:"unauthenticated"`
151
152	// MFACreds holds the parsed MFA information supplied over the API as part of
153	// X-Vault-MFA header
154	MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""`
155
156	// Cached token entry. This avoids another lookup in request handling when
157	// we've already looked it up at http handling time. Note that this token
158	// has not been "used", as in it will not properly take into account use
159	// count limitations. As a result this field should only ever be used for
160	// transport to a function that would otherwise do a lookup and then
161	// properly use the token.
162	tokenEntry *TokenEntry
163
164	// For replication, contains the last WAL on the remote side after handling
165	// the request, used for best-effort avoidance of stale read-after-write
166	lastRemoteWAL uint64
167
168	// ControlGroup holds the authorizations that have happened on this
169	// request
170	ControlGroup *ControlGroup `json:"control_group" structs:"control_group" mapstructure:"control_group" sentinel:""`
171
172	// ClientTokenSource tells us where the client token was sourced from, so
173	// we can delete it before sending off to plugins
174	ClientTokenSource ClientTokenSource
175
176	// RequestReader if set can be used to read the full request body from the
177	// http request that generated this logical.Request object.
178	RequestReader io.ReadCloser `json:"-" sentinel:""`
179
180	// ResponseWriter if set can be used to stream a response value to the http
181	// request that generated this logical.Request object.
182	ResponseWriter *HTTPResponseWriter `json:"-" sentinel:""`
183}
184
185// Get returns a data field and guards for nil Data
186func (r *Request) Get(key string) interface{} {
187	if r.Data == nil {
188		return nil
189	}
190	return r.Data[key]
191}
192
193// GetString returns a data field as a string
194func (r *Request) GetString(key string) string {
195	raw := r.Get(key)
196	s, _ := raw.(string)
197	return s
198}
199
200func (r *Request) GoString() string {
201	return fmt.Sprintf("*%#v", *r)
202}
203
204func (r *Request) SentinelGet(key string) (interface{}, error) {
205	switch key {
206	case "path":
207		// Sanitize it here so that it's consistent in policies
208		return strings.TrimPrefix(r.Path, "/"), nil
209
210	case "wrapping", "wrap_info":
211		// If the pointer is nil accessing the wrap info is considered
212		// "undefined" so this allows us to instead discover a TTL of zero
213		if r.WrapInfo == nil {
214			return &RequestWrapInfo{}, nil
215		}
216		return r.WrapInfo, nil
217	}
218
219	return nil, nil
220}
221
222func (r *Request) SentinelKeys() []string {
223	return []string{
224		"path",
225		"wrapping",
226		"wrap_info",
227	}
228}
229
230func (r *Request) LastRemoteWAL() uint64 {
231	return r.lastRemoteWAL
232}
233
234func (r *Request) SetLastRemoteWAL(last uint64) {
235	r.lastRemoteWAL = last
236}
237
238func (r *Request) TokenEntry() *TokenEntry {
239	return r.tokenEntry
240}
241
242func (r *Request) SetTokenEntry(te *TokenEntry) {
243	r.tokenEntry = te
244}
245
246// RenewRequest creates the structure of the renew request.
247func RenewRequest(path string, secret *Secret, data map[string]interface{}) *Request {
248	return &Request{
249		Operation: RenewOperation,
250		Path:      path,
251		Data:      data,
252		Secret:    secret,
253	}
254}
255
256// RenewAuthRequest creates the structure of the renew request for an auth.
257func RenewAuthRequest(path string, auth *Auth, data map[string]interface{}) *Request {
258	return &Request{
259		Operation: RenewOperation,
260		Path:      path,
261		Data:      data,
262		Auth:      auth,
263	}
264}
265
266// RevokeRequest creates the structure of the revoke request.
267func RevokeRequest(path string, secret *Secret, data map[string]interface{}) *Request {
268	return &Request{
269		Operation: RevokeOperation,
270		Path:      path,
271		Data:      data,
272		Secret:    secret,
273	}
274}
275
276// RollbackRequest creates the structure of the revoke request.
277func RollbackRequest(path string) *Request {
278	return &Request{
279		Operation: RollbackOperation,
280		Path:      path,
281		Data:      make(map[string]interface{}),
282	}
283}
284
285// Operation is an enum that is used to specify the type
286// of request being made
287type Operation string
288
289const (
290	// The operations below are called per path
291	CreateOperation         Operation = "create"
292	ReadOperation                     = "read"
293	UpdateOperation                   = "update"
294	DeleteOperation                   = "delete"
295	ListOperation                     = "list"
296	HelpOperation                     = "help"
297	AliasLookaheadOperation           = "alias-lookahead"
298
299	// The operations below are called globally, the path is less relevant.
300	RevokeOperation   Operation = "revoke"
301	RenewOperation              = "renew"
302	RollbackOperation           = "rollback"
303)
304
305type MFACreds map[string][]string
306
307// InitializationRequest stores the parameters and context of an Initialize()
308// call being made to a logical.Backend.
309type InitializationRequest struct {
310
311	// Storage can be used to durably store and retrieve state.
312	Storage Storage
313}
314