open-vault/logical/response.go

186 lines
5.2 KiB
Go
Raw Normal View History

package logical
2015-10-07 21:21:41 +00:00
import (
"fmt"
"reflect"
"time"
2015-10-07 21:21:41 +00:00
"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"
2015-08-09 19:20:06 +00:00
// 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
// The token containing the wrapped response
Token string
// The creation time. This can be used with the TTL to figure out an
// expected expiration.
CreationTime time.Time
// If the contained response is the output of a token creation call, the
// created token's accessor will be accessible here
WrappedAccessor string
}
// 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 {
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
// Secret, if not nil, denotes that this response represents a secret.
Secret *Secret
// Auth, if not nil, contains the authentication information for
// this response. This is only checked and means something for
// credential backends.
Auth *Auth
vault: clean up VaultID duplications, make secret responses clearer /cc @armon - This is a reasonably major refactor that I think cleans up a lot of the logic with secrets in responses. The reason for the refactor is that while implementing Renew/Revoke in logical/framework I found the existing API to be really awkward to work with. Primarily, we needed a way to send down internal data for Vault core to store since not all the data you need to revoke a key is always sent down to the user (for example the user than AWS key belongs to). At first, I was doing this manually in logical/framework with req.Storage, but this is going to be such a common event that I think its something core should assist with. Additionally, I think the added context for secrets will be useful in the future when we have a Vault API for returning orphaned out keys: we can also return the internal data that might help an operator. So this leads me to this refactor. I've removed most of the fields in `logical.Response` and replaced it with a single `*Secret` pointer. If this is non-nil, then the response represents a secret. The Secret struct encapsulates all the lease info and such. It also has some fields on it that are only populated at _request_ time for Revoke/Renew operations. There is precedent for this sort of behavior in the Go stdlib where http.Request/http.Response have fields that differ based on client/server. I copied this style. All core unit tests pass. The APIs fail for obvious reasons but I'll fix that up in the next commit.
2015-03-19 22:11:42 +00:00
// 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{}
2015-03-31 00:56:24 +00:00
// 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
// 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
// Information for wrapping the response in a cubbyhole
WrapInfo *WrapInfo
}
2015-10-07 21:21:41 +00:00
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 {
2016-05-08 00:03:56 +00:00
return nil, fmt.Errorf("error copying Auth: %v", err)
2015-10-07 21:21:41 +00:00
}
ret.Auth = retAuth.(*Auth)
}
if input.Data != nil {
retData, err := copystructure.Copy(&input.Data)
if err != nil {
2016-05-08 00:03:56 +00:00
return nil, fmt.Errorf("error copying Data: %v", err)
2015-10-07 21:21:41 +00:00
}
ret.Data = retData.(map[string]interface{})
}
if input.Warnings() != nil {
for _, warning := range input.Warnings() {
ret.AddWarning(warning)
}
}
2016-05-08 00:03:56 +00:00
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)
}
2015-10-07 21:21:41 +00:00
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
}
2015-03-20 16:59:48 +00:00
// IsError returns true if this response seems to indicate an error.
func (r *Response) IsError() bool {
return r != nil && len(r.Data) == 1 && r.Data["error"] != 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 {
2016-01-14 19:18:27 +00:00
resp := &Response{
Data: map[string]interface{}{},
}
2016-01-19 22:06:24 +00:00
if len(keys) != 0 {
2016-01-14 19:18:27 +00:00
resp.Data["keys"] = keys
}
2016-01-14 19:18:27 +00:00
return resp
}