Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update password policy attachment #2485

Merged
merged 2 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions docs/resources/user_password_policy_attachment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
92 changes: 36 additions & 56 deletions pkg/resources/user_password_policy_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -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()))
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved

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
}
Expand All @@ -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()
Expand All @@ -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
}
46 changes: 23 additions & 23 deletions pkg/resources/user_password_policy_attachment_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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)
}
Loading
Loading