Skip to content

Commit

Permalink
fix: add external table grant to snowflake_grant_privileges_to_role r…
Browse files Browse the repository at this point in the history
…esource (#1967)

* external table grant

* add type signature

* update docs
  • Loading branch information
sfc-gh-swinkler authored Jul 27, 2023
1 parent 9947b44 commit 8c84c4a
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 76 deletions.
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

0 comments on commit 8c84c4a

Please sign in to comment.