diff --git a/api/auth/approle/approle.go b/api/auth/approle/approle.go index 61c380cd4fa6..b8cf01228441 100644 --- a/api/auth/approle/approle.go +++ b/api/auth/approle/approle.go @@ -100,6 +100,10 @@ func NewAppRoleAuth(roleID string, secretID *SecretID, opts ...LoginOption) (*Ap } func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := map[string]interface{}{ "role_id": a.roleID, } @@ -125,7 +129,7 @@ func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secre // if the caller indicated that the value was actually a wrapping token, unwrap it first if a.unwrap { - unwrappedToken, err := client.Logical().Unwrap(secretIDValue) + unwrappedToken, err := client.Logical().UnwrapWithContext(ctx, secretIDValue) if err != nil { return nil, fmt.Errorf("unable to unwrap response wrapping token: %w", err) } @@ -135,7 +139,7 @@ func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secre } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with app role auth: %w", err) } diff --git a/api/auth/aws/aws.go b/api/auth/aws/aws.go index 9e229b871101..cef19beb8692 100644 --- a/api/auth/aws/aws.go +++ b/api/auth/aws/aws.go @@ -84,6 +84,10 @@ func NewAWSAuth(opts ...LoginOption) (*AWSAuth, error) { // variables. To specify a path to a credentials file on disk instead, set // the environment variable AWS_SHARED_CREDENTIALS_FILE. func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := make(map[string]interface{}) switch a.authType { case ec2Type: @@ -182,7 +186,7 @@ func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with AWS auth: %w", err) } diff --git a/api/auth/azure/azure.go b/api/auth/azure/azure.go index 825003889a74..370ec573dd1b 100644 --- a/api/auth/azure/azure.go +++ b/api/auth/azure/azure.go @@ -90,6 +90,10 @@ func NewAzureAuth(roleName string, opts ...LoginOption) (*AzureAuth, error) { // Login sets up the required request body for the Azure auth method's /login // endpoint, and performs a write to it. func (a *AzureAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + jwtResp, err := a.getJWT() if err != nil { return nil, fmt.Errorf("unable to get access token: %w", err) @@ -110,7 +114,7 @@ func (a *AzureAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with Azure auth: %w", err) } diff --git a/api/auth/gcp/gcp.go b/api/auth/gcp/gcp.go index efa1d0b406c3..a5dd93646128 100644 --- a/api/auth/gcp/gcp.go +++ b/api/auth/gcp/gcp.go @@ -67,6 +67,10 @@ func NewGCPAuth(roleName string, opts ...LoginOption) (*GCPAuth, error) { // endpoint, and performs a write to it. This method defaults to the "gce" // auth type unless NewGCPAuth is called with WithIAMAuth(). func (a *GCPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := map[string]interface{}{ "role": a.roleName, } @@ -86,7 +90,7 @@ func (a *GCPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with GCP auth: %w", err) } diff --git a/api/auth/kubernetes/kubernetes.go b/api/auth/kubernetes/kubernetes.go index 99541708f84b..c2fef86a5fd0 100644 --- a/api/auth/kubernetes/kubernetes.go +++ b/api/auth/kubernetes/kubernetes.go @@ -68,13 +68,17 @@ func NewKubernetesAuth(roleName string, opts ...LoginOption) (*KubernetesAuth, e } func (a *KubernetesAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := map[string]interface{}{ "jwt": a.serviceAccountToken, "role": a.roleName, } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with Kubernetes auth: %w", err) } diff --git a/api/auth/ldap/ldap.go b/api/auth/ldap/ldap.go index 0653484d3e9d..9f37abc664f7 100644 --- a/api/auth/ldap/ldap.go +++ b/api/auth/ldap/ldap.go @@ -84,6 +84,10 @@ func NewLDAPAuth(username string, password *Password, opts ...LoginOption) (*LDA } func (a *LDAPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := make(map[string]interface{}) if a.passwordFile != "" { @@ -103,7 +107,7 @@ func (a *LDAPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, } path := fmt.Sprintf("auth/%s/login/%s", a.mountPath, a.username) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with LDAP auth: %w", err) } diff --git a/api/auth/userpass/userpass.go b/api/auth/userpass/userpass.go index d33e787f9cc4..124cd7a68f8e 100644 --- a/api/auth/userpass/userpass.go +++ b/api/auth/userpass/userpass.go @@ -88,6 +88,10 @@ func NewUserpassAuth(username string, password *Password, opts ...LoginOption) ( } func (a *UserpassAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := make(map[string]interface{}) if a.passwordFile != "" { @@ -107,7 +111,7 @@ func (a *UserpassAuth) Login(ctx context.Context, client *api.Client) (*api.Secr } path := fmt.Sprintf("auth/%s/login/%s", a.mountPath, a.username) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with userpass auth: %w", err) } diff --git a/api/client.go b/api/client.go index 99813a21b19c..c9cea7ca662d 100644 --- a/api/client.go +++ b/api/client.go @@ -36,6 +36,7 @@ const ( EnvVaultAddress = "VAULT_ADDR" EnvVaultAgentAddr = "VAULT_AGENT_ADDR" EnvVaultCACert = "VAULT_CACERT" + EnvVaultCACertBytes = "VAULT_CACERT_BYTES" EnvVaultCAPath = "VAULT_CAPATH" EnvVaultClientCert = "VAULT_CLIENT_CERT" EnvVaultClientKey = "VAULT_CLIENT_KEY" @@ -172,9 +173,14 @@ type Config struct { // used to communicate with Vault. type TLSConfig struct { // CACert is the path to a PEM-encoded CA cert file to use to verify the - // Vault server SSL certificate. + // Vault server SSL certificate. It takes precedence over CACertBytes + // and CAPath. CACert string + // CACertBytes is a PEM-encoded certificate or bundle. It takes precedence + // over CAPath. + CACertBytes []byte + // CAPath is the path to a directory of PEM-encoded CA cert files to verify // the Vault server SSL certificate. CAPath string @@ -266,12 +272,13 @@ func (c *Config) configureTLS(t *TLSConfig) error { return fmt.Errorf("both client cert and client key must be provided") } - if t.CACert != "" || t.CAPath != "" { + if t.CACert != "" || len(t.CACertBytes) != 0 || t.CAPath != "" { c.curlCACert = t.CACert c.curlCAPath = t.CAPath rootConfig := &rootcerts.Config{ - CAFile: t.CACert, - CAPath: t.CAPath, + CAFile: t.CACert, + CACertificate: t.CACertBytes, + CAPath: t.CAPath, } if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil { return err @@ -313,6 +320,7 @@ func (c *Config) ReadEnvironment() error { var envAddress string var envAgentAddress string var envCACert string + var envCACertBytes []byte var envCAPath string var envClientCert string var envClientKey string @@ -343,6 +351,9 @@ func (c *Config) ReadEnvironment() error { if v := os.Getenv(EnvVaultCACert); v != "" { envCACert = v } + if v := os.Getenv(EnvVaultCACertBytes); v != "" { + envCACertBytes = []byte(v) + } if v := os.Getenv(EnvVaultCAPath); v != "" { envCAPath = v } @@ -398,6 +409,7 @@ func (c *Config) ReadEnvironment() error { // Configure the HTTP clients TLS configuration. t := &TLSConfig{ CACert: envCACert, + CACertBytes: envCACertBytes, CAPath: envCAPath, ClientCert: envClientCert, ClientKey: envClientKey, @@ -576,7 +588,6 @@ func (c *Client) CloneConfig() *Config { newConfig.CheckRetry = c.config.CheckRetry newConfig.Logger = c.config.Logger newConfig.Limiter = c.config.Limiter - newConfig.OutputCurlString = c.config.OutputCurlString newConfig.SRVLookup = c.config.SRVLookup newConfig.CloneHeaders = c.config.CloneHeaders newConfig.CloneToken = c.config.CloneToken @@ -990,22 +1001,21 @@ func (c *Client) clone(cloneHeaders bool) (*Client, error) { defer config.modifyLock.RUnlock() newConfig := &Config{ - Address: config.Address, - HttpClient: config.HttpClient, - MinRetryWait: config.MinRetryWait, - MaxRetryWait: config.MaxRetryWait, - MaxRetries: config.MaxRetries, - Timeout: config.Timeout, - Backoff: config.Backoff, - CheckRetry: config.CheckRetry, - Logger: config.Logger, - Limiter: config.Limiter, - OutputCurlString: config.OutputCurlString, - AgentAddress: config.AgentAddress, - SRVLookup: config.SRVLookup, - CloneHeaders: config.CloneHeaders, - CloneToken: config.CloneToken, - ReadYourWrites: config.ReadYourWrites, + Address: config.Address, + HttpClient: config.HttpClient, + MinRetryWait: config.MinRetryWait, + MaxRetryWait: config.MaxRetryWait, + MaxRetries: config.MaxRetries, + Timeout: config.Timeout, + Backoff: config.Backoff, + CheckRetry: config.CheckRetry, + Logger: config.Logger, + Limiter: config.Limiter, + AgentAddress: config.AgentAddress, + SRVLookup: config.SRVLookup, + CloneHeaders: config.CloneHeaders, + CloneToken: config.CloneToken, + ReadYourWrites: config.ReadYourWrites, } client, err := NewClient(newConfig) if err != nil { diff --git a/api/client_test.go b/api/client_test.go index a8434b28c5bb..73a306453645 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -262,24 +262,37 @@ func TestDefaulRetryPolicy(t *testing.T) { func TestClientEnvSettings(t *testing.T) { cwd, _ := os.Getwd() + + caCertBytes, err := os.ReadFile(cwd + "/test-fixtures/keys/cert.pem") + if err != nil { + t.Fatalf("error reading %q cert file: %v", cwd+"/test-fixtures/keys/cert.pem", err) + } + oldCACert := os.Getenv(EnvVaultCACert) + oldCACertBytes := os.Getenv(EnvVaultCACertBytes) oldCAPath := os.Getenv(EnvVaultCAPath) oldClientCert := os.Getenv(EnvVaultClientCert) oldClientKey := os.Getenv(EnvVaultClientKey) oldSkipVerify := os.Getenv(EnvVaultSkipVerify) oldMaxRetries := os.Getenv(EnvVaultMaxRetries) + os.Setenv(EnvVaultCACert, cwd+"/test-fixtures/keys/cert.pem") + os.Setenv(EnvVaultCACertBytes, string(caCertBytes)) os.Setenv(EnvVaultCAPath, cwd+"/test-fixtures/keys") os.Setenv(EnvVaultClientCert, cwd+"/test-fixtures/keys/cert.pem") os.Setenv(EnvVaultClientKey, cwd+"/test-fixtures/keys/key.pem") os.Setenv(EnvVaultSkipVerify, "true") os.Setenv(EnvVaultMaxRetries, "5") - defer os.Setenv(EnvVaultCACert, oldCACert) - defer os.Setenv(EnvVaultCAPath, oldCAPath) - defer os.Setenv(EnvVaultClientCert, oldClientCert) - defer os.Setenv(EnvVaultClientKey, oldClientKey) - defer os.Setenv(EnvVaultSkipVerify, oldSkipVerify) - defer os.Setenv(EnvVaultMaxRetries, oldMaxRetries) + + defer func() { + os.Setenv(EnvVaultCACert, oldCACert) + os.Setenv(EnvVaultCACertBytes, oldCACertBytes) + os.Setenv(EnvVaultCAPath, oldCAPath) + os.Setenv(EnvVaultClientCert, oldClientCert) + os.Setenv(EnvVaultClientKey, oldClientKey) + os.Setenv(EnvVaultSkipVerify, oldSkipVerify) + os.Setenv(EnvVaultMaxRetries, oldMaxRetries) + }() config := DefaultConfig() if err := config.ReadEnvironment(); err != nil { @@ -513,8 +526,8 @@ func TestClone(t *testing.T) { if parent.MaxRetries() != clone.MaxRetries() { t.Fatalf("maxRetries don't match: %v vs %v", parent.MaxRetries(), clone.MaxRetries()) } - if parent.OutputCurlString() != clone.OutputCurlString() { - t.Fatalf("outputCurlString doesn't match: %v vs %v", parent.OutputCurlString(), clone.OutputCurlString()) + if parent.OutputCurlString() == clone.OutputCurlString() { + t.Fatalf("outputCurlString was copied over when it shouldn't have been: %v and %v", parent.OutputCurlString(), clone.OutputCurlString()) } if parent.SRVLookup() != clone.SRVLookup() { t.Fatalf("SRVLookup doesn't match: %v vs %v", parent.SRVLookup(), clone.SRVLookup()) diff --git a/api/lifetime_watcher.go b/api/lifetime_watcher.go index f775dfb15a7d..f06263526f35 100644 --- a/api/lifetime_watcher.go +++ b/api/lifetime_watcher.go @@ -113,7 +113,9 @@ type LifetimeWatcherInput struct { // The new TTL, in seconds, that should be set on the lease. The TTL set // here may or may not be honored by the vault server, based on Vault - // configuration or any associated max TTL values. + // configuration or any associated max TTL values. If specified, the + // minimum of this value and the remaining lease duration will be used + // for grace period calculations. Increment int // RenewBehavior controls what happens when a renewal errors or the @@ -257,7 +259,7 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, initialTime := time.Now() priorDuration := time.Duration(initLeaseDuration) * time.Second - r.calculateGrace(priorDuration) + r.calculateGrace(priorDuration, time.Duration(r.increment)*time.Second) var errorBackoff backoff.BackOff for { @@ -345,7 +347,7 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, // extending. Once it stops extending, we've hit the max and need to // rely on the grace duration. if remainingLeaseDuration > priorDuration { - r.calculateGrace(remainingLeaseDuration) + r.calculateGrace(remainingLeaseDuration, time.Duration(r.increment)*time.Second) } priorDuration = remainingLeaseDuration @@ -373,16 +375,21 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, } } -// calculateGrace calculates the grace period based on a reasonable set of -// assumptions given the total lease time; it also adds some jitter to not have -// clients be in sync. -func (r *LifetimeWatcher) calculateGrace(leaseDuration time.Duration) { - if leaseDuration <= 0 { +// calculateGrace calculates the grace period based on the minimum of the +// remaining lease duration and the token increment value; it also adds some +// jitter to not have clients be in sync. +func (r *LifetimeWatcher) calculateGrace(leaseDuration, increment time.Duration) { + minDuration := leaseDuration + if minDuration > increment && increment > 0 { + minDuration = increment + } + + if minDuration <= 0 { r.grace = 0 return } - leaseNanos := float64(leaseDuration.Nanoseconds()) + leaseNanos := float64(minDuration.Nanoseconds()) jitterMax := 0.1 * leaseNanos // For a given lease duration, we want to allow 80-90% of that to elapse, diff --git a/api/renewer_test.go b/api/renewer_test.go index 3fa9ed8e2bcf..2a5d5745ba64 100644 --- a/api/renewer_test.go +++ b/api/renewer_test.go @@ -24,28 +24,28 @@ func TestRenewer_NewRenewer(t *testing.T) { err bool }{ { - "nil", - nil, - nil, - true, + name: "nil", + i: nil, + e: nil, + err: true, }, { - "missing_secret", - &RenewerInput{ + name: "missing_secret", + i: &RenewerInput{ Secret: nil, }, - nil, - true, + e: nil, + err: true, }, { - "default_grace", - &RenewerInput{ + name: "default_grace", + i: &RenewerInput{ Secret: &Secret{}, }, - &Renewer{ + e: &Renewer{ secret: &Secret{}, }, - false, + err: false, }, } @@ -98,67 +98,78 @@ func TestLifetimeWatcher(t *testing.T) { expectRenewal bool }{ { - time.Second, - "no_error", - 60, - 60, - func(_ string, _ int) (*Secret, error) { + maxTestTime: time.Second, + name: "no_error", + leaseDurationSeconds: 60, + incrementSeconds: 60, + renew: func(_ string, _ int) (*Secret, error) { return renewedSecret, nil }, - nil, - true, + expectError: nil, + expectRenewal: true, }, { - 5 * time.Second, - "one_error", - 15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: time.Second, + name: "short_increment_duration", + leaseDurationSeconds: 60, + incrementSeconds: 10, + renew: func(_ string, _ int) (*Secret, error) { + return renewedSecret, nil + }, + expectError: nil, + expectRenewal: true, + }, + { + maxTestTime: 5 * time.Second, + name: "one_error", + leaseDurationSeconds: 15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { if caseOneErrorCount == 0 { caseOneErrorCount++ return nil, fmt.Errorf("renew failure") } return renewedSecret, nil }, - nil, - true, + expectError: nil, + expectRenewal: true, }, { - 15 * time.Second, - "many_errors", - 15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: 15 * time.Second, + name: "many_errors", + leaseDurationSeconds: 15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { if caseManyErrorsCount == 3 { return renewedSecret, nil } caseManyErrorsCount++ return nil, fmt.Errorf("renew failure") }, - nil, - true, + expectError: nil, + expectRenewal: true, }, { - 15 * time.Second, - "only_errors", - 15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: 15 * time.Second, + name: "only_errors", + leaseDurationSeconds: 15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { return nil, fmt.Errorf("renew failure") }, - nil, - false, + expectError: nil, + expectRenewal: false, }, { - 15 * time.Second, - "negative_lease_duration", - -15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: 15 * time.Second, + name: "negative_lease_duration", + leaseDurationSeconds: -15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { return renewedSecret, nil }, - nil, - true, + expectError: nil, + expectRenewal: true, }, } diff --git a/builtin/credential/aws/backend_e2e_test.go b/builtin/credential/aws/backend_e2e_test.go index 0e8186a44362..ac2bb22f129a 100644 --- a/builtin/credential/aws/backend_e2e_test.go +++ b/builtin/credential/aws/backend_e2e_test.go @@ -63,10 +63,10 @@ func TestBackend_E2E_Initialize(t *testing.T) { "policies": "default", "bound_subnet_id": "subnet-abcdef", } - if _, err := core.Client.Logical().WriteWithContext(context.Background(), "auth/aws/role/test-role", data); err != nil { + if _, err := core.Client.Logical().Write("auth/aws/role/test-role", data); err != nil { t.Fatal(err) } - role, err := core.Client.Logical().ReadWithContext(context.Background(), "auth/aws/role/test-role") + role, err := core.Client.Logical().Read("auth/aws/role/test-role") if err != nil { t.Fatal(err) } diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index 50cdd37d2568..fe70de0d06e8 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -1407,6 +1407,11 @@ func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, Name: identityAlias, }, } + + if entity.Type == "assumed-role" { + auth.DisplayName = strings.Join([]string{entity.FriendlyName, entity.SessionInfo}, "/") + } + roleEntry.PopulateTokenAuth(auth) if err := identityConfigEntry.IAMAuthMetadataHandler.PopulateDesiredMetadata(auth, map[string]string{ "client_arn": callerID.Arn, diff --git a/builtin/credential/cert/backend_test.go b/builtin/credential/cert/backend_test.go index 8a58c4575f6f..db400dab780a 100644 --- a/builtin/credential/cert/backend_test.go +++ b/builtin/credential/cert/backend_test.go @@ -272,7 +272,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { var err error // Mount /pki as a root CA - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -285,7 +285,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { // Set the cluster's certificate as the root CA in /pki pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/ca", map[string]interface{}{ "pem_bundle": pemBundleRootCA, }) if err != nil { @@ -293,7 +293,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Mount /pki2 to operate as an intermediate CA - err = client.Sys().MountWithContext(context.Background(), "pki2", &api.MountInput{ + err = client.Sys().Mount("pki2", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -305,14 +305,14 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Create a CSR for the intermediate CA - secret, err := client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/generate/internal", nil) + secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil) if err != nil { t.Fatal(err) } intermediateCSR := secret.Data["csr"].(string) // Sign the intermediate CSR using /pki - secret, err = client.Logical().WriteWithContext(context.Background(), "pki/root/sign-intermediate", map[string]interface{}{ + secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{ "permitted_dns_domains": ".myvault.com", "csr": intermediateCSR, }) @@ -322,7 +322,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { intermediateCertPEM := secret.Data["certificate"].(string) // Configure the intermediate cert as the CA in /pki2 - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/set-signed", map[string]interface{}{ + _, err = client.Logical().Write("pki2/intermediate/set-signed", map[string]interface{}{ "certificate": intermediateCertPEM, }) if err != nil { @@ -330,7 +330,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Create a role on the intermediate CA mount - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/roles/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("pki2/roles/myvault-dot-com", map[string]interface{}{ "allowed_domains": "myvault.com", "allow_subdomains": "true", "max_ttl": "5m", @@ -340,7 +340,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Issue a leaf cert using the intermediate CA - secret, err = client.Logical().WriteWithContext(context.Background(), "pki2/issue/myvault-dot-com", map[string]interface{}{ + secret, err = client.Logical().Write("pki2/issue/myvault-dot-com", map[string]interface{}{ "common_name": "cert.myvault.com", "format": "pem", "ip_sans": "127.0.0.1", @@ -360,7 +360,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Set the intermediate CA cert as a trusted certificate in the backend - _, err = client.Logical().WriteWithContext(context.Background(), "auth/cert/certs/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("auth/cert/certs/myvault-dot-com", map[string]interface{}{ "display_name": "myvault.com", "policies": "default", "certificate": intermediateCertPEM, @@ -447,7 +447,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { // Create a new api client with the desired TLS configuration newClient := getAPIClient(cores[0].Listeners[0].Address.Port, cores[0].TLSConfig) - secret, err = newClient.Logical().WriteWithContext(context.Background(), "auth/cert/login", map[string]interface{}{ + secret, err = newClient.Logical().Write("auth/cert/login", map[string]interface{}{ "name": "myvault-dot-com", }) if err != nil { diff --git a/builtin/logical/database/backend_test.go b/builtin/logical/database/backend_test.go index 9a08460820e8..5dff16ea756c 100644 --- a/builtin/logical/database/backend_test.go +++ b/builtin/logical/database/backend_test.go @@ -689,7 +689,7 @@ func TestBackend_connectionCrud(t *testing.T) { if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } - if len(resp.Warnings) != 1 { + if len(resp.Warnings) == 0 { t.Fatalf("expected warning about password in url %s, resp:%#v\n", connURL, resp) } diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index f04d9fad99cf..bbb445eba8e0 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -67,7 +67,7 @@ func TestPKI_RequireCN(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -78,7 +78,7 @@ func TestPKI_RequireCN(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -89,7 +89,7 @@ func TestPKI_RequireCN(t *testing.T) { } // Create a role which does require CN (default) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/example", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/example", map[string]interface{}{ "allowed_domains": "foobar.com,zipzap.com,abc.com,xyz.com", "allow_bare_domains": true, "allow_subdomains": true, @@ -101,7 +101,7 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to true and with common name supplied. // It should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{ + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{ "common_name": "foobar.com", }) if err != nil { @@ -110,13 +110,13 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to true and with out supplying the // common name. It should error out. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{}) + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{}) if err == nil { t.Fatalf("expected an error due to missing common_name") } // Modify the role to make the common name optional - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/example", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/example", map[string]interface{}{ "allowed_domains": "foobar.com,zipzap.com,abc.com,xyz.com", "allow_bare_domains": true, "allow_subdomains": true, @@ -129,7 +129,7 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to false and without supplying the // common name. It should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{}) + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{}) if err != nil { t.Fatal(err) } @@ -140,7 +140,7 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to false and with a common name. It // should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{}) + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{}) if err != nil { t.Fatal(err) } @@ -164,7 +164,7 @@ func TestPKI_DeviceCert(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -175,7 +175,7 @@ func TestPKI_DeviceCert(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", "not_after": "9999-12-31T23:59:59Z", }) @@ -202,7 +202,7 @@ func TestPKI_DeviceCert(t *testing.T) { } // Create a role which does require CN (default) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/example", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/example", map[string]interface{}{ "allowed_domains": "foobar.com,zipzap.com,abc.com,xyz.com", "allow_bare_domains": true, "allow_subdomains": true, @@ -214,7 +214,7 @@ func TestPKI_DeviceCert(t *testing.T) { // Issue a cert with require_cn set to true and with common name supplied. // It should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{ + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{ "common_name": "foobar.com", }) if err != nil { @@ -250,7 +250,7 @@ func TestBackend_InvalidParameter(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -261,7 +261,7 @@ func TestBackend_InvalidParameter(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", "not_after": "9999-12-31T23:59:59Z", "ttl": "25h", @@ -270,7 +270,7 @@ func TestBackend_InvalidParameter(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", "not_after": "9999-12-31T23:59:59", }) @@ -702,6 +702,46 @@ func generateURLSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[s return ret } +func generateCSR(t *testing.T, csrTemplate *x509.CertificateRequest, keyType string, keyBits int) (interface{}, []byte, string) { + var priv interface{} + var err error + switch keyType { + case "rsa": + priv, err = rsa.GenerateKey(rand.Reader, keyBits) + case "ec": + switch keyBits { + case 224: + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case 256: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case 384: + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case 521: + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + t.Fatalf("Got unknown ec< key bits: %v", keyBits) + } + case "ed25519": + _, priv, err = ed25519.GenerateKey(rand.Reader) + } + + if err != nil { + t.Fatalf("Got error generating private key for CSR: %v", err) + } + + csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, priv) + if err != nil { + t.Fatalf("Got error generating CSR: %v", err) + } + + csrPem := strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: csr, + }))) + + return priv, csr, csrPem +} + func generateCSRSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[string]interface{}) []logicaltest.TestStep { csrTemplate := x509.CertificateRequest{ Subject: pkix.Name{ @@ -726,12 +766,7 @@ func generateCSRSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[s }, } - priv, _ := rsa.GenerateKey(rand.Reader, 2048) - csr, _ := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, priv) - csrPem := strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE REQUEST", - Bytes: csr, - }))) + _, _, csrPem := generateCSR(t, &csrTemplate, "rsa", 2048) ret := []logicaltest.TestStep{ { @@ -1260,7 +1295,7 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { for name, allowedInt := range cnMap { roleVals.KeyType = "rsa" roleVals.KeyBits = 2048 - if mathRand.Int()%2 == 1 { + if mathRand.Int()%3 == 1 { roleVals.KeyType = "ec" roleVals.KeyBits = 224 } @@ -1714,30 +1749,96 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { } func TestBackend_PathFetchValidRaw(t *testing.T) { - // create the backend config := logical.TestBackendConfig() storage := &logical.InmemStorage{} config.StorageView = storage b := Backend(config) err := b.Setup(context.Background(), config) - if err != nil { - t.Fatal(err) + require.NoError(t, err) + + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Path: "root/generate/internal", + Storage: storage, + Data: map[string]interface{}{ + "common_name": "test.com", + "ttl": "6h", + }, + MountPoint: "pki/", + }) + require.NoError(t, err) + if resp != nil && resp.IsError() { + t.Fatalf("failed to generate root, %#v", resp) } + rootCaAsPem := resp.Data["certificate"].(string) - expectedSerial := "17:67:16:b0:b9:45:58:c0:3a:29:e3:cb:d6:98:33:7a:a6:3b:66:c1" - expectedCert := []byte("test certificate") - entry := &logical.StorageEntry{ - Key: fmt.Sprintf("certs/%s", normalizeSerial(expectedSerial)), - Value: expectedCert, + // The ca_chain call at least for now does not return the root CA authority + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.ReadOperation, + Path: "ca_chain", + Storage: storage, + Data: map[string]interface{}{}, + MountPoint: "pki/", + }) + require.NoError(t, err) + if resp != nil && resp.IsError() { + t.Fatalf("failed read ca_chain, %#v", resp) } - err = storage.Put(context.Background(), entry) - if err != nil { - t.Fatal(err) + require.Equal(t, []byte{}, resp.Data[logical.HTTPRawBody], "ca_chain response should have been empty") + + // The ca/pem should return us the actual CA... + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.ReadOperation, + Path: "ca/pem", + Storage: storage, + Data: map[string]interface{}{}, + MountPoint: "pki/", + }) + require.NoError(t, err) + if resp != nil && resp.IsError() { + t.Fatalf("failed read ca/pem, %#v", resp) + } + // check the raw cert matches the response body + if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), []byte(rootCaAsPem)) != 0 { + t.Fatalf("failed to get raw cert") } + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Path: "roles/example", + Storage: storage, + Data: map[string]interface{}{ + "allowed_domains": "example.com", + "allow_subdomains": "true", + "max_ttl": "1h", + "no_store": "false", + }, + MountPoint: "pki/", + }) + require.NoError(t, err, "error setting up pki role: %v", err) + + // Now issue a short-lived certificate from our pki-external. + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Path: "issue/example", + Storage: storage, + Data: map[string]interface{}{ + "common_name": "test.example.com", + "ttl": "5m", + }, + MountPoint: "pki/", + }) + require.NoError(t, err, "error issuing certificate: %v", err) + require.NotNil(t, resp, "got nil response from issuing request") + + issueCrtAsPem := resp.Data["certificate"].(string) + issuedCrt := parseCert(t, issueCrtAsPem) + expectedSerial := certutil.GetHexFormatted(issuedCrt.SerialNumber.Bytes(), ":") + expectedCert := []byte(issueCrtAsPem) + // get der cert - resp, err := b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(context.Background(), &logical.Request{ Operation: logical.ReadOperation, Path: fmt.Sprintf("cert/%s/raw", expectedSerial), Storage: storage, @@ -1750,8 +1851,10 @@ func TestBackend_PathFetchValidRaw(t *testing.T) { } // check the raw cert matches the response body - if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), expectedCert) != 0 { - t.Fatalf("failed to get raw cert") + rawBody := resp.Data[logical.HTTPRawBody].([]byte) + bodyAsPem := []byte(strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rawBody})))) + if bytes.Compare(bodyAsPem, expectedCert) != 0 { + t.Fatalf("failed to get raw cert for serial number: %s", expectedSerial) } if resp.Data[logical.HTTPContentType] != "application/pkix-cert" { t.Fatalf("failed to get raw cert content-type") @@ -1770,13 +1873,8 @@ func TestBackend_PathFetchValidRaw(t *testing.T) { t.Fatal(err) } - pemBlock := &pem.Block{ - Type: "CERTIFICATE", - Bytes: expectedCert, - } - pemCert := []byte(strings.TrimSpace(string(pem.EncodeToMemory(pemBlock)))) // check the pem cert matches the response body - if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), pemCert) != 0 { + if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), expectedCert) != 0 { t.Fatalf("failed to get pem cert") } if resp.Data[logical.HTTPContentType] != "application/pem-certificate-chain" { @@ -1918,6 +2016,23 @@ func TestBackend_PathFetchCertList(t *testing.T) { } func TestBackend_SignVerbatim(t *testing.T) { + testCases := []struct { + testName string + keyType string + }{ + {testName: "RSA", keyType: "rsa"}, + {testName: "ED25519", keyType: "ed25519"}, + {testName: "EC", keyType: "ec"}, + {testName: "Any", keyType: "any"}, + } + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + runTestSignVerbatim(t, tc.keyType) + }) + } +} + +func runTestSignVerbatim(t *testing.T, keyType string) { // create the backend config := logical.TestBackendConfig() storage := &logical.InmemStorage{} @@ -1995,8 +2110,9 @@ func TestBackend_SignVerbatim(t *testing.T) { // create a role entry; we use this to check that sign-verbatim when used with a role is still honoring TTLs roleData := map[string]interface{}{ - "ttl": "4h", - "max_ttl": "8h", + "ttl": "4h", + "max_ttl": "8h", + "key_type": keyType, } resp, err = b.HandleRequest(context.Background(), &logical.Request{ Operation: logical.UpdateOperation, @@ -2109,6 +2225,7 @@ func TestBackend_SignVerbatim(t *testing.T) { "ttl": "4h", "max_ttl": "8h", "generate_lease": true, + "key_type": keyType, } resp, err = b.HandleRequest(context.Background(), &logical.Request{ Operation: logical.UpdateOperation, @@ -2161,7 +2278,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2172,7 +2289,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -2181,7 +2298,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { if resp == nil { t.Fatal("expected ca info") } - resp, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + resp, err = client.Logical().Read("pki/cert/ca_chain") if err != nil { t.Fatalf("error reading ca_chain: %v", err) } @@ -2189,7 +2306,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { r1Data := resp.Data // Try again, make sure it's a 204 and same CA - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -2201,7 +2318,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { if resp.Data != nil || len(resp.Warnings) == 0 { t.Fatalf("bad response: %#v", *resp) } - resp, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + resp, err = client.Logical().Read("pki/cert/ca_chain") if err != nil { t.Fatalf("error reading ca_chain: %v", err) } @@ -2210,7 +2327,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("got different ca certs") } - resp, err = client.Logical().DeleteWithContext(context.Background(), "pki/root") + resp, err = client.Logical().Delete("pki/root") if err != nil { t.Fatal(err) } @@ -2218,7 +2335,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("expected nil response") } // Make sure it behaves the same - resp, err = client.Logical().DeleteWithContext(context.Background(), "pki/root") + resp, err = client.Logical().Delete("pki/root") if err != nil { t.Fatal(err) } @@ -2226,12 +2343,12 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("expected nil response") } - _, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + _, err = client.Logical().Read("pki/cert/ca_chain") if err == nil { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -2241,7 +2358,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("expected ca info") } - _, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + _, err = client.Logical().Read("pki/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -2261,7 +2378,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2271,7 +2388,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { if err != nil { t.Fatal(err) } - err = client.Sys().MountWithContext(context.Background(), "int", &api.MountInput{ + err = client.Sys().Mount("int", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "4h", @@ -2283,7 +2400,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { } // Direct issuing from root - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -2291,7 +2408,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allow_bare_domains": true, "allow_subdomains": true, }) @@ -2299,7 +2416,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "int/intermediate/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("int/intermediate/generate/internal", map[string]interface{}{ "common_name": "myint.com", }) if err != nil { @@ -2308,7 +2425,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { csr := resp.Data["csr"] - _, err = client.Logical().WriteWithContext(context.Background(), "root/sign/test", map[string]interface{}{ + _, err = client.Logical().Write("root/sign/test", map[string]interface{}{ "common_name": "myint.com", "csr": csr, "ttl": "60h", @@ -2317,7 +2434,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal("expected error") } - _, err = client.Logical().WriteWithContext(context.Background(), "root/sign-verbatim/test", map[string]interface{}{ + _, err = client.Logical().Write("root/sign-verbatim/test", map[string]interface{}{ "common_name": "myint.com", "other_sans": "1.3.6.1.4.1.311.20.2.3;utf8:caadmin@example.com", "csr": csr, @@ -2327,7 +2444,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/root/sign-intermediate", map[string]interface{}{ + resp, err = client.Logical().Write("root/root/sign-intermediate", map[string]interface{}{ "common_name": "myint.com", "other_sans": "1.3.6.1.4.1.311.20.2.3;utf8:caadmin@example.com", "csr": csr, @@ -2655,7 +2772,7 @@ func TestBackend_OID_SANs(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2671,7 +2788,7 @@ func TestBackend_OID_SANs(t *testing.T) { var block *pem.Block var cert *x509.Certificate - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -2679,7 +2796,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allowed_domains": []string{"foobar.com", "zipzap.com"}, "allow_bare_domains": true, "allow_subdomains": true, @@ -2693,7 +2810,7 @@ func TestBackend_OID_SANs(t *testing.T) { // Get a baseline before adding OID SANs. In the next sections we'll verify // that the SANs are all added even as the OID SAN inclusion forces other // adding logic (custom rather than built-in Golang logic) - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foobar.com,foo.foobar.com,bar.foobar.com", @@ -2719,7 +2836,7 @@ func TestBackend_OID_SANs(t *testing.T) { } // First test some bad stuff that shouldn't work - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2731,7 +2848,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2743,7 +2860,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2755,7 +2872,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2767,7 +2884,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2780,7 +2897,7 @@ func TestBackend_OID_SANs(t *testing.T) { } // Valid for first possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2810,7 +2927,7 @@ func TestBackend_OID_SANs(t *testing.T) { } // Valid for second possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2846,7 +2963,7 @@ func TestBackend_OID_SANs(t *testing.T) { fmt.Sprintf("%s;%s:%s", oid1, type1, val1), fmt.Sprintf("%s;%s:%s", oid2, type2, val2), } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2898,7 +3015,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2914,7 +3031,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { var block *pem.Block var cert *x509.Certificate - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -2923,7 +3040,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // First test that Serial Numbers are not allowed - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allow_any_name": true, "enforce_hostnames": false, }) @@ -2931,7 +3048,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "ttl": "1h", }) @@ -2939,7 +3056,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "ttl": "1h", "serial_number": "foobar", @@ -2949,7 +3066,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // Update the role to allow serial numbers - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allow_any_name": true, "enforce_hostnames": false, "allowed_serial_numbers": "f00*,b4r*", @@ -2958,7 +3075,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "ttl": "1h", // Not a valid serial number @@ -2969,7 +3086,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // Valid for first possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "serial_number": "f00bar", }) @@ -2990,7 +3107,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // Valid for second possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "serial_number": "b4rf00", }) @@ -3025,7 +3142,7 @@ func TestBackend_URI_SANs(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3036,7 +3153,7 @@ func TestBackend_URI_SANs(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -3044,7 +3161,7 @@ func TestBackend_URI_SANs(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allowed_domains": []string{"foobar.com", "zipzap.com"}, "allow_bare_domains": true, "allow_subdomains": true, @@ -3056,7 +3173,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // First test some bad stuff that shouldn't work - _, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + _, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3068,7 +3185,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // Test valid single entry - _, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + _, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3080,7 +3197,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // Test globed entry - _, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + _, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3092,7 +3209,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // Test multiple entries - resp, err := client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err := client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3141,7 +3258,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { client := cluster.Cores[0].Client // Write test policy for userpass auth method. - err := client.Sys().PutPolicyWithContext(context.Background(), "test", ` + err := client.Sys().PutPolicy("test", ` path "pki/*" { capabilities = ["update"] }`) @@ -3155,7 +3272,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Configure test role for userpass. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/userpassname", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/userpassname", map[string]interface{}{ "password": "test", "policies": "test", }); err != nil { @@ -3163,7 +3280,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Login userpass for test role and keep client token. - secret, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/login/userpassname", map[string]interface{}{ + secret, err := client.Logical().Write("auth/userpass/login/userpassname", map[string]interface{}{ "password": "test", }) if err != nil || secret == nil { @@ -3172,14 +3289,14 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { userpassToken := secret.Auth.ClientToken // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } userpassAccessor := auths["userpass/"].Accessor // Mount PKI. - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3191,7 +3308,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Generate internal CA. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -3200,7 +3317,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Write role PKI. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_uri_sans": []string{ "spiffe://domain/{{identity.entity.aliases." + userpassAccessor + ".name}}", "spiffe://domain/{{identity.entity.aliases." + userpassAccessor + ".name}}/*", "spiffe://domain/foo", @@ -3214,27 +3331,27 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { // Issue certificate with identity templating client.SetToken(userpassToken) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname, spiffe://domain/foo"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname, spiffe://domain/foo"}) if err != nil { t.Fatal(err) } // Issue certificate with identity templating and glob client.SetToken(userpassToken) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname/bar"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname/bar"}) if err != nil { t.Fatal(err) } // Issue certificate with non-matching identity template parameter client.SetToken(userpassToken) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/unknownuser"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/unknownuser"}) if err == nil { t.Fatal(err) } // Set allowed_uri_sans_template to false. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_uri_sans_template": false, }) if err != nil { @@ -3242,7 +3359,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Issue certificate with userpassToken. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/users/userpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/users/userpassname"}) if err == nil { t.Fatal("expected error") } @@ -3265,7 +3382,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { client := cluster.Cores[0].Client // Write test policy for userpass auth method. - err := client.Sys().PutPolicyWithContext(context.Background(), "test", ` + err := client.Sys().PutPolicy("test", ` path "pki/*" { capabilities = ["update"] }`) @@ -3279,7 +3396,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Configure test role for userpass. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/userpassname", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/userpassname", map[string]interface{}{ "password": "test", "policies": "test", }); err != nil { @@ -3293,14 +3410,14 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } userpassAccessor := auths["userpass/"].Accessor // Mount PKI. - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3312,7 +3429,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Generate internal CA. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -3321,7 +3438,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Write role PKI. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_domains": []string{ "foobar.com", "zipzap.com", "{{identity.entity.aliases." + userpassAccessor + ".name}}", "foo.{{identity.entity.aliases." + userpassAccessor + ".name}}.example.com", @@ -3341,31 +3458,31 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { if err != nil || secret == nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) if err != nil { t.Fatal(err) } // Issue certificate for foobar.com to verify allowed_domain_templae doesnt break plain domains. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "foobar.com"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "foobar.com"}) if err != nil { t.Fatal(err) } // Issue certificate for unknown userpassname. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "unknownuserpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "unknownuserpassname"}) if err == nil { t.Fatal("expected error") } // Issue certificate for foo.userpassname.domain. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "foo.userpassname.example.com"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "foo.userpassname.example.com"}) if err != nil { t.Fatal("expected error") } // Set allowed_domains_template to false. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_domains_template": false, }) if err != nil { @@ -3373,12 +3490,132 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Issue certificate with userpassToken. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) if err == nil { t.Fatal("expected error") } } +func TestReadWriteDeleteRoles(t *testing.T) { + ctx := context.Background() + coreConfig := &vault.CoreConfig{ + CredentialBackends: map[string]logical.Factory{ + "userpass": userpass.Factory, + }, + LogicalBackends: map[string]logical.Factory{ + "pki": Factory, + }, + } + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + client := cluster.Cores[0].Client + + // Mount PKI. + err := client.Sys().MountWithContext(ctx, "pki", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + DefaultLeaseTTL: "16h", + MaxLeaseTTL: "60h", + }, + }) + if err != nil { + t.Fatal(err) + } + + resp, err := client.Logical().ReadWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + if resp != nil { + t.Fatalf("response should have been emtpy but was:\n%#v", resp) + } + + // Write role PKI. + _, err = client.Logical().WriteWithContext(ctx, "pki/roles/test", map[string]interface{}{}) + if err != nil { + t.Fatal(err) + } + + // Read the role. + resp, err = client.Logical().ReadWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + if resp.Data == nil { + t.Fatal("default data within response was nil when it should have contained data") + } + + // Validate that we have not changed any defaults unknowingly + expectedData := map[string]interface{}{ + "key_type": "rsa", + "use_csr_sans": true, + "client_flag": true, + "allowed_serial_numbers": []interface{}{}, + "generate_lease": false, + "signature_bits": json.Number("256"), + "allowed_domains": []interface{}{}, + "allowed_uri_sans_template": false, + "enforce_hostnames": true, + "policy_identifiers": []interface{}{}, + "require_cn": true, + "allowed_domains_template": false, + "allow_token_displayname": false, + "country": []interface{}{}, + "not_after": "", + "postal_code": []interface{}{}, + "use_csr_common_name": true, + "allow_localhost": true, + "allow_subdomains": false, + "allow_wildcard_certificates": true, + "allowed_other_sans": []interface{}{}, + "allowed_uri_sans": []interface{}{}, + "basic_constraints_valid_for_non_ca": false, + "key_usage": []interface{}{"DigitalSignature", "KeyAgreement", "KeyEncipherment"}, + "not_before_duration": json.Number("30"), + "allow_glob_domains": false, + "ttl": json.Number("0"), + "ou": []interface{}{}, + "email_protection_flag": false, + "locality": []interface{}{}, + "server_flag": true, + "allow_bare_domains": false, + "allow_ip_sans": true, + "ext_key_usage_oids": []interface{}{}, + "allow_any_name": false, + "ext_key_usage": []interface{}{}, + "key_bits": json.Number("2048"), + "max_ttl": json.Number("0"), + "no_store": false, + "organization": []interface{}{}, + "province": []interface{}{}, + "street_address": []interface{}{}, + "code_signing_flag": false, + } + + if diff := deep.Equal(expectedData, resp.Data); len(diff) > 0 { + t.Fatalf("pki role default values have changed, diff: %v", diff) + } + + _, err = client.Logical().DeleteWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + resp, err = client.Logical().ReadWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + if resp != nil { + t.Fatalf("response should have been empty but was:\n%#v", resp) + } +} + func setCerts() { cak, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { @@ -3515,7 +3752,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { var err error // Mount /pki as a root CA - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3528,7 +3765,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { // Set the cluster's certificate as the root CA in /pki pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/ca", map[string]interface{}{ "pem_bundle": pemBundleRootCA, }) if err != nil { @@ -3536,7 +3773,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { } // Mount /pki2 to operate as an intermediate CA - err = client.Sys().MountWithContext(context.Background(), "pki2", &api.MountInput{ + err = client.Sys().Mount("pki2", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3548,14 +3785,14 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { } // Create a CSR for the intermediate CA - secret, err := client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/generate/internal", nil) + secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil) if err != nil { t.Fatal(err) } intermediateCSR := secret.Data["csr"].(string) // Sign the intermediate CSR using /pki - secret, err = client.Logical().WriteWithContext(context.Background(), "pki/root/sign-intermediate", map[string]interface{}{ + secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{ "permitted_dns_domains": ".myvault.com", "csr": intermediateCSR, "ttl": "10s", @@ -3567,7 +3804,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { intermediateCASerialColon := strings.ReplaceAll(strings.ToLower(intermediateCertSerial), ":", "-") // Get the intermediate cert after signing - secret, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/"+intermediateCASerialColon) + secret, err = client.Logical().Read("pki/cert/" + intermediateCASerialColon) if err != nil { t.Fatal(err) } @@ -3576,7 +3813,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { } // Issue a revoke on on /pki - _, err = client.Logical().WriteWithContext(context.Background(), "pki/revoke", map[string]interface{}{ + _, err = client.Logical().Write("pki/revoke", map[string]interface{}{ "serial_number": intermediateCertSerial, }) if err != nil { @@ -3588,7 +3825,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { time.Sleep(3 * time.Second) // Issue a tidy on /pki - _, err = client.Logical().WriteWithContext(context.Background(), "pki/tidy", map[string]interface{}{ + _, err = client.Logical().Write("pki/tidy", map[string]interface{}{ "tidy_cert_store": true, "tidy_revoked_certs": true, "safety_buffer": "1s", @@ -3636,7 +3873,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { time.Sleep(10 * time.Second) // Issue a tidy on /pki - _, err = client.Logical().WriteWithContext(context.Background(), "pki/tidy", map[string]interface{}{ + _, err = client.Logical().Write("pki/tidy", map[string]interface{}{ "tidy_cert_store": true, "tidy_revoked_certs": true, "safety_buffer": "1s", @@ -3650,7 +3887,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { // Issue a tidy-status on /pki { - tidyStatus, err := client.Logical().ReadWithContext(context.Background(), "pki/tidy-status") + tidyStatus, err := client.Logical().Read("pki/tidy-status") if err != nil { t.Fatal(err) } @@ -3791,7 +4028,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { var err error // Generate a root CA at /pki-root - err = client.Sys().MountWithContext(context.Background(), "pki-root", &api.MountInput{ + err = client.Sys().Mount("pki-root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3802,7 +4039,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki-root/root/generate/exported", map[string]interface{}{ + resp, err := client.Logical().Write("pki-root/root/generate/exported", map[string]interface{}{ "common_name": "root myvault.com", "key_type": keyType, }) @@ -3816,7 +4053,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { rootCert := rootData["certificate"].(string) // Validate that root's /cert/ca-chain now contains the certificate. - resp, err = client.Logical().ReadWithContext(context.Background(), "pki-root/cert/ca_chain") + resp, err = client.Logical().Read("pki-root/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -3830,7 +4067,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { } // Now generate an intermediate at /pki-intermediate, signed by the root. - err = client.Sys().MountWithContext(context.Background(), "pki-intermediate", &api.MountInput{ + err = client.Sys().Mount("pki-intermediate", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3841,7 +4078,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-intermediate/intermediate/generate/exported", map[string]interface{}{ + resp, err = client.Logical().Write("pki-intermediate/intermediate/generate/exported", map[string]interface{}{ "common_name": "intermediate myvault.com", "key_type": keyType, }) @@ -3854,7 +4091,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { intermediateData := resp.Data intermediateKey := intermediateData["private_key"].(string) - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-root/root/sign-intermediate", map[string]interface{}{ + resp, err = client.Logical().Write("pki-root/root/sign-intermediate", map[string]interface{}{ "csr": intermediateData["csr"], "format": "pem_bundle", }) @@ -3871,7 +4108,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { intermediaryCaCert := parseCert(t, intermediateCert) requireSignedBy(t, intermediaryCaCert, rootCaCert.PublicKey) - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-intermediate/intermediate/set-signed", map[string]interface{}{ + resp, err = client.Logical().Write("pki-intermediate/intermediate/set-signed", map[string]interface{}{ "certificate": intermediateCert + "\n" + rootCert + "\n", }) if err != nil { @@ -3880,7 +4117,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { // Validate that intermediate's ca_chain field now includes the full // chain. - resp, err = client.Logical().ReadWithContext(context.Background(), "pki-intermediate/cert/ca_chain") + resp, err = client.Logical().Read("pki-intermediate/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -3898,7 +4135,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { // Finally, import this signing cert chain into a new mount to ensure // "external" CAs behave as expected. - err = client.Sys().MountWithContext(context.Background(), "pki-external", &api.MountInput{ + err = client.Sys().Mount("pki-external", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3909,7 +4146,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-external/config/ca", map[string]interface{}{ + resp, err = client.Logical().Write("pki-external/config/ca", map[string]interface{}{ "pem_bundle": intermediateKey + "\n" + intermediateCert + "\n" + rootCert + "\n", }) if err != nil { @@ -3917,7 +4154,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { } // Validate the external chain information was loaded correctly. - resp, err = client.Logical().ReadWithContext(context.Background(), "pki-external/cert/ca_chain") + resp, err = client.Logical().Read("pki-external/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -3997,7 +4234,7 @@ func RoleIssuanceRegressionHelper(t *testing.T, client *api.Client, index int, t for _, AllowLocalhost := range test.AllowLocalhost.ToValues() { for _, AllowWildcardCertificates := range test.AllowWildcardCertificates.ToValues() { role := fmt.Sprintf("issuance-regression-%d-bare-%v-glob-%v-subdomains-%v-localhost-%v-wildcard-%v", index, AllowBareDomains, AllowGlobDomains, AllowSubdomains, AllowLocalhost, AllowWildcardCertificates) - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/roles/"+role, map[string]interface{}{ + resp, err := client.Logical().Write("pki/roles/"+role, map[string]interface{}{ "allowed_domains": test.AllowedDomains, "allow_bare_domains": AllowBareDomains, "allow_glob_domains": AllowGlobDomains, @@ -4014,7 +4251,7 @@ func RoleIssuanceRegressionHelper(t *testing.T, client *api.Client, index int, t t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/"+role, map[string]interface{}{ + resp, err = client.Logical().Write("pki/issue/"+role, map[string]interface{}{ "common_name": test.CommonName, }) @@ -4205,7 +4442,7 @@ func TestBackend_Roles_IssuanceRegression(t *testing.T) { var err error // Generate a root CA at /pki to use for our tests - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "12h", @@ -4216,11 +4453,12 @@ func TestBackend_Roles_IssuanceRegression(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/exported", map[string]interface{}{ + // We need a RSA key so all signature sizes are valid with it. + resp, err := client.Logical().Write("pki/root/generate/exported", map[string]interface{}{ "common_name": "myvault.com", "ttl": "128h", - "key_type": "ec", - "key_bits": 256, + "key_type": "rsa", + "key_bits": 2048, }) if err != nil { t.Fatal(err) @@ -4237,6 +4475,168 @@ func TestBackend_Roles_IssuanceRegression(t *testing.T) { t.Log(fmt.Sprintf("Issuance regression expanded matrix test scenarios: %d", tested)) } +type KeySizeRegression struct { + // Values reused for both Role and CA configuration. + RoleKeyType string + RoleKeyBits []int + + // Signature Bits presently is only specified on the role. + RoleSignatureBits []int + + // These are tuples; must be of the same length. + TestKeyTypes []string + TestKeyBits []int + + // All of the above key types/sizes must pass or fail together. + ExpectError bool +} + +func (k KeySizeRegression) KeyTypeValues() []string { + if k.RoleKeyType == "any" { + return []string{"rsa", "ec", "ed25519"} + } + + return []string{k.RoleKeyType} +} + +func RoleKeySizeRegressionHelper(t *testing.T, client *api.Client, index int, test KeySizeRegression) int { + tested := 0 + + for _, caKeyType := range test.KeyTypeValues() { + for _, caKeyBits := range test.RoleKeyBits { + // Generate a new CA key. + resp, err := client.Logical().Write("pki/root/generate/exported", map[string]interface{}{ + "common_name": "myvault.com", + "ttl": "128h", + "key_type": caKeyType, + "key_bits": caKeyBits, + }) + if err != nil { + t.Fatal(err) + } + if resp == nil { + t.Fatal("expected ca info") + } + + for _, roleKeyBits := range test.RoleKeyBits { + for _, roleSignatureBits := range test.RoleSignatureBits { + role := fmt.Sprintf("key-size-regression-%d-keytype-%v-keybits-%d-signature-bits-%d", index, test.RoleKeyType, roleKeyBits, roleSignatureBits) + resp, err := client.Logical().Write("pki/roles/"+role, map[string]interface{}{ + "key_type": test.RoleKeyType, + "key_bits": roleKeyBits, + "signature_bits": roleSignatureBits, + }) + if err != nil { + t.Fatal(err) + } + + for index, keyType := range test.TestKeyTypes { + keyBits := test.TestKeyBits[index] + + _, _, csrPem := generateCSR(t, &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "localhost", + }, + }, keyType, keyBits) + + resp, err = client.Logical().Write("pki/sign/"+role, map[string]interface{}{ + "common_name": "localhost", + "csr": csrPem, + }) + + haveErr := err != nil || resp == nil + + if haveErr != test.ExpectError { + t.Fatalf("key size regression test [%d] failed: haveErr: %v, expectErr: %v, err: %v, resp: %v, test case: %v, caKeyType: %v, caKeyBits: %v, role: %v, keyType: %v, keyBits: %v", index, haveErr, test.ExpectError, err, resp, test, caKeyType, caKeyBits, role, keyType, keyBits) + } + + tested += 1 + } + } + } + + _, err = client.Logical().Delete("pki/root") + if err != nil { + t.Fatal(err) + } + } + } + + return tested +} + +func TestBackend_Roles_KeySizeRegression(t *testing.T) { + // Regression testing of role's issuance policy. + testCases := []KeySizeRegression{ + // RSA with default parameters should fail to issue smaller RSA keys + // and any size ECDSA/Ed25519 keys. + /* 0 */ {"rsa", []int{0, 2048}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "ec", "ec", "ec", "ec", "ed25519"}, []int{512, 1024, 224, 256, 384, 521, 0}, true}, + // But it should work to issue larger RSA keys. + /* 1 */ {"rsa", []int{0, 2048}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "rsa"}, []int{2048, 3072, 4906}, false}, + + // EC with default parameters should fail to issue smaller EC keys + // and any size RSA/Ed25519 keys. + /* 2 */ {"ec", []int{0}, []int{0}, []string{"rsa", "rsa", "rsa", "ec", "ed25519"}, []int{1024, 2048, 4096, 224, 0}, true}, + // But it should work to issue larger EC keys. Note that we should be + // independent of signature bits as that's computed from the issuer + // type (for EC based issuers). + /* 3 */ {"ec", []int{224}, []int{0, 256, 384, 521}, []string{"ec", "ec", "ec", "ec"}, []int{224, 256, 384, 521}, false}, + /* 4 */ {"ec", []int{0, 256}, []int{0, 256, 384, 521}, []string{"ec", "ec", "ec"}, []int{256, 384, 521}, false}, + /* 5 */ {"ec", []int{384}, []int{0, 256, 384, 521}, []string{"ec", "ec"}, []int{384, 521}, false}, + /* 6 */ {"ec", []int{521}, []int{0, 256, 384, 512}, []string{"ec"}, []int{521}, false}, + + // Ed25519 should reject RSA and EC keys. + /* 7 */ {"ed25519", []int{0}, []int{0}, []string{"rsa", "rsa", "rsa", "ec", "ec"}, []int{1024, 2048, 4096, 256, 521}, true}, + // But it should work to issue Ed25519 keys. + /* 8 */ {"ed25519", []int{0}, []int{0}, []string{"ed25519"}, []int{0}, false}, + + // Any key type should reject insecure RSA key sizes. + /* 9 */ {"any", []int{0}, []int{0, 256, 384, 512}, []string{"rsa", "rsa"}, []int{512, 1024}, true}, + // But work for everything else. + /* 10 */ {"any", []int{0}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "rsa", "ec", "ec", "ec", "ec", "ed25519"}, []int{2048, 3072, 4906, 224, 256, 384, 521, 0}, false}, + + // RSA with larger than default key size should reject smaller ones. + /* 11 */ {"rsa", []int{3072}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "rsa"}, []int{512, 1024, 2048}, true}, + } + + if len(testCases) != 12 { + t.Fatalf("misnumbered test case entries will make it hard to find bugs: %v", len(testCases)) + } + + coreConfig := &vault.CoreConfig{ + LogicalBackends: map[string]logical.Factory{ + "pki": Factory, + }, + } + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + + client := cluster.Cores[0].Client + var err error + + // Generate a root CA at /pki to use for our tests + err = client.Sys().Mount("pki", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + DefaultLeaseTTL: "12h", + MaxLeaseTTL: "128h", + }, + }) + if err != nil { + t.Fatal(err) + } + + tested := 0 + for index, test := range testCases { + tested += RoleKeySizeRegressionHelper(t, client, index, test) + } + + t.Log(fmt.Sprintf("Key size regression expanded matrix test scenarios: %d", tested)) +} + var ( initTest sync.Once rsaCAKey string diff --git a/builtin/logical/pki/ca_test.go b/builtin/logical/pki/ca_test.go index 6ec791f6fd58..c1ba77cbde41 100644 --- a/builtin/logical/pki/ca_test.go +++ b/builtin/logical/pki/ca_test.go @@ -155,7 +155,7 @@ func TestBackend_CA_Steps(t *testing.T) { // Setup backends var rsaRoot, rsaInt, ecRoot, ecInt, edRoot, edInt *backend { - if err := client.Sys().MountWithContext(context.Background(), "rsaroot", &api.MountInput{ + if err := client.Sys().Mount("rsaroot", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -166,7 +166,7 @@ func TestBackend_CA_Steps(t *testing.T) { } rsaRoot = b - if err := client.Sys().MountWithContext(context.Background(), "rsaint", &api.MountInput{ + if err := client.Sys().Mount("rsaint", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -177,7 +177,7 @@ func TestBackend_CA_Steps(t *testing.T) { } rsaInt = b - if err := client.Sys().MountWithContext(context.Background(), "ecroot", &api.MountInput{ + if err := client.Sys().Mount("ecroot", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -188,7 +188,7 @@ func TestBackend_CA_Steps(t *testing.T) { } ecRoot = b - if err := client.Sys().MountWithContext(context.Background(), "ecint", &api.MountInput{ + if err := client.Sys().Mount("ecint", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -199,7 +199,7 @@ func TestBackend_CA_Steps(t *testing.T) { } ecInt = b - if err := client.Sys().MountWithContext(context.Background(), "ed25519root", &api.MountInput{ + if err := client.Sys().Mount("ed25519root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -210,7 +210,7 @@ func TestBackend_CA_Steps(t *testing.T) { } edRoot = b - if err := client.Sys().MountWithContext(context.Background(), "ed25519int", &api.MountInput{ + if err := client.Sys().Mount("ed25519int", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -259,7 +259,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // Attempt import but only provide one the cert { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/ca", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/ca", map[string]interface{}{ "pem_bundle": caCert, }) if err == nil { @@ -269,7 +269,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Same but with only the key { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/ca", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/ca", map[string]interface{}{ "pem_bundle": caKey, }) if err == nil { @@ -279,7 +279,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Import CA bundle { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/ca", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/ca", map[string]interface{}{ "pem_bundle": strings.Join([]string{caKey, caCert}, "\n"), }) if err != nil { @@ -292,7 +292,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // cert/ca path { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"cert/ca") + resp, err := client.Logical().Read(rootName + "cert/ca") if err != nil { t.Fatal(err) } @@ -359,7 +359,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // Set CRL config { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/crl", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/crl", map[string]interface{}{ "expiry": "16h", }) if err != nil { @@ -369,7 +369,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Verify it { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"config/crl") + resp, err := client.Logical().Read(rootName + "config/crl") if err != nil { t.Fatal(err) } @@ -390,7 +390,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // First, delete the existing CA info { - _, err := client.Logical().DeleteWithContext(context.Background(), rootName+"root") + _, err := client.Logical().Delete(rootName + "root") if err != nil { t.Fatal(err) } @@ -399,7 +399,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, var rootPEM, rootKey, rootPEMBundle string // Test exported root generation { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"root/generate/exported", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"root/generate/exported", map[string]interface{}{ "common_name": "Root Cert", "ttl": "180h", }) @@ -421,7 +421,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, var intPEM, intCSR, intKey string // Test exported intermediate CSR generation { - resp, err := client.Logical().WriteWithContext(context.Background(), intName+"intermediate/generate/exported", map[string]interface{}{ + resp, err := client.Logical().Write(intName+"intermediate/generate/exported", map[string]interface{}{ "common_name": "intermediate.cert.com", "ttl": "180h", }) @@ -441,7 +441,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Test signing { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"root/sign-intermediate", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"root/sign-intermediate", map[string]interface{}{ "common_name": "intermediate.cert.com", "ttl": "10s", "csr": intCSR, @@ -458,7 +458,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Test setting signed { - resp, err := client.Logical().WriteWithContext(context.Background(), intName+"intermediate/set-signed", map[string]interface{}{ + resp, err := client.Logical().Write(intName+"intermediate/set-signed", map[string]interface{}{ "certificate": intPEM, }) if err != nil { @@ -471,7 +471,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Verify we can find it via the root { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"cert/"+intSerialNumber) + resp, err := client.Logical().Read(rootName + "cert/" + intSerialNumber) if err != nil { t.Fatal(err) } @@ -485,7 +485,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Revoke the intermediate { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"revoke", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"revoke", map[string]interface{}{ "serial_number": intSerialNumber, }) if err != nil { @@ -501,7 +501,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, t.Helper() // Verify it is now revoked { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"cert/"+intSerialNumber) + resp, err := client.Logical().Read(rootName + "cert/" + intSerialNumber) if err != nil { t.Fatal(err) } @@ -559,7 +559,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, } verifyTidyStatus := func(expectedCertStoreDeleteCount int, expectedRevokedCertDeletedCount int) { - tidyStatus, err := client.Logical().ReadWithContext(context.Background(), rootName+"tidy-status") + tidyStatus, err := client.Logical().Read(rootName + "tidy-status") if err != nil { t.Fatal(err) } @@ -594,7 +594,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // Run with a high safety buffer, nothing should happen { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"tidy", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"tidy", map[string]interface{}{ "safety_buffer": "3h", "tidy_cert_store": true, "tidy_revoked_certs": true, @@ -617,7 +617,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Run with both values set false, nothing should happen { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"tidy", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"tidy", map[string]interface{}{ "safety_buffer": "1s", "tidy_cert_store": false, "tidy_revoked_certs": false, @@ -640,7 +640,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Run with a short safety buffer and both set to true, both should be cleared { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"tidy", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"tidy", map[string]interface{}{ "safety_buffer": "1s", "tidy_cert_store": true, "tidy_revoked_certs": true, diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 301f4534d617..11ac905e069e 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -673,6 +673,11 @@ func signCert(b *backend, return nil, errutil.UserError{Err: fmt.Sprintf("certificate request could not be parsed: %v", err)} } + // This switch validates that the CSR key type matches the role and sets + // the value in the actualKeyType/actualKeyBits values. + actualKeyType := "" + actualKeyBits := 0 + switch data.role.KeyType { case "rsa": // Verify that the key matches the role type @@ -681,24 +686,14 @@ func signCert(b *backend, "role requires keys of type %s", data.role.KeyType)} } + pubKey, ok := csr.PublicKey.(*rsa.PublicKey) if !ok { return nil, errutil.UserError{Err: "could not parse CSR's public key"} } - // Verify that the key is at least 2048 bits - if pubKey.N.BitLen() < 2048 { - return nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} - } - - // Verify that the bit size is at least the size specified in the role - if pubKey.N.BitLen() < data.role.KeyBits { - return nil, errutil.UserError{Err: fmt.Sprintf( - "role requires a minimum of a %d-bit key, but CSR's key is %d bits", - data.role.KeyBits, - pubKey.N.BitLen())} - } - + actualKeyType = "rsa" + actualKeyBits = pubKey.N.BitLen() case "ec": // Verify that the key matches the role type if csr.PublicKeyAlgorithm != x509.ECDSA { @@ -711,14 +706,8 @@ func signCert(b *backend, return nil, errutil.UserError{Err: "could not parse CSR's public key"} } - // Verify that the bit size is at least the size specified in the role - if pubKey.Params().BitSize < data.role.KeyBits { - return nil, errutil.UserError{Err: fmt.Sprintf( - "role requires a minimum of a %d-bit key, but CSR's key is %d bits", - data.role.KeyBits, - pubKey.Params().BitSize)} - } - + actualKeyType = "ec" + actualKeyBits = pubKey.Params().BitSize case "ed25519": // Verify that the key matches the role type if csr.PublicKeyAlgorithm != x509.Ed25519 { @@ -726,29 +715,105 @@ func signCert(b *backend, "role requires keys of type %s", data.role.KeyType)} } + _, ok := csr.PublicKey.(ed25519.PublicKey) if !ok { return nil, errutil.UserError{Err: "could not parse CSR's public key"} } + actualKeyType = "ed25519" + actualKeyBits = 0 case "any": - // We only care about running RSA < 2048 bit checks, so if not RSA - // break out - if csr.PublicKeyAlgorithm != x509.RSA { - break + // We need to compute the actual key type and key bits, to correctly + // validate minimums and SignatureBits below. + switch csr.PublicKeyAlgorithm { + case x509.RSA: + pubKey, ok := csr.PublicKey.(*rsa.PublicKey) + if !ok { + return nil, errutil.UserError{Err: "could not parse CSR's public key"} + } + if pubKey.N.BitLen() < 2048 { + return nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} + } + + actualKeyType = "rsa" + actualKeyBits = pubKey.N.BitLen() + case x509.ECDSA: + pubKey, ok := csr.PublicKey.(*ecdsa.PublicKey) + if !ok { + return nil, errutil.UserError{Err: "could not parse CSR's public key"} + } + + actualKeyType = "ec" + actualKeyBits = pubKey.Params().BitSize + case x509.Ed25519: + _, ok := csr.PublicKey.(ed25519.PublicKey) + if !ok { + return nil, errutil.UserError{Err: "could not parse CSR's public key"} + } + + actualKeyType = "ed25519" + actualKeyBits = 0 + default: + return nil, errutil.UserError{Err: "Unknown key type in CSR: " + csr.PublicKeyAlgorithm.String()} } + default: + return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported key type value: %s", data.role.KeyType)} + } - // Run RSA < 2048 bit checks - pubKey, ok := csr.PublicKey.(*rsa.PublicKey) - if !ok { - return nil, errutil.UserError{Err: "could not parse CSR's public key"} + // Before validating key lengths, update our KeyBits/SignatureBits based + // on the actual CSR key type. + if data.role.KeyType == "any" { + // We update the value of KeyBits and SignatureBits here (from the + // role), using the specified key type. This allows us to convert + // the default value (0) for SignatureBits and KeyBits to a + // meaningful value. In the event KeyBits takes a zero value, we also + // update that to a new value. + // + // This is mandatory because on some roles, with KeyType any, we'll + // set a default SignatureBits to 0, but this will need to be updated + // in order to behave correctly during signing. + roleBitsWasZero := data.role.KeyBits == 0 + if data.role.KeyBits, data.role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(actualKeyType, data.role.KeyBits, data.role.SignatureBits); err != nil { + return nil, errutil.InternalError{Err: fmt.Sprintf("unknown internal error updating default values: %v", err)} } - if pubKey.N.BitLen() < 2048 { - return nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} + + // We're using the KeyBits field as a minimum value, and P-224 is safe + // and a previously allowed value. However, the above call defaults + // to P-256 as that's a saner default than P-224 (w.r.t. generation). + // So, override our fake Role value if it was previously zero. + if actualKeyType == "ec" && roleBitsWasZero { + data.role.KeyBits = 224 } + } - default: - return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported key type value: %s", data.role.KeyType)} + // At this point, data.role.KeyBits and data.role.SignatureBits should both + // be non-zero, for RSA and ECDSA keys. Validate the actualKeyBits based on + // the role's values. If the KeyType was any, and KeyBits was set to 0, + // KeyBits should be updated to 2048 unless some other value was chosen + // explicitly. + // + // This validation needs to occur regardless of the role's key type, so + // that we always validate both RSA and ECDSA key sizes. + if actualKeyType == "rsa" { + if actualKeyBits < data.role.KeyBits { + return nil, errutil.UserError{Err: fmt.Sprintf( + "role requires a minimum of a %d-bit key, but CSR's key is %d bits", + data.role.KeyBits, actualKeyBits)} + } + + if actualKeyBits < 2048 { + return nil, errutil.UserError{Err: fmt.Sprintf( + "Vault requires a minimum of a 2048-bit key, but CSR's key is %d bits", + actualKeyBits)} + } + } else if actualKeyType == "ec" { + if actualKeyBits < data.role.KeyBits { + return nil, errutil.UserError{Err: fmt.Sprintf( + "role requires a minimum of a %d-bit key, but CSR's key is %d bits", + data.role.KeyBits, + actualKeyBits)} + } } creation, err := generateCreationBundle(b, data, caSign, csr) diff --git a/builtin/logical/pki/crl_test.go b/builtin/logical/pki/crl_test.go index 867c8fcc49e9..4f52050bba7b 100644 --- a/builtin/logical/pki/crl_test.go +++ b/builtin/logical/pki/crl_test.go @@ -3,12 +3,15 @@ package pki import ( "context" "crypto/x509" + "crypto/x509/pkix" "testing" + "time" "github.com/hashicorp/vault/api" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault" + "github.com/stretchr/testify/require" ) func TestBackend_CRL_EnableDisable(t *testing.T) { @@ -25,7 +28,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -33,7 +36,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { }, }) - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -42,7 +45,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { } caSerial := resp.Data["serial_number"] - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allow_bare_domains": true, "allow_subdomains": true, "allowed_domains": "foobar.com", @@ -54,7 +57,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { serials := make(map[int]string) for i := 0; i < 6; i++ { - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{ + resp, err := client.Logical().Write("pki/issue/test", map[string]interface{}{ "common_name": "test.foobar.com", }) if err != nil { @@ -64,30 +67,22 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { } test := func(num int) { - resp, err := client.Logical().ReadWithContext(context.Background(), "pki/cert/crl") - if err != nil { - t.Fatal(err) - } - crlPem := resp.Data["certificate"].(string) - certList, err := x509.ParseCRL([]byte(crlPem)) - if err != nil { - t.Fatal(err) - } - lenList := len(certList.TBSCertList.RevokedCertificates) + certList := getCrlCertificateList(t, client) + lenList := len(certList.RevokedCertificates) if lenList != num { - t.Fatalf("expected %d, found %d", num, lenList) + t.Fatalf("expected %d revoked certificates, found %d", num, lenList) } } revoke := func(num int) { - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/revoke", map[string]interface{}{ + resp, err = client.Logical().Write("pki/revoke", map[string]interface{}{ "serial_number": serials[num], }) if err != nil { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/revoke", map[string]interface{}{ + resp, err = client.Logical().Write("pki/revoke", map[string]interface{}{ "serial_number": caSerial, }) if err == nil { @@ -96,7 +91,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { } toggle := func(disabled bool) { - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/crl", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/crl", map[string]interface{}{ "disable": disabled, }) if err != nil { @@ -122,4 +117,23 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { test(0) toggle(false) test(6) + + // The rotate command should reset the update time of the CRL. + crlCreationTime1 := getCrlCertificateList(t, client).ThisUpdate + time.Sleep(1 * time.Second) + _, err = client.Logical().Read("pki/crl/rotate") + require.NoError(t, err) + + crlCreationTime2 := getCrlCertificateList(t, client).ThisUpdate + require.NotEqual(t, crlCreationTime1, crlCreationTime2) +} + +func getCrlCertificateList(t *testing.T, client *api.Client) pkix.TBSCertificateList { + resp, err := client.Logical().ReadWithContext(context.Background(), "pki/cert/crl") + require.NoError(t, err) + + crlPem := resp.Data["certificate"].(string) + certList, err := x509.ParseCRL([]byte(crlPem)) + require.NoError(t, err) + return certList.TBSCertList } diff --git a/builtin/logical/ssh/backend_test.go b/builtin/logical/ssh/backend_test.go index 2137468c9a5e..adee82aa1b83 100644 --- a/builtin/logical/ssh/backend_test.go +++ b/builtin/logical/ssh/backend_test.go @@ -1464,14 +1464,14 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { client := cluster.Cores[0].Client // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } userpassAccessor := auths["userpass/"].Accessor // Write SSH role. - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/test", map[string]interface{}{ "key_type": "ca", "allowed_extensions": "login@zipzap.com", "allow_user_certificates": true, @@ -1490,7 +1490,7 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { // Issue SSH certificate with default extensions templating enabled, and no user-provided extensions client.SetToken(userpassToken) - resp, err := client.Logical().WriteWithContext(context.Background(), "ssh/sign/test", map[string]interface{}{ + resp, err := client.Logical().Write("ssh/sign/test", map[string]interface{}{ "public_key": publicKey4096, }) if err != nil { @@ -1518,7 +1518,7 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { userProvidedExtensionPermissions := map[string]string{ "login@zipzap.com": "some_other_user_name", } - resp, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test", map[string]interface{}{ + resp, err = client.Logical().Write("ssh/sign/test", map[string]interface{}{ "public_key": publicKey4096, "extensions": userProvidedExtensionPermissions, }) @@ -1542,7 +1542,7 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { invalidUserProvidedExtensionPermissions := map[string]string{ "login@foobar.com": "{{identity.entity.metadata}}", } - resp, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test", map[string]interface{}{ + resp, err = client.Logical().Write("ssh/sign/test", map[string]interface{}{ "public_key": publicKey4096, "extensions": invalidUserProvidedExtensionPermissions, }) @@ -1557,7 +1557,7 @@ func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) { client := cluster.Cores[0].Client // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -1565,7 +1565,7 @@ func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) { // Write SSH role to test with no allowed extension. We also provide a templated default extension, // to verify that it's not actually being evaluated - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/test_allow_all_extensions", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/test_allow_all_extensions", map[string]interface{}{ "key_type": "ca", "allow_user_certificates": true, "allowed_users": "tuber", @@ -1585,7 +1585,7 @@ func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) { userProvidedAnyExtensionPermissions := map[string]string{ "login@foobar.com": "not_userpassname", } - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test_allow_all_extensions", map[string]interface{}{ + _, err = client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{ "public_key": publicKey4096, "extensions": userProvidedAnyExtensionPermissions, }) @@ -1604,7 +1604,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { client := cluster.Cores[0].Client // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -1612,7 +1612,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { // Write SSH role to test with any extension. We also provide a templated default extension, // to verify that it's not actually being evaluated - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/test_allow_all_extensions", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/test_allow_all_extensions", map[string]interface{}{ "key_type": "ca", "allow_user_certificates": true, "allowed_users": "tuber", @@ -1635,7 +1635,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { "login@foobar.com": "{{identity.entity.aliases." + userpassAccessor + ".name}}", "login@zipzap.com": "some_other_user_name", } - resp, err := client.Logical().WriteWithContext(context.Background(), "ssh/sign/test_allow_all_extensions", map[string]interface{}{ + resp, err := client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{ "public_key": publicKey4096, "extensions": defaultExtensionPermissions, }) @@ -1661,7 +1661,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { "login@foobar.com": "not_userpassname", "login@zipzap.com": "some_other_user_name", } - resp, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test_allow_all_extensions", map[string]interface{}{ + resp, err = client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{ "public_key": publicKey4096, "extensions": userProvidedAnyExtensionPermissions, }) @@ -1698,7 +1698,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, client := cluster.Cores[0].Client // Write test policy for userpass auth method. - err := client.Sys().PutPolicyWithContext(context.Background(), "test", ` + err := client.Sys().PutPolicy("test", ` path "ssh/*" { capabilities = ["update"] }`) @@ -1712,7 +1712,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, } // Configure test role for userpass. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/"+userIdentity, map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/"+userIdentity, map[string]interface{}{ "password": "test", "policies": "test", }); err != nil { @@ -1720,7 +1720,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, } // Login userpass for test role and keep client token. - secret, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/login/"+userIdentity, map[string]interface{}{ + secret, err := client.Logical().Write("auth/userpass/login/"+userIdentity, map[string]interface{}{ "password": "test", }) if err != nil || secret == nil { @@ -1729,7 +1729,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, userpassToken := secret.Auth.ClientToken // Mount SSH. - err = client.Sys().MountWithContext(context.Background(), "ssh", &api.MountInput{ + err = client.Sys().Mount("ssh", &api.MountInput{ Type: "ssh", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -1741,7 +1741,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, } // Configure SSH CA. - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("ssh/config/ca", map[string]interface{}{ "public_key": testCAPublicKey, "private_key": testCAPrivateKey, }) @@ -1759,21 +1759,21 @@ func testAllowedUsersTemplate(t *testing.T, testAllowedUsersTemplate string, client := cluster.Cores[0].Client // set metadata "ssh_username" to userpass username - tokenLookupResponse, err := client.Logical().WriteWithContext(context.Background(), "/auth/token/lookup", map[string]interface{}{ + tokenLookupResponse, err := client.Logical().Write("/auth/token/lookup", map[string]interface{}{ "token": userpassToken, }) if err != nil { t.Fatal(err) } entityID := tokenLookupResponse.Data["entity_id"].(string) - _, err = client.Logical().WriteWithContext(context.Background(), "/identity/entity/id/"+entityID, map[string]interface{}{ + _, err = client.Logical().Write("/identity/entity/id/"+entityID, map[string]interface{}{ "metadata": testEntityMetadata, }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/my-role", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/my-role", map[string]interface{}{ "key_type": testCaKeyType, "allow_user_certificates": true, "allowed_users": testAllowedUsersTemplate, @@ -1785,7 +1785,7 @@ func testAllowedUsersTemplate(t *testing.T, testAllowedUsersTemplate string, // sign SSH key as userpass user client.SetToken(userpassToken) - signResponse, err := client.Logical().WriteWithContext(context.Background(), "ssh/sign/my-role", map[string]interface{}{ + signResponse, err := client.Logical().Write("ssh/sign/my-role", map[string]interface{}{ "public_key": testCAPublicKey, "valid_principals": expectedValidPrincipal, }) diff --git a/builtin/logical/transit/path_config_test.go b/builtin/logical/transit/path_config_test.go index 87f665104ca6..f6dee45090dc 100644 --- a/builtin/logical/transit/path_config_test.go +++ b/builtin/logical/transit/path_config_test.go @@ -348,7 +348,7 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) { cores := cluster.Cores vault.TestWaitActive(t, cores[0].Core) client := cores[0].Client - err := client.Sys().MountWithContext(context.Background(), "transit", &api.MountInput{ + err := client.Sys().Mount("transit", &api.MountInput{ Type: "transit", }) if err != nil { @@ -363,13 +363,13 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) { } keyName := hex.EncodeToString(keyNameBytes) - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ + _, err = client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ "auto_rotate_period": test.initialAutoRotatePeriod, }) if err != nil { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("transit/keys/%s/config", keyName), map[string]interface{}{ + resp, err := client.Logical().Write(fmt.Sprintf("transit/keys/%s/config", keyName), map[string]interface{}{ "auto_rotate_period": test.newAutoRotatePeriod, }) switch { @@ -380,7 +380,7 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) { } if !test.shouldError { - resp, err = client.Logical().ReadWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName)) + resp, err = client.Logical().Read(fmt.Sprintf("transit/keys/%s", keyName)) if err != nil { t.Fatal(err) } diff --git a/builtin/logical/transit/path_keys_test.go b/builtin/logical/transit/path_keys_test.go index 3d91e6608e62..04c1d8da092d 100644 --- a/builtin/logical/transit/path_keys_test.go +++ b/builtin/logical/transit/path_keys_test.go @@ -1,7 +1,6 @@ package transit_test import ( - "context" "encoding/hex" "encoding/json" "fmt" @@ -40,7 +39,7 @@ func TestTransit_Issue_2958(t *testing.T) { client := cores[0].Client - err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "/dev/null", @@ -50,45 +49,45 @@ func TestTransit_Issue_2958(t *testing.T) { t.Fatal(err) } - err = client.Sys().MountWithContext(context.Background(), "transit", &api.MountInput{ + err = client.Sys().Mount("transit", &api.MountInput{ Type: "transit", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "transit/keys/foo", map[string]interface{}{ + _, err = client.Logical().Write("transit/keys/foo", map[string]interface{}{ "type": "ecdsa-p256", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "transit/keys/foobar", map[string]interface{}{ + _, err = client.Logical().Write("transit/keys/foobar", map[string]interface{}{ "type": "ecdsa-p384", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "transit/keys/bar", map[string]interface{}{ + _, err = client.Logical().Write("transit/keys/bar", map[string]interface{}{ "type": "ed25519", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().ReadWithContext(context.Background(), "transit/keys/foo") + _, err = client.Logical().Read("transit/keys/foo") if err != nil { t.Fatal(err) } - _, err = client.Logical().ReadWithContext(context.Background(), "transit/keys/foobar") + _, err = client.Logical().Read("transit/keys/foobar") if err != nil { t.Fatal(err) } - _, err = client.Logical().ReadWithContext(context.Background(), "transit/keys/bar") + _, err = client.Logical().Read("transit/keys/bar") if err != nil { t.Fatal(err) } @@ -145,7 +144,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { cores := cluster.Cores vault.TestWaitActive(t, cores[0].Core) client := cores[0].Client - err := client.Sys().MountWithContext(context.Background(), "transit", &api.MountInput{ + err := client.Sys().Mount("transit", &api.MountInput{ Type: "transit", }) if err != nil { @@ -160,7 +159,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { } keyName := hex.EncodeToString(keyNameBytes) - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ + _, err = client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ "auto_rotate_period": test.autoRotatePeriod, }) switch { @@ -171,7 +170,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { } if !test.shouldError { - resp, err := client.Logical().ReadWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName)) + resp, err := client.Logical().Read(fmt.Sprintf("transit/keys/%s", keyName)) if err != nil { t.Fatal(err) } diff --git a/changelog/14424.txt b/changelog/14424.txt new file mode 100644 index 000000000000..cc54058f91c4 --- /dev/null +++ b/changelog/14424.txt @@ -0,0 +1,3 @@ +```release-note:bug +agent: Fix log level mismatch between ERR and ERROR +``` diff --git a/changelog/14753.txt b/changelog/14753.txt new file mode 100644 index 000000000000..af7fbb5344d5 --- /dev/null +++ b/changelog/14753.txt @@ -0,0 +1,3 @@ +```release-note:improvement +api: Add ability to pass certificate as PEM bytes to api.Client. +``` diff --git a/changelog/14775.txt b/changelog/14775.txt new file mode 100644 index 000000000000..03beb827a25b --- /dev/null +++ b/changelog/14775.txt @@ -0,0 +1,3 @@ +```release-note:improvement +api: Use the context passed to the api/auth Login helpers. +``` diff --git a/changelog/14794.txt b/changelog/14794.txt new file mode 100644 index 000000000000..fb454922201b --- /dev/null +++ b/changelog/14794.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix KV secret showing in the edit form after a user creates a new version but doesn't have read capabilities +``` \ No newline at end of file diff --git a/changelog/14807.txt b/changelog/14807.txt new file mode 100644 index 000000000000..3c338a21155d --- /dev/null +++ b/changelog/14807.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Alternative flag-based syntax for KV to mitigate confusion from automatically appended /data +``` \ No newline at end of file diff --git a/changelog/14817.txt b/changelog/14817.txt new file mode 100644 index 000000000000..9b8e39ac4c48 --- /dev/null +++ b/changelog/14817.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core : check uid and permissions of config dir, config file, plugin dir and plugin binaries +``` \ No newline at end of file diff --git a/changelog/14836.txt b/changelog/14836.txt new file mode 100644 index 000000000000..6ee8d54a2929 --- /dev/null +++ b/changelog/14836.txt @@ -0,0 +1,3 @@ +```release-note:bug +api: Respect increment value in grace period calculations in LifetimeWatcher +``` diff --git a/changelog/14846.txt b/changelog/14846.txt new file mode 100644 index 000000000000..10621ff4fba1 --- /dev/null +++ b/changelog/14846.txt @@ -0,0 +1,3 @@ +```release-note:bug +core: fixing excessive unix file permissions on dir, files and archive created by vault debug command +``` \ No newline at end of file diff --git a/changelog/14875.txt b/changelog/14875.txt new file mode 100644 index 000000000000..ef4622d2e874 --- /dev/null +++ b/changelog/14875.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/pki: Fix handling of "any" key type with default zero signature bits value. +``` diff --git a/changelog/14916.txt b/changelog/14916.txt new file mode 100644 index 000000000000..d61065a8de48 --- /dev/null +++ b/changelog/14916.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes issue logging in with OIDC from a listed auth mounts tab +``` \ No newline at end of file diff --git a/changelog/14941.txt b/changelog/14941.txt new file mode 100644 index 000000000000..f82b63366854 --- /dev/null +++ b/changelog/14941.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix issue with KV not recomputing model when you changed versions. +``` diff --git a/changelog/14943.txt b/changelog/14943.txt new file mode 100644 index 000000000000..1501c563d728 --- /dev/null +++ b/changelog/14943.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/pki: Fixed bug where larger SHA-2 hashes were truncated with shorter ECDSA CA certificates +``` diff --git a/changelog/14954.txt b/changelog/14954.txt new file mode 100644 index 000000000000..fc8be705694e --- /dev/null +++ b/changelog/14954.txt @@ -0,0 +1,3 @@ +```release-note:change +auth/aws: Add RoleSession to DisplayName when using assumeRole for authentication +``` \ No newline at end of file diff --git a/changelog/14962.txt b/changelog/14962.txt new file mode 100644 index 000000000000..b8bea4582b17 --- /dev/null +++ b/changelog/14962.txt @@ -0,0 +1,6 @@ +```release-note:improvement +api: If the parameters supplied over the API payload are ignored due to not +being what the endpoints were expecting, or if the parameters supplied get +replaced by the values in the endpoint's path itself, warnings will be added to +the non-empty responses listing all the ignored and replaced parameters. +``` diff --git a/changelog/14966.txt b/changelog/14966.txt new file mode 100644 index 000000000000..889294400a2c --- /dev/null +++ b/changelog/14966.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes edit auth method capabilities issue +``` \ No newline at end of file diff --git a/changelog/14968.txt b/changelog/14968.txt new file mode 100644 index 000000000000..a9a5e76fae26 --- /dev/null +++ b/changelog/14968.txt @@ -0,0 +1,3 @@ +```release-note:bug +api: Fixes bug where OutputCurlString field was unintentionally being copied over during client cloning +``` diff --git a/changelog/14977.txt b/changelog/14977.txt new file mode 100644 index 000000000000..937c5d779cc3 --- /dev/null +++ b/changelog/14977.txt @@ -0,0 +1,3 @@ +```release-note:bug +raft: Ensure initialMmapSize is set to 0 on Windows +``` diff --git a/command/agent/alicloud_end_to_end_test.go b/command/agent/alicloud_end_to_end_test.go index e4660f99e9b5..1684ecae4ad7 100644 --- a/command/agent/alicloud_end_to_end_test.go +++ b/command/agent/alicloud_end_to_end_test.go @@ -60,7 +60,7 @@ func TestAliCloudEndToEnd(t *testing.T) { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/alicloud/role/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/alicloud/role/test", map[string]interface{}{ "arn": os.Getenv(envVarAlicloudRoleArn), }); err != nil { t.Fatal(err) diff --git a/command/agent/approle_end_to_end_test.go b/command/agent/approle_end_to_end_test.go index 382073110efa..35186cd8e606 100644 --- a/command/agent/approle_end_to_end_test.go +++ b/command/agent/approle_end_to_end_test.go @@ -93,7 +93,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ "bind_secret_id": bindSecretID, "token_ttl": "6s", "token_max_ttl": "10s", @@ -109,7 +109,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo secretID1 := "" secretID2 := "" if bindSecretID { - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } @@ -117,13 +117,13 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo } else { logger.Trace("skipped write to auth/approle/role/test1/secret-id") } - resp, err := client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err := client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } roleID1 := resp.Data["role_id"].(string) - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test2", addConstraints(!bindSecretID, map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test2", addConstraints(!bindSecretID, map[string]interface{}{ "bind_secret_id": bindSecretID, "token_ttl": "6s", "token_max_ttl": "10s", @@ -132,7 +132,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo t.Fatal(err) } if bindSecretID { - resp, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test2/secret-id", nil) + resp, err = client.Logical().Write("auth/approle/role/test2/secret-id", nil) if err != nil { t.Fatal(err) } @@ -140,7 +140,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo } else { logger.Trace("skipped write to auth/approle/role/test2/secret-id") } - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test2/role-id") + resp, err = client.Logical().Read("auth/approle/role/test2/role-id") if err != nil { t.Fatal(err) } @@ -321,7 +321,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo } } client.SetToken(string(val)) - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -345,7 +345,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -385,7 +385,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -455,7 +455,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ "bind_secret_id": bindSecretID, "token_ttl": "6s", "token_max_ttl": "10s", @@ -474,7 +474,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, secret := "" secretID1 := "" if bindSecretID { - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } @@ -482,7 +482,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, } else { logger.Trace("skipped write to auth/approle/role/test1/secret-id") } - resp, err := client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err := client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -664,7 +664,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, } client.SetToken(string(val)) - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -690,7 +690,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -708,7 +708,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, logger.Trace("origToken set into client", "origToken", origToken) if bindSecretID { - resp, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err = client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } @@ -732,7 +732,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } diff --git a/command/agent/auth/approle/approle.go b/command/agent/auth/approle/approle.go index 8a1a9b3a60d3..e58299ad7b2e 100644 --- a/command/agent/auth/approle/approle.go +++ b/command/agent/auth/approle/approle.go @@ -138,7 +138,7 @@ func (a *approleMethod) Authenticate(ctx context.Context, client *api.Client) (s } clonedClient.SetToken(stringSecretID) // Validate the creation path - resp, err := clonedClient.Logical().Read("sys/wrapping/lookup") + resp, err := clonedClient.Logical().ReadWithContext(ctx, "sys/wrapping/lookup") if err != nil { return "", nil, nil, fmt.Errorf("error looking up wrapped secret ID: %w", err) } @@ -161,7 +161,7 @@ func (a *approleMethod) Authenticate(ctx context.Context, client *api.Client) (s return "", nil, nil, errors.New("unable to validate wrapping token creation path") } // Now get the secret ID - resp, err = clonedClient.Logical().Unwrap("") + resp, err = clonedClient.Logical().UnwrapWithContext(ctx, "") if err != nil { return "", nil, nil, fmt.Errorf("error unwrapping secret ID: %w", err) } diff --git a/command/agent/auth/auth.go b/command/agent/auth/auth.go index 889eedd85bd8..c00028608803 100644 --- a/command/agent/auth/auth.go +++ b/command/agent/auth/auth.go @@ -172,7 +172,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { ah.logger.Debug("lookup-self with preloaded token") clientToUse.SetToken(ah.token) - secret, err = clientToUse.Logical().Read("auth/token/lookup-self") + secret, err = clientToUse.Auth().Token().LookupSelfWithContext(ctx) if err != nil { ah.logger.Error("could not look up token", "err", err, "backoff", backoff) backoffOrQuit(ctx, backoff) @@ -220,7 +220,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { // This should only happen if there's no preloaded token (regular auto-auth login) // or if a preloaded token has expired and is now switching to auto-auth. if secret.Auth == nil { - secret, err = clientToUse.Logical().Write(path, data) + secret, err = clientToUse.Logical().WriteWithContext(ctx, path, data) // Check errors/sanity if err != nil { ah.logger.Error("error authenticating", "error", err, "backoff", backoff) diff --git a/command/agent/auth/auth_test.go b/command/agent/auth/auth_test.go index b2d2c26ad86c..05c24fe1fa39 100644 --- a/command/agent/auth/auth_test.go +++ b/command/agent/auth/auth_test.go @@ -33,7 +33,7 @@ func newUserpassTestMethod(t *testing.T, client *api.Client) AuthMethod { } func (u *userpassTestMethod) Authenticate(_ context.Context, client *api.Client) (string, http.Header, map[string]interface{}, error) { - _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/foo", map[string]interface{}{ + _, err := client.Logical().Write("auth/userpass/users/foo", map[string]interface{}{ "password": "bar", }) if err != nil { diff --git a/command/agent/auto_auth_preload_token_end_to_end_test.go b/command/agent/auto_auth_preload_token_end_to_end_test.go index 8ecabd1158b8..3f8d972a32cf 100644 --- a/command/agent/auto_auth_preload_token_end_to_end_test.go +++ b/command/agent/auto_auth_preload_token_end_to_end_test.go @@ -48,7 +48,7 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { } // Setup Approle - _, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", map[string]interface{}{ + _, err := client.Logical().Write("auth/approle/role/test1", map[string]interface{}{ "bind_secret_id": "true", "token_ttl": "3s", "token_max_ttl": "10s", @@ -58,13 +58,13 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } secretID1 := resp.Data["secret_id"].(string) - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err = client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -108,7 +108,7 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { } // Setup Preload Token - tokenRespRaw, err := client.Logical().WriteWithContext(context.Background(), "auth/token/create", map[string]interface{}{ + tokenRespRaw, err := client.Logical().Write("auth/token/create", map[string]interface{}{ "ttl": "10s", "explicit-max-ttl": "15s", "policies": []string{""}, @@ -222,7 +222,7 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { wrappedToken := map[string]interface{}{ "token": authToken.Token, } - unwrapResp, err := client.Logical().WriteWithContext(context.Background(), "sys/wrapping/unwrap", wrappedToken) + unwrapResp, err := client.Logical().Write("sys/wrapping/unwrap", wrappedToken) if err != nil { t.Fatalf("error unwrapping token: %s", err) } diff --git a/command/agent/aws_end_to_end_test.go b/command/agent/aws_end_to_end_test.go index 71fcd427100c..ca7b419648c7 100644 --- a/command/agent/aws_end_to_end_test.go +++ b/command/agent/aws_end_to_end_test.go @@ -70,7 +70,7 @@ func TestAWSEndToEnd(t *testing.T) { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/aws/role/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/aws/role/test", map[string]interface{}{ "auth_type": "iam", "policies": "default", // Retain thru the account number of the given arn and wildcard the rest. diff --git a/command/agent/cache/cache_test.go b/command/agent/cache/cache_test.go index 0b16b663f9ca..bee5fc0e87f7 100644 --- a/command/agent/cache/cache_test.go +++ b/command/agent/cache/cache_test.go @@ -92,7 +92,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v } // Add an admin policy - if err := activeClient.Sys().PutPolicyWithContext(context.Background(), "admin", policyAdmin); err != nil { + if err := activeClient.Sys().PutPolicy("admin", policyAdmin); err != nil { t.Fatal(err) } @@ -105,7 +105,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v t.Fatal(err) } - _, err = activeClient.Logical().WriteWithContext(context.Background(), "auth/userpass/users/foo", map[string]interface{}{ + _, err = activeClient.Logical().Write("auth/userpass/users/foo", map[string]interface{}{ "password": "bar", "policies": []string{"admin"}, }) @@ -174,7 +174,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v // Login via userpass method to derive a managed token. Set that token as the // testClient's token - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/userpass/login/foo", map[string]interface{}{ + resp, err := testClient.Logical().Write("auth/userpass/login/foo", map[string]interface{}{ "password": "bar", }) if err != nil { @@ -264,7 +264,7 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) { // Empty the token in the client. Auto-auth token should be put to use. testClient.SetToken("") - secret, err := testClient.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := testClient.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -272,7 +272,7 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) { t.Fatalf("failed to strip off auto-auth token on lookup-self") } - secret, err = testClient.Auth().Token().LookupWithContext(context.Background(), "") + secret, err = testClient.Auth().Token().Lookup("") if err != nil { t.Fatal(err) } @@ -329,7 +329,7 @@ func TestCache_AutoAuthClientTokenProxyStripping(t *testing.T) { // Empty the token in the client. Auto-auth token should be put to use. testClient.SetToken(dummyToken) - _, err = testClient.Auth().Token().LookupSelfWithContext(context.Background()) + _, err = testClient.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -351,7 +351,7 @@ func TestCache_ConcurrentRequests(t *testing.T) { cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig) defer cleanup() - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -364,13 +364,13 @@ func TestCache_ConcurrentRequests(t *testing.T) { go func(i int) { defer wg.Done() key := fmt.Sprintf("kv/foo/%d_%d", i, rand.Int()) - _, err := testClient.Logical().WriteWithContext(context.Background(), key, map[string]interface{}{ + _, err := testClient.Logical().Write(key, map[string]interface{}{ "key": key, }) if err != nil { t.Fatal(err) } - secret, err := testClient.Logical().ReadWithContext(context.Background(), key) + secret, err := testClient.Logical().Read(key) if err != nil { t.Fatal(err) } @@ -402,7 +402,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -410,7 +410,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -419,14 +419,14 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -435,14 +435,14 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -451,7 +451,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -469,7 +469,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { // including the child tokens and leases of the child tokens should be // untouched. testClient.SetToken(token2) - err = testClient.Auth().Token().RevokeOrphanWithContext(context.Background(), token2) + err = testClient.Auth().Token().RevokeOrphan(token2) if err != nil { t.Fatal(err) } @@ -503,7 +503,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -511,7 +511,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -520,14 +520,14 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -536,14 +536,14 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -552,7 +552,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -569,7 +569,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { // token, evict entries for all the child tokens and their respective // leases. testClient.SetToken(token3) - err = testClient.Auth().Token().RevokeSelfWithContext(context.Background(), "") + err = testClient.Auth().Token().RevokeSelf("") if err != nil { t.Fatal(err) } @@ -603,7 +603,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -611,7 +611,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -620,14 +620,14 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -636,14 +636,14 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -652,7 +652,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -669,7 +669,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { // belonging to this token, evict entries for all the child tokens and // their respective leases. testClient.SetToken(token2) - err = testClient.Auth().Token().RevokeSelfWithContext(context.Background(), "") + err = testClient.Auth().Token().RevokeSelf("") if err != nil { t.Fatal(err) } @@ -701,7 +701,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -709,7 +709,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -718,14 +718,14 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -734,14 +734,14 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -750,7 +750,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -767,7 +767,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { // to this token, evict entries for all the child tokens and their // respective leases. testClient.SetToken(token1) - err = testClient.Auth().Token().RevokeSelfWithContext(context.Background(), "") + err = testClient.Auth().Token().RevokeSelf("") if err != nil { t.Fatal(err) } @@ -797,7 +797,7 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -805,7 +805,7 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -814,14 +814,14 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -830,14 +830,14 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -846,7 +846,7 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -886,7 +886,7 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -894,7 +894,7 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -903,14 +903,14 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -919,14 +919,14 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -935,7 +935,7 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -972,13 +972,13 @@ func TestCache_NonCacheable(t *testing.T) { defer cleanup() // Query mounts first - origMounts, err := testClient.Sys().ListMountsWithContext(context.Background()) + origMounts, err := testClient.Sys().ListMounts() if err != nil { t.Fatal(err) } // Mount a kv backend - if err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + if err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", Options: map[string]string{ "version": "2", @@ -988,7 +988,7 @@ func TestCache_NonCacheable(t *testing.T) { } // Query mounts again - newMounts, err := testClient.Sys().ListMountsWithContext(context.Background()) + newMounts, err := testClient.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -1020,7 +1020,7 @@ func TestCache_Caching_AuthResponse(t *testing.T) { cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, nil) defer cleanup() - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -1028,7 +1028,7 @@ func TestCache_Caching_AuthResponse(t *testing.T) { testClient.SetToken(token) authTokeCreateReq := func(t *testing.T, policies map[string]interface{}) *api.Secret { - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", policies) + resp, err := testClient.Logical().Write("auth/token/create", policies) if err != nil { t.Fatal(err) } @@ -1079,7 +1079,7 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { cleanup, client, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig) defer cleanup() - err := client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := client.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -1089,14 +1089,14 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { // Test proxy by issuing two different requests { // Write data to the lease-kv backend - _, err := testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err := testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) if err != nil { t.Fatal(err) } - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foobar", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foobar", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -1104,12 +1104,12 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { t.Fatal(err) } - firstResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + firstResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } - secondResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foobar") + secondResp, err := testClient.Logical().Read("kv/foobar") if err != nil { t.Fatal(err) } @@ -1122,7 +1122,7 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { // Test caching behavior by issue the same request twice { - _, err := testClient.Logical().WriteWithContext(context.Background(), "kv/baz", map[string]interface{}{ + _, err := testClient.Logical().Write("kv/baz", map[string]interface{}{ "value": "foo", "ttl": "1h", }) @@ -1130,12 +1130,12 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { t.Fatal(err) } - proxiedResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/baz") + proxiedResp, err := testClient.Logical().Read("kv/baz") if err != nil { t.Fatal(err) } - cachedResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/baz") + cachedResp, err := testClient.Logical().Read("kv/baz") if err != nil { t.Fatal(err) } @@ -1181,7 +1181,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { cleanup, client, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig) defer cleanup() - err := client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := client.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -1189,7 +1189,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { } // Write data to the lease-kv backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -1198,7 +1198,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { } // Proxy this request, agent should cache the response - resp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + resp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -1228,7 +1228,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { case "token": data["value"] = testClient.Token() case "token_accessor": - lookupResp, err := client.Auth().Token().LookupWithContext(context.Background(), testClient.Token()) + lookupResp, err := client.Auth().Token().Lookup(testClient.Token()) if err != nil { t.Fatal(err) } @@ -1285,7 +1285,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { Policies: []string{"default"}, NoParent: true, } - resp, err := testClient.Auth().Token().CreateWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().Create(reqOpts) if err != nil { t.Fatal(err) } @@ -1312,7 +1312,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { // Use the test client but set the token to one that's not managed by agent testClient.SetToken(clusterClient.Token()) - resp, err := testClient.Auth().Token().CreateWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().Create(reqOpts) if err != nil { t.Fatal(err) } @@ -1336,7 +1336,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { reqOpts := &api.TokenCreateRequest{ Policies: []string{"default"}, } - resp, err := testClient.Auth().Token().CreateOrphanWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().CreateOrphan(reqOpts) if err != nil { t.Fatal(err) } @@ -1362,7 +1362,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { // Use the test client but set the token to one that's not managed by agent testClient.SetToken(clusterClient.Token()) - resp, err := testClient.Auth().Token().CreateOrphanWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().CreateOrphan(reqOpts) if err != nil { t.Fatal(err) } diff --git a/command/agent/cache_end_to_end_test.go b/command/agent/cache_end_to_end_test.go index 69d3e3fc0bd7..4ad056a850cc 100644 --- a/command/agent/cache_end_to_end_test.go +++ b/command/agent/cache_end_to_end_test.go @@ -71,7 +71,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { defer os.Setenv(api.EnvVaultCACert, os.Getenv(api.EnvVaultCACert)) os.Setenv(api.EnvVaultCACert, fmt.Sprintf("%s/ca_cert.pem", cluster.TempDir)) - err = client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err = client.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -79,7 +79,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { } // Create a secret in the backend - _, err = client.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = client.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -88,7 +88,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { } // Add an kv-admin policy - if err := client.Sys().PutPolicyWithContext(context.Background(), "test-autoauth", policyAutoAuthAppRole); err != nil { + if err := client.Sys().PutPolicy("test-autoauth", policyAutoAuthAppRole); err != nil { t.Fatal(err) } @@ -100,7 +100,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", map[string]interface{}{ "bind_secret_id": "true", "token_ttl": "3s", "token_max_ttl": "10s", @@ -110,13 +110,13 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } secretID1 := resp.Data["secret_id"].(string) - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err = client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -343,7 +343,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // Empty the token in the client to ensure that auto-auth token is used testClient.SetToken("") - resp, err = testClient.Logical().ReadWithContext(context.Background(), "auth/token/lookup-self") + resp, err = testClient.Logical().Read("auth/token/lookup-self") if err != nil { t.Fatal(err) } @@ -354,14 +354,14 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // This block tests lease creation caching using the auto-auth token. { - resp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + resp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } origReqID := resp.RequestID - resp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + resp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -379,7 +379,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // This block tests auth token creation caching (child, non-orphan tokens) // using the auto-auth token. { - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -388,7 +388,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // Sleep for a bit to allow renewer logic to kick in time.Sleep(20 * time.Millisecond) - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -405,7 +405,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // Empty the token in the client to ensure that auto-auth token is used testClient.SetToken(client.Token()) - resp, err = testClient.Logical().ReadWithContext(context.Background(), "auth/token/lookup-self") + resp, err = testClient.Logical().Read("auth/token/lookup-self") if err != nil { t.Fatal(err) } diff --git a/command/agent/cert_end_to_end_test.go b/command/agent/cert_end_to_end_test.go index 127274b5df30..bacb188021cd 100644 --- a/command/agent/cert_end_to_end_test.go +++ b/command/agent/cert_end_to_end_test.go @@ -90,7 +90,7 @@ func testCertEndToEnd(t *testing.T, withCertRoleName, ahWrapping bool) { certificatePEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cluster.CACert.Raw}) certRoleName := "test" - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("auth/cert/certs/%s", certRoleName), map[string]interface{}{ + _, err = client.Logical().Write(fmt.Sprintf("auth/cert/certs/%s", certRoleName), map[string]interface{}{ "certificate": string(certificatePEM), "policies": "default", }) @@ -327,7 +327,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { // ///////////// // Mount /pki as a root CA - err := client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err := client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -340,7 +340,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { // Set the cluster's certificate as the root CA in /pki pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/ca", map[string]interface{}{ "pem_bundle": pemBundleRootCA, }) if err != nil { @@ -348,7 +348,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Mount /pki2 to operate as an intermediate CA - err = client.Sys().MountWithContext(context.Background(), "pki2", &api.MountInput{ + err = client.Sys().Mount("pki2", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -360,14 +360,14 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Create a CSR for the intermediate CA - secret, err := client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/generate/internal", nil) + secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil) if err != nil { t.Fatal(err) } intermediateCSR := secret.Data["csr"].(string) // Sign the intermediate CSR using /pki - secret, err = client.Logical().WriteWithContext(context.Background(), "pki/root/sign-intermediate", map[string]interface{}{ + secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{ "permitted_dns_domains": ".myvault.com", "csr": intermediateCSR, }) @@ -377,7 +377,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { intermediateCertPEM := secret.Data["certificate"].(string) // Configure the intermediate cert as the CA in /pki2 - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/set-signed", map[string]interface{}{ + _, err = client.Logical().Write("pki2/intermediate/set-signed", map[string]interface{}{ "certificate": intermediateCertPEM, }) if err != nil { @@ -385,7 +385,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Create a role on the intermediate CA mount - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/roles/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("pki2/roles/myvault-dot-com", map[string]interface{}{ "allowed_domains": "myvault.com", "allow_subdomains": "true", "max_ttl": "5m", @@ -395,7 +395,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Issue a leaf cert using the intermediate CA - secret, err = client.Logical().WriteWithContext(context.Background(), "pki2/issue/myvault-dot-com", map[string]interface{}{ + secret, err = client.Logical().Write("pki2/issue/myvault-dot-com", map[string]interface{}{ "common_name": "cert.myvault.com", "format": "pem", "ip_sans": "127.0.0.1", @@ -457,7 +457,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Set the intermediate CA cert as a trusted certificate in the backend - _, err = client.Logical().WriteWithContext(context.Background(), "auth/cert/certs/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("auth/cert/certs/myvault-dot-com", map[string]interface{}{ "display_name": "myvault.com", "policies": "default", "certificate": intermediateCertPEM, diff --git a/command/agent/cf_end_to_end_test.go b/command/agent/cf_end_to_end_test.go index 17963fda729f..6bc1fa8b6a07 100644 --- a/command/agent/cf_end_to_end_test.go +++ b/command/agent/cf_end_to_end_test.go @@ -68,7 +68,7 @@ func TestCFEndToEnd(t *testing.T) { defer mockCFAPI.Close() // Configure a CA certificate like a Vault operator would in setting up CF. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/cf/config", map[string]interface{}{ + if _, err := client.Logical().Write("auth/cf/config", map[string]interface{}{ "identity_ca_certificates": testCFCerts.CACertificate, "cf_api_addr": mockCFAPI.URL, "cf_username": cfAPI.AuthUsername, @@ -78,7 +78,7 @@ func TestCFEndToEnd(t *testing.T) { } // Configure a role to be used for logging in, another thing a Vault operator would do. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/cf/roles/test-role", map[string]interface{}{ + if _, err := client.Logical().Write("auth/cf/roles/test-role", map[string]interface{}{ "bound_instance_ids": cfAPI.FoundServiceGUID, "bound_organization_ids": cfAPI.FoundOrgGUID, "bound_space_ids": cfAPI.FoundSpaceGUID, diff --git a/command/agent/jwt_end_to_end_test.go b/command/agent/jwt_end_to_end_test.go index 96ea28d67a52..c2d74d9f37dc 100644 --- a/command/agent/jwt_end_to_end_test.go +++ b/command/agent/jwt_end_to_end_test.go @@ -53,7 +53,7 @@ func testJWTEndToEnd(t *testing.T, ahWrapping bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/config", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{ "bound_issuer": "https://team-vault.auth0.com/", "jwt_validation_pubkeys": TestECDSAPubKey, "jwt_supported_algs": "ES256", @@ -62,7 +62,7 @@ func testJWTEndToEnd(t *testing.T, ahWrapping bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/role/test", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{ "role_type": "jwt", "bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients", "bound_audiences": "https://vault.plugin.auth.jwt.test", diff --git a/command/agent/template/template.go b/command/agent/template/template.go index 3ad546787c81..cf21944ab0a8 100644 --- a/command/agent/template/template.go +++ b/command/agent/template/template.go @@ -341,7 +341,7 @@ func logLevelToStringPtr(level hclog.Level) *string { case hclog.Warn: levelStr = "WARN" case hclog.Error: - levelStr = "ERROR" + levelStr = "ERR" default: levelStr = "INFO" } diff --git a/command/agent/template/template_test.go b/command/agent/template/template_test.go index 9f0464730e11..77f166affc08 100644 --- a/command/agent/template/template_test.go +++ b/command/agent/template/template_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -188,7 +187,7 @@ func TestCacheConfigNoListener(t *testing.T) { assert.NotNil(t, ctConfig.Vault.Transport.CustomDialer) } -func TestServerRun(t *testing.T) { +func createHttpTestServer() *httptest.Server { // create http test server mux := http.NewServeMux() mux.HandleFunc("/v1/kv/myapp/config", func(w http.ResponseWriter, r *http.Request) { @@ -203,9 +202,14 @@ func TestServerRun(t *testing.T) { fmt.Fprintln(w, `{"errors":["1 error occurred:\n\t* permission denied\n\n"]}`) }) - ts := httptest.NewServer(mux) + return httptest.NewServer(mux) +} + +func TestServerRun(t *testing.T) { + ts := createHttpTestServer() defer ts.Close() - tmpDir, err := ioutil.TempDir("", "agent-tests") + + tmpDir, err := os.MkdirTemp("", "agent-tests") defer os.RemoveAll(tmpDir) if err != nil { t.Fatal(err) @@ -379,7 +383,7 @@ func TestServerRun(t *testing.T) { if template.Destination == nil { t.Fatal("nil template destination") } - content, err := ioutil.ReadFile(*template.Destination) + content, err := os.ReadFile(*template.Destination) if err != nil { t.Fatal(err) } @@ -400,6 +404,70 @@ func TestServerRun(t *testing.T) { } } +// TestNewServerLogLevels tests that the server can be started with any log +// level. +func TestNewServerLogLevels(t *testing.T) { + ts := createHttpTestServer() + defer ts.Close() + + tmpDir, err := os.MkdirTemp("", "agent-tests") + defer os.RemoveAll(tmpDir) + if err != nil { + t.Fatal(err) + } + + levels := []hclog.Level{hclog.NoLevel, hclog.Trace, hclog.Debug, hclog.Info, hclog.Warn, hclog.Error} + for _, level := range levels { + name := fmt.Sprintf("log_%s", level) + t.Run(name, func(t *testing.T) { + server := NewServer(&ServerConfig{ + Logger: logging.NewVaultLogger(level), + LogWriter: hclog.DefaultOutput, + LogLevel: level, + ExitAfterAuth: true, + AgentConfig: &config.Config{ + Vault: &config.Vault{ + Address: ts.URL, + }, + }, + }) + if server == nil { + t.Fatal("nil server returned") + } + defer server.Stop() + + templateTokenCh := make(chan string, 1) + + templateTest := &ctconfig.TemplateConfig{ + Contents: pointerutil.StringPtr(templateContents), + } + dstFile := fmt.Sprintf("%s/%s", tmpDir, name) + templateTest.Destination = pointerutil.StringPtr(dstFile) + templatesToRender := []*ctconfig.TemplateConfig{templateTest} + + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + errCh := make(chan error) + go func() { + errCh <- server.Run(ctx, templateTokenCh, templatesToRender) + }() + + // send a dummy value to trigger auth so the server will exit + templateTokenCh <- "test" + + select { + case <-ctx.Done(): + t.Fatal("timeout reached before templates were rendered") + case err := <-errCh: + if err != nil { + t.Fatalf("did not expect error, got: %v", err) + } + } + }) + } +} + var jsonResponse = ` { "request_id": "8af096e9-518c-7351-eff5-5ba20554b21f", diff --git a/command/agent_test.go b/command/agent_test.go index 2f191aa951a7..4b62020e1dfb 100644 --- a/command/agent_test.go +++ b/command/agent_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -76,7 +75,7 @@ func TestAgent_Cache_UnixListener(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/config", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{ "bound_issuer": "https://team-vault.auth0.com/", "jwt_validation_pubkeys": agent.TestECDSAPubKey, }) @@ -84,7 +83,7 @@ func TestAgent_Cache_UnixListener(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/role/test", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{ "role_type": "jwt", "bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients", "bound_audiences": "https://vault.plugin.auth.jwt.test", @@ -219,7 +218,7 @@ cache { time.Sleep(1 * time.Second) // Invoke lookup self through the agent - secret, err := testClient.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := testClient.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -264,7 +263,7 @@ func testAgentExitAfterAuth(t *testing.T, viaFlag bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/config", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{ "bound_issuer": "https://team-vault.auth0.com/", "jwt_validation_pubkeys": agent.TestECDSAPubKey, "jwt_supported_algs": "ES256", @@ -273,7 +272,7 @@ func testAgentExitAfterAuth(t *testing.T, viaFlag bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/role/test", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{ "role_type": "jwt", "bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients", "bound_audiences": "https://vault.plugin.auth.jwt.test", @@ -1307,7 +1306,7 @@ func TestAgent_Template_Retry(t *testing.T) { methodConf, cleanup := prepAgentApproleKV(t, serverClient) defer cleanup() - err := serverClient.Sys().TuneMountWithContext(context.Background(), "secret", api.MountConfigInput{ + err := serverClient.Sys().TuneMount("secret", api.MountConfigInput{ Options: map[string]string{ "version": "2", }, @@ -1316,7 +1315,7 @@ func TestAgent_Template_Retry(t *testing.T) { t.Fatal(err) } - _, err = serverClient.Logical().WriteWithContext(context.Background(), "secret/data/otherapp", map[string]interface{}{ + _, err = serverClient.Logical().Write("secret/data/otherapp", map[string]interface{}{ "data": map[string]interface{}{ "username": "barstuff", "password": "zap", @@ -1500,7 +1499,7 @@ path "/secret/*" { } ` // Add an kv-admin policy - if err := client.Sys().PutPolicyWithContext(context.Background(), "test-autoauth", policyAutoAuthAppRole); err != nil { + if err := client.Sys().PutPolicy("test-autoauth", policyAutoAuthAppRole); err != nil { t.Fatal(err) } @@ -1512,7 +1511,7 @@ path "/secret/*" { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", map[string]interface{}{ "bind_secret_id": "true", "token_ttl": "1h", "token_max_ttl": "2h", @@ -1522,14 +1521,14 @@ path "/secret/*" { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } secretID := resp.Data["secret_id"].(string) secretIDFile := makeTempFile(t, "secret_id.txt", secretID+"\n") - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err = client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -1591,7 +1590,7 @@ func TestAgent_Cache_Retry(t *testing.T) { defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress)) os.Unsetenv(api.EnvVaultAddress) - _, err := serverClient.Logical().WriteWithContext(context.Background(), "secret/foo", map[string]interface{}{ + _, err := serverClient.Logical().Write("secret/foo", map[string]interface{}{ "bar": "baz", }) if err != nil { @@ -1689,7 +1688,7 @@ vault { if err != nil { t.Fatal(err) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/foo") + secret, err := client.Logical().Read("secret/foo") switch { case (err != nil || secret == nil) && tc.expectError: case (err == nil || secret != nil) && !tc.expectError: @@ -1743,7 +1742,7 @@ func TestAgent_TemplateConfig_ExitOnRetryFailure(t *testing.T) { autoAuthConfig, cleanup := prepAgentApproleKV(t, serverClient) defer cleanup() - err := serverClient.Sys().TuneMountWithContext(context.Background(), "secret", api.MountConfigInput{ + err := serverClient.Sys().TuneMount("secret", api.MountConfigInput{ Options: map[string]string{ "version": "2", }, @@ -1752,7 +1751,7 @@ func TestAgent_TemplateConfig_ExitOnRetryFailure(t *testing.T) { t.Fatal(err) } - _, err = serverClient.Logical().WriteWithContext(context.Background(), "secret/data/otherapp", map[string]interface{}{ + _, err = serverClient.Logical().Write("secret/data/otherapp", map[string]interface{}{ "data": map[string]interface{}{ "username": "barstuff", "password": "zap", diff --git a/command/approle_concurrency_integ_test.go b/command/approle_concurrency_integ_test.go index 2b465e586bf2..5dbcce064c8d 100644 --- a/command/approle_concurrency_integ_test.go +++ b/command/approle_concurrency_integ_test.go @@ -45,7 +45,7 @@ func TestAppRole_Integ_ConcurrentLogins(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/role1", map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/role1", map[string]interface{}{ "bind_secret_id": "true", "period": "300", }) @@ -53,13 +53,13 @@ func TestAppRole_Integ_ConcurrentLogins(t *testing.T) { t.Fatal(err) } - secret, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/role1/secret-id", nil) + secret, err := client.Logical().Write("auth/approle/role/role1/secret-id", nil) if err != nil { t.Fatal(err) } secretID := secret.Data["secret_id"].(string) - secret, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/role1/role-id") + secret, err = client.Logical().Read("auth/approle/role/role1/role-id") if err != nil { t.Fatal(err) } diff --git a/command/audit_disable_test.go b/command/audit_disable_test.go index 17043326c481..0a7e8e4dcd99 100644 --- a/command/audit_disable_test.go +++ b/command/audit_disable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -64,7 +63,7 @@ func TestAuditDisableCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", @@ -94,7 +93,7 @@ func TestAuditDisableCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "integration_audit_disable", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("integration_audit_disable", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", @@ -119,7 +118,7 @@ func TestAuditDisableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/audit_enable_test.go b/command/audit_enable_test.go index b0526d5679ed..1f55703c27bf 100644 --- a/command/audit_enable_test.go +++ b/command/audit_enable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "io/ioutil" "os" "strings" @@ -109,7 +108,7 @@ func TestAuditEnableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - audits, err := client.Sys().ListAuditWithContext(context.Background()) + audits, err := client.Sys().ListAudit() if err != nil { t.Fatal(err) } diff --git a/command/audit_list_test.go b/command/audit_list_test.go index 3660f5275c6a..9cbb0af5eee3 100644 --- a/command/audit_list_test.go +++ b/command/audit_list_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -58,7 +57,7 @@ func TestAuditListCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", diff --git a/command/auth_disable_test.go b/command/auth_disable_test.go index 3b1ba9cc9dd9..51419b86637a 100644 --- a/command/auth_disable_test.go +++ b/command/auth_disable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -96,7 +95,7 @@ func TestAuthDisableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/auth_enable_test.go b/command/auth_enable_test.go index a1574a4d22dc..0cc125fc9756 100644 --- a/command/auth_enable_test.go +++ b/command/auth_enable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "io/ioutil" "strings" "testing" @@ -106,7 +105,7 @@ func TestAuthEnableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/auth_move_test.go b/command/auth_move_test.go index 5143e086c5ea..035938efe5aa 100644 --- a/command/auth_move_test.go +++ b/command/auth_move_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -105,7 +104,7 @@ func TestAuthMoveCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListAuthWithContext(context.Background()) + mounts, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/auth_tune_test.go b/command/auth_tune_test.go index b890877ad332..227330ea774e 100644 --- a/command/auth_tune_test.go +++ b/command/auth_tune_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -110,7 +109,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -176,7 +175,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -219,7 +218,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/base_predict_test.go b/command/base_predict_test.go index 65b08b99a04c..12f364106f7a 100644 --- a/command/base_predict_test.go +++ b/command/base_predict_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "reflect" "testing" @@ -17,25 +16,25 @@ func TestPredictVaultPaths(t *testing.T) { defer closer() data := map[string]interface{}{"a": "b"} - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/bar", data); err != nil { + if _, err := client.Logical().Write("secret/bar", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/foo", data); err != nil { + if _, err := client.Logical().Write("secret/foo", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/zap", data); err != nil { + if _, err := client.Logical().Write("secret/zip/zap", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/zonk", data); err != nil { + if _, err := client.Logical().Write("secret/zip/zonk", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/twoot", data); err != nil { + if _, err := client.Logical().Write("secret/zip/twoot", data); err != nil { t.Fatal(err) } - if err := client.Sys().MountWithContext(context.Background(), "level1a/level2a/level3a", &api.MountInput{Type: "kv"}); err != nil { + if err := client.Sys().Mount("level1a/level2a/level3a", &api.MountInput{Type: "kv"}); err != nil { t.Fatal(err) } - if err := client.Sys().MountWithContext(context.Background(), "level1a/level2a/level3b", &api.MountInput{Type: "kv"}); err != nil { + if err := client.Sys().Mount("level1a/level2a/level3b", &api.MountInput{Type: "kv"}); err != nil { t.Fatal(err) } @@ -231,7 +230,7 @@ func TestPredict_Audits(t *testing.T) { badClient, badCloser := testVaultServerBad(t) defer badCloser() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", @@ -496,13 +495,13 @@ func TestPredict_Paths(t *testing.T) { defer closer() data := map[string]interface{}{"a": "b"} - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/bar", data); err != nil { + if _, err := client.Logical().Write("secret/bar", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/foo", data); err != nil { + if _, err := client.Logical().Write("secret/foo", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/zap", data); err != nil { + if _, err := client.Logical().Write("secret/zip/zap", data); err != nil { t.Fatal(err) } @@ -572,10 +571,10 @@ func TestPredict_ListPaths(t *testing.T) { defer badCloser() data := map[string]interface{}{"a": "b"} - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/bar", data); err != nil { + if _, err := client.Logical().Write("secret/bar", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/foo", data); err != nil { + if _, err := client.Logical().Write("secret/foo", data); err != nil { t.Fatal(err) } diff --git a/command/command_test.go b/command/command_test.go index 76ab22575156..8de036e40ffb 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -236,7 +236,7 @@ func testVaultServerBad(tb testing.TB) (*api.Client, func()) { func testTokenAndAccessor(tb testing.TB, client *api.Client) (string, string) { tb.Helper() - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"default"}, TTL: "30m", }) diff --git a/command/debug.go b/command/debug.go index f981b18478f6..e0fd8cd3144c 100644 --- a/command/debug.go +++ b/command/debug.go @@ -8,9 +8,11 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strconv" "strings" "sync" + "syscall" "time" "github.com/hashicorp/go-hclog" @@ -356,7 +358,7 @@ func (c *DebugCommand) generateIndex() error { } // Write out file - if err := ioutil.WriteFile(filepath.Join(c.flagOutput, "index.json"), bytes, 0o644); err != nil { + if err := ioutil.WriteFile(filepath.Join(c.flagOutput, "index.json"), bytes, 0o600); err != nil { return fmt.Errorf("error generating index file; %s", err) } @@ -453,7 +455,7 @@ func (c *DebugCommand) preflight(rawArgs []string) (string, error) { _, err = os.Stat(c.flagOutput) switch { case os.IsNotExist(err): - err := os.MkdirAll(c.flagOutput, 0o755) + err := os.MkdirAll(c.flagOutput, 0o700) if err != nil { return "", fmt.Errorf("unable to create output directory: %s", err) } @@ -741,7 +743,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { // Create a sub-directory for pprof data currentDir := currentTimestamp.Format(fileFriendlyTimeFormat) dirName := filepath.Join(c.flagOutput, currentDir) - if err := os.MkdirAll(dirName, 0o755); err != nil { + if err := os.MkdirAll(dirName, 0o700); err != nil { c.UI.Error(fmt.Sprintf("Error creating sub-directory for time interval: %s", err)) continue } @@ -758,7 +760,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, target+".prof"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, target+".prof"), data, 0o600) if err != nil { c.captureError("pprof."+target, err) } @@ -776,7 +778,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, "goroutines.txt"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, "goroutines.txt"), data, 0o600) if err != nil { c.captureError("pprof.goroutines-text", err) } @@ -800,7 +802,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, "profile.prof"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, "profile.prof"), data, 0o600) if err != nil { c.captureError("pprof.profile", err) } @@ -816,7 +818,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, "trace.out"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, "trace.out"), data, 0o600) if err != nil { c.captureError("pprof.trace", err) } @@ -952,7 +954,7 @@ func (c *DebugCommand) persistCollection(collection []map[string]interface{}, ou if err != nil { return err } - if err := ioutil.WriteFile(filepath.Join(c.flagOutput, outFile), bytes, 0o644); err != nil { + if err := ioutil.WriteFile(filepath.Join(c.flagOutput, outFile), bytes, 0o600); err != nil { return err } @@ -960,6 +962,10 @@ func (c *DebugCommand) persistCollection(collection []map[string]interface{}, ou } func (c *DebugCommand) compress(dst string) error { + if runtime.GOOS != "windows" { + defer syscall.Umask(syscall.Umask(0o077)) + } + tgz := archiver.NewTarGz() if err := tgz.Archive([]string{c.flagOutput}, dst); err != nil { return fmt.Errorf("failed to compress data: %s", err) @@ -1044,7 +1050,7 @@ func (c *DebugCommand) captureError(target string, err error) { } func (c *DebugCommand) writeLogs(ctx context.Context) { - out, err := os.Create(filepath.Join(c.flagOutput, "vault.log")) + out, err := os.OpenFile(filepath.Join(c.flagOutput, "vault.log"), os.O_CREATE, 0o600) if err != nil { c.captureError("log", err) return diff --git a/command/debug_test.go b/command/debug_test.go index 3c6ca4567c07..046474af8660 100644 --- a/command/debug_test.go +++ b/command/debug_test.go @@ -2,13 +2,14 @@ package command import ( "archive/tar" - "context" "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" + "runtime" "strings" + "syscall" "testing" "time" @@ -599,7 +600,7 @@ func TestDebugCommand_OutputExists(t *testing.T) { t.Fatal(err) } } else { - err = os.Mkdir(outputPath, 0o755) + err = os.Mkdir(outputPath, 0o700) if err != nil { t.Fatal(err) } @@ -641,7 +642,7 @@ func TestDebugCommand_PartialPermissions(t *testing.T) { defer closer() // Create a new token with default policy - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/token/create", map[string]interface{}{ + resp, err := client.Logical().Write("auth/token/create", map[string]interface{}{ "policies": "default", }) if err != nil { @@ -705,3 +706,134 @@ func TestDebugCommand_PartialPermissions(t *testing.T) { t.Fatal(err) } } + +// set insecure umask to see if the files and directories get created with right permissions +func TestDebugCommand_InsecureUmask(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("test does not work in windows environment") + } + t.Parallel() + + cases := []struct { + name string + compress bool + outputFile string + expectError bool + }{ + { + "with-compress", + true, + "with-compress.tar.gz", + false, + }, + { + "no-compress", + false, + "no-compress", + false, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + // set insecure umask + defer syscall.Umask(syscall.Umask(0)) + + testDir, err := ioutil.TempDir("", "vault-debug") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(testDir) + + client, closer := testVaultServer(t) + defer closer() + + ui, cmd := testDebugCommand(t) + cmd.client = client + cmd.skipTimingChecks = true + + outputPath := filepath.Join(testDir, tc.outputFile) + + args := []string{ + fmt.Sprintf("-compress=%t", tc.compress), + "-duration=1s", + "-interval=1s", + "-metrics-interval=1s", + fmt.Sprintf("-output=%s", outputPath), + } + + code := cmd.Run(args) + if exp := 0; code != exp { + t.Log(ui.ErrorWriter.String()) + t.Fatalf("expected %d to be %d", code, exp) + } + // If we expect an error we're done here + if tc.expectError { + return + } + + bundlePath := filepath.Join(testDir, tc.outputFile) + fs, err := os.Stat(bundlePath) + if os.IsNotExist(err) { + t.Log(ui.OutputWriter.String()) + t.Fatal(err) + } + // check permissions of the parent debug directory + err = isValidFilePermissions(fs) + if err != nil { + t.Fatalf(err.Error()) + } + + // check permissions of the files within the parent directory + switch tc.compress { + case true: + tgz := archiver.NewTarGz() + + err = tgz.Walk(bundlePath, func(f archiver.File) error { + fh, ok := f.Header.(*tar.Header) + if !ok { + return fmt.Errorf("invalid file header: %#v", f.Header) + } + err = isValidFilePermissions(fh.FileInfo()) + if err != nil { + t.Fatalf(err.Error()) + } + return nil + }) + + case false: + err = filepath.Walk(bundlePath, func(path string, info os.FileInfo, err error) error { + err = isValidFilePermissions(info) + if err != nil { + t.Fatalf(err.Error()) + } + return nil + }) + } + + if err != nil { + t.Fatal(err) + } + }) + } +} + +func isValidFilePermissions(info os.FileInfo) (err error) { + mode := info.Mode() + // check group permissions + for i := 4; i < 7; i++ { + if string(mode.String()[i]) != "-" { + return fmt.Errorf("expected no permissions for group but got %s permissions for file %s", string(mode.String()[i]), info.Name()) + } + } + + // check others permissions + for i := 7; i < 10; i++ { + if string(mode.String()[i]) != "-" { + return fmt.Errorf("expected no permissions for others but got %s permissions for file %s", string(mode.String()[i]), info.Name()) + } + } + return err +} diff --git a/command/delete_test.go b/command/delete_test.go index 5cebe3c55cd9..e26d393b16fe 100644 --- a/command/delete_test.go +++ b/command/delete_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -82,7 +81,7 @@ func TestDeleteCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/delete/foo", map[string]interface{}{ + if _, err := client.Logical().Write("secret/delete/foo", map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) @@ -104,7 +103,7 @@ func TestDeleteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - secret, _ := client.Logical().ReadWithContext(context.Background(), "secret/delete/foo") + secret, _ := client.Logical().Read("secret/delete/foo") if secret != nil { t.Errorf("expected deletion: %#v", secret) } diff --git a/command/kv.go b/command/kv.go index 3fa91c8c586f..2172576dbd6f 100644 --- a/command/kv.go +++ b/command/kv.go @@ -27,19 +27,25 @@ Usage: vault kv [options] [args] Create or update the key named "foo" in the "secret" mount with the value "bar=baz": - $ vault kv put secret/foo bar=baz + $ vault kv put -mount=secret foo bar=baz Read this value back: - $ vault kv get secret/foo + $ vault kv get -mount=secret foo Get metadata for the key: - $ vault kv metadata get secret/foo + $ vault kv metadata get -mount=secret foo Get a specific version of the key: - $ vault kv get -version=1 secret/foo + $ vault kv get -mount=secret -version=1 foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + + $ vault kv get secret/foo Please see the individual subcommand help for detailed usage information. ` diff --git a/command/kv_delete.go b/command/kv_delete.go index 59e558fbf537..65e0927630ad 100644 --- a/command/kv_delete.go +++ b/command/kv_delete.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "path" "strings" "github.com/hashicorp/vault/api" @@ -18,6 +19,7 @@ type KVDeleteCommand struct { *BaseCommand flagVersions []string + flagMount string } func (c *KVDeleteCommand) Synopsis() string { @@ -34,11 +36,17 @@ Usage: vault kv delete [options] PATH To delete the latest version of the key "foo": + $ vault kv delete -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv delete secret/foo To delete version 3 of key foo: - $ vault kv delete -versions=3 secret/foo + $ vault kv delete -mount=secret -versions=3 foo To delete all versions and metadata, see the "vault kv metadata" subcommand. @@ -61,6 +69,17 @@ func (c *KVDeleteCommand) Flags() *FlagSets { Usage: `Specifies the version numbers to delete.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -96,22 +115,54 @@ func (c *KVDeleteCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } var secret *api.Secret + var fullPath string if v2 { - secret, err = c.deleteV2(path, mountPath, client) + secret, err = c.deleteV2(partialPath, mountPath, client) + fullPath = addPrefixToKVPath(partialPath, mountPath, "data") } else { - secret, err = client.Logical().Delete(path) + // v1 + if mountFlagSyntax { + fullPath = path.Join(mountPath, partialPath) + } else { + fullPath = partialPath + } + secret, err = client.Logical().Delete(fullPath) } if err != nil { - c.UI.Error(fmt.Sprintf("Error deleting %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error deleting %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -121,7 +172,7 @@ func (c *KVDeleteCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", fullPath)) } return 0 } @@ -139,22 +190,12 @@ func (c *KVDeleteCommand) deleteV2(path, mountPath string, client *api.Client) ( switch { case len(c.flagVersions) > 0: path = addPrefixToKVPath(path, mountPath, "delete") - if err != nil { - return nil, err - } - data := map[string]interface{}{ "versions": kvParseVersionsFlags(c.flagVersions), } - secret, err = client.Logical().Write(path, data) default: - path = addPrefixToKVPath(path, mountPath, "data") - if err != nil { - return nil, err - } - secret, err = client.Logical().Delete(path) } diff --git a/command/kv_destroy.go b/command/kv_destroy.go index 53a1416a786e..33f5452a74e0 100644 --- a/command/kv_destroy.go +++ b/command/kv_destroy.go @@ -17,6 +17,7 @@ type KVDestroyCommand struct { *BaseCommand flagVersions []string + flagMount string } func (c *KVDestroyCommand) Synopsis() string { @@ -32,6 +33,12 @@ Usage: vault kv destroy [options] KEY To destroy version 3 of key foo: + $ vault kv destroy -mount=secret -versions=3 foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv destroy -versions=3 secret/foo Additional flags and more advanced use cases are detailed below. @@ -53,6 +60,17 @@ func (c *KVDestroyCommand) Flags() *FlagSets { Usage: `Specifies the version numbers to destroy.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -86,8 +104,8 @@ func (c *KVDestroyCommand) Run(args []string) int { c.UI.Error("No versions provided, use the \"-versions\" flag to specify the version to destroy.") return 1 } + var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -95,16 +113,42 @@ func (c *KVDestroyCommand) Run(args []string) int { return 2 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Destroy not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "destroy") + destroyPath := addPrefixToKVPath(partialPath, mountPath, "destroy") if err != nil { c.UI.Error(err.Error()) return 2 @@ -114,9 +158,9 @@ func (c *KVDestroyCommand) Run(args []string) int { "versions": kvParseVersionsFlags(c.flagVersions), } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(destroyPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", destroyPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -125,7 +169,7 @@ func (c *KVDestroyCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", destroyPath)) } return 0 } diff --git a/command/kv_get.go b/command/kv_get.go index 14202d76449b..07033448187a 100644 --- a/command/kv_get.go +++ b/command/kv_get.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "path" "strings" "github.com/mitchellh/cli" @@ -17,6 +18,7 @@ type KVGetCommand struct { *BaseCommand flagVersion int + flagMount string } func (c *KVGetCommand) Synopsis() string { @@ -31,12 +33,18 @@ Usage: vault kv get [options] KEY key exists with that name, an error is returned. If a key exists with that name but has no data, nothing is returned. + $ vault kv get -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv get secret/foo To view the given key name at a specific version in time, specify the "-version" flag: - $ vault kv get -version=1 secret/foo + $ vault kv get -mount=secret -version=1 foo Additional flags and more advanced use cases are detailed below. @@ -57,6 +65,17 @@ func (c *KVGetCommand) Flags() *FlagSets { Usage: `If passed, the value at the version number will be returned.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -92,35 +111,67 @@ func (c *KVGetCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } var versionParam map[string]string - + var fullPath string + // Add /data to v2 paths only if v2 { - path = addPrefixToKVPath(path, mountPath, "data") + fullPath = addPrefixToKVPath(partialPath, mountPath, "data") if c.flagVersion > 0 { versionParam = map[string]string{ "version": fmt.Sprintf("%d", c.flagVersion), } } + } else { + // v1 + if mountFlagSyntax { + fullPath = path.Join(mountPath, partialPath) + } else { + fullPath = partialPath + } } - secret, err := kvReadRequest(client, path, versionParam) + secret, err := kvReadRequest(client, fullPath, versionParam) if err != nil { - c.UI.Error(fmt.Sprintf("Error reading %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error reading %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } return 2 } if secret == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } @@ -140,7 +191,7 @@ func (c *KVGetCommand) Run(args []string) int { } return PrintRawField(c.UI, data, c.flagField) } else { - c.UI.Error(fmt.Sprintf("No data found at %s", path)) + c.UI.Error(fmt.Sprintf("No data found at %s", fullPath)) return 2 } } else { @@ -159,7 +210,7 @@ func (c *KVGetCommand) Run(args []string) int { } if v2 { - outputPath(c.UI, path, "Secret Path") + outputPath(c.UI, fullPath, "Secret Path") } if metadata, ok := secret.Data["metadata"]; ok && metadata != nil { diff --git a/command/kv_metadata.go b/command/kv_metadata.go index badb08a48fd1..c4ab37910555 100644 --- a/command/kv_metadata.go +++ b/command/kv_metadata.go @@ -26,16 +26,22 @@ Usage: vault kv metadata [options] [args] Create or update a metadata entry for a key: - $ vault kv metadata put -max-versions=5 -delete-version-after=3h25m19s secret/foo + $ vault kv metadata put -mount=secret -max-versions=5 -delete-version-after=3h25m19s foo Get the metadata for a key, this provides information about each existing version: - $ vault kv metadata get secret/foo + $ vault kv metadata get -mount=secret foo Delete a key and all existing versions: - $ vault kv metadata delete secret/foo + $ vault kv metadata delete -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + + $ vault kv metadata get secret/foo Please see the individual subcommand help for detailed usage information. ` diff --git a/command/kv_metadata_delete.go b/command/kv_metadata_delete.go index 3b9509a2df66..911f00117e76 100644 --- a/command/kv_metadata_delete.go +++ b/command/kv_metadata_delete.go @@ -15,6 +15,7 @@ var ( type KVMetadataDeleteCommand struct { *BaseCommand + flagMount string } func (c *KVMetadataDeleteCommand) Synopsis() string { @@ -27,6 +28,12 @@ Usage: vault kv metadata delete [options] PATH Deletes all versions and metadata for the provided key. + $ vault kv metadata delete -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata delete secret/foo Additional flags and more advanced use cases are detailed below. @@ -37,7 +44,23 @@ Usage: vault kv metadata delete [options] PATH } func (c *KVMetadataDeleteCommand) Flags() *FlagSets { - return c.flagSet(FlagSetHTTP) + set := c.flagSet(FlagSetHTTP) + + // Common Options + f := set.NewFlagSet("Common Options") + + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + + return set } func (c *KVMetadataDeleteCommand) AutocompleteArgs() complete.Predictor { @@ -72,26 +95,51 @@ func (c *KVMetadataDeleteCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") - if secret, err := client.Logical().Delete(path); err != nil { - c.UI.Error(fmt.Sprintf("Error deleting %s: %s", path, err)) + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") + if secret, err := client.Logical().Delete(fullPath); err != nil { + c.UI.Error(fmt.Sprintf("Error deleting %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } return 2 } - c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", fullPath)) return 0 } diff --git a/command/kv_metadata_get.go b/command/kv_metadata_get.go index 61abf3a57c9b..08e401374a70 100644 --- a/command/kv_metadata_get.go +++ b/command/kv_metadata_get.go @@ -17,6 +17,7 @@ var ( type KVMetadataGetCommand struct { *BaseCommand + flagMount string } func (c *KVMetadataGetCommand) Synopsis() string { @@ -30,6 +31,12 @@ Usage: vault kv metadata get [options] KEY Retrieves the metadata from Vault's key-value store at the given key name. If no key exists with that name, an error is returned. + $ vault kv metadata get -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata get secret/foo Additional flags and more advanced use cases are detailed below. @@ -41,6 +48,20 @@ Usage: vault kv metadata get [options] KEY func (c *KVMetadataGetCommand) Flags() *FlagSets { set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) + // Common Options + f := set.NewFlagSet("Common Options") + + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -76,25 +97,50 @@ func (c *KVMetadataGetCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") - secret, err := client.Logical().Read(path) + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") + secret, err := client.Logical().Read(fullPath) if err != nil { - c.UI.Error(fmt.Sprintf("Error reading %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error reading %s: %s", fullPath, err)) return 2 } if secret == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } @@ -109,7 +155,7 @@ func (c *KVMetadataGetCommand) Run(args []string) int { versionsRaw, ok := secret.Data["versions"] if !ok || versionsRaw == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) OutputSecret(c.UI, secret) return 2 } @@ -117,7 +163,7 @@ func (c *KVMetadataGetCommand) Run(args []string) int { delete(secret.Data, "versions") - outputPath(c.UI, path, "Metadata Path") + outputPath(c.UI, fullPath, "Metadata Path") c.UI.Info(getHeaderForMap("Metadata", secret.Data)) OutputSecret(c.UI, secret) diff --git a/command/kv_metadata_patch.go b/command/kv_metadata_patch.go index c2a0956e1432..cbee81a93b80 100644 --- a/command/kv_metadata_patch.go +++ b/command/kv_metadata_patch.go @@ -23,6 +23,7 @@ type KVMetadataPatchCommand struct { flagCASRequired BoolPtr flagDeleteVersionAfter time.Duration flagCustomMetadata map[string]string + flagMount string testStdin io.Reader // for tests } @@ -39,23 +40,29 @@ Usage: vault metadata kv patch [options] KEY Create a key in the key-value store with no data: + $ vault kv metadata patch -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata patch secret/foo Set a max versions setting on the key: - $ vault kv metadata patch -max-versions=5 secret/foo + $ vault kv metadata patch -mount=secret -max-versions=5 foo Set delete-version-after on the key: - $ vault kv metadata patch -delete-version-after=3h25m19s secret/foo + $ vault kv metadata patch -mount=secret -delete-version-after=3h25m19s foo Require Check-and-Set for this key: - $ vault kv metadata patch -cas-required secret/foo + $ vault kv metadata patch -mount=secret -cas-required foo Set custom metadata on the key: - $ vault kv metadata patch -custom-metadata=foo=abc -custom-metadata=bar=123 secret/foo + $ vault kv metadata patch -mount=secret -custom-metadata=foo=abc -custom-metadata=bar=123 foo Additional flags and more advanced use cases are detailed below. @@ -103,6 +110,17 @@ func (c *KVMetadataPatchCommand) Flags() *FlagSets { This can be specified multiple times to add multiple pieces of metadata.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -139,19 +157,42 @@ func (c *KVMetadataPatchCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") data := map[string]interface{}{} @@ -171,9 +212,9 @@ func (c *KVMetadataPatchCommand) Run(args []string) int { data["custom_metadata"] = c.flagCustomMetadata } - secret, err := client.Logical().JSONMergePatch(context.Background(), path, data) + secret, err := client.Logical().JSONMergePatch(context.Background(), fullPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) @@ -184,7 +225,7 @@ func (c *KVMetadataPatchCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } diff --git a/command/kv_metadata_patch_test.go b/command/kv_metadata_patch_test.go index 7c10d07fdc4b..40b74dc8d9ee 100644 --- a/command/kv_metadata_patch_test.go +++ b/command/kv_metadata_patch_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "encoding/json" "io" "strings" @@ -63,7 +62,7 @@ func TestKvMetadataPatchCommand_EmptyArgs(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -180,7 +179,7 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) { secretPath := basePath + "my-secret" metadataPath := basePath + "metadata/" + "my-secret" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -193,7 +192,7 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) { t.Fatalf("initial metadata put failed, code: %d, output: %s", code, combined) } - initialMetadata, err := client.Logical().ReadWithContext(context.Background(), metadataPath) + initialMetadata, err := client.Logical().Read(metadataPath) if err != nil { t.Fatalf("metadata read failed, err: %#v", err) } @@ -209,7 +208,7 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) { t.Fatalf("expected code to be %d but was %d for patch cmd with args %#v", tc.code, code, patchArgs) } - patchedMetadata, err := client.Logical().ReadWithContext(context.Background(), metadataPath) + patchedMetadata, err := client.Logical().Read(metadataPath) if err != nil { t.Fatalf("metadata read failed, err: %#v", err) } @@ -236,7 +235,7 @@ func TestKvMetadataPatchCommand_CasWarning(t *testing.T) { defer closer() basePath := "kv/" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -255,7 +254,7 @@ func TestKvMetadataPatchCommand_CasWarning(t *testing.T) { "cas_required": true, } - _, err := client.Logical().WriteWithContext(context.Background(), basePath+"config", casConfig) + _, err := client.Logical().Write(basePath+"config", casConfig) if err != nil { t.Fatalf("config write failed, err: #%v", err) } diff --git a/command/kv_metadata_put.go b/command/kv_metadata_put.go index 67a951731f5f..2681583b0ac6 100644 --- a/command/kv_metadata_put.go +++ b/command/kv_metadata_put.go @@ -22,6 +22,7 @@ type KVMetadataPutCommand struct { flagCASRequired BoolPtr flagDeleteVersionAfter time.Duration flagCustomMetadata map[string]string + flagMount string testStdin io.Reader // for tests } @@ -38,23 +39,29 @@ Usage: vault metadata kv put [options] KEY Create a key in the key-value store with no data: + $ vault kv metadata put -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata put secret/foo Set a max versions setting on the key: - $ vault kv metadata put -max-versions=5 secret/foo + $ vault kv metadata put -mount=secret -max-versions=5 foo Set delete-version-after on the key: - $ vault kv metadata put -delete-version-after=3h25m19s secret/foo + $ vault kv metadata put -mount=secret -delete-version-after=3h25m19s foo Require Check-and-Set for this key: - $ vault kv metadata put -cas-required secret/foo + $ vault kv metadata put -mount=secret -cas-required foo Set custom metadata on the key: - $ vault kv metadata put -custom-metadata=foo=abc -custom-metadata=bar=123 secret/foo + $ vault kv metadata put -mount=secret -custom-metadata=foo=abc -custom-metadata=bar=123 foo Additional flags and more advanced use cases are detailed below. @@ -102,6 +109,17 @@ func (c *KVMetadataPutCommand) Flags() *FlagSets { "This can be specified multiple times to add multiple pieces of metadata.", }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -138,18 +156,43 @@ func (c *KVMetadataPutCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") data := map[string]interface{}{} if c.flagMaxVersions >= 0 { @@ -168,9 +211,9 @@ func (c *KVMetadataPutCommand) Run(args []string) int { data["custom_metadata"] = c.flagCustomMetadata } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(fullPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -179,7 +222,7 @@ func (c *KVMetadataPutCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } diff --git a/command/kv_metadata_put_test.go b/command/kv_metadata_put_test.go index d726ad24fda2..a952802cc469 100644 --- a/command/kv_metadata_put_test.go +++ b/command/kv_metadata_put_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "encoding/json" "strings" "testing" @@ -27,7 +26,7 @@ func TestKvMetadataPutCommand_DeleteVersionAfter(t *testing.T) { defer closer() basePath := t.Name() + "/" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -49,7 +48,7 @@ func TestKvMetadataPutCommand_DeleteVersionAfter(t *testing.T) { t.Fatalf("expected %q but received %q", success, combined) } - secret, err := client.Logical().ReadWithContext(context.Background(), metaFullPath) + secret, err := client.Logical().Read(metaFullPath) if err != nil { t.Fatal(err) } @@ -72,7 +71,7 @@ func TestKvMetadataPutCommand_DeleteVersionAfter(t *testing.T) { t.Errorf("expected %q but received %q", success, combined) } - secret, err = client.Logical().ReadWithContext(context.Background(), metaFullPath) + secret, err = client.Logical().Read(metaFullPath) if err != nil { t.Fatal(err) } @@ -88,7 +87,7 @@ func TestKvMetadataPutCommand_CustomMetadata(t *testing.T) { basePath := t.Name() + "/" secretPath := basePath + "secret/my-secret" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -111,7 +110,7 @@ func TestKvMetadataPutCommand_CustomMetadata(t *testing.T) { t.Fatalf("Expected command output %q but received %q", expectedOutput, commandOutput) } - metadata, err := client.Logical().ReadWithContext(context.Background(), metaFullPath) + metadata, err := client.Logical().Read(metaFullPath) if err != nil { t.Fatalf("Metadata read error: %#v", err) } @@ -142,7 +141,7 @@ func TestKvMetadataPutCommand_CustomMetadata(t *testing.T) { t.Fatalf("Expected command output %q but received %q", expectedOutput, commandOutput) } - metadata, err = client.Logical().ReadWithContext(context.Background(), metaFullPath) + metadata, err = client.Logical().Read(metaFullPath) if err != nil { t.Fatalf("Metadata read error: %#v", err) @@ -164,7 +163,7 @@ func TestKvMetadataPutCommand_UnprovidedFlags(t *testing.T) { basePath := t.Name() + "/" secretPath := basePath + "my-secret" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -187,7 +186,7 @@ func TestKvMetadataPutCommand_UnprovidedFlags(t *testing.T) { t.Fatalf("expected 0 exit status but received %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), basePath+"metadata/"+"my-secret") + secret, err := client.Logical().Read(basePath + "metadata/" + "my-secret") if err != nil { t.Fatal(err) } diff --git a/command/kv_patch.go b/command/kv_patch.go index 334ba6f463ac..5f813fb15730 100644 --- a/command/kv_patch.go +++ b/command/kv_patch.go @@ -22,6 +22,7 @@ type KVPatchCommand struct { flagCAS int flagMethod string + flagMount string testStdin io.Reader // for tests } @@ -35,25 +36,31 @@ Usage: vault kv patch [options] KEY [DATA] *NOTE*: This is only supported for KV v2 engine mounts. - Writes the data to the given path in the key-value store. The data can be of + Writes the data to the corresponding path in the key-value store. The data can be of any type. + $ vault kv patch -mount=secret foo bar=baz + + The deprecated path-like syntax can also be used, but this should be avoided, + as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv patch secret/foo bar=baz The data can also be consumed from a file on disk by prefixing with the "@" symbol. For example: - $ vault kv patch secret/foo @data.json + $ vault kv patch -mount=secret foo @data.json Or it can be read from stdin using the "-" symbol: - $ echo "abcd1234" | vault kv patch secret/foo bar=- + $ echo "abcd1234" | vault kv patch -mount=secret foo bar=- To perform a Check-And-Set operation, specify the -cas flag with the appropriate version number corresponding to the key you want to perform the CAS operation on: - $ vault kv patch -cas=1 secret/foo bar=baz + $ vault kv patch -mount=secret -cas=1 foo bar=baz By default, this operation will attempt an HTTP PATCH operation. If your policy does not allow that, it will fall back to a read/local update/write approach. @@ -61,12 +68,12 @@ Usage: vault kv patch [options] KEY [DATA] with the -method flag. When -method=patch is specified, only an HTTP PATCH operation will be tried. If it fails, the entire command will fail. - $ vault kv patch -method=patch secret/foo bar=baz + $ vault kv patch -mount=secret -method=patch foo bar=baz When -method=rw is specified, only a read/local update/write approach will be tried. This was the default behavior previous to Vault 1.9. - $ vault kv patch -method=rw secret/foo bar=baz + $ vault kv patch -mount=secret -method=rw foo bar=baz Additional flags and more advanced use cases are detailed below. @@ -98,6 +105,17 @@ func (c *KVPatchCommand) Flags() *FlagSets { performed, then a local update, followed by a remote update.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -134,7 +152,6 @@ func (c *KVPatchCommand) Run(args []string) int { } var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -148,10 +165,35 @@ func (c *KVPatchCommand) Run(args []string) int { return 1 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } if !v2 { @@ -159,7 +201,7 @@ func (c *KVPatchCommand) Run(args []string) int { return 2 } - path = addPrefixToKVPath(path, mountPath, "data") + fullPath := addPrefixToKVPath(partialPath, mountPath, "data") if err != nil { c.UI.Error(err.Error()) return 2 @@ -171,11 +213,11 @@ func (c *KVPatchCommand) Run(args []string) int { switch c.flagMethod { case "rw": - secret, code = c.readThenWrite(client, path, newData) + secret, code = c.readThenWrite(client, fullPath, newData) case "patch": - secret, code = c.mergePatch(client, path, newData, false) + secret, code = c.mergePatch(client, fullPath, newData, false) case "": - secret, code = c.mergePatch(client, path, newData, true) + secret, code = c.mergePatch(client, fullPath, newData, true) default: c.UI.Error(fmt.Sprintf("Unsupported method provided to -method flag: %s", c.flagMethod)) return 2 @@ -186,7 +228,7 @@ func (c *KVPatchCommand) Run(args []string) int { } if Format(c.UI) == "table" { - outputPath(c.UI, path, "Secret Path") + outputPath(c.UI, fullPath, "Secret Path") metadata := secret.Data c.UI.Info(getHeaderForMap("Metadata", metadata)) return OutputData(c.UI, metadata) diff --git a/command/kv_put.go b/command/kv_put.go index ef48d09da194..f380e2d64d99 100644 --- a/command/kv_put.go +++ b/command/kv_put.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "path" "strings" "github.com/mitchellh/cli" @@ -19,6 +20,7 @@ type KVPutCommand struct { *BaseCommand flagCAS int + flagMount string testStdin io.Reader // for tests } @@ -33,22 +35,28 @@ Usage: vault kv put [options] KEY [DATA] Writes the data to the given path in the key-value store. The data can be of any type. + $ vault kv put -mount=secret foo bar=baz + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv put secret/foo bar=baz The data can also be consumed from a file on disk by prefixing with the "@" symbol. For example: - $ vault kv put secret/foo @data.json + $ vault kv put -mount=secret foo @data.json Or it can be read from stdin using the "-" symbol: - $ echo "abcd1234" | vault kv put secret/foo bar=- + $ echo "abcd1234" | vault kv put -mount=secret foo bar=- To perform a Check-And-Set operation, specify the -cas flag with the appropriate version number corresponding to the key you want to perform the CAS operation on: - $ vault kv put -cas=1 secret/foo bar=baz + $ vault kv put -mount=secret -cas=1 foo bar=baz Additional flags and more advanced use cases are detailed below. @@ -73,6 +81,17 @@ func (c *KVPutCommand) Flags() *FlagSets { parameter.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -109,7 +128,6 @@ func (c *KVPutCommand) Run(args []string) int { } var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -123,14 +141,41 @@ func (c *KVPutCommand) Run(args []string) int { return 1 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + // Add /data to v2 paths only + var fullPath string if v2 { - path = addPrefixToKVPath(path, mountPath, "data") + fullPath = addPrefixToKVPath(partialPath, mountPath, "data") data = map[string]interface{}{ "data": data, "options": map[string]interface{}{}, @@ -139,11 +184,18 @@ func (c *KVPutCommand) Run(args []string) int { if c.flagCAS > -1 { data["options"].(map[string]interface{})["cas"] = c.flagCAS } + } else { + // v1 + if mountFlagSyntax { + fullPath = path.Join(mountPath, partialPath) + } else { + fullPath = partialPath + } } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(fullPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -152,7 +204,7 @@ func (c *KVPutCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } @@ -162,7 +214,7 @@ func (c *KVPutCommand) Run(args []string) int { } if Format(c.UI) == "table" { - outputPath(c.UI, path, "Secret Path") + outputPath(c.UI, fullPath, "Secret Path") metadata := secret.Data c.UI.Info(getHeaderForMap("Metadata", metadata)) return OutputData(c.UI, metadata) diff --git a/command/kv_rollback.go b/command/kv_rollback.go index c9f3eb3621e5..e69dba6f6c80 100644 --- a/command/kv_rollback.go +++ b/command/kv_rollback.go @@ -18,6 +18,7 @@ type KVRollbackCommand struct { *BaseCommand flagVersion int + flagMount string } func (c *KVRollbackCommand) Synopsis() string { @@ -35,6 +36,12 @@ Usage: vault kv rollback [options] KEY is 5 and the rollback version is 2, the data from version 2 will become version 6. + $ vault kv rollback -mount=secret -version=2 foo + + The deprecated path-like syntax can also be used, but this should be avoided, + as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv rollback -version=2 secret/foo Additional flags and more advanced use cases are detailed below. @@ -55,6 +62,17 @@ func (c *KVRollbackCommand) Flags() *FlagSets { Usage: `Specifies the version number that should be made current again.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -96,7 +114,6 @@ func (c *KVRollbackCommand) Run(args []string) int { } var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -104,10 +121,35 @@ func (c *KVRollbackCommand) Run(args []string) int { return 2 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } if !v2 { @@ -115,7 +157,7 @@ func (c *KVRollbackCommand) Run(args []string) int { return 2 } - path = addPrefixToKVPath(path, mountPath, "data") + fullPath := addPrefixToKVPath(partialPath, mountPath, "data") if err != nil { c.UI.Error(err.Error()) return 2 @@ -124,31 +166,31 @@ func (c *KVRollbackCommand) Run(args []string) int { // First, do a read to get the current version for check-and-set var meta map[string]interface{} { - secret, err := kvReadRequest(client, path, nil) + secret, err := kvReadRequest(client, fullPath, nil) if err != nil { - c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", fullPath, err)) return 2 } // Make sure a value already exists if secret == nil || secret.Data == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } // Verify metadata found rawMeta, ok := secret.Data["metadata"] if !ok || rawMeta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } meta, ok = rawMeta.(map[string]interface{}) if !ok { - c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", path)) + c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", fullPath)) return 2 } if meta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } } @@ -163,31 +205,31 @@ func (c *KVRollbackCommand) Run(args []string) int { // Now run it again and read the version we want to roll back to var data map[string]interface{} { - secret, err := kvReadRequest(client, path, versionParam) + secret, err := kvReadRequest(client, fullPath, versionParam) if err != nil { - c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", fullPath, err)) return 2 } // Make sure a value already exists if secret == nil || secret.Data == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } // Verify metadata found rawMeta, ok := secret.Data["metadata"] if !ok || rawMeta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } meta, ok := rawMeta.(map[string]interface{}) if !ok { - c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", path)) + c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", fullPath)) return 2 } if meta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } @@ -205,34 +247,34 @@ func (c *KVRollbackCommand) Run(args []string) int { // Verify old data found rawData, ok := secret.Data["data"] if !ok || rawData == nil { - c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", fullPath)) return 2 } data, ok = rawData.(map[string]interface{}) if !ok { - c.UI.Error(fmt.Sprintf("Data found at %s is not the expected type (JSON object)", path)) + c.UI.Error(fmt.Sprintf("Data found at %s is not the expected type (JSON object)", fullPath)) return 2 } if data == nil { - c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", fullPath)) return 2 } } - secret, err := client.Logical().Write(path, map[string]interface{}{ + secret, err := client.Logical().Write(fullPath, map[string]interface{}{ "data": data, "options": map[string]interface{}{ "cas": casVersion, }, }) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) return 2 } if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } diff --git a/command/kv_test.go b/command/kv_test.go index 4909b0cdfa4e..21df402a904f 100644 --- a/command/kv_test.go +++ b/command/kv_test.go @@ -123,6 +123,12 @@ func TestKVPutCommand(t *testing.T) { []string{"Success!"}, 0, }, + { + "v1_mount_flag_syntax", + []string{"-mount", "secret", "write/foo", "foo=bar"}, + []string{"Success!"}, + 0, + }, { "v2_single_value", []string{"kv/write/foo", "foo=bar"}, @@ -141,6 +147,12 @@ func TestKVPutCommand(t *testing.T) { []string{"== Secret Path ==", "kv/data/write/foo"}, 0, }, + { + "v2_mount_flag_syntax", + []string{"-mount", "kv", "write/foo", "foo=bar"}, + v2ExpectedFields, + 0, + }, { "v2_single_value_backslash", []string{"kv/write/foo", "foo=\\"}, @@ -158,7 +170,7 @@ func TestKVPutCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -183,7 +195,7 @@ func TestKVPutCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -290,7 +302,7 @@ func TestKVPutCommand(t *testing.T) { t.Fatalf("expected 0 to be %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/write/stdin_full") + secret, err := client.Logical().Read("secret/write/stdin_full") if err != nil { t.Fatal(err) } @@ -325,7 +337,7 @@ func TestKVPutCommand(t *testing.T) { t.Fatalf("expected 0 to be %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/write/stdin_value") + secret, err := client.Logical().Read("secret/write/stdin_value") if err != nil { t.Fatal(err) } @@ -353,7 +365,7 @@ func TestKVPutCommand(t *testing.T) { t.Fatalf("expected 0 to be %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/write/integration") + secret, err := client.Logical().Read("secret/write/integration") if err != nil { t.Fatal(err) } @@ -428,20 +440,30 @@ func TestKVGetCommand(t *testing.T) { []string{"bar"}, 0, }, + { + "v1_mount_flag_syntax", + []string{"-mount", "secret", "read/foo"}, + []string{"foo"}, + 0, + }, { "v2_field", []string{"-field", "foo", "kv/read/foo"}, []string{"bar"}, 0, }, - + { + "v2_mount_flag_syntax", + []string{"-mount", "kv", "read/foo"}, + append(baseV2ExpectedFields, "foo"), + 0, + }, { "v2_not_found", []string{"kv/nope/not/once/never"}, []string{"No value found at kv/data/nope/not/once/never"}, 2, }, - { "v2_read", []string{"kv/read/foo"}, @@ -467,7 +489,7 @@ func TestKVGetCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -476,13 +498,13 @@ func TestKVGetCommand(t *testing.T) { // Give time for the upgrade code to run/finish time.Sleep(time.Second) - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/read/foo", map[string]interface{}{ + if _, err := client.Logical().Write("secret/read/foo", map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/read/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/read/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "bar", }, @@ -573,6 +595,12 @@ func TestKVMetadataGetCommand(t *testing.T) { append(expectedTopLevelFields, expectedVersionFields[:]...), 0, }, + { + "mount_flag_syntax", + []string{"-mount", "kv", "foo"}, + expectedTopLevelFields, + 0, + }, } t.Run("validations", func(t *testing.T) { @@ -586,7 +614,7 @@ func TestKVMetadataGetCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -595,7 +623,7 @@ func TestKVMetadataGetCommand(t *testing.T) { // Give time for the upgrade code to run/finish time.Sleep(time.Second) - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "bar", }, @@ -667,6 +695,12 @@ func TestKVPatchCommand_ArgValidation(t *testing.T) { "Failed to parse K=V data", 1, }, + { + "mount_flag_syntax", + []string{"-mount", "kv"}, + "Not enough arguments", + 1, + }, } for _, tc := range cases { @@ -676,7 +710,7 @@ func TestKVPatchCommand_ArgValidation(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -708,17 +742,17 @@ func expectedPatchFields() []string { } } -func TestKvPatchCommand_StdinFull(t *testing.T) { +func TestKVPatchCommand_StdinFull(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/patch/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/patch/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "a", }, @@ -726,62 +760,66 @@ func TestKvPatchCommand_StdinFull(t *testing.T) { t.Fatalf("write failed, err: %#v\n", err) } - stdinR, stdinW := io.Pipe() - go func() { - stdinW.Write([]byte(`{"foo":"bar"}`)) - stdinW.Close() - }() - - args := []string{"kv/patch/foo", "-"} - code, combined := kvPatchWithRetry(t, client, args, stdinR) + cases := [][]string{ + {"kv/patch/foo", "-"}, + {"-mount", "kv", "patch/foo", "-"}, + } + for i, args := range cases { + stdinR, stdinW := io.Pipe() + go func() { + stdinW.Write([]byte(fmt.Sprintf(`{"foo%d":"bar%d"}`, i, i))) + stdinW.Close() + }() + code, combined := kvPatchWithRetry(t, client, args, stdinR) - for _, str := range expectedPatchFields() { - if !strings.Contains(combined, str) { - t.Errorf("expected %q to contain %q", combined, str) + for _, str := range expectedPatchFields() { + if !strings.Contains(combined, str) { + t.Errorf("expected %q to contain %q", combined, str) + } } - } - if code != 0 { - t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) - } + if code != 0 { + t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) + } - secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") - if err != nil { - t.Fatalf("read failed, err: %#v\n", err) - } + secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") + if err != nil { + t.Fatalf("read failed, err: %#v\n", err) + } - if secret == nil || secret.Data == nil { - t.Fatal("expected secret to have data") - } + if secret == nil || secret.Data == nil { + t.Fatal("expected secret to have data") + } - secretDataRaw, ok := secret.Data["data"] + secretDataRaw, ok := secret.Data["data"] - if !ok { - t.Fatalf("expected secret to have nested data key, data: %#v", secret.Data) - } + if !ok { + t.Fatalf("expected secret to have nested data key, data: %#v", secret.Data) + } - secretData := secretDataRaw.(map[string]interface{}) - foo, ok := secretData["foo"].(string) - if !ok { - t.Fatal("expected foo to be a string but it wasn't") - } + secretData := secretDataRaw.(map[string]interface{}) + foo, ok := secretData[fmt.Sprintf("foo%d", i)].(string) + if !ok { + t.Fatal("expected foo to be a string but it wasn't") + } - if exp, act := "bar", foo; exp != act { - t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + if exp, act := fmt.Sprintf("bar%d", i), foo; exp != act { + t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + } } } -func TestKvPatchCommand_StdinValue(t *testing.T) { +func TestKVPatchCommand_StdinValue(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/patch/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/patch/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "a", }, @@ -789,43 +827,49 @@ func TestKvPatchCommand_StdinValue(t *testing.T) { t.Fatalf("write failed, err: %#v\n", err) } - stdinR, stdinW := io.Pipe() - go func() { - stdinW.Write([]byte("bar")) - stdinW.Close() - }() - - args := []string{"kv/patch/foo", "foo=-"} - code, combined := kvPatchWithRetry(t, client, args, stdinR) - if code != 0 { - t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) + cases := [][]string{ + {"kv/patch/foo", "foo=-"}, + {"-mount", "kv", "patch/foo", "foo=-"}, } - for _, str := range expectedPatchFields() { - if !strings.Contains(combined, str) { - t.Errorf("expected %q to contain %q", combined, str) + for i, args := range cases { + stdinR, stdinW := io.Pipe() + go func() { + stdinW.Write([]byte(fmt.Sprintf("bar%d", i))) + stdinW.Close() + }() + + code, combined := kvPatchWithRetry(t, client, args, stdinR) + if code != 0 { + t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) } - } - secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") - if err != nil { - t.Fatalf("read failed, err: %#v\n", err) - } + for _, str := range expectedPatchFields() { + if !strings.Contains(combined, str) { + t.Errorf("expected %q to contain %q", combined, str) + } + } - if secret == nil || secret.Data == nil { - t.Fatal("expected secret to have data") - } + secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") + if err != nil { + t.Fatalf("read failed, err: %#v\n", err) + } + + if secret == nil || secret.Data == nil { + t.Fatal("expected secret to have data") + } - secretDataRaw, ok := secret.Data["data"] + secretDataRaw, ok := secret.Data["data"] - if !ok { - t.Fatalf("expected secret to have nested data key, data: %#v\n", secret.Data) - } + if !ok { + t.Fatalf("expected secret to have nested data key, data: %#v\n", secret.Data) + } - secretData := secretDataRaw.(map[string]interface{}) + secretData := secretDataRaw.(map[string]interface{}) - if exp, act := "bar", secretData["foo"].(string); exp != act { - t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + if exp, act := fmt.Sprintf("bar%d", i), secretData["foo"].(string); exp != act { + t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + } } } @@ -833,22 +877,28 @@ func TestKVPatchCommand_RWMethodNotExists(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - args := []string{"-method", "rw", "kv/patch/foo", "foo=a"} - code, combined := kvPatchWithRetry(t, client, args, nil) - - if code != 2 { - t.Fatalf("expected code to be 2 but was %d for patch cmd with args %#v\n", code, args) + cases := [][]string{ + {"-method", "rw", "kv/patch/foo", "foo=a"}, + {"-method", "rw", "-mount", "kv", "patch/foo", "foo=a"}, } - expectedOutputSubstr := "No value found" - if !strings.Contains(combined, expectedOutputSubstr) { - t.Fatalf("expected output %q to contain %q for patch cmd with args %#v\n", combined, expectedOutputSubstr, args) + for _, args := range cases { + code, combined := kvPatchWithRetry(t, client, args, nil) + + if code != 2 { + t.Fatalf("expected code to be 2 but was %d for patch cmd with args %#v\n", code, args) + } + + expectedOutputSubstr := "No value found" + if !strings.Contains(combined, expectedOutputSubstr) { + t.Fatalf("expected output %q to contain %q for patch cmd with args %#v\n", combined, expectedOutputSubstr, args) + } } } @@ -856,13 +906,13 @@ func TestKVPatchCommand_RWMethodSucceeds(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/patch/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/patch/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "a", "bar": "b", @@ -929,6 +979,13 @@ func TestKVPatchCommand_CAS(t *testing.T) { []string{"check-and-set parameter did not match the current version"}, 2, }, + { + "mount_flag_syntax", + []string{"-mount", "kv", "-cas", "1", "foo", "bar=quux"}, + "quux", + expectedPatchFields(), + 0, + }, } for _, tc := range cases { @@ -940,7 +997,7 @@ func TestKVPatchCommand_CAS(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -960,7 +1017,7 @@ func TestKVPatchCommand_CAS(t *testing.T) { kvClient.SetToken(secretAuth.ClientToken) - _, err = kvClient.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) + _, err = kvClient.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) if err != nil { t.Fatal(err) } @@ -1019,7 +1076,7 @@ func TestKVPatchCommand_Methods(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -1039,7 +1096,7 @@ func TestKVPatchCommand_Methods(t *testing.T) { kvClient.SetToken(secretAuth.ClientToken) - _, err = kvClient.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) + _, err = kvClient.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) if err != nil { t.Fatal(err) } @@ -1093,7 +1150,7 @@ func TestKVPatchCommand_403Fallback(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -1114,7 +1171,7 @@ func TestKVPatchCommand_403Fallback(t *testing.T) { kvClient.SetToken(secretAuth.ClientToken) // Write a value then attempt to patch it - _, err = kvClient.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) + _, err = kvClient.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) if err != nil { t.Fatal(err) } @@ -1174,7 +1231,7 @@ func TestKVPatchCommand_RWMethodPolicyVariations(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -1258,11 +1315,11 @@ func TestPadEqualSigns(t *testing.T) { func createTokenForPolicy(t *testing.T, client *api.Client, policy string) (*api.SecretAuth, error) { t.Helper() - if err := client.Sys().PutPolicyWithContext(context.Background(), "policy", policy); err != nil { + if err := client.Sys().PutPolicy("policy", policy); err != nil { return nil, err } - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"policy"}, TTL: "30m", }) diff --git a/command/kv_undelete.go b/command/kv_undelete.go index b734ff876a30..90ea608a7316 100644 --- a/command/kv_undelete.go +++ b/command/kv_undelete.go @@ -17,6 +17,7 @@ type KVUndeleteCommand struct { *BaseCommand flagVersions []string + flagMount string } func (c *KVUndeleteCommand) Synopsis() string { @@ -32,6 +33,12 @@ Usage: vault kv undelete [options] KEY To undelete version 3 of key "foo": + $ vault kv undelete -mount=secret -versions=3 foo + + The deprecated path-like syntax can also be used, but this should be avoided, + as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv undelete -versions=3 secret/foo Additional flags and more advanced use cases are detailed below. @@ -53,6 +60,17 @@ func (c *KVUndeleteCommand) Flags() *FlagSets { Usage: `Specifies the version numbers to undelete.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -93,25 +111,50 @@ func (c *KVUndeleteCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Undelete not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "undelete") + undeletePath := addPrefixToKVPath(partialPath, mountPath, "undelete") data := map[string]interface{}{ "versions": kvParseVersionsFlags(c.flagVersions), } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(undeletePath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", undeletePath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -120,7 +163,7 @@ func (c *KVUndeleteCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", undeletePath)) } return 0 } diff --git a/command/lease_lookup_test.go b/command/lease_lookup_test.go index f43405ef27b9..4de63200f5ce 100644 --- a/command/lease_lookup_test.go +++ b/command/lease_lookup_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -23,13 +22,13 @@ func testLeaseLookupCommand(tb testing.TB) (*cli.MockUi, *LeaseLookupCommand) { // testLeaseLookupCommandMountAndLease mounts a leased secret backend and returns // the leaseID of an item. func testLeaseLookupCommandMountAndLease(tb testing.TB, client *api.Client) string { - if err := client.Sys().MountWithContext(context.Background(), "testing", &api.MountInput{ + if err := client.Sys().Mount("testing", &api.MountInput{ Type: "generic-leased", }); err != nil { tb.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "testing/foo", map[string]interface{}{ + if _, err := client.Logical().Write("testing/foo", map[string]interface{}{ "key": "value", "lease": "5m", }); err != nil { @@ -37,7 +36,7 @@ func testLeaseLookupCommandMountAndLease(tb testing.TB, client *api.Client) stri } // Read the secret back to get the leaseID - secret, err := client.Logical().ReadWithContext(context.Background(), "testing/foo") + secret, err := client.Logical().Read("testing/foo") if err != nil { tb.Fatal(err) } diff --git a/command/lease_renew_test.go b/command/lease_renew_test.go index fbbd700ae80e..aa3b32d0d8b3 100644 --- a/command/lease_renew_test.go +++ b/command/lease_renew_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -23,13 +22,13 @@ func testLeaseRenewCommand(tb testing.TB) (*cli.MockUi, *LeaseRenewCommand) { // testLeaseRenewCommandMountAndLease mounts a leased secret backend and returns // the leaseID of an item. func testLeaseRenewCommandMountAndLease(tb testing.TB, client *api.Client) string { - if err := client.Sys().MountWithContext(context.Background(), "testing", &api.MountInput{ + if err := client.Sys().Mount("testing", &api.MountInput{ Type: "generic-leased", }); err != nil { tb.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "testing/foo", map[string]interface{}{ + if _, err := client.Logical().Write("testing/foo", map[string]interface{}{ "key": "value", "lease": "5m", }); err != nil { @@ -37,7 +36,7 @@ func testLeaseRenewCommandMountAndLease(tb testing.TB, client *api.Client) strin } // Read the secret back to get the leaseID - secret, err := client.Logical().ReadWithContext(context.Background(), "testing/foo") + secret, err := client.Logical().Read("testing/foo") if err != nil { tb.Fatal(err) } diff --git a/command/lease_revoke_test.go b/command/lease_revoke_test.go index 0c544c7b4337..1aa58c38ac76 100644 --- a/command/lease_revoke_test.go +++ b/command/lease_revoke_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -79,7 +78,7 @@ func TestLeaseRevokeCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "secret-leased", &api.MountInput{ + if err := client.Sys().Mount("secret-leased", &api.MountInput{ Type: "generic-leased", }); err != nil { t.Fatal(err) @@ -90,10 +89,10 @@ func TestLeaseRevokeCommand_Run(t *testing.T) { "key": "value", "lease": "1m", } - if _, err := client.Logical().WriteWithContext(context.Background(), path, data); err != nil { + if _, err := client.Logical().Write(path, data); err != nil { t.Fatal(err) } - secret, err := client.Logical().ReadWithContext(context.Background(), path) + secret, err := client.Logical().Read(path) if err != nil { t.Fatal(err) } diff --git a/command/list_test.go b/command/list_test.go index fa36b3aedd53..b1b6680507f1 100644 --- a/command/list_test.go +++ b/command/list_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -78,7 +77,7 @@ func TestListCommand_Run(t *testing.T) { "secret/list/baz", } for _, k := range keys { - if _, err := client.Logical().WriteWithContext(context.Background(), k, map[string]interface{}{ + if _, err := client.Logical().Write(k, map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) diff --git a/command/login_test.go b/command/login_test.go index d6a247318784..aefdd2585190 100644 --- a/command/login_test.go +++ b/command/login_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -49,7 +48,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("my-auth", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/my-auth/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/my-auth/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -99,7 +98,7 @@ func TestLoginCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"default"}, TTL: "30m", }) @@ -145,7 +144,7 @@ func TestLoginCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"default"}, TTL: "30m", }) @@ -188,7 +187,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -266,7 +265,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -303,7 +302,7 @@ func TestLoginCommand_Run(t *testing.T) { client.SetToken(token) // Ensure the resulting token is unwrapped - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Error(err) } @@ -325,7 +324,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -368,7 +367,7 @@ func TestLoginCommand_Run(t *testing.T) { // Ensure the resulting token is, in fact, still wrapped. client.SetToken(token) - secret, err := client.Logical().UnwrapWithContext(context.Background(), "") + secret, err := client.Logical().Unwrap("") if err != nil { t.Error(err) } @@ -386,7 +385,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { diff --git a/command/operator_diagnose_test.go b/command/operator_diagnose_test.go index 2bf8713c9ed4..bacbbeb2a82c 100644 --- a/command/operator_diagnose_test.go +++ b/command/operator_diagnose_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/vault/diagnose" "github.com/mitchellh/cli" ) @@ -478,7 +479,7 @@ func TestOperatorDiagnoseCommand_Run(t *testing.T) { t.Parallel() client, closer := testVaultServer(t) defer closer() - + os.Setenv(consts.VaultDisableFilePermissionsCheckEnv, "true") cmd := testOperatorDiagnoseCommand(t) cmd.client = client diff --git a/command/operator_generate_root_test.go b/command/operator_generate_root_test.go index fadd7560623e..b4489718efbe 100644 --- a/command/operator_generate_root_test.go +++ b/command/operator_generate_root_test.go @@ -3,7 +3,6 @@ package command import ( - "context" "encoding/base64" "io" "os" @@ -256,7 +255,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { defer closer() // Initialize a generation - if _, err := client.Sys().GenerateRootInitWithContext(context.Background(), "", ""); err != nil { + if _, err := client.Sys().GenerateRootInit("", ""); err != nil { t.Fatal(err) } @@ -276,7 +275,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -308,7 +307,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -344,7 +343,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -387,7 +386,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { defer closer() // Initialize a generation - status, err := client.Sys().GenerateRootInitWithContext(context.Background(), "", "") + status, err := client.Sys().GenerateRootInit("", "") if err != nil { t.Fatal(err) } @@ -448,7 +447,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { defer closer() // Initialize a generation - status, err := client.Sys().GenerateRootInitWithContext(context.Background(), "", "") + status, err := client.Sys().GenerateRootInit("", "") if err != nil { t.Fatal(err) } diff --git a/command/operator_init_test.go b/command/operator_init_test.go index 7e1466a8743a..491d623a1473 100644 --- a/command/operator_init_test.go +++ b/command/operator_init_test.go @@ -3,7 +3,6 @@ package command import ( - "context" "fmt" "os" "regexp" @@ -144,7 +143,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { } // Now init to verify the init response code - if _, err := client.Sys().InitWithContext(context.Background(), &api.InitRequest{ + if _, err := client.Sys().Init(&api.InitRequest{ SecretShares: 1, SecretThreshold: 1, }); err != nil { @@ -176,7 +175,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String()) } - init, err := client.Sys().InitStatusWithContext(context.Background()) + init, err := client.Sys().InitStatus() if err != nil { t.Fatal(err) } @@ -199,7 +198,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { // Try unsealing with those keys - only use 3, which is the default // threshold. for i, key := range keys[:3] { - resp, err := client.Sys().UnsealWithContext(context.Background(), key) + resp, err := client.Sys().Unseal(key) if err != nil { t.Fatal(err) } @@ -210,7 +209,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { } } - status, err := client.Sys().SealStatusWithContext(context.Background()) + status, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } @@ -238,7 +237,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String()) } - init, err := client.Sys().InitStatusWithContext(context.Background()) + init, err := client.Sys().InitStatus() if err != nil { t.Fatal(err) } @@ -261,7 +260,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { // Try unsealing with those keys - only use 3, which is the default // threshold. for i, key := range keys[:keyThreshold] { - resp, err := client.Sys().UnsealWithContext(context.Background(), key) + resp, err := client.Sys().Unseal(key) if err != nil { t.Fatal(err) } @@ -272,7 +271,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { } } - status, err := client.Sys().SealStatusWithContext(context.Background()) + status, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } @@ -321,7 +320,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { // Try unsealing with one key decryptedKey := testPGPDecrypt(t, pgpkeys.TestPrivKey1, keys[0]) - if _, err := client.Sys().UnsealWithContext(context.Background(), decryptedKey); err != nil { + if _, err := client.Sys().Unseal(decryptedKey); err != nil { t.Fatal(err) } diff --git a/command/operator_rekey_test.go b/command/operator_rekey_test.go index 6ba2a4049be3..31617e5ac4bc 100644 --- a/command/operator_rekey_test.go +++ b/command/operator_rekey_test.go @@ -3,7 +3,6 @@ package command import ( - "context" "io" "reflect" "regexp" @@ -118,7 +117,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { } // Now init to verify the init response - if _, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + if _, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }); err != nil { @@ -149,7 +148,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { defer closer() // Initialize a rekey - if _, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + if _, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }); err != nil { @@ -172,7 +171,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -206,7 +205,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().RekeyStatusWithContext(context.Background()) + status, err := client.Sys().RekeyStatus() if err != nil { t.Fatal(err) } @@ -243,7 +242,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().RekeyStatusWithContext(context.Background()) + status, err := client.Sys().RekeyStatus() if err != nil { t.Fatal(err) } @@ -262,7 +261,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { defer closer() // Initialize a rekey - status, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }) @@ -308,7 +307,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { if err := client.Sys().Seal(); err != nil { t.Fatal(err) } - sealStatus, err := client.Sys().UnsealWithContext(context.Background(), unsealKey) + sealStatus, err := client.Sys().Unseal(unsealKey) if err != nil { t.Fatal(err) } @@ -324,7 +323,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { defer closer() // Initialize a rekey - status, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }) @@ -384,7 +383,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { if err := client.Sys().Seal(); err != nil { t.Fatal(err) } - sealStatus, err := client.Sys().UnsealWithContext(context.Background(), unsealKey) + sealStatus, err := client.Sys().Unseal(unsealKey) if err != nil { t.Fatal(err) } @@ -417,7 +416,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { } // Get the status for the nonce - status, err := client.Sys().RekeyStatusWithContext(context.Background()) + status, err := client.Sys().RekeyStatus() if err != nil { t.Fatal(err) } @@ -480,7 +479,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String()) } - secret, err := client.Sys().RekeyRetrieveBackupWithContext(context.Background()) + secret, err := client.Sys().RekeyRetrieveBackup() if err == nil { t.Errorf("expected error: %#v", secret) } diff --git a/command/operator_seal_test.go b/command/operator_seal_test.go index 7557b0b8ee29..86722d2e84dd 100644 --- a/command/operator_seal_test.go +++ b/command/operator_seal_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -84,7 +83,7 @@ func TestOperatorSealCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - sealStatus, err := client.Sys().SealStatusWithContext(context.Background()) + sealStatus, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } diff --git a/command/operator_unseal_test.go b/command/operator_unseal_test.go index a633721034fe..06d618cacf72 100644 --- a/command/operator_unseal_test.go +++ b/command/operator_unseal_test.go @@ -2,7 +2,6 @@ package command import ( "bytes" - "context" "encoding/json" "io/ioutil" "os" @@ -60,7 +59,7 @@ func TestOperatorUnsealCommand_Run(t *testing.T) { } // Enter an unseal key - if _, err := client.Sys().UnsealWithContext(context.Background(), keys[0]); err != nil { + if _, err := client.Sys().Unseal(keys[0]); err != nil { t.Fatal(err) } @@ -107,7 +106,7 @@ func TestOperatorUnsealCommand_Run(t *testing.T) { } } - status, err := client.Sys().SealStatusWithContext(context.Background()) + status, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } diff --git a/command/path_map_upgrade_api_test.go b/command/path_map_upgrade_api_test.go index 83cb18f4f4cd..57c1f773405a 100644 --- a/command/path_map_upgrade_api_test.go +++ b/command/path_map_upgrade_api_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "testing" log "github.com/hashicorp/go-hclog" @@ -46,7 +45,7 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // Create an app-id - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/map/app-id/test-app-id", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/map/app-id/test-app-id", map[string]interface{}{ "policy": "test-policy", }) if err != nil { @@ -54,7 +53,7 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // Create a user-id - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/map/user-id/test-user-id", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/map/user-id/test-user-id", map[string]interface{}{ "value": "test-app-id", }) if err != nil { @@ -62,7 +61,7 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // Perform a login. It should succeed. - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/login", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/login", map[string]interface{}{ "app_id": "test-app-id", "user_id": "test-user-id", }) @@ -71,20 +70,20 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // List the hashed app-ids in the storage - secret, err := client.Logical().ListWithContext(context.Background(), "auth/app-id/map/app-id") + secret, err := client.Logical().List("auth/app-id/map/app-id") if err != nil { t.Fatal(err) } hashedAppID := secret.Data["keys"].([]interface{})[0].(string) // Try reading it. This used to cause an issue which is fixed in [GH-3806]. - _, err = client.Logical().ReadWithContext(context.Background(), "auth/app-id/map/app-id/"+hashedAppID) + _, err = client.Logical().Read("auth/app-id/map/app-id/" + hashedAppID) if err != nil { t.Fatal(err) } // Ensure that there was no issue by performing another login - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/login", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/login", map[string]interface{}{ "app_id": "test-app-id", "user_id": "test-user-id", }) diff --git a/command/plugin_deregister_test.go b/command/plugin_deregister_test.go index b8d0fa2d19e0..9696c2f33c66 100644 --- a/command/plugin_deregister_test.go +++ b/command/plugin_deregister_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -89,7 +88,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { ui, cmd := testPluginDeregisterCommand(t) cmd.client = client - if err := client.Sys().RegisterPluginWithContext(context.Background(), &api.RegisterPluginInput{ + if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{ Name: pluginName, Type: consts.PluginTypeCredential, Command: pluginName, @@ -112,7 +111,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - resp, err := client.Sys().ListPluginsWithContext(context.Background(), &api.ListPluginsInput{ + resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{ Type: consts.PluginTypeCredential, }) if err != nil { diff --git a/command/plugin_register_test.go b/command/plugin_register_test.go index c58df9a5a9a5..05b358e6f478 100644 --- a/command/plugin_register_test.go +++ b/command/plugin_register_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -104,7 +103,7 @@ func TestPluginRegisterCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - resp, err := client.Sys().ListPluginsWithContext(context.Background(), &api.ListPluginsInput{ + resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{ Type: consts.PluginTypeCredential, }) if err != nil { diff --git a/command/plugin_reload_test.go b/command/plugin_reload_test.go index 3ccc997e79fc..99b0c03c7f6e 100644 --- a/command/plugin_reload_test.go +++ b/command/plugin_reload_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -95,7 +94,7 @@ func TestPluginReloadCommand_Run(t *testing.T) { ui, cmd := testPluginReloadCommand(t) cmd.client = client - if err := client.Sys().RegisterPluginWithContext(context.Background(), &api.RegisterPluginInput{ + if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{ Name: pluginName, Type: consts.PluginTypeCredential, Command: pluginName, diff --git a/command/plugin_test.go b/command/plugin_test.go index 6a0a97770808..786abdb52f4e 100644 --- a/command/plugin_test.go +++ b/command/plugin_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "crypto/sha256" "fmt" "io" @@ -68,7 +67,7 @@ func testPluginCreateAndRegister(tb testing.TB, client *api.Client, dir, name st pth, sha256Sum := testPluginCreate(tb, dir, name) - if err := client.Sys().RegisterPluginWithContext(context.Background(), &api.RegisterPluginInput{ + if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{ Name: name, Type: pluginType, Command: name, diff --git a/command/policy_delete_test.go b/command/policy_delete_test.go index d8b84654b07a..2c822de9d4fd 100644 --- a/command/policy_delete_test.go +++ b/command/policy_delete_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "reflect" "strings" "testing" @@ -78,7 +77,7 @@ func TestPolicyDeleteCommand_Run(t *testing.T) { defer closer() policy := `path "secret/" {}` - if err := client.Sys().PutPolicyWithContext(context.Background(), "my-policy", policy); err != nil { + if err := client.Sys().PutPolicy("my-policy", policy); err != nil { t.Fatal(err) } @@ -98,7 +97,7 @@ func TestPolicyDeleteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - policies, err := client.Sys().ListPoliciesWithContext(context.Background()) + policies, err := client.Sys().ListPolicies() if err != nil { t.Fatal(err) } diff --git a/command/policy_read_test.go b/command/policy_read_test.go index ca09c8cb5065..8cd7c066b8ce 100644 --- a/command/policy_read_test.go +++ b/command/policy_read_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -77,7 +76,7 @@ func TestPolicyReadCommand_Run(t *testing.T) { defer closer() policy := `path "secret/" {}` - if err := client.Sys().PutPolicyWithContext(context.Background(), "my-policy", policy); err != nil { + if err := client.Sys().PutPolicy("my-policy", policy); err != nil { t.Fatal(err) } diff --git a/command/policy_write_test.go b/command/policy_write_test.go index f89667cccbd4..c8db7dc9ddc2 100644 --- a/command/policy_write_test.go +++ b/command/policy_write_test.go @@ -2,7 +2,6 @@ package command import ( "bytes" - "context" "io" "io/ioutil" "os" @@ -124,7 +123,7 @@ func TestPolicyWriteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - policies, err := client.Sys().ListPoliciesWithContext(context.Background()) + policies, err := client.Sys().ListPolicies() if err != nil { t.Fatal(err) } @@ -165,7 +164,7 @@ func TestPolicyWriteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - policies, err := client.Sys().ListPoliciesWithContext(context.Background()) + policies, err := client.Sys().ListPolicies() if err != nil { t.Fatal(err) } diff --git a/command/read_test.go b/command/read_test.go index 78f7e0ae4340..13f41da7e4a1 100644 --- a/command/read_test.go +++ b/command/read_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -84,7 +83,7 @@ func TestReadCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/read/foo", map[string]interface{}{ + if _, err := client.Logical().Write("secret/read/foo", map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) diff --git a/command/rotate_test.go b/command/rotate_test.go index 7560a3f63c7f..37ac32340590 100644 --- a/command/rotate_test.go +++ b/command/rotate_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -84,7 +83,7 @@ func TestOperatorRotateCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().KeyStatusWithContext(context.Background()) + status, err := client.Sys().KeyStatus() if err != nil { t.Fatal(err) } diff --git a/command/secrets_disable_test.go b/command/secrets_disable_test.go index f6b07a859b9a..567c8956d630 100644 --- a/command/secrets_disable_test.go +++ b/command/secrets_disable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -89,7 +88,7 @@ func TestSecretsDisableCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "my-secret/", &api.MountInput{ + if err := client.Sys().Mount("my-secret/", &api.MountInput{ Type: "generic", }); err != nil { t.Fatal(err) @@ -111,7 +110,7 @@ func TestSecretsDisableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/secrets_enable_test.go b/command/secrets_enable_test.go index a8dff093fe4d..814f4731204f 100644 --- a/command/secrets_enable_test.go +++ b/command/secrets_enable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "io/ioutil" "strings" "testing" @@ -128,7 +127,7 @@ func TestSecretsEnableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/secrets_move_test.go b/command/secrets_move_test.go index 4aed4460e0d0..153fbeb2cdc0 100644 --- a/command/secrets_move_test.go +++ b/command/secrets_move_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -98,7 +97,7 @@ func TestSecretsMoveCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/secrets_tune_test.go b/command/secrets_tune_test.go index b1e35c6d8c9e..f51b8fb34b78 100644 --- a/command/secrets_tune_test.go +++ b/command/secrets_tune_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -81,7 +80,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + if err := client.Sys().Mount("kv", &api.MountInput{ Type: "kv", Options: map[string]string{ "version": "2", @@ -91,7 +90,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { } // confirm default max_versions - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -126,7 +125,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err = client.Sys().ListMountsWithContext(context.Background()) + mounts, err = client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -156,7 +155,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "mount_tune_integration", &api.MountInput{ + if err := client.Sys().Mount("mount_tune_integration", &api.MountInput{ Type: "pki", }); err != nil { t.Fatal(err) @@ -185,7 +184,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -233,7 +232,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "mount_tune_integration", &api.MountInput{ + if err := client.Sys().Mount("mount_tune_integration", &api.MountInput{ Type: "pki", Description: "initial description", }); err != nil { @@ -254,7 +253,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -276,7 +275,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "mount_tune_integration", &api.MountInput{ + if err := client.Sys().Mount("mount_tune_integration", &api.MountInput{ Type: "pki", Description: "initial description", }); err != nil { @@ -297,7 +296,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/server.go b/command/server.go index 656bfc28570f..1b0a3231a9c8 100644 --- a/command/server.go +++ b/command/server.go @@ -2518,6 +2518,8 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical. ClusterName: config.ClusterName, CacheSize: config.CacheSize, PluginDirectory: config.PluginDirectory, + PluginFileUid: config.PluginFileUid, + PluginFilePermissions: config.PluginFilePermissions, EnableUI: config.EnableUI, EnableRaw: config.EnableRawEndpoint, DisableSealWrap: config.DisableSealWrap, @@ -2535,6 +2537,7 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical. LicensePath: config.LicensePath, DisableSSCTokens: config.DisableSSCTokens, } + if c.flagDev { coreConfig.EnableRaw = true coreConfig.DevToken = c.flagDevRootTokenID diff --git a/command/server/config.go b/command/server/config.go index 92a911185231..911dfe537ce9 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -16,7 +16,9 @@ import ( "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/vault/helper/osutil" "github.com/hashicorp/vault/internalshared/configutil" + "github.com/hashicorp/vault/sdk/helper/consts" ) var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError { @@ -54,6 +56,11 @@ type Config struct { PluginDirectory string `hcl:"plugin_directory"` + PluginFileUid int `hcl:"plugin_file_uid"` + + PluginFilePermissions int `hcl:"-"` + PluginFilePermissionsRaw interface{} `hcl:"plugin_file_permissions,alias:PluginFilePermissions"` + EnableRawEndpoint bool `hcl:"-"` EnableRawEndpointRaw interface{} `hcl:"raw_storage_endpoint,alias:EnableRawEndpoint"` @@ -127,7 +134,6 @@ telemetry { prometheus_retention_time = "24h" disable_hostname = true } - enable_raw_endpoint = true storage "%s" { @@ -276,6 +282,17 @@ func (c *Config) Merge(c2 *Config) *Config { result.PluginDirectory = c2.PluginDirectory } + result.PluginFileUid = c.PluginFileUid + if c2.PluginFileUid != 0 { + result.PluginFileUid = c2.PluginFileUid + } + + result.PluginFilePermissions = c.PluginFilePermissions + if c2.PluginFilePermissionsRaw != nil { + result.PluginFilePermissions = c2.PluginFilePermissions + result.PluginFilePermissionsRaw = c2.PluginFilePermissionsRaw + } + result.DisablePerformanceStandby = c.DisablePerformanceStandby if c2.DisablePerformanceStandby { result.DisablePerformanceStandby = c2.DisablePerformanceStandby @@ -350,6 +367,13 @@ func LoadConfig(path string) (*Config, error) { } if fi.IsDir() { + // check permissions on the config directory + if os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" { + err = osutil.OwnerPermissionsMatch(path, 0, 0) + if err != nil { + return nil, err + } + } return CheckConfig(LoadConfigDir(path)) } return CheckConfig(LoadConfigFile(path)) @@ -385,6 +409,21 @@ func LoadConfigFile(path string) (*Config, error) { return nil, err } + if os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" { + // check permissions of the config file + err = osutil.OwnerPermissionsMatch(path, 0, 0) + if err != nil { + return nil, err + } + // check permissions of the plugin directory + if conf.PluginDirectory != "" { + + err = osutil.OwnerPermissionsMatch(conf.PluginDirectory, conf.PluginFileUid, conf.PluginFilePermissions) + if err != nil { + return nil, err + } + } + } return conf, nil } @@ -459,6 +498,18 @@ func ParseConfig(d, source string) (*Config, error) { } } + if result.PluginFilePermissionsRaw != nil { + octalPermissionsString, err := parseutil.ParseString(result.PluginFilePermissionsRaw) + if err != nil { + return nil, err + } + pluginFilePermissions, err := strconv.ParseInt(octalPermissionsString, 8, 64) + if err != nil { + return nil, err + } + result.PluginFilePermissions = int(pluginFilePermissions) + } + if result.DisableSentinelTraceRaw != nil { if result.DisableSentinelTrace, err = parseutil.ParseBool(result.DisableSentinelTraceRaw); err != nil { return nil, err @@ -838,6 +889,10 @@ func (c *Config) Sanitized() map[string]interface{} { "plugin_directory": c.PluginDirectory, + "plugin_file_uid": c.PluginFileUid, + + "plugin_file_permissions": c.PluginFilePermissions, + "raw_storage_endpoint": c.EnableRawEndpoint, "api_addr": c.APIAddr, diff --git a/command/server/config_test_helpers.go b/command/server/config_test_helpers.go index 30260466feb5..424d8fe816b3 100644 --- a/command/server/config_test_helpers.go +++ b/command/server/config_test_helpers.go @@ -694,6 +694,8 @@ func testConfig_Sanitized(t *testing.T) { "disable_indexing": false, "disable_mlock": true, "disable_performance_standby": false, + "plugin_file_uid": 0, + "plugin_file_permissions": 0, "disable_printable_check": false, "disable_sealwrap": true, "raw_storage_endpoint": true, @@ -855,6 +857,7 @@ func testParseSockaddrTemplate(t *testing.T) { api_addr = <.+)", + Fields: map[string]*FieldSchema{ + "an_int": {Type: TypeInt}, + "a_string": {Type: TypeString}, + "name": {Type: TypeString}, + }, + Operations: map[logical.Operation]OperationHandler{ + logical.UpdateOperation: &PathOperation{Callback: handler}, + }, + }, + }, + } + ctx := context.Background() + resp, err := backend.HandleRequest(ctx, &logical.Request{ + Operation: logical.UpdateOperation, + Path: "foo/bar/baz", + Data: map[string]interface{}{ + "an_int": 10, + "a_string": "accepted", + "unrecognized1": "unrecognized", + "unrecognized2": 20.2, + "name": "noop", + }, + }) + require.NoError(t, err) + require.NotNil(t, resp) + t.Log(resp.Warnings) + require.Len(t, resp.Warnings, 2) + require.True(t, strutil.StrListContains(resp.Warnings, "Endpoint ignored these unrecognized parameters: [unrecognized1 unrecognized2]")) + require.True(t, strutil.StrListContains(resp.Warnings, "Endpoint replaced the value of these parameters with the values captured from the endpoint's path: [name]")) +} + func TestBackendHandleRequest(t *testing.T) { callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) { return &logical.Response{ diff --git a/sdk/helper/certutil/helpers.go b/sdk/helper/certutil/helpers.go index b6bee2e342c7..c26020f43575 100644 --- a/sdk/helper/certutil/helpers.go +++ b/sdk/helper/certutil/helpers.go @@ -585,25 +585,17 @@ func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) { // certain internal circumstances. func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) { if keyType == "ec" { - // To comply with BSI recommendations Section 4.2 and Mozilla root - // store policy section 5.1.2, enforce that NIST P-curves use a hash - // length corresponding to curve length. Note that ed25519 does not - // the "ec" key type. - expectedHashBits := expectedNISTPCurveHashBits[keyBits] - - if expectedHashBits != hashBits && hashBits != 0 { - return hashBits, fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", hashBits, keyBits) - } else if hashBits == 0 { - hashBits = expectedHashBits - } + // Enforcement of curve moved to selectSignatureAlgorithmForECDSA. See + // note there about why. } else if keyType == "rsa" && hashBits == 0 { // To match previous behavior (and ignoring NIST's recommendations for // hash size to align with RSA key sizes), default to SHA-2-256. hashBits = 256 - } else if keyType == "ed25519" || keyType == "ed448" { + } else if keyType == "ed25519" || keyType == "ed448" || keyType == "any" { // No-op; ed25519 and ed448 internally specify their own hash and // we do not need to select one. Double hashing isn't supported in - // certificate signing and we must + // certificate signing. Additionally, the any key type can't know + // what hash algorithm to use yet, so default to zero. return 0, nil } @@ -642,7 +634,7 @@ func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, h // Validates that the length of the hash (in bits) used in the signature // calculation is a known, approved value. func ValidateSignatureLength(keyType string, hashBits int) error { - if keyType == "ed25519" || keyType == "ed448" { + if keyType == "any" || keyType == "ec" || keyType == "ed25519" || keyType == "ed448" { // ed25519 and ed448 include built-in hashing and is not externally // configurable. There are three modes for each of these schemes: // @@ -654,6 +646,13 @@ func ValidateSignatureLength(keyType string, hashBits int) error { // // In all cases, we won't have a hash algorithm to validate here, so // return nil. + // + // Additionally, when KeyType is any, we can't yet validate the + // signature algorithm size, so it takes the default zero value. + // + // When KeyType is ec, we also can't validate this value as we're + // forcefully ignoring the users' choice and specifying a value based + // on issuer type. return nil } @@ -859,16 +858,25 @@ func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGen } func selectSignatureAlgorithmForECDSA(pub crypto.PublicKey, signatureBits int) x509.SignatureAlgorithm { - // If signature bits are configured, prefer them to the default choice. - switch signatureBits { - case 256: - return x509.ECDSAWithSHA256 - case 384: - return x509.ECDSAWithSHA384 - case 512: - return x509.ECDSAWithSHA512 - } - + // Previously we preferred the user-specified signature bits for ECDSA + // keys. However, this could result in using a longer hash function than + // the underlying NIST P-curve will encode (e.g., a SHA-512 hash with a + // P-256 key). This isn't ideal: the hash is implicitly truncated + // (effectively turning it into SHA-512/256) and we then need to rely + // on the prefix security of the hash. Since both NIST and Mozilla guidance + // suggest instead using the correct hash function, we should prefer that + // over the operator-specified signatureBits. + // + // Lastly, note that pub above needs to be the _signer's_ public key; + // the issue with DefaultOrValueHashBits is that it is called at role + // configuration time, which might _precede_ issuer generation. Thus + // it only has access to the desired key type and not the actual issuer. + // The reference from that function is reproduced below: + // + // > To comply with BSI recommendations Section 4.2 and Mozilla root + // > store policy section 5.1.2, enforce that NIST P-curves use a hash + // > length corresponding to curve length. Note that ed25519 does not + // > implement the "ec" key type. key, ok := pub.(*ecdsa.PublicKey) if !ok { return x509.ECDSAWithSHA256 diff --git a/sdk/helper/consts/consts.go b/sdk/helper/consts/consts.go index b10f57a22925..86d4f06967e9 100644 --- a/sdk/helper/consts/consts.go +++ b/sdk/helper/consts/consts.go @@ -32,4 +32,6 @@ const ( // ReplicationResolverALPN is the negotiated protocol used for // resolving replicaiton addresses ReplicationResolverALPN = "replication_resolver_v1" + + VaultDisableFilePermissionsCheckEnv = "VAULT_DISABLE_FILE_PERMISSIONS_CHECK" ) diff --git a/sdk/helper/pluginutil/multiplexing.pb.go b/sdk/helper/pluginutil/multiplexing.pb.go index fa3357d49045..d0ff51e57b24 100644 --- a/sdk/helper/pluginutil/multiplexing.pb.go +++ b/sdk/helper/pluginutil/multiplexing.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/helper/pluginutil/multiplexing.proto package pluginutil diff --git a/sdk/helper/useragent/useragent.go b/sdk/helper/useragent/useragent.go index 9bc212535cdc..e7e23ac2b6f5 100644 --- a/sdk/helper/useragent/useragent.go +++ b/sdk/helper/useragent/useragent.go @@ -44,12 +44,29 @@ func String(comments ...string) string { // Given comments will be appended to the semicolon-delimited comment section. // // e.g. Vault/0.10.4 (+https://www.vaultproject.io/; azure-auth; go1.10.1; comment-0; comment-1) +// +// Returns an empty string if the given env is nil. func PluginString(env *logical.PluginEnvironment, pluginName string, comments ...string) string { + if env == nil { + return "" + } + + // Construct comments c := []string{"+" + projectURL} if pluginName != "" { c = append(c, pluginName) } c = append(c, rt) c = append(c, comments...) - return fmt.Sprintf("Vault/%s (%s)", env.VaultVersion, strings.Join(c, "; ")) + + // Construct version string + v := env.VaultVersion + if env.VaultVersionPrerelease != "" { + v = fmt.Sprintf("%s-%s", v, env.VaultVersionPrerelease) + } + if env.VaultVersionMetadata != "" { + v = fmt.Sprintf("%s+%s", v, env.VaultVersionMetadata) + } + + return fmt.Sprintf("Vault/%s (%s)", v, strings.Join(c, "; ")) } diff --git a/sdk/helper/useragent/useragent_test.go b/sdk/helper/useragent/useragent_test.go index bfd439603f55..f0d014f6c570 100644 --- a/sdk/helper/useragent/useragent_test.go +++ b/sdk/helper/useragent/useragent_test.go @@ -51,12 +51,10 @@ func TestUserAgent(t *testing.T) { func TestUserAgentPlugin(t *testing.T) { projectURL = "https://vault-test.com" rt = "go5.0" - env := &logical.PluginEnvironment{ - VaultVersion: "1.2.3", - } type args struct { pluginName string + pluginEnv *logical.PluginEnvironment comments []string } tests := []struct { @@ -64,15 +62,38 @@ func TestUserAgentPlugin(t *testing.T) { args args want string }{ + { + name: "Plugin user agent with nil plugin env", + args: args{ + pluginEnv: nil, + }, + want: "", + }, { name: "Plugin user agent without plugin name", - args: args{}, + args: args{ + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + }, + want: "Vault/1.2.3 (+https://vault-test.com; go5.0)", + }, + { + name: "Plugin user agent without plugin name", + args: args{ + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + }, want: "Vault/1.2.3 (+https://vault-test.com; go5.0)", }, { name: "Plugin user agent with plugin name", args: args{ pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, }, want: "Vault/1.2.3 (+https://vault-test.com; azure-auth; go5.0)", }, @@ -80,7 +101,10 @@ func TestUserAgentPlugin(t *testing.T) { name: "Plugin user agent with plugin name and additional comment", args: args{ pluginName: "azure-auth", - comments: []string{"pid-abcdefg"}, + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + comments: []string{"pid-abcdefg"}, }, want: "Vault/1.2.3 (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg)", }, @@ -88,21 +112,64 @@ func TestUserAgentPlugin(t *testing.T) { name: "Plugin user agent with plugin name and additional comments", args: args{ pluginName: "azure-auth", - comments: []string{"pid-abcdefg", "cloud-provider"}, + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, }, want: "Vault/1.2.3 (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", }, { name: "Plugin user agent with no plugin name and additional comments", args: args{ + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, comments: []string{"pid-abcdefg", "cloud-provider"}, }, want: "Vault/1.2.3 (+https://vault-test.com; go5.0; pid-abcdefg; cloud-provider)", }, + { + name: "Plugin user agent with version prerelease", + args: args{ + pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + VaultVersionPrerelease: "dev", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, + }, + want: "Vault/1.2.3-dev (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", + }, + { + name: "Plugin user agent with version metadata", + args: args{ + pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + VaultVersionMetadata: "ent", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, + }, + want: "Vault/1.2.3+ent (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", + }, + { + name: "Plugin user agent with version prerelease and metadata", + args: args{ + pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + VaultVersionPrerelease: "dev", + VaultVersionMetadata: "ent", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, + }, + want: "Vault/1.2.3-dev+ent (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := PluginString(env, tt.args.pluginName, tt.args.comments...); got != tt.want { + if got := PluginString(tt.args.pluginEnv, tt.args.pluginName, tt.args.comments...); got != tt.want { t.Errorf("PluginString() = %v, want %v", got, tt.want) } }) diff --git a/sdk/logical/identity.pb.go b/sdk/logical/identity.pb.go index c472b68a099e..4b1a36b39826 100644 --- a/sdk/logical/identity.pb.go +++ b/sdk/logical/identity.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/logical/identity.proto package logical diff --git a/sdk/logical/plugin.pb.go b/sdk/logical/plugin.pb.go index d4722ce09761..1fb53f9a79c9 100644 --- a/sdk/logical/plugin.pb.go +++ b/sdk/logical/plugin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/logical/plugin.proto package logical @@ -27,6 +27,10 @@ type PluginEnvironment struct { // VaultVersion is the version of the Vault server VaultVersion string `protobuf:"bytes,1,opt,name=vault_version,json=vaultVersion,proto3" json:"vault_version,omitempty"` + // VaultVersionPrerelease is the prerelease information of the Vault server + VaultVersionPrerelease string `protobuf:"bytes,2,opt,name=vault_version_prerelease,json=vaultVersionPrerelease,proto3" json:"vault_version_prerelease,omitempty"` + // VaultVersionMetadata is the version metadata of the Vault server + VaultVersionMetadata string `protobuf:"bytes,3,opt,name=vault_version_metadata,json=vaultVersionMetadata,proto3" json:"vault_version_metadata,omitempty"` } func (x *PluginEnvironment) Reset() { @@ -68,18 +72,39 @@ func (x *PluginEnvironment) GetVaultVersion() string { return "" } +func (x *PluginEnvironment) GetVaultVersionPrerelease() string { + if x != nil { + return x.VaultVersionPrerelease + } + return "" +} + +func (x *PluginEnvironment) GetVaultVersionMetadata() string { + if x != nil { + return x.VaultVersionMetadata + } + return "" +} + var File_sdk_logical_plugin_proto protoreflect.FileDescriptor var file_sdk_logical_plugin_proto_rawDesc = []byte{ 0x0a, 0x18, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67, 0x69, - 0x63, 0x61, 0x6c, 0x22, 0x38, 0x0a, 0x11, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, - 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, 0x6c, - 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x28, 0x5a, - 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, - 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x61, 0x6c, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, + 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, + 0x0a, 0x18, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x16, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, + 0x65, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x76, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x28, + 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/logical/plugin.proto b/sdk/logical/plugin.proto index 5992c21395a3..f2df6c75d97c 100644 --- a/sdk/logical/plugin.proto +++ b/sdk/logical/plugin.proto @@ -7,4 +7,10 @@ package logical; message PluginEnvironment { // VaultVersion is the version of the Vault server string vault_version = 1; + + // VaultVersionPrerelease is the prerelease information of the Vault server + string vault_version_prerelease = 2; + + // VaultVersionMetadata is the version metadata of the Vault server + string vault_version_metadata = 3; } diff --git a/sdk/plugin/grpc_system_test.go b/sdk/plugin/grpc_system_test.go index cf1449f0599f..6e78ba87eba7 100644 --- a/sdk/plugin/grpc_system_test.go +++ b/sdk/plugin/grpc_system_test.go @@ -213,7 +213,9 @@ func TestSystem_GRPC_groupsForEntity(t *testing.T) { func TestSystem_GRPC_pluginEnv(t *testing.T) { sys := logical.TestSystemView() sys.PluginEnvironment = &logical.PluginEnvironment{ - VaultVersion: "0.10.42", + VaultVersion: "0.10.42", + VaultVersionPrerelease: "dev", + VaultVersionMetadata: "prem", } client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ diff --git a/sdk/plugin/pb/backend.pb.go b/sdk/plugin/pb/backend.pb.go index 184717a97540..dbad4da977ce 100644 --- a/sdk/plugin/pb/backend.pb.go +++ b/sdk/plugin/pb/backend.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/plugin/pb/backend.proto package pb diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js index 490711636dab..8497d76ff206 100644 --- a/ui/app/components/auth-form.js +++ b/ui/app/components/auth-form.js @@ -117,7 +117,8 @@ export default Component.extend(DEFAULTS, { if (!methods && !wrappedToken) { return {}; } - if (keyIsPath) { + // if type is provided we can ignore path since we are attempting to lookup a specific backend by type + if (keyIsPath && !type) { return methods.findBy('path', selected); } return BACKENDS.findBy('type', selected); @@ -227,7 +228,7 @@ export default Component.extend(DEFAULTS, { }); this.onSuccess(authResponse, backendType, data); } catch (e) { - this.set('loading', false); + this.set('isLoading', false); if (!this.auth.mfaError) { this.set('error', `Authentication failed: ${this.auth.handleError(e)}`); } @@ -260,7 +261,7 @@ export default Component.extend(DEFAULTS, { error: null, }); // if callback from oidc or jwt we have a token at this point - let backend = ['oidc', 'jwt'].includes(this.selectedAuth) + let backend = ['oidc', 'jwt'].includes(this.providerName) ? this.getAuthBackend('token') : this.selectedAuthBackend || {}; let backendMeta = BACKENDS.find( @@ -279,7 +280,7 @@ export default Component.extend(DEFAULTS, { }, handleError(e) { this.setProperties({ - loading: false, + isLoading: false, error: e ? this.auth.handleError(e) : null, }); }, diff --git a/ui/app/components/auth-jwt.js b/ui/app/components/auth-jwt.js index 6d200fd74744..f5ae2a44fb5a 100644 --- a/ui/app/components/auth-jwt.js +++ b/ui/app/components/auth-jwt.js @@ -178,8 +178,14 @@ export default Component.extend({ if (!this.isOIDC || !this.role || !this.role.authUrl) { return; } - - await this.fetchRole.perform(this.roleName, { debounce: false }); + try { + await this.fetchRole.perform(this.roleName, { debounce: false }); + } catch (error) { + // this task could be cancelled if the instances in didReceiveAttrs resolve after this was started + if (error?.name !== 'TaskCancelation') { + throw error; + } + } let win = this.getWindow(); const POPUP_WIDTH = 500; diff --git a/ui/app/components/secret-create-or-update.js b/ui/app/components/secret-create-or-update.js index 05f97327ecaa..ea7ba030942f 100644 --- a/ui/app/components/secret-create-or-update.js +++ b/ui/app/components/secret-create-or-update.js @@ -129,6 +129,9 @@ export default class SecretCreateOrUpdate extends Component { return secretData .save() .then(() => { + if (!this.args.canReadSecretData && secret.selectedVersion) { + delete secret.selectedVersion.secretData; + } if (!secretData.isError) { if (isV2) { secret.set('id', key); diff --git a/ui/app/components/secret-edit-toolbar.js b/ui/app/components/secret-edit-toolbar.js index f2bfb44c54db..b953c2545155 100644 --- a/ui/app/components/secret-edit-toolbar.js +++ b/ui/app/components/secret-edit-toolbar.js @@ -12,7 +12,6 @@ * @secretDataIsAdvanced={{secretDataIsAdvanced}} * @showAdvancedMode={{showAdvancedMode}} * @modelForData={{this.modelForData}} - * @navToNearestAncestor={{this.navToNearestAncestor}} * @canUpdateSecretData={{canUpdateSecretData}} * @codemirrorString={{codemirrorString}} * @wrappedData={{wrappedData}} @@ -30,7 +29,6 @@ * @param {boolean} secretDataIsAdvanced - used to determine if show JSON toggle * @param {boolean} showAdvacnedMode - used for JSON toggle * @param {object} modelForData - a modified version of the model with secret data - * @param {string} navToNearestAncestor - route to nav to if press cancel * @param {boolean} canUpdateSecretData - permissions that show the create new version button or not. * @param {string} codemirrorString - used to copy the JSON * @param {object} wrappedData - when copy the data it's the token of the secret returned. diff --git a/ui/app/components/secret-edit.js b/ui/app/components/secret-edit.js index 5e968a71586c..9b413d74691e 100644 --- a/ui/app/components/secret-edit.js +++ b/ui/app/components/secret-edit.js @@ -1,84 +1,63 @@ +/* eslint ember/no-computed-properties-in-native-classes: 'warn' */ /** * @module SecretEdit * SecretEdit component manages the secret and model data, and displays either the create, update, empty state or show view of a KV secret. * * @example * ```js - * + * * ``` -/ - * @param {object} model - Model returned from route secret-v2 +/This component is initialized from the secret-edit-layout.hbs file + * @param {object} model - Model returned from secret-v2 which is generated in the secret-edit route + * @param {string} mode - Edit, create, etc. + * @param {string} baseKey - Provided for navigation. + * @param {object} key - Passed through, copy of the model. + * @param {string} initialKey - model's name. + * @param {function} onRefresh - action that refreshes the model + * @param {function} onToggleAdvancedEdit - changes the preferAdvancedEdit to true or false + * @param {boolean} preferAdvancedEdit - property set from the controller of show/edit/create route passed in through secret-edit-layout */ import { inject as service } from '@ember/service'; -import Component from '@ember/component'; -import { computed } from '@ember/object'; -import { alias, or } from '@ember/object/computed'; -import FocusOnInsertMixin from 'vault/mixins/focus-on-insert'; -import WithNavToNearestAncestor from 'vault/mixins/with-nav-to-nearest-ancestor'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; import KVObject from 'vault/lib/kv-object'; import { maybeQueryRecord } from 'vault/macros/maybe-query-record'; +import { alias, or } from '@ember/object/computed'; -export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { - wizard: service(), - store: service(), - - // a key model - key: null, - model: null, - - // a value to pre-fill the key input - this is populated by the corresponding - // 'initialKey' queryParam - initialKey: null, - - // set in the route's setupController hook - mode: null, - - secretData: null, - - // called with a bool indicating if there's been a change in the secretData and customMetadata - onDataChange() {}, - onRefresh() {}, - onToggleAdvancedEdit() {}, - - // did user request advanced mode - preferAdvancedEdit: false, - - // use a named action here so we don't have to pass one in - // this will bubble to the route - toggleAdvancedEdit: 'toggleAdvancedEdit', - - codemirrorString: null, +export default class SecretEdit extends Component { + @service wizard; + @service store; - isV2: false, + @tracked secretData = null; + @tracked isV2 = false; + @tracked codemirrorString = null; - init() { - this._super(...arguments); - let secrets = this.model.secretData; - if (!secrets && this.model.selectedVersion) { - this.set('isV2', true); - secrets = this.model.belongsTo('selectedVersion').value().secretData; + constructor() { + super(...arguments); + let secrets = this.args.model.secretData; + if (!secrets && this.args.model.selectedVersion) { + this.isV2 = true; + secrets = this.args.model.belongsTo('selectedVersion').value().secretData; } const data = KVObject.create({ content: [] }).fromJSON(secrets); - this.set('secretData', data); - this.set('codemirrorString', data.toJSONString()); - if (data.isAdvanced()) { - this.set('preferAdvancedEdit', true); - } - if (this.wizard.featureState === 'details' && this.mode === 'create') { - let engine = this.model.backend.includes('kv') ? 'kv' : this.model.backend; + this.secretData = data; + this.codemirrorString = data.toJSONString(); + if (this.wizard.featureState === 'details' && this.args.mode === 'create') { + let engine = this.args.model.backend.includes('kv') ? 'kv' : this.args.model.backend; this.wizard.transitionFeatureMachine('details', 'CONTINUE', engine); } - }, + } - checkSecretCapabilities: maybeQueryRecord( + @maybeQueryRecord( 'capabilities', (context) => { - if (!context.model || context.mode === 'create') { + if (!context.args.model || context.args.mode === 'create') { return; } - let backend = context.isV2 ? context.get('model.engine.id') : context.model.backend; - let id = context.model.id; + let backend = context.isV2 ? context.args.model.engine.id : context.args.model.backend; + let id = context.args.model.id; let path = context.isV2 ? `${backend}/data/${id}` : `${backend}/${id}`; return { id: path, @@ -88,17 +67,18 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { 'model', 'model.id', 'mode' - ), - canUpdateSecretData: alias('checkSecretCapabilities.canUpdate'), - canReadSecretData: alias('checkSecretCapabilities.canRead'), + ) + checkSecretCapabilities; + @alias('checkSecretCapabilities.canUpdate') canUpdateSecretData; + @alias('checkSecretCapabilities.canRead') canReadSecretData; - checkMetadataCapabilities: maybeQueryRecord( + @maybeQueryRecord( 'capabilities', (context) => { - if (!context.model || !context.isV2) { + if (!context.args.model || !context.isV2) { return; } - let backend = context.model.backend; + let backend = context.args.model.backend; let path = `${backend}/metadata/`; return { id: path, @@ -108,60 +88,59 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { 'model', 'model.id', 'mode' - ), - canDeleteSecretMetadata: alias('checkMetadataCapabilities.canDelete'), - canUpdateSecretMetadata: alias('checkMetadataCapabilities.canUpdate'), - canReadSecretMetadata: alias('checkMetadataCapabilities.canRead'), + ) + checkMetadataCapabilities; + @alias('checkMetadataCapabilities.canDelete') canDeleteSecretMetadata; + @alias('checkMetadataCapabilities.canUpdate') canUpdateSecretMetadata; + @alias('checkMetadataCapabilities.canRead') canReadSecretMetadata; - requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'), + @or('model.isLoading', 'model.isReloading', 'model.isSaving') requestInFlight; + @or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid') buttonDisabled; - buttonDisabled: or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid'), - - modelForData: computed('isV2', 'model', function () { - let { model } = this; + get modelForData() { + let { model } = this.args; if (!model) return null; return this.isV2 ? model.belongsTo('selectedVersion').value() : model; - }), + } - basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function () { + get basicModeDisabled() { return this.secretDataIsAdvanced || this.showAdvancedMode === false; - }), + } - secretDataAsJSON: computed('secretData', 'secretData.[]', function () { + get secretDataAsJSON() { return this.secretData.toJSON(); - }), + } - secretDataIsAdvanced: computed('secretData', 'secretData.[]', function () { + get secretDataIsAdvanced() { return this.secretData.isAdvanced(); - }), + } - showAdvancedMode: or('secretDataIsAdvanced', 'preferAdvancedEdit'), + get showAdvancedMode() { + return this.secretDataIsAdvanced || this.args.preferAdvancedEdit; + } - isWriteWithoutRead: computed( - 'model.failedServerRead', - 'modelForData.failedServerRead', - 'isV2', - function () { - if (!this.model) return; - // if the version couldn't be read from the server - if (this.isV2 && this.modelForData.failedServerRead) { - return true; - } - // if the model couldn't be read from the server - if (!this.isV2 && this.model.failedServerRead) { - return true; - } + get isWriteWithoutRead() { + if (!this.args.model) { return false; } - ), - - actions: { - refresh() { - this.onRefresh(); - }, - - toggleAdvanced(bool) { - this.onToggleAdvancedEdit(bool); - }, - }, -}); + // if the version couldn't be read from the server + if (this.isV2 && this.modelForData.failedServerRead) { + return true; + } + // if the model couldn't be read from the server + if (!this.isV2 && this.args.model.failedServerRead) { + return true; + } + return false; + } + + @action + refresh() { + this.args.onRefresh(); + } + + @action + toggleAdvanced(bool) { + this.args.onToggleAdvancedEdit(bool); + } +} diff --git a/ui/app/models/auth-method.js b/ui/app/models/auth-method.js index 3ba5eb335194..a54cc80d4f02 100644 --- a/ui/app/models/auth-method.js +++ b/ui/app/models/auth-method.js @@ -116,9 +116,9 @@ export default attachCapabilities(ModelExport, { deletePath: apiPath`sys/auth/${'id'}`, configPath: function (context) { if (context.type === 'aws') { - return apiPath`auth/${'id'}/config/client`; + return apiPath`auth/${'id'}/config/client`.call(this, context); } else { - return apiPath`auth/${'id'}/config`; + return apiPath`auth/${'id'}/config`.call(this, context); } }, }); diff --git a/ui/app/models/clients/activity.js b/ui/app/models/clients/activity.js index f4edac595bbc..2462336989e2 100644 --- a/ui/app/models/clients/activity.js +++ b/ui/app/models/clients/activity.js @@ -1,10 +1,12 @@ import Model, { attr } from '@ember-data/model'; export default class Activity extends Model { - @attr('string') responseTimestamp; + @attr('array') byMonthTotalClients; + @attr('array') byMonthNewClients; @attr('array') byNamespace; + @attr('object') total; @attr('array') formattedEndTime; @attr('array') formattedStartTime; @attr('string') startTime; @attr('string') endTime; - @attr('object') total; + @attr('string') responseTimestamp; } diff --git a/ui/app/serializers/clients/activity.js b/ui/app/serializers/clients/activity.js index 1b1926ed138c..c831f8b9f86f 100644 --- a/ui/app/serializers/clients/activity.js +++ b/ui/app/serializers/clients/activity.js @@ -11,14 +11,17 @@ export default class ActivitySerializer extends ApplicationSerializer { Object.keys(ns['counts']).forEach((key) => (flattenedNs[key] = ns['counts'][key])); flattenedNs = this.homogenizeClientNaming(flattenedNs); - // TODO CMB check how this works with actual API endpoint // if no mounts, mounts will be an empty array flattenedNs.mounts = ns.mounts ? ns.mounts.map((mount) => { let flattenedMount = {}; - flattenedMount.label = mount['mount_path']; + let label = mount['mount_path']; Object.keys(mount['counts']).forEach((key) => (flattenedMount[key] = mount['counts'][key])); - return flattenedMount; + flattenedMount = this.homogenizeClientNaming(flattenedMount); + return { + label, + ...flattenedMount, + }; }) : []; @@ -29,22 +32,59 @@ export default class ActivitySerializer extends ApplicationSerializer { }); } - // For 1.10 release naming changed from 'distinct_entities' to 'entity_clients' and + // for vault usage - vertical bar chart + flattenByMonths(payload, isNewClients = false) { + if (isNewClients) { + return payload.map((m) => { + return { + month: m.timestamp, + entity_clients: m.new_clients.counts.entity_clients, + non_entity_clients: m.new_clients.counts.non_entity_clients, + total: m.new_clients.counts.clients, + namespaces: this.flattenDataset(m.new_clients.namespaces), + }; + }); + } else { + return payload.map((m) => { + return { + month: m.timestamp, + entity_clients: m.counts.entity_clients, + non_entity_clients: m.counts.non_entity_clients, + total: m.counts.clients, + namespaces: this.flattenDataset(m.namespaces), + new_clients: { + entity_clients: m.new_clients.counts.entity_clients, + non_entity_clients: m.new_clients.counts.non_entity_clients, + total: m.new_clients.counts.clients, + namespaces: this.flattenDataset(m.new_clients.namespaces), + }, + }; + }); + } + } + + // In 1.10 'distinct_entities' changed to 'entity_clients' and // 'non_entity_tokens' to 'non_entity_clients' - // accounting for deprecated API keys here and updating to latest nomenclature homogenizeClientNaming(object) { - // TODO CMB check with API payload, latest draft includes both new and old key names - // TODO CMB Delete old key names IF correct ones exist? - if (Object.keys(object).includes('distinct_entities', 'non_entity_tokens')) { - let entity_clients = object.distinct_entities; - let non_entity_clients = object.non_entity_tokens; - let { clients } = object; + // if new key names exist, only return those key/value pairs + if (Object.keys(object).includes('entity_clients')) { + let { clients, entity_clients, non_entity_clients } = object; return { clients, entity_clients, non_entity_clients, }; } + // if object only has outdated key names, update naming + if (Object.keys(object).includes('distinct_entities')) { + let { clients, distinct_entities, non_entity_tokens } = object; + return { + clients, + entity_clients: distinct_entities, + non_entity_clients: non_entity_tokens, + }; + } + // TODO CMB: test what to return if neither key exists return object; } @@ -64,11 +104,14 @@ export default class ActivitySerializer extends ApplicationSerializer { ...payload, response_timestamp, by_namespace: this.flattenDataset(payload.data.by_namespace), + by_month_total_clients: this.flattenByMonths(payload.data.months), + by_month_new_clients: this.flattenByMonths(payload.data.months, { isNewClients: true }), total: this.homogenizeClientNaming(payload.data.total), formatted_end_time: this.parseRFC3339(payload.data.end_time), formatted_start_time: this.parseRFC3339(payload.data.start_time), }; delete payload.data.by_namespace; + delete payload.data.months; delete payload.data.total; return super.normalizeResponse(store, primaryModelClass, transformedPayload, id, requestType); } diff --git a/ui/app/templates/components/secret-edit-toolbar.hbs b/ui/app/templates/components/secret-edit-toolbar.hbs index 8e4bf21c99af..80eb99556d3c 100644 --- a/ui/app/templates/components/secret-edit-toolbar.hbs +++ b/ui/app/templates/components/secret-edit-toolbar.hbs @@ -19,7 +19,6 @@

- {{#if (eq this.mode "create")}} + {{#if (eq @mode "create")}} Create secret - {{else if (and this.isV2 (eq this.mode "edit"))}} + {{else if (and this.isV2 (eq @mode "edit"))}} Create new version - {{else if (eq this.mode "edit")}} + {{else if (eq @mode "edit")}} Edit secret {{else}} - {{this.key.id}} + {{@key.id}} {{/if}}

{{! tabs for show only }} -{{#if (eq this.mode "show")}} +{{#if (eq @mode "show")}}