vendor vault api

This commit is contained in:
Alex Dadgar 2017-08-01 09:30:55 -07:00
parent 8df67563c7
commit 562ea52c8e
16 changed files with 591 additions and 44 deletions

View file

@ -480,6 +480,7 @@ func runnerConfig(config *config.Config, vaultToken string) (*ctconf.Config, err
if config.VaultConfig != nil && config.VaultConfig.IsEnabled() { if config.VaultConfig != nil && config.VaultConfig.IsEnabled() {
conf.Vault.Address = &config.VaultConfig.Addr conf.Vault.Address = &config.VaultConfig.Addr
conf.Vault.Token = &vaultToken conf.Vault.Token = &vaultToken
// XXX
if strings.HasPrefix(config.VaultConfig.Addr, "https") || config.VaultConfig.TLSCertFile != "" { if strings.HasPrefix(config.VaultConfig.Addr, "https") || config.VaultConfig.TLSCertFile != "" {
skipVerify := config.VaultConfig.TLSSkipVerify != nil && *config.VaultConfig.TLSSkipVerify skipVerify := config.VaultConfig.TLSSkipVerify != nil && *config.VaultConfig.TLSSkipVerify

View file

@ -135,6 +135,26 @@ func (c *TokenAuth) RenewSelf(increment int) (*Secret, error) {
return ParseSecret(resp.Body) return ParseSecret(resp.Body)
} }
// RenewTokenAsSelf behaves like renew-self, but authenticates using a provided
// token instead of the token attached to the client.
func (c *TokenAuth) RenewTokenAsSelf(token string, increment int) (*Secret, error) {
r := c.c.NewRequest("PUT", "/v1/auth/token/renew-self")
r.ClientToken = token
body := map[string]interface{}{"increment": increment}
if err := r.SetJSONBody(body); err != nil {
return nil, err
}
resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ParseSecret(resp.Body)
}
// RevokeAccessor revokes a token associated with the given accessor // RevokeAccessor revokes a token associated with the given accessor
// along with all the child tokens. // along with all the child tokens.
func (c *TokenAuth) RevokeAccessor(accessor string) error { func (c *TokenAuth) RevokeAccessor(accessor string) error {

View file

@ -6,13 +6,17 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"golang.org/x/net/http2"
"github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-cleanhttp"
"github.com/hashicorp/go-rootcerts" "github.com/hashicorp/go-rootcerts"
"github.com/hashicorp/vault/helper/parseutil"
"github.com/sethgrid/pester" "github.com/sethgrid/pester"
) )
@ -21,10 +25,12 @@ const EnvVaultCACert = "VAULT_CACERT"
const EnvVaultCAPath = "VAULT_CAPATH" const EnvVaultCAPath = "VAULT_CAPATH"
const EnvVaultClientCert = "VAULT_CLIENT_CERT" const EnvVaultClientCert = "VAULT_CLIENT_CERT"
const EnvVaultClientKey = "VAULT_CLIENT_KEY" const EnvVaultClientKey = "VAULT_CLIENT_KEY"
const EnvVaultClientTimeout = "VAULT_CLIENT_TIMEOUT"
const EnvVaultInsecure = "VAULT_SKIP_VERIFY" const EnvVaultInsecure = "VAULT_SKIP_VERIFY"
const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME" const EnvVaultTLSServerName = "VAULT_TLS_SERVER_NAME"
const EnvVaultWrapTTL = "VAULT_WRAP_TTL" const EnvVaultWrapTTL = "VAULT_WRAP_TTL"
const EnvVaultMaxRetries = "VAULT_MAX_RETRIES" const EnvVaultMaxRetries = "VAULT_MAX_RETRIES"
const EnvVaultToken = "VAULT_TOKEN"
// WrappingLookupFunc is a function that, given an HTTP verb and a path, // WrappingLookupFunc is a function that, given an HTTP verb and a path,
// returns an optional string duration to be used for response wrapping (e.g. // returns an optional string duration to be used for response wrapping (e.g.
@ -50,6 +56,9 @@ type Config struct {
// MaxRetries controls the maximum number of times to retry when a 5xx error // MaxRetries controls the maximum number of times to retry when a 5xx error
// occurs. Set to 0 or less to disable retrying. Defaults to 0. // occurs. Set to 0 or less to disable retrying. Defaults to 0.
MaxRetries int MaxRetries int
// Timeout is for setting custom timeout parameter in the HttpClient
Timeout time.Duration
} }
// TLSConfig contains the parameters needed to configure TLS on the HTTP client // TLSConfig contains the parameters needed to configure TLS on the HTTP client
@ -85,7 +94,6 @@ type TLSConfig struct {
func DefaultConfig() *Config { func DefaultConfig() *Config {
config := &Config{ config := &Config{
Address: "https://127.0.0.1:8200", Address: "https://127.0.0.1:8200",
HttpClient: cleanhttp.DefaultClient(), HttpClient: cleanhttp.DefaultClient(),
} }
config.HttpClient.Timeout = time.Second * 60 config.HttpClient.Timeout = time.Second * 60
@ -104,9 +112,8 @@ func DefaultConfig() *Config {
// ConfigureTLS takes a set of TLS configurations and applies those to the the HTTP client. // ConfigureTLS takes a set of TLS configurations and applies those to the the HTTP client.
func (c *Config) ConfigureTLS(t *TLSConfig) error { func (c *Config) ConfigureTLS(t *TLSConfig) error {
if c.HttpClient == nil { if c.HttpClient == nil {
return fmt.Errorf("config HTTP Client must be set") c.HttpClient = DefaultConfig().HttpClient
} }
var clientCert tls.Certificate var clientCert tls.Certificate
@ -154,6 +161,7 @@ func (c *Config) ReadEnvironment() error {
var envCAPath string var envCAPath string
var envClientCert string var envClientCert string
var envClientKey string var envClientKey string
var envClientTimeout time.Duration
var envInsecure bool var envInsecure bool
var envTLSServerName string var envTLSServerName string
var envMaxRetries *uint64 var envMaxRetries *uint64
@ -181,6 +189,13 @@ func (c *Config) ReadEnvironment() error {
if v := os.Getenv(EnvVaultClientKey); v != "" { if v := os.Getenv(EnvVaultClientKey); v != "" {
envClientKey = v envClientKey = v
} }
if t := os.Getenv(EnvVaultClientTimeout); t != "" {
clientTimeout, err := parseutil.ParseDurationSecond(t)
if err != nil {
return fmt.Errorf("Could not parse %s", EnvVaultClientTimeout)
}
envClientTimeout = clientTimeout
}
if v := os.Getenv(EnvVaultInsecure); v != "" { if v := os.Getenv(EnvVaultInsecure); v != "" {
var err error var err error
envInsecure, err = strconv.ParseBool(v) envInsecure, err = strconv.ParseBool(v)
@ -213,6 +228,10 @@ func (c *Config) ReadEnvironment() error {
c.MaxRetries = int(*envMaxRetries) + 1 c.MaxRetries = int(*envMaxRetries) + 1
} }
if envClientTimeout != 0 {
c.Timeout = envClientTimeout
}
return nil return nil
} }
@ -247,6 +266,11 @@ func NewClient(c *Config) (*Client, error) {
c.HttpClient = DefaultConfig().HttpClient c.HttpClient = DefaultConfig().HttpClient
} }
tp := c.HttpClient.Transport.(*http.Transport)
if err := http2.ConfigureTransport(tp); err != nil {
return nil, err
}
redirFunc := func() { redirFunc := func() {
// Ensure redirects are not automatically followed // Ensure redirects are not automatically followed
// Note that this is sane for the API client as it has its own // Note that this is sane for the API client as it has its own
@ -254,9 +278,9 @@ func NewClient(c *Config) (*Client, error) {
// but in e.g. http_test actual redirect handling is necessary // but in e.g. http_test actual redirect handling is necessary
c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { c.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
// Returning this value causes the Go net library to not close the // Returning this value causes the Go net library to not close the
// response body and nil out the error. Otherwise pester tries // response body and to nil out the error. Otherwise pester tries
// three times on every redirect because it sees an error from this // three times on every redirect because it sees an error from this
// function being passed through. // function (to prevent redirects) passing through to it.
return http.ErrUseLastResponse return http.ErrUseLastResponse
} }
} }
@ -268,7 +292,7 @@ func NewClient(c *Config) (*Client, error) {
config: c, config: c,
} }
if token := os.Getenv("VAULT_TOKEN"); token != "" { if token := os.Getenv(EnvVaultToken); token != "" {
client.SetToken(token) client.SetToken(token)
} }
@ -292,6 +316,16 @@ func (c *Client) Address() string {
return c.addr.String() return c.addr.String()
} }
// SetMaxRetries sets the number of retries that will be used in the case of certain errors
func (c *Client) SetMaxRetries(retries int) {
c.config.MaxRetries = retries
}
// SetClientTimeout sets the client request timeout
func (c *Client) SetClientTimeout(timeout time.Duration) {
c.config.Timeout = timeout
}
// SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs // SetWrappingLookupFunc sets a lookup function that returns desired wrap TTLs
// for a given operation and path // for a given operation and path
func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) { func (c *Client) SetWrappingLookupFunc(lookupFunc WrappingLookupFunc) {
@ -315,16 +349,22 @@ func (c *Client) ClearToken() {
c.token = "" c.token = ""
} }
// Clone creates a copy of this client.
func (c *Client) Clone() (*Client, error) {
return NewClient(c.config)
}
// NewRequest creates a new raw request object to query the Vault server // NewRequest creates a new raw request object to query the Vault server
// configured for this client. This is an advanced method and generally // configured for this client. This is an advanced method and generally
// doesn't need to be called externally. // doesn't need to be called externally.
func (c *Client) NewRequest(method, path string) *Request { func (c *Client) NewRequest(method, requestPath string) *Request {
req := &Request{ req := &Request{
Method: method, Method: method,
URL: &url.URL{ URL: &url.URL{
User: c.addr.User,
Scheme: c.addr.Scheme, Scheme: c.addr.Scheme,
Host: c.addr.Host, Host: c.addr.Host,
Path: path, Path: path.Join(c.addr.Path, requestPath),
}, },
ClientToken: c.token, ClientToken: c.token,
Params: make(map[string][]string), Params: make(map[string][]string),
@ -332,18 +372,21 @@ func (c *Client) NewRequest(method, path string) *Request {
var lookupPath string var lookupPath string
switch { switch {
case strings.HasPrefix(path, "/v1/"): case strings.HasPrefix(requestPath, "/v1/"):
lookupPath = strings.TrimPrefix(path, "/v1/") lookupPath = strings.TrimPrefix(requestPath, "/v1/")
case strings.HasPrefix(path, "v1/"): case strings.HasPrefix(requestPath, "v1/"):
lookupPath = strings.TrimPrefix(path, "v1/") lookupPath = strings.TrimPrefix(requestPath, "v1/")
default: default:
lookupPath = path lookupPath = requestPath
} }
if c.wrappingLookupFunc != nil { if c.wrappingLookupFunc != nil {
req.WrapTTL = c.wrappingLookupFunc(method, lookupPath) req.WrapTTL = c.wrappingLookupFunc(method, lookupPath)
} else { } else {
req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath) req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
} }
if c.config.Timeout != 0 {
c.config.HttpClient.Timeout = c.config.Timeout
}
return req return req
} }

302
vendor/github.com/hashicorp/vault/api/renewer.go generated vendored Normal file
View file

@ -0,0 +1,302 @@
package api
import (
"errors"
"math/rand"
"sync"
"time"
)
var (
ErrRenewerMissingInput = errors.New("missing input to renewer")
ErrRenewerMissingSecret = errors.New("missing secret to renew")
ErrRenewerNotRenewable = errors.New("secret is not renewable")
ErrRenewerNoSecretData = errors.New("returned empty secret data")
// DefaultRenewerGrace is the default grace period
DefaultRenewerGrace = 15 * time.Second
// DefaultRenewerRenewBuffer is the default size of the buffer for renew
// messages on the channel.
DefaultRenewerRenewBuffer = 5
)
// Renewer is a process for renewing a secret.
//
// renewer, err := client.NewRenewer(&RenewerInput{
// Secret: mySecret,
// })
// go renewer.Renew()
// defer renewer.Stop()
//
// for {
// select {
// case err := <-renewer.DoneCh():
// if err != nil {
// log.Fatal(err)
// }
//
// // Renewal is now over
// case renewal := <-renewer.RenewCh():
// log.Printf("Successfully renewed: %#v", renewal)
// }
// }
//
//
// The `DoneCh` will return if renewal fails or if the remaining lease duration
// after a renewal is less than or equal to the grace (in number of seconds). In
// both cases, the caller should attempt a re-read of the secret. Clients should
// check the return value of the channel to see if renewal was successful.
type Renewer struct {
l sync.Mutex
client *Client
secret *Secret
grace time.Duration
random *rand.Rand
doneCh chan error
renewCh chan *RenewOutput
stopped bool
stopCh chan struct{}
}
// RenewerInput is used as input to the renew function.
type RenewerInput struct {
// Secret is the secret to renew
Secret *Secret
// Grace is a minimum renewal before returning so the upstream client
// can do a re-read. This can be used to prevent clients from waiting
// too long to read a new credential and incur downtime.
Grace time.Duration
// Rand is the randomizer to use for underlying randomization. If not
// provided, one will be generated and seeded automatically. If provided, it
// is assumed to have already been seeded.
Rand *rand.Rand
// RenewBuffer is the size of the buffered channel where renew messages are
// dispatched.
RenewBuffer int
}
// RenewOutput is the metadata returned to the client (if it's listening) to
// renew messages.
type RenewOutput struct {
// RenewedAt is the timestamp when the renewal took place (UTC).
RenewedAt time.Time
// Secret is the underlying renewal data. It's the same struct as all data
// that is returned from Vault, but since this is renewal data, it will not
// usually include the secret itself.
Secret *Secret
}
// NewRenewer creates a new renewer from the given input.
func (c *Client) NewRenewer(i *RenewerInput) (*Renewer, error) {
if i == nil {
return nil, ErrRenewerMissingInput
}
secret := i.Secret
if secret == nil {
return nil, ErrRenewerMissingSecret
}
grace := i.Grace
if grace == 0 {
grace = DefaultRenewerGrace
}
random := i.Rand
if random == nil {
random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
}
renewBuffer := i.RenewBuffer
if renewBuffer == 0 {
renewBuffer = DefaultRenewerRenewBuffer
}
return &Renewer{
client: c,
secret: secret,
grace: grace,
random: random,
doneCh: make(chan error, 1),
renewCh: make(chan *RenewOutput, renewBuffer),
stopped: false,
stopCh: make(chan struct{}),
}, nil
}
// DoneCh returns the channel where the renewer will publish when renewal stops.
// If there is an error, this will be an error.
func (r *Renewer) DoneCh() <-chan error {
return r.doneCh
}
// RenewCh is a channel that receives a message when a successful renewal takes
// place and includes metadata about the renewal.
func (r *Renewer) RenewCh() <-chan *RenewOutput {
return r.renewCh
}
// Stop stops the renewer.
func (r *Renewer) Stop() {
r.l.Lock()
if !r.stopped {
close(r.stopCh)
r.stopped = true
}
r.l.Unlock()
}
// Renew starts a background process for renewing this secret. When the secret
// is has auth data, this attempts to renew the auth (token). When the secret
// has a lease, this attempts to renew the lease.
func (r *Renewer) Renew() {
var result error
if r.secret.Auth != nil {
result = r.renewAuth()
} else {
result = r.renewLease()
}
select {
case r.doneCh <- result:
case <-r.stopCh:
}
}
// renewAuth is a helper for renewing authentication.
func (r *Renewer) renewAuth() error {
if !r.secret.Auth.Renewable || r.secret.Auth.ClientToken == "" {
return ErrRenewerNotRenewable
}
client, token := r.client, r.secret.Auth.ClientToken
for {
// Check if we are stopped.
select {
case <-r.stopCh:
return nil
default:
}
// Renew the auth.
renewal, err := client.Auth().Token().RenewTokenAsSelf(token, 0)
if err != nil {
return err
}
// Push a message that a renewal took place.
select {
case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
default:
}
// Somehow, sometimes, this happens.
if renewal == nil || renewal.Auth == nil {
return ErrRenewerNoSecretData
}
// Do nothing if we are not renewable
if !renewal.Auth.Renewable {
return ErrRenewerNotRenewable
}
// Grab the lease duration and sleep duration - note that we grab the auth
// lease duration, not the secret lease duration.
leaseDuration := time.Duration(renewal.Auth.LeaseDuration) * time.Second
sleepDuration := r.sleepDuration(leaseDuration)
// If we are within grace, return now.
if leaseDuration <= r.grace || sleepDuration <= r.grace {
return nil
}
select {
case <-r.stopCh:
return nil
case <-time.After(sleepDuration):
continue
}
}
}
// renewLease is a helper for renewing a lease.
func (r *Renewer) renewLease() error {
if !r.secret.Renewable || r.secret.LeaseID == "" {
return ErrRenewerNotRenewable
}
client, leaseID := r.client, r.secret.LeaseID
for {
// Check if we are stopped.
select {
case <-r.stopCh:
return nil
default:
}
// Renew the lease.
renewal, err := client.Sys().Renew(leaseID, 0)
if err != nil {
return err
}
// Push a message that a renewal took place.
select {
case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
default:
}
// Somehow, sometimes, this happens.
if renewal == nil {
return ErrRenewerNoSecretData
}
// Do nothing if we are not renewable
if !renewal.Renewable {
return ErrRenewerNotRenewable
}
// Grab the lease duration and sleep duration
leaseDuration := time.Duration(renewal.LeaseDuration) * time.Second
sleepDuration := r.sleepDuration(leaseDuration)
// If we are within grace, return now.
if leaseDuration <= r.grace || sleepDuration <= r.grace {
return nil
}
select {
case <-r.stopCh:
return nil
case <-time.After(sleepDuration):
continue
}
}
}
// sleepDuration calculates the time to sleep given the base lease duration. The
// base is the resulting lease duration. It will be reduced to 1/3 and
// multiplied by a random float between 0.0 and 1.0. This extra randomness
// prevents multiple clients from all trying to renew simultaneously.
func (r *Renewer) sleepDuration(base time.Duration) time.Duration {
sleep := float64(base)
// Renew at 1/3 the remaining lease. This will give us an opportunity to retry
// at least one more time should the first renewal fail.
sleep = sleep / 3.0
// Use a randomness so many clients do not hit Vault simultaneously.
sleep = sleep * (r.random.Float64() + 1) / 2.0
return time.Duration(sleep)
}

View file

@ -14,6 +14,7 @@ type Request struct {
Method string Method string
URL *url.URL URL *url.URL
Params url.Values Params url.Values
Headers http.Header
ClientToken string ClientToken string
WrapTTL string WrapTTL string
Obj interface{} Obj interface{}
@ -55,10 +56,19 @@ func (r *Request) ToHTTP() (*http.Request, error) {
return nil, err return nil, err
} }
req.URL.User = r.URL.User
req.URL.Scheme = r.URL.Scheme req.URL.Scheme = r.URL.Scheme
req.URL.Host = r.URL.Host req.URL.Host = r.URL.Host
req.Host = r.URL.Host req.Host = r.URL.Host
if r.Headers != nil {
for header, vals := range r.Headers {
for _, val := range vals {
req.Header.Add(header, val)
}
}
}
if len(r.ClientToken) != 0 { if len(r.ClientToken) != 0 {
req.Header.Set("X-Vault-Token", r.ClientToken) req.Header.Set("X-Vault-Token", r.ClientToken)
} }

View file

@ -25,8 +25,9 @@ func (r *Response) DecodeJSON(out interface{}) error {
// this will fully consume the response body, but will not close it. The // this will fully consume the response body, but will not close it. The
// body must still be closed manually. // body must still be closed manually.
func (r *Response) Error() error { func (r *Response) Error() error {
// 200 to 399 are okay status codes // 200 to 399 are okay status codes. 429 is the code for health status of
if r.StatusCode >= 200 && r.StatusCode < 400 { // standby nodes.
if (r.StatusCode >= 200 && r.StatusCode < 400) || r.StatusCode == 429 {
return nil return nil
} }

View file

@ -3,6 +3,7 @@ package api
import ( import (
"fmt" "fmt"
"github.com/fatih/structs"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
) )
@ -71,14 +72,19 @@ func (c *Sys) ListAudit() (map[string]*Audit, error) {
return mounts, nil return mounts, nil
} }
// DEPRECATED: Use EnableAuditWithOptions instead
func (c *Sys) EnableAudit( func (c *Sys) EnableAudit(
path string, auditType string, desc string, opts map[string]string) error { path string, auditType string, desc string, opts map[string]string) error {
body := map[string]interface{}{ return c.EnableAuditWithOptions(path, &EnableAuditOptions{
"type": auditType, Type: auditType,
"description": desc, Description: desc,
"options": opts, Options: opts,
})
} }
func (c *Sys) EnableAuditWithOptions(path string, options *EnableAuditOptions) error {
body := structs.Map(options)
r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/audit/%s", path)) r := c.c.NewRequest("PUT", fmt.Sprintf("/v1/sys/audit/%s", path))
if err := r.SetJSONBody(body); err != nil { if err := r.SetJSONBody(body); err != nil {
return err return err
@ -106,9 +112,17 @@ func (c *Sys) DisableAudit(path string) error {
// individually documented because the map almost directly to the raw HTTP API // individually documented because the map almost directly to the raw HTTP API
// documentation. Please refer to that documentation for more details. // documentation. Please refer to that documentation for more details.
type EnableAuditOptions struct {
Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"`
Options map[string]string `json:"options" structs:"options"`
Local bool `json:"local" structs:"local"`
}
type Audit struct { type Audit struct {
Path string Path string
Type string Type string
Description string Description string
Options map[string]string Options map[string]string
Local bool
} }

View file

@ -3,6 +3,7 @@ package api
import ( import (
"fmt" "fmt"
"github.com/fatih/structs"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
) )
@ -42,12 +43,17 @@ func (c *Sys) ListAuth() (map[string]*AuthMount, error) {
return mounts, nil return mounts, nil
} }
// DEPRECATED: Use EnableAuthWithOptions instead
func (c *Sys) EnableAuth(path, authType, desc string) error { func (c *Sys) EnableAuth(path, authType, desc string) error {
body := map[string]string{ return c.EnableAuthWithOptions(path, &EnableAuthOptions{
"type": authType, Type: authType,
"description": desc, Description: desc,
})
} }
func (c *Sys) EnableAuthWithOptions(path string, options *EnableAuthOptions) error {
body := structs.Map(options)
r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/auth/%s", path)) r := c.c.NewRequest("POST", fmt.Sprintf("/v1/sys/auth/%s", path))
if err := r.SetJSONBody(body); err != nil { if err := r.SetJSONBody(body); err != nil {
return err return err
@ -75,13 +81,23 @@ func (c *Sys) DisableAuth(path string) error {
// individually documentd because the map almost directly to the raw HTTP API // individually documentd because the map almost directly to the raw HTTP API
// documentation. Please refer to that documentation for more details. // documentation. Please refer to that documentation for more details.
type EnableAuthOptions struct {
Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"`
Local bool `json:"local" structs:"local"`
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
}
type AuthMount struct { type AuthMount struct {
Type string `json:"type" structs:"type" mapstructure:"type"` Type string `json:"type" structs:"type" mapstructure:"type"`
Description string `json:"description" structs:"description" mapstructure:"description"` Description string `json:"description" structs:"description" mapstructure:"description"`
Accessor string `json:"accessor" structs:"accessor" mapstructure:"accessor"`
Config AuthConfigOutput `json:"config" structs:"config" mapstructure:"config"` Config AuthConfigOutput `json:"config" structs:"config" mapstructure:"config"`
Local bool `json:"local" structs:"local" mapstructure:"local"`
} }
type AuthConfigOutput struct { type AuthConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
} }

View file

@ -0,0 +1,56 @@
package api
func (c *Sys) CORSStatus() (*CORSResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/config/cors")
resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result CORSResponse
err = resp.DecodeJSON(&result)
return &result, err
}
func (c *Sys) ConfigureCORS(req *CORSRequest) (*CORSResponse, error) {
r := c.c.NewRequest("PUT", "/v1/sys/config/cors")
if err := r.SetJSONBody(req); err != nil {
return nil, err
}
resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result CORSResponse
err = resp.DecodeJSON(&result)
return &result, err
}
func (c *Sys) DisableCORS() (*CORSResponse, error) {
r := c.c.NewRequest("DELETE", "/v1/sys/config/cors")
resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result CORSResponse
err = resp.DecodeJSON(&result)
return &result, err
}
type CORSRequest struct {
AllowedOrigins string `json:"allowed_origins"`
Enabled bool `json:"enabled"`
}
type CORSResponse struct {
AllowedOrigins string `json:"allowed_origins"`
Enabled bool `json:"enabled"`
}

24
vendor/github.com/hashicorp/vault/api/sys_health.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
package api
func (c *Sys) Health() (*HealthResponse, error) {
r := c.c.NewRequest("GET", "/v1/sys/health")
resp, err := c.c.RawRequest(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result HealthResponse
err = resp.DecodeJSON(&result)
return &result, err
}
type HealthResponse struct {
Initialized bool `json:"initialized"`
Sealed bool `json:"sealed"`
Standby bool `json:"standby"`
ServerTimeUTC int64 `json:"server_time_utc"`
Version string `json:"version"`
ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"`
}

View file

@ -17,4 +17,5 @@ type LeaderResponse struct {
HAEnabled bool `json:"ha_enabled"` HAEnabled bool `json:"ha_enabled"`
IsSelf bool `json:"is_self"` IsSelf bool `json:"is_self"`
LeaderAddress string `json:"leader_address"` LeaderAddress string `json:"leader_address"`
LeaderClusterAddress string `json:"leader_cluster_address"`
} }

View file

@ -1,7 +1,7 @@
package api package api
func (c *Sys) Renew(id string, increment int) (*Secret, error) { func (c *Sys) Renew(id string, increment int) (*Secret, error) {
r := c.c.NewRequest("PUT", "/v1/sys/renew") r := c.c.NewRequest("PUT", "/v1/sys/leases/renew")
body := map[string]interface{}{ body := map[string]interface{}{
"increment": increment, "increment": increment,
@ -21,7 +21,7 @@ func (c *Sys) Renew(id string, increment int) (*Secret, error) {
} }
func (c *Sys) Revoke(id string) error { func (c *Sys) Revoke(id string) error {
r := c.c.NewRequest("PUT", "/v1/sys/revoke/"+id) r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke/"+id)
resp, err := c.c.RawRequest(r) resp, err := c.c.RawRequest(r)
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
@ -30,7 +30,7 @@ func (c *Sys) Revoke(id string) error {
} }
func (c *Sys) RevokePrefix(id string) error { func (c *Sys) RevokePrefix(id string) error {
r := c.c.NewRequest("PUT", "/v1/sys/revoke-prefix/"+id) r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-prefix/"+id)
resp, err := c.c.RawRequest(r) resp, err := c.c.RawRequest(r)
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
@ -39,7 +39,7 @@ func (c *Sys) RevokePrefix(id string) error {
} }
func (c *Sys) RevokeForce(id string) error { func (c *Sys) RevokeForce(id string) error {
r := c.c.NewRequest("PUT", "/v1/sys/revoke-force/"+id) r := c.c.NewRequest("PUT", "/v1/sys/leases/revoke-force/"+id)
resp, err := c.c.RawRequest(r) resp, err := c.c.RawRequest(r)
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()

View file

@ -123,20 +123,27 @@ type MountInput struct {
Type string `json:"type" structs:"type"` Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"` Description string `json:"description" structs:"description"`
Config MountConfigInput `json:"config" structs:"config"` Config MountConfigInput `json:"config" structs:"config"`
Local bool `json:"local" structs:"local"`
} }
type MountConfigInput struct { type MountConfigInput struct {
DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
} }
type MountOutput struct { type MountOutput struct {
Type string `json:"type" structs:"type"` Type string `json:"type" structs:"type"`
Description string `json:"description" structs:"description"` Description string `json:"description" structs:"description"`
Accessor string `json:"accessor" structs:"accessor"`
Config MountConfigOutput `json:"config" structs:"config"` Config MountConfigOutput `json:"config" structs:"config"`
Local bool `json:"local" structs:"local"`
} }
type MountConfigOutput struct { type MountConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` DefaultLeaseTTL int `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` MaxLeaseTTL int `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"`
ForceNoCache bool `json:"force_no_cache" structs:"force_no_cache" mapstructure:"force_no_cache"`
PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"`
} }

View file

@ -53,6 +53,7 @@ type SealStatusResponse struct {
T int `json:"t"` T int `json:"t"`
N int `json:"n"` N int `json:"n"`
Progress int `json:"progress"` Progress int `json:"progress"`
Nonce string `json:"nonce"`
Version string `json:"version"` Version string `json:"version"`
ClusterName string `json:"cluster_name,omitempty"` ClusterName string `json:"cluster_name,omitempty"`
ClusterID string `json:"cluster_id,omitempty"` ClusterID string `json:"cluster_id,omitempty"`

View file

@ -2,13 +2,19 @@ package jsonutil
import ( import (
"bytes" "bytes"
"compress/gzip"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"github.com/hashicorp/vault/helper/compressutil"
) )
// Encodes/Marshals the given object into JSON // Encodes/Marshals the given object into JSON
func EncodeJSON(in interface{}) ([]byte, error) { func EncodeJSON(in interface{}) ([]byte, error) {
if in == nil {
return nil, fmt.Errorf("input for encoding is nil")
}
var buf bytes.Buffer var buf bytes.Buffer
enc := json.NewEncoder(&buf) enc := json.NewEncoder(&buf)
if err := enc.Encode(in); err != nil { if err := enc.Encode(in); err != nil {
@ -17,15 +23,60 @@ func EncodeJSON(in interface{}) ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
// Decodes/Unmarshals the given JSON into a desired object // EncodeJSONAndCompress encodes the given input into JSON and compresses the
// encoded value (using Gzip format BestCompression level, by default). A
// canary byte is placed at the beginning of the returned bytes for the logic
// in decompression method to identify compressed input.
func EncodeJSONAndCompress(in interface{}, config *compressutil.CompressionConfig) ([]byte, error) {
if in == nil {
return nil, fmt.Errorf("input for encoding is nil")
}
// First JSON encode the given input
encodedBytes, err := EncodeJSON(in)
if err != nil {
return nil, err
}
if config == nil {
config = &compressutil.CompressionConfig{
Type: compressutil.CompressionTypeGzip,
GzipCompressionLevel: gzip.BestCompression,
}
}
return compressutil.Compress(encodedBytes, config)
}
// DecodeJSON tries to decompress the given data. The call to decompress, fails
// if the content was not compressed in the first place, which is identified by
// a canary byte before the compressed data. If the data is not compressed, it
// is JSON decoded directly. Otherwise the decompressed data will be JSON
// decoded.
func DecodeJSON(data []byte, out interface{}) error { func DecodeJSON(data []byte, out interface{}) error {
if data == nil { if data == nil || len(data) == 0 {
return fmt.Errorf("'data' being decoded is nil") return fmt.Errorf("'data' being decoded is nil")
} }
if out == nil { if out == nil {
return fmt.Errorf("output parameter 'out' is nil") return fmt.Errorf("output parameter 'out' is nil")
} }
// Decompress the data if it was compressed in the first place
decompressedBytes, uncompressed, err := compressutil.Decompress(data)
if err != nil {
return fmt.Errorf("failed to decompress JSON: err: %v", err)
}
if !uncompressed && (decompressedBytes == nil || len(decompressedBytes) == 0) {
return fmt.Errorf("decompressed data being decoded is invalid")
}
// If the input supplied failed to contain the compression canary, it
// will be notified by the compression utility. Decode the decompressed
// input.
if !uncompressed {
data = decompressedBytes
}
return DecodeJSONFromReader(bytes.NewReader(data), out) return DecodeJSONFromReader(bytes.NewReader(data), out)
} }

12
vendor/vendor.json vendored
View file

@ -911,16 +911,16 @@
"revisionTime": "2016-08-21T23:40:57Z" "revisionTime": "2016-08-21T23:40:57Z"
}, },
{ {
"checksumSHA1": "31yBeS6U3xm7VJ7ZvDxRgBxXP0A=", "checksumSHA1": "hLIXn9iQhPcjY+/G64j3mIlLlK8=",
"path": "github.com/hashicorp/vault/api", "path": "github.com/hashicorp/vault/api",
"revision": "f4adc7fa960ed8e828f94bc6785bcdbae8d1b263", "revision": "0c3e14f047aede0a70256e1e8b321610910b246e",
"revisionTime": "2016-12-16T21:07:16Z" "revisionTime": "2017-08-01T15:50:41Z"
}, },
{ {
"checksumSHA1": "5lR6EdY0ARRdKAq3hZcL38STD8Q=", "checksumSHA1": "yUiSTPf0QUuL2r/81sjuytqBoeQ=",
"path": "github.com/hashicorp/vault/helper/jsonutil", "path": "github.com/hashicorp/vault/helper/jsonutil",
"revision": "bcf98fa8d61d1870c3af689f1b090b29a9c12d8c", "revision": "0c3e14f047aede0a70256e1e8b321610910b246e",
"revisionTime": "2016-08-02T20:35:37Z" "revisionTime": "2017-08-01T15:50:41Z"
}, },
{ {
"checksumSHA1": "VMaF3Q7RIrRzvbnPbqxuSLryOvc=", "checksumSHA1": "VMaF3Q7RIrRzvbnPbqxuSLryOvc=",