From 6ec9ff7128d7b4bb2cf87f6cdbc57216c966b0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Tue, 13 Feb 2024 08:24:13 +0100 Subject: [PATCH] chore: update password policy attachment (#2485) Continue the change for password policy attachment (to a user): - minor resource changes - minor test changes - changed returned field types for policy_references call - added code conventions - added integration test --- .../user_password_policy_attachment.md | 4 +- .../user_password_policy_attachment.go | 92 +++++------- ...sword_policy_attachment_acceptance_test.go | 46 +++--- pkg/sdk/policy_references.go | 102 +++++++------ pkg/sdk/policy_references_dto.go | 17 ++- pkg/sdk/policy_references_dto_builders_gen.go | 15 ++ pkg/sdk/policy_references_impl.go | 19 +-- pkg/sdk/policy_references_test.go | 140 +++++++----------- pkg/sdk/policy_references_validation.go | 18 ++- .../policy_references_integration_test.go | 65 ++++++++ 10 files changed, 275 insertions(+), 243 deletions(-) create mode 100644 pkg/sdk/policy_references_dto_builders_gen.go create mode 100644 pkg/sdk/testint/policy_references_integration_test.go diff --git a/docs/resources/user_password_policy_attachment.md b/docs/resources/user_password_policy_attachment.md index 6edcb557e9..134e05c4ab 100644 --- a/docs/resources/user_password_policy_attachment.md +++ b/docs/resources/user_password_policy_attachment.md @@ -35,9 +35,7 @@ resource "snowflake_user_password_policy_attachment" "ppa" { ### Required -- `password_policy_database` (String) Database name where the password policy is stored -- `password_policy_name` (String) Non-qualified name of the password policy -- `password_policy_schema` (String) Schema name where the password policy is stored +- `password_policy_name` (String) Fully qualified name of the password policy - `user_name` (String) User name of the user you want to attach the password policy to ### Read-Only diff --git a/pkg/resources/user_password_policy_attachment.go b/pkg/resources/user_password_policy_attachment.go index 68a9ee6869..3e14727e39 100644 --- a/pkg/resources/user_password_policy_attachment.go +++ b/pkg/resources/user_password_policy_attachment.go @@ -18,53 +18,35 @@ var userPasswordPolicyAttachmentSchema = map[string]*schema.Schema{ ForceNew: true, Description: "User name of the user you want to attach the password policy to", }, - "password_policy_database": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "Database name where the password policy is stored", - }, - "password_policy_schema": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "Schema name where the password policy is stored", - }, "password_policy_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "Non-qualified name of the password policy", + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Fully qualified name of the password policy", + ValidateDiagFunc: IsValidIdentifier[sdk.SchemaObjectIdentifier](), }, } func UserPasswordPolicyAttachment() *schema.Resource { return &schema.Resource{ Description: "Specifies the password policy to use for a certain user.", - - Create: CreateUserPasswordPolicyAttachment, - Read: ReadUserPasswordPolicyAttachment, - Delete: DeleteUserPasswordPolicyAttachment, - - Schema: userPasswordPolicyAttachmentSchema, - + Create: CreateUserPasswordPolicyAttachment, + Read: ReadUserPasswordPolicyAttachment, + Delete: DeleteUserPasswordPolicyAttachment, + Schema: userPasswordPolicyAttachmentSchema, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, } } -func CreateUserPasswordPolicyAttachment(d *schema.ResourceData, meta interface{}) error { +func CreateUserPasswordPolicyAttachment(d *schema.ResourceData, meta any) error { db := meta.(*sql.DB) client := sdk.NewClientFromDB(db) ctx := context.Background() userName := sdk.NewAccountObjectIdentifierFromFullyQualifiedName(d.Get("user_name").(string)) - passwordPolicy := sdk.NewSchemaObjectIdentifier( - d.Get("password_policy_database").(string), - d.Get("password_policy_schema").(string), - d.Get("password_policy_name").(string), - ) + passwordPolicy := sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(d.Get("password_policy_name").(string)) err := client.Users.Alter(ctx, userName, &sdk.AlterUserOptions{ Set: &sdk.UserSet{ @@ -74,27 +56,25 @@ func CreateUserPasswordPolicyAttachment(d *schema.ResourceData, meta interface{} if err != nil { return err } - d.SetId(fmt.Sprintf(`%s|%s`, helpers.EncodeSnowflakeID(passwordPolicy), helpers.EncodeSnowflakeID(userName))) + + d.SetId(helpers.EncodeSnowflakeID(userName.FullyQualifiedName(), passwordPolicy.FullyQualifiedName())) return ReadUserPasswordPolicyAttachment(d, meta) } -func ReadUserPasswordPolicyAttachment(d *schema.ResourceData, meta interface{}) error { - parts := strings.Split(d.Id(), helpers.IDDelimiter) - if len(parts) != 4 { - // Note: this exception handling is particularly useful when importing - return fmt.Errorf("id should be in the format 'database|schema|password_policy|user_name', but I got '%s'", d.Id()) - } - // Note: there is no alphanumeric id for an attachment, so we retrieve the password policies attached to a certain user. - userName := sdk.NewAccountObjectIdentifierFromFullyQualifiedName(parts[3]) +func ReadUserPasswordPolicyAttachment(d *schema.ResourceData, meta any) error { db := meta.(*sql.DB) client := sdk.NewClientFromDB(db) ctx := context.Background() - policyReferences, err := client.PolicyReferences.GetForEntity(ctx, &sdk.GetForEntityPolicyReferenceRequest{ - // Note: I cannot insert both single and double quotes in the SDK, so for now I need to do this - RefEntityName: userName.FullyQualifiedName(), - RefEntityDomain: "user", - }) + + parts := strings.Split(d.Id(), helpers.IDDelimiter) + if len(parts) != 2 { + return fmt.Errorf("required id format 'user_name|password_policy_name', but got: '%s'", d.Id()) + } + + // Note: there is no alphanumeric id for an attachment, so we retrieve the password policies attached to a certain user. + userName := sdk.NewAccountObjectIdentifierFromFullyQualifiedName(parts[0]) + policyReferences, err := client.PolicyReferences.GetForEntity(ctx, sdk.NewGetForEntityPolicyReferenceRequest(userName, sdk.PolicyEntityDomainUser)) if err != nil { return err } @@ -109,23 +89,24 @@ func ReadUserPasswordPolicyAttachment(d *schema.ResourceData, meta interface{}) d.SetId("") return nil } - if err := d.Set("password_policy_database", sdk.NewAccountIdentifierFromFullyQualifiedName(policyReferences[0].PolicyDb).Name()); err != nil { - return err - } - if err := d.Set("password_policy_schema", sdk.NewAccountIdentifierFromFullyQualifiedName(policyReferences[0].PolicySchema).Name()); err != nil { - return err - } - if err := d.Set("password_policy_name", sdk.NewAccountIdentifierFromFullyQualifiedName(policyReferences[0].PolicyName).Name()); err != nil { + + if err := d.Set("user_name", userName.Name()); err != nil { return err } - if err := d.Set("user_name", helpers.EncodeSnowflakeID(userName)); err != nil { + if err := d.Set( + "password_policy_name", + sdk.NewSchemaObjectIdentifier( + *policyReferences[0].PolicyDb, + *policyReferences[0].PolicySchema, + policyReferences[0].PolicyName, + ).FullyQualifiedName()); err != nil { return err } + return err } -// DeleteAccountPasswordPolicyAttachment implements schema.DeleteFunc. -func DeleteUserPasswordPolicyAttachment(d *schema.ResourceData, meta interface{}) error { +func DeleteUserPasswordPolicyAttachment(d *schema.ResourceData, meta any) error { db := meta.(*sql.DB) client := sdk.NewClientFromDB(db) ctx := context.Background() @@ -137,12 +118,11 @@ func DeleteUserPasswordPolicyAttachment(d *schema.ResourceData, meta interface{} PasswordPolicy: sdk.Bool(true), }, }) - - d.SetId("") - if err != nil { return err } + d.SetId("") + return nil } diff --git a/pkg/resources/user_password_policy_attachment_acceptance_test.go b/pkg/resources/user_password_policy_attachment_acceptance_test.go index 96a2f1d3a2..c538a778e4 100644 --- a/pkg/resources/user_password_policy_attachment_acceptance_test.go +++ b/pkg/resources/user_password_policy_attachment_acceptance_test.go @@ -16,8 +16,10 @@ import ( ) func TestAcc_UserPasswordPolicyAttachment(t *testing.T) { - prefix := "tst-terraform" + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) - prefix2 := "tst-terraform" + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + userName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + NewUserName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + passwordPolicyName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + newPasswordPolicyName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, @@ -26,18 +28,21 @@ func TestAcc_UserPasswordPolicyAttachment(t *testing.T) { Steps: []resource.TestStep{ // CREATE { - Config: userPasswordPolicyAttachmentConfig("USER", acc.TestDatabaseName, acc.TestSchemaName, prefix), + Config: userPasswordPolicyAttachmentConfig(userName, acc.TestDatabaseName, acc.TestSchemaName, passwordPolicyName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("snowflake_user_password_policy_attachment.ppa", "id"), + resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "user_name", userName), + resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "password_policy_name", sdk.NewSchemaObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName, passwordPolicyName).FullyQualifiedName()), + resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "id", fmt.Sprintf("%s|%s", sdk.NewAccountObjectIdentifier(userName).FullyQualifiedName(), sdk.NewSchemaObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName, passwordPolicyName).FullyQualifiedName())), ), Destroy: false, }, // UPDATE { - Config: userPasswordPolicyAttachmentConfig(fmt.Sprintf("USER_%s", prefix), acc.TestDatabaseName, acc.TestSchemaName, prefix2), + Config: userPasswordPolicyAttachmentConfig(NewUserName, acc.TestDatabaseName, acc.TestSchemaName, newPasswordPolicyName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("snowflake_user_password_policy_attachment.ppa", "id"), - resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "user_name", fmt.Sprintf("USER_%s", prefix)), + resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "user_name", NewUserName), + resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "password_policy_name", sdk.NewSchemaObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName, newPasswordPolicyName).FullyQualifiedName()), + resource.TestCheckResourceAttr("snowflake_user_password_policy_attachment.ppa", "id", fmt.Sprintf("%s|%s", sdk.NewAccountObjectIdentifier(NewUserName).FullyQualifiedName(), sdk.NewSchemaObjectIdentifier(acc.TestDatabaseName, acc.TestSchemaName, newPasswordPolicyName).FullyQualifiedName())), ), }, // IMPORT @@ -55,46 +60,41 @@ func testAccCheckUserPasswordPolicyAttachmentDestroy(s *terraform.State) error { client := sdk.NewClientFromDB(db) ctx := context.Background() for _, rs := range s.RootModule().Resources { - // Note: I leverage the fact that the state during the test is specific to the test case, so there should only be there resources created in this test if rs.Type != "snowflake_user_password_policy_attachment" { continue } - userName := sdk.NewAccountObjectIdentifierFromFullyQualifiedName(rs.Primary.Attributes["user_name"]) - policyReferences, err := client.PolicyReferences.GetForEntity(ctx, &sdk.GetForEntityPolicyReferenceRequest{ - RefEntityName: userName.FullyQualifiedName(), - RefEntityDomain: "user", - }) + policyReferences, err := client.PolicyReferences.GetForEntity(ctx, sdk.NewGetForEntityPolicyReferenceRequest( + sdk.NewAccountObjectIdentifierFromFullyQualifiedName(rs.Primary.Attributes["user_name"]), + sdk.PolicyEntityDomainUser, + )) if err != nil { if strings.Contains(err.Error(), "does not exist or not authorized") { - // Note: this can happen if the Policy Reference or the User have been deleted as well; in this case, just ignore the error + // Note: this can happen if the Policy Reference or the User has been deleted as well; in this case, ignore the error continue } return err } if len(policyReferences) > 0 { - return fmt.Errorf("User Password Policy attachment %v still exists", policyReferences[0].PolicyName) + return fmt.Errorf("user password policy attachment %v still exists", policyReferences[0].PolicyName) } } return nil } -func userPasswordPolicyAttachmentConfig(userName, databaseName, schemaName, prefix string) string { - s := ` +func userPasswordPolicyAttachmentConfig(userName, databaseName, schemaName, passwordPolicyName string) string { + return fmt.Sprintf(` resource "snowflake_user" "user" { name = "%s" } resource "snowflake_password_policy" "pp" { database = "%s" schema = "%s" - name = "pp_%v" + name = "%s" } resource "snowflake_user_password_policy_attachment" "ppa" { - password_policy_database = snowflake_password_policy.pp.database - password_policy_schema = snowflake_password_policy.pp.schema - password_policy_name = snowflake_password_policy.pp.name + password_policy_name = "\"${snowflake_password_policy.pp.database}\".\"${snowflake_password_policy.pp.schema}\".\"${snowflake_password_policy.pp.name}\"" user_name = snowflake_user.user.name } -` - return fmt.Sprintf(s, userName, databaseName, schemaName, prefix) +`, userName, databaseName, schemaName, passwordPolicyName) } diff --git a/pkg/sdk/policy_references.go b/pkg/sdk/policy_references.go index 9cac3db020..b4f6225851 100644 --- a/pkg/sdk/policy_references.go +++ b/pkg/sdk/policy_references.go @@ -5,108 +5,106 @@ import ( "database/sql" ) +var _ convertibleRow[PolicyReference] = new(policyReferenceDBRow) + type PolicyReferences interface { GetForEntity(ctx context.Context, request *GetForEntityPolicyReferenceRequest) ([]PolicyReference, error) } type getForEntityPolicyReferenceOptions struct { - select_ bool `ddl:"static" sql:"SELECT"` - asterisk bool `ddl:"static" sql:"*"` - from bool `ddl:"static" sql:"FROM"` - tableFunction *tableFunction `ddl:"keyword"` -} - -type tableFunction struct { - table *bool `ddl:"keyword" sql:"TABLE"` - policyReferenceFunction *policyReferenceFunction `ddl:"list,parentheses,no_comma"` + selectEverythingFrom bool `ddl:"static" sql:"SELECT * FROM TABLE"` + parameters *policyReferenceParameters `ddl:"list,parentheses,no_comma"` } -type policyReferenceFunction struct { - functionFullyQualifiedName *bool `ddl:"keyword" sql:"SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES"` +type policyReferenceParameters struct { + functionFullyQualifiedName bool `ddl:"static" sql:"SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES"` arguments *policyReferenceFunctionArguments `ddl:"list,parentheses"` } + +type PolicyEntityDomain string + +const ( + PolicyEntityDomainAccount PolicyEntityDomain = "ACCOUNT" + PolicyEntityDomainIntegration PolicyEntityDomain = "INTEGRATION" + PolicyEntityDomainTable PolicyEntityDomain = "TABLE" + PolicyEntityDomainTag PolicyEntityDomain = "TAG" + PolicyEntityDomainUser PolicyEntityDomain = "USER" + PolicyEntityDomainView PolicyEntityDomain = "VIEW" +) + type policyReferenceFunctionArguments struct { - refEntityName []ObjectIdentifier `ddl:"parameter,single_quotes,arrow_equals" sql:"ref_entity_name"` - refEntityDomain *string `ddl:"parameter,single_quotes,arrow_equals" sql:"ref_entity_domain"` + refEntityName []ObjectIdentifier `ddl:"parameter,single_quotes,arrow_equals" sql:"REF_ENTITY_NAME"` + refEntityDomain *PolicyEntityDomain `ddl:"parameter,single_quotes,arrow_equals" sql:"REF_ENTITY_DOMAIN"` } type PolicyReference struct { - PolicyDb string - PolicySchema string + PolicyDb *string + PolicySchema *string PolicyName string PolicyKind string - RefDatabaseName string - RefSchemaName string + RefDatabaseName *string + RefSchemaName *string RefEntityName string RefEntityDomain string - RefColumnName string - RefArgColumnNames string - TagDatabase string - TagSchema string - TagName string + RefColumnName *string + RefArgColumnNames *string + TagDatabase *string + TagSchema *string + TagName *string PolicyStatus string } type policyReferenceDBRow struct { PolicyDb sql.NullString `db:"POLICY_DB"` PolicySchema sql.NullString `db:"POLICY_SCHEMA"` - PolicyName sql.NullString `db:"POLICY_NAME"` - PolicyKind sql.NullString `db:"POLICY_KIND"` + PolicyName string `db:"POLICY_NAME"` + PolicyKind string `db:"POLICY_KIND"` RefDatabaseName sql.NullString `db:"REF_DATABASE_NAME"` RefSchemaName sql.NullString `db:"REF_SCHEMA_NAME"` - RefEntityName sql.NullString `db:"REF_ENTITY_NAME"` - RefEntityDomain sql.NullString `db:"REF_ENTITY_DOMAIN"` + RefEntityName string `db:"REF_ENTITY_NAME"` + RefEntityDomain string `db:"REF_ENTITY_DOMAIN"` RefColumnName sql.NullString `db:"REF_COLUMN_NAME"` RefArgColumnNames sql.NullString `db:"REF_ARG_COLUMN_NAMES"` TagDatabase sql.NullString `db:"TAG_DATABASE"` TagSchema sql.NullString `db:"TAG_SCHEMA"` TagName sql.NullString `db:"TAG_NAME"` - PolicyStatus sql.NullString `db:"POLICY_STATUS"` + PolicyStatus string `db:"POLICY_STATUS"` } func (row policyReferenceDBRow) convert() *PolicyReference { - policyReference := PolicyReference{} + policyReference := PolicyReference{ + PolicyName: row.PolicyName, + PolicyKind: row.PolicyKind, + RefEntityName: row.RefEntityName, + RefEntityDomain: row.RefEntityDomain, + PolicyStatus: row.PolicyStatus, + } if row.PolicyDb.Valid { - policyReference.PolicyDb = row.PolicyDb.String + policyReference.PolicyDb = &row.PolicyDb.String } if row.PolicySchema.Valid { - policyReference.PolicySchema = row.PolicySchema.String - } - if row.PolicyName.Valid { - policyReference.PolicyName = row.PolicyName.String - } - if row.PolicyKind.Valid { - policyReference.PolicyKind = row.PolicyKind.String + policyReference.PolicySchema = &row.PolicySchema.String } if row.RefDatabaseName.Valid { - policyReference.RefDatabaseName = row.RefDatabaseName.String + policyReference.RefDatabaseName = &row.RefDatabaseName.String } if row.RefSchemaName.Valid { - policyReference.RefSchemaName = row.RefSchemaName.String - } - if row.RefEntityName.Valid { - policyReference.RefEntityName = row.RefEntityName.String - } - if row.RefEntityDomain.Valid { - policyReference.RefEntityDomain = row.RefEntityDomain.String + policyReference.RefSchemaName = &row.RefSchemaName.String } if row.RefColumnName.Valid { - policyReference.RefColumnName = row.RefColumnName.String + policyReference.RefColumnName = &row.RefColumnName.String } if row.RefArgColumnNames.Valid { - policyReference.RefArgColumnNames = row.RefArgColumnNames.String + policyReference.RefArgColumnNames = &row.RefArgColumnNames.String } if row.TagDatabase.Valid { - policyReference.TagDatabase = row.TagDatabase.String + policyReference.TagDatabase = &row.TagDatabase.String } if row.TagSchema.Valid { - policyReference.TagSchema = row.TagSchema.String + policyReference.TagSchema = &row.TagSchema.String } if row.TagName.Valid { - policyReference.TagName = row.TagName.String - } - if row.PolicyStatus.Valid { - policyReference.PolicyStatus = row.PolicyStatus.String + policyReference.TagName = &row.TagName.String } return &policyReference } diff --git a/pkg/sdk/policy_references_dto.go b/pkg/sdk/policy_references_dto.go index 416df3f490..d34468159a 100644 --- a/pkg/sdk/policy_references_dto.go +++ b/pkg/sdk/policy_references_dto.go @@ -2,7 +2,20 @@ package sdk var _ optionsProvider[getForEntityPolicyReferenceOptions] = new(GetForEntityPolicyReferenceRequest) +//go:generate go run ./dto-builder-generator/main.go + type GetForEntityPolicyReferenceRequest struct { - RefEntityName string - RefEntityDomain string + RefEntityName ObjectIdentifier // required + RefEntityDomain PolicyEntityDomain // required +} + +func (request *GetForEntityPolicyReferenceRequest) toOpts() *getForEntityPolicyReferenceOptions { + return &getForEntityPolicyReferenceOptions{ + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{request.RefEntityName}, + refEntityDomain: Pointer(request.RefEntityDomain), + }, + }, + } } diff --git a/pkg/sdk/policy_references_dto_builders_gen.go b/pkg/sdk/policy_references_dto_builders_gen.go new file mode 100644 index 0000000000..43724b75f7 --- /dev/null +++ b/pkg/sdk/policy_references_dto_builders_gen.go @@ -0,0 +1,15 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewGetForEntityPolicyReferenceRequest( + RefEntityName ObjectIdentifier, + RefEntityDomain PolicyEntityDomain, +) *GetForEntityPolicyReferenceRequest { + s := GetForEntityPolicyReferenceRequest{} + s.RefEntityName = RefEntityName + s.RefEntityDomain = RefEntityDomain + return &s +} diff --git a/pkg/sdk/policy_references_impl.go b/pkg/sdk/policy_references_impl.go index 751aba9389..743eb848de 100644 --- a/pkg/sdk/policy_references_impl.go +++ b/pkg/sdk/policy_references_impl.go @@ -2,6 +2,8 @@ package sdk import "context" +var _ PolicyReferences = new(policyReference) + type policyReference struct { client *Client } @@ -12,23 +14,6 @@ func (v *policyReference) GetForEntity(ctx context.Context, request *GetForEntit if err != nil { return nil, err } - resultList := convertRows[policyReferenceDBRow, PolicyReference](dbRows) - return resultList, nil } - -func (request *GetForEntityPolicyReferenceRequest) toOpts() *getForEntityPolicyReferenceOptions { - return &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{NewObjectIdentifierFromFullyQualifiedName(request.RefEntityName)}, - refEntityDomain: String(request.RefEntityDomain), - }, - }, - }, - } -} diff --git a/pkg/sdk/policy_references_test.go b/pkg/sdk/policy_references_test.go index f25ea82368..2154f957c4 100644 --- a/pkg/sdk/policy_references_test.go +++ b/pkg/sdk/policy_references_test.go @@ -1,143 +1,113 @@ package sdk import ( - "strings" "testing" ) func TestPolicyReferencesGetForEntity(t *testing.T) { - userName := NewAccountObjectIdentifierFromFullyQualifiedName("USER") + t.Run("validation: missing parameters", func(t *testing.T) { + opts := &getForEntityPolicyReferenceOptions{} + assertOptsInvalidJoinedErrors(t, opts, errNotSet("getForEntityPolicyReferenceOptions", "parameters")) + }) + + t.Run("validation: missing arguments", func(t *testing.T) { + opts := &getForEntityPolicyReferenceOptions{ + parameters: &policyReferenceParameters{}, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("policyReferenceParameters", "arguments")) + }) t.Run("validation: missing refEntityName", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: nil, - refEntityDomain: String("user"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityDomain: Pointer(PolicyEntityDomainUser), }, }, } - assertOptsInvalidJoinedErrors(t, opts, errNotSet("getForEntityPolicyReferenceOptions", "refEntityName")) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("policyReferenceFunctionArguments", "refEntityName")) }) t.Run("validation: missing refEntityDomain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{userName}, - refEntityDomain: nil, - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewAccountObjectIdentifierFromFullyQualifiedName("user_name")}, }, }, } - assertOptsInvalidJoinedErrors(t, opts, errNotSet("getForEntityPolicyReferenceOptions", "refEntityDomain")) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("policyReferenceFunctionArguments", "refEntityDomain")) }) - t.Run("validation: domain: user", func(t *testing.T) { + t.Run("user domain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{userName}, - refEntityDomain: String("user"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewAccountObjectIdentifier("user_name")}, + refEntityDomain: Pointer(PolicyEntityDomainUser), }, }, } - assertOptsValidAndSQLEquals(t, opts, "SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (ref_entity_name => '%s', ref_entity_domain => 'user'))", strings.ReplaceAll(userName.FullyQualifiedName(), `"`, `\"`)) + assertOptsValidAndSQLEquals(t, opts, `SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (REF_ENTITY_NAME => '\"user_name\"', REF_ENTITY_DOMAIN => 'USER'))`) }) - tableName := NewSchemaObjectIdentifier("db", "schema", "table") - t.Run("validation: domain: table", func(t *testing.T) { + t.Run("table domain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{tableName}, - refEntityDomain: String("table"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewSchemaObjectIdentifier("db", "schema", "table")}, + refEntityDomain: Pointer(PolicyEntityDomainTable), }, }, } - assertOptsValidAndSQLEquals(t, opts, "SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (ref_entity_name => '%s', ref_entity_domain => 'table'))", strings.ReplaceAll(tableName.FullyQualifiedName(), `"`, `\"`)) + assertOptsValidAndSQLEquals(t, opts, `SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (REF_ENTITY_NAME => '\"db\".\"schema\".\"table\"', REF_ENTITY_DOMAIN => 'TABLE'))`) }) - accountName := NewAccountObjectIdentifier("account") - t.Run("validation: domain: account", func(t *testing.T) { + t.Run("account domain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{accountName}, - refEntityDomain: String("account"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewAccountObjectIdentifier("account_name")}, + refEntityDomain: Pointer(PolicyEntityDomainAccount), }, }, } - assertOptsValidAndSQLEquals(t, opts, "SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (ref_entity_name => '%s', ref_entity_domain => 'account'))", strings.ReplaceAll(accountName.FullyQualifiedName(), `"`, `\"`)) + assertOptsValidAndSQLEquals(t, opts, `SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (REF_ENTITY_NAME => '\"account_name\"', REF_ENTITY_DOMAIN => 'ACCOUNT'))`) }) - integrationName := NewAccountObjectIdentifier("integration") - t.Run("validation: domain: integration", func(t *testing.T) { + t.Run("integration domain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{integrationName}, - refEntityDomain: String("integration"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewAccountObjectIdentifier("integration_name")}, + refEntityDomain: Pointer(PolicyEntityDomainIntegration), }, }, } - assertOptsValidAndSQLEquals(t, opts, "SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (ref_entity_name => '%s', ref_entity_domain => 'integration'))", strings.ReplaceAll(integrationName.FullyQualifiedName(), `"`, `\"`)) + assertOptsValidAndSQLEquals(t, opts, `SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (REF_ENTITY_NAME => '\"integration_name\"', REF_ENTITY_DOMAIN => 'INTEGRATION'))`) }) - tagName := NewSchemaObjectIdentifier("db", "schema", "tag") - t.Run("validation: domain: tag", func(t *testing.T) { + t.Run("tag domain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{tagName}, - refEntityDomain: String("tag"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewSchemaObjectIdentifier("db", "schema", "tag_name")}, + refEntityDomain: Pointer(PolicyEntityDomainTag), }, }, } - assertOptsValidAndSQLEquals(t, opts, "SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (ref_entity_name => '%s', ref_entity_domain => 'tag'))", strings.ReplaceAll(tagName.FullyQualifiedName(), `"`, `\"`)) + assertOptsValidAndSQLEquals(t, opts, `SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (REF_ENTITY_NAME => '\"db\".\"schema\".\"tag_name\"', REF_ENTITY_DOMAIN => 'TAG'))`) }) - viewName := NewSchemaObjectIdentifier("db", "schema", "view") - t.Run("validation: domain: integration", func(t *testing.T) { + t.Run("view domain", func(t *testing.T) { opts := &getForEntityPolicyReferenceOptions{ - tableFunction: &tableFunction{ - table: Bool(true), - policyReferenceFunction: &policyReferenceFunction{ - functionFullyQualifiedName: Bool(true), - arguments: &policyReferenceFunctionArguments{ - refEntityName: []ObjectIdentifier{viewName}, - refEntityDomain: String("view"), - }, + parameters: &policyReferenceParameters{ + arguments: &policyReferenceFunctionArguments{ + refEntityName: []ObjectIdentifier{NewSchemaObjectIdentifier("db", "schema", "view_name")}, + refEntityDomain: Pointer(PolicyEntityDomainView), }, }, } - assertOptsValidAndSQLEquals(t, opts, "SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (ref_entity_name => '%s', ref_entity_domain => 'view'))", strings.ReplaceAll(viewName.FullyQualifiedName(), `"`, `\"`)) + assertOptsValidAndSQLEquals(t, opts, `SELECT * FROM TABLE (SNOWFLAKE.INFORMATION_SCHEMA.POLICY_REFERENCES (REF_ENTITY_NAME => '\"db\".\"schema\".\"view_name\"', REF_ENTITY_DOMAIN => 'VIEW'))`) }) } diff --git a/pkg/sdk/policy_references_validation.go b/pkg/sdk/policy_references_validation.go index e92ed6f7d9..1506895cab 100644 --- a/pkg/sdk/policy_references_validation.go +++ b/pkg/sdk/policy_references_validation.go @@ -11,11 +11,19 @@ func (opts *getForEntityPolicyReferenceOptions) validate() error { return errors.Join(ErrNilOptions) } var errs []error - if opts.tableFunction.policyReferenceFunction.arguments.refEntityDomain == nil { - errs = append(errs, errNotSet("getForEntityPolicyReferenceOptions", "refEntityDomain")) - } - if opts.tableFunction.policyReferenceFunction.arguments.refEntityName == nil { - errs = append(errs, errNotSet("getForEntityPolicyReferenceOptions", "refEntityName")) + if !valueSet(opts.parameters) { + errs = append(errs, errNotSet("getForEntityPolicyReferenceOptions", "parameters")) + } else { + if !valueSet(opts.parameters.arguments) { + errs = append(errs, errNotSet("policyReferenceParameters", "arguments")) + } else { + if opts.parameters.arguments.refEntityDomain == nil { + errs = append(errs, errNotSet("policyReferenceFunctionArguments", "refEntityDomain")) + } + if opts.parameters.arguments.refEntityName == nil { + errs = append(errs, errNotSet("policyReferenceFunctionArguments", "refEntityName")) + } + } } return errors.Join(errs...) } diff --git a/pkg/sdk/testint/policy_references_integration_test.go b/pkg/sdk/testint/policy_references_integration_test.go new file mode 100644 index 0000000000..c7c4b0b722 --- /dev/null +++ b/pkg/sdk/testint/policy_references_integration_test.go @@ -0,0 +1,65 @@ +package testint + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" + "github.com/stretchr/testify/require" +) + +func TestInt_PolicyReferences(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + passwordPolicyName := sdk.NewSchemaObjectIdentifier(testDb(t).Name, testSchema(t).Name, random.String()) + err := client.PasswordPolicies.Create(ctx, passwordPolicyName, &sdk.CreatePasswordPolicyOptions{}) + require.NoError(t, err) + + t.Cleanup(func() { + err := client.PasswordPolicies.Drop(ctx, passwordPolicyName, &sdk.DropPasswordPolicyOptions{IfExists: sdk.Bool(true)}) + require.NoError(t, err) + }) + + t.Run("user domain", func(t *testing.T) { + user, userCleanup := createUser(t, client) + t.Cleanup(userCleanup) + + err = client.Users.Alter(ctx, user.ID(), &sdk.AlterUserOptions{ + Set: &sdk.UserSet{ + PasswordPolicy: &passwordPolicyName, + }, + }) + require.NoError(t, err) + + policyReferences, err := client.PolicyReferences.GetForEntity(ctx, sdk.NewGetForEntityPolicyReferenceRequest(user.ID(), sdk.PolicyEntityDomainUser)) + require.NoError(t, err) + require.Equal(t, 1, len(policyReferences)) + require.Equal(t, passwordPolicyName.Name(), policyReferences[0].PolicyName) + require.Equal(t, "PASSWORD_POLICY", policyReferences[0].PolicyKind) + }) + + t.Run("tag domain", func(t *testing.T) { + maskingPolicy, maskingPolicyCleanup := createMaskingPolicy(t, client, testDb(t), testSchema(t)) + t.Cleanup(maskingPolicyCleanup) + + tag, tagCleanup := createTag(t, client, testDb(t), testSchema(t)) + t.Cleanup(tagCleanup) + + err = client.Tags.Alter(ctx, sdk.NewAlterTagRequest(tag.ID()).WithSet( + sdk.NewTagSetRequest().WithMaskingPolicies([]sdk.SchemaObjectIdentifier{maskingPolicy.ID()}), + )) + require.NoError(t, err) + + policyReferences, err := client.PolicyReferences.GetForEntity(ctx, sdk.NewGetForEntityPolicyReferenceRequest(tag.ID(), sdk.PolicyEntityDomainTag)) + require.NoError(t, err) + require.Equal(t, 1, len(policyReferences)) + require.Equal(t, maskingPolicy.ID().Name(), policyReferences[0].PolicyName) + require.Equal(t, "MASKING_POLICY", policyReferences[0].PolicyKind) + + err = client.Tags.Alter(ctx, sdk.NewAlterTagRequest(tag.ID()).WithUnset( + sdk.NewTagUnsetRequest().WithMaskingPolicies([]sdk.SchemaObjectIdentifier{maskingPolicy.ID()}), + )) + require.NoError(t, err) + }) +}