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: Allow multiple resources of the same object grant #824

Merged
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
2 changes: 2 additions & 0 deletions pkg/resources/account_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func AccountGrant() *TerraformGrantResource {
func CreateAccountGrant(d *schema.ResourceData, meta interface{}) error {
priv := d.Get("privilege").(string)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

builder := snowflake.AccountGrant()

Expand All @@ -86,6 +87,7 @@ func CreateAccountGrant(d *schema.ResourceData, meta interface{}) error {
ResourceName: "ACCOUNT",
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grantID.String()
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions pkg/resources/account_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestAccountGrantCreate(t *testing.T) { //lintignore:AT003
func TestAccountGrantRead(t *testing.T) {
r := require.New(t)

d := accountGrant(t, "ACCOUNT|||MANAGE GRANTS|true", map[string]interface{}{
d := accountGrant(t, "ACCOUNT|||MANAGE GRANTS||true", map[string]interface{}{
"privilege": "MANAGE GRANTS",
"roles": []interface{}{"test-role-1", "test-role-2"},
"with_grant_option": true,
Expand All @@ -63,7 +63,7 @@ func TestAccountGrantRead(t *testing.T) {
func TestMonitorExecution(t *testing.T) {
r := require.New(t)

d := accountGrant(t, "ACCOUNT|||MONITOR EXECUTION|true", map[string]interface{}{
d := accountGrant(t, "ACCOUNT|||MONITOR EXECUTION||true", map[string]interface{}{
"privilege": "MONITOR EXECUTION",
"roles": []interface{}{"test-role-1", "test-role-2"},
"with_grant_option": true,
Expand All @@ -81,7 +81,7 @@ func TestMonitorExecution(t *testing.T) {
func TestExecuteTask(t *testing.T) {
r := require.New(t)

d := accountGrant(t, "ACCOUNT|||EXECUTE TASK|false", map[string]interface{}{
d := accountGrant(t, "ACCOUNT|||EXECUTE TASK||false", map[string]interface{}{
"privilege": "EXECUTE TASK",
"roles": []interface{}{"test-role-1", "test-role-2"},
"with_grant_option": false,
Expand Down Expand Up @@ -110,7 +110,7 @@ func expectReadAccountGrant(mock sqlmock.Sqlmock) {
func TestApplyMaskingPolicy(t *testing.T) {
r := require.New(t)

d := accountGrant(t, "ACCOUNT|||APPLY MASKING POLICY|true", map[string]interface{}{
d := accountGrant(t, "ACCOUNT|||APPLY MASKING POLICY||true", map[string]interface{}{
"privilege": "APPLY MASKING POLICY",
"roles": []interface{}{"test-role-1", "test-role-2"},
"with_grant_option": true,
Expand Down
3 changes: 3 additions & 0 deletions pkg/resources/database_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var databaseGrantSchema = map[string]*schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
ForceNew: true,
Description: "Grants privilege to these roles.",
},
"shares": {
Expand Down Expand Up @@ -77,6 +78,7 @@ func CreateDatabaseGrant(d *schema.ResourceData, meta interface{}) error {
builder := snowflake.DatabaseGrant(dbName)
priv := d.Get("privilege").(string)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

err := createGenericGrant(d, meta, builder)
if err != nil {
Expand All @@ -87,6 +89,7 @@ func CreateDatabaseGrant(d *schema.ResourceData, meta interface{}) error {
ResourceName: dbName,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/database_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestDatabaseGrantCreate(t *testing.T) {
func TestDatabaseGrantRead(t *testing.T) {
r := require.New(t)

d := databaseGrant(t, "test-database|||USAGE|false", map[string]interface{}{
d := databaseGrant(t, "test-database|||USAGE||false", map[string]interface{}{
"database_name": "test-database",
"privilege": "USAGE",
"roles": []interface{}{},
Expand Down
2 changes: 2 additions & 0 deletions pkg/resources/external_table_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func CreateExternalTableGrant(d *schema.ResourceData, meta interface{}) error {
priv := d.Get("privilege").(string)
futureExternalTables := d.Get("on_future").(bool)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

if (externalTableName == "") && !futureExternalTables {
return errors.New("external_table_name must be set unless on_future is true.")
Expand All @@ -124,6 +125,7 @@ func CreateExternalTableGrant(d *schema.ResourceData, meta interface{}) error {
ObjectName: externalTableName,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/external_table_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestExternalTableGrantCreate(t *testing.T) {
func TestExternalTableGrantRead(t *testing.T) {
r := require.New(t)

d := externalTableGrant(t, "test-db|PUBLIC|test-external-table|SELECT|false", map[string]interface{}{
d := externalTableGrant(t, "test-db|PUBLIC|test-external-table|SELECT||false", map[string]interface{}{
"external_table_name": "test-external-table",
"schema_name": "PUBLIC",
"database_name": "test-db",
Expand Down
2 changes: 2 additions & 0 deletions pkg/resources/file_format_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func CreateFileFormatGrant(d *schema.ResourceData, meta interface{}) error {
priv := d.Get("privilege").(string)
futureFileFormats := d.Get("on_future").(bool)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

if (fileFormatName == "") && !futureFileFormats {
return errors.New("file_format_name must be set unless on_future is true.")
Expand All @@ -116,6 +117,7 @@ func CreateFileFormatGrant(d *schema.ResourceData, meta interface{}) error {
ObjectName: fileFormatName,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/file_format_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestFileFormatGrantCreate(t *testing.T) {
func TestFileFormatGrantRead(t *testing.T) {
r := require.New(t)

d := fileFormatGrant(t, "test-db|PUBLIC|test-file-format|USAGE|false", map[string]interface{}{
d := fileFormatGrant(t, "test-db|PUBLIC|test-file-format|USAGE||false", map[string]interface{}{
"file_format_name": "test-file-format",
"schema_name": "PUBLIC",
"database_name": "test-db",
Expand Down
2 changes: 2 additions & 0 deletions pkg/resources/function_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func CreateFunctionGrant(d *schema.ResourceData, meta interface{}) error {
futureFunctions := d.Get("on_future").(bool)
grantOption := d.Get("with_grant_option").(bool)
arguments = d.Get("arguments").([]interface{})
roles := expandStringList(d.Get("roles").(*schema.Set).List())

if (functionName == "") && !futureFunctions {
return errors.New("function_name must be set unless on_future is true.")
Expand Down Expand Up @@ -169,6 +170,7 @@ func CreateFunctionGrant(d *schema.ResourceData, meta interface{}) error {
ObjectName: functionSignature,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/function_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestFunctionGrantCreate(t *testing.T) {
func TestFunctionGrantRead(t *testing.T) {
r := require.New(t)

d := functionGrant(t, "test-db|PUBLIC|test-function(A ARRAY, B STRING):STRING|USAGE|false", map[string]interface{}{
d := functionGrant(t, "test-db|PUBLIC|test-function(A ARRAY, B STRING):STRING|USAGE||false", map[string]interface{}{
"function_name": "test-function",
"arguments": []interface{}{map[string]interface{}{
"name": "a",
Expand Down
23 changes: 14 additions & 9 deletions pkg/resources/grant_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,19 @@ type grantID struct {
SchemaName string
ObjectName string
Privilege string
Roles []string
GrantOption bool
}

// String() takes in a grantID object and returns a pipe-delimited string:
// resourceName|schemaName|ObjectName|Privilege|GrantOption
// resourceName|schemaName|ObjectName|Privilege|Roles|GrantOption
func (gi *grantID) String() (string, error) {
var buf bytes.Buffer
csvWriter := csv.NewWriter(&buf)
csvWriter.Comma = grantIDDelimiter
grantOption := fmt.Sprintf("%v", gi.GrantOption)
dataIdentifiers := [][]string{{gi.ResourceName, gi.SchemaName, gi.ObjectName, gi.Privilege, grantOption}}
roles := strings.Join(gi.Roles, ",")
dataIdentifiers := [][]string{{gi.ResourceName, gi.SchemaName, gi.ObjectName, gi.Privilege, roles, grantOption}}
err := csvWriter.WriteAll(dataIdentifiers)
if err != nil {
return "", err
Expand All @@ -100,7 +102,7 @@ func (gi *grantID) String() (string, error) {
return strGrantID, nil
}

// grantIDFromString() takes in a pipe-delimited string: resourceName|schemaName|ObjectName|Privilege
// grantIDFromString() takes in a pipe-delimited string: resourceName|schemaName|ObjectName|Privilege|Roles
// and returns a grantID object
func grantIDFromString(stringID string) (*grantID, error) {
reader := csv.NewReader(strings.NewReader(stringID))
Expand All @@ -113,12 +115,12 @@ func grantIDFromString(stringID string) (*grantID, error) {
if len(lines) != 1 {
return nil, fmt.Errorf("1 line per grant")
}
if len(lines[0]) != 4 && len(lines[0]) != 5 {
return nil, fmt.Errorf("4 or 5 fields allowed")
if len(lines[0]) != 5 && len(lines[0]) != 6 {
return nil, fmt.Errorf("5 or 6 fields allowed")
}

grantOption := false
if len(lines[0]) == 5 && lines[0][4] == "true" {
if len(lines[0]) == 6 && lines[0][5] == "true" {
grantOption = true
}

Expand All @@ -127,6 +129,7 @@ func grantIDFromString(stringID string) (*grantID, error) {
SchemaName: lines[0][1],
ObjectName: lines[0][2],
Privilege: lines[0][3],
Roles: strings.Split(lines[0][4], ","),
GrantOption: grantOption,
}
return grantResult, nil
Expand Down Expand Up @@ -176,7 +179,7 @@ func createGenericGrant(d *schema.ResourceData, meta interface{}, builder snowfl
func readGenericGrant(
d *schema.ResourceData,
meta interface{},
schema map[string]*schema.Schema,
grantSchema map[string]*schema.Schema,
builder snowflake.GrantBuilder,
futureObjects bool,
validPrivileges PrivilegeSet) error {
Expand Down Expand Up @@ -243,11 +246,13 @@ func readGenericGrant(
}
}

existingRoles := d.Get("roles").(*schema.Set)
var roles, shares []string
// Now see which roles have our privilege
for roleName, privileges := range rolePrivileges {
// Where priv is not all so it should match exactly
if privileges.hasString(priv) {
// Match to currently assigned roles or let everything through if no specific role grants
if privileges.hasString(priv) && (existingRoles.Contains(roleName) || existingRoles.Len() == 0) {
roles = append(roles, roleName)
}
}
Expand All @@ -269,7 +274,7 @@ func readGenericGrant(
return err
}

_, sharesOk := schema["shares"]
_, sharesOk := grantSchema["shares"]
if sharesOk && !futureObjects {
err = d.Set("shares", shares)
if err != nil {
Expand Down
20 changes: 11 additions & 9 deletions pkg/resources/grant_helpers_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func TestGrantIDFromString(t *testing.T) {
r := require.New(t)
// Vanilla without GrantOption
id := "database_name|schema|view_name|privilege"
id := "database_name|schema|view_name|privilege|test1,test2"
grant, err := grantIDFromString(id)
r.NoError(err)

Expand All @@ -21,7 +21,7 @@ func TestGrantIDFromString(t *testing.T) {
r.Equal(false, grant.GrantOption)

// Vanilla with GrantOption
id = "database_name|schema|view_name|privilege|true"
id = "database_name|schema|view_name|privilege|test1,test2|true"
grant, err = grantIDFromString(id)
r.NoError(err)

Expand All @@ -44,17 +44,17 @@ func TestGrantIDFromString(t *testing.T) {
// Bad ID -- not enough fields
id = "database|name-privilege"
_, err = grantIDFromString(id)
r.Equal(fmt.Errorf("4 or 5 fields allowed"), err)
r.Equal(fmt.Errorf("5 or 6 fields allowed"), err)

// Bad ID -- privilege in wrong area
id = "database||name-privilege"
_, err = grantIDFromString(id)
r.Equal(fmt.Errorf("4 or 5 fields allowed"), err)
r.Equal(fmt.Errorf("5 or 6 fields allowed"), err)

// too many fields
id = "database_name|schema|view_name|privilege|false|2"
id = "database_name|schema|view_name|privilege|false|2|too-many"
_, err = grantIDFromString(id)
r.Equal(fmt.Errorf("4 or 5 fields allowed"), err)
r.Equal(fmt.Errorf("5 or 6 fields allowed"), err)

// 0 lines
id = ""
Expand All @@ -70,31 +70,32 @@ func TestGrantIDFromString(t *testing.T) {

func TestGrantStruct(t *testing.T) {
r := require.New(t)

// Vanilla
grant := &grantID{
ResourceName: "database_name",
SchemaName: "schema",
ObjectName: "view_name",
Privilege: "priv",
Roles: []string{"test1", "test2"},
GrantOption: true,
}
gID, err := grant.String()
r.NoError(err)
r.Equal("database_name|schema|view_name|priv|true", gID)
r.Equal("database_name|schema|view_name|priv|test1,test2|true", gID)

// Empty grant
grant = &grantID{}
gID, err = grant.String()
r.NoError(err)
r.Equal("||||false", gID)
r.Equal("|||||false", gID)

// Grant with extra delimiters
grant = &grantID{
ResourceName: "database|name",
SchemaName: "schema|name",
ObjectName: "view|name",
Privilege: "priv",
Roles: []string{"test3", "test4"},
GrantOption: false,
}
gID, err = grant.String()
Expand All @@ -105,5 +106,6 @@ func TestGrantStruct(t *testing.T) {
r.Equal("schema|name", newGrant.SchemaName)
r.Equal("view|name", newGrant.ObjectName)
r.Equal("priv", newGrant.Privilege)
r.Equal([]string{"test3", "test4"}, newGrant.Roles)
r.Equal(false, newGrant.GrantOption)
}
3 changes: 3 additions & 0 deletions pkg/resources/integration_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ func CreateIntegrationGrant(d *schema.ResourceData, meta interface{}) error {
w := d.Get("integration_name").(string)
priv := d.Get("privilege").(string)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

builder := snowflake.IntegrationGrant(w)

err := createGenericGrant(d, meta, builder)
Expand All @@ -74,6 +76,7 @@ func CreateIntegrationGrant(d *schema.ResourceData, meta interface{}) error {
ResourceName: w,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/integration_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestIntegrationGrantCreate(t *testing.T) {
func TestIntegrationGrantRead(t *testing.T) {
r := require.New(t)

d := integrationGrant(t, "test-integration|||IMPORTED PRIVILIGES|false", map[string]interface{}{
d := integrationGrant(t, "test-integration|||IMPORTED PRIVILIGES||false", map[string]interface{}{
"integration_name": "test-integration",
"privilege": "IMPORTED PRIVILIGES",
"roles": []interface{}{"test-role-1", "test-role-2"},
Expand Down
2 changes: 2 additions & 0 deletions pkg/resources/masking_policy_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func CreateMaskingPolicyGrant(d *schema.ResourceData, meta interface{}) error {
schemaName := d.Get("schema_name").(string)
priv := d.Get("privilege").(string)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

builder := snowflake.MaskingPolicyGrant(dbName, schemaName, maskingPolicyName)

Expand All @@ -95,6 +96,7 @@ func CreateMaskingPolicyGrant(d *schema.ResourceData, meta interface{}) error {
ObjectName: maskingPolicyName,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/resources/masking_policy_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestMaskingPolicyGrantCreate(t *testing.T) {
func TestMaskingPolicyGrantRead(t *testing.T) {
r := require.New(t)

d := maskingPolicyGrant(t, "test-db|PUBLIC|test-masking-policy|APPLY|false", map[string]interface{}{
d := maskingPolicyGrant(t, "test-db|PUBLIC|test-masking-policy|APPLY||false", map[string]interface{}{
"masking_policy_name": "test-masking-policy",
"schema_name": "PUBLIC",
"database_name": "test-db",
Expand Down
2 changes: 2 additions & 0 deletions pkg/resources/materialized_view_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func CreateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error
priv := d.Get("privilege").(string)
futureMaterializedViews := d.Get("on_future").(bool)
grantOption := d.Get("with_grant_option").(bool)
roles := expandStringList(d.Get("roles").(*schema.Set).List())

if (schemaName == "") && !futureMaterializedViews {
return errors.New("schema_name must be set unless on_future is true.")
Expand Down Expand Up @@ -134,6 +135,7 @@ func CreateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error
ObjectName: materializedViewName,
Privilege: priv,
GrantOption: grantOption,
Roles: roles,
}
dataIDInput, err := grant.String()
if err != nil {
Expand Down
Loading