From 2c85896558dee2bb338ff79046b88cb8755b6547 Mon Sep 17 00:00:00 2001 From: Jakub Michalak Date: Thu, 27 Jun 2024 10:36:07 +0200 Subject: [PATCH 1/6] Add oauth integration for custom clients --- docs/index.md | 1 + docs/resources/oauth_integration.md | 2 +- .../oauth_integration_for_custom_clients.md | 289 ++++++++ ...th_integration_for_partner_applications.md | 41 ++ examples/additional/deprecated_resources.MD | 1 + pkg/acceptance/helpers/random/certs.go | 12 +- .../helpers/security_integration_client.go | 8 + pkg/acceptance/importchecks/import_checks.go | 17 + pkg/provider/provider.go | 128 ++-- pkg/resources/common.go | 76 ++ pkg/resources/custom_diffs.go | 16 + pkg/resources/oauth_integration.go | 9 +- .../oauth_integration_for_custom_clients.go | 670 ++++++++++++++++++ ...tion_for_custom_clients_acceptance_test.go | 667 +++++++++++++++++ ...th_integration_for_partner_applications.go | 418 +++++++++++ ...or_partner_applications_acceptance_test.go | 231 ++++++ .../basic/test.tf | 6 + .../basic/variables.tf | 12 + .../complete/test.tf | 21 + .../complete/variables.tf | 46 ++ .../default_values/test.tf | 18 + .../default_values/variables.tf | 45 ++ .../invalid/test.tf | 7 + .../invalid/variables.tf | 15 + .../basic/test.tf | 4 + .../basic/variables.tf | 6 + .../complete/test.tf | 11 + .../complete/variables.tf | 28 + .../complete_looker/test.tf | 11 + .../complete_looker/variables.tf | 28 + .../complete_tableau_server copy/test.tf | 11 + .../complete_tableau_server copy/variables.tf | 28 + .../complete_tableau_server/test.tf | 10 + .../complete_tableau_server/variables.tf | 25 + .../invalid/test.tf | 3 + .../invalid/variables.tf | 3 + .../oauth_integration_for_custom_clients.go | 69 ++ pkg/sdk/security_integrations_def.go | 165 ++++- ...urity_integrations_gen_integration_test.go | 4 +- v1-preparations/REMAINING_GA_OBJECTS.MD | 2 +- 40 files changed, 3079 insertions(+), 85 deletions(-) create mode 100644 docs/resources/oauth_integration_for_custom_clients.md create mode 100644 docs/resources/oauth_integration_for_partner_applications.md create mode 100644 pkg/resources/oauth_integration_for_custom_clients.go create mode 100644 pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go create mode 100644 pkg/resources/oauth_integration_for_partner_applications.go create mode 100644 pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf create mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf create mode 100644 pkg/schemas/oauth_integration_for_custom_clients.go diff --git a/docs/index.md b/docs/index.md index a2e8c0ec88..78963d2a08 100644 --- a/docs/index.md +++ b/docs/index.md @@ -230,6 +230,7 @@ The Snowflake provider will use the following order of precedence when determini ## Currently deprecated resources - [snowflake_database_old](./docs/resources/database_old) +- [snowflake_oauth_integration](./docs/resources/oauth_integration) - [snowflake_unsafe_execute](./docs/resources/unsafe_execute) ## Currently deprecated datasources diff --git a/docs/resources/oauth_integration.md b/docs/resources/oauth_integration.md index d68a1f3a51..bda0f33d36 100644 --- a/docs/resources/oauth_integration.md +++ b/docs/resources/oauth_integration.md @@ -7,7 +7,7 @@ description: |- # snowflake_oauth_integration (Resource) - +~> **Deprecation** This resource is deprecated and will be removed in a future major version release. Please use snowflake_oauth_integration_for_custom_clients or snowflake_oauth_integration_for_partner_applications instead. ## Example Usage diff --git a/docs/resources/oauth_integration_for_custom_clients.md b/docs/resources/oauth_integration_for_custom_clients.md new file mode 100644 index 0000000000..2c4c6d0f7c --- /dev/null +++ b/docs/resources/oauth_integration_for_custom_clients.md @@ -0,0 +1,289 @@ +--- +page_title: "snowflake_oauth_integration_for_custom_clients Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_oauth_integration_for_custom_clients (Resource) + + + + + + +## Schema + +### Required + +- `blocked_roles_list` (Set of String) Comma-separated list of Snowflake roles that a user cannot explicitly consent to using after authenticating. +- `name` (String) Specifies the name of the OAuth integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account. +- `oauth_client_type` (String) Specifies the type of client being registered. Snowflake supports both confidential and public clients. Valid options are: [PUBLIC CONFIDENTIAL] +- `oauth_redirect_uri` (String) Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. + +### Optional + +- `comment` (String) Specifies a comment for the OAuth integration. +- `enabled` (String) Specifies whether this OAuth integration is enabled or disabled. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. +- `network_policy` (String) Specifies an existing network policy. This network policy controls network traffic that is attempting to exchange an authorization code for an access or refresh token or to use a refresh token to obtain a new access token. +- `oauth_allow_non_tls_redirect_uri` (String) If true, allows setting oauth_redirect_uri to a URI not protected by TLS. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. +- `oauth_client_rsa_public_key` (String) Hash of `oauth_client_rsa_public_key` returned from Snowflake. +- `oauth_client_rsa_public_key_2` (String) Hash of `oauth_client_rsa_public_key` returned from Snowflake. +- `oauth_enforce_pkce` (String) Boolean that specifies whether Proof Key for Code Exchange (PKCE) should be required for the integration. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. +- `oauth_issue_refresh_tokens` (String) Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. +- `oauth_refresh_token_validity` (Number) Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE. +- `oauth_use_secondary_roles` (String) Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. Valid options are: [IMPLICIT NONE] +- `pre_authorized_roles_list` (Set of String) Comma-separated list of Snowflake roles that a user does not need to explicitly consent to using after authenticating. + +### Read-Only + +- `describe_output` (List of Object) Outputs the result of `DESCRIBE SECURITY INTEGRATION` for the given integration. (see [below for nested schema](#nestedatt--describe_output)) +- `id` (String) The ID of this resource. +- `show_output` (List of Object) Outputs the result of `SHOW SECURITY INTEGRATION` for the given integration. (see [below for nested schema](#nestedatt--show_output)) + + +### Nested Schema for `describe_output` + +Read-Only: + +- `blocked_roles_list` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--blocked_roles_list)) +- `comment` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--comment)) +- `enabled` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--enabled)) +- `network_policy` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--network_policy)) +- `oauth_allow_non_tls_redirect_uri` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_allow_non_tls_redirect_uri)) +- `oauth_allowed_authorization_endpoints` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_allowed_authorization_endpoints)) +- `oauth_allowed_token_endpoints` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_allowed_token_endpoints)) +- `oauth_authorization_endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_authorization_endpoint)) +- `oauth_client_id` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_client_id)) +- `oauth_client_rsa_public_key_2_fp` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_client_rsa_public_key_2_fp)) +- `oauth_client_rsa_public_key_fp` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_client_rsa_public_key_fp)) +- `oauth_client_type` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_client_type)) +- `oauth_enforce_pkce` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_enforce_pkce)) +- `oauth_issue_refresh_tokens` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_issue_refresh_tokens)) +- `oauth_redirect_uri` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_redirect_uri)) +- `oauth_refresh_token_validity` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_refresh_token_validity)) +- `oauth_token_endpoint` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_token_endpoint)) +- `oauth_use_secondary_roles` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--oauth_use_secondary_roles)) +- `pre_authorized_roles_list` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--pre_authorized_roles_list)) + + +### Nested Schema for `describe_output.blocked_roles_list` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.comment` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.enabled` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.network_policy` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_allow_non_tls_redirect_uri` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_allowed_authorization_endpoints` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_allowed_token_endpoints` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_authorization_endpoint` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_client_id` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_client_rsa_public_key_2_fp` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_client_rsa_public_key_fp` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_client_type` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_enforce_pkce` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_issue_refresh_tokens` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_redirect_uri` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_refresh_token_validity` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_token_endpoint` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.oauth_use_secondary_roles` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.pre_authorized_roles_list` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + + +### Nested Schema for `show_output` + +Read-Only: + +- `category` (String) +- `comment` (String) +- `created_on` (String) +- `enabled` (Boolean) +- `integration_type` (String) +- `name` (String) diff --git a/docs/resources/oauth_integration_for_partner_applications.md b/docs/resources/oauth_integration_for_partner_applications.md new file mode 100644 index 0000000000..854ea05cd6 --- /dev/null +++ b/docs/resources/oauth_integration_for_partner_applications.md @@ -0,0 +1,41 @@ +--- +page_title: "snowflake_oauth_integration_for_partner_applications Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_oauth_integration_for_partner_applications (Resource) + + + + + + +## Schema + +### Required + +- `name` (String) Specifies the name of the SAML2 integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account. +- `oauth_client` (String) Creates an OAuth interface between Snowflake and a partner application. Valid options are: [LOOKER TABLEAU_DESKTOP TABLEAU_SERVER] + +### Optional + +- `blocked_roles_list` (Set of String) List of roles that a user cannot explicitly consent to using after authenticating. Do not include ACCOUNTADMIN, ORGADMIN or SECURITYADMIN as they are already implicitly enforced and will cause in-place updates. +- `comment` (String) Specifies a comment for the OAuth integration. +- `enabled` (Boolean) Specifies whether this OAuth integration is enabled or disabled. +- `oauth_issue_refresh_tokens` (String) Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired. +- `oauth_redirect_uri` (String) Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. +- `oauth_refresh_token_validity` (Number) Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE. +- `oauth_use_secondary_roles` (String) Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. + +### Read-Only + +- `created_on` (String) Date and time when the OAuth integration was created. +- `id` (String) The ID of this resource. +- `oauth_add_privileged_roles_to_blocked_list` (Boolean) +- `oauth_allowed_authorization_endpoints` (Set of String) A list of allowed authorization endpoints for oauth. +- `oauth_allowed_token_endpoints` (Set of String) A list of allowed token endpoints for oauth. +- `oauth_authorization_endpoint` (String) Authorization endpoint for oauth. +- `oauth_client_id` (String) Oauth client ID. +- `oauth_token_endpoint` (String) Token endpoint for oauth. diff --git a/examples/additional/deprecated_resources.MD b/examples/additional/deprecated_resources.MD index 979b77f0fd..db6b1421d0 100644 --- a/examples/additional/deprecated_resources.MD +++ b/examples/additional/deprecated_resources.MD @@ -1,4 +1,5 @@ ## Currently deprecated resources - [snowflake_database_old](./docs/resources/database_old) +- [snowflake_oauth_integration](./docs/resources/oauth_integration) - [snowflake_unsafe_execute](./docs/resources/unsafe_execute) diff --git a/pkg/acceptance/helpers/random/certs.go b/pkg/acceptance/helpers/random/certs.go index 80a53a83eb..770c49983c 100644 --- a/pkg/acceptance/helpers/random/certs.go +++ b/pkg/acceptance/helpers/random/certs.go @@ -4,8 +4,10 @@ import ( "bytes" "crypto/rand" "crypto/rsa" + "crypto/sha256" "crypto/x509" "crypto/x509/pkix" + "encoding/base64" "encoding/pem" "fmt" "math/big" @@ -39,7 +41,7 @@ func GenerateX509(t *testing.T) string { } // GenerateRSA returns an RSA public key without BEGIN and END markers. -func GenerateRSAPublicKey(t *testing.T) string { +func GenerateRSAPublicKey(t *testing.T) (string, string) { t.Helper() key, err := rsa.GenerateKey(rand.Reader, 2048) require.NoError(t, err) @@ -47,7 +49,13 @@ func GenerateRSAPublicKey(t *testing.T) string { pub := key.Public() b, err := x509.MarshalPKIXPublicKey(pub.(*rsa.PublicKey)) require.NoError(t, err) - return encode(t, "RSA PUBLIC KEY", b) + return encode(t, "RSA PUBLIC KEY", b), fmt.Sprintf("SHA256:%s", hash(t, b)) +} + +func hash(t *testing.T, b []byte) string { + t.Helper() + hash := sha256.Sum256(b) + return base64.StdEncoding.EncodeToString(hash[:]) } func encode(t *testing.T, pemType string, b []byte) string { diff --git a/pkg/acceptance/helpers/security_integration_client.go b/pkg/acceptance/helpers/security_integration_client.go index 08d854fbb4..6152800446 100644 --- a/pkg/acceptance/helpers/security_integration_client.go +++ b/pkg/acceptance/helpers/security_integration_client.go @@ -61,6 +61,14 @@ func (c *SecurityIntegrationClient) CreateScimWithRequest(t *testing.T, request return si, c.DropSecurityIntegrationFunc(t, request.GetName()) } +func (c *SecurityIntegrationClient) UpdateOauthForClients(t *testing.T, request *sdk.AlterOauthForCustomClientsSecurityIntegrationRequest) { + t.Helper() + ctx := context.Background() + + err := c.client().AlterOauthForCustomClients(ctx, request) + require.NoError(t, err) +} + func (c *SecurityIntegrationClient) DropSecurityIntegrationFunc(t *testing.T, id sdk.AccountObjectIdentifier) func() { t.Helper() ctx := context.Background() diff --git a/pkg/acceptance/importchecks/import_checks.go b/pkg/acceptance/importchecks/import_checks.go index 68b3721485..ade9f7f2d8 100644 --- a/pkg/acceptance/importchecks/import_checks.go +++ b/pkg/acceptance/importchecks/import_checks.go @@ -1,12 +1,29 @@ package importchecks import ( + "errors" "fmt" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) +// ComposeAggregateImportStateCheck does the same as ComposeImportStateCheck, but it aggregates all the occurred errors, +// instead of returning the first encountered one. +func ComposeAggregateImportStateCheck(fs ...resource.ImportStateCheckFunc) resource.ImportStateCheckFunc { + return func(s []*terraform.InstanceState) error { + var result []error + + for i, f := range fs { + if err := f(s); err != nil { + result = append(result, fmt.Errorf("check %d/%d error: %w", i+1, len(fs), err)) + } + } + + return errors.Join(result...) + } +} + // ComposeImportStateCheck is based on unexported composeImportStateCheck from teststep_providers_test.go func ComposeImportStateCheck(fs ...resource.ImportStateCheckFunc) resource.ImportStateCheckFunc { return func(s []*terraform.InstanceState) error { diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index c3b04d55d9..c063e8cd37 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -421,69 +421,71 @@ func Provider() *schema.Provider { func getResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "snowflake_account": resources.Account(), - "snowflake_account_password_policy_attachment": resources.AccountPasswordPolicyAttachment(), - "snowflake_account_parameter": resources.AccountParameter(), - "snowflake_alert": resources.Alert(), - "snowflake_api_integration": resources.APIIntegration(), - "snowflake_cortex_search_service": resources.CortexSearchService(), - "snowflake_database_old": resources.DatabaseOld(), - "snowflake_database": resources.Database(), - "snowflake_database_role": resources.DatabaseRole(), - "snowflake_dynamic_table": resources.DynamicTable(), - "snowflake_email_notification_integration": resources.EmailNotificationIntegration(), - "snowflake_external_function": resources.ExternalFunction(), - "snowflake_external_oauth_integration": resources.ExternalOauthIntegration(), - "snowflake_external_table": resources.ExternalTable(), - "snowflake_failover_group": resources.FailoverGroup(), - "snowflake_file_format": resources.FileFormat(), - "snowflake_function": resources.Function(), - "snowflake_grant_account_role": resources.GrantAccountRole(), - "snowflake_grant_application_role": resources.GrantApplicationRole(), - "snowflake_grant_database_role": resources.GrantDatabaseRole(), - "snowflake_grant_ownership": resources.GrantOwnership(), - "snowflake_grant_privileges_to_account_role": resources.GrantPrivilegesToAccountRole(), - "snowflake_grant_privileges_to_database_role": resources.GrantPrivilegesToDatabaseRole(), - "snowflake_grant_privileges_to_share": resources.GrantPrivilegesToShare(), - "snowflake_managed_account": resources.ManagedAccount(), - "snowflake_masking_policy": resources.MaskingPolicy(), - "snowflake_materialized_view": resources.MaterializedView(), - "snowflake_network_policy": resources.NetworkPolicy(), - "snowflake_network_policy_attachment": resources.NetworkPolicyAttachment(), - "snowflake_network_rule": resources.NetworkRule(), - "snowflake_notification_integration": resources.NotificationIntegration(), - "snowflake_oauth_integration": resources.OAuthIntegration(), - "snowflake_object_parameter": resources.ObjectParameter(), - "snowflake_password_policy": resources.PasswordPolicy(), - "snowflake_pipe": resources.Pipe(), - "snowflake_procedure": resources.Procedure(), - "snowflake_resource_monitor": resources.ResourceMonitor(), - "snowflake_role": resources.Role(), - "snowflake_row_access_policy": resources.RowAccessPolicy(), - "snowflake_saml_integration": resources.SAMLIntegration(), - "snowflake_schema": resources.Schema(), - "snowflake_scim_integration": resources.SCIMIntegration(), - "snowflake_secondary_database": resources.SecondaryDatabase(), - "snowflake_sequence": resources.Sequence(), - "snowflake_session_parameter": resources.SessionParameter(), - "snowflake_share": resources.Share(), - "snowflake_shared_database": resources.SharedDatabase(), - "snowflake_stage": resources.Stage(), - "snowflake_storage_integration": resources.StorageIntegration(), - "snowflake_stream": resources.Stream(), - "snowflake_table": resources.Table(), - "snowflake_table_column_masking_policy_application": resources.TableColumnMaskingPolicyApplication(), - "snowflake_table_constraint": resources.TableConstraint(), - "snowflake_tag": resources.Tag(), - "snowflake_tag_association": resources.TagAssociation(), - "snowflake_tag_masking_policy_association": resources.TagMaskingPolicyAssociation(), - "snowflake_task": resources.Task(), - "snowflake_unsafe_execute": resources.UnsafeExecute(), - "snowflake_user": resources.User(), - "snowflake_user_password_policy_attachment": resources.UserPasswordPolicyAttachment(), - "snowflake_user_public_keys": resources.UserPublicKeys(), - "snowflake_view": resources.View(), - "snowflake_warehouse": resources.Warehouse(), + "snowflake_account": resources.Account(), + "snowflake_account_password_policy_attachment": resources.AccountPasswordPolicyAttachment(), + "snowflake_account_parameter": resources.AccountParameter(), + "snowflake_alert": resources.Alert(), + "snowflake_api_integration": resources.APIIntegration(), + "snowflake_cortex_search_service": resources.CortexSearchService(), + "snowflake_database_old": resources.DatabaseOld(), + "snowflake_database": resources.Database(), + "snowflake_database_role": resources.DatabaseRole(), + "snowflake_dynamic_table": resources.DynamicTable(), + "snowflake_email_notification_integration": resources.EmailNotificationIntegration(), + "snowflake_external_function": resources.ExternalFunction(), + "snowflake_external_oauth_integration": resources.ExternalOauthIntegration(), + "snowflake_external_table": resources.ExternalTable(), + "snowflake_failover_group": resources.FailoverGroup(), + "snowflake_file_format": resources.FileFormat(), + "snowflake_function": resources.Function(), + "snowflake_grant_account_role": resources.GrantAccountRole(), + "snowflake_grant_application_role": resources.GrantApplicationRole(), + "snowflake_grant_database_role": resources.GrantDatabaseRole(), + "snowflake_grant_ownership": resources.GrantOwnership(), + "snowflake_grant_privileges_to_account_role": resources.GrantPrivilegesToAccountRole(), + "snowflake_grant_privileges_to_database_role": resources.GrantPrivilegesToDatabaseRole(), + "snowflake_grant_privileges_to_share": resources.GrantPrivilegesToShare(), + "snowflake_managed_account": resources.ManagedAccount(), + "snowflake_masking_policy": resources.MaskingPolicy(), + "snowflake_materialized_view": resources.MaterializedView(), + "snowflake_network_policy": resources.NetworkPolicy(), + "snowflake_network_policy_attachment": resources.NetworkPolicyAttachment(), + "snowflake_network_rule": resources.NetworkRule(), + "snowflake_notification_integration": resources.NotificationIntegration(), + "snowflake_oauth_integration": resources.OAuthIntegration(), + "snowflake_oauth_integration_for_custom_clients": resources.OauthIntegrationForCustomClients(), + "snowflake_oauth_integration_for_partner_applications": resources.OauthIntegrationForPartnerApplications(), + "snowflake_object_parameter": resources.ObjectParameter(), + "snowflake_password_policy": resources.PasswordPolicy(), + "snowflake_pipe": resources.Pipe(), + "snowflake_procedure": resources.Procedure(), + "snowflake_resource_monitor": resources.ResourceMonitor(), + "snowflake_role": resources.Role(), + "snowflake_row_access_policy": resources.RowAccessPolicy(), + "snowflake_saml_integration": resources.SAMLIntegration(), + "snowflake_schema": resources.Schema(), + "snowflake_scim_integration": resources.SCIMIntegration(), + "snowflake_secondary_database": resources.SecondaryDatabase(), + "snowflake_sequence": resources.Sequence(), + "snowflake_session_parameter": resources.SessionParameter(), + "snowflake_share": resources.Share(), + "snowflake_shared_database": resources.SharedDatabase(), + "snowflake_stage": resources.Stage(), + "snowflake_storage_integration": resources.StorageIntegration(), + "snowflake_stream": resources.Stream(), + "snowflake_table": resources.Table(), + "snowflake_table_column_masking_policy_application": resources.TableColumnMaskingPolicyApplication(), + "snowflake_table_constraint": resources.TableConstraint(), + "snowflake_tag": resources.Tag(), + "snowflake_tag_association": resources.TagAssociation(), + "snowflake_tag_masking_policy_association": resources.TagMaskingPolicyAssociation(), + "snowflake_task": resources.Task(), + "snowflake_unsafe_execute": resources.UnsafeExecute(), + "snowflake_user": resources.User(), + "snowflake_user_password_policy_attachment": resources.UserPasswordPolicyAttachment(), + "snowflake_user_public_keys": resources.UserPublicKeys(), + "snowflake_view": resources.View(), + "snowflake_warehouse": resources.Warehouse(), } } diff --git a/pkg/resources/common.go b/pkg/resources/common.go index 3f6380258d..3797a9fb06 100644 --- a/pkg/resources/common.go +++ b/pkg/resources/common.go @@ -1,9 +1,19 @@ package resources import ( + "context" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -52,3 +62,69 @@ func suppressQuoting(_, oldValue, newValue string, _ *schema.ResourceData) bool return oldWithoutQuotes == newWithoutQuotes } } + +func DeleteContextSecurityIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + client := meta.(*provider.Context).Client + + err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(sdk.NewAccountObjectIdentifier(id.Name())).WithIfExists(true)) + if err != nil { + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Error deleting integration", + Detail: fmt.Sprintf("id %v err = %v", id.Name(), err), + }, + } + } + + d.SetId("") + return nil +} + +func RSAKeyHash(key string) (string, error) { + keyBytes := []byte(fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", key)) + + block, _ := pem.Decode(keyBytes) + if block == nil || block.Type != "PUBLIC KEY" { + return "", errors.New("Failed to decode PEM block containing public key") + } + + pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return "", fmt.Errorf("Unable to parse public key: %w", err) + } + + pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey) + if err != nil { + return "", fmt.Errorf("Unable to marshal public key: %w", err) + } + + hash := sha256.Sum256(pubKeyBytes) + return fmt.Sprintf("SHA256:%s", base64.StdEncoding.EncodeToString(hash[:])), nil +} + +func getParameterInAccount(ctx context.Context, client *sdk.Client, param string) (string, error) { + params, err := client.Parameters.ShowParameters(ctx, &sdk.ShowParametersOptions{ + Like: &sdk.Like{ + Pattern: sdk.Pointer(param), + }, + In: &sdk.ParametersIn{ + Account: sdk.Pointer(true), + }, + }) + if err != nil { + return "", err + } + var found *sdk.Parameter + for _, v := range params { + if v.Key == param { + found = v + break + } + } + if found == nil { + return "", fmt.Errorf("parameter %s not found", param) + } + return found.Value, nil +} diff --git a/pkg/resources/custom_diffs.go b/pkg/resources/custom_diffs.go index 72183af3e5..86a4d04c6e 100644 --- a/pkg/resources/custom_diffs.go +++ b/pkg/resources/custom_diffs.go @@ -58,6 +58,22 @@ func ParameterValueComputedIf(key string, parameters []*sdk.Parameter, objectPar } } +// ForceNewIfChangeToEmptySet sets a ForceNew for a set field which was set to an empty value. +func ForceNewIfChangeToEmptySet[T any](key string) schema.CustomizeDiffFunc { + return customdiff.ForceNewIfChange(key, func(ctx context.Context, oldValue, newValue, meta any) bool { + oldList, newList := oldValue.(*schema.Set).List(), newValue.(*schema.Set).List() + return len(oldList) > 0 && len(newList) == 0 + }) +} + +// ForceNewIfChangeToEmptyString sets a ForceNew for a string field which was set to an empty value. +func ForceNewIfChangeToEmptyString(key string) schema.CustomizeDiffFunc { + return customdiff.ForceNewIfChange(key, func(ctx context.Context, oldValue, newValue, meta any) bool { + oldString, newString := oldValue.(string), newValue.(string) + return len(oldString) > 0 && len(newString) == 0 + }) +} + // TODO [follow-up PR]: test func ComputedIfAnyAttributeChanged(key string, changedAttributeKeys ...string) schema.CustomizeDiffFunc { return customdiff.ComputedIf(key, func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool { diff --git a/pkg/resources/oauth_integration.go b/pkg/resources/oauth_integration.go index de2c745618..2d7e4152ae 100644 --- a/pkg/resources/oauth_integration.go +++ b/pkg/resources/oauth_integration.go @@ -86,10 +86,11 @@ var oauthIntegrationSchema = map[string]*schema.Schema{ // OAuthIntegration returns a pointer to the resource representing an OAuth integration. func OAuthIntegration() *schema.Resource { return &schema.Resource{ - Create: CreateOAuthIntegration, - Read: ReadOAuthIntegration, - Update: UpdateOAuthIntegration, - Delete: DeleteOAuthIntegration, + Create: CreateOAuthIntegration, + Read: ReadOAuthIntegration, + Update: UpdateOAuthIntegration, + Delete: DeleteOAuthIntegration, + DeprecationMessage: "This resource is deprecated and will be removed in a future major version release. Please use snowflake_oauth_integration_for_custom_clients or snowflake_oauth_integration_for_partner_applications instead.", Schema: oauthIntegrationSchema, Importer: &schema.ResourceImporter{ diff --git a/pkg/resources/oauth_integration_for_custom_clients.go b/pkg/resources/oauth_integration_for_custom_clients.go new file mode 100644 index 0000000000..39f393dc3a --- /dev/null +++ b/pkg/resources/oauth_integration_for_custom_clients.go @@ -0,0 +1,670 @@ +package resources + +import ( + "context" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + + "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" +) + +var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Specifies the name of the OAuth integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account.", + }, + "oauth_client_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: fmt.Sprintf("Specifies the type of client being registered. Snowflake supports both confidential and public clients. Valid options are: %v", sdk.AllOauthSecurityIntegrationClientTypes), + ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationClientTypes), true), + }, + "oauth_redirect_uri": { + Type: schema.TypeString, + Required: true, + Description: "Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI.", + }, + "enabled": { + Type: schema.TypeString, + Optional: true, + Default: BooleanDefault, + ValidateDiagFunc: validateBooleanString, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("enabled"), + Description: booleanStringFieldDescription("Specifies whether this OAuth integration is enabled or disabled."), + }, + "oauth_allow_non_tls_redirect_uri": { + Type: schema.TypeString, + Optional: true, + Default: BooleanDefault, + ValidateDiagFunc: validateBooleanString, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("oauth_allow_non_tls_redirect_uri"), + Description: booleanStringFieldDescription("If true, allows setting oauth_redirect_uri to a URI not protected by TLS."), + }, + "oauth_enforce_pkce": { + Type: schema.TypeString, + Optional: true, + Default: BooleanDefault, + ValidateDiagFunc: validateBooleanString, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("oauth_enforce_pkce"), + Description: booleanStringFieldDescription("Boolean that specifies whether Proof Key for Code Exchange (PKCE) should be required for the integration."), + }, + "oauth_use_secondary_roles": { + Type: schema.TypeString, + Optional: true, + Description: fmt.Sprintf("Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. Valid options are: %v", sdk.AllOauthSecurityIntegrationUseSecondaryRoles), + ValidateDiagFunc: StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationUseSecondaryRoles), true), + }, + "pre_authorized_roles_list": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: "Comma-separated list of Snowflake roles that a user does not need to explicitly consent to using after authenticating.", + }, + "blocked_roles_list": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + // TODO(SNOW-1517937): Check if can make optional + Required: true, + Description: "Comma-separated list of Snowflake roles that a user cannot explicitly consent to using after authenticating.", + }, + "oauth_issue_refresh_tokens": { + Type: schema.TypeString, + Optional: true, + Default: BooleanDefault, + ValidateDiagFunc: validateBooleanString, + DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("oauth_issue_refresh_tokens"), + Description: booleanStringFieldDescription("Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired."), + }, + "oauth_refresh_token_validity": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntAtLeast(0), + Description: "Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE.", + }, + "network_policy": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies an existing network policy. This network policy controls network traffic that is attempting to exchange an authorization " + + "code for an access or refresh token or to use a refresh token to obtain a new access token.", + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + DiffSuppressFunc: func(_, old, new string, d *schema.ResourceData) bool { + return sdk.NewAccountObjectIdentifierFromFullyQualifiedName(old) == sdk.NewAccountObjectIdentifierFromFullyQualifiedName(new) + }, + }, + "oauth_client_rsa_public_key": { + Type: schema.TypeString, + Optional: true, + Description: "Hash of `oauth_client_rsa_public_key` returned from Snowflake.", + }, + "oauth_client_rsa_public_key_2": { + Type: schema.TypeString, + Optional: true, + Description: "Hash of `oauth_client_rsa_public_key` returned from Snowflake.", + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the OAuth integration.", + }, + ShowOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `SHOW SECURITY INTEGRATION` for the given integration.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecurityIntegrationSchema, + }, + }, + DescribeOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `DESCRIBE SECURITY INTEGRATION` for the given integration.", + Elem: &schema.Resource{ + Schema: schemas.DescribeOauthIntegrationForCustomClients, + }, + }, +} + +func OauthIntegrationForCustomClients() *schema.Resource { + return &schema.Resource{ + Schema: oauthIntegrationForCustomClientsSchema, + + CreateContext: CreateContextOauthIntegrationForCustomClients, + ReadContext: ReadContextOauthIntegrationForCustomClients(true), + UpdateContext: UpdateContextOauthIntegrationForCustomClients, + DeleteContext: DeleteContextOauthIntegrationForCustomClients, + + CustomizeDiff: customdiff.All( + ComputedIfAnyAttributeChanged( + ShowOutputAttributeName, + "name", + "enabled", + "comment", + ), + ComputedIfAnyAttributeChanged( + DescribeOutputAttributeName, + "oauth_client_type", + "oauth_redirect_uri", + "enabled", + "oauth_allow_non_tls_redirect_uri", + "oauth_enforce_pkce", + "oauth_use_secondary_roles", + "pre_authorized_roles_list", + "blocked_roles_list", + "oauth_issue_refresh_tokens", + "oauth_refresh_token_validity", + "network_policy", + "oauth_client_rsa_public_key", + "oauth_client_rsa_public_key_2", + "comment", + ), + ), + + Importer: &schema.ResourceImporter{ + StateContext: ImportOauthForCustomClientsIntegration, + }, + } +} + +func ImportOauthForCustomClientsIntegration(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting oauth integration for custom clients import") + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + + integration, err := client.SecurityIntegrations.ShowByID(ctx, id) + if err != nil { + return nil, err + } + + integrationProperties, err := client.SecurityIntegrations.Describe(ctx, id) + if err != nil { + return nil, err + } + + if err = d.Set("enabled", strconv.FormatBool(integration.Enabled)); err != nil { + return nil, err + } + + if allowNonTlsRedirectUri, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_ALLOW_NON_TLS_REDIRECT_URI" + }); err == nil { + if err = d.Set("oauth_allow_non_tls_redirect_uri", allowNonTlsRedirectUri.Value); err != nil { + return nil, err + } + } + + if enforcePkce, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_ENFORCE_PKCE" + }); err == nil { + if err = d.Set("oauth_enforce_pkce", enforcePkce.Value); err != nil { + return nil, err + } + } + + if issueRefreshTokens, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_ISSUE_REFRESH_TOKENS" + }); err == nil { + if err = d.Set("oauth_issue_refresh_tokens", issueRefreshTokens.Value); err != nil { + return nil, err + } + } + + if refreshTokenValidity, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_REFRESH_TOKEN_VALIDITY" + }); err == nil { + refreshTokenValidityValue, err := strconv.ParseInt(refreshTokenValidity.Value, 10, 64) + if err != nil { + return nil, err + } + if err = d.Set("oauth_refresh_token_validity", refreshTokenValidityValue); err != nil { + return nil, err + } + } + + return []*schema.ResourceData{d}, nil +} + +func CreateContextOauthIntegrationForCustomClients(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + + id := sdk.NewAccountObjectIdentifier(d.Get("name").(string)) + oauthClientType, err := sdk.ToOauthSecurityIntegrationClientTypeOption(d.Get("oauth_client_type").(string)) + if err != nil { + return diag.FromErr(err) + } + req := sdk.NewCreateOauthForCustomClientsSecurityIntegrationRequest(id, oauthClientType, d.Get("oauth_redirect_uri").(string)) + + if v := d.Get("enabled").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + req.WithEnabled(parsedBool) + } + + if v := d.Get("oauth_allow_non_tls_redirect_uri").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + req.WithOauthAllowNonTlsRedirectUri(parsedBool) + } + + if v := d.Get("oauth_enforce_pkce").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + req.WithOauthEnforcePkce(parsedBool) + } + + if v := d.Get("oauth_use_secondary_roles").(string); len(v) > 0 { + oauthUseSecondaryRoles, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v) + if err != nil { + return diag.FromErr(err) + } + req.WithOauthUseSecondaryRoles(oauthUseSecondaryRoles) + } + + if v, ok := d.GetOk("pre_authorized_roles_list"); ok { + elems := expandStringList(v.(*schema.Set).List()) + preAuthorizedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) + for i := range elems { + preAuthorizedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) + } + req.WithPreAuthorizedRolesList(sdk.PreAuthorizedRolesListRequest{PreAuthorizedRolesList: preAuthorizedRoles}) + } + + if v, ok := d.GetOk("blocked_roles_list"); ok { + elems := expandStringList(v.(*schema.Set).List()) + blockedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) + for i := range elems { + blockedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) + } + req.WithBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) + } + + if v := d.Get("oauth_issue_refresh_tokens").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + req.WithOauthIssueRefreshTokens(parsedBool) + } + + if v := d.Get("oauth_refresh_token_validity").(int); v != -1 { + req.WithOauthRefreshTokenValidity(v) + } + + if v, ok := d.GetOk("network_policy"); ok { + req.WithNetworkPolicy(sdk.NewAccountObjectIdentifier(v.(string))) + } + + if v, ok := d.GetOk("oauth_client_rsa_public_key"); ok { + req.WithOauthClientRsaPublicKey(v.(string)) + } + + if v, ok := d.GetOk("oauth_client_rsa_public_key_2"); ok { + req.WithOauthClientRsaPublicKey2(v.(string)) + } + + if v, ok := d.GetOk("comment"); ok { + req.WithComment(v.(string)) + } + + if err := client.SecurityIntegrations.CreateOauthForCustomClients(ctx, req); err != nil { + return diag.FromErr(err) + } + + d.SetId(helpers.EncodeSnowflakeID(id)) + + return ReadContextOauthIntegrationForCustomClients(false)(ctx, d, meta) +} + +func ReadContextOauthIntegrationForCustomClients(withExternalChangesMarking bool) schema.ReadContextFunc { + return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + + integration, err := client.SecurityIntegrations.ShowByID(ctx, id) + if err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to query security integration. Marking the resource as removed.", + Detail: fmt.Sprintf("Security integration name: %s, Err: %s", id.FullyQualifiedName(), err), + }, + } + } + return diag.FromErr(err) + } + + integrationProperties, err := client.SecurityIntegrations.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + + if c := integration.Category; c != sdk.SecurityIntegrationCategory { + return diag.FromErr(fmt.Errorf("expected %v to be a %s integration, got %v", id, sdk.SecurityIntegrationCategory, c)) + } + + if err := d.Set("name", integration.Name); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("comment", integration.Comment); err != nil { + return diag.FromErr(err) + } + + oauthClientType, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_CLIENT_TYPE" + }) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to find oauth client type, err = %w", err)) + } + if err := d.Set("oauth_client_type", oauthClientType.Value); err != nil { + return diag.FromErr(err) + } + + oauthRedirectUri, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_REDIRECT_URI" + }) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to find oauth redirect uri, err = %w", err)) + } + if err := d.Set("oauth_redirect_uri", oauthRedirectUri.Value); err != nil { + return diag.FromErr(err) + } + + preAuthorizedRolesList, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "PRE_AUTHORIZED_ROLES_LIST" + }) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to find pre authorized roles list, err = %w", err)) + } + var preAuthorizedRoles []string + if len(preAuthorizedRolesList.Value) > 0 { + preAuthorizedRoles = strings.Split(preAuthorizedRolesList.Value, ",") + } + if err := d.Set("pre_authorized_roles_list", preAuthorizedRoles); err != nil { + return diag.FromErr(err) + } + + blockedRolesList, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "BLOCKED_ROLES_LIST" + }) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to find pre authorized roles list, err = %w", err)) + } + var blockedRoles []string + if len(blockedRolesList.Value) > 0 { + blockedRoles = strings.Split(blockedRolesList.Value, ",") + } + if err := d.Set("blocked_roles_list", blockedRoles); err != nil { + return diag.FromErr(err) + } + + networkPolicy, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "NETWORK_POLICY" + }) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to find network policy, err = %w", err)) + } + if err := d.Set("network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Value).Name()); err != nil { + return diag.FromErr(err) + } + + if withExternalChangesMarking { + if err = handleExternalChangesToObjectInShow(d, + showMapping{"enabled", "enabled", integration.Enabled, strconv.FormatBool(integration.Enabled), nil}, + ); err != nil { + return diag.FromErr(err) + } + + oauthAllowNonTlsRedirectUri, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_ALLOW_NON_TLS_REDIRECT_URI" + }) + if err != nil { + return diag.FromErr(err) + } + + oauthEnforcePkce, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_ENFORCE_PKCE" + }) + if err != nil { + return diag.FromErr(err) + } + + oauthUseSecondaryRoles, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_USE_SECONDARY_ROLES" + }) + if err != nil { + return diag.FromErr(err) + } + + oauthIssueRefreshTokens, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_ISSUE_REFRESH_TOKENS" + }) + if err != nil { + return diag.FromErr(err) + } + + oauthRefreshTokenValidity, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_REFRESH_TOKEN_VALIDITY" + }) + if err != nil { + return diag.FromErr(err) + } + + if err = handleExternalChangesToObjectInDescribe(d, + describeMapping{"oauth_allow_non_tls_redirect_uri", "oauth_allow_non_tls_redirect_uri", oauthAllowNonTlsRedirectUri.Value, oauthAllowNonTlsRedirectUri.Value, nil}, + describeMapping{"oauth_enforce_pkce", "oauth_enforce_pkce", oauthEnforcePkce.Value, oauthEnforcePkce.Value, nil}, + describeMapping{"oauth_use_secondary_roles", "oauth_use_secondary_roles", oauthUseSecondaryRoles.Value, oauthUseSecondaryRoles.Value, nil}, + describeMapping{"oauth_issue_refresh_tokens", "oauth_issue_refresh_tokens", oauthIssueRefreshTokens.Value, oauthIssueRefreshTokens.Value, nil}, + describeMapping{"oauth_refresh_token_validity", "oauth_refresh_token_validity", oauthRefreshTokenValidity.Value, oauthRefreshTokenValidity.Value, nil}, + ); err != nil { + return diag.FromErr(err) + } + } + + if err = setStateToValuesFromConfig(d, oauthIntegrationForCustomClientsSchema, []string{ + "enabled", + "oauth_allow_non_tls_redirect_uri", + "oauth_enforce_pkce", + "oauth_use_secondary_roles", + "oauth_issue_refresh_tokens", + "oauth_refresh_token_validity", + }); err != nil { + return diag.FromErr(err) + } + + if err = d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecurityIntegrationToSchema(integration)}); err != nil { + return diag.FromErr(err) + } + + if err = d.Set(DescribeOutputAttributeName, []map[string]any{schemas.DescribeOauthIntegrationForCustomClientsToSchema(integrationProperties)}); err != nil { + return diag.FromErr(err) + } + + return nil + } +} + +func UpdateContextOauthIntegrationForCustomClients(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + set, unset := sdk.NewOauthForCustomClientsIntegrationSetRequest(), sdk.NewOauthForCustomClientsIntegrationUnsetRequest() + + if d.HasChange("oauth_redirect_uri") { + set.WithOauthRedirectUri(d.Get("oauth_redirect_uri").(string)) + } + + if d.HasChange("enabled") { + if v := d.Get("enabled").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + set.WithEnabled(parsedBool) + } else { + unset.WithEnabled(true) + } + } + + if d.HasChange("oauth_allow_non_tls_redirect_uri") { + if v := d.Get("oauth_allow_non_tls_redirect_uri").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + set.WithOauthAllowNonTlsRedirectUri(parsedBool) + } else { + // No unset available for this field (setting Snowflake default) + set.WithOauthAllowNonTlsRedirectUri(false) + } + } + + if d.HasChange("oauth_enforce_pkce") { + if v := d.Get("oauth_enforce_pkce").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + set.WithOauthEnforcePkce(parsedBool) + } else { + // No unset available for this field (setting Snowflake default) + set.WithOauthEnforcePkce(false) + } + } + + if d.HasChange("oauth_use_secondary_roles") { + if v := d.Get("oauth_use_secondary_roles").(string); len(v) > 0 { + oauthUseSecondaryRoles, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v) + if err != nil { + return diag.FromErr(err) + } + set.WithOauthUseSecondaryRoles(oauthUseSecondaryRoles) + } else { + unset.WithOauthUseSecondaryRoles(true) + } + } + + if d.HasChange("pre_authorized_roles_list") { + elems := expandStringList(d.Get("pre_authorized_roles_list").(*schema.Set).List()) + preAuthorizedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) + for i := range elems { + preAuthorizedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) + } + set.WithPreAuthorizedRolesList(sdk.PreAuthorizedRolesListRequest{PreAuthorizedRolesList: preAuthorizedRoles}) + } + + if d.HasChange("blocked_roles_list") { + elems := expandStringList(d.Get("blocked_roles_list").(*schema.Set).List()) + blockedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) + for i := range elems { + blockedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) + } + set.WithBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) + } + + if d.HasChange("oauth_issue_refresh_tokens") { + if v := d.Get("oauth_issue_refresh_tokens").(string); v != BooleanDefault { + parsedBool, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + set.WithOauthIssueRefreshTokens(parsedBool) + } else { + // No unset available for this field (setting Snowflake default) + set.WithOauthIssueRefreshTokens(true) + } + } + + if d.HasChange("oauth_refresh_token_validity") { + if v := d.Get("oauth_refresh_token_validity").(int); v != -1 { + set.WithOauthRefreshTokenValidity(v) + } else { + // No unset available for this field (setting Snowflake default; 90 days in seconds) + set.WithOauthRefreshTokenValidity(7_776_000) + } + } + + if d.HasChange("network_policy") { + if v := d.Get("network_policy").(string); v != "" { + set.WithNetworkPolicy(sdk.NewAccountObjectIdentifier(v)) + } else { + unset.WithNetworkPolicy(true) + } + } + + if d.HasChange("oauth_client_rsa_public_key") { + if v := d.Get("oauth_client_rsa_public_key").(string); v != "" { + set.WithOauthClientRsaPublicKey(v) + } else { + unset.WithOauthClientRsaPublicKey(true) + } + } + + if d.HasChange("oauth_client_rsa_public_key_2") { + if v := d.Get("oauth_client_rsa_public_key_2").(string); v != "" { + set.WithOauthClientRsaPublicKey2(v) + } else { + unset.WithOauthClientRsaPublicKey2(true) + } + } + + if d.HasChange("comment") { + set.WithComment(d.Get("comment").(string)) + } + + if !reflect.DeepEqual(*set, sdk.OauthForCustomClientsIntegrationSetRequest{}) { + if err := client.SecurityIntegrations.AlterOauthForCustomClients(ctx, sdk.NewAlterOauthForCustomClientsSecurityIntegrationRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } + + if !reflect.DeepEqual(*unset, sdk.OauthForCustomClientsIntegrationUnsetRequest{}) { + if err := client.SecurityIntegrations.AlterOauthForCustomClients(ctx, sdk.NewAlterOauthForCustomClientsSecurityIntegrationRequest(id).WithUnset(*unset)); err != nil { + return diag.FromErr(err) + } + } + + return ReadContextOauthIntegrationForCustomClients(false)(ctx, d, meta) +} + +func DeleteContextOauthIntegrationForCustomClients(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + client := meta.(*provider.Context).Client + + err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(sdk.NewAccountObjectIdentifier(id.Name())).WithIfExists(true)) + if err != nil { + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Error deleting integration", + Detail: fmt.Sprintf("id %v err = %v", id.Name(), err), + }, + } + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go new file mode 100644 index 0000000000..c6b45057ad --- /dev/null +++ b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go @@ -0,0 +1,667 @@ +package resources_test + +import ( + "regexp" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { + networkPolicy, networkPolicyCleanup := acc.TestClient().NetworkPolicy.CreateNetworkPolicy(t) + t.Cleanup(networkPolicyCleanup) + + preAuthorizedRole, preauthorizedRoleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(preauthorizedRoleCleanup) + + blockedRole, blockedRoleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(blockedRoleCleanup) + + validUrl := "https://example.com" + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + key, _ := random.GenerateRSAPublicKey(t) + comment := random.Comment() + + m := func(complete bool) map[string]config.Variable { + c := map[string]config.Variable{ + "name": config.StringVariable(id.Name()), + "oauth_client_type": config.StringVariable(string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + "oauth_redirect_uri": config.StringVariable(validUrl), + "blocked_roles_list": config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN")), + } + if complete { + c["blocked_roles_list"] = config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN"), config.StringVariable(blockedRole.ID().Name())) + c["comment"] = config.StringVariable(comment) + c["enabled"] = config.BoolVariable(true) + c["network_policy"] = config.StringVariable(networkPolicy.Name) + c["oauth_allow_non_tls_redirect_uri"] = config.BoolVariable(true) + c["oauth_allowed_authorization_endpoints"] = config.SetVariable(config.StringVariable("http://allowed.com")) + c["oauth_allowed_token_endpoints"] = config.SetVariable(config.StringVariable("http://allowed.com")) + c["oauth_authorization_endpoint"] = config.StringVariable("http://auth.com") + c["oauth_client_rsa_public_key"] = config.StringVariable(key) + c["oauth_client_rsa_public_key_2"] = config.StringVariable(key) + c["oauth_enforce_pkce"] = config.BoolVariable(true) + c["oauth_issue_refresh_tokens"] = config.BoolVariable(true) + c["oauth_refresh_token_validity"] = config.IntegerVariable(86400) + c["oauth_token_endpoint"] = config.StringVariable("http://auth.com") + c["oauth_use_secondary_roles"] = config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)) + c["pre_authorized_roles_list"] = config.SetVariable(config.StringVariable(preAuthorizedRole.ID().Name())) + } + return c + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + // create with empty optionals + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/basic"), + ConfigVariables: m(false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", resources.BooleanDefault), + resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "0"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "2"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "-1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", ""), + resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key"), + resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", ""), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "7776000"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + // import - without optionals + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/basic"), + ConfigVariables: m(false), + ResourceName: "snowflake_oauth_integration_for_custom_clients.test", + ImportState: true, + ImportStateCheck: importchecks.ComposeAggregateImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_redirect_uri", validUrl), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "false"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_allow_non_tls_redirect_uri", "false"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_enforce_pkce", "false"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "pre_authorized_roles_list.#", "0"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "blocked_roles_list.#", "2"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "7776000"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", ""), + // oauth_client_rsa_public_key and oauth_client_rsa_public_key_2 cannot be imported, because they're not available in SHOW and DESC outputs. + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", ""), + ), + }, + // set optionals + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/complete"), + ConfigVariables: m(true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "3"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "86400"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", key), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", key), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", comment), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", comment), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", preAuthorizedRole.ID().Name()), + // Not asserted, because it also contains other default roles + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "86400"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", comment), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + // import - complete + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/complete"), + ConfigVariables: m(true), + ResourceName: "snowflake_oauth_integration_for_custom_clients.test", + ImportState: true, + ImportStateCheck: importchecks.ComposeAggregateImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_redirect_uri", validUrl), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_allow_non_tls_redirect_uri", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_enforce_pkce", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "pre_authorized_roles_list.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "blocked_roles_list.#", "3"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "86400"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + // oauth_client_rsa_public_key and oauth_client_rsa_public_key_2 cannot be imported, because they're not available in SHOW and DESC outputs. + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", comment), + ), + }, + // change externally + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/complete"), + ConfigVariables: m(true), + PreConfig: func() { + acc.TestClient().SecurityIntegration.UpdateOauthForClients(t, sdk.NewAlterOauthForCustomClientsSecurityIntegrationRequest(id).WithUnset( + *sdk.NewOauthForCustomClientsIntegrationUnsetRequest(). + WithEnabled(true). + WithNetworkPolicy(true). + WithOauthUseSecondaryRoles(true). + WithOauthClientRsaPublicKey(true). + WithOauthClientRsaPublicKey2(true), + )) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + planchecks.PrintPlanDetails("snowflake_saml2_integration.test", "enabled", "network_policy", "oauth_use_secondary_roles", "oauth_client_rsa_public_key", "oauth_client_rsa_public_key_2"), + + // Not showing any drift and change, but alter is run underneath to set the values back + // planchecks.ExpectDrift("snowflake_saml2_integration.test", "enabled", sdk.String("true"), sdk.String("false")), + // planchecks.ExpectDrift("snowflake_saml2_integration.test", "network_policy", sdk.String(sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), sdk.String("")), + // planchecks.ExpectDrift("snowflake_saml2_integration.test", "oauth_use_secondary_roles", sdk.String("true"), sdk.String("false")), + // planchecks.ExpectDrift("snowflake_saml2_integration.test", "oauth_client_rsa_public_key", sdk.String(key), sdk.String("")), + // planchecks.ExpectDrift("snowflake_saml2_integration.test", "oauth_client_rsa_public_key_2", sdk.String(key), sdk.String("")), + + // planchecks.ExpectChange("snowflake_saml2_integration.test", "enabled", tfjson.ActionUpdate, sdk.String("false"), sdk.String("true")), + // planchecks.ExpectChange("snowflake_saml2_integration.test", "network_policy", tfjson.ActionUpdate, sdk.String(""), sdk.String(sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name())), + // planchecks.ExpectChange("snowflake_saml2_integration.test", "oauth_use_secondary_roles", tfjson.ActionUpdate, sdk.String("false"), sdk.String("true")), + // planchecks.ExpectChange("snowflake_saml2_integration.test", "oauth_client_rsa_public_key", tfjson.ActionUpdate, sdk.String(""), sdk.String(key)), + // planchecks.ExpectChange("snowflake_saml2_integration.test", "oauth_client_rsa_public_key_2", tfjson.ActionUpdate, sdk.String(""), sdk.String(key)), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "3"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "86400"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", key), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", key), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", comment), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", comment), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", preAuthorizedRole.ID().Name()), + // Not asserted, because it also contains other default roles + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "86400"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + // Change not detected + // resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value"), + // resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", comment), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + // unset + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/basic"), + ConfigVariables: m(false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "0"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "2"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "-1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", ""), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "7776000"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + }, + }) +} + +func TestAcc_OauthIntegrationForCustomClients_Complete(t *testing.T) { + networkPolicy, networkPolicyCleanup := acc.TestClient().NetworkPolicy.CreateNetworkPolicy(t) + t.Cleanup(networkPolicyCleanup) + + preAuthorizedRole, preauthorizedRoleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(preauthorizedRoleCleanup) + + blockedRole, blockedRoleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(blockedRoleCleanup) + + validUrl := "https://example.com" + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + key, _ := random.GenerateRSAPublicKey(t) + comment := random.Comment() + + configVariables := config.Variables{ + "name": config.StringVariable(id.Name()), + "oauth_client_type": config.StringVariable(string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + "oauth_redirect_uri": config.StringVariable(validUrl), + "blocked_roles_list": config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN"), config.StringVariable(blockedRole.ID().Name())), + "comment": config.StringVariable(comment), + "enabled": config.BoolVariable(true), + "network_policy": config.StringVariable(networkPolicy.Name), + "oauth_allow_non_tls_redirect_uri": config.BoolVariable(true), + "oauth_allowed_authorization_endpoints": config.SetVariable(config.StringVariable("http://allowed.com")), + "oauth_allowed_token_endpoints": config.SetVariable(config.StringVariable("http://allowed.com")), + "oauth_authorization_endpoint": config.StringVariable("http://auth.com"), + "oauth_client_rsa_public_key": config.StringVariable(key), + "oauth_client_rsa_public_key_2": config.StringVariable(key), + "oauth_enforce_pkce": config.BoolVariable(true), + "oauth_issue_refresh_tokens": config.BoolVariable(true), + "oauth_refresh_token_validity": config.IntegerVariable(86400), + "oauth_token_endpoint": config.StringVariable("http://auth.com"), + "oauth_use_secondary_roles": config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + "pre_authorized_roles_list": config.SetVariable(config.StringVariable(preAuthorizedRole.ID().Name())), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/complete"), + ConfigVariables: configVariables, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "3"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "86400"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", key), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", key), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", comment), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", comment), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", preAuthorizedRole.ID().Name()), + // Not asserted, because it also contains other default roles + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "86400"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", comment), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/complete"), + ConfigVariables: configVariables, + ResourceName: "snowflake_oauth_integration_for_custom_clients.test", + ImportState: true, + ImportStateCheck: importchecks.ComposeAggregateImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_redirect_uri", validUrl), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_allow_non_tls_redirect_uri", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_enforce_pkce", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "pre_authorized_roles_list.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "blocked_roles_list.#", "3"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "86400"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework + // oauth_client_rsa_public_key and oauth_client_rsa_public_key_2 cannot be imported, because they're not available in SHOW and DESC outputs. + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", comment), + ), + }, + }, + }) +} + +func TestAcc_OauthIntegrationForCustomClients_DefaultValues(t *testing.T) { + validUrl := "https://example.com" + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + configVariables := func(defaultsSet bool) config.Variables { + variables := config.Variables{ + "name": config.StringVariable(id.Name()), + "oauth_client_type": config.StringVariable(string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + "oauth_redirect_uri": config.StringVariable(validUrl), + "blocked_roles_list": config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN")), + } + + variables["enabled"] = config.BoolVariable(false) + variables["oauth_allow_non_tls_redirect_uri"] = config.BoolVariable(false) + variables["oauth_enforce_pkce"] = config.BoolVariable(false) + variables["oauth_use_secondary_roles"] = config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)) + variables["pre_authorized_roles_list"] = config.SetVariable() + variables["oauth_issue_refresh_tokens"] = config.BoolVariable(false) + variables["oauth_refresh_token_validity"] = config.IntegerVariable(7776000) + variables["network_policy"] = config.StringVariable("") + variables["oauth_client_rsa_public_key"] = config.StringVariable("") + variables["oauth_client_rsa_public_key_2"] = config.StringVariable("") + variables["comment"] = config.StringVariable("") + + return variables + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/default_values"), + ConfigVariables: configVariables(true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "0"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "2"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "7776000"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", ""), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "7776000"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/basic"), + ConfigVariables: configVariables(false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_redirect_uri", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "0"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "2"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_issue_refresh_tokens", resources.BooleanDefault), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_refresh_token_validity", "-1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "network_policy", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "comment", ""), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.integration_type", "OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.enabled", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_redirect_uri.0.value", validUrl), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "7776000"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", ""), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_token_endpoint.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_authorization_endpoints.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allowed_token_endpoints.0.value"), + ), + }, + }, + }) +} + +func TestAcc_OauthIntegrationForCustomClients_InvalidOauthUseSecondaryRoles(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + configVariables := config.Variables{ + "name": config.StringVariable(id.Name()), + "oauth_client_type": config.StringVariable(string(sdk.OauthSecurityIntegrationClientTypeConfidential)), + "oauth_redirect_uri": config.StringVariable("https://example.com"), + "oauth_use_secondary_roles": config.StringVariable("invalid"), + "blocked_roles_list": config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN")), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/invalid"), + ConfigVariables: configVariables, + ExpectError: regexp.MustCompile(`Error: expected \[{{} oauth_use_secondary_roles}] to be one of \["IMPLICIT" "NONE"], got invalid`), + }, + }, + }) +} + +func TestAcc_OauthIntegrationForCustomClients_InvalidClientType(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + configVariables := config.Variables{ + "name": config.StringVariable(id.Name()), + "oauth_client_type": config.StringVariable("invalid"), + "oauth_redirect_uri": config.StringVariable("https://example.com"), + "oauth_use_secondary_roles": config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + "blocked_roles_list": config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN")), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/invalid"), + ConfigVariables: configVariables, + ExpectError: regexp.MustCompile(`Error: expected oauth_client_type to be one of \["PUBLIC" "CONFIDENTIAL"], got invalid`), + }, + }, + }) +} diff --git a/pkg/resources/oauth_integration_for_partner_applications.go b/pkg/resources/oauth_integration_for_partner_applications.go new file mode 100644 index 0000000000..9044b9836a --- /dev/null +++ b/pkg/resources/oauth_integration_for_partner_applications.go @@ -0,0 +1,418 @@ +package resources + +import ( + "context" + "fmt" + "log" + "reflect" + "strconv" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +var oauthIntegrationForPartnerApplicationsSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Specifies the name of the SAML2 integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account.", + }, + "oauth_client": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: fmt.Sprintf("Creates an OAuth interface between Snowflake and a partner application. Valid options are: %v", sdk.AllOauthSecurityIntegrationClients), + ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationClients), false), + DiffSuppressFunc: ignoreCaseAndTrimSpaceSuppressFunc, + }, + "oauth_redirect_uri": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI.", + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Description: "Specifies whether this OAuth integration is enabled or disabled.", + }, + "oauth_issue_refresh_tokens": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{"true", "false"}, true), + Default: "unknown", + Optional: true, + Description: "Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired.", + DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { + return oldValue == "true" && newValue == "unknown" + }, + }, + "oauth_refresh_token_validity": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + Description: "Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE.", + DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { + return d.Get(k).(int) == 7776000 && newValue == "0" + }, + }, + "oauth_use_secondary_roles": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies whether default secondary roles set in the user properties are activated by default in the session being opened.", + ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationUseSecondaryRoles), false), + DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { + return strings.EqualFold(oldValue, newValue) || d.Get(k).(string) == string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone) && newValue == "" + }, + }, + "blocked_roles_list": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Computed: true, + Description: "List of roles that a user cannot explicitly consent to using after authenticating. Do not include ACCOUNTADMIN, ORGADMIN or SECURITYADMIN as they are already implicitly enforced and will cause in-place updates.", + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + value := d.Get("oauth_add_privileged_roles_to_blocked_list").(bool) + if !value { + return old == new + } + return old == "ACCOUNTADMIN" || old == "SECURITYADMIN" + }, + }, + "oauth_add_privileged_roles_to_blocked_list": { + Type: schema.TypeBool, + Computed: true, + }, + "oauth_authorization_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: "Authorization endpoint for oauth.", + }, + "oauth_token_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: "Token endpoint for oauth.", + }, + "oauth_allowed_authorization_endpoints": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "A list of allowed authorization endpoints for oauth.", + }, + "oauth_allowed_token_endpoints": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + Description: "A list of allowed token endpoints for oauth.", + }, + "oauth_client_id": { + Type: schema.TypeString, + Computed: true, + Description: "Oauth client ID.", + }, + "comment": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a comment for the OAuth integration.", + }, + "created_on": { + Type: schema.TypeString, + Computed: true, + Description: "Date and time when the OAuth integration was created.", + }, +} + +func OauthIntegrationForPartnerApplications() *schema.Resource { + return &schema.Resource{ + CreateContext: CreateContextOauthIntegrationForPartnerApplications, + ReadContext: ReadContextOauthIntegrationForPartnerApplications, + UpdateContext: UpdateContextOauthIntegrationForPartnerApplications, + DeleteContext: DeleteContextSecurityIntegration, + Schema: oauthIntegrationForPartnerApplicationsSchema, + CustomizeDiff: customdiff.All( + // SuppressIfParameterSet("blocked_roles_list", "OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST"), + ), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func CreateContextOauthIntegrationForPartnerApplications(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + name := d.Get("name").(string) + oauthClientRaw := d.Get("oauth_client").(string) + id := sdk.NewAccountObjectIdentifier(name) + oauthClient, err := sdk.ToOauthSecurityIntegrationClientOption(oauthClientRaw) + if err != nil { + return diag.FromErr(err) + } + req := sdk.NewCreateOauthForPartnerApplicationsSecurityIntegrationRequest(id, oauthClient) + + if v, ok := d.GetOk("blocked_roles_list"); ok { + elems := expandStringList(v.(*schema.Set).List()) + blockedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) + for i := range elems { + blockedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) + } + req.WithBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) + } + + if v, ok := d.GetOk("comment"); ok { + req.WithComment(v.(string)) + } + + if v, ok := d.GetOk("enabled"); ok { + req.WithEnabled(v.(bool)) + } + if v := d.Get("oauth_issue_refresh_tokens").(string); v != "unknown" { + parsed, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + req.WithOauthIssueRefreshTokens(parsed) + } + + if v, ok := d.GetOk("oauth_redirect_uri"); ok { + req.WithOauthRedirectUri(v.(string)) + } + + if v, ok := d.GetOk("oauth_refresh_token_validity"); ok { + req.WithOauthRefreshTokenValidity(v.(int)) + } + + if v, ok := d.GetOk("oauth_use_secondary_roles"); ok { + valueRaw := v.(string) + value, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(valueRaw) + if err != nil { + return diag.FromErr(err) + } + req.WithOauthUseSecondaryRoles(value) + } + + if err := client.SecurityIntegrations.CreateOauthForPartnerApplications(ctx, req); err != nil { + return diag.FromErr(err) + } + + d.SetId(name) + + return ReadContextOauthIntegrationForPartnerApplications(ctx, d, meta) +} + +func ReadContextOauthIntegrationForPartnerApplications(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + + integration, err := client.SecurityIntegrations.ShowByID(ctx, id) + if err != nil { + log.Printf("[DEBUG] OauthIntegrationForPartnerApplications (%s) not found", d.Id()) + d.SetId("") + return diag.FromErr(err) + } + + if c := integration.Category; c != sdk.SecurityIntegrationCategory { + return diag.FromErr(fmt.Errorf("expected %v to be a %s integration, got %v", id, sdk.SecurityIntegrationCategory, c)) + } + if err := d.Set("name", integration.Name); err != nil { + return diag.FromErr(err) + } + if err := d.Set("comment", integration.Comment); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("created_on", integration.CreatedOn.String()); err != nil { + return diag.FromErr(err) + } + + if err := d.Set("enabled", integration.Enabled); err != nil { + return diag.FromErr(err) + } + oauthClient, err := integration.SubType() + if err != nil { + return diag.FromErr(err) + } + if err := d.Set("oauth_client", oauthClient); err != nil { + return diag.FromErr(err) + } + + properties, err := client.SecurityIntegrations.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } + defaults := make(map[string]string) + for _, property := range properties { + name := property.Name + value := property.Value + switch name { + case "BLOCKED_ROLES_LIST": + var blockedRoles []string + if len(value) > 0 { + blockedRoles = strings.Split(value, ",") + } + + if err := d.Set("blocked_roles_list", blockedRoles); err != nil { + return diag.FromErr(err) + } + case "COMMENT": + if err := d.Set("comment", value); err != nil { + return diag.FromErr(err) + } + case "CREATED_ON": + if err := d.Set("created_on", value); err != nil { + return diag.FromErr(err) + } + case "ENABLED": + if err := d.Set("enabled", helpers.StringToBool(value)); err != nil { + return diag.FromErr(err) + } + case "OAUTH_CLIENT": + if err := d.Set("oauth_client", value); err != nil { + return diag.FromErr(err) + } + case "OAUTH_ISSUE_REFRESH_TOKENS": + defaults["OAUTH_ISSUE_REFRESH_TOKENS"] = property.Default + if err := d.Set("oauth_issue_refresh_tokens", value); err != nil { + return diag.FromErr(err) + } + case "OAUTH_REDIRECT_URI": + if err := d.Set("oauth_redirect_uri", value); err != nil { + return diag.FromErr(err) + } + case "OAUTH_REFRESH_TOKEN_VALIDITY": + v, err := strconv.Atoi(value) + if err != nil { + return diag.FromErr(err) + } + defaults["OAUTH_REFRESH_TOKEN_VALIDITY"] = property.Default + if err := d.Set("oauth_refresh_token_validity", v); err != nil { + return diag.FromErr(err) + } + case "OAUTH_USE_SECONDARY_ROLES": + if err := d.Set("oauth_use_secondary_roles", value); err != nil { + return diag.FromErr(err) + } + case "OAUTH_AUTHORIZATION_ENDPOINT": + if err := d.Set("oauth_authorization_endpoint", value); err != nil { + return diag.FromErr(err) + } + case "OAUTH_TOKEN_ENDPOINT": + if err := d.Set("oauth_token_endpoint", value); err != nil { + return diag.FromErr(err) + } + case "OAUTH_ALLOWED_AUTHORIZATION_ENDPOINTS": + var elems []string + if len(value) > 0 { + elems = strings.Split(value, ",") + } + + if err := d.Set("oauth_allowed_authorization_endpoints", elems); err != nil { + return diag.FromErr(err) + } + case "OAUTH_ALLOWED_TOKEN_ENDPOINTS": + var elems []string + if len(value) > 0 { + elems = strings.Split(value, ",") + } + if err := d.Set("oauth_allowed_token_endpoints", elems); err != nil { + return diag.FromErr(err) + } + case "OAUTH_CLIENT_ID": + if err := d.Set("oauth_client_id", value); err != nil { + return diag.FromErr(err) + } + + default: + log.Printf("[WARN] unexpected property %v returned from Snowflake", name) + } + } + paramRaw, err := getParameterInAccount(ctx, client, "OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST") + if err != nil { + return nil + } + param := helpers.StringToBool(paramRaw) + if err := d.Set("oauth_add_privileged_roles_to_blocked_list", param); err != nil { + return diag.FromErr(err) + } + return nil +} + +func UpdateContextOauthIntegrationForPartnerApplications(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + set, unset := sdk.NewOauthForPartnerApplicationsIntegrationSetRequest(), sdk.NewOauthForPartnerApplicationsIntegrationUnsetRequest() + + if d.HasChange("blocked_roles_list") { + elems := expandStringList(d.Get("blocked_roles_list").(*schema.Set).List()) + blockedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) + for i := range elems { + blockedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) + } + set.WithBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) + } + + if d.HasChange("comment") { + set.WithComment(d.Get("comment").(string)) + } + + if d.HasChange("enabled") { + set.WithEnabled(d.Get("enabled").(bool)) + } + + if d.HasChange("oauth_issue_refresh_tokens") { + if v := d.Get("oauth_issue_refresh_tokens").(string); v != "unknown" { + parsed, err := strconv.ParseBool(v) + if err != nil { + return diag.FromErr(err) + } + set.WithOauthIssueRefreshTokens(parsed) + } else { + // TODO: fix + set.WithOauthIssueRefreshTokens(true) + } + } + + if d.HasChange("oauth_redirect_uri") { + set.WithOauthRedirectUri(d.Get("oauth_redirect_uri").(string)) + } + + if d.HasChange("oauth_refresh_token_validity") { + v := d.Get("oauth_refresh_token_validity").(int) + if v > 0 { + set.WithOauthRefreshTokenValidity(v) + } else { + // TODO: fix + // TODO: better logic, like in docs + set.WithOauthRefreshTokenValidity(7776000) + } + } + + if d.HasChange("oauth_use_secondary_roles") { + v := d.Get("oauth_use_secondary_roles").(string) + if len(v) > 0 { + value, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v) + if err != nil { + return diag.FromErr(err) + } + set.WithOauthUseSecondaryRoles(value) + } else { + unset.WithOauthUseSecondaryRoles(true) + } + } + if !reflect.DeepEqual(*set, sdk.OauthForPartnerApplicationsIntegrationSetRequest{}) { + if err := client.SecurityIntegrations.AlterOauthForPartnerApplications(ctx, sdk.NewAlterOauthForPartnerApplicationsSecurityIntegrationRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } + if !reflect.DeepEqual(*unset, sdk.OauthForPartnerApplicationsIntegrationUnsetRequest{}) { + if err := client.SecurityIntegrations.AlterOauthForPartnerApplications(ctx, sdk.NewAlterOauthForPartnerApplicationsSecurityIntegrationRequest(id).WithUnset(*unset)); err != nil { + return diag.FromErr(err) + } + } + return ReadContextOauthIntegrationForPartnerApplications(ctx, d, meta) +} diff --git a/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go b/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go new file mode 100644 index 0000000000..edf0bcd9de --- /dev/null +++ b/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go @@ -0,0 +1,231 @@ +package resources_test + +import ( + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestAcc_OauthIntegrationForPartnerApplications_basic(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role1, role1Cleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(role1Cleanup) + + m := func(oauthClient string, complete bool, redirectUri *string) map[string]config.Variable { + c := map[string]config.Variable{ + "name": config.StringVariable(id.Name()), + "oauth_client": config.StringVariable(oauthClient), + } + if complete { + c["blocked_roles_list"] = config.SetVariable(config.StringVariable(role1.ID().Name())) + c["comment"] = config.StringVariable("foo") + c["enabled"] = config.BoolVariable(true) + c["oauth_issue_refresh_tokens"] = config.StringVariable("false") + c["oauth_refresh_token_validity"] = config.IntegerVariable(12345) + c["oauth_use_secondary_roles"] = config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)) + } + if redirectUri != nil { + c["oauth_redirect_uri"] = config.StringVariable(*redirectUri) + } + return c + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/basic"), + ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), false, nil), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientTableauServer)), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_authorization_endpoints.#"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_token_endpoints.#"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_authorization_endpoint"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "enabled"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server"), + ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), true, nil), + Check: resource.ComposeTestCheckFunc( + // TODO: proper check + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.#", "1"), + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.0", role1.ID().Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "comment", "foo"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientTableauServer)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity", "12345"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server"), + ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), true, nil), + ResourceName: "snowflake_oauth_integration_for_partner_applications.test", + ImportState: true, + ImportStateVerify: true, + }, + // unset + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/basic"), + ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), false, nil), + Check: resource.ComposeTestCheckFunc( + // TODO: proper check + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.#", "0"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "comment", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "enabled", "false"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientTableauServer)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity", "7776000"), + + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), + ), + }, + // change client_type + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete_looker"), + ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientLooker), true, sdk.Pointer("https://example.com")), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientLooker)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_redirect_uri", "https://example.com"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), + ), + }, + }, + }) +} + +func TestAcc_OauthIntegrationForPartnerApplications_complete(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + validURL := "https://example.com" + role1, role1Cleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(role1Cleanup) + + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "blocked_roles_list": config.SetVariable(config.StringVariable(role1.ID().Name())), + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "name": config.StringVariable(id.Name()), + "oauth_client": config.StringVariable(string(sdk.OauthSecurityIntegrationClientLooker)), + "oauth_issue_refresh_tokens": config.BoolVariable(true), + "oauth_redirect_uri": config.StringVariable(validURL), + "oauth_refresh_token_validity": config.IntegerVariable(12345), + "oauth_use_secondary_roles": config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete"), + ConfigVariables: m(), + Check: resource.ComposeTestCheckFunc( + // TODO: proper assert, also assert OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.#", "3"), + // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.0", role1.ID().Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "comment", "foo"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientLooker)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens", "true"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_redirect_uri", validURL), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity", "12345"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_authorization_endpoints.#"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_token_endpoints.#"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_authorization_endpoint"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_token_endpoint"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_client_id"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete"), + ConfigVariables: m(), + ResourceName: "snowflake_oauth_integration_for_partner_applications.test", + ImportState: true, + ImportStateVerify: true, + // ignore because this field is not returned from snowflake + ImportStateVerifyIgnore: []string{"oauth_redirect_uri"}, + }, + }, + }) +} + +func TestAcc_OauthIntegrationForPartnerApplications_invalid(t *testing.T) { + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "blocked_roles_list": config.SetVariable(config.StringVariable("foo")), + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "name": config.StringVariable("foo"), + "oauth_client": config.StringVariable("invalid"), + "oauth_issue_refresh_tokens": config.BoolVariable(true), + "oauth_redirect_uri": config.StringVariable("foo"), + "oauth_refresh_token_validity": config.IntegerVariable(1), + "oauth_use_secondary_roles": config.StringVariable("invalid"), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete"), + ConfigVariables: m(), + }, + }, + }) +} + +func TestAcc_OauthIntegrationForPartnerApplications_InvalidIncomplete(t *testing.T) { + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable("foo"), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + ErrorCheck: helpers.AssertErrorContainsPartsFunc(t, []string{ + `The argument "oauth_client" is required, but no definition was found.`, + }), + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/invalid"), + ConfigVariables: m(), + }, + }, + }) +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/test.tf new file mode 100644 index 0000000000..83e0ae30b7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/test.tf @@ -0,0 +1,6 @@ +resource "snowflake_oauth_integration_for_custom_clients" "test" { + name = var.name + oauth_client_type = var.oauth_client_type + oauth_redirect_uri = var.oauth_redirect_uri + blocked_roles_list = var.blocked_roles_list +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/variables.tf new file mode 100644 index 0000000000..eacd431fd3 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/basic/variables.tf @@ -0,0 +1,12 @@ +variable "name" { + type = string +} +variable "oauth_client_type" { + type = string +} +variable "oauth_redirect_uri" { + type = string +} +variable "blocked_roles_list" { + type = set(string) +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf new file mode 100644 index 0000000000..626a09f295 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf @@ -0,0 +1,21 @@ +resource "snowflake_oauth_integration_for_custom_clients" "test" { + blocked_roles_list = var.blocked_roles_list + comment = var.comment + enabled = var.enabled + name = var.name + network_policy = var.network_policy + oauth_allow_non_tls_redirect_uri = var.oauth_allow_non_tls_redirect_uri + # oauth_client_rsa_public_key { + # value = var.oauth_client_rsa_public_key + # } + + oauth_client_rsa_public_key = var.oauth_client_rsa_public_key + oauth_client_rsa_public_key_2 = var.oauth_client_rsa_public_key_2 + oauth_client_type = var.oauth_client_type + oauth_enforce_pkce = var.oauth_enforce_pkce + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_redirect_uri = var.oauth_redirect_uri + oauth_refresh_token_validity = var.oauth_refresh_token_validity + oauth_use_secondary_roles = var.oauth_use_secondary_roles + pre_authorized_roles_list = var.pre_authorized_roles_list +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/variables.tf new file mode 100644 index 0000000000..9d98ea92c8 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/variables.tf @@ -0,0 +1,46 @@ + +variable "blocked_roles_list" { + type = set(string) +} +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "name" { + type = string +} +variable "network_policy" { + type = string +} +variable "oauth_allow_non_tls_redirect_uri" { + type = bool +} +variable "oauth_client_rsa_public_key" { + type = string +} +variable "oauth_client_rsa_public_key_2" { + type = string +} +variable "oauth_client_type" { + type = string +} +variable "oauth_enforce_pkce" { + type = bool +} +variable "oauth_issue_refresh_tokens" { + type = bool +} +variable "oauth_redirect_uri" { + type = string +} +variable "oauth_refresh_token_validity" { + type = number +} +variable "oauth_use_secondary_roles" { + type = string +} +variable "pre_authorized_roles_list" { + type = set(string) +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/test.tf new file mode 100644 index 0000000000..6cf1b66134 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/test.tf @@ -0,0 +1,18 @@ +resource "snowflake_oauth_integration_for_custom_clients" "test" { + name = var.name + oauth_client_type = var.oauth_client_type + oauth_redirect_uri = var.oauth_redirect_uri + blocked_roles_list = var.blocked_roles_list + + enabled = var.enabled + oauth_allow_non_tls_redirect_uri = var.oauth_allow_non_tls_redirect_uri + oauth_enforce_pkce = var.oauth_enforce_pkce + oauth_use_secondary_roles = var.oauth_use_secondary_roles + pre_authorized_roles_list = var.pre_authorized_roles_list + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_refresh_token_validity = var.oauth_refresh_token_validity + network_policy = var.network_policy + oauth_client_rsa_public_key = var.oauth_client_rsa_public_key + oauth_client_rsa_public_key_2 = var.oauth_client_rsa_public_key_2 + comment = var.comment +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/variables.tf new file mode 100644 index 0000000000..d2e2c543f2 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/default_values/variables.tf @@ -0,0 +1,45 @@ +variable "name" { + type = string +} +variable "oauth_client_type" { + type = string +} +variable "oauth_redirect_uri" { + type = string +} +variable "blocked_roles_list" { + type = set(string) +} +variable "enabled" { + type = bool +} +variable "oauth_allow_non_tls_redirect_uri" { + type = bool +} +variable "oauth_enforce_pkce" { + type = bool +} +variable "oauth_use_secondary_roles" { + type = string +} +variable "pre_authorized_roles_list" { + type = set(string) +} +variable "oauth_issue_refresh_tokens" { + type = bool +} +variable "oauth_refresh_token_validity" { + type = number +} +variable "network_policy" { + type = string +} +variable "oauth_client_rsa_public_key" { + type = string +} +variable "oauth_client_rsa_public_key_2" { + type = string +} +variable "comment" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/test.tf new file mode 100644 index 0000000000..f0166eb688 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/test.tf @@ -0,0 +1,7 @@ +resource "snowflake_oauth_integration_for_custom_clients" "test" { + name = var.name + oauth_client_type = var.oauth_client_type + oauth_redirect_uri = var.oauth_redirect_uri + oauth_use_secondary_roles = var.oauth_use_secondary_roles + blocked_roles_list = var.blocked_roles_list +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/variables.tf new file mode 100644 index 0000000000..71afeb8ae8 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/invalid/variables.tf @@ -0,0 +1,15 @@ +variable "name" { + type = string +} +variable "oauth_client_type" { + type = string +} +variable "oauth_redirect_uri" { + type = string +} +variable "oauth_use_secondary_roles" { + type = string +} +variable "blocked_roles_list" { + type = set(string) +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf new file mode 100644 index 0000000000..77aab112a1 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf @@ -0,0 +1,4 @@ +resource "snowflake_oauth_integration_for_partner_applications" "test" { + name = var.name + oauth_client = var.oauth_client +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf new file mode 100644 index 0000000000..bb7c71f314 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf @@ -0,0 +1,6 @@ +variable "name" { + type = string +} +variable "oauth_client" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf new file mode 100644 index 0000000000..87c2a43cb7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_oauth_integration_for_partner_applications" "test" { + blocked_roles_list = var.blocked_roles_list + comment = var.comment + enabled = var.enabled + name = var.name + oauth_client = var.oauth_client + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_redirect_uri = var.oauth_redirect_uri + oauth_refresh_token_validity = var.oauth_refresh_token_validity + oauth_use_secondary_roles = var.oauth_use_secondary_roles +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf new file mode 100644 index 0000000000..e16f09d94b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf @@ -0,0 +1,28 @@ + +variable "blocked_roles_list" { + type = set(string) +} +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "name" { + type = string +} +variable "oauth_client" { + type = string +} +variable "oauth_issue_refresh_tokens" { + type = bool +} +variable "oauth_redirect_uri" { + type = string +} +variable "oauth_refresh_token_validity" { + type = number +} +variable "oauth_use_secondary_roles" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf new file mode 100644 index 0000000000..87c2a43cb7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_oauth_integration_for_partner_applications" "test" { + blocked_roles_list = var.blocked_roles_list + comment = var.comment + enabled = var.enabled + name = var.name + oauth_client = var.oauth_client + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_redirect_uri = var.oauth_redirect_uri + oauth_refresh_token_validity = var.oauth_refresh_token_validity + oauth_use_secondary_roles = var.oauth_use_secondary_roles +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf new file mode 100644 index 0000000000..e16f09d94b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf @@ -0,0 +1,28 @@ + +variable "blocked_roles_list" { + type = set(string) +} +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "name" { + type = string +} +variable "oauth_client" { + type = string +} +variable "oauth_issue_refresh_tokens" { + type = bool +} +variable "oauth_redirect_uri" { + type = string +} +variable "oauth_refresh_token_validity" { + type = number +} +variable "oauth_use_secondary_roles" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf new file mode 100644 index 0000000000..87c2a43cb7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf @@ -0,0 +1,11 @@ +resource "snowflake_oauth_integration_for_partner_applications" "test" { + blocked_roles_list = var.blocked_roles_list + comment = var.comment + enabled = var.enabled + name = var.name + oauth_client = var.oauth_client + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_redirect_uri = var.oauth_redirect_uri + oauth_refresh_token_validity = var.oauth_refresh_token_validity + oauth_use_secondary_roles = var.oauth_use_secondary_roles +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf new file mode 100644 index 0000000000..e16f09d94b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf @@ -0,0 +1,28 @@ + +variable "blocked_roles_list" { + type = set(string) +} +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "name" { + type = string +} +variable "oauth_client" { + type = string +} +variable "oauth_issue_refresh_tokens" { + type = bool +} +variable "oauth_redirect_uri" { + type = string +} +variable "oauth_refresh_token_validity" { + type = number +} +variable "oauth_use_secondary_roles" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf new file mode 100644 index 0000000000..bbfd609f49 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf @@ -0,0 +1,10 @@ +resource "snowflake_oauth_integration_for_partner_applications" "test" { + blocked_roles_list = var.blocked_roles_list + comment = var.comment + enabled = var.enabled + name = var.name + oauth_client = var.oauth_client + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_refresh_token_validity = var.oauth_refresh_token_validity + oauth_use_secondary_roles = var.oauth_use_secondary_roles +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf new file mode 100644 index 0000000000..2dc336ee1f --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf @@ -0,0 +1,25 @@ + +variable "blocked_roles_list" { + type = set(string) +} +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "name" { + type = string +} +variable "oauth_client" { + type = string +} +variable "oauth_issue_refresh_tokens" { + type = string +} +variable "oauth_refresh_token_validity" { + type = number +} +variable "oauth_use_secondary_roles" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf new file mode 100644 index 0000000000..dc0806c21b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf @@ -0,0 +1,3 @@ +resource "snowflake_oauth_integration_for_partner_applications" "test" { + name = var.name +} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf new file mode 100644 index 0000000000..77e5cc9698 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf @@ -0,0 +1,3 @@ +variable "name" { + type = string +} diff --git a/pkg/schemas/oauth_integration_for_custom_clients.go b/pkg/schemas/oauth_integration_for_custom_clients.go new file mode 100644 index 0000000000..3f00b3e610 --- /dev/null +++ b/pkg/schemas/oauth_integration_for_custom_clients.go @@ -0,0 +1,69 @@ +package schemas + +import ( + "log" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var DescribeOauthIntegrationForCustomClients = map[string]*schema.Schema{ + "oauth_client_type": DescribePropertyListSchema, + "oauth_redirect_uri": DescribePropertyListSchema, + "enabled": DescribePropertyListSchema, + "oauth_allow_non_tls_redirect_uri": DescribePropertyListSchema, + "oauth_enforce_pkce": DescribePropertyListSchema, + "oauth_use_secondary_roles": DescribePropertyListSchema, + "pre_authorized_roles_list": DescribePropertyListSchema, + "blocked_roles_list": DescribePropertyListSchema, + "oauth_issue_refresh_tokens": DescribePropertyListSchema, + "oauth_refresh_token_validity": DescribePropertyListSchema, + "network_policy": DescribePropertyListSchema, + "oauth_client_rsa_public_key_fp": DescribePropertyListSchema, + "oauth_client_rsa_public_key_2_fp": DescribePropertyListSchema, + "comment": DescribePropertyListSchema, + "oauth_client_id": DescribePropertyListSchema, + "oauth_authorization_endpoint": DescribePropertyListSchema, + "oauth_token_endpoint": DescribePropertyListSchema, + "oauth_allowed_authorization_endpoints": DescribePropertyListSchema, + "oauth_allowed_token_endpoints": DescribePropertyListSchema, +} + +func DescribeOauthIntegrationForCustomClientsToSchema(integrationProperties []sdk.SecurityIntegrationProperty) map[string]any { + propsSchema := make(map[string]any) + for _, property := range integrationProperties { + switch property.Name { + case "OAUTH_CLIENT_TYPE", + "OAUTH_REDIRECT_URI", + "ENABLED", + "OAUTH_ALLOW_NON_TLS_REDIRECT_URI", + "OAUTH_ENFORCE_PKCE", + "OAUTH_USE_SECONDARY_ROLES", + "PRE_AUTHORIZED_ROLES_LIST", + "BLOCKED_ROLES_LIST", + "OAUTH_ISSUE_REFRESH_TOKENS", + "OAUTH_REFRESH_TOKEN_VALIDITY", + "NETWORK_POLICY", + "OAUTH_CLIENT_RSA_PUBLIC_KEY_FP", + "OAUTH_CLIENT_RSA_PUBLIC_KEY_2_FP", + "COMMENT", + "OAUTH_CLIENT_ID", + "OAUTH_AUTHORIZATION_ENDPOINT", + "OAUTH_TOKEN_ENDPOINT", + "OAUTH_ALLOWED_AUTHORIZATION_ENDPOINTS", + "OAUTH_ALLOWED_TOKEN_ENDPOINTS": + propsSchema[strings.ToLower(property.Name)] = []map[string]any{ + { + "name": property.Name, + "type": property.Type, + "value": property.Value, + "default": property.Default, + }, + } + default: + log.Printf("[WARN] unexpected property %v returned from Snowflake", property.Name) + } + } + return propsSchema +} diff --git a/pkg/sdk/security_integrations_def.go b/pkg/sdk/security_integrations_def.go index 8c87adb64c..db276281ee 100644 --- a/pkg/sdk/security_integrations_def.go +++ b/pkg/sdk/security_integrations_def.go @@ -48,6 +48,23 @@ const ( OauthSecurityIntegrationUseSecondaryRolesNone OauthSecurityIntegrationUseSecondaryRolesOption = "NONE" ) +var AllOauthSecurityIntegrationUseSecondaryRoles = []OauthSecurityIntegrationUseSecondaryRolesOption{ + OauthSecurityIntegrationUseSecondaryRolesImplicit, + OauthSecurityIntegrationUseSecondaryRolesNone, +} + +func ToOauthSecurityIntegrationUseSecondaryRolesOption(s string) (OauthSecurityIntegrationUseSecondaryRolesOption, error) { + s = strings.ToUpper(s) + switch s { + case string(OauthSecurityIntegrationUseSecondaryRolesImplicit): + return OauthSecurityIntegrationUseSecondaryRolesImplicit, nil + case string(OauthSecurityIntegrationUseSecondaryRolesNone): + return OauthSecurityIntegrationUseSecondaryRolesNone, nil + default: + return "", fmt.Errorf("invalid OauthSecurityIntegrationUseSecondaryRolesOption: %s", s) + } +} + type OauthSecurityIntegrationClientTypeOption string const ( @@ -55,6 +72,23 @@ const ( OauthSecurityIntegrationClientTypeConfidential OauthSecurityIntegrationClientTypeOption = "CONFIDENTIAL" ) +var AllOauthSecurityIntegrationClientTypes = []OauthSecurityIntegrationClientTypeOption{ + OauthSecurityIntegrationClientTypePublic, + OauthSecurityIntegrationClientTypeConfidential, +} + +func ToOauthSecurityIntegrationClientTypeOption(s string) (OauthSecurityIntegrationClientTypeOption, error) { + s = strings.ToUpper(s) + switch s { + case string(OauthSecurityIntegrationClientTypePublic): + return OauthSecurityIntegrationClientTypePublic, nil + case string(OauthSecurityIntegrationClientTypeConfidential): + return OauthSecurityIntegrationClientTypeConfidential, nil + default: + return "", fmt.Errorf("invalid OauthSecurityIntegrationClientTypeOption: %s", s) + } +} + type OauthSecurityIntegrationClientOption string const ( @@ -63,6 +97,97 @@ const ( OauthSecurityIntegrationClientTableauServer OauthSecurityIntegrationClientOption = "TABLEAU_SERVER" ) +var AllOauthSecurityIntegrationClients = []OauthSecurityIntegrationClientOption{ + OauthSecurityIntegrationClientLooker, + OauthSecurityIntegrationClientTableauDesktop, + OauthSecurityIntegrationClientTableauServer, +} + +func ToOauthSecurityIntegrationClientOption(s string) (OauthSecurityIntegrationClientOption, error) { + s = strings.ToUpper(s) + switch s { + case string(OauthSecurityIntegrationClientLooker): + return OauthSecurityIntegrationClientLooker, nil + case string(OauthSecurityIntegrationClientTableauDesktop): + return OauthSecurityIntegrationClientTableauDesktop, nil + case string(OauthSecurityIntegrationClientTableauServer): + return OauthSecurityIntegrationClientTableauServer, nil + default: + return "", fmt.Errorf("invalid OauthSecurityIntegrationClientOption: %s", s) + } +} + +type Saml2SecurityIntegrationSaml2ProviderOption string + +const ( + Saml2SecurityIntegrationSaml2ProviderOkta Saml2SecurityIntegrationSaml2ProviderOption = "OKTA" + Saml2SecurityIntegrationSaml2ProviderAdfs Saml2SecurityIntegrationSaml2ProviderOption = "ADFS" + Saml2SecurityIntegrationSaml2ProviderCustom Saml2SecurityIntegrationSaml2ProviderOption = "CUSTOM" +) + +var AllSaml2SecurityIntegrationSaml2Providers = []Saml2SecurityIntegrationSaml2ProviderOption{ + Saml2SecurityIntegrationSaml2ProviderOkta, + Saml2SecurityIntegrationSaml2ProviderAdfs, + Saml2SecurityIntegrationSaml2ProviderCustom, +} + +func ToSaml2SecurityIntegrationSaml2ProviderOption(s string) (Saml2SecurityIntegrationSaml2ProviderOption, error) { + s = strings.ToUpper(s) + switch s { + case string(Saml2SecurityIntegrationSaml2ProviderOkta): + return Saml2SecurityIntegrationSaml2ProviderOkta, nil + case string(Saml2SecurityIntegrationSaml2ProviderAdfs): + return Saml2SecurityIntegrationSaml2ProviderAdfs, nil + case string(Saml2SecurityIntegrationSaml2ProviderCustom): + return Saml2SecurityIntegrationSaml2ProviderCustom, nil + default: + return "", fmt.Errorf("invalid Saml2SecurityIntegrationSaml2ProviderOption: %s", s) + } +} + +type Saml2SecurityIntegrationSaml2RequestedNameidFormatOption string + +const ( + Saml2SecurityIntegrationSaml2RequestedNameidFormatUnspecified Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + Saml2SecurityIntegrationSaml2RequestedNameidFormatEmailAddress Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" + Saml2SecurityIntegrationSaml2RequestedNameidFormatX509SubjectName Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName" + Saml2SecurityIntegrationSaml2RequestedNameidFormatWindowsDomainQualifiedName Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName" + Saml2SecurityIntegrationSaml2RequestedNameidFormatKerberos Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos" + Saml2SecurityIntegrationSaml2RequestedNameidFormatPersistent Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" + Saml2SecurityIntegrationSaml2RequestedNameidFormatTransient Saml2SecurityIntegrationSaml2RequestedNameidFormatOption = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" +) + +var AllSaml2SecurityIntegrationSaml2RequestedNameidFormats = []Saml2SecurityIntegrationSaml2RequestedNameidFormatOption{ + Saml2SecurityIntegrationSaml2RequestedNameidFormatUnspecified, + Saml2SecurityIntegrationSaml2RequestedNameidFormatEmailAddress, + Saml2SecurityIntegrationSaml2RequestedNameidFormatX509SubjectName, + Saml2SecurityIntegrationSaml2RequestedNameidFormatWindowsDomainQualifiedName, + Saml2SecurityIntegrationSaml2RequestedNameidFormatKerberos, + Saml2SecurityIntegrationSaml2RequestedNameidFormatPersistent, + Saml2SecurityIntegrationSaml2RequestedNameidFormatTransient, +} + +func ToSaml2SecurityIntegrationSaml2RequestedNameidFormatOption(s string) (Saml2SecurityIntegrationSaml2RequestedNameidFormatOption, error) { + switch s { + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatUnspecified): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatUnspecified, nil + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatEmailAddress): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatEmailAddress, nil + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatX509SubjectName): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatX509SubjectName, nil + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatWindowsDomainQualifiedName): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatWindowsDomainQualifiedName, nil + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatKerberos): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatKerberos, nil + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatPersistent): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatPersistent, nil + case string(Saml2SecurityIntegrationSaml2RequestedNameidFormatTransient): + return Saml2SecurityIntegrationSaml2RequestedNameidFormatTransient, nil + default: + return "", fmt.Errorf("invalid Saml2SecurityIntegrationSaml2RequestedNameidFormatOption: %s", s) + } +} + type ScimSecurityIntegrationScimClientOption string const ( @@ -80,11 +205,11 @@ var AllScimSecurityIntegrationScimClients = []ScimSecurityIntegrationScimClientO func ToScimSecurityIntegrationScimClientOption(s string) (ScimSecurityIntegrationScimClientOption, error) { s = strings.ToUpper(s) switch s { - case "OKTA": + case string(ScimSecurityIntegrationScimClientOkta): return ScimSecurityIntegrationScimClientOkta, nil - case "AZURE": + case string(ScimSecurityIntegrationScimClientAzure): return ScimSecurityIntegrationScimClientAzure, nil - case "GENERIC": + case string(ScimSecurityIntegrationScimClientGeneric): return ScimSecurityIntegrationScimClientGeneric, nil default: return "", fmt.Errorf("invalid ScimSecurityIntegrationScimClientOption: %s", s) @@ -108,11 +233,11 @@ var AllScimSecurityIntegrationRunAsRoles = []ScimSecurityIntegrationRunAsRoleOpt func ToScimSecurityIntegrationRunAsRoleOption(s string) (ScimSecurityIntegrationRunAsRoleOption, error) { s = strings.ToUpper(s) switch s { - case "OKTA_PROVISIONER": + case string(ScimSecurityIntegrationRunAsRoleOktaProvisioner): return ScimSecurityIntegrationRunAsRoleOktaProvisioner, nil - case "AAD_PROVISIONER": + case string(ScimSecurityIntegrationRunAsRoleAadProvisioner): return ScimSecurityIntegrationRunAsRoleAadProvisioner, nil - case "GENERIC_SCIM_PROVISIONER": + case string(ScimSecurityIntegrationRunAsRoleGenericScimProvisioner): return ScimSecurityIntegrationRunAsRoleGenericScimProvisioner, nil default: return "", fmt.Errorf("invalid ScimSecurityIntegrationRunAsRoleOption: %s", s) @@ -325,7 +450,11 @@ var saml2IntegrationSetDef = g.NewQueryStruct("Saml2IntegrationSet"). OptionalBooleanAssignment("ENABLED", g.ParameterOptions()). OptionalTextAssignment("SAML2_ISSUER", g.ParameterOptions().SingleQuotes()). OptionalTextAssignment("SAML2_SSO_URL", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("SAML2_PROVIDER", g.ParameterOptions().SingleQuotes()). + OptionalAssignment( + "SAML2_PROVIDER", + g.KindOfT[Saml2SecurityIntegrationSaml2ProviderOption](), + g.ParameterOptions().SingleQuotes(), + ). OptionalTextAssignment("SAML2_X509_CERT", g.ParameterOptions().SingleQuotes()). ListAssignment("ALLOWED_USER_DOMAINS", "UserDomain", g.ParameterOptions().Parentheses()). ListAssignment("ALLOWED_EMAIL_PATTERNS", "EmailPattern", g.ParameterOptions().Parentheses()). @@ -333,7 +462,11 @@ var saml2IntegrationSetDef = g.NewQueryStruct("Saml2IntegrationSet"). OptionalBooleanAssignment("SAML2_ENABLE_SP_INITIATED", g.ParameterOptions()). OptionalTextAssignment("SAML2_SNOWFLAKE_X509_CERT", g.ParameterOptions().SingleQuotes()). OptionalBooleanAssignment("SAML2_SIGN_REQUEST", g.ParameterOptions()). - OptionalTextAssignment("SAML2_REQUESTED_NAMEID_FORMAT", g.ParameterOptions().SingleQuotes()). + OptionalAssignment( + "SAML2_REQUESTED_NAMEID_FORMAT", + g.KindOfT[Saml2SecurityIntegrationSaml2RequestedNameidFormatOption](), + g.ParameterOptions().SingleQuotes(), + ). OptionalTextAssignment("SAML2_POST_LOGOUT_REDIRECT_URL", g.ParameterOptions().SingleQuotes()). OptionalBooleanAssignment("SAML2_FORCE_AUTHN", g.ParameterOptions()). OptionalTextAssignment("SAML2_SNOWFLAKE_ISSUER_URL", g.ParameterOptions().SingleQuotes()). @@ -517,7 +650,7 @@ var SecurityIntegrationsDef = g.NewInterface( g.KindOfT[OauthSecurityIntegrationClientTypeOption](), g.ParameterOptions().Required().SingleQuotes(), ). - TextAssignment("OAUTH_REDIRECT_URI", g.ParameterOptions().Required().SingleQuotes()). + OptionalTextAssignment("OAUTH_REDIRECT_URI", g.ParameterOptions().SingleQuotes()). OptionalBooleanAssignment("ENABLED", g.ParameterOptions()). OptionalBooleanAssignment("OAUTH_ALLOW_NON_TLS_REDIRECT_URI", g.ParameterOptions()). OptionalBooleanAssignment("OAUTH_ENFORCE_PKCE", g.ParameterOptions()). @@ -541,10 +674,14 @@ var SecurityIntegrationsDef = g.NewInterface( createSecurityIntegrationOperation("CreateSaml2", func(qs *g.QueryStruct) *g.QueryStruct { return qs. PredefinedQueryStructField("integrationType", "string", g.StaticOptions().SQL("TYPE = SAML2")). - BooleanAssignment("ENABLED", g.ParameterOptions().Required()). + OptionalBooleanAssignment("ENABLED", g.ParameterOptions()). TextAssignment("SAML2_ISSUER", g.ParameterOptions().Required().SingleQuotes()). TextAssignment("SAML2_SSO_URL", g.ParameterOptions().Required().SingleQuotes()). - TextAssignment("SAML2_PROVIDER", g.ParameterOptions().Required().SingleQuotes()). + Assignment( + "SAML2_PROVIDER", + g.KindOfT[Saml2SecurityIntegrationSaml2ProviderOption](), + g.ParameterOptions().Required().SingleQuotes(), + ). TextAssignment("SAML2_X509_CERT", g.ParameterOptions().Required().SingleQuotes()). ListAssignment("ALLOWED_USER_DOMAINS", "UserDomain", g.ParameterOptions().Parentheses()). ListAssignment("ALLOWED_EMAIL_PATTERNS", "EmailPattern", g.ParameterOptions().Parentheses()). @@ -552,7 +689,11 @@ var SecurityIntegrationsDef = g.NewInterface( OptionalBooleanAssignment("SAML2_ENABLE_SP_INITIATED", g.ParameterOptions()). OptionalTextAssignment("SAML2_SNOWFLAKE_X509_CERT", g.ParameterOptions().SingleQuotes()). OptionalBooleanAssignment("SAML2_SIGN_REQUEST", g.ParameterOptions()). - OptionalTextAssignment("SAML2_REQUESTED_NAMEID_FORMAT", g.ParameterOptions().SingleQuotes()). + OptionalAssignment( + "SAML2_REQUESTED_NAMEID_FORMAT", + g.KindOfT[Saml2SecurityIntegrationSaml2RequestedNameidFormatOption](), + g.ParameterOptions().SingleQuotes(), + ). OptionalTextAssignment("SAML2_POST_LOGOUT_REDIRECT_URL", g.ParameterOptions().SingleQuotes()). OptionalBooleanAssignment("SAML2_FORCE_AUTHN", g.ParameterOptions()). OptionalTextAssignment("SAML2_SNOWFLAKE_ISSUER_URL", g.ParameterOptions().SingleQuotes()). diff --git a/pkg/sdk/testint/security_integrations_gen_integration_test.go b/pkg/sdk/testint/security_integrations_gen_integration_test.go index 993d6500a4..0c2c94cec5 100644 --- a/pkg/sdk/testint/security_integrations_gen_integration_test.go +++ b/pkg/sdk/testint/security_integrations_gen_integration_test.go @@ -18,7 +18,7 @@ func TestInt_SecurityIntegrations(t *testing.T) { acsURL := testClientHelper().Context.ACSURL(t) issuerURL := testClientHelper().Context.IssuerURL(t) cert := random.GenerateX509(t) - rsaKey := random.GenerateRSAPublicKey(t) + rsaKey, _ := random.GenerateRSAPublicKey(t) revertParameter := testClientHelper().Parameter.UpdateAccountParameterTemporarily(t, sdk.AccountParameterEnableIdentifierFirstLogin, "true") t.Cleanup(revertParameter) @@ -135,7 +135,7 @@ func TestInt_SecurityIntegrations(t *testing.T) { } err := client.SecurityIntegrations.CreateSaml2(ctx, saml2Req) require.NoError(t, err) - cleanupSecurityIntegration(t, id) + // cleanupSecurityIntegration(t, id) integration, err := client.SecurityIntegrations.ShowByID(ctx, id) require.NoError(t, err) diff --git a/v1-preparations/REMAINING_GA_OBJECTS.MD b/v1-preparations/REMAINING_GA_OBJECTS.MD index fe6ea6cc2d..ebdb328e3c 100644 --- a/v1-preparations/REMAINING_GA_OBJECTS.MD +++ b/v1-preparations/REMAINING_GA_OBJECTS.MD @@ -39,4 +39,4 @@ Known issues lists open issues touching the given object. Note that some of thes | PIPE | ❌ | [#2785](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2785), [#2075](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2075), [#1781](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1781), [#1707](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1707), [#1478](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/1478), [#533](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/533) | | SECRET | ❌ | [#2545](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2545) | | SEQUENCE | ❌ | [#2589](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2589) | -| SESSION POLICY | ❌ | - | +| SESSION POLICY | ❌ | [#2870](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2870) | From c7ce02688b8e4bed794e7d154382ccfe1c6ada5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Wed, 3 Jul 2024 15:16:38 +0200 Subject: [PATCH 2/6] Remove oauth integration for partner applications --- ...th_integration_for_partner_applications.md | 41 -- pkg/provider/provider.go | 129 +++--- pkg/resources/common.go | 76 ---- pkg/resources/custom_diffs.go | 16 - .../oauth_integration_for_custom_clients.go | 10 +- ...th_integration_for_partner_applications.go | 418 ------------------ ...or_partner_applications_acceptance_test.go | 231 ---------- .../basic/test.tf | 4 - .../basic/variables.tf | 6 - .../complete/test.tf | 11 - .../complete/variables.tf | 28 -- .../complete_looker/test.tf | 11 - .../complete_looker/variables.tf | 28 -- .../complete_tableau_server copy/test.tf | 11 - .../complete_tableau_server copy/variables.tf | 28 -- .../complete_tableau_server/test.tf | 10 - .../complete_tableau_server/variables.tf | 25 -- .../invalid/test.tf | 3 - .../invalid/variables.tf | 3 - 19 files changed, 67 insertions(+), 1022 deletions(-) delete mode 100644 docs/resources/oauth_integration_for_partner_applications.md delete mode 100644 pkg/resources/oauth_integration_for_partner_applications.go delete mode 100644 pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf delete mode 100644 pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf diff --git a/docs/resources/oauth_integration_for_partner_applications.md b/docs/resources/oauth_integration_for_partner_applications.md deleted file mode 100644 index 854ea05cd6..0000000000 --- a/docs/resources/oauth_integration_for_partner_applications.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -page_title: "snowflake_oauth_integration_for_partner_applications Resource - terraform-provider-snowflake" -subcategory: "" -description: |- - ---- - -# snowflake_oauth_integration_for_partner_applications (Resource) - - - - - - -## Schema - -### Required - -- `name` (String) Specifies the name of the SAML2 integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account. -- `oauth_client` (String) Creates an OAuth interface between Snowflake and a partner application. Valid options are: [LOOKER TABLEAU_DESKTOP TABLEAU_SERVER] - -### Optional - -- `blocked_roles_list` (Set of String) List of roles that a user cannot explicitly consent to using after authenticating. Do not include ACCOUNTADMIN, ORGADMIN or SECURITYADMIN as they are already implicitly enforced and will cause in-place updates. -- `comment` (String) Specifies a comment for the OAuth integration. -- `enabled` (Boolean) Specifies whether this OAuth integration is enabled or disabled. -- `oauth_issue_refresh_tokens` (String) Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired. -- `oauth_redirect_uri` (String) Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. -- `oauth_refresh_token_validity` (Number) Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE. -- `oauth_use_secondary_roles` (String) Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. - -### Read-Only - -- `created_on` (String) Date and time when the OAuth integration was created. -- `id` (String) The ID of this resource. -- `oauth_add_privileged_roles_to_blocked_list` (Boolean) -- `oauth_allowed_authorization_endpoints` (Set of String) A list of allowed authorization endpoints for oauth. -- `oauth_allowed_token_endpoints` (Set of String) A list of allowed token endpoints for oauth. -- `oauth_authorization_endpoint` (String) Authorization endpoint for oauth. -- `oauth_client_id` (String) Oauth client ID. -- `oauth_token_endpoint` (String) Token endpoint for oauth. diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index c063e8cd37..7fadd62573 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -421,71 +421,70 @@ func Provider() *schema.Provider { func getResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "snowflake_account": resources.Account(), - "snowflake_account_password_policy_attachment": resources.AccountPasswordPolicyAttachment(), - "snowflake_account_parameter": resources.AccountParameter(), - "snowflake_alert": resources.Alert(), - "snowflake_api_integration": resources.APIIntegration(), - "snowflake_cortex_search_service": resources.CortexSearchService(), - "snowflake_database_old": resources.DatabaseOld(), - "snowflake_database": resources.Database(), - "snowflake_database_role": resources.DatabaseRole(), - "snowflake_dynamic_table": resources.DynamicTable(), - "snowflake_email_notification_integration": resources.EmailNotificationIntegration(), - "snowflake_external_function": resources.ExternalFunction(), - "snowflake_external_oauth_integration": resources.ExternalOauthIntegration(), - "snowflake_external_table": resources.ExternalTable(), - "snowflake_failover_group": resources.FailoverGroup(), - "snowflake_file_format": resources.FileFormat(), - "snowflake_function": resources.Function(), - "snowflake_grant_account_role": resources.GrantAccountRole(), - "snowflake_grant_application_role": resources.GrantApplicationRole(), - "snowflake_grant_database_role": resources.GrantDatabaseRole(), - "snowflake_grant_ownership": resources.GrantOwnership(), - "snowflake_grant_privileges_to_account_role": resources.GrantPrivilegesToAccountRole(), - "snowflake_grant_privileges_to_database_role": resources.GrantPrivilegesToDatabaseRole(), - "snowflake_grant_privileges_to_share": resources.GrantPrivilegesToShare(), - "snowflake_managed_account": resources.ManagedAccount(), - "snowflake_masking_policy": resources.MaskingPolicy(), - "snowflake_materialized_view": resources.MaterializedView(), - "snowflake_network_policy": resources.NetworkPolicy(), - "snowflake_network_policy_attachment": resources.NetworkPolicyAttachment(), - "snowflake_network_rule": resources.NetworkRule(), - "snowflake_notification_integration": resources.NotificationIntegration(), - "snowflake_oauth_integration": resources.OAuthIntegration(), - "snowflake_oauth_integration_for_custom_clients": resources.OauthIntegrationForCustomClients(), - "snowflake_oauth_integration_for_partner_applications": resources.OauthIntegrationForPartnerApplications(), - "snowflake_object_parameter": resources.ObjectParameter(), - "snowflake_password_policy": resources.PasswordPolicy(), - "snowflake_pipe": resources.Pipe(), - "snowflake_procedure": resources.Procedure(), - "snowflake_resource_monitor": resources.ResourceMonitor(), - "snowflake_role": resources.Role(), - "snowflake_row_access_policy": resources.RowAccessPolicy(), - "snowflake_saml_integration": resources.SAMLIntegration(), - "snowflake_schema": resources.Schema(), - "snowflake_scim_integration": resources.SCIMIntegration(), - "snowflake_secondary_database": resources.SecondaryDatabase(), - "snowflake_sequence": resources.Sequence(), - "snowflake_session_parameter": resources.SessionParameter(), - "snowflake_share": resources.Share(), - "snowflake_shared_database": resources.SharedDatabase(), - "snowflake_stage": resources.Stage(), - "snowflake_storage_integration": resources.StorageIntegration(), - "snowflake_stream": resources.Stream(), - "snowflake_table": resources.Table(), - "snowflake_table_column_masking_policy_application": resources.TableColumnMaskingPolicyApplication(), - "snowflake_table_constraint": resources.TableConstraint(), - "snowflake_tag": resources.Tag(), - "snowflake_tag_association": resources.TagAssociation(), - "snowflake_tag_masking_policy_association": resources.TagMaskingPolicyAssociation(), - "snowflake_task": resources.Task(), - "snowflake_unsafe_execute": resources.UnsafeExecute(), - "snowflake_user": resources.User(), - "snowflake_user_password_policy_attachment": resources.UserPasswordPolicyAttachment(), - "snowflake_user_public_keys": resources.UserPublicKeys(), - "snowflake_view": resources.View(), - "snowflake_warehouse": resources.Warehouse(), + "snowflake_account": resources.Account(), + "snowflake_account_password_policy_attachment": resources.AccountPasswordPolicyAttachment(), + "snowflake_account_parameter": resources.AccountParameter(), + "snowflake_alert": resources.Alert(), + "snowflake_api_integration": resources.APIIntegration(), + "snowflake_cortex_search_service": resources.CortexSearchService(), + "snowflake_database_old": resources.DatabaseOld(), + "snowflake_database": resources.Database(), + "snowflake_database_role": resources.DatabaseRole(), + "snowflake_dynamic_table": resources.DynamicTable(), + "snowflake_email_notification_integration": resources.EmailNotificationIntegration(), + "snowflake_external_function": resources.ExternalFunction(), + "snowflake_external_oauth_integration": resources.ExternalOauthIntegration(), + "snowflake_external_table": resources.ExternalTable(), + "snowflake_failover_group": resources.FailoverGroup(), + "snowflake_file_format": resources.FileFormat(), + "snowflake_function": resources.Function(), + "snowflake_grant_account_role": resources.GrantAccountRole(), + "snowflake_grant_application_role": resources.GrantApplicationRole(), + "snowflake_grant_database_role": resources.GrantDatabaseRole(), + "snowflake_grant_ownership": resources.GrantOwnership(), + "snowflake_grant_privileges_to_account_role": resources.GrantPrivilegesToAccountRole(), + "snowflake_grant_privileges_to_database_role": resources.GrantPrivilegesToDatabaseRole(), + "snowflake_grant_privileges_to_share": resources.GrantPrivilegesToShare(), + "snowflake_managed_account": resources.ManagedAccount(), + "snowflake_masking_policy": resources.MaskingPolicy(), + "snowflake_materialized_view": resources.MaterializedView(), + "snowflake_network_policy": resources.NetworkPolicy(), + "snowflake_network_policy_attachment": resources.NetworkPolicyAttachment(), + "snowflake_network_rule": resources.NetworkRule(), + "snowflake_notification_integration": resources.NotificationIntegration(), + "snowflake_oauth_integration": resources.OAuthIntegration(), + "snowflake_oauth_integration_for_custom_clients": resources.OauthIntegrationForCustomClients(), + "snowflake_object_parameter": resources.ObjectParameter(), + "snowflake_password_policy": resources.PasswordPolicy(), + "snowflake_pipe": resources.Pipe(), + "snowflake_procedure": resources.Procedure(), + "snowflake_resource_monitor": resources.ResourceMonitor(), + "snowflake_role": resources.Role(), + "snowflake_row_access_policy": resources.RowAccessPolicy(), + "snowflake_saml_integration": resources.SAMLIntegration(), + "snowflake_schema": resources.Schema(), + "snowflake_scim_integration": resources.SCIMIntegration(), + "snowflake_secondary_database": resources.SecondaryDatabase(), + "snowflake_sequence": resources.Sequence(), + "snowflake_session_parameter": resources.SessionParameter(), + "snowflake_share": resources.Share(), + "snowflake_shared_database": resources.SharedDatabase(), + "snowflake_stage": resources.Stage(), + "snowflake_storage_integration": resources.StorageIntegration(), + "snowflake_stream": resources.Stream(), + "snowflake_table": resources.Table(), + "snowflake_table_column_masking_policy_application": resources.TableColumnMaskingPolicyApplication(), + "snowflake_table_constraint": resources.TableConstraint(), + "snowflake_tag": resources.Tag(), + "snowflake_tag_association": resources.TagAssociation(), + "snowflake_tag_masking_policy_association": resources.TagMaskingPolicyAssociation(), + "snowflake_task": resources.Task(), + "snowflake_unsafe_execute": resources.UnsafeExecute(), + "snowflake_user": resources.User(), + "snowflake_user_password_policy_attachment": resources.UserPasswordPolicyAttachment(), + "snowflake_user_public_keys": resources.UserPublicKeys(), + "snowflake_view": resources.View(), + "snowflake_warehouse": resources.Warehouse(), } } diff --git a/pkg/resources/common.go b/pkg/resources/common.go index 3797a9fb06..3f6380258d 100644 --- a/pkg/resources/common.go +++ b/pkg/resources/common.go @@ -1,19 +1,9 @@ package resources import ( - "context" - "crypto/sha256" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "errors" - "fmt" "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -62,69 +52,3 @@ func suppressQuoting(_, oldValue, newValue string, _ *schema.ResourceData) bool return oldWithoutQuotes == newWithoutQuotes } } - -func DeleteContextSecurityIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - client := meta.(*provider.Context).Client - - err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(sdk.NewAccountObjectIdentifier(id.Name())).WithIfExists(true)) - if err != nil { - return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: "Error deleting integration", - Detail: fmt.Sprintf("id %v err = %v", id.Name(), err), - }, - } - } - - d.SetId("") - return nil -} - -func RSAKeyHash(key string) (string, error) { - keyBytes := []byte(fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", key)) - - block, _ := pem.Decode(keyBytes) - if block == nil || block.Type != "PUBLIC KEY" { - return "", errors.New("Failed to decode PEM block containing public key") - } - - pubKey, err := x509.ParsePKIXPublicKey(block.Bytes) - if err != nil { - return "", fmt.Errorf("Unable to parse public key: %w", err) - } - - pubKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey) - if err != nil { - return "", fmt.Errorf("Unable to marshal public key: %w", err) - } - - hash := sha256.Sum256(pubKeyBytes) - return fmt.Sprintf("SHA256:%s", base64.StdEncoding.EncodeToString(hash[:])), nil -} - -func getParameterInAccount(ctx context.Context, client *sdk.Client, param string) (string, error) { - params, err := client.Parameters.ShowParameters(ctx, &sdk.ShowParametersOptions{ - Like: &sdk.Like{ - Pattern: sdk.Pointer(param), - }, - In: &sdk.ParametersIn{ - Account: sdk.Pointer(true), - }, - }) - if err != nil { - return "", err - } - var found *sdk.Parameter - for _, v := range params { - if v.Key == param { - found = v - break - } - } - if found == nil { - return "", fmt.Errorf("parameter %s not found", param) - } - return found.Value, nil -} diff --git a/pkg/resources/custom_diffs.go b/pkg/resources/custom_diffs.go index 86a4d04c6e..72183af3e5 100644 --- a/pkg/resources/custom_diffs.go +++ b/pkg/resources/custom_diffs.go @@ -58,22 +58,6 @@ func ParameterValueComputedIf(key string, parameters []*sdk.Parameter, objectPar } } -// ForceNewIfChangeToEmptySet sets a ForceNew for a set field which was set to an empty value. -func ForceNewIfChangeToEmptySet[T any](key string) schema.CustomizeDiffFunc { - return customdiff.ForceNewIfChange(key, func(ctx context.Context, oldValue, newValue, meta any) bool { - oldList, newList := oldValue.(*schema.Set).List(), newValue.(*schema.Set).List() - return len(oldList) > 0 && len(newList) == 0 - }) -} - -// ForceNewIfChangeToEmptyString sets a ForceNew for a string field which was set to an empty value. -func ForceNewIfChangeToEmptyString(key string) schema.CustomizeDiffFunc { - return customdiff.ForceNewIfChange(key, func(ctx context.Context, oldValue, newValue, meta any) bool { - oldString, newString := oldValue.(string), newValue.(string) - return len(oldString) > 0 && len(newString) == 0 - }) -} - // TODO [follow-up PR]: test func ComputedIfAnyAttributeChanged(key string, changedAttributeKeys ...string) schema.CustomizeDiffFunc { return customdiff.ComputedIf(key, func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool { diff --git a/pkg/resources/oauth_integration_for_custom_clients.go b/pkg/resources/oauth_integration_for_custom_clients.go index 39f393dc3a..2af44a18f3 100644 --- a/pkg/resources/oauth_integration_for_custom_clients.go +++ b/pkg/resources/oauth_integration_for_custom_clients.go @@ -99,14 +99,10 @@ var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ Description: "Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE.", }, "network_policy": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies an existing network policy. This network policy controls network traffic that is attempting to exchange an authorization " + - "code for an access or refresh token or to use a refresh token to obtain a new access token.", + Type: schema.TypeString, + Optional: true, + Description: "Specifies an existing network policy. This network policy controls network traffic that is attempting to exchange an authorization code for an access or refresh token or to use a refresh token to obtain a new access token.", ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), - DiffSuppressFunc: func(_, old, new string, d *schema.ResourceData) bool { - return sdk.NewAccountObjectIdentifierFromFullyQualifiedName(old) == sdk.NewAccountObjectIdentifierFromFullyQualifiedName(new) - }, }, "oauth_client_rsa_public_key": { Type: schema.TypeString, diff --git a/pkg/resources/oauth_integration_for_partner_applications.go b/pkg/resources/oauth_integration_for_partner_applications.go deleted file mode 100644 index 9044b9836a..0000000000 --- a/pkg/resources/oauth_integration_for_partner_applications.go +++ /dev/null @@ -1,418 +0,0 @@ -package resources - -import ( - "context" - "fmt" - "log" - "reflect" - "strconv" - "strings" - - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -var oauthIntegrationForPartnerApplicationsSchema = map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "Specifies the name of the SAML2 integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account.", - }, - "oauth_client": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: fmt.Sprintf("Creates an OAuth interface between Snowflake and a partner application. Valid options are: %v", sdk.AllOauthSecurityIntegrationClients), - ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationClients), false), - DiffSuppressFunc: ignoreCaseAndTrimSpaceSuppressFunc, - }, - "oauth_redirect_uri": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI.", - }, - "enabled": { - Type: schema.TypeBool, - Optional: true, - Description: "Specifies whether this OAuth integration is enabled or disabled.", - }, - "oauth_issue_refresh_tokens": { - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{"true", "false"}, true), - Default: "unknown", - Optional: true, - Description: "Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired.", - DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { - return oldValue == "true" && newValue == "unknown" - }, - }, - "oauth_refresh_token_validity": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntAtLeast(1), - Description: "Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE.", - DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { - return d.Get(k).(int) == 7776000 && newValue == "0" - }, - }, - "oauth_use_secondary_roles": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies whether default secondary roles set in the user properties are activated by default in the session being opened.", - ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationUseSecondaryRoles), false), - DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool { - return strings.EqualFold(oldValue, newValue) || d.Get(k).(string) == string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone) && newValue == "" - }, - }, - "blocked_roles_list": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Computed: true, - Description: "List of roles that a user cannot explicitly consent to using after authenticating. Do not include ACCOUNTADMIN, ORGADMIN or SECURITYADMIN as they are already implicitly enforced and will cause in-place updates.", - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - value := d.Get("oauth_add_privileged_roles_to_blocked_list").(bool) - if !value { - return old == new - } - return old == "ACCOUNTADMIN" || old == "SECURITYADMIN" - }, - }, - "oauth_add_privileged_roles_to_blocked_list": { - Type: schema.TypeBool, - Computed: true, - }, - "oauth_authorization_endpoint": { - Type: schema.TypeString, - Computed: true, - Description: "Authorization endpoint for oauth.", - }, - "oauth_token_endpoint": { - Type: schema.TypeString, - Computed: true, - Description: "Token endpoint for oauth.", - }, - "oauth_allowed_authorization_endpoints": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, - Description: "A list of allowed authorization endpoints for oauth.", - }, - "oauth_allowed_token_endpoints": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, - Description: "A list of allowed token endpoints for oauth.", - }, - "oauth_client_id": { - Type: schema.TypeString, - Computed: true, - Description: "Oauth client ID.", - }, - "comment": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a comment for the OAuth integration.", - }, - "created_on": { - Type: schema.TypeString, - Computed: true, - Description: "Date and time when the OAuth integration was created.", - }, -} - -func OauthIntegrationForPartnerApplications() *schema.Resource { - return &schema.Resource{ - CreateContext: CreateContextOauthIntegrationForPartnerApplications, - ReadContext: ReadContextOauthIntegrationForPartnerApplications, - UpdateContext: UpdateContextOauthIntegrationForPartnerApplications, - DeleteContext: DeleteContextSecurityIntegration, - Schema: oauthIntegrationForPartnerApplicationsSchema, - CustomizeDiff: customdiff.All( - // SuppressIfParameterSet("blocked_roles_list", "OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST"), - ), - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - } -} - -func CreateContextOauthIntegrationForPartnerApplications(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - name := d.Get("name").(string) - oauthClientRaw := d.Get("oauth_client").(string) - id := sdk.NewAccountObjectIdentifier(name) - oauthClient, err := sdk.ToOauthSecurityIntegrationClientOption(oauthClientRaw) - if err != nil { - return diag.FromErr(err) - } - req := sdk.NewCreateOauthForPartnerApplicationsSecurityIntegrationRequest(id, oauthClient) - - if v, ok := d.GetOk("blocked_roles_list"); ok { - elems := expandStringList(v.(*schema.Set).List()) - blockedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) - for i := range elems { - blockedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) - } - req.WithBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) - } - - if v, ok := d.GetOk("comment"); ok { - req.WithComment(v.(string)) - } - - if v, ok := d.GetOk("enabled"); ok { - req.WithEnabled(v.(bool)) - } - if v := d.Get("oauth_issue_refresh_tokens").(string); v != "unknown" { - parsed, err := strconv.ParseBool(v) - if err != nil { - return diag.FromErr(err) - } - req.WithOauthIssueRefreshTokens(parsed) - } - - if v, ok := d.GetOk("oauth_redirect_uri"); ok { - req.WithOauthRedirectUri(v.(string)) - } - - if v, ok := d.GetOk("oauth_refresh_token_validity"); ok { - req.WithOauthRefreshTokenValidity(v.(int)) - } - - if v, ok := d.GetOk("oauth_use_secondary_roles"); ok { - valueRaw := v.(string) - value, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(valueRaw) - if err != nil { - return diag.FromErr(err) - } - req.WithOauthUseSecondaryRoles(value) - } - - if err := client.SecurityIntegrations.CreateOauthForPartnerApplications(ctx, req); err != nil { - return diag.FromErr(err) - } - - d.SetId(name) - - return ReadContextOauthIntegrationForPartnerApplications(ctx, d, meta) -} - -func ReadContextOauthIntegrationForPartnerApplications(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - - integration, err := client.SecurityIntegrations.ShowByID(ctx, id) - if err != nil { - log.Printf("[DEBUG] OauthIntegrationForPartnerApplications (%s) not found", d.Id()) - d.SetId("") - return diag.FromErr(err) - } - - if c := integration.Category; c != sdk.SecurityIntegrationCategory { - return diag.FromErr(fmt.Errorf("expected %v to be a %s integration, got %v", id, sdk.SecurityIntegrationCategory, c)) - } - if err := d.Set("name", integration.Name); err != nil { - return diag.FromErr(err) - } - if err := d.Set("comment", integration.Comment); err != nil { - return diag.FromErr(err) - } - - if err := d.Set("created_on", integration.CreatedOn.String()); err != nil { - return diag.FromErr(err) - } - - if err := d.Set("enabled", integration.Enabled); err != nil { - return diag.FromErr(err) - } - oauthClient, err := integration.SubType() - if err != nil { - return diag.FromErr(err) - } - if err := d.Set("oauth_client", oauthClient); err != nil { - return diag.FromErr(err) - } - - properties, err := client.SecurityIntegrations.Describe(ctx, id) - if err != nil { - return diag.FromErr(err) - } - defaults := make(map[string]string) - for _, property := range properties { - name := property.Name - value := property.Value - switch name { - case "BLOCKED_ROLES_LIST": - var blockedRoles []string - if len(value) > 0 { - blockedRoles = strings.Split(value, ",") - } - - if err := d.Set("blocked_roles_list", blockedRoles); err != nil { - return diag.FromErr(err) - } - case "COMMENT": - if err := d.Set("comment", value); err != nil { - return diag.FromErr(err) - } - case "CREATED_ON": - if err := d.Set("created_on", value); err != nil { - return diag.FromErr(err) - } - case "ENABLED": - if err := d.Set("enabled", helpers.StringToBool(value)); err != nil { - return diag.FromErr(err) - } - case "OAUTH_CLIENT": - if err := d.Set("oauth_client", value); err != nil { - return diag.FromErr(err) - } - case "OAUTH_ISSUE_REFRESH_TOKENS": - defaults["OAUTH_ISSUE_REFRESH_TOKENS"] = property.Default - if err := d.Set("oauth_issue_refresh_tokens", value); err != nil { - return diag.FromErr(err) - } - case "OAUTH_REDIRECT_URI": - if err := d.Set("oauth_redirect_uri", value); err != nil { - return diag.FromErr(err) - } - case "OAUTH_REFRESH_TOKEN_VALIDITY": - v, err := strconv.Atoi(value) - if err != nil { - return diag.FromErr(err) - } - defaults["OAUTH_REFRESH_TOKEN_VALIDITY"] = property.Default - if err := d.Set("oauth_refresh_token_validity", v); err != nil { - return diag.FromErr(err) - } - case "OAUTH_USE_SECONDARY_ROLES": - if err := d.Set("oauth_use_secondary_roles", value); err != nil { - return diag.FromErr(err) - } - case "OAUTH_AUTHORIZATION_ENDPOINT": - if err := d.Set("oauth_authorization_endpoint", value); err != nil { - return diag.FromErr(err) - } - case "OAUTH_TOKEN_ENDPOINT": - if err := d.Set("oauth_token_endpoint", value); err != nil { - return diag.FromErr(err) - } - case "OAUTH_ALLOWED_AUTHORIZATION_ENDPOINTS": - var elems []string - if len(value) > 0 { - elems = strings.Split(value, ",") - } - - if err := d.Set("oauth_allowed_authorization_endpoints", elems); err != nil { - return diag.FromErr(err) - } - case "OAUTH_ALLOWED_TOKEN_ENDPOINTS": - var elems []string - if len(value) > 0 { - elems = strings.Split(value, ",") - } - if err := d.Set("oauth_allowed_token_endpoints", elems); err != nil { - return diag.FromErr(err) - } - case "OAUTH_CLIENT_ID": - if err := d.Set("oauth_client_id", value); err != nil { - return diag.FromErr(err) - } - - default: - log.Printf("[WARN] unexpected property %v returned from Snowflake", name) - } - } - paramRaw, err := getParameterInAccount(ctx, client, "OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST") - if err != nil { - return nil - } - param := helpers.StringToBool(paramRaw) - if err := d.Set("oauth_add_privileged_roles_to_blocked_list", param); err != nil { - return diag.FromErr(err) - } - return nil -} - -func UpdateContextOauthIntegrationForPartnerApplications(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - client := meta.(*provider.Context).Client - id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - set, unset := sdk.NewOauthForPartnerApplicationsIntegrationSetRequest(), sdk.NewOauthForPartnerApplicationsIntegrationUnsetRequest() - - if d.HasChange("blocked_roles_list") { - elems := expandStringList(d.Get("blocked_roles_list").(*schema.Set).List()) - blockedRoles := make([]sdk.AccountObjectIdentifier, len(elems)) - for i := range elems { - blockedRoles[i] = sdk.NewAccountObjectIdentifier(elems[i]) - } - set.WithBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) - } - - if d.HasChange("comment") { - set.WithComment(d.Get("comment").(string)) - } - - if d.HasChange("enabled") { - set.WithEnabled(d.Get("enabled").(bool)) - } - - if d.HasChange("oauth_issue_refresh_tokens") { - if v := d.Get("oauth_issue_refresh_tokens").(string); v != "unknown" { - parsed, err := strconv.ParseBool(v) - if err != nil { - return diag.FromErr(err) - } - set.WithOauthIssueRefreshTokens(parsed) - } else { - // TODO: fix - set.WithOauthIssueRefreshTokens(true) - } - } - - if d.HasChange("oauth_redirect_uri") { - set.WithOauthRedirectUri(d.Get("oauth_redirect_uri").(string)) - } - - if d.HasChange("oauth_refresh_token_validity") { - v := d.Get("oauth_refresh_token_validity").(int) - if v > 0 { - set.WithOauthRefreshTokenValidity(v) - } else { - // TODO: fix - // TODO: better logic, like in docs - set.WithOauthRefreshTokenValidity(7776000) - } - } - - if d.HasChange("oauth_use_secondary_roles") { - v := d.Get("oauth_use_secondary_roles").(string) - if len(v) > 0 { - value, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v) - if err != nil { - return diag.FromErr(err) - } - set.WithOauthUseSecondaryRoles(value) - } else { - unset.WithOauthUseSecondaryRoles(true) - } - } - if !reflect.DeepEqual(*set, sdk.OauthForPartnerApplicationsIntegrationSetRequest{}) { - if err := client.SecurityIntegrations.AlterOauthForPartnerApplications(ctx, sdk.NewAlterOauthForPartnerApplicationsSecurityIntegrationRequest(id).WithSet(*set)); err != nil { - return diag.FromErr(err) - } - } - if !reflect.DeepEqual(*unset, sdk.OauthForPartnerApplicationsIntegrationUnsetRequest{}) { - if err := client.SecurityIntegrations.AlterOauthForPartnerApplications(ctx, sdk.NewAlterOauthForPartnerApplicationsSecurityIntegrationRequest(id).WithUnset(*unset)); err != nil { - return diag.FromErr(err) - } - } - return ReadContextOauthIntegrationForPartnerApplications(ctx, d, meta) -} diff --git a/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go b/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go deleted file mode 100644 index edf0bcd9de..0000000000 --- a/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package resources_test - -import ( - "testing" - - acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - - "github.com/hashicorp/terraform-plugin-testing/config" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/tfversion" -) - -func TestAcc_OauthIntegrationForPartnerApplications_basic(t *testing.T) { - id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - role1, role1Cleanup := acc.TestClient().Role.CreateRole(t) - t.Cleanup(role1Cleanup) - - m := func(oauthClient string, complete bool, redirectUri *string) map[string]config.Variable { - c := map[string]config.Variable{ - "name": config.StringVariable(id.Name()), - "oauth_client": config.StringVariable(oauthClient), - } - if complete { - c["blocked_roles_list"] = config.SetVariable(config.StringVariable(role1.ID().Name())) - c["comment"] = config.StringVariable("foo") - c["enabled"] = config.BoolVariable(true) - c["oauth_issue_refresh_tokens"] = config.StringVariable("false") - c["oauth_refresh_token_validity"] = config.IntegerVariable(12345) - c["oauth_use_secondary_roles"] = config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)) - } - if redirectUri != nil { - c["oauth_redirect_uri"] = config.StringVariable(*redirectUri) - } - return c - } - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - Steps: []resource.TestStep{ - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/basic"), - ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), false, nil), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientTableauServer)), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_authorization_endpoints.#"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_token_endpoints.#"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_authorization_endpoint"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "enabled"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), - ), - }, - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server"), - ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), true, nil), - Check: resource.ComposeTestCheckFunc( - // TODO: proper check - // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.#", "1"), - // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.0", role1.ID().Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "comment", "foo"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "enabled", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientTableauServer)), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens", "false"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity", "12345"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), - ), - }, - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server"), - ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), true, nil), - ResourceName: "snowflake_oauth_integration_for_partner_applications.test", - ImportState: true, - ImportStateVerify: true, - }, - // unset - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/basic"), - ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientTableauServer), false, nil), - Check: resource.ComposeTestCheckFunc( - // TODO: proper check - // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.#", "0"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "comment", ""), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "enabled", "false"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientTableauServer)), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity", "7776000"), - - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), - ), - }, - // change client_type - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete_looker"), - ConfigVariables: m(string(sdk.OauthSecurityIntegrationClientLooker), true, sdk.Pointer("https://example.com")), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientLooker)), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_redirect_uri", "https://example.com"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), - ), - }, - }, - }) -} - -func TestAcc_OauthIntegrationForPartnerApplications_complete(t *testing.T) { - id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - validURL := "https://example.com" - role1, role1Cleanup := acc.TestClient().Role.CreateRole(t) - t.Cleanup(role1Cleanup) - - m := func() map[string]config.Variable { - return map[string]config.Variable{ - "blocked_roles_list": config.SetVariable(config.StringVariable(role1.ID().Name())), - "comment": config.StringVariable("foo"), - "enabled": config.BoolVariable(true), - "name": config.StringVariable(id.Name()), - "oauth_client": config.StringVariable(string(sdk.OauthSecurityIntegrationClientLooker)), - "oauth_issue_refresh_tokens": config.BoolVariable(true), - "oauth_redirect_uri": config.StringVariable(validURL), - "oauth_refresh_token_validity": config.IntegerVariable(12345), - "oauth_use_secondary_roles": config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), - } - } - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - Steps: []resource.TestStep{ - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete"), - ConfigVariables: m(), - Check: resource.ComposeTestCheckFunc( - // TODO: proper assert, also assert OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST - // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.#", "3"), - // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "blocked_roles_list.0", role1.ID().Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "comment", "foo"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "enabled", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "name", id.Name()), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_client", string(sdk.OauthSecurityIntegrationClientLooker)), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_issue_refresh_tokens", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_redirect_uri", validURL), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_refresh_token_validity", "12345"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_authorization_endpoints.#"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_allowed_token_endpoints.#"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_authorization_endpoint"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_token_endpoint"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "oauth_client_id"), - resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "created_on"), - ), - }, - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete"), - ConfigVariables: m(), - ResourceName: "snowflake_oauth_integration_for_partner_applications.test", - ImportState: true, - ImportStateVerify: true, - // ignore because this field is not returned from snowflake - ImportStateVerifyIgnore: []string{"oauth_redirect_uri"}, - }, - }, - }) -} - -func TestAcc_OauthIntegrationForPartnerApplications_invalid(t *testing.T) { - m := func() map[string]config.Variable { - return map[string]config.Variable{ - "blocked_roles_list": config.SetVariable(config.StringVariable("foo")), - "comment": config.StringVariable("foo"), - "enabled": config.BoolVariable(true), - "name": config.StringVariable("foo"), - "oauth_client": config.StringVariable("invalid"), - "oauth_issue_refresh_tokens": config.BoolVariable(true), - "oauth_redirect_uri": config.StringVariable("foo"), - "oauth_refresh_token_validity": config.IntegerVariable(1), - "oauth_use_secondary_roles": config.StringVariable("invalid"), - } - } - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - Steps: []resource.TestStep{ - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/complete"), - ConfigVariables: m(), - }, - }, - }) -} - -func TestAcc_OauthIntegrationForPartnerApplications_InvalidIncomplete(t *testing.T) { - m := func() map[string]config.Variable { - return map[string]config.Variable{ - "name": config.StringVariable("foo"), - } - } - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - ErrorCheck: helpers.AssertErrorContainsPartsFunc(t, []string{ - `The argument "oauth_client" is required, but no definition was found.`, - }), - Steps: []resource.TestStep{ - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForPartnerApplications/invalid"), - ConfigVariables: m(), - }, - }, - }) -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf deleted file mode 100644 index 77aab112a1..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/test.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "snowflake_oauth_integration_for_partner_applications" "test" { - name = var.name - oauth_client = var.oauth_client -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf deleted file mode 100644 index bb7c71f314..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/basic/variables.tf +++ /dev/null @@ -1,6 +0,0 @@ -variable "name" { - type = string -} -variable "oauth_client" { - type = string -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf deleted file mode 100644 index 87c2a43cb7..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/test.tf +++ /dev/null @@ -1,11 +0,0 @@ -resource "snowflake_oauth_integration_for_partner_applications" "test" { - blocked_roles_list = var.blocked_roles_list - comment = var.comment - enabled = var.enabled - name = var.name - oauth_client = var.oauth_client - oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens - oauth_redirect_uri = var.oauth_redirect_uri - oauth_refresh_token_validity = var.oauth_refresh_token_validity - oauth_use_secondary_roles = var.oauth_use_secondary_roles -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf deleted file mode 100644 index e16f09d94b..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete/variables.tf +++ /dev/null @@ -1,28 +0,0 @@ - -variable "blocked_roles_list" { - type = set(string) -} -variable "comment" { - type = string -} -variable "enabled" { - type = bool -} -variable "name" { - type = string -} -variable "oauth_client" { - type = string -} -variable "oauth_issue_refresh_tokens" { - type = bool -} -variable "oauth_redirect_uri" { - type = string -} -variable "oauth_refresh_token_validity" { - type = number -} -variable "oauth_use_secondary_roles" { - type = string -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf deleted file mode 100644 index 87c2a43cb7..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/test.tf +++ /dev/null @@ -1,11 +0,0 @@ -resource "snowflake_oauth_integration_for_partner_applications" "test" { - blocked_roles_list = var.blocked_roles_list - comment = var.comment - enabled = var.enabled - name = var.name - oauth_client = var.oauth_client - oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens - oauth_redirect_uri = var.oauth_redirect_uri - oauth_refresh_token_validity = var.oauth_refresh_token_validity - oauth_use_secondary_roles = var.oauth_use_secondary_roles -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf deleted file mode 100644 index e16f09d94b..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_looker/variables.tf +++ /dev/null @@ -1,28 +0,0 @@ - -variable "blocked_roles_list" { - type = set(string) -} -variable "comment" { - type = string -} -variable "enabled" { - type = bool -} -variable "name" { - type = string -} -variable "oauth_client" { - type = string -} -variable "oauth_issue_refresh_tokens" { - type = bool -} -variable "oauth_redirect_uri" { - type = string -} -variable "oauth_refresh_token_validity" { - type = number -} -variable "oauth_use_secondary_roles" { - type = string -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf deleted file mode 100644 index 87c2a43cb7..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/test.tf +++ /dev/null @@ -1,11 +0,0 @@ -resource "snowflake_oauth_integration_for_partner_applications" "test" { - blocked_roles_list = var.blocked_roles_list - comment = var.comment - enabled = var.enabled - name = var.name - oauth_client = var.oauth_client - oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens - oauth_redirect_uri = var.oauth_redirect_uri - oauth_refresh_token_validity = var.oauth_refresh_token_validity - oauth_use_secondary_roles = var.oauth_use_secondary_roles -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf deleted file mode 100644 index e16f09d94b..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server copy/variables.tf +++ /dev/null @@ -1,28 +0,0 @@ - -variable "blocked_roles_list" { - type = set(string) -} -variable "comment" { - type = string -} -variable "enabled" { - type = bool -} -variable "name" { - type = string -} -variable "oauth_client" { - type = string -} -variable "oauth_issue_refresh_tokens" { - type = bool -} -variable "oauth_redirect_uri" { - type = string -} -variable "oauth_refresh_token_validity" { - type = number -} -variable "oauth_use_secondary_roles" { - type = string -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf deleted file mode 100644 index bbfd609f49..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/test.tf +++ /dev/null @@ -1,10 +0,0 @@ -resource "snowflake_oauth_integration_for_partner_applications" "test" { - blocked_roles_list = var.blocked_roles_list - comment = var.comment - enabled = var.enabled - name = var.name - oauth_client = var.oauth_client - oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens - oauth_refresh_token_validity = var.oauth_refresh_token_validity - oauth_use_secondary_roles = var.oauth_use_secondary_roles -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf deleted file mode 100644 index 2dc336ee1f..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/complete_tableau_server/variables.tf +++ /dev/null @@ -1,25 +0,0 @@ - -variable "blocked_roles_list" { - type = set(string) -} -variable "comment" { - type = string -} -variable "enabled" { - type = bool -} -variable "name" { - type = string -} -variable "oauth_client" { - type = string -} -variable "oauth_issue_refresh_tokens" { - type = string -} -variable "oauth_refresh_token_validity" { - type = number -} -variable "oauth_use_secondary_roles" { - type = string -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf deleted file mode 100644 index dc0806c21b..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/test.tf +++ /dev/null @@ -1,3 +0,0 @@ -resource "snowflake_oauth_integration_for_partner_applications" "test" { - name = var.name -} diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf deleted file mode 100644 index 77e5cc9698..0000000000 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForPartnerApplications/invalid/variables.tf +++ /dev/null @@ -1,3 +0,0 @@ -variable "name" { - type = string -} From 336ace7335aef725fbe7d9940572cfedc5597863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Fri, 5 Jul 2024 11:00:22 +0200 Subject: [PATCH 3/6] wip --- .../import.sh | 1 + .../resource.tf | 8 ++ .../snowflake_saml_integration/import.sh | 2 +- .../snowflake_scim_integration/import.sh | 2 +- pkg/acceptance/importchecks/import_checks.go | 17 +++ .../oauth_integration_for_custom_clients.go | 108 ++++++++++-------- ...tion_for_custom_clients_acceptance_test.go | 43 +++---- .../scim_integration_acceptance_test.go | 2 +- .../complete/test.tf | 4 - .../oauth_integration_for_custom_clients.go | 10 +- ...urity_integrations_gen_integration_test.go | 2 +- 11 files changed, 106 insertions(+), 93 deletions(-) create mode 100644 examples/resources/snowflake_oauth_integration_for_custom_clients/import.sh create mode 100644 examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf diff --git a/examples/resources/snowflake_oauth_integration_for_custom_clients/import.sh b/examples/resources/snowflake_oauth_integration_for_custom_clients/import.sh new file mode 100644 index 0000000000..beeddc5d18 --- /dev/null +++ b/examples/resources/snowflake_oauth_integration_for_custom_clients/import.sh @@ -0,0 +1 @@ +terraform import snowflake_oauth_integration_for_custom_clients.example "name" diff --git a/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf b/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf new file mode 100644 index 0000000000..6e0f5683ce --- /dev/null +++ b/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf @@ -0,0 +1,8 @@ +resource "snowflake_oauth_integration_for_custom_clients" "tableau_desktop" { + name = "TABLEAU_DESKTOP" + oauth_client = "TABLEAU_DESKTOP" + enabled = true + oauth_issue_refresh_tokens = true + oauth_refresh_token_validity = 3600 + blocked_roles_list = ["SYSADMIN"] +} diff --git a/examples/resources/snowflake_saml_integration/import.sh b/examples/resources/snowflake_saml_integration/import.sh index a2356ebbdf..17e9bf907c 100644 --- a/examples/resources/snowflake_saml_integration/import.sh +++ b/examples/resources/snowflake_saml_integration/import.sh @@ -1 +1 @@ -terraform import snowflake_saml_integration.example name \ No newline at end of file +terraform import snowflake_saml_integration.example "name" \ No newline at end of file diff --git a/examples/resources/snowflake_scim_integration/import.sh b/examples/resources/snowflake_scim_integration/import.sh index 86e162e445..365c14b973 100644 --- a/examples/resources/snowflake_scim_integration/import.sh +++ b/examples/resources/snowflake_scim_integration/import.sh @@ -1 +1 @@ -terraform import snowflake_scim_integration.example name +terraform import snowflake_scim_integration.example "name" diff --git a/pkg/acceptance/importchecks/import_checks.go b/pkg/acceptance/importchecks/import_checks.go index ade9f7f2d8..f6682440b9 100644 --- a/pkg/acceptance/importchecks/import_checks.go +++ b/pkg/acceptance/importchecks/import_checks.go @@ -56,3 +56,20 @@ func TestCheckResourceAttrInstanceState(id string, attributeName, attributeValue return fmt.Errorf("attribute %s not found in instance state", attributeName) } } + +// TestCheckNoResourceAttrInstanceState checks if the value is present in the instatnce state and returns an error if it does. +func TestCheckNoResourceAttrInstanceState(id string, attributeName string) resource.ImportStateCheckFunc { + return func(is []*terraform.InstanceState) error { + for _, v := range is { + if v.ID != id { + continue + } + + if _, ok := v.Attributes[attributeName]; ok { + return fmt.Errorf("attribute %s found in instance state, but was not expected", attributeName) + } + } + + return nil + } +} diff --git a/pkg/resources/oauth_integration_for_custom_clients.go b/pkg/resources/oauth_integration_for_custom_clients.go index 2af44a18f3..c6610ed93d 100644 --- a/pkg/resources/oauth_integration_for_custom_clients.go +++ b/pkg/resources/oauth_integration_for_custom_clients.go @@ -4,21 +4,19 @@ import ( "context" "errors" "fmt" - "reflect" - "strconv" - "strings" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "reflect" + "strconv" + "strings" ) var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ @@ -29,11 +27,12 @@ var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ Description: "Specifies the name of the OAuth integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account.", }, "oauth_client_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: fmt.Sprintf("Specifies the type of client being registered. Snowflake supports both confidential and public clients. Valid options are: %v", sdk.AllOauthSecurityIntegrationClientTypes), - ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationClientTypes), true), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: sdkValidation(sdk.ToOauthSecurityIntegrationClientTypeOption), + DiffSuppressFunc: NormalizeAndCompare(sdk.ToOauthSecurityIntegrationClientTypeOption), + Description: fmt.Sprintf("Specifies the type of client being registered. Snowflake supports both confidential and public clients. Valid options are: %v", sdk.AllOauthSecurityIntegrationClientTypes), }, "oauth_redirect_uri": { Type: schema.TypeString, @@ -67,21 +66,28 @@ var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ "oauth_use_secondary_roles": { Type: schema.TypeString, Optional: true, + ValidateDiagFunc: sdkValidation(sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption), + DiffSuppressFunc: NormalizeAndCompare(sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption), Description: fmt.Sprintf("Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. Valid options are: %v", sdk.AllOauthSecurityIntegrationUseSecondaryRoles), - ValidateDiagFunc: StringInSlice(sdk.AsStringList(sdk.AllOauthSecurityIntegrationUseSecondaryRoles), true), }, "pre_authorized_roles_list": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, + Type: schema.TypeSet, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + }, Optional: true, - Description: "Comma-separated list of Snowflake roles that a user does not need to explicitly consent to using after authenticating.", + Description: "A set of Snowflake roles that a user does not need to explicitly consent to using after authenticating.", }, "blocked_roles_list": { Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), + }, // TODO(SNOW-1517937): Check if can make optional Required: true, - Description: "Comma-separated list of Snowflake roles that a user cannot explicitly consent to using after authenticating.", + Description: "A set of Snowflake roles that a user cannot explicitly consent to using after authenticating.", }, "oauth_issue_refresh_tokens": { Type: schema.TypeString, @@ -94,7 +100,7 @@ var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ "oauth_refresh_token_validity": { Type: schema.TypeInt, Optional: true, - Default: -1, + Default: IntDefault, ValidateFunc: validation.IntAtLeast(0), Description: "Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE.", }, @@ -105,14 +111,16 @@ var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](), }, "oauth_client_rsa_public_key": { - Type: schema.TypeString, - Optional: true, - Description: "Hash of `oauth_client_rsa_public_key` returned from Snowflake.", + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: ignoreTrimSpaceSuppressFunc, + Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers.", }, "oauth_client_rsa_public_key_2": { - Type: schema.TypeString, - Optional: true, - Description: "Hash of `oauth_client_rsa_public_key` returned from Snowflake.", + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: ignoreTrimSpaceSuppressFunc, + Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers.", }, "comment": { Type: schema.TypeString, @@ -193,7 +201,7 @@ func ImportOauthForCustomClientsIntegration(ctx context.Context, d *schema.Resou return nil, err } - if err = d.Set("enabled", strconv.FormatBool(integration.Enabled)); err != nil { + if err = d.Set("enabled", booleanStringFromBool(integration.Enabled)); err != nil { return nil, err } @@ -247,7 +255,7 @@ func CreateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem req := sdk.NewCreateOauthForCustomClientsSecurityIntegrationRequest(id, oauthClientType, d.Get("oauth_redirect_uri").(string)) if v := d.Get("enabled").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } @@ -255,7 +263,7 @@ func CreateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem } if v := d.Get("oauth_allow_non_tls_redirect_uri").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } @@ -263,15 +271,15 @@ func CreateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem } if v := d.Get("oauth_enforce_pkce").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } req.WithOauthEnforcePkce(parsedBool) } - if v := d.Get("oauth_use_secondary_roles").(string); len(v) > 0 { - oauthUseSecondaryRoles, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v) + if v, ok := d.GetOk("oauth_use_secondary_roles"); ok { + oauthUseSecondaryRoles, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v.(string)) if err != nil { return diag.FromErr(err) } @@ -297,14 +305,14 @@ func CreateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem } if v := d.Get("oauth_issue_refresh_tokens").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } req.WithOauthIssueRefreshTokens(parsedBool) } - if v := d.Get("oauth_refresh_token_validity").(int); v != -1 { + if v := d.Get("oauth_refresh_token_validity").(int); v != IntDefault { req.WithOauthRefreshTokenValidity(v) } @@ -430,7 +438,7 @@ func ReadContextOauthIntegrationForCustomClients(withExternalChangesMarking bool if withExternalChangesMarking { if err = handleExternalChangesToObjectInShow(d, - showMapping{"enabled", "enabled", integration.Enabled, strconv.FormatBool(integration.Enabled), nil}, + showMapping{"enabled", "enabled", integration.Enabled, booleanStringFromBool(integration.Enabled), nil}, ); err != nil { return diag.FromErr(err) } @@ -515,7 +523,7 @@ func UpdateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem if d.HasChange("enabled") { if v := d.Get("enabled").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } @@ -527,33 +535,33 @@ func UpdateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem if d.HasChange("oauth_allow_non_tls_redirect_uri") { if v := d.Get("oauth_allow_non_tls_redirect_uri").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } set.WithOauthAllowNonTlsRedirectUri(parsedBool) } else { - // No unset available for this field (setting Snowflake default) + // TODO(SNOW-1515781): No unset available for this field (setting Snowflake default) set.WithOauthAllowNonTlsRedirectUri(false) } } if d.HasChange("oauth_enforce_pkce") { if v := d.Get("oauth_enforce_pkce").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } set.WithOauthEnforcePkce(parsedBool) } else { - // No unset available for this field (setting Snowflake default) + // TODO(SNOW-1515781): No unset available for this field (setting Snowflake default) set.WithOauthEnforcePkce(false) } } if d.HasChange("oauth_use_secondary_roles") { - if v := d.Get("oauth_use_secondary_roles").(string); len(v) > 0 { - oauthUseSecondaryRoles, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v) + if v, ok := d.GetOk("oauth_use_secondary_roles"); ok { + oauthUseSecondaryRoles, err := sdk.ToOauthSecurityIntegrationUseSecondaryRolesOption(v.(string)) if err != nil { return diag.FromErr(err) } @@ -583,45 +591,45 @@ func UpdateContextOauthIntegrationForCustomClients(ctx context.Context, d *schem if d.HasChange("oauth_issue_refresh_tokens") { if v := d.Get("oauth_issue_refresh_tokens").(string); v != BooleanDefault { - parsedBool, err := strconv.ParseBool(v) + parsedBool, err := booleanStringToBool(v) if err != nil { return diag.FromErr(err) } set.WithOauthIssueRefreshTokens(parsedBool) } else { - // No unset available for this field (setting Snowflake default) + // TODO(SNOW-1515781): No unset available for this field (setting Snowflake default) set.WithOauthIssueRefreshTokens(true) } } if d.HasChange("oauth_refresh_token_validity") { - if v := d.Get("oauth_refresh_token_validity").(int); v != -1 { + if v := d.Get("oauth_refresh_token_validity").(int); v != IntDefault { set.WithOauthRefreshTokenValidity(v) } else { - // No unset available for this field (setting Snowflake default; 90 days in seconds) + // TODO(SNOW-1515781): No unset available for this field (setting Snowflake default; 90 days in seconds) set.WithOauthRefreshTokenValidity(7_776_000) } } if d.HasChange("network_policy") { - if v := d.Get("network_policy").(string); v != "" { - set.WithNetworkPolicy(sdk.NewAccountObjectIdentifier(v)) + if v, ok := d.GetOk("network_policy"); ok { + set.WithNetworkPolicy(sdk.NewAccountObjectIdentifier(v.(string))) } else { unset.WithNetworkPolicy(true) } } if d.HasChange("oauth_client_rsa_public_key") { - if v := d.Get("oauth_client_rsa_public_key").(string); v != "" { - set.WithOauthClientRsaPublicKey(v) + if v, ok := d.GetOk("oauth_client_rsa_public_key"); ok { + set.WithOauthClientRsaPublicKey(v.(string)) } else { unset.WithOauthClientRsaPublicKey(true) } } if d.HasChange("oauth_client_rsa_public_key_2") { - if v := d.Get("oauth_client_rsa_public_key_2").(string); v != "" { - set.WithOauthClientRsaPublicKey2(v) + if v, ok := d.GetOk("oauth_client_rsa_public_key_2"); ok { + set.WithOauthClientRsaPublicKey2(v.(string)) } else { unset.WithOauthClientRsaPublicKey2(true) } diff --git a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go index c6b45057ad..4638581abf 100644 --- a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go +++ b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go @@ -135,7 +135,8 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "7776000"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", ""), - // oauth_client_rsa_public_key and oauth_client_rsa_public_key_2 cannot be imported, because they're not available in SHOW and DESC outputs. + importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key"), + importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", ""), ), }, @@ -211,7 +212,8 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "86400"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework - // oauth_client_rsa_public_key and oauth_client_rsa_public_key_2 cannot be imported, because they're not available in SHOW and DESC outputs. + importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key"), + importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", comment), ), }, @@ -466,7 +468,8 @@ func TestAcc_OauthIntegrationForCustomClients_Complete(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "86400"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework - // oauth_client_rsa_public_key and oauth_client_rsa_public_key_2 cannot be imported, because they're not available in SHOW and DESC outputs. + importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key"), + importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", comment), ), }, @@ -612,10 +615,10 @@ func TestAcc_OauthIntegrationForCustomClients_DefaultValues(t *testing.T) { }) } -func TestAcc_OauthIntegrationForCustomClients_InvalidOauthUseSecondaryRoles(t *testing.T) { +func TestAcc_OauthIntegrationForCustomClients_Invalid(t *testing.T) { id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - configVariables := config.Variables{ + invalidUseSecondaryRoles := config.Variables{ "name": config.StringVariable(id.Name()), "oauth_client_type": config.StringVariable(string(sdk.OauthSecurityIntegrationClientTypeConfidential)), "oauth_redirect_uri": config.StringVariable("https://example.com"), @@ -623,26 +626,7 @@ func TestAcc_OauthIntegrationForCustomClients_InvalidOauthUseSecondaryRoles(t *t "blocked_roles_list": config.SetVariable(config.StringVariable("ACCOUNTADMIN"), config.StringVariable("SECURITYADMIN")), } - resource.Test(t, resource.TestCase{ - ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, - PreCheck: func() { acc.TestAccPreCheck(t) }, - TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(tfversion.Version1_5_0), - }, - Steps: []resource.TestStep{ - { - ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/invalid"), - ConfigVariables: configVariables, - ExpectError: regexp.MustCompile(`Error: expected \[{{} oauth_use_secondary_roles}] to be one of \["IMPLICIT" "NONE"], got invalid`), - }, - }, - }) -} - -func TestAcc_OauthIntegrationForCustomClients_InvalidClientType(t *testing.T) { - id := acc.TestClient().Ids.RandomAccountObjectIdentifier() - - configVariables := config.Variables{ + invalidClientType := config.Variables{ "name": config.StringVariable(id.Name()), "oauth_client_type": config.StringVariable("invalid"), "oauth_redirect_uri": config.StringVariable("https://example.com"), @@ -659,8 +643,13 @@ func TestAcc_OauthIntegrationForCustomClients_InvalidClientType(t *testing.T) { Steps: []resource.TestStep{ { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/invalid"), - ConfigVariables: configVariables, - ExpectError: regexp.MustCompile(`Error: expected oauth_client_type to be one of \["PUBLIC" "CONFIDENTIAL"], got invalid`), + ConfigVariables: invalidUseSecondaryRoles, + ExpectError: regexp.MustCompile(`Error: invalid OauthSecurityIntegrationUseSecondaryRolesOption: INVALID`), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/invalid"), + ConfigVariables: invalidClientType, + ExpectError: regexp.MustCompile(`Error: invalid OauthSecurityIntegrationClientTypeOption: INVALID`), }, }, }) diff --git a/pkg/resources/scim_integration_acceptance_test.go b/pkg/resources/scim_integration_acceptance_test.go index aa93f03a8a..e41646a72b 100644 --- a/pkg/resources/scim_integration_acceptance_test.go +++ b/pkg/resources/scim_integration_acceptance_test.go @@ -62,7 +62,7 @@ func TestAcc_ScimIntegration_basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_scim_integration.test", "run_as_role", role.Name()), resource.TestCheckNoResourceAttr("snowflake_scim_integration.test", "network_policy"), resource.TestCheckResourceAttr("snowflake_scim_integration.test", "sync_password", r.BooleanDefault), - resource.TestCheckNoResourceAttr("snowflake_scim_integration.test", "comment"), + resource.TestCheckResourceAttr("snowflake_scim_integration.test", "comment", ""), resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.#", "1"), resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.name", id.Name()), diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf index 626a09f295..72217d3178 100644 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf @@ -5,10 +5,6 @@ resource "snowflake_oauth_integration_for_custom_clients" "test" { name = var.name network_policy = var.network_policy oauth_allow_non_tls_redirect_uri = var.oauth_allow_non_tls_redirect_uri - # oauth_client_rsa_public_key { - # value = var.oauth_client_rsa_public_key - # } - oauth_client_rsa_public_key = var.oauth_client_rsa_public_key oauth_client_rsa_public_key_2 = var.oauth_client_rsa_public_key_2 oauth_client_type = var.oauth_client_type diff --git a/pkg/schemas/oauth_integration_for_custom_clients.go b/pkg/schemas/oauth_integration_for_custom_clients.go index 3f00b3e610..06f35e4106 100644 --- a/pkg/schemas/oauth_integration_for_custom_clients.go +++ b/pkg/schemas/oauth_integration_for_custom_clients.go @@ -33,6 +33,7 @@ var DescribeOauthIntegrationForCustomClients = map[string]*schema.Schema{ func DescribeOauthIntegrationForCustomClientsToSchema(integrationProperties []sdk.SecurityIntegrationProperty) map[string]any { propsSchema := make(map[string]any) for _, property := range integrationProperties { + property := property switch property.Name { case "OAUTH_CLIENT_TYPE", "OAUTH_REDIRECT_URI", @@ -53,14 +54,7 @@ func DescribeOauthIntegrationForCustomClientsToSchema(integrationProperties []sd "OAUTH_TOKEN_ENDPOINT", "OAUTH_ALLOWED_AUTHORIZATION_ENDPOINTS", "OAUTH_ALLOWED_TOKEN_ENDPOINTS": - propsSchema[strings.ToLower(property.Name)] = []map[string]any{ - { - "name": property.Name, - "type": property.Type, - "value": property.Value, - "default": property.Default, - }, - } + propsSchema[strings.ToLower(property.Name)] = []map[string]any{SecurityIntegrationPropertyToSchema(&property)} default: log.Printf("[WARN] unexpected property %v returned from Snowflake", property.Name) } diff --git a/pkg/sdk/testint/security_integrations_gen_integration_test.go b/pkg/sdk/testint/security_integrations_gen_integration_test.go index 0c2c94cec5..c7f6cdcd8b 100644 --- a/pkg/sdk/testint/security_integrations_gen_integration_test.go +++ b/pkg/sdk/testint/security_integrations_gen_integration_test.go @@ -135,7 +135,7 @@ func TestInt_SecurityIntegrations(t *testing.T) { } err := client.SecurityIntegrations.CreateSaml2(ctx, saml2Req) require.NoError(t, err) - // cleanupSecurityIntegration(t, id) + cleanupSecurityIntegration(t, id) integration, err := client.SecurityIntegrations.ShowByID(ctx, id) require.NoError(t, err) From a9bc7006e1b5bf6ea183d39b091ce10c16255e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Fri, 5 Jul 2024 11:56:40 +0200 Subject: [PATCH 4/6] changes after review --- MIGRATION_GUIDE.md | 8 +++ docs/index.md | 1 + .../oauth_integration_for_custom_clients.md | 49 ++++++++++++-- docs/resources/saml2_integration.md | 2 + examples/additional/deprecated_resources.MD | 2 +- .../resource.tf | 32 +++++++-- .../snowflake_saml_integration/import.sh | 2 +- pkg/acceptance/importchecks/import_checks.go | 33 ---------- .../oauth_integration_for_custom_clients.go | 11 ++-- ...tion_for_custom_clients_acceptance_test.go | 66 +++++++++++-------- .../complete/test.tf | 18 ++--- ...uth_integration_for_custom_clients.md.tmpl | 32 +++++++++ templates/resources/saml2_integration.md.tmpl | 32 +++++++++ 13 files changed, 199 insertions(+), 89 deletions(-) create mode 100644 templates/resources/oauth_integration_for_custom_clients.md.tmpl create mode 100644 templates/resources/saml2_integration.md.tmpl diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index bbd93e1eeb..01361dd640 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -20,6 +20,14 @@ They are all described in short in the [changes before v1 doc](./v1-preparations ### old grant resources removal Following the [announcement](https://github.com/Snowflake-Labs/terraform-provider-snowflake/discussions/2736) we have removed the old grant resources. The two resources [snowflake_role_ownership_grant](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_ownership_grant) and [snowflake_user_ownership_grant](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/user_ownership_grant) were not listed in the announcement, but they were also marked as deprecated ones. We are removing them too to conclude the grants redesign saga. +### *(new feature)* snowflake_oauth_integration_for_custom_clients and snowflake_oauth_integration_for_partner_applications resources + +To enhance clarity and functionality, the new resources `snowflake_oauth_integration_for_custom_clients` and `snowflake_oauth_integration_for_partner_applications` have been introduced +to replace the previous `snowflake_oauth_integration`. Recognizing that the old resource carried multiple responsibilities within a single entity, we opted to divide it into two more specialized resources. +The newly introduced resources are aligned with the latest Snowflake documentation at the time of implementation, and adhere to our [new conventions](#general-changes). +This segregation was based on the `oauth_client` attribute, where `CUSTOM` corresponds to `snowflake_oauth_integration_for_custom_clients`, +while other attributes align with `snowflake_oauth_integration_for_partner_applications`. + ### *(new feature)* snowflake_security_integrations datasource Added a new datasource enabling querying and filtering all types of security integrations. Notes: - all results are stored in `security_integrations` field. diff --git a/docs/index.md b/docs/index.md index 6ec1a994f7..0bc8493349 100644 --- a/docs/index.md +++ b/docs/index.md @@ -230,6 +230,7 @@ The Snowflake provider will use the following order of precedence when determini ## Currently deprecated resources - [snowflake_database_old](./docs/resources/database_old) +- [snowflake_oauth_integration](./docs/resources/oauth_integration) - [snowflake_saml_integration](./docs/resources/saml_integration) - use [snowflake_saml2_integration](./docs/resources/saml2_integration) instead - [snowflake_unsafe_execute](./docs/resources/unsafe_execute) diff --git a/docs/resources/oauth_integration_for_custom_clients.md b/docs/resources/oauth_integration_for_custom_clients.md index 2c4c6d0f7c..020b62a829 100644 --- a/docs/resources/oauth_integration_for_custom_clients.md +++ b/docs/resources/oauth_integration_for_custom_clients.md @@ -5,18 +5,49 @@ description: |- --- -# snowflake_oauth_integration_for_custom_clients (Resource) +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. +# snowflake_oauth_integration_for_custom_clients (Resource) +## Example Usage + +```terraform +# basic resource +resource "snowflake_oauth_integration_for_custom_clients" "basic" { + name = "saml_integration" + oauth_client_type = "CONFIDENTIAL" + oauth_redirect_uri = "https://example.com" + blocked_roles_list = ["ACCOUNTADMIN", "SECURITYADMIN"] +} + +# resource with all fields set +resource "snowflake_oauth_integration_for_custom_clients" "complete" { + name = "saml_integration" + oauth_client_type = "CONFIDENTIAL" + oauth_redirect_uri = "https://example.com" + enabled = "true" + oauth_allow_non_tls_redirect_uri = "true" + oauth_enforce_pkce = "true" + oauth_use_secondary_roles = "NONE" + pre_authorized_roles_list = ["role_id1", "role_id2"] + blocked_roles_list = ["ACCOUNTADMIN", "SECURITYADMIN", "role_id1", "role_id2"] + oauth_issue_refresh_tokens = "true" + oauth_refresh_token_validity = 87600 + network_policy = "network_policy_id" + oauth_client_rsa_public_key = file("rsa.pub") + oauth_client_rsa_public_key_2 = file("rsa2.pub") + comment = "my oauth integration" +} +``` ## Schema ### Required -- `blocked_roles_list` (Set of String) Comma-separated list of Snowflake roles that a user cannot explicitly consent to using after authenticating. +- `blocked_roles_list` (Set of String) A set of Snowflake roles that a user cannot explicitly consent to using after authenticating. - `name` (String) Specifies the name of the OAuth integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account. - `oauth_client_type` (String) Specifies the type of client being registered. Snowflake supports both confidential and public clients. Valid options are: [PUBLIC CONFIDENTIAL] - `oauth_redirect_uri` (String) Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. @@ -27,13 +58,13 @@ description: |- - `enabled` (String) Specifies whether this OAuth integration is enabled or disabled. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. - `network_policy` (String) Specifies an existing network policy. This network policy controls network traffic that is attempting to exchange an authorization code for an access or refresh token or to use a refresh token to obtain a new access token. - `oauth_allow_non_tls_redirect_uri` (String) If true, allows setting oauth_redirect_uri to a URI not protected by TLS. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. -- `oauth_client_rsa_public_key` (String) Hash of `oauth_client_rsa_public_key` returned from Snowflake. -- `oauth_client_rsa_public_key_2` (String) Hash of `oauth_client_rsa_public_key` returned from Snowflake. +- `oauth_client_rsa_public_key` (String) Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource using `terraform taint`. +- `oauth_client_rsa_public_key_2` (String) Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource using `terraform taint`. - `oauth_enforce_pkce` (String) Boolean that specifies whether Proof Key for Code Exchange (PKCE) should be required for the integration. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. - `oauth_issue_refresh_tokens` (String) Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. - `oauth_refresh_token_validity` (Number) Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE. - `oauth_use_secondary_roles` (String) Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. Valid options are: [IMPLICIT NONE] -- `pre_authorized_roles_list` (Set of String) Comma-separated list of Snowflake roles that a user does not need to explicitly consent to using after authenticating. +- `pre_authorized_roles_list` (Set of String) A set of Snowflake roles that a user does not need to explicitly consent to using after authenticating. ### Read-Only @@ -287,3 +318,11 @@ Read-Only: - `enabled` (Boolean) - `integration_type` (String) - `name` (String) + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_oauth_integration_for_custom_clients.example "name" +``` diff --git a/docs/resources/saml2_integration.md b/docs/resources/saml2_integration.md index 998bfab775..295163a695 100644 --- a/docs/resources/saml2_integration.md +++ b/docs/resources/saml2_integration.md @@ -5,6 +5,8 @@ description: |- --- +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + # snowflake_saml2_integration (Resource) diff --git a/examples/additional/deprecated_resources.MD b/examples/additional/deprecated_resources.MD index e7d1374747..e33ef71107 100644 --- a/examples/additional/deprecated_resources.MD +++ b/examples/additional/deprecated_resources.MD @@ -1,6 +1,6 @@ ## Currently deprecated resources - [snowflake_database_old](./docs/resources/database_old) -- [snowflake_saml_integration](./docs/resources/saml_integration) - use [snowflake_saml2_integration](./docs/resources/saml2_integration) instead - [snowflake_oauth_integration](./docs/resources/oauth_integration) +- [snowflake_saml_integration](./docs/resources/saml_integration) - use [snowflake_saml2_integration](./docs/resources/saml2_integration) instead - [snowflake_unsafe_execute](./docs/resources/unsafe_execute) diff --git a/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf b/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf index 6e0f5683ce..77f64e69ba 100644 --- a/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf +++ b/examples/resources/snowflake_oauth_integration_for_custom_clients/resource.tf @@ -1,8 +1,26 @@ -resource "snowflake_oauth_integration_for_custom_clients" "tableau_desktop" { - name = "TABLEAU_DESKTOP" - oauth_client = "TABLEAU_DESKTOP" - enabled = true - oauth_issue_refresh_tokens = true - oauth_refresh_token_validity = 3600 - blocked_roles_list = ["SYSADMIN"] +# basic resource +resource "snowflake_oauth_integration_for_custom_clients" "basic" { + name = "saml_integration" + oauth_client_type = "CONFIDENTIAL" + oauth_redirect_uri = "https://example.com" + blocked_roles_list = ["ACCOUNTADMIN", "SECURITYADMIN"] +} + +# resource with all fields set +resource "snowflake_oauth_integration_for_custom_clients" "complete" { + name = "saml_integration" + oauth_client_type = "CONFIDENTIAL" + oauth_redirect_uri = "https://example.com" + enabled = "true" + oauth_allow_non_tls_redirect_uri = "true" + oauth_enforce_pkce = "true" + oauth_use_secondary_roles = "NONE" + pre_authorized_roles_list = ["role_id1", "role_id2"] + blocked_roles_list = ["ACCOUNTADMIN", "SECURITYADMIN", "role_id1", "role_id2"] + oauth_issue_refresh_tokens = "true" + oauth_refresh_token_validity = 87600 + network_policy = "network_policy_id" + oauth_client_rsa_public_key = file("rsa.pub") + oauth_client_rsa_public_key_2 = file("rsa2.pub") + comment = "my oauth integration" } diff --git a/examples/resources/snowflake_saml_integration/import.sh b/examples/resources/snowflake_saml_integration/import.sh index 17e9bf907c..a2356ebbdf 100644 --- a/examples/resources/snowflake_saml_integration/import.sh +++ b/examples/resources/snowflake_saml_integration/import.sh @@ -1 +1 @@ -terraform import snowflake_saml_integration.example "name" \ No newline at end of file +terraform import snowflake_saml_integration.example name \ No newline at end of file diff --git a/pkg/acceptance/importchecks/import_checks.go b/pkg/acceptance/importchecks/import_checks.go index a83b2fc5b4..fe80b244cd 100644 --- a/pkg/acceptance/importchecks/import_checks.go +++ b/pkg/acceptance/importchecks/import_checks.go @@ -8,22 +8,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" ) -// ComposeAggregateImportStateCheck does the same as ComposeImportStateCheck, but it aggregates all the occurred errors, -// instead of returning the first encountered one. -func ComposeAggregateImportStateCheck(fs ...resource.ImportStateCheckFunc) resource.ImportStateCheckFunc { - return func(s []*terraform.InstanceState) error { - var result []error - - for i, f := range fs { - if err := f(s); err != nil { - result = append(result, fmt.Errorf("check %d/%d error: %w", i+1, len(fs), err)) - } - } - - return errors.Join(result...) - } -} - // ComposeImportStateCheck is based on unexported composeImportStateCheck from teststep_providers_test.go func ComposeImportStateCheck(fs ...resource.ImportStateCheckFunc) resource.ImportStateCheckFunc { return func(s []*terraform.InstanceState) error { @@ -108,20 +92,3 @@ func TestCheckResourceAttrInstanceStateSet(id string, attributeName string) reso return fmt.Errorf("attribute %s not found in instance state", attributeName) } } - -// TestCheckNoResourceAttrInstanceState checks if the value is present in the instatnce state and returns an error if it does. -func TestCheckNoResourceAttrInstanceState(id string, attributeName string) resource.ImportStateCheckFunc { - return func(is []*terraform.InstanceState) error { - for _, v := range is { - if v.ID != id { - continue - } - - if _, ok := v.Attributes[attributeName]; ok { - return fmt.Errorf("attribute %s found in instance state, but was not expected", attributeName) - } - } - - return nil - } -} diff --git a/pkg/resources/oauth_integration_for_custom_clients.go b/pkg/resources/oauth_integration_for_custom_clients.go index c6610ed93d..edd3aa9161 100644 --- a/pkg/resources/oauth_integration_for_custom_clients.go +++ b/pkg/resources/oauth_integration_for_custom_clients.go @@ -4,6 +4,10 @@ import ( "context" "errors" "fmt" + "reflect" + "strconv" + "strings" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" @@ -14,9 +18,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "reflect" - "strconv" - "strings" ) var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ @@ -114,13 +115,13 @@ var oauthIntegrationForCustomClientsSchema = map[string]*schema.Schema{ Type: schema.TypeString, Optional: true, DiffSuppressFunc: ignoreTrimSpaceSuppressFunc, - Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers.", + Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource using `terraform taint`.", }, "oauth_client_rsa_public_key_2": { Type: schema.TypeString, Optional: true, DiffSuppressFunc: ignoreTrimSpaceSuppressFunc, - Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers.", + Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource using `terraform taint`.", }, "comment": { Type: schema.TypeString, diff --git a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go index 4638581abf..e2d7327c12 100644 --- a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go +++ b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go @@ -4,6 +4,8 @@ import ( "regexp" "testing" + tfjson "github.com/hashicorp/terraform-json" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" @@ -54,7 +56,7 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { c["oauth_issue_refresh_tokens"] = config.BoolVariable(true) c["oauth_refresh_token_validity"] = config.IntegerVariable(86400) c["oauth_token_endpoint"] = config.StringVariable("http://auth.com") - c["oauth_use_secondary_roles"] = config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)) + c["oauth_use_secondary_roles"] = config.StringVariable(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)) c["pre_authorized_roles_list"] = config.SetVariable(config.StringVariable(preAuthorizedRole.ID().Name())) } return c @@ -135,8 +137,8 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "7776000"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", ""), - importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key"), - importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), + importchecks.TestCheckResourceAttrNotInInstanceState(id.Name(), "oauth_client_rsa_public_key"), + importchecks.TestCheckResourceAttrNotInInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", ""), ), }, @@ -144,6 +146,11 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/complete"), ConfigVariables: m(true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_oauth_integration_for_custom_clients.test", plancheck.ResourceActionUpdate), + }, + }, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), @@ -151,7 +158,7 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "1"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "3"), @@ -176,7 +183,7 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", preAuthorizedRole.ID().Name()), // Not asserted, because it also contains other default roles // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value"), @@ -212,8 +219,8 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "86400"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework - importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key"), - importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), + importchecks.TestCheckResourceAttrNotInInstanceState(id.Name(), "oauth_client_rsa_public_key"), + importchecks.TestCheckResourceAttrNotInInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", comment), ), }, @@ -233,20 +240,19 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { }, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - planchecks.PrintPlanDetails("snowflake_saml2_integration.test", "enabled", "network_policy", "oauth_use_secondary_roles", "oauth_client_rsa_public_key", "oauth_client_rsa_public_key_2"), - - // Not showing any drift and change, but alter is run underneath to set the values back - // planchecks.ExpectDrift("snowflake_saml2_integration.test", "enabled", sdk.String("true"), sdk.String("false")), - // planchecks.ExpectDrift("snowflake_saml2_integration.test", "network_policy", sdk.String(sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), sdk.String("")), - // planchecks.ExpectDrift("snowflake_saml2_integration.test", "oauth_use_secondary_roles", sdk.String("true"), sdk.String("false")), - // planchecks.ExpectDrift("snowflake_saml2_integration.test", "oauth_client_rsa_public_key", sdk.String(key), sdk.String("")), - // planchecks.ExpectDrift("snowflake_saml2_integration.test", "oauth_client_rsa_public_key_2", sdk.String(key), sdk.String("")), - - // planchecks.ExpectChange("snowflake_saml2_integration.test", "enabled", tfjson.ActionUpdate, sdk.String("false"), sdk.String("true")), - // planchecks.ExpectChange("snowflake_saml2_integration.test", "network_policy", tfjson.ActionUpdate, sdk.String(""), sdk.String(sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name())), - // planchecks.ExpectChange("snowflake_saml2_integration.test", "oauth_use_secondary_roles", tfjson.ActionUpdate, sdk.String("false"), sdk.String("true")), - // planchecks.ExpectChange("snowflake_saml2_integration.test", "oauth_client_rsa_public_key", tfjson.ActionUpdate, sdk.String(""), sdk.String(key)), - // planchecks.ExpectChange("snowflake_saml2_integration.test", "oauth_client_rsa_public_key_2", tfjson.ActionUpdate, sdk.String(""), sdk.String(key)), + plancheck.ExpectResourceAction("snowflake_oauth_integration_for_custom_clients.test", plancheck.ResourceActionUpdate), + + planchecks.ExpectDrift("snowflake_oauth_integration_for_custom_clients.test", "enabled", sdk.String("true"), sdk.String("false")), + planchecks.ExpectDrift("snowflake_oauth_integration_for_custom_clients.test", "network_policy", sdk.String(sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), sdk.String("")), + planchecks.ExpectDrift("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", sdk.String(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), sdk.String(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone))), + planchecks.ExpectDrift("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", sdk.String(key), sdk.String(key)), + planchecks.ExpectDrift("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", sdk.String(key), sdk.String(key)), + + planchecks.ExpectChange("snowflake_oauth_integration_for_custom_clients.test", "enabled", tfjson.ActionUpdate, sdk.String("false"), sdk.String("true")), + planchecks.ExpectChange("snowflake_oauth_integration_for_custom_clients.test", "network_policy", tfjson.ActionUpdate, sdk.String(""), sdk.String(sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name())), + planchecks.ExpectChange("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", tfjson.ActionUpdate, sdk.String(string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), sdk.String(string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit))), + planchecks.ExpectChange("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key", tfjson.ActionUpdate, sdk.String(key), sdk.String(key)), + planchecks.ExpectChange("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_rsa_public_key_2", tfjson.ActionUpdate, sdk.String(key), sdk.String(key)), }, }, Check: resource.ComposeAggregateTestCheckFunc( @@ -256,7 +262,7 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "enabled", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_allow_non_tls_redirect_uri", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_enforce_pkce", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesNone)), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_use_secondary_roles", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.#", "1"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "pre_authorized_roles_list.0", preAuthorizedRole.ID().Name()), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "blocked_roles_list.#", "3"), @@ -281,16 +287,15 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.enabled.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_allow_non_tls_redirect_uri.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_enforce_pkce.0.value", "true"), - resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_use_secondary_roles.0.value", string(sdk.OauthSecurityIntegrationUseSecondaryRolesImplicit)), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.pre_authorized_roles_list.0.value", preAuthorizedRole.ID().Name()), // Not asserted, because it also contains other default roles // resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.blocked_roles_list.0.value"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "86400"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework - // Change not detected - // resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value"), - // resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value"), + resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value"), + resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", comment), resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), @@ -303,6 +308,11 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { { ConfigDirectory: acc.ConfigurationDirectory("TestAcc_OauthIntegrationForCustomClients/basic"), ConfigVariables: m(false), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_oauth_integration_for_custom_clients.test", plancheck.ResourceActionUpdate), + }, + }, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "name", id.Name()), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "oauth_client_type", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), @@ -468,8 +478,8 @@ func TestAcc_OauthIntegrationForCustomClients_Complete(t *testing.T) { importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_issue_refresh_tokens", "true"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "oauth_refresh_token_validity", "86400"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework - importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key"), - importchecks.TestCheckNoResourceAttrInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), + importchecks.TestCheckResourceAttrNotInInstanceState(id.Name(), "oauth_client_rsa_public_key"), + importchecks.TestCheckResourceAttrNotInInstanceState(id.Name(), "oauth_client_rsa_public_key_2"), importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", comment), ), }, diff --git a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf index 72217d3178..8330c46690 100644 --- a/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf +++ b/pkg/resources/testdata/TestAcc_OauthIntegrationForCustomClients/complete/test.tf @@ -5,13 +5,13 @@ resource "snowflake_oauth_integration_for_custom_clients" "test" { name = var.name network_policy = var.network_policy oauth_allow_non_tls_redirect_uri = var.oauth_allow_non_tls_redirect_uri - oauth_client_rsa_public_key = var.oauth_client_rsa_public_key - oauth_client_rsa_public_key_2 = var.oauth_client_rsa_public_key_2 - oauth_client_type = var.oauth_client_type - oauth_enforce_pkce = var.oauth_enforce_pkce - oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens - oauth_redirect_uri = var.oauth_redirect_uri - oauth_refresh_token_validity = var.oauth_refresh_token_validity - oauth_use_secondary_roles = var.oauth_use_secondary_roles - pre_authorized_roles_list = var.pre_authorized_roles_list + oauth_client_rsa_public_key = var.oauth_client_rsa_public_key + oauth_client_rsa_public_key_2 = var.oauth_client_rsa_public_key_2 + oauth_client_type = var.oauth_client_type + oauth_enforce_pkce = var.oauth_enforce_pkce + oauth_issue_refresh_tokens = var.oauth_issue_refresh_tokens + oauth_redirect_uri = var.oauth_redirect_uri + oauth_refresh_token_validity = var.oauth_refresh_token_validity + oauth_use_secondary_roles = var.oauth_use_secondary_roles + pre_authorized_roles_list = var.pre_authorized_roles_list } diff --git a/templates/resources/oauth_integration_for_custom_clients.md.tmpl b/templates/resources/oauth_integration_for_custom_clients.md.tmpl new file mode 100644 index 0000000000..e7f66bbf91 --- /dev/null +++ b/templates/resources/oauth_integration_for_custom_clients.md.tmpl @@ -0,0 +1,32 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} diff --git a/templates/resources/saml2_integration.md.tmpl b/templates/resources/saml2_integration.md.tmpl new file mode 100644 index 0000000000..e7f66bbf91 --- /dev/null +++ b/templates/resources/saml2_integration.md.tmpl @@ -0,0 +1,32 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }} From 73805dcf658a4cb98ff9959e8ac59d48cfbed7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Mon, 8 Jul 2024 08:47:03 +0200 Subject: [PATCH 5/6] Test fix --- .../oauth_integration_for_custom_clients_acceptance_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go index e2d7327c12..2bc16b4af1 100644 --- a/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go +++ b/pkg/resources/oauth_integration_for_custom_clients_acceptance_test.go @@ -294,8 +294,8 @@ func TestAcc_OauthIntegrationForCustomClients_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_issue_refresh_tokens.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_refresh_token_validity.0.value", "86400"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.network_policy.0.value", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework - resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value"), - resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value"), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_fp.0.value", ""), + resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_rsa_public_key_2_fp.0.value", ""), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.comment.0.value", comment), resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_client_id.0.value"), resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_custom_clients.test", "describe_output.0.oauth_authorization_endpoint.0.value"), From 2f9bb42ed1b3212650fe74ad90aa7497fddf299b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Tue, 9 Jul 2024 11:51:00 +0200 Subject: [PATCH 6/6] Resolve merge conflicts --- pkg/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index bf101a81cb..ea1473e9f1 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -457,7 +457,7 @@ func getResources() map[string]*schema.Resource { "snowflake_notification_integration": resources.NotificationIntegration(), "snowflake_oauth_integration": resources.OAuthIntegration(), "snowflake_oauth_integration_for_partner_applications": resources.OauthIntegrationForPartnerApplications(), - "snowflake_oauth_integration_for_custom_clients": resources.OauthIntegrationForCustomClients(), + "snowflake_oauth_integration_for_custom_clients": resources.OauthIntegrationForCustomClients(), "snowflake_object_parameter": resources.ObjectParameter(), "snowflake_password_policy": resources.PasswordPolicy(), "snowflake_pipe": resources.Pipe(),