diff --git a/internal/consts/consts.go b/internal/consts/consts.go index cb271ad56..c34ead7c7 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -437,6 +437,8 @@ const ( FieldTune = "tune" FieldMaxRetries = "max_retries" FieldSessionTags = "session_tags" + FieldTokenType = "token_type" + FieldUserLockoutConfig = "user_lockout_config" /* common environment variables diff --git a/vault/auth_mount.go b/vault/auth_mount.go index dfe3fb8a9..4f4881636 100644 --- a/vault/auth_mount.go +++ b/vault/auth_mount.go @@ -5,18 +5,27 @@ package vault import ( "context" - - "log" - + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" + "github.com/hashicorp/terraform-provider-vault/util/mountutil" "github.com/hashicorp/vault/api" + "log" +) + +const ( + fieldLockoutThreshold = "lockout_threshold" + fieldLockoutDuration = "lockout_duration" + fieldLockoutCounterResetDuration = "lockout_counter_reset_duration" + fieldLockoutDisable = "lockout_disable" ) func authMountTuneSchema() *schema.Schema { return &schema.Schema{ + Deprecated: `Deprecated. Please use dedicated schema fields instead. Will be removed in next major release`, Type: schema.TypeSet, Optional: true, Computed: true, @@ -77,28 +86,293 @@ func authMountTuneSchema() *schema.Schema { } } -func authMountTune(ctx context.Context, client *api.Client, path string, configured interface{}) error { - input := expandAuthMethodTune(configured.(*schema.Set).List()) +type createMountRequestParams struct { + Path string + MountType string + + // some auth engines manage the token type separately + SkipTokenType bool +} + +func getAuthMountSchema(excludes ...string) schemaMap { + s := schemaMap{ + consts.FieldTokenType: { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Specifies the type of tokens that should be returned by the mount.", + ValidateFunc: validation.StringInSlice([]string{"default-service", "default-batch", "service", "batch"}, false), + }, + consts.FieldUserLockoutConfig: { + Type: schema.TypeMap, + Optional: true, + Description: "Specifies the user lockout configuration for the mount. Requires Vault 1.13+.", + }, + } + + for k, v := range getMountSchema( + // not used by auth engines + consts.FieldDelegatedAuthAccessors, + consts.FieldExternalEntropyAccess, + consts.FieldAllowedManagedKeys, + consts.FieldOptions, + ) { + s[k] = v + } - return tuneMount(ctx, client, path, input) + for _, v := range excludes { + delete(s, v) + } + return s } -func tuneMount(ctx context.Context, client *api.Client, path string, input api.MountConfigInput) error { - err := client.Sys().TuneMountWithContext(ctx, path, input) +func createAuthMount(ctx context.Context, d *schema.ResourceData, meta interface{}, client *api.Client, params *createMountRequestParams) error { + options := &api.EnableAuthOptions{ + Type: params.MountType, + Description: d.Get(consts.FieldDescription).(string), + Local: d.Get(consts.FieldLocal).(bool), + SealWrap: d.Get(consts.FieldSealWrap).(bool), + Config: api.MountConfigInput{ + DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTL)), + MaxLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTL)), + }, + } + + if v, ok := d.GetOk(consts.FieldAuditNonHMACRequestKeys); ok { + options.Config.AuditNonHMACRequestKeys = expandStringSlice(v.([]interface{})) + } + if v, ok := d.GetOk(consts.FieldAuditNonHMACResponseKeys); ok { + options.Config.AuditNonHMACResponseKeys = expandStringSlice(v.([]interface{})) + } + + if v, ok := d.GetOk(consts.FieldPassthroughRequestHeaders); ok { + options.Config.PassthroughRequestHeaders = expandStringSlice(v.([]interface{})) + } + + if v, ok := d.GetOk(consts.FieldAllowedResponseHeaders); ok { + options.Config.AllowedResponseHeaders = expandStringSlice(v.([]interface{})) + } + + if v, ok := d.GetOk(consts.FieldListingVisibility); ok { + options.Config.ListingVisibility = v.(string) + } + + if v, ok := d.GetOk(consts.FieldPluginVersion); ok { + options.Config.PluginVersion = v.(string) + } + + if !params.SkipTokenType { + if v, ok := d.GetOk(consts.FieldTokenType); ok { + options.Config.TokenType = v.(string) + } + } + + useAPIVer116Ent := provider.IsAPISupported(meta, provider.VaultVersion116) && provider.IsEnterpriseSupported(meta) + if useAPIVer116Ent { + if d.HasChange(consts.FieldIdentityTokenKey) { + options.Config.IdentityTokenKey = d.Get(consts.FieldIdentityTokenKey).(string) + } + } + + log.Printf("[DEBUG] Creating auth mount %s in Vault", params.Path) + + err := client.Sys().EnableAuthWithOptionsWithContext(ctx, params.Path, options) if err != nil { - return err + return fmt.Errorf("error writing to Vault: %s", err) } + return nil } -func authMountTuneGet(ctx context.Context, client *api.Client, path string) (map[string]interface{}, error) { - tune, err := client.Sys().MountConfigWithContext(ctx, path) +func updateAuthMount(ctx context.Context, d *schema.ResourceData, meta interface{}, excludeType bool, skipTokenType bool) error { + client, err := provider.GetClient(d, meta) if err != nil { - log.Printf("[ERROR] Error when reading tune config from path %q: %s", path+"/tune", err) - return nil, err + return err + } + + config := api.MountConfigInput{ + DefaultLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldDefaultLeaseTTL)), + MaxLeaseTTL: fmt.Sprintf("%ds", d.Get(consts.FieldMaxLeaseTTL)), + } + + if d.HasChange(consts.FieldAuditNonHMACRequestKeys) { + config.AuditNonHMACRequestKeys = expandStringSlice(d.Get(consts.FieldAuditNonHMACRequestKeys).([]interface{})) + } + + if d.HasChange(consts.FieldAuditNonHMACResponseKeys) { + config.AuditNonHMACResponseKeys = expandStringSlice(d.Get(consts.FieldAuditNonHMACResponseKeys).([]interface{})) + } + + if d.HasChange(consts.FieldDescription) { + description := fmt.Sprintf("%s", d.Get(consts.FieldDescription)) + config.Description = &description + } + + path := d.Id() + authPath := "auth/" + path + + if d.HasChange(consts.FieldPassthroughRequestHeaders) { + config.PassthroughRequestHeaders = expandStringSlice(d.Get(consts.FieldPassthroughRequestHeaders).([]interface{})) + } + + if d.HasChange(consts.FieldAllowedResponseHeaders) { + config.AllowedResponseHeaders = expandStringSlice(d.Get(consts.FieldAllowedResponseHeaders).([]interface{})) + } + + if d.HasChange(consts.FieldListingVisibility) { + config.ListingVisibility = d.Get(consts.FieldListingVisibility).(string) + } + + if d.HasChange(consts.FieldPluginVersion) { + config.PluginVersion = d.Get(consts.FieldPluginVersion).(string) + } + + if !skipTokenType { + if d.HasChange(consts.FieldTokenType) { + config.TokenType = d.Get(consts.FieldTokenType).(string) + } + } + + if d.HasChange(consts.FieldUserLockoutConfig) { + userLockoutCfg, err := getUserLockoutConfig(d.Get(consts.FieldUserLockoutConfig).(map[string]interface{})) + if err != nil { + return fmt.Errorf("error reading '%s': %s", consts.FieldUserLockoutConfig, err) + } + + config.UserLockoutConfig = userLockoutCfg + } + + useAPIVer116Ent := provider.IsAPISupported(meta, provider.VaultVersion116) && provider.IsEnterpriseSupported(meta) + if useAPIVer116Ent { + if d.HasChange(consts.FieldIdentityTokenKey) { + config.IdentityTokenKey = d.Get(consts.FieldIdentityTokenKey).(string) + } + } + + log.Printf("[DEBUG] Updating auth mount %s in Vault", path) + + if err := client.Sys().TuneMountWithContext(ctx, authPath, config); err != nil { + return fmt.Errorf("error updating Vault: %s", err) + } + + return readAuthMount(ctx, d, meta, excludeType, skipTokenType) +} + +func readAuthMount(ctx context.Context, d *schema.ResourceData, meta interface{}, excludeType bool, skipTokenType bool) error { + client, e := provider.GetClient(d, meta) + if e != nil { + return e + } + + path := d.Id() + + log.Printf("[DEBUG] Reading auth mount %s from Vault", path) + + mount, err := mountutil.GetAuthMount(ctx, client, path) + if err != nil { + if mountutil.IsMountNotFoundError(err) { + log.Printf("[WARN] Mount %q not found, removing from state.", path) + d.SetId("") + return nil + } + return err + } + + if !excludeType { + if err := d.Set(consts.FieldType, mount.Type); err != nil { + return err + } + } + + if err := d.Set(consts.FieldPath, path); err != nil { + return err + } + if err := d.Set(consts.FieldDescription, mount.Description); err != nil { + return err + } + if err := d.Set(consts.FieldDefaultLeaseTTL, mount.Config.DefaultLeaseTTL); err != nil { + return err + } + if err := d.Set(consts.FieldMaxLeaseTTL, mount.Config.MaxLeaseTTL); err != nil { + return err + } + if err := d.Set(consts.FieldAuditNonHMACRequestKeys, mount.Config.AuditNonHMACRequestKeys); err != nil { + return err + } + if err := d.Set(consts.FieldAuditNonHMACResponseKeys, mount.Config.AuditNonHMACResponseKeys); err != nil { + return err + } + if err := d.Set(consts.FieldAccessor, mount.Accessor); err != nil { + return err + } + if err := d.Set(consts.FieldLocal, mount.Local); err != nil { + return err + } + if err := d.Set(consts.FieldSealWrap, mount.SealWrap); err != nil { + return err + } + + if err := d.Set(consts.FieldPassthroughRequestHeaders, mount.Config.PassthroughRequestHeaders); err != nil { + return err + } + if err := d.Set(consts.FieldAllowedResponseHeaders, mount.Config.AllowedResponseHeaders); err != nil { + return err + } + + if err := d.Set(consts.FieldListingVisibility, mount.Config.ListingVisibility); err != nil { + return err + } + if err := d.Set(consts.FieldIdentityTokenKey, mount.Config.IdentityTokenKey); err != nil { + return err + } + + if !skipTokenType { + if err := d.Set(consts.FieldTokenType, mount.Config.TokenType); err != nil { + return err + } + } + + // TODO uncomment after fixing bug in vault/api package — user_lockout_config can not be read + //if err := d.Set(consts.FieldUserLockoutConfig, flattenUserLockoutConfig(mount.Config.UserLockoutConfig)); err != nil { + // return err + //} + + return nil +} + +func getUserLockoutConfig(m map[string]interface{}) (*api.UserLockoutConfigInput, error) { + result := &api.UserLockoutConfigInput{} + + if v, ok := m[fieldLockoutDuration]; ok && v != nil { + result.LockoutDuration = v.(string) + } + + if v, ok := m[fieldLockoutCounterResetDuration]; ok && v != nil { + result.LockoutCounterResetDuration = v.(string) + } + + if v, ok := m[fieldLockoutThreshold]; ok && v != nil { + result.LockoutThreshold = v.(string) + } + + if v, ok := m[fieldLockoutDisable]; ok && v != nil { + result.DisableLockout = v.(*bool) + } + + return result, nil +} + +func flattenUserLockoutConfig(output *api.UserLockoutConfigOutput) map[string]string { + m := make(map[string]string) + + if output != nil { + m[fieldLockoutThreshold] = fmt.Sprintf("%d", output.LockoutThreshold) + m[fieldLockoutDisable] = fmt.Sprintf("%t", *output.DisableLockout) + m[fieldLockoutDuration] = fmt.Sprintf("%d", output.LockoutDuration) + m[fieldLockoutCounterResetDuration] = fmt.Sprintf("%d", output.LockoutCounterReset) } - return flattenAuthMethodTune(tune), nil + return m } func authMountDisable(ctx context.Context, client *api.Client, path string) diag.Diagnostics { diff --git a/vault/resource_auth_backend.go b/vault/resource_auth_backend.go index 5d28679ba..d6fdac220 100644 --- a/vault/resource_auth_backend.go +++ b/vault/resource_auth_backend.go @@ -9,16 +9,13 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" - "github.com/hashicorp/terraform-provider-vault/util/mountutil" ) func AuthBackendResource() *schema.Resource { - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ SchemaVersion: 1, CreateContext: authBackendWrite, @@ -78,6 +75,18 @@ func AuthBackendResource() *schema.Resource { consts.FieldTune: authMountTuneSchema(), }, }, false) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + consts.FieldDescription, + consts.FieldAccessor, + consts.FieldLocal, + consts.FieldIdentityTokenKey, + )) + + return r } func authBackendWrite(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -93,26 +102,14 @@ func authBackendWrite(ctx context.Context, d *schema.ResourceData, meta interfac path = mountType } - config := &api.MountConfigInput{} - useAPIver117Ent := provider.IsAPISupported(meta, provider.VaultVersion117) && provider.IsEnterpriseSupported(meta) - if useAPIver117Ent { - if v, ok := d.GetOk(consts.FieldIdentityTokenKey); ok { - config.IdentityTokenKey = v.(string) - } - } - - options := &api.EnableAuthOptions{ - Type: mountType, - Description: d.Get(consts.FieldDescription).(string), - Local: d.Get(consts.FieldLocal).(bool), - Config: *config, - } - log.Printf("[DEBUG] Writing auth %q to Vault", path) - if err := client.Sys().EnableAuthWithOptionsWithContext(ctx, path, options); err != nil { - return diag.Errorf("error writing to Vault: %s", err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: mountType, + SkipTokenType: false, + }); err != nil { + return diag.FromErr(err) } - d.SetId(path) return authBackendUpdate(ctx, d, meta) @@ -123,56 +120,13 @@ func authBackendDelete(ctx context.Context, d *schema.ResourceData, meta interfa if e != nil { return diag.FromErr(e) } - - path := d.Id() - - log.Printf("[DEBUG] Deleting auth %s from Vault", path) - - if err := client.Sys().DisableAuthWithContext(ctx, path); err != nil { - return diag.Errorf("error disabling auth from Vault: %s", err) - } - - return nil + return authMountDisable(ctx, client, d.Id()) } func authBackendRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client, e := provider.GetClient(d, meta) - if e != nil { - return diag.FromErr(e) - } - - path := d.Id() - - mount, err := mountutil.GetAuthMount(ctx, client, path) - if err != nil { - if mountutil.IsMountNotFoundError(err) { - log.Printf("[WARN] Mount %q not found, removing from state.", path) - d.SetId("") - return nil - } - return diag.FromErr(err) - } - - if err := d.Set(consts.FieldType, mount.Type); err != nil { - return diag.FromErr(err) - } - if err := d.Set(consts.FieldPath, path); err != nil { + if err := readAuthMount(ctx, d, meta, true, false); err != nil { return diag.FromErr(err) } - if err := d.Set(consts.FieldDescription, mount.Description); err != nil { - return diag.FromErr(err) - } - if err := d.Set(consts.FieldLocal, mount.Local); err != nil { - return diag.FromErr(err) - } - if err := d.Set(consts.FieldAccessor, mount.Accessor); err != nil { - return diag.FromErr(err) - } - // TODO: uncomment when identity token key is being returned on the read mount endpoint - // if err := d.Set(consts.FieldIdentityTokenKey, mount.Config.IdentityTokenKey); err != nil { - // return diag.FromErr(err) - // } - return nil } @@ -190,41 +144,11 @@ func authBackendUpdate(ctx context.Context, d *schema.ResourceData, meta interfa if e != nil { return diag.FromErr(e) } - } - - backendType := d.Get(consts.FieldType).(string) - var config api.MountConfigInput - var callTune bool - if d.HasChange(consts.FieldTune) { - log.Printf("[INFO] Auth '%q' tune configuration changed", path) - - if raw, ok := d.GetOk(consts.FieldTune); ok { - log.Printf("[DEBUG] Writing %s auth tune to '%q'", backendType, path) - - config = expandAuthMethodTune(raw.(*schema.Set).List()) - } - callTune = true - } - - if d.HasChanges(consts.FieldIdentityTokenKey, consts.FieldDescription) && !d.IsNewResource() { - desc := d.Get(consts.FieldDescription).(string) - config.Description = &desc - - useAPIVer117Ent := provider.IsAPISupported(meta, provider.VaultVersion117) && provider.IsEnterpriseSupported(meta) - if useAPIVer117Ent { - config.IdentityTokenKey = d.Get(consts.FieldIdentityTokenKey).(string) + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, true, false); err != nil { + return diag.FromErr(err) } - - callTune = true - } - - if callTune { - if err := tuneMount(ctx, client, "auth/"+path, config); err != nil { - return diag.FromErr(e) - } - - log.Printf("[INFO] Written %s auth tune to '%q'", backendType, path) } return authBackendRead(ctx, d, meta) diff --git a/vault/resource_gcp_auth_backend.go b/vault/resource_gcp_auth_backend.go index f4db02170..153f42b70 100644 --- a/vault/resource_gcp_auth_backend.go +++ b/vault/resource_gcp_auth_backend.go @@ -12,12 +12,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" - "github.com/hashicorp/terraform-provider-vault/util/mountutil" ) const ( @@ -31,7 +28,7 @@ const ( ) func gcpAuthBackendResource() *schema.Resource { - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ CreateContext: gcpAuthBackendWrite, UpdateContext: gcpAuthBackendUpdate, ReadContext: provider.ReadContextWrapper(gcpAuthBackendRead), @@ -123,6 +120,18 @@ func gcpAuthBackendResource() *schema.Resource { consts.FieldTune: authMountTuneSchema(), }, }, false) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + consts.FieldDescription, + consts.FieldAccessor, + consts.FieldLocal, + consts.FieldIdentityTokenKey, + )) + + return r } func gcpAuthCustomEndpointSchema() map[string]*schema.Schema { @@ -195,28 +204,15 @@ func gcpAuthBackendWrite(ctx context.Context, d *schema.ResourceData, meta inter return diag.FromErr(e) } - authType := gcpAuthType path := d.Get(consts.FieldPath).(string) - desc := d.Get(consts.FieldDescription).(string) - local := d.Get(consts.FieldLocal).(bool) - - config := &api.MountConfigInput{} - useAPIver117Ent := provider.IsAPISupported(meta, provider.VaultVersion117) && provider.IsEnterpriseSupported(meta) - if useAPIver117Ent { - if v, ok := d.GetOk(consts.FieldIdentityTokenKey); ok { - config.IdentityTokenKey = v.(string) - } - } log.Printf("[DEBUG] Enabling gcp auth backend %q", path) - err := client.Sys().EnableAuthWithOptionsWithContext(ctx, path, &api.EnableAuthOptions{ - Type: authType, - Description: desc, - Local: local, - Config: *config, - }) - if err != nil { - return diag.Errorf("error enabling gcp auth backend %q: %s", path, err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: gcpAuthType, + SkipTokenType: false, + }); err != nil { + return diag.FromErr(err) } log.Printf("[DEBUG] Enabled gcp auth backend %q", path) @@ -232,7 +228,6 @@ func gcpAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta inte } gcpPath := d.Id() - gcpAuthPath := "auth/" + gcpPath path := gcpAuthBackendConfigPath(gcpPath) if !d.IsNewResource() { @@ -241,21 +236,11 @@ func gcpAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta inte return diag.FromErr(err) } - gcpAuthPath = "auth/" + newMount path = gcpAuthBackendConfigPath(newMount) - if d.HasChanges(consts.FieldIdentityTokenKey, consts.FieldDescription) { - desc := d.Get(consts.FieldDescription).(string) - config := api.MountConfigInput{ - Description: &desc, - } - useAPIVer117Ent := provider.IsAPISupported(meta, provider.VaultVersion117) && provider.IsEnterpriseSupported(meta) - if useAPIVer117Ent { - config.IdentityTokenKey = d.Get(consts.FieldIdentityTokenKey).(string) - } - if err := client.Sys().TuneMountWithContext(ctx, path, config); err != nil { - return diag.FromErr(err) - } + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, true, false); err != nil { + return diag.FromErr(err) } } @@ -277,27 +262,6 @@ func gcpAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta inte data[consts.FieldCustomEndpoint] = endpoints } - if d.HasChange(consts.FieldTune) { - log.Printf("[INFO] %s Auth %q tune configuration changed", gcpAuthType, gcpAuthPath) - if raw, ok := d.GetOk(consts.FieldTune); ok { - log.Printf("[DEBUG] Writing %s auth tune to %q", gcpAuthType, gcpAuthPath) - err := authMountTune(ctx, client, gcpAuthPath, raw) - if err != nil { - return nil - } - } - } - - if d.HasChange(consts.FieldDescription) { - description := d.Get(consts.FieldDescription).(string) - tune := api.MountConfigInput{Description: &description} - err := client.Sys().TuneMountWithContext(ctx, gcpAuthPath, tune) - if err != nil { - log.Printf("[ERROR] Error updating %s auth description at %q", gcpAuthType, gcpAuthPath) - return diag.FromErr(err) - } - } - useAPIver117Ent := provider.IsAPISupported(meta, provider.VaultVersion117) && provider.IsEnterpriseSupported(meta) if useAPIver117Ent { fields := []string{ @@ -332,7 +296,6 @@ func gcpAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interf } gcpPath := d.Id() - gcpAuthPath := "auth/" + gcpPath path := gcpAuthBackendConfigPath(gcpPath) log.Printf("[DEBUG] Reading gcp auth backend config %q", path) @@ -381,35 +344,7 @@ func gcpAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interf } } - mount, err := mountutil.GetAuthMount(ctx, client, gcpPath) - if err != nil { - if mountutil.IsMountNotFoundError(err) { - log.Printf("[WARN] Mount %q not found, removing from state.", gcpPath) - d.SetId("") - return nil - } - return diag.FromErr(err) - } - - log.Printf("[DEBUG] Reading %s auth tune from '%s/tune'", gcpAuthType, gcpAuthPath) - rawTune, err := authMountTuneGet(ctx, client, gcpAuthPath) - if err != nil { - return diag.Errorf("error reading tune information from Vault: %s", err) - } - data := map[string]interface{}{} - data[consts.FieldTune] = []map[string]interface{}{rawTune} - if err := util.SetResourceData(d, data); err != nil { - return diag.FromErr(err) - } - - if err := d.Set(consts.FieldAccessor, mount.Accessor); err != nil { - return diag.FromErr(err) - } - if err := d.Set(consts.FieldDescription, mount.Description); err != nil { - return diag.FromErr(err) - } - // set the auth backend's path - if err := d.Set(consts.FieldPath, gcpPath); err != nil { + if err := readAuthMount(ctx, d, meta, true, false); err != nil { return diag.FromErr(err) } @@ -421,15 +356,5 @@ func gcpAuthBackendDelete(ctx context.Context, d *schema.ResourceData, meta inte if e != nil { return diag.FromErr(e) } - - path := d.Id() - - log.Printf("[DEBUG] Deleting gcp auth backend %q", path) - err := client.Sys().DisableAuthWithContext(ctx, path) - if err != nil { - return diag.Errorf("error deleting gcp auth backend %q: %q", path, err) - } - log.Printf("[DEBUG] Deleted gcp auth backend %q", path) - - return nil + return authMountDisable(ctx, client, d.Id()) } diff --git a/vault/resource_github_auth_backend.go b/vault/resource_github_auth_backend.go index edf83442c..8dfe9be6e 100644 --- a/vault/resource_github_auth_backend.go +++ b/vault/resource_github_auth_backend.go @@ -10,12 +10,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" - "github.com/hashicorp/terraform-provider-vault/util/mountutil" ) func githubAuthBackendResource() *schema.Resource { @@ -62,7 +59,7 @@ func githubAuthBackendResource() *schema.Resource { addTokenFields(fields, &addTokenFieldsConfig{}) - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ CreateContext: githubAuthBackendCreate, ReadContext: provider.ReadContextWrapper(githubAuthBackendRead), UpdateContext: githubAuthBackendUpdate, @@ -73,6 +70,17 @@ func githubAuthBackendResource() *schema.Resource { Schema: fields, CustomizeDiff: getMountCustomizeDiffFunc(consts.FieldPath), }, false) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + consts.FieldDescription, + consts.FieldAccessor, + consts.FieldTokenType, + )) + + return r } func githubAuthBackendCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -80,21 +88,16 @@ func githubAuthBackendCreate(ctx context.Context, d *schema.ResourceData, meta i if e != nil { return diag.FromErr(e) } - var description string path := strings.Trim(d.Get(consts.FieldPath).(string), "/") - if v, ok := d.GetOk("description"); ok { - description = v.(string) - } - log.Printf("[DEBUG] Enabling github auth backend at '%s'", path) - err := client.Sys().EnableAuthWithOptionsWithContext(ctx, path, &api.EnableAuthOptions{ - Type: consts.MountTypeGitHub, - Description: description, - }) - if err != nil { - return diag.Errorf("error enabling github auth backend at '%s': %s", path, err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: consts.MountTypeGitHub, + SkipTokenType: true, + }); err != nil { + return diag.FromErr(err) } log.Printf("[INFO] Enabled github auth backend at '%s'", path) @@ -120,6 +123,11 @@ func githubAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta i path = "auth/" + mount configPath = path + "/config" + + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, true, true); err != nil { + return diag.FromErr(err) + } } data := map[string]interface{}{} @@ -144,30 +152,6 @@ func githubAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta i } log.Printf("[INFO] Github auth config successfully written to '%q'", configPath) - if d.HasChange("tune") { - log.Printf("[INFO] Github Auth '%q' tune configuration changed", d.Id()) - if raw, ok := d.GetOk("tune"); ok { - log.Printf("[DEBUG] Writing github auth tune to '%q'", path) - - err := authMountTune(ctx, client, path, raw) - if err != nil { - return nil - } - - log.Printf("[INFO] Written github auth tune to '%q'", path) - } - } - - if d.HasChange("description") { - description := d.Get("description").(string) - tune := api.MountConfigInput{Description: &description} - err := client.Sys().TuneMountWithContext(ctx, path, tune) - if err != nil { - log.Printf("[ERROR] Error updating github auth description to '%q'", path) - return diag.FromErr(err) - } - } - d.Partial(false) return githubAuthBackendRead(ctx, d, meta) } @@ -182,13 +166,7 @@ func githubAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta int configPath := path + "/config" log.Printf("[DEBUG] Reading github auth mount from '%q'", path) - mount, err := mountutil.GetAuthMount(ctx, client, d.Id()) - if err != nil { - if mountutil.IsMountNotFoundError(err) { - log.Printf("[WARN] Mount %q not found, removing from state.", path) - d.SetId("") - return nil - } + if err := readAuthMount(ctx, d, meta, true, true); err != nil { return diag.FromErr(err) } @@ -207,19 +185,10 @@ func githubAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta int return nil } - log.Printf("[DEBUG] Reading github auth tune from '%q/tune'", path) - rawTune, err := authMountTuneGet(ctx, client, path) - if err != nil { - return diag.Errorf("error reading tune information from Vault: %s", err) - } - data := getCommonTokenFieldMap(resp) data["path"] = d.Id() data["organization"] = resp.Data["organization"] data["base_url"] = resp.Data["base_url"] - data["description"] = mount.Description - data["accessor"] = mount.Accessor - data["tune"] = []map[string]interface{}{rawTune} if orgID, ok := resp.Data["organization_id"]; ok { data["organization_id"] = orgID diff --git a/vault/resource_jwt_auth_backend.go b/vault/resource_jwt_auth_backend.go index a0a61da3c..e211bef21 100644 --- a/vault/resource_jwt_auth_backend.go +++ b/vault/resource_jwt_auth_backend.go @@ -13,16 +13,13 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" - "github.com/hashicorp/terraform-provider-vault/util/mountutil" ) func jwtAuthBackendResource() *schema.Resource { - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -170,6 +167,17 @@ func jwtAuthBackendResource() *schema.Resource { "tune": authMountTuneSchema(), }, }, false) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + consts.FieldDescription, + consts.FieldAccessor, + consts.FieldLocal, + )) + + return r } func jwtCustomizeDiff(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error { @@ -220,19 +228,14 @@ func jwtAuthBackendWrite(ctx context.Context, d *schema.ResourceData, meta inter return diag.FromErr(e) } - authType := d.Get("type").(string) path := getJwtPath(d) - options := &api.EnableAuthOptions{ - Type: authType, - Description: d.Get("description").(string), - Local: d.Get("local").(bool), - } - - log.Printf("[DEBUG] Writing auth %s to Vault", authType) - err := client.Sys().EnableAuthWithOptionsWithContext(ctx, path, options) - if err != nil { - return diag.Errorf("error writing to Vault: %s", err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: d.Get(consts.FieldType).(string), + SkipTokenType: false, + }); err != nil { + return diag.FromErr(err) } d.SetId(path) @@ -245,17 +248,7 @@ func jwtAuthBackendDelete(ctx context.Context, d *schema.ResourceData, meta inte if e != nil { return diag.FromErr(e) } - - path := getJwtPath(d) - - log.Printf("[DEBUG] Deleting auth %s from Vault", path) - - err := client.Sys().DisableAuthWithContext(ctx, path) - if err != nil { - return diag.Errorf("error disabling auth from Vault: %s", err) - } - - return nil + return authMountDisable(ctx, client, d.Id()) } func jwtAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -274,15 +267,8 @@ func jwtAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interf // use the ID value path = d.Id() } - d.Set("path", path) - mount, err := mountutil.GetAuthMount(ctx, client, path) - if err != nil { - if mountutil.IsMountNotFoundError(err) { - log.Printf("[WARN] Mount %q not found, removing from state.", path) - d.SetId("") - return nil - } + if err := readAuthMount(ctx, d, meta, false, false); err != nil { return diag.FromErr(err) } @@ -297,10 +283,6 @@ func jwtAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interf return nil } - d.Set("type", mount.Type) - d.Set("local", mount.Local) - - d.Set("accessor", mount.Accessor) for _, configOption := range matchingJwtMountConfigOptions { // The oidc_client_secret is sensitive so it will not be in the response // Our options are to always assume it must be updated or always assume it @@ -314,17 +296,9 @@ func jwtAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interf if configOption == "oidc_client_secret" { continue } - d.Set(configOption, config.Data[configOption]) - } - - log.Printf("[DEBUG] Reading jwt auth tune from %q", path+"/tune") - rawTune, err := authMountTuneGet(ctx, client, "auth/"+path) - if err != nil { - return diag.Errorf("error reading tune information from Vault: %s", err) - } - if err := d.Set("tune", []map[string]interface{}{rawTune}); err != nil { - log.Printf("[ERROR] Error when setting tune config from path %q to state: %s", path+"/tune", err) - return diag.FromErr(err) + if err := d.Set(configOption, config.Data[configOption]); err != nil { + return diag.FromErr(err) + } } return nil @@ -368,6 +342,11 @@ func jwtAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta inte if e != nil { return diag.FromErr(e) } + + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, false, false); err != nil { + return diag.FromErr(err) + } } configuration := map[string]interface{}{} @@ -391,21 +370,6 @@ func jwtAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta inte return diag.Errorf("error updating configuration to Vault for path %s: %s", path, err) } - if d.HasChange("tune") { - log.Printf("[INFO] JWT/OIDC Auth '%q' tune configuration changed", d.Id()) - if raw, ok := d.GetOk("tune"); ok { - backendType := d.Get("type") - log.Printf("[DEBUG] Writing %s auth tune to '%q'", backendType, path) - - err := authMountTune(ctx, client, "auth/"+path, raw) - if err != nil { - return nil - } - - log.Printf("[INFO] Written %s auth tune to %q", backendType, path) - } - } - return jwtAuthBackendRead(ctx, d, meta) } diff --git a/vault/resource_jwt_auth_backend_test.go b/vault/resource_jwt_auth_backend_test.go index 04c8a7678..f9e9eaf02 100644 --- a/vault/resource_jwt_auth_backend_test.go +++ b/vault/resource_jwt_auth_backend_test.go @@ -295,6 +295,96 @@ func TestJWTAuthBackend_remount(t *testing.T) { }) } +func TestJWTAuthBackend_authMountSchema(t *testing.T) { + t.Parallel() + path := acctest.RandomWithPrefix("tf-test-auth-jwt") + + resourceName := "vault_jwt_auth_backend.test" + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { testutil.TestAccPreCheck(t) }, + Steps: []resource.TestStep{ + { + Config: testAccJWTAuthBackendConfig_authMountSchema(path, false), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "path", path), + resource.TestCheckResourceAttr(resourceName, "description", "test desc"), + resource.TestCheckResourceAttr(resourceName, "oidc_discovery_url", "https://myco.auth0.com/"), + resource.TestCheckResourceAttr(resourceName, "default_lease_ttl_seconds", "3600"), + resource.TestCheckResourceAttr(resourceName, "max_lease_ttl_seconds", "36000"), + resource.TestCheckResourceAttr(resourceName, "token_type", "default-service"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "listing_visibility", "hidden"), + ), + }, + { + Config: testAccJWTAuthBackendConfig_authMountSchema(path, true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "path", path), + resource.TestCheckResourceAttr(resourceName, "description", "test desc updated"), + resource.TestCheckResourceAttr(resourceName, "oidc_discovery_url", "https://myco.auth0.com/"), + resource.TestCheckResourceAttr(resourceName, "default_lease_ttl_seconds", "7200"), + resource.TestCheckResourceAttr(resourceName, "max_lease_ttl_seconds", "48000"), + resource.TestCheckResourceAttr(resourceName, "token_type", "batch"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.#", "3"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.2", "header3"), + resource.TestCheckResourceAttr(resourceName, "listing_visibility", "unauth"), + ), + }, + // TODO user_lockout_config can not be read + testutil.GetImportTestStep(resourceName, false, nil, + consts.FieldDisableRemount, + "user_lockout_config.%", + "user_lockout_config.lockout_counter_reset_duration", + "user_lockout_config.lockout_duration", + "user_lockout_config.lockout_threshold", + ), + }, + }) +} + +func testAccJWTAuthBackendConfig_authMountSchema(path string, isUpdate bool) string { + if !isUpdate { + + return fmt.Sprintf(` +resource "vault_jwt_auth_backend" "test" { + path = "%s" + description = "test desc" + oidc_discovery_url = "https://myco.auth0.com/" + default_lease_ttl_seconds = 3600 + max_lease_ttl_seconds = 36000 + passthrough_request_headers = ["header1", "header2"] + allowed_response_headers = ["header1", "header2"] + listing_visibility = "hidden" +}`, path) + } else { + return fmt.Sprintf(` +resource "vault_jwt_auth_backend" "test" { + path = "%s" + description = "test desc updated" + oidc_discovery_url = "https://myco.auth0.com/" + default_lease_ttl_seconds = 7200 + max_lease_ttl_seconds = 48000 + passthrough_request_headers = ["header1", "header2"] + allowed_response_headers = ["header1", "header2", "header3"] + listing_visibility = "unauth" + token_type = "batch" +}`, path) + } +} + func testAccJWTAuthBackendConfig(path, ns string, local bool) string { c := fmt.Sprintf(` resource "vault_jwt_auth_backend" "jwt" { diff --git a/vault/resource_ldap_auth_backend.go b/vault/resource_ldap_auth_backend.go index 497e07d5f..c781ca57e 100644 --- a/vault/resource_ldap_auth_backend.go +++ b/vault/resource_ldap_auth_backend.go @@ -11,12 +11,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" - "github.com/hashicorp/terraform-provider-vault/util/mountutil" ) const ldapAuthType string = "ldap" @@ -197,7 +194,7 @@ func ldapAuthBackendResource() *schema.Resource { addTokenFields(fields, &addTokenFieldsConfig{}) - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ SchemaVersion: 2, // Handle custom state upgrade case since schema version was already 1 StateUpgraders: []schema.StateUpgrader{ @@ -217,6 +214,18 @@ func ldapAuthBackendResource() *schema.Resource { CustomizeDiff: getMountCustomizeDiffFunc(consts.FieldPath), Schema: fields, }, true) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + consts.FieldDescription, + consts.FieldAccessor, + consts.FieldLocal, + consts.FieldTokenType, + )) + + return r } func ldapAuthBackendConfigPath(path string) string { @@ -230,16 +239,13 @@ func ldapAuthBackendWrite(ctx context.Context, d *schema.ResourceData, meta inte } path := d.Get("path").(string) - options := &api.EnableAuthOptions{ - Type: ldapAuthType, - Description: d.Get("description").(string), - Local: d.Get("local").(bool), - } - log.Printf("[DEBUG] Enabling LDAP auth backend %q", path) - err := client.Sys().EnableAuthWithOptions(path, options) - if err != nil { - return diag.Errorf("error enabling ldap auth backend %q: %s", path, err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: ldapAuthType, + SkipTokenType: true, + }); err != nil { + return diag.FromErr(err) } log.Printf("[DEBUG] Enabled LDAP auth backend %q", path) @@ -263,6 +269,15 @@ func ldapAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta int } path = ldapAuthBackendConfigPath(newMount) + + } + + // for LDAP, user_lockout_config can only be configured on tune calls + // we always check if we need to tune the mount, even if it's a new resource + + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, true, true); err != nil { + return diag.FromErr(err) } data := map[string]interface{}{} @@ -318,21 +333,10 @@ func ldapAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta inter path := d.Id() - mount, err := mountutil.GetAuthMount(ctx, client, path) - if err != nil { - if mountutil.IsMountNotFoundError(err) { - log.Printf("[WARN] Mount %q not found, removing from state.", path) - d.SetId("") - return nil - } + if err := readAuthMount(ctx, d, meta, true, true); err != nil { return diag.FromErr(err) } - d.Set(consts.FieldPath, path) - d.Set(consts.FieldDescription, mount.Description) - d.Set(consts.FieldAccessor, mount.Accessor) - d.Set(consts.FieldLocal, mount.Local) - path = ldapAuthBackendConfigPath(path) log.Printf("[DEBUG] Reading LDAP auth backend config %q", path) @@ -384,19 +388,10 @@ func ldapAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta inter return diags } -func ldapAuthBackendDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func ldapAuthBackendDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client, e := provider.GetClient(d, meta) if e != nil { return diag.FromErr(e) } - path := d.Id() - - log.Printf("[DEBUG] Deleting LDAP auth backend %q", path) - err := client.Sys().DisableAuth(path) - if err != nil { - return diag.Errorf("error deleting ldap auth backend %q: %q", path, err) - } - log.Printf("[DEBUG] Deleted LDAP auth backend %q", path) - - return nil + return authMountDisable(ctx, client, d.Id()) } diff --git a/vault/resource_ldap_auth_backend_test.go b/vault/resource_ldap_auth_backend_test.go index 4687deab2..76a54d4e3 100644 --- a/vault/resource_ldap_auth_backend_test.go +++ b/vault/resource_ldap_auth_backend_test.go @@ -5,6 +5,7 @@ package vault import ( "fmt" + "github.com/hashicorp/terraform-provider-vault/internal/consts" "strings" "testing" @@ -117,6 +118,112 @@ func TestLDAPAuthBackend_remount(t *testing.T) { }) } +func TestLDAPAuthBackend_authMountSchema(t *testing.T) { + t.Parallel() + path := acctest.RandomWithPrefix("tf-test-auth-ldap") + + resourceName := "vault_ldap_auth_backend.test" + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + PreCheck: func() { testutil.TestAccPreCheck(t) }, + Steps: []resource.TestStep{ + { + Config: testAccLDAPAuthBackendConfig_authMountSchema(path, false), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "path", path), + resource.TestCheckResourceAttr(resourceName, "description", "test desc"), + resource.TestCheckResourceAttr(resourceName, "url", "ldaps://example.org"), + resource.TestCheckResourceAttr(resourceName, "default_lease_ttl_seconds", "3600"), + resource.TestCheckResourceAttr(resourceName, "max_lease_ttl_seconds", "36000"), + resource.TestCheckResourceAttr(resourceName, "token_type", "default"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "user_lockout_config.lockout_threshold", "20"), + resource.TestCheckResourceAttr(resourceName, "user_lockout_config.lockout_duration", "5m"), + resource.TestCheckResourceAttr(resourceName, "user_lockout_config.lockout_counter_reset_duration", "5m"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "listing_visibility", "hidden"), + ), + }, + { + Config: testAccLDAPAuthBackendConfig_authMountSchema(path, true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "path", path), + resource.TestCheckResourceAttr(resourceName, "description", "test desc updated"), + resource.TestCheckResourceAttr(resourceName, "url", "ldaps://example.org"), + resource.TestCheckResourceAttr(resourceName, "default_lease_ttl_seconds", "7200"), + resource.TestCheckResourceAttr(resourceName, "max_lease_ttl_seconds", "48000"), + resource.TestCheckResourceAttr(resourceName, "token_type", "batch"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.#", "2"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "passthrough_request_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.#", "3"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.0", "header1"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.1", "header2"), + resource.TestCheckResourceAttr(resourceName, "allowed_response_headers.2", "header3"), + resource.TestCheckResourceAttr(resourceName, "user_lockout_config.lockout_threshold", "30"), + resource.TestCheckResourceAttr(resourceName, "user_lockout_config.lockout_duration", "10m"), + resource.TestCheckResourceAttr(resourceName, "user_lockout_config.lockout_counter_reset_duration", "10m"), + resource.TestCheckResourceAttr(resourceName, "listing_visibility", "unauth"), + ), + }, + // TODO user_lockout_config can not be read + testutil.GetImportTestStep(resourceName, false, nil, + consts.FieldDisableRemount, + "user_lockout_config.%", + "user_lockout_config.lockout_counter_reset_duration", + "user_lockout_config.lockout_duration", + "user_lockout_config.lockout_threshold", + ), + }, + }) +} + +func testAccLDAPAuthBackendConfig_authMountSchema(path string, isUpdate bool) string { + if !isUpdate { + + return fmt.Sprintf(` +resource "vault_ldap_auth_backend" "test" { + path = "%s" + description = "test desc" + url = "ldaps://example.org" + default_lease_ttl_seconds = 3600 + max_lease_ttl_seconds = 36000 + passthrough_request_headers = ["header1", "header2"] + allowed_response_headers = ["header1", "header2"] + listing_visibility = "hidden" + user_lockout_config = { + lockout_threshold = "20", + lockout_duration = "5m", + lockout_counter_reset_duration = "5m" + } +}`, path) + } else { + return fmt.Sprintf(` +resource "vault_ldap_auth_backend" "test" { + path = "%s" + description = "test desc updated" + url = "ldaps://example.org" + default_lease_ttl_seconds = 7200 + max_lease_ttl_seconds = 48000 + passthrough_request_headers = ["header1", "header2"] + allowed_response_headers = ["header1", "header2", "header3"] + listing_visibility = "unauth" + token_type = "batch" + user_lockout_config = { + lockout_threshold = "30", + lockout_duration = "10m", + lockout_counter_reset_duration = "10m" + } +}`, path) + } +} + func testLDAPAuthBackendDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "vault_ldap_auth_backend" { diff --git a/vault/resource_okta_auth_backend.go b/vault/resource_okta_auth_backend.go index 697f5e78a..44046c943 100644 --- a/vault/resource_okta_auth_backend.go +++ b/vault/resource_okta_auth_backend.go @@ -20,7 +20,6 @@ import ( "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" - "github.com/hashicorp/terraform-provider-vault/util/mountutil" ) var oktaAuthType = "okta" @@ -221,7 +220,7 @@ func oktaAuthBackendResource() *schema.Resource { addTokenFields(fields, &addTokenFieldsConfig{}) - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ CreateContext: oktaAuthBackendWrite, DeleteContext: oktaAuthBackendDelete, ReadContext: provider.ReadContextWrapper(oktaAuthBackendRead), @@ -232,6 +231,17 @@ func oktaAuthBackendResource() *schema.Resource { CustomizeDiff: getMountCustomizeDiffFunc(consts.FieldPath), Schema: fields, }, false) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + consts.FieldDescription, + consts.FieldAccessor, + consts.FieldTokenType, + )) + + return r } func normalizeOktaTTL(i interface{}) string { @@ -269,18 +279,18 @@ func oktaAuthBackendWrite(ctx context.Context, d *schema.ResourceData, meta inte } authType := oktaAuthType - desc := d.Get(consts.FieldDescription).(string) path := d.Get(consts.FieldPath).(string) log.Printf("[DEBUG] Writing auth %s to Vault", authType) - err := client.Sys().EnableAuthWithOptions(path, &api.EnableAuthOptions{ - Type: authType, - Description: desc, - }) - if err != nil { - return diag.Errorf("error writing to Vault: %s", err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: oktaAuthType, + SkipTokenType: false, + }); err != nil { + return diag.FromErr(err) } + log.Printf("[INFO] Enabled okta auth backend at '%s'", path) d.SetId(path) @@ -292,17 +302,7 @@ func oktaAuthBackendDelete(ctx context.Context, d *schema.ResourceData, meta int if e != nil { return diag.FromErr(e) } - - path := d.Id() - - log.Printf("[DEBUG] Deleting auth %s from Vault", path) - - err := client.Sys().DisableAuthWithContext(ctx, path) - if err != nil { - return diag.Errorf("error disabling auth from Vault: %s", err) - } - - return nil + return authMountDisable(ctx, client, d.Id()) } func oktaAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -314,24 +314,7 @@ func oktaAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta inter path := d.Id() log.Printf("[DEBUG] Reading auth %s from Vault", path) - mount, err := mountutil.GetAuthMount(ctx, client, path) - if err != nil { - if mountutil.IsMountNotFoundError(err) { - log.Printf("[WARN] Mount %q not found, removing from state.", path) - d.SetId("") - return nil - } - return diag.FromErr(err) - } - - if err := d.Set(consts.FieldPath, path); err != nil { - return diag.FromErr(err) - } - - if err := d.Set(consts.FieldAccessor, mount.Accessor); err != nil { - return diag.FromErr(err) - } - if err := d.Set(consts.FieldDescription, mount.Description); err != nil { + if err := readAuthMount(ctx, d, meta, true, false); err != nil { return diag.FromErr(err) } @@ -398,6 +381,11 @@ func oktaAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta int if e != nil { return diag.FromErr(e) } + + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, true, false); err != nil { + return diag.FromErr(err) + } } log.Printf("[DEBUG] Updating auth %s in Vault", path) diff --git a/vault/resource_saml_auth_backend.go b/vault/resource_saml_auth_backend.go index 4cf9b962d..87026a9c0 100644 --- a/vault/resource_saml_auth_backend.go +++ b/vault/resource_saml_auth_backend.go @@ -10,8 +10,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/vault/api" - "github.com/hashicorp/terraform-provider-vault/internal/consts" "github.com/hashicorp/terraform-provider-vault/internal/provider" "github.com/hashicorp/terraform-provider-vault/util" @@ -45,7 +43,7 @@ var ( ) func samlAuthBackendResource() *schema.Resource { - return provider.MustAddMountMigrationSchema(&schema.Resource{ + r := provider.MustAddMountMigrationSchema(&schema.Resource{ CreateContext: provider.MountCreateContextWrapper(samlAuthBackendWrite, provider.VaultVersion115), ReadContext: provider.ReadContextWrapper(samlAuthBackendRead), UpdateContext: samlAuthBackendUpdate, @@ -119,6 +117,14 @@ func samlAuthBackendResource() *schema.Resource { }, }, }, true) + + // Add common mount schema to the resource + provider.MustAddSchema(r, getAuthMountSchema( + consts.FieldPath, + consts.FieldType, + )) + + return r } func samlAuthBackendWrite(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -130,12 +136,14 @@ func samlAuthBackendWrite(ctx context.Context, d *schema.ResourceData, meta inte path := d.Get(consts.FieldPath).(string) log.Printf("[DEBUG] Enabling SAML auth backend %q", path) - err := client.Sys().EnableAuthWithOptions(path, &api.EnableAuthOptions{ - Type: consts.MountTypeSAML, - }) - if err != nil { - return diag.Errorf("error enabling SAML auth backend %q: %s", path, err) + if err := createAuthMount(ctx, d, meta, client, &createMountRequestParams{ + Path: path, + MountType: consts.MountTypeSAML, + SkipTokenType: false, + }); err != nil { + return diag.FromErr(err) } + log.Printf("[DEBUG] Enabled SAML auth backend %q", path) // set ID to where engine is mounted @@ -159,6 +167,11 @@ func samlAuthBackendUpdate(ctx context.Context, d *schema.ResourceData, meta int } path = newMount + + // tune auth mount if needed + if err := updateAuthMount(ctx, d, meta, true, false); err != nil { + return diag.FromErr(err) + } } configPath := samlAuthBackendConfigPath(path) @@ -207,7 +220,7 @@ func samlAuthBackendRead(ctx context.Context, d *schema.ResourceData, meta inter return nil } - if err := d.Set(consts.FieldPath, id); err != nil { + if err := readAuthMount(ctx, d, meta, true, false); err != nil { return diag.FromErr(err) } @@ -229,17 +242,7 @@ func samlAuthBackendDelete(ctx context.Context, d *schema.ResourceData, meta int if e != nil { return diag.FromErr(e) } - - path := d.Id() - - log.Printf("[DEBUG] Deleting SAML auth backend %q", path) - err := client.Sys().DisableAuth(path) - if err != nil { - return diag.Errorf("error deleting SAML auth backend %q: %q", path, err) - } - log.Printf("[DEBUG] Deleted SAML auth backend %q", path) - - return nil + return authMountDisable(ctx, client, d.Id()) } func samlAuthBackendConfigPath(path string) string {