diff --git a/api/secret.go b/api/secret.go index 49d0112c34..bc52f78b72 100644 --- a/api/secret.go +++ b/api/secret.go @@ -15,6 +15,11 @@ type Secret struct { // is arbitrary and up to the secret backend. Data map[string]interface{} `json:"data"` + // 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"` + // Auth, if non-nil, means that there was authentication information // attached to this response. Auth *SecretAuth `json:"auth,omitempty"` diff --git a/command/format.go b/command/format.go index d0bd7a142a..3df863a716 100644 --- a/command/format.go +++ b/command/format.go @@ -43,6 +43,7 @@ func outputFormatTable(ui cli.Ui, s *api.Secret, whitespace bool) int { config.Prefix = "" input := make([]string, 0, 5) + input = append(input, fmt.Sprintf("Key %s Value", config.Delim)) if s.LeaseDuration > 0 { @@ -71,6 +72,14 @@ func outputFormatTable(ui cli.Ui, s *api.Secret, whitespace bool) int { input = append(input, fmt.Sprintf("%s %s %v", k, config.Delim, v)) } + if len(s.Warnings) != 0 { + input = append(input, "") + input = append(input, "The following warnings were generated:") + for _, warning := range s.Warnings { + input = append(input, fmt.Sprintf("* %s", warning)) + } + } + ui.Output(columnize.Format(input, config)) return 0 } diff --git a/http/logical.go b/http/logical.go index f5fa2326c4..ce03b384f4 100644 --- a/http/logical.go +++ b/http/logical.go @@ -96,7 +96,10 @@ func respondLogical(w http.ResponseWriter, r *http.Request, path string, dataOnl return } - logicalResp := &LogicalResponse{Data: resp.Data} + logicalResp := &LogicalResponse{ + Data: resp.Data, + Warnings: resp.GetWarnings(), + } if resp.Secret != nil { logicalResp.LeaseID = resp.Secret.LeaseID logicalResp.Renewable = resp.Secret.Renewable @@ -196,6 +199,7 @@ type LogicalResponse struct { Renewable bool `json:"renewable"` LeaseDuration int `json:"lease_duration"` Data map[string]interface{} `json:"data"` + Warnings []string `json:"warnings"` Auth *Auth `json:"auth"` } diff --git a/logical/response.go b/logical/response.go index 8af0b3de51..79619aa7d4 100644 --- a/logical/response.go +++ b/logical/response.go @@ -40,6 +40,28 @@ type Response struct { // 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 +} + +func (r *Response) AddWarning(warning string) { + if r.warnings == nil { + r.warnings = make([]string, 0, 1) + } + r.warnings = append(r.warnings, warning) +} + +func (r *Response) GetWarnings() []string { + return r.warnings +} + +func (r *Response) ClearWarnings() { + r.warnings = make([]string, 0, 1) } // IsError returns true if this response seems to indicate an error. diff --git a/vault/token_store.go b/vault/token_store.go index cdc41a1423..f72f9d9d93 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -46,6 +46,8 @@ type TokenStore struct { expiration *ExpirationManager cubbyholeBackend *CubbyholeBackend + + policyLookupFunc func() ([]string, error) } // NewTokenStore is used to construct a token store that is @@ -59,6 +61,10 @@ func NewTokenStore(c *Core, config *logical.BackendConfig) (*TokenStore, error) view: view, } + if c.policy != nil { + t.policyLookupFunc = c.policy.ListPolicies + } + // Setup the salt salt, err := salt.NewSalt(view, &salt.Config{ HashFunc: salt.SHA1Hash, @@ -636,6 +642,23 @@ func (ts *TokenStore) handleCreate( }, } + if ts.policyLookupFunc != nil { + availPolicies, err := ts.policyLookupFunc() + if err == nil { + policies := map[string]bool{} + if availPolicies != nil && len(availPolicies) > 0 { + for _, p := range availPolicies { + policies[p] = true + } + } + for _, p := range te.Policies { + if !policies[p] { + resp.AddWarning(fmt.Sprintf("policy \"%s\" does not exist", p)) + } + } + } + } + return resp, nil }