diff --git a/docs/resources/client.md b/docs/resources/client.md index 94f23403a..cfccc5985 100644 --- a/docs/resources/client.md +++ b/docs/resources/client.md @@ -18,7 +18,6 @@ resource "auth0_client" "my_client" { custom_login_page_on = true is_first_party = true is_token_endpoint_ip_header_trusted = true - token_endpoint_auth_method = "client_secret_post" oidc_conformant = false callbacks = ["https://example.com/callback"] allowed_origins = ["https://example.com"] diff --git a/examples/resources/auth0_client/resource.tf b/examples/resources/auth0_client/resource.tf index 7717b6193..43142a851 100644 --- a/examples/resources/auth0_client/resource.tf +++ b/examples/resources/auth0_client/resource.tf @@ -5,7 +5,6 @@ resource "auth0_client" "my_client" { custom_login_page_on = true is_first_party = true is_token_endpoint_ip_header_trusted = true - token_endpoint_auth_method = "client_secret_post" oidc_conformant = false callbacks = ["https://example.com/callback"] allowed_origins = ["https://example.com"] diff --git a/internal/auth0/branding/resource.go b/internal/auth0/branding/resource.go index bfb5aaaf6..c95182a5c 100644 --- a/internal/auth0/branding/resource.go +++ b/internal/auth0/branding/resource.go @@ -8,10 +8,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/auth0/terraform-provider-auth0/internal/config" internalError "github.com/auth0/terraform-provider-auth0/internal/error" + internalValidation "github.com/auth0/terraform-provider-auth0/internal/validation" ) var errNoCustomDomain = fmt.Errorf( @@ -94,7 +94,7 @@ func NewResource() *schema.Resource { "body": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: internalValidation.UniversalLoginTemplateContainsCorrectTags, Description: "The html template for the New Universal Login Experience.", }, }, diff --git a/internal/auth0/branding/resource_test.go b/internal/auth0/branding/resource_test.go index 12e08a311..f0eeef9f2 100644 --- a/internal/auth0/branding/resource_test.go +++ b/internal/auth0/branding/resource_test.go @@ -142,7 +142,7 @@ func TestAccBranding(t *testing.T) { }, { Config: testAccBrandingConfigThrowsAValidationErrorIfUniversalLoginBodyIsEmpty, - ExpectError: regexp.MustCompile("expected \"universal_login.0.body\" to not be an empty string"), + ExpectError: regexp.MustCompile("expected \"universal_login.0.body\" to contain a single auth0:head tag and at least one auth0:widget tag"), }, { Config: testAccBrandingConfigRemovesUniversalLoginTemplate, diff --git a/internal/auth0/connection/expand.go b/internal/auth0/connection/expand.go index cd94b52b9..2502f1de3 100644 --- a/internal/auth0/connection/expand.go +++ b/internal/auth0/connection/expand.go @@ -28,6 +28,7 @@ var expandConnectionOptionsMap = map[string]expandConnectionOptionsFunc{ management.ConnectionStrategyBox: expandConnectionOptionsOAuth2, management.ConnectionStrategyWordpress: expandConnectionOptionsOAuth2, management.ConnectionStrategyShopify: expandConnectionOptionsOAuth2, + management.ConnectionStrategyLine: expandConnectionOptionsOAuth2, management.ConnectionStrategyCustom: expandConnectionOptionsOAuth2, management.ConnectionStrategyFacebook: expandConnectionOptionsFacebook, management.ConnectionStrategyApple: expandConnectionOptionsApple, @@ -975,3 +976,26 @@ func passThroughUnconfigurableConnectionOptionsPingFederate( return nil } + +// checkForUnmanagedConfigurationSecrets is used to assess keys diff because values are sent back encrypted. +func checkForUnmanagedConfigurationSecrets(configFromTF, configFromAPI map[string]string) diag.Diagnostics { + var warnings diag.Diagnostics + + for key := range configFromAPI { + if _, ok := configFromTF[key]; !ok { + warnings = append(warnings, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unmanaged Configuration Secret", + Detail: fmt.Sprintf("Detected a configuration secret not managed through terraform: %q. "+ + "If you proceed, this configuration secret will get deleted. It is required to "+ + "add this configuration secret to your custom database settings to "+ + "prevent unintentionally destructive results.", + key, + ), + AttributePath: cty.Path{cty.GetAttrStep{Name: "options.configuration"}}, + }) + } + } + + return warnings +} diff --git a/internal/auth0/connection/flatten.go b/internal/auth0/connection/flatten.go index 9acd5df18..530a78306 100644 --- a/internal/auth0/connection/flatten.go +++ b/internal/auth0/connection/flatten.go @@ -1,6 +1,7 @@ package connection import ( + "errors" "fmt" "github.com/auth0/go-auth0/management" @@ -11,8 +12,54 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" ) +var errUnsupportedConnectionOptionsType = errors.New("unsupported connection options type") + +var flattenConnectionOptionsMap = map[string]flattenConnectionOptionsFunc{ + // Database Connection. + management.ConnectionStrategyAuth0: flattenConnectionOptionsAuth0, + + // Social Connections. + management.ConnectionStrategyGoogleOAuth2: flattenConnectionOptionsGoogleOAuth2, + management.ConnectionStrategyOAuth2: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyDropbox: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyBitBucket: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyPaypal: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyTwitter: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyAmazon: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyYahoo: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyBox: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyWordpress: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyShopify: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyLine: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyCustom: flattenConnectionOptionsOAuth2, + management.ConnectionStrategyFacebook: flattenConnectionOptionsFacebook, + management.ConnectionStrategyApple: flattenConnectionOptionsApple, + management.ConnectionStrategyLinkedin: flattenConnectionOptionsLinkedin, + management.ConnectionStrategyGitHub: flattenConnectionOptionsGitHub, + management.ConnectionStrategyWindowsLive: flattenConnectionOptionsWindowsLive, + management.ConnectionStrategySalesforce: flattenConnectionOptionsSalesforce, + management.ConnectionStrategySalesforceCommunity: flattenConnectionOptionsSalesforce, + management.ConnectionStrategySalesforceSandbox: flattenConnectionOptionsSalesforce, + + // Passwordless Connections. + management.ConnectionStrategySMS: flattenConnectionOptionsSMS, + management.ConnectionStrategyEmail: flattenConnectionOptionsEmail, + + // Enterprise Connections. + management.ConnectionStrategyOIDC: flattenConnectionOptionsOIDC, + management.ConnectionStrategyGoogleApps: flattenConnectionOptionsGoogleApps, + management.ConnectionStrategyOkta: flattenConnectionOptionsOkta, + management.ConnectionStrategyAD: flattenConnectionOptionsAD, + management.ConnectionStrategyAzureAD: flattenConnectionOptionsAzureAD, + management.ConnectionStrategySAML: flattenConnectionOptionsSAML, + management.ConnectionStrategyADFS: flattenConnectionOptionsADFS, + management.ConnectionStrategyPingFederate: flattenConnectionOptionsPingFederate, +} + +type flattenConnectionOptionsFunc func(data *schema.ResourceData, options interface{}) (interface{}, diag.Diagnostics) + func flattenConnection(data *schema.ResourceData, connection *management.Connection) diag.Diagnostics { - connectionOptions, diags := flattenConnectionOptions(data, connection.Options) + connectionOptions, diags := flattenConnectionOptions(data, connection) if diags.HasError() { return diags } @@ -27,13 +74,7 @@ func flattenConnection(data *schema.ResourceData, connection *management.Connect data.Set("metadata", connection.GetMetadata()), ) - switch connection.GetStrategy() { - case management.ConnectionStrategyGoogleApps, - management.ConnectionStrategyOIDC, - management.ConnectionStrategyAD, - management.ConnectionStrategyAzureAD, - management.ConnectionStrategySAML, - management.ConnectionStrategyADFS: + if connectionIsEnterprise(connection.GetStrategy()) { result = multierror.Append(result, data.Set("show_as_button", connection.GetShowAsButton())) } @@ -50,104 +91,105 @@ func flattenConnectionForDataSource(data *schema.ResourceData, connection *manag return diags } -func flattenConnectionOptions(data *schema.ResourceData, options interface{}) ([]interface{}, diag.Diagnostics) { - if options == nil { +func flattenConnectionOptions(data *schema.ResourceData, connection *management.Connection) ([]interface{}, diag.Diagnostics) { + if connection == nil || connection.Options == nil { return nil, nil } - var m interface{} - var diags diag.Diagnostics - switch connectionOptions := options.(type) { - case *management.ConnectionOptions: - m, diags = flattenConnectionOptionsAuth0(data, connectionOptions) - case *management.ConnectionOptionsGoogleOAuth2: - m, diags = flattenConnectionOptionsGoogleOAuth2(connectionOptions) - case *management.ConnectionOptionsGoogleApps: - m, diags = flattenConnectionOptionsGoogleApps(connectionOptions) - case *management.ConnectionOptionsOAuth2: - m, diags = flattenConnectionOptionsOAuth2(connectionOptions) - case *management.ConnectionOptionsFacebook: - m, diags = flattenConnectionOptionsFacebook(connectionOptions) - case *management.ConnectionOptionsApple: - m, diags = flattenConnectionOptionsApple(connectionOptions) - case *management.ConnectionOptionsLinkedin: - m, diags = flattenConnectionOptionsLinkedin(connectionOptions) - case *management.ConnectionOptionsGitHub: - m, diags = flattenConnectionOptionsGitHub(connectionOptions) - case *management.ConnectionOptionsWindowsLive: - m, diags = flattenConnectionOptionsWindowsLive(connectionOptions) - case *management.ConnectionOptionsSalesforce: - m, diags = flattenConnectionOptionsSalesforce(connectionOptions) - case *management.ConnectionOptionsEmail: - m, diags = flattenConnectionOptionsEmail(connectionOptions) - case *management.ConnectionOptionsSMS: - m, diags = flattenConnectionOptionsSMS(connectionOptions) - case *management.ConnectionOptionsOIDC: - m, diags = flattenConnectionOptionsOIDC(connectionOptions) - case *management.ConnectionOptionsOkta: - m, diags = flattenConnectionOptionsOkta(connectionOptions) - case *management.ConnectionOptionsAD: - m, diags = flattenConnectionOptionsAD(connectionOptions) - case *management.ConnectionOptionsAzureAD: - m, diags = flattenConnectionOptionsAzureAD(connectionOptions) - case *management.ConnectionOptionsADFS: - m, diags = flattenConnectionOptionsADFS(connectionOptions) - case *management.ConnectionOptionsPingFederate: - m, diags = flattenConnectionOptionsPingFederate(connectionOptions) - case *management.ConnectionOptionsSAML: - m, diags = flattenConnectionOptionsSAML(data, connectionOptions) - } - - return []interface{}{m}, diags + connectionOptionsFunc, ok := flattenConnectionOptionsMap[connection.GetStrategy()] + if !ok { + return nil, diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "Unsupported Connection Strategy", + Detail: fmt.Sprintf( + "Raise an issue at %s in order to have the following connection strategy supported: %q", + "https://github.com/auth0/terraform-provider-auth0/issues/new", + connection.GetStrategy(), + ), + AttributePath: cty.Path{cty.GetAttrStep{Name: "strategy"}}, + }, + } + } + + connectionOptionsMap, diagnostics := connectionOptionsFunc(data, connection.Options) + + return []interface{}{connectionOptionsMap}, diagnostics } -func flattenConnectionOptionsGitHub(options *management.ConnectionOptionsGitHub) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ +func flattenConnectionOptionsGitHub( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsGitHub) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) + } + + upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) + if err != nil { + return nil, diag.FromErr(err) + } + + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), "scopes": options.Scopes(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsWindowsLive( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsWindowsLive) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - - return m, nil -} -func flattenConnectionOptionsWindowsLive(options *management.ConnectionOptionsWindowsLive) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "scopes": options.Scopes(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), "strategy_version": options.GetStrategyVersion(), + "upstream_params": upstreamParams, } - upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) - if err != nil { - return nil, diag.FromErr(err) - } - m["upstream_params"] = upstreamParams - - return m, nil + return optionsMap, nil } func flattenConnectionOptionsAuth0( data *schema.ResourceData, - options *management.ConnectionOptions, + rawOptions interface{}, ) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptions) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) + } + + upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) + if err != nil { + return nil, diag.FromErr(err) + } + dbSecretConfig, ok := data.GetOk("options.0.configuration") if !ok { dbSecretConfig = make(map[string]interface{}) } - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "password_policy": options.GetPasswordPolicy(), "enable_script_context": options.GetEnableScriptContext(), "enabled_database_customization": options.GetEnabledDatabaseCustomization(), @@ -160,25 +202,31 @@ func flattenConnectionOptionsAuth0( "configuration": dbSecretConfig, // Values do not get read back. "non_persistent_attrs": options.GetNonPersistentAttrs(), "set_user_root_attributes": options.GetSetUserAttributes(), + "upstream_params": upstreamParams, } if options.PasswordComplexityOptions != nil { - m["password_complexity_options"] = []interface{}{options.PasswordComplexityOptions} + optionsMap["password_complexity_options"] = []interface{}{options.PasswordComplexityOptions} } + if options.PasswordDictionary != nil { - m["password_dictionary"] = []interface{}{options.PasswordDictionary} + optionsMap["password_dictionary"] = []interface{}{options.PasswordDictionary} } + if options.PasswordNoPersonalInfo != nil { - m["password_no_personal_info"] = []interface{}{options.PasswordNoPersonalInfo} + optionsMap["password_no_personal_info"] = []interface{}{options.PasswordNoPersonalInfo} } + if options.PasswordHistory != nil { - m["password_history"] = []interface{}{options.PasswordHistory} + optionsMap["password_history"] = []interface{}{options.PasswordHistory} } + if options.MFA != nil { - m["mfa"] = []interface{}{options.MFA} + optionsMap["mfa"] = []interface{}{options.MFA} } + if options.Validation != nil { - m["validation"] = []interface{}{ + optionsMap["validation"] = []interface{}{ map[string]interface{}{ "username": []interface{}{ options.Validation["username"], @@ -187,90 +235,86 @@ func flattenConnectionOptionsAuth0( } } - upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) - if err != nil { - return nil, diag.FromErr(err) - } - m["upstream_params"] = upstreamParams - - return m, nil + return optionsMap, nil } -// checkForUnmanagedConfigurationSecrets is used to assess keys diff because values are sent back encrypted. -func checkForUnmanagedConfigurationSecrets(configFromTF, configFromAPI map[string]string) diag.Diagnostics { - var warnings diag.Diagnostics - - for key := range configFromAPI { - if _, ok := configFromTF[key]; !ok { - warnings = append(warnings, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Unmanaged Configuration Secret", - Detail: fmt.Sprintf("Detected a configuration secret not managed through terraform: %q. "+ - "If you proceed, this configuration secret will get deleted. It is required to "+ - "add this configuration secret to your custom database settings to "+ - "prevent unintentionally destructive results.", - key, - ), - AttributePath: cty.Path{cty.GetAttrStep{Name: "options.configuration"}}, - }) - } +func flattenConnectionOptionsGoogleOAuth2( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsGoogleOAuth2) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } - return warnings -} + upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) + if err != nil { + return nil, diag.FromErr(err) + } -func flattenConnectionOptionsGoogleOAuth2( - options *management.ConnectionOptionsGoogleOAuth2, -) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "allowed_audiences": options.GetAllowedAudiences(), "scopes": options.Scopes(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsGoogleApps( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsGoogleApps) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil + optionsMap := map[string]interface{}{ + "client_id": options.GetClientID(), + "client_secret": options.GetClientSecret(), + "domain": options.GetDomain(), + "tenant_domain": options.GetTenantDomain(), + "api_enable_users": options.GetEnableUsersAPI(), + "scopes": options.Scopes(), + "non_persistent_attrs": options.GetNonPersistentAttrs(), + "domain_aliases": options.GetDomainAliases(), + "icon_url": options.GetLogoURL(), + "set_user_root_attributes": options.GetSetUserAttributes(), + "upstream_params": upstreamParams, + } + + if options.GetSetUserAttributes() == "" { + optionsMap["set_user_root_attributes"] = "on_each_login" + } + + return optionsMap, nil } -func flattenConnectionOptionsGoogleApps( - options *management.ConnectionOptionsGoogleApps, +func flattenConnectionOptionsOAuth2( + _ *schema.ResourceData, + rawOptions interface{}, ) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ - "client_id": options.GetClientID(), - "client_secret": options.GetClientSecret(), - "domain": options.GetDomain(), - "tenant_domain": options.GetTenantDomain(), - "api_enable_users": options.GetEnableUsersAPI(), - "scopes": options.Scopes(), - "non_persistent_attrs": options.GetNonPersistentAttrs(), - "domain_aliases": options.GetDomainAliases(), - "icon_url": options.GetLogoURL(), - } - - m["set_user_root_attributes"] = options.GetSetUserAttributes() - if options.GetSetUserAttributes() == "" { - m["set_user_root_attributes"] = "on_each_login" + options, ok := rawOptions.(*management.ConnectionOptionsOAuth2) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsOAuth2(options *management.ConnectionOptionsOAuth2) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "scopes": options.Scopes(), @@ -281,37 +325,53 @@ func flattenConnectionOptionsOAuth2(options *management.ConnectionOptionsOAuth2) "non_persistent_attrs": options.GetNonPersistentAttrs(), "icon_url": options.GetLogoURL(), "pkce_enabled": options.GetPKCEEnabled(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsFacebook( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsFacebook) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - - return m, nil -} -func flattenConnectionOptionsFacebook(options *management.ConnectionOptionsFacebook) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "scopes": options.Scopes(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsApple( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsApple) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - - return m, nil -} -func flattenConnectionOptionsApple(options *management.ConnectionOptionsApple) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "team_id": options.GetTeamID(), @@ -319,57 +379,81 @@ func flattenConnectionOptionsApple(options *management.ConnectionOptionsApple) ( "scopes": options.Scopes(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsLinkedin( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsLinkedin) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - - return m, nil -} -func flattenConnectionOptionsLinkedin(options *management.ConnectionOptionsLinkedin) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "strategy_version": options.GetStrategyVersion(), "scopes": options.Scopes(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsSalesforce( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsSalesforce) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsSalesforce(options *management.ConnectionOptionsSalesforce) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "community_base_url": options.GetCommunityBaseURL(), "scopes": options.Scopes(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsSMS( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsSMS) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - - return m, nil -} -func flattenConnectionOptionsSMS(options *management.ConnectionOptionsSMS) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "name": options.GetName(), "from": options.GetFrom(), "syntax": options.GetSyntax(), @@ -382,10 +466,11 @@ func flattenConnectionOptionsSMS(options *management.ConnectionOptionsSMS) (inte "provider": options.GetProvider(), "gateway_url": options.GetGatewayURL(), "forward_request_info": options.GetForwardRequestInfo(), + "upstream_params": upstreamParams, } if options.OTP != nil { - m["totp"] = []interface{}{ + optionsMap["totp"] = []interface{}{ map[string]interface{}{ "time_step": options.OTP.GetTimeStep(), "length": options.OTP.GetLength(), @@ -394,7 +479,7 @@ func flattenConnectionOptionsSMS(options *management.ConnectionOptionsSMS) (inte } if options.GatewayAuthentication != nil { - m["gateway_authentication"] = []interface{}{ + optionsMap["gateway_authentication"] = []interface{}{ map[string]interface{}{ "method": options.GatewayAuthentication.GetMethod(), "subject": options.GatewayAuthentication.GetSubject(), @@ -405,17 +490,24 @@ func flattenConnectionOptionsSMS(options *management.ConnectionOptionsSMS) (inte } } + return optionsMap, nil +} + +func flattenConnectionOptionsOIDC( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsOIDC) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) + } + upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsOIDC(options *management.ConnectionOptionsOIDC) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "icon_url": options.GetLogoURL(), @@ -431,19 +523,27 @@ func flattenConnectionOptionsOIDC(options *management.ConnectionOptionsOIDC) (in "authorization_endpoint": options.GetAuthorizationEndpoint(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsOkta( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsOkta) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsOkta(options *management.ConnectionOptionsOkta) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "domain": options.GetDomain(), @@ -457,19 +557,27 @@ func flattenConnectionOptionsOkta(options *management.ConnectionOptionsOkta) (in "non_persistent_attrs": options.GetNonPersistentAttrs(), "set_user_root_attributes": options.GetSetUserAttributes(), "icon_url": options.GetLogoURL(), + "upstream_params": upstreamParams, + } + + return optionsMap, nil +} + +func flattenConnectionOptionsEmail( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsEmail) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsEmail(options *management.ConnectionOptionsEmail) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "name": options.GetName(), "from": options.GetEmail().GetFrom(), "syntax": options.GetEmail().GetSyntax(), @@ -479,10 +587,11 @@ func flattenConnectionOptionsEmail(options *management.ConnectionOptionsEmail) ( "brute_force_protection": options.GetBruteForceProtection(), "set_user_root_attributes": options.GetSetUserAttributes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "upstream_params": upstreamParams, } if options.OTP != nil { - m["totp"] = []interface{}{ + optionsMap["totp"] = []interface{}{ map[string]interface{}{ "time_step": options.OTP.GetTimeStep(), "length": options.OTP.GetLength(), @@ -490,57 +599,73 @@ func flattenConnectionOptionsEmail(options *management.ConnectionOptionsEmail) ( } } - upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) - if err != nil { - return nil, diag.FromErr(err) - } - m["upstream_params"] = upstreamParams - if options.AuthParams != nil { v, ok := options.AuthParams.(map[string]interface{}) if !ok { - return m, diag.Diagnostics{{ + return optionsMap, diag.Diagnostics{{ Severity: diag.Warning, Summary: "Unable to cast auth_params to map[string]string", Detail: fmt.Sprintf(`Authentication Parameters are required to be a map of strings, the existing value of %v is not compatible. It is recommended to express the existing value as a valid map[string]string. Subsequent terraform applys will clear this configuration to empty map.`, options.AuthParams), AttributePath: cty.Path{cty.GetAttrStep{Name: "options.auth_params"}}, }} } - m["auth_params"] = v + + optionsMap["auth_params"] = v } - return m, nil + return optionsMap, nil } -func flattenConnectionOptionsAD(options *management.ConnectionOptionsAD) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ - "tenant_domain": options.GetTenantDomain(), - "domain_aliases": options.GetDomainAliases(), - "icon_url": options.GetLogoURL(), - "ips": options.GetIPs(), - "use_cert_auth": options.GetCertAuth(), - "use_kerberos": options.GetKerberos(), - "disable_cache": options.GetDisableCache(), - "brute_force_protection": options.GetBruteForceProtection(), - "non_persistent_attrs": options.GetNonPersistentAttrs(), +func flattenConnectionOptionsAD( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsAD) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) + } + + upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) + if err != nil { + return nil, diag.FromErr(err) + } + + optionsMap := map[string]interface{}{ + "tenant_domain": options.GetTenantDomain(), + "domain_aliases": options.GetDomainAliases(), + "icon_url": options.GetLogoURL(), + "ips": options.GetIPs(), + "use_cert_auth": options.GetCertAuth(), + "use_kerberos": options.GetKerberos(), + "disable_cache": options.GetDisableCache(), + "brute_force_protection": options.GetBruteForceProtection(), + "non_persistent_attrs": options.GetNonPersistentAttrs(), + "set_user_root_attributes": options.GetSetUserAttributes(), + "upstream_params": upstreamParams, } - m["set_user_root_attributes"] = options.GetSetUserAttributes() if options.GetSetUserAttributes() == "" { - m["set_user_root_attributes"] = "on_each_login" + optionsMap["set_user_root_attributes"] = "on_each_login" + } + + return optionsMap, nil +} + +func flattenConnectionOptionsAzureAD( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsAzureAD) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsAzureAD(options *management.ConnectionOptionsAzureAD) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "client_id": options.GetClientID(), "client_secret": options.GetClientSecret(), "app_id": options.GetAppID(), @@ -557,24 +682,32 @@ func flattenConnectionOptionsAzureAD(options *management.ConnectionOptionsAzureA "scopes": options.Scopes(), "non_persistent_attrs": options.GetNonPersistentAttrs(), "should_trust_email_verified_connection": options.GetTrustEmailVerified(), + "set_user_root_attributes": options.GetSetUserAttributes(), + "upstream_params": upstreamParams, } - m["set_user_root_attributes"] = options.GetSetUserAttributes() if options.GetSetUserAttributes() == "" { - m["set_user_root_attributes"] = "on_each_login" + optionsMap["set_user_root_attributes"] = "on_each_login" + } + + return optionsMap, nil +} + +func flattenConnectionOptionsADFS( + _ *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsADFS) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} - -func flattenConnectionOptionsADFS(options *management.ConnectionOptionsADFS) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ + optionsMap := map[string]interface{}{ "tenant_domain": options.GetTenantDomain(), "domain_aliases": options.GetDomainAliases(), "icon_url": options.GetLogoURL(), @@ -584,54 +717,66 @@ func flattenConnectionOptionsADFS(options *management.ConnectionOptionsADFS) (in "api_enable_users": options.GetEnableUsersAPI(), "should_trust_email_verified_connection": options.GetTrustEmailVerified(), "non_persistent_attrs": options.GetNonPersistentAttrs(), + "set_user_root_attributes": options.GetSetUserAttributes(), + "upstream_params": upstreamParams, } - m["set_user_root_attributes"] = options.GetSetUserAttributes() if options.GetSetUserAttributes() == "" { - m["set_user_root_attributes"] = "on_each_login" + optionsMap["set_user_root_attributes"] = "on_each_login" + } + + return optionsMap, nil +} + +func flattenConnectionOptionsSAML( + data *schema.ResourceData, + rawOptions interface{}, +) (interface{}, diag.Diagnostics) { + options, ok := rawOptions.(*management.ConnectionOptionsSAML) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) + } + + fieldsMap, err := structure.FlattenJsonToString(options.FieldsMap) + if err != nil { + return nil, diag.FromErr(err) } upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) if err != nil { return nil, diag.FromErr(err) } - m["upstream_params"] = upstreamParams - return m, nil -} + optionsMap := map[string]interface{}{ + "signing_cert": options.GetSigningCert(), + "protocol_binding": options.GetProtocolBinding(), + "debug": options.GetDebug(), + "tenant_domain": options.GetTenantDomain(), + "domain_aliases": options.GetDomainAliases(), + "sign_in_endpoint": options.GetSignInEndpoint(), + "sign_out_endpoint": options.GetSignOutEndpoint(), + "disable_sign_out": options.GetDisableSignOut(), + "signature_algorithm": options.GetSignatureAlgorithm(), + "digest_algorithm": options.GetDigestAglorithm(), + "sign_saml_request": options.GetSignSAMLRequest(), + "icon_url": options.GetLogoURL(), + "request_template": options.GetRequestTemplate(), + "user_id_attribute": options.GetUserIDAttribute(), + "non_persistent_attrs": options.GetNonPersistentAttrs(), + "entity_id": options.GetEntityID(), + "metadata_url": options.GetMetadataURL(), + "metadata_xml": data.Get("options.0.metadata_xml").(string), // Does not get read back. + "set_user_root_attributes": options.GetSetUserAttributes(), + "fields_map": fieldsMap, + "upstream_params": upstreamParams, + } -func flattenConnectionOptionsSAML( - data *schema.ResourceData, - options *management.ConnectionOptionsSAML, -) (interface{}, diag.Diagnostics) { - m := map[string]interface{}{ - "signing_cert": options.GetSigningCert(), - "protocol_binding": options.GetProtocolBinding(), - "debug": options.GetDebug(), - "tenant_domain": options.GetTenantDomain(), - "domain_aliases": options.GetDomainAliases(), - "sign_in_endpoint": options.GetSignInEndpoint(), - "sign_out_endpoint": options.GetSignOutEndpoint(), - "disable_sign_out": options.GetDisableSignOut(), - "signature_algorithm": options.GetSignatureAlgorithm(), - "digest_algorithm": options.GetDigestAglorithm(), - "sign_saml_request": options.GetSignSAMLRequest(), - "icon_url": options.GetLogoURL(), - "request_template": options.GetRequestTemplate(), - "user_id_attribute": options.GetUserIDAttribute(), - "non_persistent_attrs": options.GetNonPersistentAttrs(), - "entity_id": options.GetEntityID(), - "metadata_url": options.GetMetadataURL(), - "metadata_xml": data.Get("options.0.metadata_xml").(string), // Does not get read back. - } - - m["set_user_root_attributes"] = options.GetSetUserAttributes() if options.GetSetUserAttributes() == "" { - m["set_user_root_attributes"] = "on_each_login" + optionsMap["set_user_root_attributes"] = "on_each_login" } if options.IdpInitiated != nil { - m["idp_initiated"] = []interface{}{ + optionsMap["idp_initiated"] = []interface{}{ map[string]interface{}{ "client_id": options.IdpInitiated.GetClientID(), "client_protocol": options.IdpInitiated.GetClientProtocol(), @@ -641,7 +786,7 @@ func flattenConnectionOptionsSAML( } if options.SigningKey != nil { - m["signing_key"] = []interface{}{ + optionsMap["signing_key"] = []interface{}{ map[string]interface{}{ "key": options.GetSigningKey().GetKey(), "cert": options.GetSigningKey().GetCert(), @@ -650,7 +795,7 @@ func flattenConnectionOptionsSAML( } if options.DecryptionKey != nil { - m["decryption_key"] = []interface{}{ + optionsMap["decryption_key"] = []interface{}{ map[string]interface{}{ "key": options.GetDecryptionKey().GetKey(), "cert": options.GetDecryptionKey().GetCert(), @@ -658,62 +803,54 @@ func flattenConnectionOptionsSAML( } } - fieldsMap, err := structure.FlattenJsonToString(options.FieldsMap) - if err != nil { - return nil, diag.FromErr(err) - } - m["fields_map"] = fieldsMap - - upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) - if err != nil { - return nil, diag.FromErr(err) - } - m["upstream_params"] = upstreamParams - - return m, nil + return optionsMap, nil } func flattenConnectionOptionsPingFederate( - options *management.ConnectionOptionsPingFederate, + _ *schema.ResourceData, + rawOptions interface{}, ) (interface{}, diag.Diagnostics) { - signingCert := options.GetSigningCert() - if signingCert == "" { - signingCert = options.GetCert() + options, ok := rawOptions.(*management.ConnectionOptionsPingFederate) + if !ok { + return nil, diag.FromErr(errUnsupportedConnectionOptionsType) } - m := map[string]interface{}{ - "signing_cert": signingCert, - "tenant_domain": options.GetTenantDomain(), - "domain_aliases": options.GetDomainAliases(), - "sign_in_endpoint": options.GetSignInEndpoint(), - "signature_algorithm": options.GetSignatureAlgorithm(), - "digest_algorithm": options.GetDigestAlgorithm(), - "sign_saml_request": options.GetSignSAMLRequest(), - "ping_federate_base_url": options.GetPingFederateBaseURL(), - "icon_url": options.GetLogoURL(), - "non_persistent_attrs": options.GetNonPersistentAttrs(), + upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) + if err != nil { + return nil, diag.FromErr(err) } - m["set_user_root_attributes"] = options.GetSetUserAttributes() - if options.GetSetUserAttributes() == "" { - m["set_user_root_attributes"] = "on_each_login" + optionsMap := map[string]interface{}{ + "signing_cert": options.GetSigningCert(), + "tenant_domain": options.GetTenantDomain(), + "domain_aliases": options.GetDomainAliases(), + "sign_in_endpoint": options.GetSignInEndpoint(), + "signature_algorithm": options.GetSignatureAlgorithm(), + "digest_algorithm": options.GetDigestAlgorithm(), + "sign_saml_request": options.GetSignSAMLRequest(), + "ping_federate_base_url": options.GetPingFederateBaseURL(), + "icon_url": options.GetLogoURL(), + "non_persistent_attrs": options.GetNonPersistentAttrs(), + "set_user_root_attributes": options.GetSetUserAttributes(), + "upstream_params": upstreamParams, + "idp_initiated": []map[string]interface{}{ + { + "client_id": options.GetIdpInitiated().GetClientID(), + "client_protocol": options.GetIdpInitiated().GetClientProtocol(), + "client_authorize_query": options.GetIdpInitiated().GetClientAuthorizeQuery(), + }, + }, } - m["idp_initiated"] = []interface{}{ - map[string]interface{}{ - "client_id": options.GetIdpInitiated().GetClientID(), - "client_protocol": options.GetIdpInitiated().GetClientProtocol(), - "client_authorize_query": options.GetIdpInitiated().GetClientAuthorizeQuery(), - }, + if options.GetSigningCert() == "" { + optionsMap["signing_cert"] = options.GetCert() } - upstreamParams, err := structure.FlattenJsonToString(options.UpstreamParams) - if err != nil { - return nil, diag.FromErr(err) + if options.GetSetUserAttributes() == "" { + optionsMap["set_user_root_attributes"] = "on_each_login" } - m["upstream_params"] = upstreamParams - return m, nil + return optionsMap, nil } func flattenConnectionClient(data *schema.ResourceData, connection *management.Connection) error { diff --git a/internal/auth0/connection/flatten_test.go b/internal/auth0/connection/flatten_test.go index a70fa0414..b7005616a 100644 --- a/internal/auth0/connection/flatten_test.go +++ b/internal/auth0/connection/flatten_test.go @@ -21,7 +21,7 @@ func TestFlattenConnectionOptions(t *testing.T) { func TestFlattenConnectionOptionsEmail(t *testing.T) { // Invalid Authentication Params. invalidAuthParams := "some non-map value" - _, diags := flattenConnectionOptionsEmail(&management.ConnectionOptionsEmail{ + _, diags := flattenConnectionOptionsEmail(nil, &management.ConnectionOptionsEmail{ AuthParams: invalidAuthParams, }) @@ -42,7 +42,7 @@ func TestFlattenConnectionOptionsEmail(t *testing.T) { "foo": "bar", "bar": "baz", } - _, diags = flattenConnectionOptionsEmail(&management.ConnectionOptionsEmail{ + _, diags = flattenConnectionOptionsEmail(nil, &management.ConnectionOptionsEmail{ AuthParams: validAuthParams, }) diff --git a/internal/auth0/prompt/flatten.go b/internal/auth0/prompt/flatten.go index 19085a1cc..d7bbf07e8 100644 --- a/internal/auth0/prompt/flatten.go +++ b/internal/auth0/prompt/flatten.go @@ -30,6 +30,10 @@ func flattenPromptCustomText(data *schema.ResourceData, customText map[string]in } func marshalCustomTextBody(b map[string]interface{}) (string, error) { + if b == nil { + return "{}", nil + } + bodyBytes, err := json.Marshal(b) if err != nil { return "", fmt.Errorf("failed to serialize the custom texts to JSON: %w", err) diff --git a/internal/auth0/prompt/resource_custom_text_test.go b/internal/auth0/prompt/resource_custom_text_test.go index 039a8b17b..5fd373a9c 100644 --- a/internal/auth0/prompt/resource_custom_text_test.go +++ b/internal/auth0/prompt/resource_custom_text_test.go @@ -11,6 +11,14 @@ import ( func TestAccPromptCustomText(t *testing.T) { acctest.Test(t, resource.TestCase{ Steps: []resource.TestStep{ + { + Config: testAccPromptCustomTextEmptyBody, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("auth0_prompt_custom_text.prompt_custom_text", "prompt", "login"), + resource.TestCheckResourceAttr("auth0_prompt_custom_text.prompt_custom_text", "language", "en"), + resource.TestCheckResourceAttr("auth0_prompt_custom_text.prompt_custom_text", "body", "{}"), + ), + }, { Config: testAccPromptCustomTextCreate, Check: resource.ComposeTestCheckFunc( @@ -35,10 +43,26 @@ func TestAccPromptCustomText(t *testing.T) { ), ), }, + { + Config: testAccPromptCustomTextEmptyBody, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("auth0_prompt_custom_text.prompt_custom_text", "prompt", "login"), + resource.TestCheckResourceAttr("auth0_prompt_custom_text.prompt_custom_text", "language", "en"), + resource.TestCheckResourceAttr("auth0_prompt_custom_text.prompt_custom_text", "body", "{}"), + ), + }, }, }) } +const testAccPromptCustomTextEmptyBody = ` +resource "auth0_prompt_custom_text" "prompt_custom_text" { + prompt = "login" + language = "en" + body = "{}" +} +` + const testAccPromptCustomTextCreate = ` resource "auth0_prompt_custom_text" "prompt_custom_text" { prompt = "login" diff --git a/internal/validation/validation.go b/internal/validation/validation.go index 4037c9f08..98b21beac 100644 --- a/internal/validation/validation.go +++ b/internal/validation/validation.go @@ -3,6 +3,7 @@ package validation import ( "fmt" "net/url" + "strings" ) // IsURLWithHTTPSorEmptyString is a validation func that checks @@ -40,3 +41,20 @@ func IsURLWithHTTPSorEmptyString(rawURL interface{}, key string) ([]string, []er return nil, nil } + +// UniversalLoginTemplateContainsCorrectTags is a validation func that checks +// that the given universal login template body contains the correct tags. +func UniversalLoginTemplateContainsCorrectTags(rawBody interface{}, key string) ([]string, []error) { + v, ok := rawBody.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %q to be string", key)} + } + + if strings.Contains(v, "{%- auth0:head -%}") && strings.Contains(v, "{%- auth0:widget -%}") { + return nil, nil + } + + return nil, []error{ + fmt.Errorf("expected %q to contain a single auth0:head tag and at least one auth0:widget tag", key), + } +} diff --git a/internal/validation/validation_test.go b/internal/validation/validation_test.go index acc0be360..f73614373 100644 --- a/internal/validation/validation_test.go +++ b/internal/validation/validation_test.go @@ -74,3 +74,50 @@ func TestIsURLWithHTTPSorEmptyString(t *testing.T) { }) } } + +func TestUniversalLoginTemplateContainsCorrectTags(t *testing.T) { + tests := []struct { + name string + input interface{} + key string + expectedError string + }{ + { + name: "valid input", + input: `Some content {%- auth0:head -%} More content {%- auth0:widget -%}`, + key: "testKey", + expectedError: "", + }, + { + name: "missing auth0:head tag", + input: `Some content More content {%- auth0:widget -%}`, + key: "testKey", + expectedError: "expected \"testKey\" to contain a single auth0:head tag and at least one auth0:widget tag", + }, + { + name: "missing auth0:widget tag", + input: `Some content {%- auth0:head -%} More content`, + key: "testKey", + expectedError: "expected \"testKey\" to contain a single auth0:head tag and at least one auth0:widget tag", + }, + { + name: "incorrect input type", + input: 42, + key: "testKey", + expectedError: "expected type of \"testKey\" to be string", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, errors := UniversalLoginTemplateContainsCorrectTags(test.input, test.key) + + if test.expectedError != "" { + assert.EqualError(t, errors[0], test.expectedError) + return + } + + assert.Len(t, errors, 0) + }) + } +} diff --git a/test/data/recordings/TestAccPromptCustomText.yaml b/test/data/recordings/TestAccPromptCustomText.yaml index 3baa042b5..f9da6d615 100644 --- a/test/data/recordings/TestAccPromptCustomText.yaml +++ b/test/data/recordings/TestAccPromptCustomText.yaml @@ -2,6 +2,150 @@ version: 2 interactions: - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 3 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + {} + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: PUT + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 154.318375ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 5 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + null + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 68.087542ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 5 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + null + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 178.246334ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 5 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + null + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 157.751125ms + - id: 4 request: proto: HTTP/1.1 proto_major: 1 @@ -19,7 +163,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: PUT response: @@ -36,8 +180,8 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 1 + duration: 170.454667ms + - id: 5 request: proto: HTTP/1.1 proto_major: 1 @@ -55,7 +199,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: GET response: @@ -72,8 +216,8 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 2 + duration: 151.822042ms + - id: 6 request: proto: HTTP/1.1 proto_major: 1 @@ -91,7 +235,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: GET response: @@ -108,8 +252,8 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 3 + duration: 152.117917ms + - id: 7 request: proto: HTTP/1.1 proto_major: 1 @@ -127,7 +271,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: GET response: @@ -144,8 +288,8 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 4 + duration: 63.871834ms + - id: 8 request: proto: HTTP/1.1 proto_major: 1 @@ -163,7 +307,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: PUT response: @@ -180,8 +324,8 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 5 + duration: 74.097333ms + - id: 9 request: proto: HTTP/1.1 proto_major: 1 @@ -199,7 +343,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: GET response: @@ -216,8 +360,8 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 6 + duration: 89.6465ms + - id: 10 request: proto: HTTP/1.1 proto_major: 1 @@ -235,7 +379,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: GET response: @@ -252,8 +396,44 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 7 + duration: 151.20125ms + - id: 11 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 5 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + null + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: -1 + uncompressed: true + body: '{"login":{"alertListTitle":"Alerts","buttonText":"Proceed","emailPlaceholder":"Email Address","title":"Welcome to ${companyName}"}}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 144.794875ms + - id: 12 request: proto: HTTP/1.1 proto_major: 1 @@ -271,7 +451,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: PUT response: @@ -288,8 +468,116 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms - - id: 8 + duration: 83.862042ms + - id: 13 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 5 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + null + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 174.305792ms + - id: 14 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 5 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + null + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: GET + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 144.166875ms + - id: 15 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 3 + transfer_encoding: [] + trailer: {} + host: terraform-provider-auth0-dev.eu.auth0.com + remote_addr: "" + request_uri: "" + body: | + {} + form: {} + headers: + Content-Type: + - application/json + User-Agent: + - Go-Auth0/1.0.2 + url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en + method: PUT + response: + proto: HTTP/2.0 + proto_major: 2 + proto_minor: 0 + transfer_encoding: [] + trailer: {} + content_length: 2 + uncompressed: false + body: '{}' + headers: + Content-Type: + - application/json; charset=utf-8 + status: 200 OK + code: 200 + duration: 163.848917ms + - id: 16 request: proto: HTTP/1.1 proto_major: 1 @@ -307,7 +595,7 @@ interactions: Content-Type: - application/json User-Agent: - - Go-Auth0-SDK/0.11.0 + - Go-Auth0/1.0.2 url: https://terraform-provider-auth0-dev.eu.auth0.com/api/v2/prompts/login/custom-text/en method: GET response: @@ -324,4 +612,4 @@ interactions: - application/json; charset=utf-8 status: 200 OK code: 200 - duration: 1ms + duration: 68.379458ms