diff --git a/pkg/resources/grant_privileges_to_account_role_acceptance_test.go b/pkg/resources/grant_privileges_to_account_role_acceptance_test.go index a4306ecfdb..a2c99d1327 100644 --- a/pkg/resources/grant_privileges_to_account_role_acceptance_test.go +++ b/pkg/resources/grant_privileges_to_account_role_acceptance_test.go @@ -509,6 +509,55 @@ func TestAcc_GrantPrivilegesToAccountRole_OnSchemaObject_OnAll_InDatabase(t *tes }) } +func TestAcc_GrantPrivilegesToAccountRole_OnSchemaObject_OnAllPipes(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + roleName := sdk.NewAccountObjectIdentifier(name).FullyQualifiedName() + databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName() + configVariables := config.Variables{ + "name": config.StringVariable(roleName), + "privileges": config.ListVariable( + config.StringVariable(string(sdk.SchemaObjectPrivilegeMonitor)), + ), + "database": config.StringVariable(databaseName), + "with_grant_option": config.BoolVariable(false), + } + resourceName := "snowflake_grant_privileges_to_account_role.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckAccountRolePrivilegesRevoked(name), + Steps: []resource.TestStep{ + { + PreConfig: func() { createAccountRoleOutsideTerraform(t, name) }, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToAccountRole/OnAllPipes"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "account_role_name", roleName), + resource.TestCheckResourceAttr(resourceName, "privileges.#", "1"), + resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaObjectPrivilegeMonitor)), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.#", "1"), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.#", "1"), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.object_type_plural", string(sdk.PluralObjectTypePipes)), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.in_database", databaseName), + resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"), + resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|MONITOR|OnSchemaObject|OnAll|PIPES|InDatabase|%s", roleName, databaseName)), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToAccountRole/OnAllPipes"), + ConfigVariables: configVariables, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAcc_GrantPrivilegesToAccountRole_OnSchemaObject_OnFuture_InDatabase(t *testing.T) { name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) roleName := sdk.NewAccountObjectIdentifier(name).FullyQualifiedName() diff --git a/pkg/resources/grant_privileges_to_database_role_acceptance_test.go b/pkg/resources/grant_privileges_to_database_role_acceptance_test.go index 2a704dd7c3..e4061199f8 100644 --- a/pkg/resources/grant_privileges_to_database_role_acceptance_test.go +++ b/pkg/resources/grant_privileges_to_database_role_acceptance_test.go @@ -428,6 +428,56 @@ func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnAll_InDatabase(t *te }) } +func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnAllPipes(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "name": config.StringVariable(name), + "privileges": config.ListVariable( + config.StringVariable(string(sdk.SchemaObjectPrivilegeMonitor)), + ), + "database": config.StringVariable(acc.TestDatabaseName), + "with_grant_option": config.BoolVariable(false), + } + resourceName := "snowflake_grant_privileges_to_database_role.test" + + databaseRoleName := sdk.NewDatabaseObjectIdentifier(acc.TestDatabaseName, name).FullyQualifiedName() + databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckDatabaseRolePrivilegesRevoked, + Steps: []resource.TestStep{ + { + PreConfig: func() { createDatabaseRoleOutsideTerraform(t, name) }, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "database_role_name", databaseRoleName), + resource.TestCheckResourceAttr(resourceName, "privileges.#", "1"), + resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaObjectPrivilegeMonitor)), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.#", "1"), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.#", "1"), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.object_type_plural", string(sdk.PluralObjectTypePipes)), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.in_database", databaseName), + resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"), + resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|false|false|MONITOR|OnSchemaObject|OnAll|PIPES|InDatabase|%s", databaseRoleName, databaseName)), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes"), + ConfigVariables: configVariables, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAcc_GrantPrivilegesToDatabaseRole_OnSchemaObject_OnFuture_InDatabase(t *testing.T) { name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) configVariables := config.Variables{ diff --git a/pkg/resources/grant_privileges_to_role_acceptance_test.go b/pkg/resources/grant_privileges_to_role_acceptance_test.go index bd76b12553..3a9b4197dd 100644 --- a/pkg/resources/grant_privileges_to_role_acceptance_test.go +++ b/pkg/resources/grant_privileges_to_role_acceptance_test.go @@ -884,7 +884,7 @@ func grantPrivilegesToRole_multipleResources(name string, privileges1, privilege func TestAcc_GrantPrivilegesToRole_onSchemaObject_futureInDatabase_externalTable(t *testing.T) { name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) objectType := "EXTERNAL TABLES" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ Providers: acc.TestAccProviders(), PreCheck: func() { acc.TestAccPreCheck(t) }, CheckDestroy: nil, @@ -921,6 +921,55 @@ func TestAcc_GrantPrivilegesToRole_onSchemaObject_futureInDatabase_externalTable }) } +func TestAcc_GrantPrivilegesToRole_OnAllPipes(t *testing.T) { + name := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + roleName := sdk.NewAccountObjectIdentifier(name).FullyQualifiedName() + databaseName := sdk.NewAccountObjectIdentifier(acc.TestDatabaseName).FullyQualifiedName() + configVariables := config.Variables{ + "name": config.StringVariable(roleName), + "privileges": config.ListVariable( + config.StringVariable(string(sdk.SchemaObjectPrivilegeMonitor)), + ), + "database": config.StringVariable(databaseName), + "with_grant_option": config.BoolVariable(false), + } + resourceName := "snowflake_grant_privileges_to_role.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: testAccCheckAccountRolePrivilegesRevoked(name), + Steps: []resource.TestStep{ + { + PreConfig: func() { createAccountRoleOutsideTerraform(t, name) }, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToRole/OnAllPipes"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "role_name", roleName), + resource.TestCheckResourceAttr(resourceName, "privileges.#", "1"), + resource.TestCheckResourceAttr(resourceName, "privileges.0", string(sdk.SchemaObjectPrivilegeMonitor)), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.#", "1"), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.#", "1"), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.object_type_plural", string(sdk.PluralObjectTypePipes)), + resource.TestCheckResourceAttr(resourceName, "on_schema_object.0.all.0.in_database", databaseName), + resource.TestCheckResourceAttr(resourceName, "with_grant_option", "false"), + resource.TestCheckResourceAttr(resourceName, "id", fmt.Sprintf("%s|MONITOR|false|false|false|false|false|true|true|false|||PIPES|false||true|%s", roleName, databaseName)), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_GrantPrivilegesToRole/OnAllPipes"), + ConfigVariables: configVariables, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAcc_GrantPrivilegesToRole_onSchemaObject_futureIcebergTables(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToAccountRole/OnAllPipes/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToAccountRole/OnAllPipes/test.tf new file mode 100644 index 0000000000..59af2a9860 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToAccountRole/OnAllPipes/test.tf @@ -0,0 +1,12 @@ +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = var.name + privileges = var.privileges + with_grant_option = var.with_grant_option + + on_schema_object { + all { + object_type_plural = "PIPES" + in_database = var.database + } + } +} diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToAccountRole/OnAllPipes/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToAccountRole/OnAllPipes/variables.tf new file mode 100644 index 0000000000..0e22e903d7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToAccountRole/OnAllPipes/variables.tf @@ -0,0 +1,15 @@ +variable "name" { + type = string +} + +variable "privileges" { + type = list(string) +} + +variable "database" { + type = string +} + +variable "with_grant_option" { + type = bool +} diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes/test.tf new file mode 100644 index 0000000000..a8769b0cda --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes/test.tf @@ -0,0 +1,12 @@ +resource "snowflake_grant_privileges_to_database_role" "test" { + database_role_name = "\"${var.database}\".\"${var.name}\"" + privileges = var.privileges + with_grant_option = var.with_grant_option + + on_schema_object { + all { + object_type_plural = "PIPES" + in_database = "\"${var.database}\"" + } + } +} diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes/variables.tf new file mode 100644 index 0000000000..0e22e903d7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToDatabaseRole/OnAllPipes/variables.tf @@ -0,0 +1,15 @@ +variable "name" { + type = string +} + +variable "privileges" { + type = list(string) +} + +variable "database" { + type = string +} + +variable "with_grant_option" { + type = bool +} diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToRole/OnAllPipes/test.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToRole/OnAllPipes/test.tf new file mode 100644 index 0000000000..dff7e4df30 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToRole/OnAllPipes/test.tf @@ -0,0 +1,12 @@ +resource "snowflake_grant_privileges_to_role" "test" { + role_name = var.name + privileges = var.privileges + with_grant_option = var.with_grant_option + + on_schema_object { + all { + object_type_plural = "PIPES" + in_database = var.database + } + } +} diff --git a/pkg/resources/testdata/TestAcc_GrantPrivilegesToRole/OnAllPipes/variables.tf b/pkg/resources/testdata/TestAcc_GrantPrivilegesToRole/OnAllPipes/variables.tf new file mode 100644 index 0000000000..0e22e903d7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_GrantPrivilegesToRole/OnAllPipes/variables.tf @@ -0,0 +1,15 @@ +variable "name" { + type = string +} + +variable "privileges" { + type = list(string) +} + +variable "database" { + type = string +} + +variable "with_grant_option" { + type = bool +} diff --git a/pkg/sdk/grants_impl.go b/pkg/sdk/grants_impl.go index 5df299e877..bf11482e14 100644 --- a/pkg/sdk/grants_impl.go +++ b/pkg/sdk/grants_impl.go @@ -2,6 +2,7 @@ package sdk import ( "context" + "errors" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" ) @@ -21,6 +22,37 @@ func (v *grants) GrantPrivilegesToAccountRole(ctx context.Context, privileges *A opts.on = on opts.accountRole = role logging.DebugLogger.Printf("[DEBUG] Grant privileges to account role: opts %+v", opts) + + // Snowflake doesn't allow bulk operations on Pipes. Because of that, when SDK user + // issues "grant x on all pipes" operation, we'll go and grant specified privileges + // to every Pipe one by one. + if on != nil && + on.SchemaObject != nil && + on.SchemaObject.All != nil && + on.SchemaObject.All.PluralObjectType == PluralObjectTypePipes { + return v.runOnAllPipes( + ctx, + on.SchemaObject.All.InDatabase, + on.SchemaObject.All.InSchema, + func(pipe Pipe) error { + return v.client.Grants.GrantPrivilegesToAccountRole( + ctx, + privileges, + &AccountRoleGrantOn{ + SchemaObject: &GrantOnSchemaObject{ + SchemaObject: &Object{ + ObjectType: ObjectTypePipe, + Name: NewSchemaObjectIdentifier(pipe.DatabaseName, pipe.SchemaName, pipe.Name), + }, + }, + }, + role, + opts, + ) + }, + ) + } + return validateAndExec(v.client, ctx, opts) } @@ -33,6 +65,37 @@ func (v *grants) RevokePrivilegesFromAccountRole(ctx context.Context, privileges opts.on = on opts.accountRole = role logging.DebugLogger.Printf("[DEBUG] Revoke privileges from account role: opts %+v", opts) + + // Snowflake doesn't allow bulk operations on Pipes. Because of that, when SDK user + // issues "revoke x on all pipes" operation, we'll go and revoke specified privileges + // from every Pipe one by one. + if on != nil && + on.SchemaObject != nil && + on.SchemaObject.All != nil && + on.SchemaObject.All.PluralObjectType == PluralObjectTypePipes { + return v.runOnAllPipes( + ctx, + on.SchemaObject.All.InDatabase, + on.SchemaObject.All.InSchema, + func(pipe Pipe) error { + return v.client.Grants.RevokePrivilegesFromAccountRole( + ctx, + privileges, + &AccountRoleGrantOn{ + SchemaObject: &GrantOnSchemaObject{ + SchemaObject: &Object{ + ObjectType: ObjectTypePipe, + Name: NewSchemaObjectIdentifier(pipe.DatabaseName, pipe.SchemaName, pipe.Name), + }, + }, + }, + role, + opts, + ) + }, + ) + } + return validateAndExec(v.client, ctx, opts) } @@ -43,6 +106,37 @@ func (v *grants) GrantPrivilegesToDatabaseRole(ctx context.Context, privileges * opts.privileges = privileges opts.on = on opts.databaseRole = role + + // Snowflake doesn't allow bulk operations on Pipes. Because of that, when SDK user + // issues "grant x on all pipes" operation, we'll go and grant specified privileges + // to every Pipe one by one. + if on != nil && + on.SchemaObject != nil && + on.SchemaObject.All != nil && + on.SchemaObject.All.PluralObjectType == PluralObjectTypePipes { + return v.runOnAllPipes( + ctx, + on.SchemaObject.All.InDatabase, + on.SchemaObject.All.InSchema, + func(pipe Pipe) error { + return v.client.Grants.GrantPrivilegesToDatabaseRole( + ctx, + privileges, + &DatabaseRoleGrantOn{ + SchemaObject: &GrantOnSchemaObject{ + SchemaObject: &Object{ + ObjectType: ObjectTypePipe, + Name: NewSchemaObjectIdentifier(pipe.DatabaseName, pipe.SchemaName, pipe.Name), + }, + }, + }, + role, + opts, + ) + }, + ) + } + return validateAndExec(v.client, ctx, opts) } @@ -53,6 +147,37 @@ func (v *grants) RevokePrivilegesFromDatabaseRole(ctx context.Context, privilege opts.privileges = privileges opts.on = on opts.databaseRole = role + + // Snowflake doesn't allow bulk operations on Pipes. Because of that, when SDK user + // issues "revoke x on all pipes" operation, we'll go and revoke specified privileges + // from every Pipe one by one. + if on != nil && + on.SchemaObject != nil && + on.SchemaObject.All != nil && + on.SchemaObject.All.PluralObjectType == PluralObjectTypePipes { + return v.runOnAllPipes( + ctx, + on.SchemaObject.All.InDatabase, + on.SchemaObject.All.InSchema, + func(pipe Pipe) error { + return v.client.Grants.RevokePrivilegesFromDatabaseRole( + ctx, + privileges, + &DatabaseRoleGrantOn{ + SchemaObject: &GrantOnSchemaObject{ + SchemaObject: &Object{ + ObjectType: ObjectTypePipe, + Name: NewSchemaObjectIdentifier(pipe.DatabaseName, pipe.SchemaName, pipe.Name), + }, + }, + }, + role, + opts, + ) + }, + ) + } + return validateAndExec(v.client, ctx, opts) } @@ -100,3 +225,31 @@ func (v *grants) Show(ctx context.Context, opts *ShowGrantOptions) ([]Grant, err logging.DebugLogger.Printf("[DEBUG] Show grants: rows converted") return resultList, nil } + +func (v *grants) runOnAllPipes(ctx context.Context, inDatabase *AccountObjectIdentifier, inSchema *DatabaseObjectIdentifier, command func(Pipe) error) error { + var in *In + switch { + case inDatabase != nil: + in = &In{ + Database: *inDatabase, + } + case inSchema != nil: + in = &In{ + Schema: *inSchema, + } + } + + pipes, err := v.client.Pipes.Show(ctx, &ShowPipeOptions{In: in}) + if err != nil { + return err + } + + var errs []error + for _, pipe := range pipes { + if err := command(pipe); err != nil { + errs = append(errs, err) + } + } + + return errors.Join(errs...) +} diff --git a/pkg/sdk/testint/grants_integration_test.go b/pkg/sdk/testint/grants_integration_test.go index 90e8d1a539..1b1a908778 100644 --- a/pkg/sdk/testint/grants_integration_test.go +++ b/pkg/sdk/testint/grants_integration_test.go @@ -5,6 +5,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -13,6 +14,32 @@ func TestInt_GrantAndRevokePrivilegesToAccountRole(t *testing.T) { client := testClient(t) ctx := testContext(t) + assertPrivilegeGrantedOnPipe := func(pipeId sdk.SchemaObjectIdentifier, privilege sdk.SchemaObjectPrivilege) { + grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + On: &sdk.ShowGrantsOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectTypePipe, + Name: pipeId, + }, + }, + }) + require.NoError(t, err) + require.Contains(t, grantsToPrivileges(grants), privilege.String()) + } + + assertPrivilegeNotGrantedOnPipe := func(pipeId sdk.SchemaObjectIdentifier, privilege sdk.SchemaObjectPrivilege) { + grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + On: &sdk.ShowGrantsOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectTypePipe, + Name: pipeId, + }, + }, + }) + require.NoError(t, err) + require.NotContains(t, grantsToPrivileges(grants), privilege.String()) + } + t.Run("on account", func(t *testing.T) { roleTest, roleCleanup := createRole(t, client) t.Cleanup(roleCleanup) @@ -205,12 +232,160 @@ func TestInt_GrantAndRevokePrivilegesToAccountRole(t *testing.T) { require.NoError(t, err) assert.Equal(t, 0, len(grants)) }) + + t.Run("grant and revoke on all pipes", func(t *testing.T) { + schema, schemaCleanup := createSchemaWithIdentifier(t, itc.client, testDb(t), random.AlphaN(20)) + t.Cleanup(schemaCleanup) + + table, tableCleanup := createTable(t, itc.client, testDb(t), schema) + t.Cleanup(tableCleanup) + + stage, stageCleanup := createStage(t, itc.client, sdk.NewSchemaObjectIdentifier(testDb(t).Name, schema.Name, random.AlphaN(20))) + t.Cleanup(stageCleanup) + + pipe, pipeCleanup := createPipe(t, client, testDb(t), schema, random.AlphaN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(pipeCleanup) + + secondPipe, secondPipeCleanup := createPipe(t, client, testDb(t), schema, random.AlphaN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(secondPipeCleanup) + + role, roleCleanup := createRole(t, client) + t.Cleanup(roleCleanup) + + err := client.Grants.GrantPrivilegesToAccountRole( + ctx, + &sdk.AccountRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeMonitor}, + }, + &sdk.AccountRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + role.ID(), + &sdk.GrantPrivilegesToAccountRoleOptions{}, + ) + require.NoError(t, err) + assertPrivilegeGrantedOnPipe(pipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + assertPrivilegeGrantedOnPipe(secondPipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + + err = client.Grants.RevokePrivilegesFromAccountRole( + ctx, + &sdk.AccountRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeMonitor}, + }, + &sdk.AccountRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + role.ID(), + &sdk.RevokePrivilegesFromAccountRoleOptions{}, + ) + require.NoError(t, err) + assertPrivilegeNotGrantedOnPipe(pipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + assertPrivilegeNotGrantedOnPipe(secondPipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + }) + + t.Run("grant and revoke on all pipes with multiple errors", func(t *testing.T) { + schema, schemaCleanup := createSchemaWithIdentifier(t, itc.client, testDb(t), random.AlphaN(20)) + t.Cleanup(schemaCleanup) + + table, tableCleanup := createTable(t, itc.client, testDb(t), schema) + t.Cleanup(tableCleanup) + + stage, stageCleanup := createStage(t, itc.client, sdk.NewSchemaObjectIdentifier(testDb(t).Name, schema.Name, random.AlphaN(20))) + t.Cleanup(stageCleanup) + + _, pipeCleanup := createPipe(t, client, testDb(t), schema, random.AlphaN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(pipeCleanup) + + _, secondPipeCleanup := createPipe(t, client, testDb(t), schema, random.AlphaN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(secondPipeCleanup) + + role, roleCleanup := createRole(t, client) + t.Cleanup(roleCleanup) + + err := client.Grants.GrantPrivilegesToAccountRole( + ctx, + &sdk.AccountRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeInsert}, // Invalid privilege + }, + &sdk.AccountRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + role.ID(), + &sdk.GrantPrivilegesToAccountRoleOptions{}, + ) + require.Error(t, err) + joined, ok := err.(interface{ Unwrap() []error }) //nolint:all + require.True(t, ok) + require.Len(t, joined.Unwrap(), 2) + + err = client.Grants.RevokePrivilegesFromAccountRole( + ctx, + &sdk.AccountRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeInsert}, // Invalid privilege + }, + &sdk.AccountRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + role.ID(), + &sdk.RevokePrivilegesFromAccountRoleOptions{}, + ) + require.Error(t, err) + joined, ok = err.(interface{ Unwrap() []error }) //nolint:all + require.True(t, ok) + require.Len(t, joined.Unwrap(), 2) + }) } func TestInt_GrantAndRevokePrivilegesToDatabaseRole(t *testing.T) { client := testClient(t) ctx := testContext(t) + assertPrivilegeGrantedOnPipe := func(pipeId sdk.SchemaObjectIdentifier, privilege sdk.SchemaObjectPrivilege) { + grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + On: &sdk.ShowGrantsOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectTypePipe, + Name: pipeId, + }, + }, + }) + require.NoError(t, err) + require.Contains(t, grantsToPrivileges(grants), privilege.String()) + } + + assertPrivilegeNotGrantedOnPipe := func(pipeId sdk.SchemaObjectIdentifier, privilege sdk.SchemaObjectPrivilege) { + grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + On: &sdk.ShowGrantsOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectTypePipe, + Name: pipeId, + }, + }, + }) + require.NoError(t, err) + require.NotContains(t, grantsToPrivileges(grants), privilege.String()) + } + t.Run("on database", func(t *testing.T) { databaseRole, _ := createDatabaseRole(t, client, testDb(t)) databaseRoleId := sdk.NewDatabaseObjectIdentifier(testDb(t).Name, databaseRole.Name) @@ -402,6 +577,128 @@ func TestInt_GrantAndRevokePrivilegesToDatabaseRole(t *testing.T) { require.NoError(t, err) assert.Equal(t, 0, len(returnedGrants)) }) + + t.Run("grant and revoke on all pipes", func(t *testing.T) { + schema, schemaCleanup := createSchemaWithIdentifier(t, itc.client, testDb(t), random.AlphaN(20)) + t.Cleanup(schemaCleanup) + + table, tableCleanup := createTable(t, itc.client, testDb(t), schema) + t.Cleanup(tableCleanup) + + stage, stageCleanup := createStage(t, itc.client, sdk.NewSchemaObjectIdentifier(testDb(t).Name, schema.Name, random.AlphaN(20))) + t.Cleanup(stageCleanup) + + pipe, pipeCleanup := createPipe(t, client, testDb(t), schema, random.StringN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(pipeCleanup) + + secondPipe, secondPipeCleanup := createPipe(t, client, testDb(t), schema, random.StringN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(secondPipeCleanup) + + role, roleCleanup := createDatabaseRole(t, client, testDb(t)) + t.Cleanup(roleCleanup) + + err := client.Grants.GrantPrivilegesToDatabaseRole( + ctx, + &sdk.DatabaseRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeMonitor}, + }, + &sdk.DatabaseRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + sdk.NewDatabaseObjectIdentifier(testDb(t).Name, role.Name), + &sdk.GrantPrivilegesToDatabaseRoleOptions{}, + ) + require.NoError(t, err) + assertPrivilegeGrantedOnPipe(pipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + assertPrivilegeGrantedOnPipe(secondPipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + + err = client.Grants.RevokePrivilegesFromDatabaseRole( + ctx, + &sdk.DatabaseRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeMonitor}, + }, + &sdk.DatabaseRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + sdk.NewDatabaseObjectIdentifier(testDb(t).Name, role.Name), + &sdk.RevokePrivilegesFromDatabaseRoleOptions{}, + ) + require.NoError(t, err) + assertPrivilegeNotGrantedOnPipe(pipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + assertPrivilegeNotGrantedOnPipe(secondPipe.ID(), sdk.SchemaObjectPrivilegeMonitor) + }) + + t.Run("grant and revoke on all pipes with multiple errors", func(t *testing.T) { + schema, schemaCleanup := createSchemaWithIdentifier(t, itc.client, testDb(t), random.AlphaN(20)) + t.Cleanup(schemaCleanup) + + table, tableCleanup := createTable(t, itc.client, testDb(t), schema) + t.Cleanup(tableCleanup) + + stage, stageCleanup := createStage(t, itc.client, sdk.NewSchemaObjectIdentifier(testDb(t).Name, schema.Name, random.AlphaN(20))) + t.Cleanup(stageCleanup) + + _, pipeCleanup := createPipe(t, client, testDb(t), schema, random.AlphaN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(pipeCleanup) + + _, secondPipeCleanup := createPipe(t, client, testDb(t), schema, random.AlphaN(20), createPipeCopyStatement(t, table, stage)) + t.Cleanup(secondPipeCleanup) + + role, roleCleanup := createDatabaseRole(t, client, testDb(t)) + t.Cleanup(roleCleanup) + + err := client.Grants.GrantPrivilegesToDatabaseRole( + ctx, + &sdk.DatabaseRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeInsert}, // Invalid privilege + }, + &sdk.DatabaseRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + sdk.NewDatabaseObjectIdentifier(testDb(t).Name, role.Name), + &sdk.GrantPrivilegesToDatabaseRoleOptions{}, + ) + require.Error(t, err) + joined, ok := err.(interface{ Unwrap() []error }) //nolint:all + require.True(t, ok) + require.Len(t, joined.Unwrap(), 2) + + err = client.Grants.RevokePrivilegesFromDatabaseRole( + ctx, + &sdk.DatabaseRoleGrantPrivileges{ + SchemaObjectPrivileges: []sdk.SchemaObjectPrivilege{sdk.SchemaObjectPrivilegeInsert}, // Invalid privilege + }, + &sdk.DatabaseRoleGrantOn{ + SchemaObject: &sdk.GrantOnSchemaObject{ + All: &sdk.GrantOnSchemaObjectIn{ + PluralObjectType: sdk.PluralObjectTypePipes, + InSchema: sdk.Pointer(schema.ID()), + }, + }, + }, + sdk.NewDatabaseObjectIdentifier(testDb(t).Name, role.Name), + &sdk.RevokePrivilegesFromDatabaseRoleOptions{}, + ) + require.Error(t, err) + joined, ok = err.(interface{ Unwrap() []error }) //nolint:all + require.True(t, ok) + require.Len(t, joined.Unwrap(), 2) + }) } func TestInt_GrantPrivilegeToShare(t *testing.T) { @@ -635,3 +932,11 @@ func TestInt_ShowGrants(t *testing.T) { assert.LessOrEqual(t, 2, len(grants)) }) } + +func grantsToPrivileges(grants []sdk.Grant) []string { + privileges := make([]string, len(grants)) + for i, grant := range grants { + privileges[i] = grant.Privilege + } + return privileges +}