diff --git a/pkg/acceptance/helpers/random/certs.go b/pkg/acceptance/helpers/random/certs.go new file mode 100644 index 0000000000..aa23b530b4 --- /dev/null +++ b/pkg/acceptance/helpers/random/certs.go @@ -0,0 +1,47 @@ +package random + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// Generate X509 returns base64 encoded certificate on a single line without the leading -----BEGIN CERTIFICATE----- and ending -----END CERTIFICATE----- markers. +func GenerateX509(t *testing.T) string { + t.Helper() + ca := &x509.Certificate{ + SerialNumber: big.NewInt(1658), + Subject: pkix.Name{ + Organization: []string{"Company, INC."}, + }, + NotAfter: time.Now().AddDate(10, 0, 0), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + + caPrivKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + + caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) + require.NoError(t, err) + + certPEM := new(bytes.Buffer) + err = pem.Encode(certPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caBytes, + }) + require.NoError(t, err) + + cert := strings.TrimPrefix(certPEM.String(), "-----BEGIN CERTIFICATE-----\n") + cert = strings.TrimSuffix(cert, "-----END CERTIFICATE-----\n") + return cert +} diff --git a/pkg/acceptance/helpers/role_client.go b/pkg/acceptance/helpers/role_client.go index 8df0492a85..9e36712500 100644 --- a/pkg/acceptance/helpers/role_client.go +++ b/pkg/acceptance/helpers/role_client.go @@ -95,6 +95,19 @@ func (c *RoleClient) GrantRoleToUser(t *testing.T, id sdk.AccountObjectIdentifie require.NoError(t, err) } +func (c *RoleClient) GrantRoleToCurrentRole(t *testing.T, id sdk.AccountObjectIdentifier) { + t.Helper() + ctx := context.Background() + + currentRole, err := c.context.client.ContextFunctions.CurrentRole(ctx) + require.NoError(t, err) + + err = c.client().Grant(ctx, sdk.NewGrantRoleRequest(id, sdk.GrantRole{ + Role: sdk.Pointer(currentRole), + })) + require.NoError(t, err) +} + // TODO: move later to grants client func (c *RoleClient) GrantOwnershipOnAccountObject(t *testing.T, roleId sdk.AccountObjectIdentifier, objectId sdk.AccountObjectIdentifier, objectType sdk.ObjectType) { t.Helper() diff --git a/pkg/acceptance/helpers/security_integration_client.go b/pkg/acceptance/helpers/security_integration_client.go new file mode 100644 index 0000000000..0c37b0b48d --- /dev/null +++ b/pkg/acceptance/helpers/security_integration_client.go @@ -0,0 +1,72 @@ +package helpers + +import ( + "context" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/require" +) + +type SecurityIntegrationClient struct { + context *TestClientContext + ids *IdsGenerator +} + +func NewSecurityIntegrationClient(context *TestClientContext, idsGenerator *IdsGenerator) *SecurityIntegrationClient { + return &SecurityIntegrationClient{ + context: context, + ids: idsGenerator, + } +} + +func (c *SecurityIntegrationClient) client() sdk.SecurityIntegrations { + return c.context.client.SecurityIntegrations +} + +func (c *SecurityIntegrationClient) CreateSaml2(t *testing.T, id sdk.AccountObjectIdentifier) (*sdk.SecurityIntegration, func()) { + t.Helper() + return c.CreateSaml2WithRequest(t, sdk.NewCreateSaml2SecurityIntegrationRequest(id, false, c.ids.Alpha(), "https://example.com", "Custom", random.GenerateX509(t))) +} + +func (c *SecurityIntegrationClient) CreateSaml2WithRequest(t *testing.T, request *sdk.CreateSaml2SecurityIntegrationRequest) (*sdk.SecurityIntegration, func()) { + t.Helper() + ctx := context.Background() + + err := c.client().CreateSaml2(ctx, request) + require.NoError(t, err) + + si, err := c.client().ShowByID(ctx, request.GetName()) + require.NoError(t, err) + + return si, c.DropSecurityIntegrationFunc(t, request.GetName()) +} + +func (c *SecurityIntegrationClient) CreateScim(t *testing.T) (*sdk.SecurityIntegration, func()) { + t.Helper() + return c.CreateScimWithRequest(t, sdk.NewCreateScimSecurityIntegrationRequest(c.ids.RandomAccountObjectIdentifier(), false, sdk.ScimSecurityIntegrationScimClientGeneric, sdk.ScimSecurityIntegrationRunAsRoleGenericScimProvisioner)) +} + +func (c *SecurityIntegrationClient) CreateScimWithRequest(t *testing.T, request *sdk.CreateScimSecurityIntegrationRequest) (*sdk.SecurityIntegration, func()) { + t.Helper() + ctx := context.Background() + + err := c.client().CreateScim(ctx, request) + require.NoError(t, err) + + si, err := c.client().ShowByID(ctx, request.GetName()) + require.NoError(t, err) + + return si, c.DropSecurityIntegrationFunc(t, request.GetName()) +} + +func (c *SecurityIntegrationClient) DropSecurityIntegrationFunc(t *testing.T, id sdk.AccountObjectIdentifier) func() { + t.Helper() + ctx := context.Background() + + return func() { + err := c.client().Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id).WithIfExists(sdk.Bool(true))) + require.NoError(t, err) + } +} diff --git a/pkg/acceptance/helpers/test_client.go b/pkg/acceptance/helpers/test_client.go index 36ce573cd1..8cebf3621f 100644 --- a/pkg/acceptance/helpers/test_client.go +++ b/pkg/acceptance/helpers/test_client.go @@ -9,36 +9,37 @@ type TestClient struct { Ids *IdsGenerator - Account *AccountClient - Alert *AlertClient - ApiIntegration *ApiIntegrationClient - Application *ApplicationClient - ApplicationPackage *ApplicationPackageClient - Context *ContextClient - Database *DatabaseClient - DatabaseRole *DatabaseRoleClient - DynamicTable *DynamicTableClient - FailoverGroup *FailoverGroupClient - FileFormat *FileFormatClient - MaskingPolicy *MaskingPolicyClient - MaterializedView *MaterializedViewClient - NetworkPolicy *NetworkPolicyClient - Parameter *ParameterClient - PasswordPolicy *PasswordPolicyClient - Pipe *PipeClient - ResourceMonitor *ResourceMonitorClient - Role *RoleClient - RowAccessPolicy *RowAccessPolicyClient - Schema *SchemaClient - SessionPolicy *SessionPolicyClient - Share *ShareClient - Stage *StageClient - Table *TableClient - Tag *TagClient - Task *TaskClient - User *UserClient - View *ViewClient - Warehouse *WarehouseClient + Account *AccountClient + Alert *AlertClient + ApiIntegration *ApiIntegrationClient + Application *ApplicationClient + ApplicationPackage *ApplicationPackageClient + Context *ContextClient + Database *DatabaseClient + DatabaseRole *DatabaseRoleClient + DynamicTable *DynamicTableClient + FailoverGroup *FailoverGroupClient + FileFormat *FileFormatClient + MaskingPolicy *MaskingPolicyClient + MaterializedView *MaterializedViewClient + NetworkPolicy *NetworkPolicyClient + Parameter *ParameterClient + PasswordPolicy *PasswordPolicyClient + Pipe *PipeClient + ResourceMonitor *ResourceMonitorClient + Role *RoleClient + RowAccessPolicy *RowAccessPolicyClient + Schema *SchemaClient + SecurityIntegration *SecurityIntegrationClient + SessionPolicy *SessionPolicyClient + Share *ShareClient + Stage *StageClient + Table *TableClient + Tag *TagClient + Task *TaskClient + User *UserClient + View *ViewClient + Warehouse *WarehouseClient } func NewTestClient(c *sdk.Client, database string, schema string, warehouse string, testObjectSuffix string) *TestClient { @@ -55,36 +56,37 @@ func NewTestClient(c *sdk.Client, database string, schema string, warehouse stri Ids: idsGenerator, - Account: NewAccountClient(context), - Alert: NewAlertClient(context, idsGenerator), - ApiIntegration: NewApiIntegrationClient(context, idsGenerator), - Application: NewApplicationClient(context, idsGenerator), - ApplicationPackage: NewApplicationPackageClient(context, idsGenerator), - Context: NewContextClient(context), - Database: NewDatabaseClient(context, idsGenerator), - DatabaseRole: NewDatabaseRoleClient(context, idsGenerator), - DynamicTable: NewDynamicTableClient(context, idsGenerator), - FailoverGroup: NewFailoverGroupClient(context, idsGenerator), - FileFormat: NewFileFormatClient(context, idsGenerator), - MaskingPolicy: NewMaskingPolicyClient(context, idsGenerator), - MaterializedView: NewMaterializedViewClient(context, idsGenerator), - NetworkPolicy: NewNetworkPolicyClient(context, idsGenerator), - Parameter: NewParameterClient(context), - PasswordPolicy: NewPasswordPolicyClient(context, idsGenerator), - Pipe: NewPipeClient(context, idsGenerator), - ResourceMonitor: NewResourceMonitorClient(context, idsGenerator), - Role: NewRoleClient(context, idsGenerator), - RowAccessPolicy: NewRowAccessPolicyClient(context, idsGenerator), - Schema: NewSchemaClient(context, idsGenerator), - SessionPolicy: NewSessionPolicyClient(context, idsGenerator), - Share: NewShareClient(context, idsGenerator), - Stage: NewStageClient(context, idsGenerator), - Table: NewTableClient(context, idsGenerator), - Tag: NewTagClient(context, idsGenerator), - Task: NewTaskClient(context, idsGenerator), - User: NewUserClient(context, idsGenerator), - View: NewViewClient(context, idsGenerator), - Warehouse: NewWarehouseClient(context, idsGenerator), + Account: NewAccountClient(context), + Alert: NewAlertClient(context, idsGenerator), + ApiIntegration: NewApiIntegrationClient(context, idsGenerator), + Application: NewApplicationClient(context, idsGenerator), + ApplicationPackage: NewApplicationPackageClient(context, idsGenerator), + Context: NewContextClient(context), + Database: NewDatabaseClient(context, idsGenerator), + DatabaseRole: NewDatabaseRoleClient(context, idsGenerator), + DynamicTable: NewDynamicTableClient(context, idsGenerator), + FailoverGroup: NewFailoverGroupClient(context, idsGenerator), + FileFormat: NewFileFormatClient(context, idsGenerator), + MaskingPolicy: NewMaskingPolicyClient(context, idsGenerator), + MaterializedView: NewMaterializedViewClient(context, idsGenerator), + NetworkPolicy: NewNetworkPolicyClient(context, idsGenerator), + Parameter: NewParameterClient(context), + PasswordPolicy: NewPasswordPolicyClient(context, idsGenerator), + Pipe: NewPipeClient(context, idsGenerator), + ResourceMonitor: NewResourceMonitorClient(context, idsGenerator), + Role: NewRoleClient(context, idsGenerator), + RowAccessPolicy: NewRowAccessPolicyClient(context, idsGenerator), + Schema: NewSchemaClient(context, idsGenerator), + SecurityIntegration: NewSecurityIntegrationClient(context, idsGenerator), + SessionPolicy: NewSessionPolicyClient(context, idsGenerator), + Share: NewShareClient(context, idsGenerator), + Stage: NewStageClient(context, idsGenerator), + Table: NewTableClient(context, idsGenerator), + Tag: NewTagClient(context, idsGenerator), + Task: NewTaskClient(context, idsGenerator), + User: NewUserClient(context, idsGenerator), + View: NewViewClient(context, idsGenerator), + Warehouse: NewWarehouseClient(context, idsGenerator), } } diff --git a/pkg/internal/snowflakeroles/snowflake_predefined_roles.go b/pkg/internal/snowflakeroles/snowflake_predefined_roles.go index 065b624694..067abc4fe7 100644 --- a/pkg/internal/snowflakeroles/snowflake_predefined_roles.go +++ b/pkg/internal/snowflakeroles/snowflake_predefined_roles.go @@ -3,6 +3,7 @@ package snowflakeroles import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" var ( - Orgadmin = sdk.NewAccountObjectIdentifier("ORGADMIN") - Accountadmin = sdk.NewAccountObjectIdentifier("ACCOUNTADMIN") + Orgadmin = sdk.NewAccountObjectIdentifier("ORGADMIN") + Accountadmin = sdk.NewAccountObjectIdentifier("ACCOUNTADMIN") + GenericScimProvisioner = sdk.NewAccountObjectIdentifier("GENERIC_SCIM_PROVISIONER") ) diff --git a/pkg/sdk/client.go b/pkg/sdk/client.go index d854b3110b..f0dcba5db8 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -71,6 +71,7 @@ type Client struct { Roles Roles RowAccessPolicies RowAccessPolicies Schemas Schemas + SecurityIntegrations SecurityIntegrations Sequences Sequences SessionPolicies SessionPolicies Sessions Sessions @@ -226,6 +227,7 @@ func (c *Client) initialize() { c.Roles = &roles{client: c} c.RowAccessPolicies = &rowAccessPolicies{client: c} c.Schemas = &schemas{client: c} + c.SecurityIntegrations = &securityIntegrations{client: c} c.Sequences = &sequences{client: c} c.SessionPolicies = &sessionPolicies{client: c} c.Sessions = &sessions{client: c} diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index 385abdb7a7..9944d51f2b 100644 --- a/pkg/sdk/parameters.go +++ b/pkg/sdk/parameters.go @@ -64,6 +64,12 @@ func (parameters *parameters) SetAccountParameter(ctx context.Context, parameter return fmt.Errorf("CLIENT_ENCRYPTION_KEY_SIZE session parameter is an integer, got %v", value) } opts.Set.Parameters.AccountParameters.ClientEncryptionKeySize = Pointer(v) + case AccountParameterEnableIdentifierFirstLogin: + b, err := parseBooleanParameter(string(parameter), value) + if err != nil { + return err + } + opts.Set.Parameters.AccountParameters.EnableIdentifierFirstLogin = b case AccountParameterEnableInternalStagesPrivatelink: b, err := parseBooleanParameter(string(parameter), value) if err != nil { @@ -328,6 +334,7 @@ const ( AccountParameterAllowClientMFACaching AccountParameter = "ALLOW_CLIENT_MFA_CACHING" AccountParameterAllowIDToken AccountParameter = "ALLOW_ID_TOKEN" // #nosec G101 AccountParameterClientEncryptionKeySize AccountParameter = "CLIENT_ENCRYPTION_KEY_SIZE" + AccountParameterEnableIdentifierFirstLogin AccountParameter = "ENABLE_IDENTIFIER_FIRST_LOGIN" AccountParameterEnableInternalStagesPrivatelink AccountParameter = "ENABLE_INTERNAL_STAGES_PRIVATELINK" AccountParameterEnableTriSecretAndRekeyOptOutForImageRepository AccountParameter = "ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_IMAGE_REPOSITORY" // #nosec G101 AccountParameterEnableTriSecretAndRekeyOptOutForSpcsBlockStorage AccountParameter = "ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_SPCS_BLOCK_STORAGE" // #nosec G101 @@ -524,6 +531,7 @@ type AccountParameters struct { AllowClientMFACaching *bool `ddl:"parameter" sql:"ALLOW_CLIENT_MFA_CACHING"` AllowIDToken *bool `ddl:"parameter" sql:"ALLOW_ID_TOKEN"` ClientEncryptionKeySize *int `ddl:"parameter" sql:"CLIENT_ENCRYPTION_KEY_SIZE"` + EnableIdentifierFirstLogin *bool `ddl:"parameter" sql:"ENABLE_IDENTIFIER_FIRST_LOGIN"` EnableInternalStagesPrivatelink *bool `ddl:"parameter" sql:"ENABLE_INTERNAL_STAGES_PRIVATELINK"` EnableUnredactedQuerySyntaxError *bool `ddl:"parameter" sql:"ENABLE_UNREDACTED_QUERY_SYNTAX_ERROR"` EnableTriSecretAndRekeyOptOutForImageRepository *bool `ddl:"parameter" sql:"ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_IMAGE_REPOSITORY"` @@ -567,6 +575,7 @@ type AccountParametersUnset struct { AllowClientMFACaching *bool `ddl:"keyword" sql:"ALLOW_CLIENT_MFA_CACHING"` AllowIDToken *bool `ddl:"keyword" sql:"ALLOW_ID_TOKEN"` ClientEncryptionKeySize *bool `ddl:"keyword" sql:"CLIENT_ENCRYPTION_KEY_SIZE"` + EnableIdentifierFirstLogin *bool `ddl:"keyword" sql:"ENABLE_IDENTIFIER_FIRST_LOGIN"` EnableInternalStagesPrivatelink *bool `ddl:"keyword" sql:"ENABLE_INTERNAL_STAGES_PRIVATELINK"` EnableTriSecretAndRekeyOptOutForImageRepository *bool `ddl:"keyword" sql:"ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_IMAGE_REPOSITORY"` EnableTriSecretAndRekeyOptOutForSpcsBlockStorage *bool `ddl:"keyword" sql:"ENABLE_TRI_SECRET_AND_REKEY_OPT_OUT_FOR_SPCS_BLOCK_STORAGE"` diff --git a/pkg/sdk/poc/generator/operation.go b/pkg/sdk/poc/generator/operation.go index c62f99712a..8c8bd84342 100644 --- a/pkg/sdk/poc/generator/operation.go +++ b/pkg/sdk/poc/generator/operation.go @@ -170,6 +170,6 @@ func (i *Interface) DescribeOperation(describeKind DescriptionMappingKind, doc s return i } -func (i *Interface) CustomOperation(kind string, doc string, queryStruct *QueryStruct) *Interface { - return i.newSimpleOperation(kind, doc, queryStruct) +func (i *Interface) CustomOperation(kind string, doc string, queryStruct *QueryStruct, helperStructs ...IntoField) *Interface { + return i.newSimpleOperation(kind, doc, queryStruct, helperStructs...) } diff --git a/pkg/sdk/poc/main.go b/pkg/sdk/poc/main.go index d626fe16af..b4d13e106a 100644 --- a/pkg/sdk/poc/main.go +++ b/pkg/sdk/poc/main.go @@ -39,6 +39,7 @@ var definitionMapping = map[string]*generator.Interface{ "external_functions_def.go": sdk.ExternalFunctionsDef, "streamlits_def.go": sdk.StreamlitsDef, "network_rule_def.go": sdk.NetworkRuleDef, + "security_integrations_def.go": sdk.SecurityIntegrationsDef, } func main() { diff --git a/pkg/sdk/security_integrations_def.go b/pkg/sdk/security_integrations_def.go new file mode 100644 index 0000000000..d2b5e47e90 --- /dev/null +++ b/pkg/sdk/security_integrations_def.go @@ -0,0 +1,230 @@ +package sdk + +import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" + +//go:generate go run ./poc/main.go + +type ScimSecurityIntegrationScimClientOption string + +var ( + ScimSecurityIntegrationScimClientOkta ScimSecurityIntegrationScimClientOption = "OKTA" + ScimSecurityIntegrationScimClientAzure ScimSecurityIntegrationScimClientOption = "AZURE" + ScimSecurityIntegrationScimClientGeneric ScimSecurityIntegrationScimClientOption = "GENERIC" +) + +type ScimSecurityIntegrationRunAsRoleOption string + +var ( + ScimSecurityIntegrationRunAsRoleOktaProvisioner ScimSecurityIntegrationRunAsRoleOption = "OKTA_PROVISIONER" + ScimSecurityIntegrationRunAsRoleAadProvisioner ScimSecurityIntegrationRunAsRoleOption = "AAD_PROVISIONER" + ScimSecurityIntegrationRunAsRoleGenericScimProvisioner ScimSecurityIntegrationRunAsRoleOption = "GENERIC_SCIM_PROVISIONER" +) + +var ( + userDomainDef = g.NewQueryStruct("UserDomain").Text("Domain", g.KeywordOptions().SingleQuotes().Required()) + emailPatternDef = g.NewQueryStruct("EmailPattern").Text("Pattern", g.KeywordOptions().SingleQuotes().Required()) +) + +func createSecurityIntegrationOperation(structName string, apply func(qs *g.QueryStruct) *g.QueryStruct) *g.QueryStruct { + qs := g.NewQueryStruct(structName). + Create(). + OrReplace(). + SQL("SECURITY INTEGRATION"). + IfNotExists(). + Name() + qs = apply(qs) + return qs. + OptionalComment(). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists") +} + +func alterSecurityIntegrationOperation(structName string, apply func(qs *g.QueryStruct) *g.QueryStruct) *g.QueryStruct { + qs := g.NewQueryStruct(structName). + Alter(). + SQL("SECURITY INTEGRATION"). + IfExists(). + Name(). + OptionalSetTags(). + OptionalUnsetTags(). + WithValidation(g.ValidIdentifier, "name") + qs = apply(qs) + return qs +} + +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()). + OptionalTextAssignment("SAML2_X509_CERT", g.ParameterOptions().SingleQuotes()). + ListAssignment("ALLOWED_USER_DOMAINS", "UserDomain", g.ParameterOptions().Parentheses()). + ListAssignment("ALLOWED_EMAIL_PATTERNS", "EmailPattern", g.ParameterOptions().Parentheses()). + OptionalTextAssignment("SAML2_SP_INITIATED_LOGIN_PAGE_LABEL", g.ParameterOptions().SingleQuotes()). + 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()). + OptionalTextAssignment("SAML2_POST_LOGOUT_REDIRECT_URL", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("SAML2_FORCE_AUTHN", g.ParameterOptions()). + OptionalTextAssignment("SAML2_SNOWFLAKE_ISSUER_URL", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("SAML2_SNOWFLAKE_ACS_URL", g.ParameterOptions().SingleQuotes()). + OptionalComment(). + WithValidation(g.AtLeastOneValueSet, "Enabled", "Saml2Issuer", "Saml2SsoUrl", "Saml2Provider", "Saml2X509Cert", "AllowedUserDomains", "AllowedEmailPatterns", + "Saml2SpInitiatedLoginPageLabel", "Saml2EnableSpInitiated", "Saml2SnowflakeX509Cert", "Saml2SignRequest", "Saml2RequestedNameidFormat", "Saml2PostLogoutRedirectUrl", + "Saml2ForceAuthn", "Saml2SnowflakeIssuerUrl", "Saml2SnowflakeAcsUrl", "Comment") + +var saml2IntegrationUnsetDef = g.NewQueryStruct("Saml2IntegrationUnset"). + OptionalSQL("SAML2_FORCE_AUTHN"). + OptionalSQL("SAML2_REQUESTED_NAMEID_FORMAT"). + OptionalSQL("SAML2_POST_LOGOUT_REDIRECT_URL"). + OptionalSQL("COMMENT"). + WithValidation(g.AtLeastOneValueSet, "Saml2ForceAuthn", "Saml2RequestedNameidFormat", "Saml2PostLogoutRedirectUrl", "Comment") + +var scimIntegrationSetDef = g.NewQueryStruct("ScimIntegrationSet"). + OptionalBooleanAssignment("ENABLED", g.ParameterOptions()). + OptionalIdentifier("NetworkPolicy", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Equals().SQL("NETWORK_POLICY")). + OptionalBooleanAssignment("SYNC_PASSWORD", g.ParameterOptions()). + OptionalComment(). + WithValidation(g.AtLeastOneValueSet, "Enabled", "NetworkPolicy", "SyncPassword", "Comment") + +var scimIntegrationUnsetDef = g.NewQueryStruct("ScimIntegrationUnset"). + OptionalSQL("ENABLED"). + OptionalSQL("NETWORK_POLICY"). + OptionalSQL("SYNC_PASSWORD"). + OptionalSQL("COMMENT"). + WithValidation(g.AtLeastOneValueSet, "Enabled", "NetworkPolicy", "SyncPassword", "Comment") + +var SecurityIntegrationsDef = g.NewInterface( + "SecurityIntegrations", + "SecurityIntegration", + g.KindOfT[AccountObjectIdentifier](), +). + CustomOperation( + "CreateSaml2", + "https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-saml2", + createSecurityIntegrationOperation("CreateSaml2", func(qs *g.QueryStruct) *g.QueryStruct { + return qs. + PredefinedQueryStructField("integrationType", "string", g.StaticOptions().SQL("TYPE = SAML2")). + BooleanAssignment("ENABLED", g.ParameterOptions().Required()). + TextAssignment("SAML2_ISSUER", g.ParameterOptions().Required().SingleQuotes()). + TextAssignment("SAML2_SSO_URL", g.ParameterOptions().Required().SingleQuotes()). + TextAssignment("SAML2_PROVIDER", 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()). + OptionalTextAssignment("SAML2_SP_INITIATED_LOGIN_PAGE_LABEL", g.ParameterOptions().SingleQuotes()). + 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()). + OptionalTextAssignment("SAML2_POST_LOGOUT_REDIRECT_URL", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("SAML2_FORCE_AUTHN", g.ParameterOptions()). + OptionalTextAssignment("SAML2_SNOWFLAKE_ISSUER_URL", g.ParameterOptions().SingleQuotes()). + OptionalTextAssignment("SAML2_SNOWFLAKE_ACS_URL", g.ParameterOptions().SingleQuotes()) + }), + userDomainDef, + emailPatternDef, + ). + CustomOperation( + "CreateScim", + "https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-scim", + createSecurityIntegrationOperation("CreateScim", func(qs *g.QueryStruct) *g.QueryStruct { + return qs. + PredefinedQueryStructField("integrationType", "string", g.StaticOptions().SQL("TYPE = SCIM")). + BooleanAssignment("ENABLED", g.ParameterOptions().Required()). + Assignment( + "SCIM_CLIENT", + g.KindOfT[ScimSecurityIntegrationScimClientOption](), + g.ParameterOptions().SingleQuotes().Required(), + ). + Assignment( + "RUN_AS_ROLE", + g.KindOfT[ScimSecurityIntegrationRunAsRoleOption](), + g.ParameterOptions().SingleQuotes().Required(), + ). + OptionalIdentifier("NetworkPolicy", g.KindOfT[AccountObjectIdentifier](), g.IdentifierOptions().Equals().SQL("NETWORK_POLICY")). + OptionalBooleanAssignment("SYNC_PASSWORD", g.ParameterOptions()) + }), + ). + CustomOperation( + "AlterSaml2", + "https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-saml2", + alterSecurityIntegrationOperation("AlterSaml2", func(qs *g.QueryStruct) *g.QueryStruct { + return qs.OptionalQueryStructField( + "Set", + saml2IntegrationSetDef, + g.ListOptions().NoParentheses().SQL("SET"), + ).OptionalQueryStructField( + "Unset", + saml2IntegrationUnsetDef, + g.ListOptions().NoParentheses().SQL("UNSET"), + ).OptionalSQL("REFRESH SAML2_SNOWFLAKE_PRIVATE_KEY"). + WithValidation(g.ExactlyOneValueSet, "Set", "Unset", "RefreshSaml2SnowflakePrivateKey", "SetTags", "UnsetTags") + }), + ). + CustomOperation( + "AlterScim", + "https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-scim", + alterSecurityIntegrationOperation("AlterScim", func(qs *g.QueryStruct) *g.QueryStruct { + return qs.OptionalQueryStructField( + "Set", + scimIntegrationSetDef, + g.ListOptions().NoParentheses().SQL("SET"), + ).OptionalQueryStructField( + "Unset", + scimIntegrationUnsetDef, + g.ListOptions().NoParentheses().SQL("UNSET"), + ).WithValidation(g.ExactlyOneValueSet, "Set", "Unset", "SetTags", "UnsetTags") + }), + ). + DropOperation( + "https://docs.snowflake.com/en/sql-reference/sql/drop-integration", + g.NewQueryStruct("DropSecurityIntegration"). + Drop(). + SQL("SECURITY INTEGRATION"). + IfExists(). + Name(). + WithValidation(g.ValidIdentifier, "name"), + ). + DescribeOperation( + g.DescriptionMappingKindSlice, + "https://docs.snowflake.com/en/sql-reference/sql/desc-integration", + g.DbStruct("securityIntegrationDescRow"). + Field("property", "string"). + Field("property_type", "string"). + Field("property_value", "string"). + Field("property_default", "string"), + g.PlainStruct("SecurityIntegrationProperty"). + Field("Name", "string"). + Field("Type", "string"). + Field("Value", "string"). + Field("Default", "string"), + g.NewQueryStruct("DescSecurityIntegration"). + Describe(). + SQL("SECURITY INTEGRATION"). + Name(). + WithValidation(g.ValidIdentifier, "name"), + ). + ShowOperation( + "https://docs.snowflake.com/en/sql-reference/sql/show-integrations", + g.DbStruct("securityIntegrationShowRow"). + Text("name"). + Text("type"). + Text("category"). + Bool("enabled"). + OptionalText("comment"). + Time("created_on"), + g.PlainStruct("SecurityIntegration"). + Text("Name"). + Text("IntegrationType"). + Text("Category"). + Bool("Enabled"). + Text("Comment"). + Time("CreatedOn"), + g.NewQueryStruct("ShowSecurityIntegrations"). + Show(). + SQL("SECURITY INTEGRATIONS"). + OptionalLike(), + ). + ShowByIdOperation() diff --git a/pkg/sdk/security_integrations_dto_builders_gen.go b/pkg/sdk/security_integrations_dto_builders_gen.go new file mode 100644 index 0000000000..d2a70ed326 --- /dev/null +++ b/pkg/sdk/security_integrations_dto_builders_gen.go @@ -0,0 +1,394 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewCreateSaml2SecurityIntegrationRequest( + name AccountObjectIdentifier, + Enabled bool, + Saml2Issuer string, + Saml2SsoUrl string, + Saml2Provider string, + Saml2X509Cert string, +) *CreateSaml2SecurityIntegrationRequest { + s := CreateSaml2SecurityIntegrationRequest{} + s.name = name + s.Enabled = Enabled + s.Saml2Issuer = Saml2Issuer + s.Saml2SsoUrl = Saml2SsoUrl + s.Saml2Provider = Saml2Provider + s.Saml2X509Cert = Saml2X509Cert + return &s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithOrReplace(OrReplace *bool) *CreateSaml2SecurityIntegrationRequest { + s.OrReplace = OrReplace + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithIfNotExists(IfNotExists *bool) *CreateSaml2SecurityIntegrationRequest { + s.IfNotExists = IfNotExists + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithAllowedUserDomains(AllowedUserDomains []UserDomain) *CreateSaml2SecurityIntegrationRequest { + s.AllowedUserDomains = AllowedUserDomains + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithAllowedEmailPatterns(AllowedEmailPatterns []EmailPattern) *CreateSaml2SecurityIntegrationRequest { + s.AllowedEmailPatterns = AllowedEmailPatterns + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2SpInitiatedLoginPageLabel(Saml2SpInitiatedLoginPageLabel *string) *CreateSaml2SecurityIntegrationRequest { + s.Saml2SpInitiatedLoginPageLabel = Saml2SpInitiatedLoginPageLabel + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2EnableSpInitiated(Saml2EnableSpInitiated *bool) *CreateSaml2SecurityIntegrationRequest { + s.Saml2EnableSpInitiated = Saml2EnableSpInitiated + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2SnowflakeX509Cert(Saml2SnowflakeX509Cert *string) *CreateSaml2SecurityIntegrationRequest { + s.Saml2SnowflakeX509Cert = Saml2SnowflakeX509Cert + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2SignRequest(Saml2SignRequest *bool) *CreateSaml2SecurityIntegrationRequest { + s.Saml2SignRequest = Saml2SignRequest + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2RequestedNameidFormat(Saml2RequestedNameidFormat *string) *CreateSaml2SecurityIntegrationRequest { + s.Saml2RequestedNameidFormat = Saml2RequestedNameidFormat + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2PostLogoutRedirectUrl(Saml2PostLogoutRedirectUrl *string) *CreateSaml2SecurityIntegrationRequest { + s.Saml2PostLogoutRedirectUrl = Saml2PostLogoutRedirectUrl + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2ForceAuthn(Saml2ForceAuthn *bool) *CreateSaml2SecurityIntegrationRequest { + s.Saml2ForceAuthn = Saml2ForceAuthn + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2SnowflakeIssuerUrl(Saml2SnowflakeIssuerUrl *string) *CreateSaml2SecurityIntegrationRequest { + s.Saml2SnowflakeIssuerUrl = Saml2SnowflakeIssuerUrl + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithSaml2SnowflakeAcsUrl(Saml2SnowflakeAcsUrl *string) *CreateSaml2SecurityIntegrationRequest { + s.Saml2SnowflakeAcsUrl = Saml2SnowflakeAcsUrl + return s +} + +func (s *CreateSaml2SecurityIntegrationRequest) WithComment(Comment *string) *CreateSaml2SecurityIntegrationRequest { + s.Comment = Comment + return s +} + +func NewCreateScimSecurityIntegrationRequest( + name AccountObjectIdentifier, + Enabled bool, + ScimClient ScimSecurityIntegrationScimClientOption, + RunAsRole ScimSecurityIntegrationRunAsRoleOption, +) *CreateScimSecurityIntegrationRequest { + s := CreateScimSecurityIntegrationRequest{} + s.name = name + s.Enabled = Enabled + s.ScimClient = ScimClient + s.RunAsRole = RunAsRole + return &s +} + +func (s *CreateScimSecurityIntegrationRequest) WithOrReplace(OrReplace *bool) *CreateScimSecurityIntegrationRequest { + s.OrReplace = OrReplace + return s +} + +func (s *CreateScimSecurityIntegrationRequest) WithIfNotExists(IfNotExists *bool) *CreateScimSecurityIntegrationRequest { + s.IfNotExists = IfNotExists + return s +} + +func (s *CreateScimSecurityIntegrationRequest) WithNetworkPolicy(NetworkPolicy *AccountObjectIdentifier) *CreateScimSecurityIntegrationRequest { + s.NetworkPolicy = NetworkPolicy + return s +} + +func (s *CreateScimSecurityIntegrationRequest) WithSyncPassword(SyncPassword *bool) *CreateScimSecurityIntegrationRequest { + s.SyncPassword = SyncPassword + return s +} + +func (s *CreateScimSecurityIntegrationRequest) WithComment(Comment *string) *CreateScimSecurityIntegrationRequest { + s.Comment = Comment + return s +} + +func NewAlterSaml2SecurityIntegrationRequest( + name AccountObjectIdentifier, +) *AlterSaml2SecurityIntegrationRequest { + s := AlterSaml2SecurityIntegrationRequest{} + s.name = name + return &s +} + +func (s *AlterSaml2SecurityIntegrationRequest) WithIfExists(IfExists *bool) *AlterSaml2SecurityIntegrationRequest { + s.IfExists = IfExists + return s +} + +func (s *AlterSaml2SecurityIntegrationRequest) WithSetTags(SetTags []TagAssociation) *AlterSaml2SecurityIntegrationRequest { + s.SetTags = SetTags + return s +} + +func (s *AlterSaml2SecurityIntegrationRequest) WithUnsetTags(UnsetTags []ObjectIdentifier) *AlterSaml2SecurityIntegrationRequest { + s.UnsetTags = UnsetTags + return s +} + +func (s *AlterSaml2SecurityIntegrationRequest) WithSet(Set *Saml2IntegrationSetRequest) *AlterSaml2SecurityIntegrationRequest { + s.Set = Set + return s +} + +func (s *AlterSaml2SecurityIntegrationRequest) WithUnset(Unset *Saml2IntegrationUnsetRequest) *AlterSaml2SecurityIntegrationRequest { + s.Unset = Unset + return s +} + +func (s *AlterSaml2SecurityIntegrationRequest) WithRefreshSaml2SnowflakePrivateKey(RefreshSaml2SnowflakePrivateKey *bool) *AlterSaml2SecurityIntegrationRequest { + s.RefreshSaml2SnowflakePrivateKey = RefreshSaml2SnowflakePrivateKey + return s +} + +func NewSaml2IntegrationSetRequest() *Saml2IntegrationSetRequest { + return &Saml2IntegrationSetRequest{} +} + +func (s *Saml2IntegrationSetRequest) WithEnabled(Enabled *bool) *Saml2IntegrationSetRequest { + s.Enabled = Enabled + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2Issuer(Saml2Issuer *string) *Saml2IntegrationSetRequest { + s.Saml2Issuer = Saml2Issuer + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2SsoUrl(Saml2SsoUrl *string) *Saml2IntegrationSetRequest { + s.Saml2SsoUrl = Saml2SsoUrl + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2Provider(Saml2Provider *string) *Saml2IntegrationSetRequest { + s.Saml2Provider = Saml2Provider + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2X509Cert(Saml2X509Cert *string) *Saml2IntegrationSetRequest { + s.Saml2X509Cert = Saml2X509Cert + return s +} + +func (s *Saml2IntegrationSetRequest) WithAllowedUserDomains(AllowedUserDomains []UserDomain) *Saml2IntegrationSetRequest { + s.AllowedUserDomains = AllowedUserDomains + return s +} + +func (s *Saml2IntegrationSetRequest) WithAllowedEmailPatterns(AllowedEmailPatterns []EmailPattern) *Saml2IntegrationSetRequest { + s.AllowedEmailPatterns = AllowedEmailPatterns + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2SpInitiatedLoginPageLabel(Saml2SpInitiatedLoginPageLabel *string) *Saml2IntegrationSetRequest { + s.Saml2SpInitiatedLoginPageLabel = Saml2SpInitiatedLoginPageLabel + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2EnableSpInitiated(Saml2EnableSpInitiated *bool) *Saml2IntegrationSetRequest { + s.Saml2EnableSpInitiated = Saml2EnableSpInitiated + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2SnowflakeX509Cert(Saml2SnowflakeX509Cert *string) *Saml2IntegrationSetRequest { + s.Saml2SnowflakeX509Cert = Saml2SnowflakeX509Cert + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2SignRequest(Saml2SignRequest *bool) *Saml2IntegrationSetRequest { + s.Saml2SignRequest = Saml2SignRequest + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2RequestedNameidFormat(Saml2RequestedNameidFormat *string) *Saml2IntegrationSetRequest { + s.Saml2RequestedNameidFormat = Saml2RequestedNameidFormat + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2PostLogoutRedirectUrl(Saml2PostLogoutRedirectUrl *string) *Saml2IntegrationSetRequest { + s.Saml2PostLogoutRedirectUrl = Saml2PostLogoutRedirectUrl + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2ForceAuthn(Saml2ForceAuthn *bool) *Saml2IntegrationSetRequest { + s.Saml2ForceAuthn = Saml2ForceAuthn + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2SnowflakeIssuerUrl(Saml2SnowflakeIssuerUrl *string) *Saml2IntegrationSetRequest { + s.Saml2SnowflakeIssuerUrl = Saml2SnowflakeIssuerUrl + return s +} + +func (s *Saml2IntegrationSetRequest) WithSaml2SnowflakeAcsUrl(Saml2SnowflakeAcsUrl *string) *Saml2IntegrationSetRequest { + s.Saml2SnowflakeAcsUrl = Saml2SnowflakeAcsUrl + return s +} + +func (s *Saml2IntegrationSetRequest) WithComment(Comment *string) *Saml2IntegrationSetRequest { + s.Comment = Comment + return s +} + +func NewSaml2IntegrationUnsetRequest() *Saml2IntegrationUnsetRequest { + return &Saml2IntegrationUnsetRequest{} +} + +func (s *Saml2IntegrationUnsetRequest) WithSaml2ForceAuthn(Saml2ForceAuthn *bool) *Saml2IntegrationUnsetRequest { + s.Saml2ForceAuthn = Saml2ForceAuthn + return s +} + +func (s *Saml2IntegrationUnsetRequest) WithSaml2RequestedNameidFormat(Saml2RequestedNameidFormat *bool) *Saml2IntegrationUnsetRequest { + s.Saml2RequestedNameidFormat = Saml2RequestedNameidFormat + return s +} + +func (s *Saml2IntegrationUnsetRequest) WithSaml2PostLogoutRedirectUrl(Saml2PostLogoutRedirectUrl *bool) *Saml2IntegrationUnsetRequest { + s.Saml2PostLogoutRedirectUrl = Saml2PostLogoutRedirectUrl + return s +} + +func (s *Saml2IntegrationUnsetRequest) WithComment(Comment *bool) *Saml2IntegrationUnsetRequest { + s.Comment = Comment + return s +} + +func NewAlterScimSecurityIntegrationRequest( + name AccountObjectIdentifier, +) *AlterScimSecurityIntegrationRequest { + s := AlterScimSecurityIntegrationRequest{} + s.name = name + return &s +} + +func (s *AlterScimSecurityIntegrationRequest) WithIfExists(IfExists *bool) *AlterScimSecurityIntegrationRequest { + s.IfExists = IfExists + return s +} + +func (s *AlterScimSecurityIntegrationRequest) WithSetTags(SetTags []TagAssociation) *AlterScimSecurityIntegrationRequest { + s.SetTags = SetTags + return s +} + +func (s *AlterScimSecurityIntegrationRequest) WithUnsetTags(UnsetTags []ObjectIdentifier) *AlterScimSecurityIntegrationRequest { + s.UnsetTags = UnsetTags + return s +} + +func (s *AlterScimSecurityIntegrationRequest) WithSet(Set *ScimIntegrationSetRequest) *AlterScimSecurityIntegrationRequest { + s.Set = Set + return s +} + +func (s *AlterScimSecurityIntegrationRequest) WithUnset(Unset *ScimIntegrationUnsetRequest) *AlterScimSecurityIntegrationRequest { + s.Unset = Unset + return s +} + +func NewScimIntegrationSetRequest() *ScimIntegrationSetRequest { + return &ScimIntegrationSetRequest{} +} + +func (s *ScimIntegrationSetRequest) WithEnabled(Enabled *bool) *ScimIntegrationSetRequest { + s.Enabled = Enabled + return s +} + +func (s *ScimIntegrationSetRequest) WithNetworkPolicy(NetworkPolicy *AccountObjectIdentifier) *ScimIntegrationSetRequest { + s.NetworkPolicy = NetworkPolicy + return s +} + +func (s *ScimIntegrationSetRequest) WithSyncPassword(SyncPassword *bool) *ScimIntegrationSetRequest { + s.SyncPassword = SyncPassword + return s +} + +func (s *ScimIntegrationSetRequest) WithComment(Comment *string) *ScimIntegrationSetRequest { + s.Comment = Comment + return s +} + +func NewScimIntegrationUnsetRequest() *ScimIntegrationUnsetRequest { + return &ScimIntegrationUnsetRequest{} +} + +func (s *ScimIntegrationUnsetRequest) WithEnabled(Enabled *bool) *ScimIntegrationUnsetRequest { + s.Enabled = Enabled + return s +} + +func (s *ScimIntegrationUnsetRequest) WithNetworkPolicy(NetworkPolicy *bool) *ScimIntegrationUnsetRequest { + s.NetworkPolicy = NetworkPolicy + return s +} + +func (s *ScimIntegrationUnsetRequest) WithSyncPassword(SyncPassword *bool) *ScimIntegrationUnsetRequest { + s.SyncPassword = SyncPassword + return s +} + +func (s *ScimIntegrationUnsetRequest) WithComment(Comment *bool) *ScimIntegrationUnsetRequest { + s.Comment = Comment + return s +} + +func NewDropSecurityIntegrationRequest( + name AccountObjectIdentifier, +) *DropSecurityIntegrationRequest { + s := DropSecurityIntegrationRequest{} + s.name = name + return &s +} + +func (s *DropSecurityIntegrationRequest) WithIfExists(IfExists *bool) *DropSecurityIntegrationRequest { + s.IfExists = IfExists + return s +} + +func NewDescribeSecurityIntegrationRequest( + name AccountObjectIdentifier, +) *DescribeSecurityIntegrationRequest { + s := DescribeSecurityIntegrationRequest{} + s.name = name + return &s +} + +func NewShowSecurityIntegrationRequest() *ShowSecurityIntegrationRequest { + return &ShowSecurityIntegrationRequest{} +} + +func (s *ShowSecurityIntegrationRequest) WithLike(Like *Like) *ShowSecurityIntegrationRequest { + s.Like = Like + return s +} diff --git a/pkg/sdk/security_integrations_dto_gen.go b/pkg/sdk/security_integrations_dto_gen.go new file mode 100644 index 0000000000..41019279fc --- /dev/null +++ b/pkg/sdk/security_integrations_dto_gen.go @@ -0,0 +1,129 @@ +package sdk + +//go:generate go run ./dto-builder-generator/main.go + +var ( + _ optionsProvider[CreateSaml2SecurityIntegrationOptions] = new(CreateSaml2SecurityIntegrationRequest) + _ optionsProvider[CreateScimSecurityIntegrationOptions] = new(CreateScimSecurityIntegrationRequest) + _ optionsProvider[AlterSaml2SecurityIntegrationOptions] = new(AlterSaml2SecurityIntegrationRequest) + _ optionsProvider[AlterScimSecurityIntegrationOptions] = new(AlterScimSecurityIntegrationRequest) + _ optionsProvider[DropSecurityIntegrationOptions] = new(DropSecurityIntegrationRequest) + _ optionsProvider[DescribeSecurityIntegrationOptions] = new(DescribeSecurityIntegrationRequest) + _ optionsProvider[ShowSecurityIntegrationOptions] = new(ShowSecurityIntegrationRequest) +) + +type CreateSaml2SecurityIntegrationRequest struct { + OrReplace *bool + IfNotExists *bool + name AccountObjectIdentifier // required + Enabled bool // required + Saml2Issuer string // required + Saml2SsoUrl string // required + Saml2Provider string // required + Saml2X509Cert string // required + AllowedUserDomains []UserDomain + AllowedEmailPatterns []EmailPattern + Saml2SpInitiatedLoginPageLabel *string + Saml2EnableSpInitiated *bool + Saml2SnowflakeX509Cert *string + Saml2SignRequest *bool + Saml2RequestedNameidFormat *string + Saml2PostLogoutRedirectUrl *string + Saml2ForceAuthn *bool + Saml2SnowflakeIssuerUrl *string + Saml2SnowflakeAcsUrl *string + Comment *string +} + +func (r *CreateSaml2SecurityIntegrationRequest) GetName() AccountObjectIdentifier { + return r.name +} + +type CreateScimSecurityIntegrationRequest struct { + OrReplace *bool + IfNotExists *bool + name AccountObjectIdentifier // required + Enabled bool // required + ScimClient ScimSecurityIntegrationScimClientOption // required + RunAsRole ScimSecurityIntegrationRunAsRoleOption // required + NetworkPolicy *AccountObjectIdentifier + SyncPassword *bool + Comment *string +} + +func (r *CreateScimSecurityIntegrationRequest) GetName() AccountObjectIdentifier { + return r.name +} + +type AlterSaml2SecurityIntegrationRequest struct { + IfExists *bool + name AccountObjectIdentifier // required + SetTags []TagAssociation + UnsetTags []ObjectIdentifier + Set *Saml2IntegrationSetRequest + Unset *Saml2IntegrationUnsetRequest + RefreshSaml2SnowflakePrivateKey *bool +} + +type Saml2IntegrationSetRequest struct { + Enabled *bool + Saml2Issuer *string + Saml2SsoUrl *string + Saml2Provider *string + Saml2X509Cert *string + AllowedUserDomains []UserDomain + AllowedEmailPatterns []EmailPattern + Saml2SpInitiatedLoginPageLabel *string + Saml2EnableSpInitiated *bool + Saml2SnowflakeX509Cert *string + Saml2SignRequest *bool + Saml2RequestedNameidFormat *string + Saml2PostLogoutRedirectUrl *string + Saml2ForceAuthn *bool + Saml2SnowflakeIssuerUrl *string + Saml2SnowflakeAcsUrl *string + Comment *string +} + +type Saml2IntegrationUnsetRequest struct { + Saml2ForceAuthn *bool + Saml2RequestedNameidFormat *bool + Saml2PostLogoutRedirectUrl *bool + Comment *bool +} + +type AlterScimSecurityIntegrationRequest struct { + IfExists *bool + name AccountObjectIdentifier // required + SetTags []TagAssociation + UnsetTags []ObjectIdentifier + Set *ScimIntegrationSetRequest + Unset *ScimIntegrationUnsetRequest +} + +type ScimIntegrationSetRequest struct { + Enabled *bool + NetworkPolicy *AccountObjectIdentifier + SyncPassword *bool + Comment *string +} + +type ScimIntegrationUnsetRequest struct { + Enabled *bool + NetworkPolicy *bool + SyncPassword *bool + Comment *bool +} + +type DropSecurityIntegrationRequest struct { + IfExists *bool + name AccountObjectIdentifier // required +} + +type DescribeSecurityIntegrationRequest struct { + name AccountObjectIdentifier // required +} + +type ShowSecurityIntegrationRequest struct { + Like *Like +} diff --git a/pkg/sdk/security_integrations_gen.go b/pkg/sdk/security_integrations_gen.go new file mode 100644 index 0000000000..6c32053016 --- /dev/null +++ b/pkg/sdk/security_integrations_gen.go @@ -0,0 +1,189 @@ +package sdk + +import ( + "context" + "database/sql" + "time" +) + +type SecurityIntegrations interface { + CreateSaml2(ctx context.Context, request *CreateSaml2SecurityIntegrationRequest) error + CreateScim(ctx context.Context, request *CreateScimSecurityIntegrationRequest) error + AlterSaml2(ctx context.Context, request *AlterSaml2SecurityIntegrationRequest) error + AlterScim(ctx context.Context, request *AlterScimSecurityIntegrationRequest) error + Drop(ctx context.Context, request *DropSecurityIntegrationRequest) error + Describe(ctx context.Context, id AccountObjectIdentifier) ([]SecurityIntegrationProperty, error) + Show(ctx context.Context, request *ShowSecurityIntegrationRequest) ([]SecurityIntegration, error) + ShowByID(ctx context.Context, id AccountObjectIdentifier) (*SecurityIntegration, error) +} + +// CreateSaml2SecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-saml2. +type CreateSaml2SecurityIntegrationOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + securityIntegration bool `ddl:"static" sql:"SECURITY INTEGRATION"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` + integrationType string `ddl:"static" sql:"TYPE = SAML2"` + Enabled bool `ddl:"parameter" sql:"ENABLED"` + Saml2Issuer string `ddl:"parameter,single_quotes" sql:"SAML2_ISSUER"` + Saml2SsoUrl string `ddl:"parameter,single_quotes" sql:"SAML2_SSO_URL"` + Saml2Provider string `ddl:"parameter,single_quotes" sql:"SAML2_PROVIDER"` + Saml2X509Cert string `ddl:"parameter,single_quotes" sql:"SAML2_X509_CERT"` + AllowedUserDomains []UserDomain `ddl:"parameter,parentheses" sql:"ALLOWED_USER_DOMAINS"` + AllowedEmailPatterns []EmailPattern `ddl:"parameter,parentheses" sql:"ALLOWED_EMAIL_PATTERNS"` + Saml2SpInitiatedLoginPageLabel *string `ddl:"parameter,single_quotes" sql:"SAML2_SP_INITIATED_LOGIN_PAGE_LABEL"` + Saml2EnableSpInitiated *bool `ddl:"parameter" sql:"SAML2_ENABLE_SP_INITIATED"` + Saml2SnowflakeX509Cert *string `ddl:"parameter,single_quotes" sql:"SAML2_SNOWFLAKE_X509_CERT"` + Saml2SignRequest *bool `ddl:"parameter" sql:"SAML2_SIGN_REQUEST"` + Saml2RequestedNameidFormat *string `ddl:"parameter,single_quotes" sql:"SAML2_REQUESTED_NAMEID_FORMAT"` + Saml2PostLogoutRedirectUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_POST_LOGOUT_REDIRECT_URL"` + Saml2ForceAuthn *bool `ddl:"parameter" sql:"SAML2_FORCE_AUTHN"` + Saml2SnowflakeIssuerUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_SNOWFLAKE_ISSUER_URL"` + Saml2SnowflakeAcsUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_SNOWFLAKE_ACS_URL"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type UserDomain struct { + Domain string `ddl:"keyword,single_quotes"` +} + +type EmailPattern struct { + Pattern string `ddl:"keyword,single_quotes"` +} + +// CreateScimSecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-scim. +type CreateScimSecurityIntegrationOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + securityIntegration bool `ddl:"static" sql:"SECURITY INTEGRATION"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` + integrationType string `ddl:"static" sql:"TYPE = SCIM"` + Enabled bool `ddl:"parameter" sql:"ENABLED"` + ScimClient ScimSecurityIntegrationScimClientOption `ddl:"parameter,single_quotes" sql:"SCIM_CLIENT"` + RunAsRole ScimSecurityIntegrationRunAsRoleOption `ddl:"parameter,single_quotes" sql:"RUN_AS_ROLE"` + NetworkPolicy *AccountObjectIdentifier `ddl:"identifier,equals" sql:"NETWORK_POLICY"` + SyncPassword *bool `ddl:"parameter" sql:"SYNC_PASSWORD"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +// AlterSaml2SecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-saml2. +type AlterSaml2SecurityIntegrationOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + securityIntegration bool `ddl:"static" sql:"SECURITY INTEGRATION"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` + SetTags []TagAssociation `ddl:"keyword" sql:"SET TAG"` + UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` + Set *Saml2IntegrationSet `ddl:"list,no_parentheses" sql:"SET"` + Unset *Saml2IntegrationUnset `ddl:"list,no_parentheses" sql:"UNSET"` + RefreshSaml2SnowflakePrivateKey *bool `ddl:"keyword" sql:"REFRESH SAML2_SNOWFLAKE_PRIVATE_KEY"` +} + +type Saml2IntegrationSet struct { + Enabled *bool `ddl:"parameter" sql:"ENABLED"` + Saml2Issuer *string `ddl:"parameter,single_quotes" sql:"SAML2_ISSUER"` + Saml2SsoUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_SSO_URL"` + Saml2Provider *string `ddl:"parameter,single_quotes" sql:"SAML2_PROVIDER"` + Saml2X509Cert *string `ddl:"parameter,single_quotes" sql:"SAML2_X509_CERT"` + AllowedUserDomains []UserDomain `ddl:"parameter,parentheses" sql:"ALLOWED_USER_DOMAINS"` + AllowedEmailPatterns []EmailPattern `ddl:"parameter,parentheses" sql:"ALLOWED_EMAIL_PATTERNS"` + Saml2SpInitiatedLoginPageLabel *string `ddl:"parameter,single_quotes" sql:"SAML2_SP_INITIATED_LOGIN_PAGE_LABEL"` + Saml2EnableSpInitiated *bool `ddl:"parameter" sql:"SAML2_ENABLE_SP_INITIATED"` + Saml2SnowflakeX509Cert *string `ddl:"parameter,single_quotes" sql:"SAML2_SNOWFLAKE_X509_CERT"` + Saml2SignRequest *bool `ddl:"parameter" sql:"SAML2_SIGN_REQUEST"` + Saml2RequestedNameidFormat *string `ddl:"parameter,single_quotes" sql:"SAML2_REQUESTED_NAMEID_FORMAT"` + Saml2PostLogoutRedirectUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_POST_LOGOUT_REDIRECT_URL"` + Saml2ForceAuthn *bool `ddl:"parameter" sql:"SAML2_FORCE_AUTHN"` + Saml2SnowflakeIssuerUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_SNOWFLAKE_ISSUER_URL"` + Saml2SnowflakeAcsUrl *string `ddl:"parameter,single_quotes" sql:"SAML2_SNOWFLAKE_ACS_URL"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type Saml2IntegrationUnset struct { + Saml2ForceAuthn *bool `ddl:"keyword" sql:"SAML2_FORCE_AUTHN"` + Saml2RequestedNameidFormat *bool `ddl:"keyword" sql:"SAML2_REQUESTED_NAMEID_FORMAT"` + Saml2PostLogoutRedirectUrl *bool `ddl:"keyword" sql:"SAML2_POST_LOGOUT_REDIRECT_URL"` + Comment *bool `ddl:"keyword" sql:"COMMENT"` +} + +// AlterScimSecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-security-integration-scim. +type AlterScimSecurityIntegrationOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + securityIntegration bool `ddl:"static" sql:"SECURITY INTEGRATION"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` + SetTags []TagAssociation `ddl:"keyword" sql:"SET TAG"` + UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` + Set *ScimIntegrationSet `ddl:"list,no_parentheses" sql:"SET"` + Unset *ScimIntegrationUnset `ddl:"list,no_parentheses" sql:"UNSET"` +} + +type ScimIntegrationSet struct { + Enabled *bool `ddl:"parameter" sql:"ENABLED"` + NetworkPolicy *AccountObjectIdentifier `ddl:"identifier,equals" sql:"NETWORK_POLICY"` + SyncPassword *bool `ddl:"parameter" sql:"SYNC_PASSWORD"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type ScimIntegrationUnset struct { + Enabled *bool `ddl:"keyword" sql:"ENABLED"` + NetworkPolicy *bool `ddl:"keyword" sql:"NETWORK_POLICY"` + SyncPassword *bool `ddl:"keyword" sql:"SYNC_PASSWORD"` + Comment *bool `ddl:"keyword" sql:"COMMENT"` +} + +// DropSecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-integration. +type DropSecurityIntegrationOptions struct { + drop bool `ddl:"static" sql:"DROP"` + securityIntegration bool `ddl:"static" sql:"SECURITY INTEGRATION"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name AccountObjectIdentifier `ddl:"identifier"` +} + +// DescribeSecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-integration. +type DescribeSecurityIntegrationOptions struct { + describe bool `ddl:"static" sql:"DESCRIBE"` + securityIntegration bool `ddl:"static" sql:"SECURITY INTEGRATION"` + name AccountObjectIdentifier `ddl:"identifier"` +} + +type securityIntegrationDescRow struct { + Property string `db:"property"` + PropertyType string `db:"property_type"` + PropertyValue string `db:"property_value"` + PropertyDefault string `db:"property_default"` +} + +type SecurityIntegrationProperty struct { + Name string + Type string + Value string + Default string +} + +// ShowSecurityIntegrationOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-integrations. +type ShowSecurityIntegrationOptions struct { + show bool `ddl:"static" sql:"SHOW"` + securityIntegrations bool `ddl:"static" sql:"SECURITY INTEGRATIONS"` + Like *Like `ddl:"keyword" sql:"LIKE"` +} + +type securityIntegrationShowRow struct { + Name string `db:"name"` + Type string `db:"type"` + Category string `db:"category"` + Enabled bool `db:"enabled"` + Comment sql.NullString `db:"comment"` + CreatedOn time.Time `db:"created_on"` +} + +type SecurityIntegration struct { + Name string + IntegrationType string + Category string + Enabled bool + Comment string + CreatedOn time.Time +} diff --git a/pkg/sdk/security_integrations_gen_test.go b/pkg/sdk/security_integrations_gen_test.go new file mode 100644 index 0000000000..6f0d835cef --- /dev/null +++ b/pkg/sdk/security_integrations_gen_test.go @@ -0,0 +1,401 @@ +package sdk + +import ( + "testing" +) + +func TestSecurityIntegrations_CreateSaml2(t *testing.T) { + id := randomAccountObjectIdentifier() + + // Minimal valid CreateSaml2SecurityIntegrationOptions + defaultOpts := func() *CreateSaml2SecurityIntegrationOptions { + return &CreateSaml2SecurityIntegrationOptions{ + name: id, + Enabled: true, + Saml2Issuer: "issuer", + Saml2SsoUrl: "url", + Saml2Provider: "provider", + Saml2X509Cert: "cert", + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateSaml2SecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.IfNotExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateSaml2SecurityIntegrationOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "CREATE OR REPLACE SECURITY INTEGRATION %s TYPE = SAML2 ENABLED = true SAML2_ISSUER = 'issuer' SAML2_SSO_URL = 'url' SAML2_PROVIDER = 'provider' SAML2_X509_CERT = 'cert'", id.FullyQualifiedName()) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.IfNotExists = Bool(true) + opts.AllowedEmailPatterns = []EmailPattern{{Pattern: "pattern"}} + opts.AllowedUserDomains = []UserDomain{{Domain: "domain"}} + opts.Comment = Pointer("a") + opts.Saml2EnableSpInitiated = Pointer(true) + opts.Saml2ForceAuthn = Pointer(true) + opts.Saml2PostLogoutRedirectUrl = Pointer("redirect") + opts.Saml2RequestedNameidFormat = Pointer("format") + opts.Saml2SignRequest = Pointer(true) + opts.Saml2SnowflakeAcsUrl = Pointer("acs") + opts.Saml2SnowflakeIssuerUrl = Pointer("issuer") + opts.Saml2SpInitiatedLoginPageLabel = Pointer("label") + opts.Saml2SnowflakeX509Cert = Pointer("cert") + + assertOptsValidAndSQLEquals(t, opts, "CREATE SECURITY INTEGRATION IF NOT EXISTS %s TYPE = SAML2 ENABLED = true SAML2_ISSUER = 'issuer' SAML2_SSO_URL = 'url' SAML2_PROVIDER = 'provider' SAML2_X509_CERT = 'cert'"+ + " ALLOWED_USER_DOMAINS = ('domain') ALLOWED_EMAIL_PATTERNS = ('pattern') SAML2_SP_INITIATED_LOGIN_PAGE_LABEL = 'label' SAML2_ENABLE_SP_INITIATED = true SAML2_SNOWFLAKE_X509_CERT = 'cert' SAML2_SIGN_REQUEST = true"+ + " SAML2_REQUESTED_NAMEID_FORMAT = 'format' SAML2_POST_LOGOUT_REDIRECT_URL = 'redirect' SAML2_FORCE_AUTHN = true SAML2_SNOWFLAKE_ISSUER_URL = 'issuer' SAML2_SNOWFLAKE_ACS_URL = 'acs'"+ + " COMMENT = 'a'", id.FullyQualifiedName()) + }) +} + +func TestSecurityIntegrations_CreateScim(t *testing.T) { + id := randomAccountObjectIdentifier() + + // Minimal valid CreateScimSecurityIntegrationOptions + defaultOpts := func() *CreateScimSecurityIntegrationOptions { + return &CreateScimSecurityIntegrationOptions{ + name: id, + Enabled: true, + ScimClient: "GENERIC", + RunAsRole: "GENERIC_SCIM_PROVISIONER", + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateScimSecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.IfNotExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateScimSecurityIntegrationOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Pointer(true) + assertOptsValidAndSQLEquals(t, opts, "CREATE OR REPLACE SECURITY INTEGRATION %s TYPE = SCIM ENABLED = true SCIM_CLIENT = 'GENERIC' RUN_AS_ROLE = 'GENERIC_SCIM_PROVISIONER'", id.FullyQualifiedName()) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + networkPolicyID := randomAccountObjectIdentifier() + opts.IfNotExists = Pointer(true) + opts.NetworkPolicy = Pointer(networkPolicyID) + opts.SyncPassword = Pointer(true) + opts.Comment = Pointer("a") + assertOptsValidAndSQLEquals(t, opts, "CREATE SECURITY INTEGRATION IF NOT EXISTS %s TYPE = SCIM ENABLED = true SCIM_CLIENT = 'GENERIC' RUN_AS_ROLE = 'GENERIC_SCIM_PROVISIONER'"+ + " NETWORK_POLICY = %s SYNC_PASSWORD = true COMMENT = 'a'", id.FullyQualifiedName(), networkPolicyID.FullyQualifiedName()) + }) +} + +func TestSecurityIntegrations_AlterSaml2(t *testing.T) { + id := randomAccountObjectIdentifier() + + // Minimal valid AlterSaml2IntegrationSecurityIntegrationOptions + defaultOpts := func() *AlterSaml2SecurityIntegrationOptions { + return &AlterSaml2SecurityIntegrationOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *AlterSaml2SecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &Saml2IntegrationSet{ + Enabled: Pointer(true), + } + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: exactly of the fields [opts.*] should be set", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSaml2SecurityIntegrationOptions", "Set", "Unset", "RefreshSaml2SnowflakePrivateKey", "SetTags", "UnsetTags")) + }) + + t.Run("validation: at least one of the fields [opts.Set.*] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &Saml2IntegrationSet{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterSaml2SecurityIntegrationOptions.Set", "Enabled", "Saml2Issuer", "Saml2SsoUrl", "Saml2Provider", + "Saml2X509Cert", "AllowedUserDomains", "AllowedEmailPatterns", "Saml2SpInitiatedLoginPageLabel", "Saml2EnableSpInitiated", "Saml2SnowflakeX509Cert", "Saml2SignRequest", + "Saml2RequestedNameidFormat", "Saml2PostLogoutRedirectUrl", "Saml2ForceAuthn", "Saml2SnowflakeIssuerUrl", "Saml2SnowflakeAcsUrl", "Comment")) + }) + + t.Run("validation: at least one of the fields [opts.Unset.*] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &Saml2IntegrationUnset{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterSaml2SecurityIntegrationOptions.Unset", + "Saml2ForceAuthn", "Saml2RequestedNameidFormat", "Saml2PostLogoutRedirectUrl", "Comment")) + }) + + t.Run("validation: exactly one of the fields [opts.*] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &Saml2IntegrationSet{} + opts.Unset = &Saml2IntegrationUnset{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterSaml2SecurityIntegrationOptions", "Set", "Unset", "RefreshSaml2SnowflakePrivateKey", "SetTags", "UnsetTags")) + }) + + t.Run("all options - set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &Saml2IntegrationSet{ + Enabled: Pointer(true), + Saml2Issuer: Pointer("issuer"), + Saml2SsoUrl: Pointer("url"), + Saml2Provider: Pointer("provider"), + Saml2X509Cert: Pointer("cert"), + AllowedUserDomains: []UserDomain{{Domain: "domain"}}, + AllowedEmailPatterns: []EmailPattern{{Pattern: "pattern"}}, + Saml2SpInitiatedLoginPageLabel: Pointer("label"), + Saml2EnableSpInitiated: Pointer(true), + Saml2SnowflakeX509Cert: Pointer("cert"), + Saml2SignRequest: Pointer(true), + Saml2RequestedNameidFormat: Pointer("format"), + Saml2PostLogoutRedirectUrl: Pointer("redirect"), + Saml2ForceAuthn: Pointer(true), + Saml2SnowflakeIssuerUrl: Pointer("issuer"), + Saml2SnowflakeAcsUrl: Pointer("acs"), + Comment: Pointer("a"), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s SET ENABLED = true, SAML2_ISSUER = 'issuer', SAML2_SSO_URL = 'url', SAML2_PROVIDER = 'provider', SAML2_X509_CERT = 'cert',"+ + " ALLOWED_USER_DOMAINS = ('domain'), ALLOWED_EMAIL_PATTERNS = ('pattern'), SAML2_SP_INITIATED_LOGIN_PAGE_LABEL = 'label', SAML2_ENABLE_SP_INITIATED = true, SAML2_SNOWFLAKE_X509_CERT = 'cert', SAML2_SIGN_REQUEST = true,"+ + " SAML2_REQUESTED_NAMEID_FORMAT = 'format', SAML2_POST_LOGOUT_REDIRECT_URL = 'redirect', SAML2_FORCE_AUTHN = true, SAML2_SNOWFLAKE_ISSUER_URL = 'issuer', SAML2_SNOWFLAKE_ACS_URL = 'acs',"+ + " COMMENT = 'a'", id.FullyQualifiedName()) + }) + + t.Run("all options - unset", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &Saml2IntegrationUnset{ + Saml2ForceAuthn: Pointer(true), + Saml2RequestedNameidFormat: Pointer(true), + Saml2PostLogoutRedirectUrl: Pointer(true), + Comment: Pointer(true), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s UNSET SAML2_FORCE_AUTHN, SAML2_REQUESTED_NAMEID_FORMAT, SAML2_POST_LOGOUT_REDIRECT_URL, COMMENT", id.FullyQualifiedName()) + }) + + t.Run("refresh SAML2_SNOWFLAKE_PRIVATE_KEY", func(t *testing.T) { + opts := defaultOpts() + opts.RefreshSaml2SnowflakePrivateKey = Pointer(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s REFRESH SAML2_SNOWFLAKE_PRIVATE_KEY", id.FullyQualifiedName()) + }) + + t.Run("set tags", func(t *testing.T) { + opts := defaultOpts() + opts.SetTags = []TagAssociation{ + { + Name: NewAccountObjectIdentifier("name"), + Value: "value", + }, + { + Name: NewAccountObjectIdentifier("second-name"), + Value: "second-value", + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER SECURITY INTEGRATION %s SET TAG "name" = 'value', "second-name" = 'second-value'`, id.FullyQualifiedName()) + }) + + t.Run("unset tags", func(t *testing.T) { + opts := defaultOpts() + opts.UnsetTags = []ObjectIdentifier{ + NewAccountObjectIdentifier("name"), + NewAccountObjectIdentifier("second-name"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER SECURITY INTEGRATION %s UNSET TAG "name", "second-name"`, id.FullyQualifiedName()) + }) +} + +func TestSecurityIntegrations_AlterScim(t *testing.T) { + id := randomAccountObjectIdentifier() + + // Minimal valid AlterScimSecurityIntegrationOptions + defaultOpts := func() *AlterScimSecurityIntegrationOptions { + return &AlterScimSecurityIntegrationOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *AlterScimSecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &ScimIntegrationSet{ + Enabled: Pointer(true), + } + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: exactly of the fields [opts.*] should be set", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterScimSecurityIntegrationOptions", "Set", "Unset", "SetTags", "UnsetTags")) + }) + + t.Run("validation: exactly one of the fields [opts.*] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &ScimIntegrationSet{} + opts.Unset = &ScimIntegrationUnset{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterScimSecurityIntegrationOptions", "Set", "Unset", "SetTags", "UnsetTags")) + }) + + t.Run("validation: at least one of the fields [opts.Set.*] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &ScimIntegrationSet{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterScimSecurityIntegrationOptions.Set", "Enabled", "NetworkPolicy", "SyncPassword", "Comment")) + }) + + t.Run("validation: at least one of the fields [opts.Unset.*] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &ScimIntegrationUnset{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterScimSecurityIntegrationOptions.Unset", "Enabled", "NetworkPolicy", "SyncPassword", "Comment")) + }) + + t.Run("all options - set", func(t *testing.T) { + opts := defaultOpts() + networkPolicyID := randomAccountObjectIdentifier() + opts.Set = &ScimIntegrationSet{ + Enabled: Pointer(true), + NetworkPolicy: Pointer(networkPolicyID), + SyncPassword: Pointer(true), + Comment: Pointer("test"), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s SET ENABLED = true, NETWORK_POLICY = %s, SYNC_PASSWORD = true, COMMENT = 'test'", + id.FullyQualifiedName(), networkPolicyID.FullyQualifiedName()) + }) + + t.Run("all options - unset", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &ScimIntegrationUnset{ + Enabled: Pointer(true), + NetworkPolicy: Pointer(true), + SyncPassword: Pointer(true), + Comment: Pointer(true), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s UNSET ENABLED, NETWORK_POLICY, SYNC_PASSWORD, COMMENT", id.FullyQualifiedName()) + }) + + t.Run("set tags", func(t *testing.T) { + opts := defaultOpts() + opts.SetTags = []TagAssociation{ + { + Name: NewAccountObjectIdentifier("name"), + Value: "value", + }, + { + Name: NewAccountObjectIdentifier("second-name"), + Value: "second-value", + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER SECURITY INTEGRATION %s SET TAG "name" = 'value', "second-name" = 'second-value'`, id.FullyQualifiedName()) + }) + + t.Run("unset tags", func(t *testing.T) { + opts := defaultOpts() + opts.UnsetTags = []ObjectIdentifier{ + NewAccountObjectIdentifier("name"), + NewAccountObjectIdentifier("second-name"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER SECURITY INTEGRATION %s UNSET TAG "name", "second-name"`, id.FullyQualifiedName()) + }) +} + +func TestSecurityIntegrations_Drop(t *testing.T) { + id := randomAccountObjectIdentifier() + + // Minimal valid DropSecurityIntegrationOptions + defaultOpts := func() *DropSecurityIntegrationOptions { + return &DropSecurityIntegrationOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *DropSecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "DROP SECURITY INTEGRATION IF EXISTS %s", id.FullyQualifiedName()) + }) +} + +func TestSecurityIntegrations_Describe(t *testing.T) { + id := randomAccountObjectIdentifier() + + // Minimal valid DescribeSecurityIntegrationOptions + defaultOpts := func() *DescribeSecurityIntegrationOptions { + return &DescribeSecurityIntegrationOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *DescribeSecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewAccountObjectIdentifier("") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "DESCRIBE SECURITY INTEGRATION %s", id.FullyQualifiedName()) + }) +} + +func TestSecurityIntegrations_Show(t *testing.T) { + // Minimal valid ShowSecurityIntegrationOptions + defaultOpts := func() *ShowSecurityIntegrationOptions { + return &ShowSecurityIntegrationOptions{} + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *ShowSecurityIntegrationOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "SHOW SECURITY INTEGRATIONS") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{ + Pattern: String("some pattern"), + } + assertOptsValidAndSQLEquals(t, opts, "SHOW SECURITY INTEGRATIONS LIKE 'some pattern'") + }) +} diff --git a/pkg/sdk/security_integrations_impl_gen.go b/pkg/sdk/security_integrations_impl_gen.go new file mode 100644 index 0000000000..479af50878 --- /dev/null +++ b/pkg/sdk/security_integrations_impl_gen.go @@ -0,0 +1,222 @@ +package sdk + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" +) + +var _ SecurityIntegrations = (*securityIntegrations)(nil) + +type securityIntegrations struct { + client *Client +} + +func (v *securityIntegrations) CreateSaml2(ctx context.Context, request *CreateSaml2SecurityIntegrationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *securityIntegrations) CreateScim(ctx context.Context, request *CreateScimSecurityIntegrationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *securityIntegrations) AlterSaml2(ctx context.Context, request *AlterSaml2SecurityIntegrationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *securityIntegrations) AlterScim(ctx context.Context, request *AlterScimSecurityIntegrationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *securityIntegrations) Drop(ctx context.Context, request *DropSecurityIntegrationRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *securityIntegrations) Describe(ctx context.Context, id AccountObjectIdentifier) ([]SecurityIntegrationProperty, error) { + opts := &DescribeSecurityIntegrationOptions{ + name: id, + } + rows, err := validateAndQuery[securityIntegrationDescRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + return convertRows[securityIntegrationDescRow, SecurityIntegrationProperty](rows), nil +} + +func (v *securityIntegrations) Show(ctx context.Context, request *ShowSecurityIntegrationRequest) ([]SecurityIntegration, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[securityIntegrationShowRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + resultList := convertRows[securityIntegrationShowRow, SecurityIntegration](dbRows) + return resultList, nil +} + +func (v *securityIntegrations) ShowByID(ctx context.Context, id AccountObjectIdentifier) (*SecurityIntegration, error) { + securityIntegrations, err := v.Show(ctx, NewShowSecurityIntegrationRequest().WithLike(&Like{ + Pattern: String(id.Name()), + })) + if err != nil { + return nil, err + } + return collections.FindOne(securityIntegrations, func(r SecurityIntegration) bool { return r.Name == id.Name() }) +} + +func (r *CreateSaml2SecurityIntegrationRequest) toOpts() *CreateSaml2SecurityIntegrationOptions { + opts := &CreateSaml2SecurityIntegrationOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + Enabled: r.Enabled, + Saml2Issuer: r.Saml2Issuer, + Saml2SsoUrl: r.Saml2SsoUrl, + Saml2Provider: r.Saml2Provider, + Saml2X509Cert: r.Saml2X509Cert, + AllowedUserDomains: r.AllowedUserDomains, + AllowedEmailPatterns: r.AllowedEmailPatterns, + Saml2SpInitiatedLoginPageLabel: r.Saml2SpInitiatedLoginPageLabel, + Saml2EnableSpInitiated: r.Saml2EnableSpInitiated, + Saml2SnowflakeX509Cert: r.Saml2SnowflakeX509Cert, + Saml2SignRequest: r.Saml2SignRequest, + Saml2RequestedNameidFormat: r.Saml2RequestedNameidFormat, + Saml2PostLogoutRedirectUrl: r.Saml2PostLogoutRedirectUrl, + Saml2ForceAuthn: r.Saml2ForceAuthn, + Saml2SnowflakeIssuerUrl: r.Saml2SnowflakeIssuerUrl, + Saml2SnowflakeAcsUrl: r.Saml2SnowflakeAcsUrl, + Comment: r.Comment, + } + return opts +} + +func (r *CreateScimSecurityIntegrationRequest) toOpts() *CreateScimSecurityIntegrationOptions { + opts := &CreateScimSecurityIntegrationOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + Enabled: r.Enabled, + ScimClient: r.ScimClient, + RunAsRole: r.RunAsRole, + NetworkPolicy: r.NetworkPolicy, + SyncPassword: r.SyncPassword, + Comment: r.Comment, + } + return opts +} + +func (r *AlterSaml2SecurityIntegrationRequest) toOpts() *AlterSaml2SecurityIntegrationOptions { + opts := &AlterSaml2SecurityIntegrationOptions{ + IfExists: r.IfExists, + name: r.name, + SetTags: r.SetTags, + UnsetTags: r.UnsetTags, + + RefreshSaml2SnowflakePrivateKey: r.RefreshSaml2SnowflakePrivateKey, + } + if r.Set != nil { + opts.Set = &Saml2IntegrationSet{ + Enabled: r.Set.Enabled, + Saml2Issuer: r.Set.Saml2Issuer, + Saml2SsoUrl: r.Set.Saml2SsoUrl, + Saml2Provider: r.Set.Saml2Provider, + Saml2X509Cert: r.Set.Saml2X509Cert, + AllowedUserDomains: r.Set.AllowedUserDomains, + AllowedEmailPatterns: r.Set.AllowedEmailPatterns, + Saml2SpInitiatedLoginPageLabel: r.Set.Saml2SpInitiatedLoginPageLabel, + Saml2EnableSpInitiated: r.Set.Saml2EnableSpInitiated, + Saml2SnowflakeX509Cert: r.Set.Saml2SnowflakeX509Cert, + Saml2SignRequest: r.Set.Saml2SignRequest, + Saml2RequestedNameidFormat: r.Set.Saml2RequestedNameidFormat, + Saml2PostLogoutRedirectUrl: r.Set.Saml2PostLogoutRedirectUrl, + Saml2ForceAuthn: r.Set.Saml2ForceAuthn, + Saml2SnowflakeIssuerUrl: r.Set.Saml2SnowflakeIssuerUrl, + Saml2SnowflakeAcsUrl: r.Set.Saml2SnowflakeAcsUrl, + Comment: r.Set.Comment, + } + } + if r.Unset != nil { + opts.Unset = &Saml2IntegrationUnset{ + Saml2ForceAuthn: r.Unset.Saml2ForceAuthn, + Saml2RequestedNameidFormat: r.Unset.Saml2RequestedNameidFormat, + Saml2PostLogoutRedirectUrl: r.Unset.Saml2PostLogoutRedirectUrl, + Comment: r.Unset.Comment, + } + } + return opts +} + +func (r *AlterScimSecurityIntegrationRequest) toOpts() *AlterScimSecurityIntegrationOptions { + opts := &AlterScimSecurityIntegrationOptions{ + IfExists: r.IfExists, + name: r.name, + SetTags: r.SetTags, + UnsetTags: r.UnsetTags, + } + if r.Set != nil { + opts.Set = &ScimIntegrationSet{ + Enabled: r.Set.Enabled, + NetworkPolicy: r.Set.NetworkPolicy, + SyncPassword: r.Set.SyncPassword, + Comment: r.Set.Comment, + } + } + if r.Unset != nil { + opts.Unset = &ScimIntegrationUnset{ + Enabled: r.Unset.Enabled, + NetworkPolicy: r.Unset.NetworkPolicy, + SyncPassword: r.Unset.SyncPassword, + Comment: r.Unset.Comment, + } + } + return opts +} + +func (r *DropSecurityIntegrationRequest) toOpts() *DropSecurityIntegrationOptions { + opts := &DropSecurityIntegrationOptions{ + IfExists: r.IfExists, + name: r.name, + } + return opts +} + +func (r *DescribeSecurityIntegrationRequest) toOpts() *DescribeSecurityIntegrationOptions { + opts := &DescribeSecurityIntegrationOptions{ + name: r.name, + } + return opts +} + +func (r securityIntegrationDescRow) convert() *SecurityIntegrationProperty { + return &SecurityIntegrationProperty{ + Name: r.Property, + Type: r.PropertyType, + Value: r.PropertyValue, + Default: r.PropertyDefault, + } +} + +func (r *ShowSecurityIntegrationRequest) toOpts() *ShowSecurityIntegrationOptions { + opts := &ShowSecurityIntegrationOptions{ + Like: r.Like, + } + return opts +} + +func (r securityIntegrationShowRow) convert() *SecurityIntegration { + s := &SecurityIntegration{ + Name: r.Name, + IntegrationType: r.Type, + Enabled: r.Enabled, + CreatedOn: r.CreatedOn, + Category: r.Category, + } + if r.Comment.Valid { + s.Comment = r.Comment.String + } + return s +} diff --git a/pkg/sdk/security_integrations_validations_gen.go b/pkg/sdk/security_integrations_validations_gen.go new file mode 100644 index 0000000000..c3330209b6 --- /dev/null +++ b/pkg/sdk/security_integrations_validations_gen.go @@ -0,0 +1,117 @@ +package sdk + +var ( + _ validatable = new(CreateSaml2SecurityIntegrationOptions) + _ validatable = new(CreateScimSecurityIntegrationOptions) + _ validatable = new(AlterSaml2SecurityIntegrationOptions) + _ validatable = new(AlterScimSecurityIntegrationOptions) + _ validatable = new(DropSecurityIntegrationOptions) + _ validatable = new(DescribeSecurityIntegrationOptions) + _ validatable = new(ShowSecurityIntegrationOptions) +) + +func (opts *CreateSaml2SecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateSaml2SecurityIntegrationOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *CreateScimSecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateScimSecurityIntegrationOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *AlterSaml2SecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !exactlyOneValueSet(opts.Set, opts.Unset, opts.RefreshSaml2SnowflakePrivateKey, opts.SetTags, opts.UnsetTags) { + errs = append(errs, errExactlyOneOf("AlterSaml2SecurityIntegrationOptions", "Set", "Unset", "RefreshSaml2SnowflakePrivateKey", "SetTags", "UnsetTags")) + } + if valueSet(opts.Set) { + if !anyValueSet(opts.Set.Enabled, opts.Set.Saml2Issuer, opts.Set.Saml2SsoUrl, opts.Set.Saml2Provider, opts.Set.Saml2X509Cert, opts.Set.AllowedUserDomains, opts.Set.AllowedEmailPatterns, opts.Set.Saml2SpInitiatedLoginPageLabel, opts.Set.Saml2EnableSpInitiated, opts.Set.Saml2SnowflakeX509Cert, opts.Set.Saml2SignRequest, opts.Set.Saml2RequestedNameidFormat, opts.Set.Saml2PostLogoutRedirectUrl, opts.Set.Saml2ForceAuthn, opts.Set.Saml2SnowflakeIssuerUrl, opts.Set.Saml2SnowflakeAcsUrl, opts.Set.Comment) { + errs = append(errs, errAtLeastOneOf("AlterSaml2SecurityIntegrationOptions.Set", "Enabled", "Saml2Issuer", "Saml2SsoUrl", "Saml2Provider", "Saml2X509Cert", "AllowedUserDomains", "AllowedEmailPatterns", "Saml2SpInitiatedLoginPageLabel", "Saml2EnableSpInitiated", "Saml2SnowflakeX509Cert", "Saml2SignRequest", "Saml2RequestedNameidFormat", "Saml2PostLogoutRedirectUrl", "Saml2ForceAuthn", "Saml2SnowflakeIssuerUrl", "Saml2SnowflakeAcsUrl", "Comment")) + } + } + if valueSet(opts.Unset) { + if !anyValueSet(opts.Unset.Saml2ForceAuthn, opts.Unset.Saml2RequestedNameidFormat, opts.Unset.Saml2PostLogoutRedirectUrl, opts.Unset.Comment) { + errs = append(errs, errAtLeastOneOf("AlterSaml2SecurityIntegrationOptions.Unset", "Saml2ForceAuthn", "Saml2RequestedNameidFormat", "Saml2PostLogoutRedirectUrl", "Comment")) + } + } + return JoinErrors(errs...) +} + +func (opts *AlterScimSecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !exactlyOneValueSet(opts.Set, opts.Unset, opts.SetTags, opts.UnsetTags) { + errs = append(errs, errExactlyOneOf("AlterScimSecurityIntegrationOptions", "Set", "Unset", "SetTags", "UnsetTags")) + } + if valueSet(opts.Set) { + if !anyValueSet(opts.Set.Enabled, opts.Set.NetworkPolicy, opts.Set.SyncPassword, opts.Set.Comment) { + errs = append(errs, errAtLeastOneOf("AlterScimSecurityIntegrationOptions.Set", "Enabled", "NetworkPolicy", "SyncPassword", "Comment")) + } + } + if valueSet(opts.Unset) { + if !anyValueSet(opts.Unset.Enabled, opts.Unset.NetworkPolicy, opts.Unset.SyncPassword, opts.Unset.Comment) { + errs = append(errs, errAtLeastOneOf("AlterScimSecurityIntegrationOptions.Unset", "Enabled", "NetworkPolicy", "SyncPassword", "Comment")) + } + } + return JoinErrors(errs...) +} + +func (opts *DropSecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *DescribeSecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *ShowSecurityIntegrationOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + return JoinErrors(errs...) +} diff --git a/pkg/sdk/testint/security_integrations_gen_integration_test.go b/pkg/sdk/testint/security_integrations_gen_integration_test.go new file mode 100644 index 0000000000..4c2a3c164f --- /dev/null +++ b/pkg/sdk/testint/security_integrations_gen_integration_test.go @@ -0,0 +1,444 @@ +package testint + +import ( + "fmt" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/snowflakeroles" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInt_SecurityIntegrations(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + // TODO: move URL to helpers + acsURL := fmt.Sprintf("https://%s.snowflakecomputing.com/fed/login", testClientHelper().Context.CurrentAccount(t)) + issuerURL := fmt.Sprintf("https://%s.snowflakecomputing.com", testClientHelper().Context.CurrentAccount(t)) + cert := random.GenerateX509(t) + revertParameter := testClientHelper().Parameter.UpdateAccountParameterTemporarily(t, sdk.AccountParameterEnableIdentifierFirstLogin, "true") + t.Cleanup(revertParameter) + + cleanupSecurityIntegration := func(t *testing.T, id sdk.AccountObjectIdentifier) { + t.Helper() + t.Cleanup(func() { + err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id).WithIfExists(sdk.Pointer(true))) + assert.NoError(t, err) + }) + } + createSAML2Integration := func(t *testing.T, siID sdk.AccountObjectIdentifier, issuer string, with func(*sdk.CreateSaml2SecurityIntegrationRequest)) *sdk.SecurityIntegration { + t.Helper() + + saml2Req := sdk.NewCreateSaml2SecurityIntegrationRequest(siID, false, issuer, "https://example.com", "Custom", cert) + if with != nil { + with(saml2Req) + } + err := client.SecurityIntegrations.CreateSaml2(ctx, saml2Req) + require.NoError(t, err) + cleanupSecurityIntegration(t, siID) + integration, err := client.SecurityIntegrations.ShowByID(ctx, siID) + require.NoError(t, err) + + return integration + } + + createSCIMIntegration := func(t *testing.T, siID sdk.AccountObjectIdentifier, with func(*sdk.CreateScimSecurityIntegrationRequest)) *sdk.SecurityIntegration { + t.Helper() + role, roleCleanup := testClientHelper().Role.CreateRoleWithRequest(t, sdk.NewCreateRoleRequest(snowflakeroles.GenericScimProvisioner).WithOrReplace(true)) + t.Cleanup(roleCleanup) + testClientHelper().Role.GrantRoleToCurrentRole(t, role.ID()) + + scimReq := sdk.NewCreateScimSecurityIntegrationRequest(siID, false, sdk.ScimSecurityIntegrationScimClientGeneric, sdk.ScimSecurityIntegrationRunAsRoleGenericScimProvisioner) + if with != nil { + with(scimReq) + } + err := client.SecurityIntegrations.CreateScim(ctx, scimReq) + require.NoError(t, err) + cleanupSecurityIntegration(t, siID) + integration, err := client.SecurityIntegrations.ShowByID(ctx, siID) + require.NoError(t, err) + + return integration + } + + assertSecurityIntegration := func(t *testing.T, si *sdk.SecurityIntegration, id sdk.AccountObjectIdentifier, siType string, enabled bool, comment string) { + t.Helper() + assert.Equal(t, id.Name(), si.Name) + assert.Equal(t, siType, si.IntegrationType) + assert.Equal(t, enabled, si.Enabled) + assert.Equal(t, comment, si.Comment) + assert.Equal(t, "SECURITY", si.Category) + } + + assertSCIMDescribe := func(details []sdk.SecurityIntegrationProperty, enabled, networkPolicy, runAsRole, syncPassword, comment string) { + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "ENABLED", Type: "Boolean", Value: enabled, Default: "false"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "NETWORK_POLICY", Type: "String", Value: networkPolicy, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "RUN_AS_ROLE", Type: "String", Value: runAsRole, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SYNC_PASSWORD", Type: "Boolean", Value: syncPassword, Default: "true"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "COMMENT", Type: "String", Value: comment, Default: ""}) + } + + type saml2details struct { + provider string + enableSPInitiated string + spInitiatedLoginPageLabel string + ssoURL string + issuer string + requestedNameIDFormat string + forceAuthn string + postLogoutRedirectUrl string + signrequest string + comment string + snowflakeIssuerURL string + snowflakeAcsURL string + allowedUserDomains string + allowedEmailPatterns string + } + + assertSAML2Describe := func(details []sdk.SecurityIntegrationProperty, d saml2details) { + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_X509_CERT", Type: "String", Value: cert, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_PROVIDER", Type: "String", Value: d.provider, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_ENABLE_SP_INITIATED", Type: "Boolean", Value: d.enableSPInitiated, Default: "false"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_SP_INITIATED_LOGIN_PAGE_LABEL", Type: "String", Value: d.spInitiatedLoginPageLabel, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_SSO_URL", Type: "String", Value: d.ssoURL, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_ISSUER", Type: "String", Value: d.issuer, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_REQUESTED_NAMEID_FORMAT", Type: "String", Value: d.requestedNameIDFormat, Default: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_FORCE_AUTHN", Type: "Boolean", Value: d.forceAuthn, Default: "false"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_POST_LOGOUT_REDIRECT_URL", Type: "String", Value: d.postLogoutRedirectUrl, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_SIGN_REQUEST", Type: "Boolean", Value: d.signrequest, Default: "false"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_DIGEST_METHODS_USED", Type: "String", Value: "http://www.w3.org/2001/04/xmlenc#sha256", Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_SIGNATURE_METHODS_USED", Type: "String", Value: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "COMMENT", Type: "String", Value: d.comment, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_SNOWFLAKE_ISSUER_URL", Type: "String", Value: d.snowflakeIssuerURL, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_SNOWFLAKE_ACS_URL", Type: "String", Value: d.snowflakeAcsURL, Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "ALLOWED_USER_DOMAINS", Type: "List", Value: d.allowedUserDomains, Default: "[]"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "ALLOWED_EMAIL_PATTERNS", Type: "List", Value: d.allowedEmailPatterns, Default: "[]"}) + } + + t.Run("CreateSaml2", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + issuer := testClientHelper().Ids.Alpha() + + createSAML2Integration(t, id, issuer, func(r *sdk.CreateSaml2SecurityIntegrationRequest) { + r.WithAllowedEmailPatterns([]sdk.EmailPattern{{Pattern: "^(.+dev)@example.com$"}}). + WithAllowedUserDomains([]sdk.UserDomain{{Domain: "example.com"}}). + WithComment(sdk.Pointer("a")). + WithSaml2EnableSpInitiated(sdk.Pointer(true)). + WithSaml2ForceAuthn(sdk.Pointer(true)). + WithSaml2PostLogoutRedirectUrl(sdk.Pointer("http://example.com/logout")). + WithSaml2RequestedNameidFormat(sdk.Pointer("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")). + WithSaml2SignRequest(sdk.Pointer(true)). + WithSaml2SnowflakeAcsUrl(&acsURL). + WithSaml2SnowflakeIssuerUrl(&issuerURL). + WithSaml2SpInitiatedLoginPageLabel(sdk.Pointer("label")) + // TODO: fix after format clarification + // WithSaml2SnowflakeX509Cert(sdk.Pointer(x509)) + }) + details, err := client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + + assertSAML2Describe(details, saml2details{ + provider: "Custom", + enableSPInitiated: "true", + spInitiatedLoginPageLabel: "label", + ssoURL: "https://example.com", + issuer: issuer, + requestedNameIDFormat: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + forceAuthn: "true", + postLogoutRedirectUrl: "http://example.com/logout", + signrequest: "true", + comment: "a", + snowflakeIssuerURL: issuerURL, + snowflakeAcsURL: acsURL, + allowedUserDomains: "[example.com]", + allowedEmailPatterns: "[^(.+dev)@example.com$]", + }) + + si, err := client.SecurityIntegrations.ShowByID(ctx, id) + require.NoError(t, err) + assertSecurityIntegration(t, si, id, "SAML2", false, "a") + }) + + t.Run("CreateScim", func(t *testing.T) { + networkPolicy, networkPolicyCleanup := testClientHelper().NetworkPolicy.CreateNetworkPolicy(t) + t.Cleanup(networkPolicyCleanup) + + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + createSCIMIntegration(t, id, func(r *sdk.CreateScimSecurityIntegrationRequest) { + r.WithComment(sdk.Pointer("a")). + WithNetworkPolicy(sdk.Pointer(sdk.NewAccountObjectIdentifier(networkPolicy.Name))). + WithSyncPassword(sdk.Pointer(false)) + }) + details, err := client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + + assertSCIMDescribe(details, "false", networkPolicy.Name, "GENERIC_SCIM_PROVISIONER", "false", "a") + + si, err := client.SecurityIntegrations.ShowByID(ctx, id) + require.NoError(t, err) + assertSecurityIntegration(t, si, id, "SCIM - GENERIC", false, "a") + }) + + t.Run("AlterSAML2Integration", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + issuer := testClientHelper().Ids.Alpha() + createSAML2Integration(t, id, issuer, nil) + + setRequest := sdk.NewAlterSaml2SecurityIntegrationRequest(id). + WithSet( + sdk.NewSaml2IntegrationSetRequest(). + WithEnabled(sdk.Pointer(true)). + WithSaml2Issuer(sdk.Pointer(issuer)). + WithSaml2SsoUrl(sdk.Pointer("http://example.com")). + WithSaml2Provider(sdk.Pointer("OKTA")). + WithSaml2X509Cert(sdk.Pointer(cert)). + WithComment(sdk.Pointer("a")). + WithSaml2EnableSpInitiated(sdk.Pointer(true)). + WithSaml2ForceAuthn(sdk.Pointer(true)). + WithSaml2PostLogoutRedirectUrl(sdk.Pointer("http://example.com/logout")). + WithSaml2RequestedNameidFormat(sdk.Pointer("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")). + WithSaml2SignRequest(sdk.Pointer(true)). + WithSaml2SnowflakeAcsUrl(&acsURL). + WithSaml2SnowflakeIssuerUrl(&issuerURL). + WithSaml2SpInitiatedLoginPageLabel(sdk.Pointer("label")). + WithAllowedEmailPatterns([]sdk.EmailPattern{{Pattern: "^(.+dev)@example.com$"}}). + WithAllowedUserDomains([]sdk.UserDomain{{Domain: "example.com"}}), + // TODO: fix after format clarification + // WithSaml2SnowflakeX509Cert(sdk.Pointer(cert)). + ) + err := client.SecurityIntegrations.AlterSaml2(ctx, setRequest) + require.NoError(t, err) + + details, err := client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + + assertSAML2Describe(details, saml2details{ + provider: "OKTA", + enableSPInitiated: "true", + spInitiatedLoginPageLabel: "label", + ssoURL: "http://example.com", + issuer: issuer, + requestedNameIDFormat: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + forceAuthn: "true", + postLogoutRedirectUrl: "http://example.com/logout", + signrequest: "true", + comment: "a", + snowflakeIssuerURL: issuerURL, + snowflakeAcsURL: acsURL, + allowedUserDomains: "[example.com]", + allowedEmailPatterns: "[^(.+dev)@example.com$]", + }) + + unsetRequest := sdk.NewAlterSaml2SecurityIntegrationRequest(id). + WithUnset( + sdk.NewSaml2IntegrationUnsetRequest(). + WithSaml2ForceAuthn(sdk.Pointer(true)). + WithSaml2RequestedNameidFormat(sdk.Pointer(true)). + WithSaml2PostLogoutRedirectUrl(sdk.Pointer(true)). + WithComment(sdk.Pointer(true)), + ) + err = client.SecurityIntegrations.AlterSaml2(ctx, unsetRequest) + require.NoError(t, err) + + details, err = client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_FORCE_AUTHN", Type: "Boolean", Value: "false", Default: "false"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_REQUESTED_NAMEID_FORMAT", Type: "String", Value: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", Default: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "SAML2_POST_LOGOUT_REDIRECT_URL", Type: "String", Value: "", Default: ""}) + assert.Contains(t, details, sdk.SecurityIntegrationProperty{Name: "COMMENT", Type: "String", Value: "", Default: ""}) + }) + + t.Run("AlterSAML2Integration - REFRESH SAML2_SNOWFLAKE_PRIVATE_KEY", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + issuer := testClientHelper().Ids.Alpha() + createSAML2Integration(t, id, issuer, nil) + + setRequest := sdk.NewAlterSaml2SecurityIntegrationRequest(id).WithRefreshSaml2SnowflakePrivateKey(sdk.Pointer(true)) + err := client.SecurityIntegrations.AlterSaml2(ctx, setRequest) + require.NoError(t, err) + }) + + t.Run("AlterSAML2Integration - set and unset tags", func(t *testing.T) { + tag, tagCleanup := testClientHelper().Tag.CreateTag(t) + t.Cleanup(tagCleanup) + + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + issuer := testClientHelper().Ids.Alpha() + createSAML2Integration(t, id, issuer, nil) + + tagValue := "abc" + tags := []sdk.TagAssociation{ + { + Name: tag.ID(), + Value: tagValue, + }, + } + alterRequestSetTags := sdk.NewAlterSaml2SecurityIntegrationRequest(id).WithSetTags(tags) + + err := client.SecurityIntegrations.AlterSaml2(ctx, alterRequestSetTags) + require.NoError(t, err) + + returnedTagValue, err := client.SystemFunctions.GetTag(ctx, tag.ID(), id, sdk.ObjectTypeIntegration) + require.NoError(t, err) + + assert.Equal(t, tagValue, returnedTagValue) + + unsetTags := []sdk.ObjectIdentifier{ + tag.ID(), + } + alterRequestUnsetTags := sdk.NewAlterSaml2SecurityIntegrationRequest(id).WithUnsetTags(unsetTags) + + err = client.SecurityIntegrations.AlterSaml2(ctx, alterRequestUnsetTags) + require.NoError(t, err) + + _, err = client.SystemFunctions.GetTag(ctx, tag.ID(), id, sdk.ObjectTypeIntegration) + require.Error(t, err) + }) + + t.Run("AlterSCIMIntegration", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + createSCIMIntegration(t, id, nil) + + networkPolicy, networkPolicyCleanup := testClientHelper().NetworkPolicy.CreateNetworkPolicy(t) + t.Cleanup(networkPolicyCleanup) + + setRequest := sdk.NewAlterScimSecurityIntegrationRequest(id). + WithSet( + sdk.NewScimIntegrationSetRequest(). + WithNetworkPolicy(sdk.Pointer(sdk.NewAccountObjectIdentifier(networkPolicy.Name))). + WithEnabled(sdk.Bool(true)). + WithSyncPassword(sdk.Bool(false)). + WithComment(sdk.String("altered")), + ) + err := client.SecurityIntegrations.AlterScim(ctx, setRequest) + require.NoError(t, err) + + details, err := client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + + assertSCIMDescribe(details, "true", networkPolicy.Name, "GENERIC_SCIM_PROVISIONER", "false", "altered") + + unsetRequest := sdk.NewAlterScimSecurityIntegrationRequest(id). + WithUnset( + sdk.NewScimIntegrationUnsetRequest(). + WithNetworkPolicy(sdk.Bool(true)). + WithSyncPassword(sdk.Bool(true)), + ) + err = client.SecurityIntegrations.AlterScim(ctx, unsetRequest) + require.NoError(t, err) + + details, err = client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + + assertSCIMDescribe(details, "true", "", "GENERIC_SCIM_PROVISIONER", "true", "altered") + }) + + t.Run("AlterSCIMIntegration - set and unset tags", func(t *testing.T) { + tag, tagCleanup := testClientHelper().Tag.CreateTag(t) + t.Cleanup(tagCleanup) + + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + createSCIMIntegration(t, id, nil) + + tagValue := "abc" + tags := []sdk.TagAssociation{ + { + Name: tag.ID(), + Value: tagValue, + }, + } + alterRequestSetTags := sdk.NewAlterScimSecurityIntegrationRequest(id).WithSetTags(tags) + + err := client.SecurityIntegrations.AlterScim(ctx, alterRequestSetTags) + require.NoError(t, err) + + returnedTagValue, err := client.SystemFunctions.GetTag(ctx, tag.ID(), id, sdk.ObjectTypeIntegration) + require.NoError(t, err) + + assert.Equal(t, tagValue, returnedTagValue) + + unsetTags := []sdk.ObjectIdentifier{ + tag.ID(), + } + alterRequestUnsetTags := sdk.NewAlterScimSecurityIntegrationRequest(id).WithUnsetTags(unsetTags) + + err = client.SecurityIntegrations.AlterScim(ctx, alterRequestUnsetTags) + require.NoError(t, err) + + _, err = client.SystemFunctions.GetTag(ctx, tag.ID(), id, sdk.ObjectTypeIntegration) + require.Error(t, err) + }) + + t.Run("Drop", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + createSCIMIntegration(t, id, nil) + + si, err := client.SecurityIntegrations.ShowByID(ctx, id) + require.NotNil(t, si) + require.NoError(t, err) + + err = client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id)) + require.NoError(t, err) + + si, err = client.SecurityIntegrations.ShowByID(ctx, id) + require.Nil(t, si) + require.Error(t, err) + }) + + t.Run("Drop non-existing", func(t *testing.T) { + id := sdk.NewAccountObjectIdentifier("does_not_exist") + + err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(id)) + assert.ErrorIs(t, err, sdk.ErrObjectNotExistOrAuthorized) + }) + + t.Run("Describe", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + createSCIMIntegration(t, id, nil) + + details, err := client.SecurityIntegrations.Describe(ctx, id) + require.NoError(t, err) + + assertSCIMDescribe(details, "false", "", "GENERIC_SCIM_PROVISIONER", "true", "") + }) + + t.Run("ShowByID", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + createSCIMIntegration(t, id, nil) + + si, err := client.SecurityIntegrations.ShowByID(ctx, id) + require.NoError(t, err) + assertSecurityIntegration(t, si, id, "SCIM - GENERIC", false, "") + }) + + t.Run("Show SAML2", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + si1 := createSAML2Integration(t, id, testClientHelper().Ids.Alpha(), nil) + id2 := testClientHelper().Ids.RandomAccountObjectIdentifier() + si2 := createSAML2Integration(t, id2, testClientHelper().Ids.Alpha(), nil) + + returnedIntegrations, err := client.SecurityIntegrations.Show(ctx, sdk.NewShowSecurityIntegrationRequest().WithLike(&sdk.Like{ + Pattern: sdk.Pointer(id.Name()), + })) + require.NoError(t, err) + assert.Contains(t, returnedIntegrations, *si1) + assert.NotContains(t, returnedIntegrations, *si2) + }) + + t.Run("Show SCIM", func(t *testing.T) { + id := testClientHelper().Ids.RandomAccountObjectIdentifier() + si1 := createSCIMIntegration(t, id, nil) + id2 := testClientHelper().Ids.RandomAccountObjectIdentifier() + si2 := createSCIMIntegration(t, id2, nil) + + returnedIntegrations, err := client.SecurityIntegrations.Show(ctx, sdk.NewShowSecurityIntegrationRequest().WithLike(&sdk.Like{ + Pattern: sdk.Pointer(id.Name()), + })) + require.NoError(t, err) + assert.Contains(t, returnedIntegrations, *si1) + assert.NotContains(t, returnedIntegrations, *si2) + }) +}