2015-03-12 00:46:25 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2017-10-07 22:41:07 +00:00
|
|
|
"fmt"
|
2015-03-12 00:46:25 +00:00
|
|
|
"io"
|
2016-06-07 20:01:09 +00:00
|
|
|
"time"
|
2016-07-06 16:25:40 +00:00
|
|
|
|
2018-04-05 15:49:21 +00:00
|
|
|
"github.com/hashicorp/errwrap"
|
2016-07-06 16:25:40 +00:00
|
|
|
"github.com/hashicorp/vault/helper/jsonutil"
|
2017-10-07 22:41:07 +00:00
|
|
|
"github.com/hashicorp/vault/helper/parseutil"
|
2015-03-12 00:46:25 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Secret is the structure returned for every secret within Vault.
|
|
|
|
type Secret struct {
|
2016-07-27 13:25:04 +00:00
|
|
|
// The request ID that generated this response
|
|
|
|
RequestID string `json:"request_id"`
|
|
|
|
|
2015-04-14 00:40:05 +00:00
|
|
|
LeaseID string `json:"lease_id"`
|
|
|
|
LeaseDuration int `json:"lease_duration"`
|
|
|
|
Renewable bool `json:"renewable"`
|
|
|
|
|
|
|
|
// Data is the actual contents of the secret. The format of the data
|
|
|
|
// is arbitrary and up to the secret backend.
|
|
|
|
Data map[string]interface{} `json:"data"`
|
|
|
|
|
2015-10-07 19:30:54 +00:00
|
|
|
// Warnings contains any warnings related to the operation. These
|
|
|
|
// are not issues that caused the command to fail, but that the
|
|
|
|
// client should be aware of.
|
|
|
|
Warnings []string `json:"warnings"`
|
|
|
|
|
2015-04-14 00:40:05 +00:00
|
|
|
// Auth, if non-nil, means that there was authentication information
|
|
|
|
// attached to this response.
|
|
|
|
Auth *SecretAuth `json:"auth,omitempty"`
|
2016-05-02 05:58:58 +00:00
|
|
|
|
|
|
|
// WrapInfo, if non-nil, means that the initial response was wrapped in the
|
|
|
|
// cubbyhole of the given token (which has a TTL of the given number of
|
|
|
|
// seconds)
|
|
|
|
WrapInfo *SecretWrapInfo `json:"wrap_info,omitempty"`
|
|
|
|
}
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// TokenID returns the standardized token ID (token) for the given secret.
|
2017-10-07 22:41:07 +00:00
|
|
|
func (s *Secret) TokenID() (string, error) {
|
2017-09-02 22:48:48 +00:00
|
|
|
if s == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return "", nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Auth != nil && len(s.Auth.ClientToken) > 0 {
|
2017-10-07 22:41:07 +00:00
|
|
|
return s.Auth.ClientToken, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Data == nil || s.Data["id"] == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return "", nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
id, ok := s.Data["id"].(string)
|
|
|
|
if !ok {
|
2017-10-07 22:41:07 +00:00
|
|
|
return "", fmt.Errorf("token found but in the wrong format")
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return id, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TokenAccessor returns the standardized token accessor for the given secret.
|
|
|
|
// If the secret is nil or does not contain an accessor, this returns the empty
|
|
|
|
// string.
|
2017-10-07 22:41:07 +00:00
|
|
|
func (s *Secret) TokenAccessor() (string, error) {
|
2017-09-02 22:48:48 +00:00
|
|
|
if s == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return "", nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Auth != nil && len(s.Auth.Accessor) > 0 {
|
2017-10-07 22:41:07 +00:00
|
|
|
return s.Auth.Accessor, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Data == nil || s.Data["accessor"] == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return "", nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
accessor, ok := s.Data["accessor"].(string)
|
|
|
|
if !ok {
|
2017-10-07 22:41:07 +00:00
|
|
|
return "", fmt.Errorf("token found but in the wrong format")
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return accessor, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TokenRemainingUses returns the standardized remaining uses for the given
|
2017-10-07 22:41:07 +00:00
|
|
|
// secret. If the secret is nil or does not contain the "num_uses", this
|
|
|
|
// returns -1. On error, this will return -1 and a non-nil error.
|
|
|
|
func (s *Secret) TokenRemainingUses() (int, error) {
|
2017-09-02 22:48:48 +00:00
|
|
|
if s == nil || s.Data == nil || s.Data["num_uses"] == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return -1, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
uses, err := parseutil.ParseInt(s.Data["num_uses"])
|
2017-09-02 22:48:48 +00:00
|
|
|
if err != nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return 0, err
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return int(uses), nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TokenPolicies returns the standardized list of policies for the given secret.
|
|
|
|
// If the secret is nil or does not contain any policies, this returns nil.
|
2017-10-07 22:41:07 +00:00
|
|
|
func (s *Secret) TokenPolicies() ([]string, error) {
|
2017-09-02 22:48:48 +00:00
|
|
|
if s == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Auth != nil && len(s.Auth.Policies) > 0 {
|
2017-10-07 22:41:07 +00:00
|
|
|
return s.Auth.Policies, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Data == nil || s.Data["policies"] == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
sList, ok := s.Data["policies"].([]string)
|
|
|
|
if ok {
|
|
|
|
return sList, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
list, ok := s.Data["policies"].([]interface{})
|
|
|
|
if !ok {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, fmt.Errorf("unable to convert token policies to expected format")
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
policies := make([]string, len(list))
|
|
|
|
for i := range list {
|
|
|
|
p, ok := list[i].(string)
|
|
|
|
if !ok {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, fmt.Errorf("unable to convert policy %v to string", list[i])
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
policies[i] = p
|
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return policies, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-09-08 01:55:36 +00:00
|
|
|
// TokenMetadata returns the map of metadata associated with this token, if any
|
|
|
|
// exists. If the secret is nil or does not contain the "metadata" key, this
|
|
|
|
// returns nil.
|
2017-10-07 22:41:07 +00:00
|
|
|
func (s *Secret) TokenMetadata() (map[string]string, error) {
|
2017-09-08 01:55:36 +00:00
|
|
|
if s == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, nil
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Auth != nil && len(s.Auth.Metadata) > 0 {
|
2017-10-07 22:41:07 +00:00
|
|
|
return s.Auth.Metadata, nil
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Data == nil || (s.Data["metadata"] == nil && s.Data["meta"] == nil) {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, nil
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data, ok := s.Data["metadata"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
data, ok = s.Data["meta"].(map[string]interface{})
|
|
|
|
if !ok {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, fmt.Errorf("unable to convert metadata field to expected format")
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
metadata := make(map[string]string, len(data))
|
|
|
|
for k, v := range data {
|
|
|
|
typed, ok := v.(string)
|
|
|
|
if !ok {
|
2017-10-07 22:41:07 +00:00
|
|
|
return nil, fmt.Errorf("unable to convert metadata value %v to string", v)
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
metadata[k] = typed
|
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return metadata, nil
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// TokenIsRenewable returns the standardized token renewability for the given
|
|
|
|
// secret. If the secret is nil or does not contain the "renewable" key, this
|
|
|
|
// returns false.
|
2017-10-07 22:41:07 +00:00
|
|
|
func (s *Secret) TokenIsRenewable() (bool, error) {
|
2017-09-02 22:48:48 +00:00
|
|
|
if s == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return false, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Auth != nil && s.Auth.Renewable {
|
2017-10-07 22:41:07 +00:00
|
|
|
return s.Auth.Renewable, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Data == nil || s.Data["renewable"] == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return false, nil
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
renewable, err := parseutil.ParseBool(s.Data["renewable"])
|
2017-09-08 01:55:36 +00:00
|
|
|
if err != nil {
|
2018-04-05 15:49:21 +00:00
|
|
|
return false, errwrap.Wrapf("could not convert renewable value to a boolean: {{err}}", err)
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return renewable, nil
|
2017-09-08 01:55:36 +00:00
|
|
|
}
|
|
|
|
|
2017-09-02 22:48:48 +00:00
|
|
|
// TokenTTL returns the standardized remaining token TTL for the given secret.
|
2017-10-07 22:41:07 +00:00
|
|
|
// If the secret is nil or does not contain a TTL, this returns 0.
|
|
|
|
func (s *Secret) TokenTTL() (time.Duration, error) {
|
2017-09-02 22:48:48 +00:00
|
|
|
if s == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return 0, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Auth != nil && s.Auth.LeaseDuration > 0 {
|
2017-10-07 22:41:07 +00:00
|
|
|
return time.Duration(s.Auth.LeaseDuration) * time.Second, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if s.Data == nil || s.Data["ttl"] == nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return 0, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
ttl, err := parseutil.ParseDurationSecond(s.Data["ttl"])
|
2017-09-02 22:48:48 +00:00
|
|
|
if err != nil {
|
2017-10-07 22:41:07 +00:00
|
|
|
return 0, err
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:41:07 +00:00
|
|
|
return ttl, nil
|
2017-09-02 22:48:48 +00:00
|
|
|
}
|
|
|
|
|
2016-06-13 23:58:17 +00:00
|
|
|
// SecretWrapInfo contains wrapping information if we have it. If what is
|
|
|
|
// contained is an authentication token, the accessor for the token will be
|
|
|
|
// available in WrappedAccessor.
|
2016-05-02 05:58:58 +00:00
|
|
|
type SecretWrapInfo struct {
|
2016-06-13 23:58:17 +00:00
|
|
|
Token string `json:"token"`
|
2017-11-13 20:31:32 +00:00
|
|
|
Accessor string `json:"accessor"`
|
2016-06-13 23:58:17 +00:00
|
|
|
TTL int `json:"ttl"`
|
|
|
|
CreationTime time.Time `json:"creation_time"`
|
2017-08-02 22:28:58 +00:00
|
|
|
CreationPath string `json:"creation_path"`
|
2016-06-13 23:58:17 +00:00
|
|
|
WrappedAccessor string `json:"wrapped_accessor"`
|
2015-04-04 22:40:41 +00:00
|
|
|
}
|
|
|
|
|
2015-09-29 07:35:16 +00:00
|
|
|
// SecretAuth is the structure containing auth information if we have it.
|
2015-04-04 22:40:41 +00:00
|
|
|
type SecretAuth struct {
|
2015-04-24 16:00:00 +00:00
|
|
|
ClientToken string `json:"client_token"`
|
2016-03-09 11:23:31 +00:00
|
|
|
Accessor string `json:"accessor"`
|
2015-04-04 22:40:41 +00:00
|
|
|
Policies []string `json:"policies"`
|
|
|
|
Metadata map[string]string `json:"metadata"`
|
|
|
|
|
|
|
|
LeaseDuration int `json:"lease_duration"`
|
|
|
|
Renewable bool `json:"renewable"`
|
2015-03-12 00:46:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParseSecret is used to parse a secret value from JSON from an io.Reader.
|
|
|
|
func ParseSecret(r io.Reader) (*Secret, error) {
|
|
|
|
// First decode the JSON into a map[string]interface{}
|
2015-03-16 03:35:33 +00:00
|
|
|
var secret Secret
|
2016-07-06 16:25:40 +00:00
|
|
|
if err := jsonutil.DecodeJSONFromReader(r, &secret); err != nil {
|
2015-03-12 00:46:25 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-03-16 03:35:33 +00:00
|
|
|
return &secret, nil
|
2015-03-12 00:46:25 +00:00
|
|
|
}
|