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

fix: add external table grant to snowflake_grant_privileges_to_role resource #1967

Merged
merged 5 commits into from
Jul 27, 2023
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
6 changes: 3 additions & 3 deletions docs/resources/grant_privileges_to_role.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,14 @@ Optional:
- `all` (Block List, Max: 1) Configures the privilege to be granted on all objects in eihter a database or schema. (see [below for nested schema](#nestedblock--on_schema_object--all))
- `future` (Block List, Max: 1) Configures the privilege to be granted on future objects in eihter a database or schema. (see [below for nested schema](#nestedblock--on_schema_object--future))
- `object_name` (String) The fully qualified name of the object on which privileges will be granted.
- `object_type` (String) The object type of the schema object on which privileges will be granted. Valid values are: USER | RESOURCE MONITOR | WAREHOUSE | DATABASE | INTEGRATION | FAILOVER GROUP | REPLICATION GROUP
- `object_type` (String) The object type of the schema object on which privileges will be granted. Valid values are: ALERT | EVENT TABLE | FILE FORMAT | FUNCTION | PROCEDURE | SECRET | SEQUENCE | PIPE | MASKING POLICY | PASSWORD POLICY | ROW ACCESS POLICY | SESSION POLICY | TAG | STAGE | STREAM | TABLE | EXTERNAL TABLE | TASK | VIEW | MATERIALIZED VIEW

<a id="nestedblock--on_schema_object--all"></a>
### Nested Schema for `on_schema_object.all`

Required:

- `object_type_plural` (String) The plural object type of the schema object on which privileges will be granted. Valid values are: USER | RESOURCE MONITOR | WAREHOUSE | DATABASE | INTEGRATION | FAILOVER GROUP | REPLICATION GROUP
- `object_type_plural` (String) The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS

Optional:

Expand All @@ -239,7 +239,7 @@ Optional:

Required:

- `object_type_plural` (String) The plural object type of the schema object on which privileges will be granted. Valid values are: USER | RESOURCE MONITOR | WAREHOUSE | DATABASE | INTEGRATION | FAILOVER GROUP | REPLICATION GROUP
- `object_type_plural` (String) The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS

Optional:

Expand Down
12 changes: 8 additions & 4 deletions pkg/resources/grant_privileges_to_role.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
"object_type": {
Type: schema.TypeString,
Optional: true,
Description: "The object type of the schema object on which privileges will be granted. Valid values are: USER | RESOURCE MONITOR | WAREHOUSE | DATABASE | INTEGRATION | FAILOVER GROUP | REPLICATION GROUP",
Description: "The object type of the schema object on which privileges will be granted. Valid values are: ALERT | EVENT TABLE | FILE FORMAT | FUNCTION | PROCEDURE | SECRET | SEQUENCE | PIPE | MASKING POLICY | PASSWORD POLICY | ROW ACCESS POLICY | SESSION POLICY | TAG | STAGE | STREAM | TABLE | EXTERNAL TABLE | TASK | VIEW | MATERIALIZED VIEW",
RequiredWith: []string{"on_schema_object.0.object_name"},
ConflictsWith: []string{"on_schema_object.0.all", "on_schema_object.0.future"},
ForceNew: true,
Expand All @@ -140,6 +140,7 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
"STAGE",
"STREAM",
"TABLE",
"EXTERNAL TABLE",
"TASK",
"VIEW",
"MATERIALIZED VIEW",
Expand All @@ -164,7 +165,7 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
"object_type_plural": {
Type: schema.TypeString,
Required: true,
Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: USER | RESOURCE MONITOR | WAREHOUSE | DATABASE | INTEGRATION | FAILOVER GROUP | REPLICATION GROUP",
Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS",
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
"ALERTS",
Expand All @@ -183,6 +184,7 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
"STAGES",
"STREAMS",
"TABLES",
"EXTERNAL TABLES",
"TASKS",
"VIEWS",
"MATERIALIZED VIEWS",
Expand Down Expand Up @@ -216,7 +218,7 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
"object_type_plural": {
Type: schema.TypeString,
Required: true,
Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: USER | RESOURCE MONITOR | WAREHOUSE | DATABASE | INTEGRATION | FAILOVER GROUP | REPLICATION GROUP",
Description: "The plural object type of the schema object on which privileges will be granted. Valid values are: ALERTS | EVENT TABLES | FILE FORMATS | FUNCTIONS | PROCEDURES | SECRETS | SEQUENCES | PIPES | MASKING POLICIES | PASSWORD POLICIES | ROW ACCESS POLICIES | SESSION POLICIES | TAGS | STAGES | STREAMS | TABLES | EXTERNAL TABLES | TASKS | VIEWS | MATERIALIZED VIEWS",
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
"ALERTS",
Expand All @@ -235,6 +237,7 @@ var grantPrivilegesToRoleSchema = map[string]*schema.Schema{
"STAGES",
"STREAMS",
"TABLES",
"EXTERNAL TABLES",
"TASKS",
"VIEWS",
"MATERIALIZED VIEWS",
Expand Down Expand Up @@ -842,7 +845,8 @@ func readAccountRoleGrantPrivileges(ctx context.Context, client *sdk.Client, gra
if !id.Future && grant.GrantedBy.Name() == "" {
continue
}
if grantedOn == grant.GrantedOn {
// grant_on is for future grants, granted_on is for current grants. They function the same way though in a test for matching the object type
if grantedOn == grant.GrantedOn || grantedOn == grant.GrantOn {
privileges = append(privileges, grant.Privilege)
}
}
Expand Down
51 changes: 45 additions & 6 deletions pkg/resources/grant_privileges_to_role_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -763,13 +763,13 @@ func grantPrivilegesToRole_onSchemaObject_futureInSchema(name string, privileges

func TestAccGrantPrivilegesToRole_onSchemaObject_futureInDatabase(t *testing.T) {
name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))

objectType := "TABLES"
resource.ParallelTest(t, resource.TestCase{
Providers: providers(),
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: grantPrivilegesToRole_onSchemaObject_futureInDatabase(name, []string{"SELECT", "REFERENCES"}),
Config: grantPrivilegesToRole_onSchemaObject_futureInDatabase(name, objectType, []string{"SELECT", "REFERENCES"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "role_name", name),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "on_schema_object.#", "1"),
Expand All @@ -783,7 +783,7 @@ func TestAccGrantPrivilegesToRole_onSchemaObject_futureInDatabase(t *testing.T)
},
// REMOVE PRIVILEGE
{
Config: grantPrivilegesToRole_onSchemaObject_futureInDatabase(name, []string{"SELECT"}),
Config: grantPrivilegesToRole_onSchemaObject_futureInDatabase(name, objectType, []string{"SELECT"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "role_name", name),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "privileges.#", "1"),
Expand All @@ -800,7 +800,7 @@ func TestAccGrantPrivilegesToRole_onSchemaObject_futureInDatabase(t *testing.T)
})
}

func grantPrivilegesToRole_onSchemaObject_futureInDatabase(name string, privileges []string) string {
func grantPrivilegesToRole_onSchemaObject_futureInDatabase(name string, objectType string, privileges []string) string {
doubleQuotePrivileges := make([]string, len(privileges))
for i, p := range privileges {
doubleQuotePrivileges[i] = fmt.Sprintf(`"%v"`, p)
Expand All @@ -826,12 +826,12 @@ func grantPrivilegesToRole_onSchemaObject_futureInDatabase(name string, privileg
privileges = [%s]
on_schema_object {
future {
object_type_plural = "TABLES"
object_type_plural = "%s"
in_database = snowflake_database.d.name
}
}
}
`, name, name, name, privilegesString)
`, name, name, name, privilegesString, objectType)
}

func TestAccGrantPrivilegesToRole_multipleResources(t *testing.T) {
Expand Down Expand Up @@ -901,3 +901,42 @@ func grantPrivilegesToRole_multipleResources(name string, privileges1, privilege
}
`, name, privilegesString1, privilegesString2)
}

func TestAccGrantPrivilegesToRole_onSchemaObject_futureInDatabase_externalTable(t *testing.T) {
name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha))
objectType := "EXTERNAL TABLES"
resource.ParallelTest(t, resource.TestCase{
Providers: providers(),
CheckDestroy: nil,
Steps: []resource.TestStep{
{
Config: grantPrivilegesToRole_onSchemaObject_futureInDatabase(name, objectType, []string{"SELECT", "REFERENCES"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "role_name", name),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "on_schema_object.#", "1"),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "on_schema_object.0.future.#", "1"),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "on_schema_object.0.future.0.object_type_plural", "EXTERNAL TABLES"),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "on_schema_object.0.future.0.in_database", name),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "privileges.#", "2"),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "privileges.0", "REFERENCES"),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "privileges.1", "SELECT"),
),
},
// REMOVE PRIVILEGE
{
Config: grantPrivilegesToRole_onSchemaObject_futureInDatabase(name, objectType, []string{"SELECT"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "role_name", name),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "privileges.#", "1"),
resource.TestCheckResourceAttr("snowflake_grant_privileges_to_role.g", "privileges.0", "SELECT"),
),
},
// IMPORT
{
ResourceName: "snowflake_grant_privileges_to_role.g",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
11 changes: 8 additions & 3 deletions pkg/sdk/grants.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Grant struct {
CreatedOn time.Time
Privilege string
GrantedOn ObjectType
GrantOn ObjectType
Name ObjectIdentifier
GrantedTo ObjectType
GranteeName AccountObjectIdentifier
Expand All @@ -50,7 +51,7 @@ type grantRow struct {
}

func (row *grantRow) toGrant() (*Grant, error) {
grantedTo := ObjectType(row.GrantedTo)
grantedTo := ObjectType(strings.ReplaceAll(row.GrantedTo, "_", " "))
granteeName := NewAccountObjectIdentifier(row.GranteeName)
if grantedTo == ObjectTypeShare {
parts := strings.Split(row.GranteeName, ".")
Expand All @@ -60,16 +61,20 @@ func (row *grantRow) toGrant() (*Grant, error) {
grant := &Grant{
CreatedOn: row.CreatedOn,
Privilege: row.Privilege,
GrantedOn: ObjectType(row.GrantedOn),
GrantedTo: grantedTo,
Name: NewAccountObjectIdentifier(strings.Trim(row.Name, "\"")),
GranteeName: granteeName,
GrantOption: row.GrantOption,
GrantedBy: NewAccountObjectIdentifier(row.GrantedBy),
}

// true for current grants
if row.GrantedOn != "" {
grant.GrantedOn = ObjectType(strings.ReplaceAll(row.GrantedOn, "_", " "))
}
// true for future grants
if row.GrantOn != "" {
grant.GrantedOn = ObjectType(row.GrantOn)
grant.GrantOn = ObjectType(strings.ReplaceAll(row.GrantOn, "_", " "))
}
return grant, nil
}
Expand Down
40 changes: 40 additions & 0 deletions pkg/sdk/grants_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,46 @@ func TestInt_GrantAndRevokePrivilegesToAccountRole(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 0, len(grants))
})

t.Run("on future schema object", func(t *testing.T) {
roleTest, roleCleanup := createRole(t, client)
t.Cleanup(roleCleanup)
databaseTest, databaseCleanup := createDatabase(t, client)
t.Cleanup(databaseCleanup)
privileges := &AccountRoleGrantPrivileges{
SchemaObjectPrivileges: []SchemaObjectPrivilege{SchemaObjectPrivilegeSelect},
}
on := &AccountRoleGrantOn{
SchemaObject: &GrantOnSchemaObject{
Future: &GrantOnSchemaObjectIn{
PluralObjectType: PluralObjectTypeExternalTables,
InDatabase: Pointer(databaseTest.ID()),
},
},
}
err := client.Grants.GrantPrivilegesToAccountRole(ctx, privileges, on, roleTest.ID(), nil)
require.NoError(t, err)
grants, err := client.Grants.Show(ctx, &ShowGrantOptions{
Future: Bool(true),
To: &ShowGrantsTo{
Role: roleTest.ID(),
},
})
require.NoError(t, err)
assert.Equal(t, 1, len(grants))
assert.Equal(t, SchemaObjectPrivilegeSelect.String(), grants[0].Privilege)

// now revoke and verify that the grant(s) are gone
err = client.Grants.RevokePrivilegesFromAccountRole(ctx, privileges, on, roleTest.ID(), nil)
require.NoError(t, err)
grants, err = client.Grants.Show(ctx, &ShowGrantOptions{
To: &ShowGrantsTo{
Role: roleTest.ID(),
},
})
require.NoError(t, err)
assert.Equal(t, 0, len(grants))
})
}

func TestInt_GrantPrivilegeToShare(t *testing.T) {
Expand Down
Loading