diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 3313ccfacb..127524395d 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -9,6 +9,82 @@ across different versions. #### *(behavior change)* Execute as validation added From now on, the `snowflake_procedure`'s `execute_as` parameter allows only two values: OWNER and CALLER (case-insensitive). Setting other values earlier resulted in falling back to the Snowflake default (currently OWNER) and creating a permadiff. +### snowflake_grants datasource changes +`snowflake_grants` datasource was refreshed as part of the ongoing [Grants Redesign](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md#redesigning-grants). + +#### *(behavior change)* role fields renames +To be aligned with the convention in other grant resources, `role` was renamed to `account_role` for the following fields: +- `grants_to.role` +- `grants_of.role` +- `future_grants_to.role`. + +To migrate simply change `role` to `account_role` in the aforementioned fields. + +#### *(behavior change)* grants_to.share type change +`grants_to.share` was a text field. Because Snowflake introduced new syntax `SHOW GRANTS TO SHARE IN APPLICATION PACKAGE ` (check more in the [docs](https://docs.snowflake.com/en/sql-reference/sql/show-grants#variants)) the type was changed to object. To migrate simply change: +```terraform +data "snowflake_grants" "example_to_share" { + grants_to { + share = "some_share" + } +} +``` +to +```terraform +data "snowflake_grants" "example_to_share" { + grants_to { + share { + share_name = "some_share" + } + } +} +``` +Note: `in_application_package` is not yet supported. + +#### *(behavior change)* future_grants_in.schema type change +`future_grants_in.schema` was an object field allowing to set required `schema_name` and optional `database_name`. Our strategy is to be explicit, so the schema field was changed to string and fully qualified name is expected. To migrate change: +```terraform +data "snowflake_grants" "example_future_in_schema" { + future_grants_in { + schema { + database_name = "some_database" + schema_name = "some_schema" + } + } +} +``` +to +```terraform +data "snowflake_grants" "example_future_in_schema" { + future_grants_in { + schema = "\"some_database\".\"some_schema\"" + } +} +``` +#### *(new feature)* grants_to new options +`grants_to` was enriched with three new options: +- `application` +- `application_role` +- `database_role` + +No migration work is needed here. + +#### *(new feature)* grants_of new options +`grants_to` was enriched with two new options: +- `database_role` +- `application_role` + +No migration work is needed here. + +#### *(new feature)* future_grants_to new options +`future_grants_to` was enriched with one new option: +- `database_role` + +No migration work is needed here. + +#### *(documentation)* improvements +Descriptions of attributes were altered. More examples were added (both for old and new features). + ## v0.86.0 ➞ v0.87.0 ### snowflake_database resource changes #### *(behavior change)* External object identifier changes diff --git a/docs/data-sources/grants.md b/docs/data-sources/grants.md index 6a330d80f8..c7ccbfafce 100644 --- a/docs/data-sources/grants.md +++ b/docs/data-sources/grants.md @@ -12,56 +12,154 @@ description: |- ## Example Usage ```terraform -# list all grants on account -data "snowflake_grants" "grants" { +################################## +### SHOW GRANTS ON ... +################################## + +# account +data "snowflake_grants" "example_on_account" { grants_on { account = true } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants2" { +# account object (e.g. database) +data "snowflake_grants" "example_on_account_object" { grants_on { - object_name = "\"tst\"" + object_name = "some_database" object_type = "DATABASE" } } -# list all grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants3" { +# database object (e.g. schema) +data "snowflake_grants" "example_on_database_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\"" + object_type = "SCHEMA" + } +} + +# schema object (e.g. table) +data "snowflake_grants" "example_on_schema_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\".\"some_table\"" + object_type = "TABLE" + } +} + +################################## +### SHOW GRANTS TO ... +################################## + +# application +data "snowflake_grants" "example_to_application" { + grants_to { + application = "some_application" + } +} + +# application role +data "snowflake_grants" "example_to_application_role" { grants_to { - role = "ACCOUNTADMIN" + application_role = "\"some_application\".\"some_application_role\"" + } +} + +# account role +data "snowflake_grants" "example_to_role" { + grants_to { + account_role = "some_role" + } +} + +# database role +data "snowflake_grants" "example_to_database_role" { + grants_to { + database_role = "\"some_database\".\"some_database_role\"" + } +} + +# share +data "snowflake_grants" "example_to_share" { + grants_to { + share { + share_name = "some_share" + } + } +} + +# user +data "snowflake_grants" "example_to_user" { + grants_to { + user = "some_user" + } +} + +################################## +### SHOW GRANTS OF ... +################################## + +# application role +data "snowflake_grants" "example_of_application_role" { + grants_of { + application_role = "\"some_application\".\"some_application_role\"" } } -# list all grants of role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants4" { +# database role +data "snowflake_grants" "example_of_database_role" { grants_of { - role = "ACCOUNTADMIN" + database_role = "\"some_database\".\"some_database_role\"" } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants5" { +# account role +data "snowflake_grants" "example_of_role" { + grants_of { + account_role = "some_role" + } +} + +# share +data "snowflake_grants" "example_of_share" { + grants_of { + share = "some_share" + } +} + +################################## +### SHOW FUTURE GRANTS IN ... +################################## + +# database +data "snowflake_grants" "example_future_in_database" { future_grants_in { - database = "\"tst\"" + database = "some_database" } } -# list all future grants in schema with name "mydatabase" and database with name "myschema" -data "snowflake_grants" "grants6" { +# schema +data "snowflake_grants" "example_future_in_schema" { future_grants_in { - schema { - database_name = "\"mydatabase\"" - schema_name = "\"myschema\"" - } + schema = "\"some_database\".\"some_schema\"" } } -# list all future grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants7" { +################################## +### SHOW FUTURE GRANTS TO ... +################################## + +# account role +data "snowflake_grants" "example_future_to_role" { future_grants_to { - role = "ACCOUNTADMIN" + account_role = "some_role" + } +} + +# database role +data "snowflake_grants" "example_future_to_database_role" { + future_grants_to { + database_role = "\"some_database\".\"some_database_role\"" } } ``` @@ -71,11 +169,11 @@ data "snowflake_grants" "grants7" { ### Optional -- `future_grants_in` (Block List, Max: 1) Lists all privileges on new (i.e. future) objects (see [below for nested schema](#nestedblock--future_grants_in)) -- `future_grants_to` (Block List, Max: 1) Lists all privileges granted to the object on new (i.e. future) objects (see [below for nested schema](#nestedblock--future_grants_to)) -- `grants_of` (Block List, Max: 1) Lists all objects to which the given object has been granted (see [below for nested schema](#nestedblock--grants_of)) -- `grants_on` (Block List, Max: 1) Lists all privileges that have been granted on an object or account (see [below for nested schema](#nestedblock--grants_on)) -- `grants_to` (Block List, Max: 1) Lists all privileges granted to the object (see [below for nested schema](#nestedblock--grants_to)) +- `future_grants_in` (Block List, Max: 1) Lists all privileges on new (i.e. future) objects. (see [below for nested schema](#nestedblock--future_grants_in)) +- `future_grants_to` (Block List, Max: 1) Lists all privileges granted to the object on new (i.e. future) objects. (see [below for nested schema](#nestedblock--future_grants_to)) +- `grants_of` (Block List, Max: 1) Lists all objects to which the given object has been granted. (see [below for nested schema](#nestedblock--grants_of)) +- `grants_on` (Block List, Max: 1) Lists all privileges that have been granted on an object or on an account. (see [below for nested schema](#nestedblock--grants_on)) +- `grants_to` (Block List, Max: 1) Lists all privileges granted to the object. (see [below for nested schema](#nestedblock--grants_to)) ### Read-Only @@ -88,27 +186,16 @@ data "snowflake_grants" "grants7" { Optional: - `database` (String) Lists all privileges on new (i.e. future) objects of a specified type in the database granted to a role. -- `schema` (Block List, Max: 1) Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role. (see [below for nested schema](#nestedblock--future_grants_in--schema)) - - -### Nested Schema for `future_grants_in.schema` - -Required: - -- `schema_name` (String) The name of the schema to list all privileges of new (ie. future) objects granted to - -Optional: - -- `database_name` (String) The database in which the scehma resides. Optional when querying a schema in the current database. - +- `schema` (String) Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role. Schema must be a fully qualified name ("<db_name>"."<schema_name>"). ### Nested Schema for `future_grants_to` -Required: +Optional: -- `role` (String) Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the role. +- `account_role` (String) Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the account role. +- `database_role` (String) Lists all privileges on new (i.e. future) objects granted to the database role. Must be a fully qualified name ("<db_name>"."<database_role_name>"). @@ -116,7 +203,9 @@ Required: Optional: -- `role` (String) Lists all users and roles to which the role has been granted +- `account_role` (String) Lists all users and roles to which the account role has been granted. +- `application_role` (String) Lists all the users and roles to which the application role has been granted. Must be a fully qualified name ("<db_name>"."<database_role_name>"). +- `database_role` (String) Lists all users and roles to which the database role has been granted. Must be a fully qualified name ("<db_name>"."<database_role_name>"). - `share` (String) Lists all the accounts for the share and indicates the accounts that are using the share. @@ -126,7 +215,7 @@ Optional: Optional: - `account` (Boolean) Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles. -- `object_name` (String) Name of object to list privileges on +- `object_name` (String) Name of object to list privileges on. - `object_type` (String) Type of object to list privileges on. @@ -135,9 +224,20 @@ Optional: Optional: -- `role` (String) Lists all privileges and roles granted to the role -- `share` (String) Lists all the privileges granted to the share -- `user` (String) Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed +- `account_role` (String) Lists all privileges and roles granted to the role. +- `application` (String) Lists all the privileges and roles granted to the application. +- `application_role` (String) Lists all the privileges and roles granted to the application role. Must be a fully qualified name ("<app_name>"."<app_role_name>"). +- `database_role` (String) Lists all privileges and roles granted to the database role. Must be a fully qualified name ("<db_name>"."<database_role_name>"). +- `share` (Block List, Max: 1) Lists all the privileges granted to the share. (see [below for nested schema](#nestedblock--grants_to--share)) +- `user` (String) Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed. + + +### Nested Schema for `grants_to.share` + +Required: + +- `share_name` (String) Lists all of the privileges and roles granted to the specified share. + diff --git a/examples/data-sources/snowflake_grants/data-source.tf b/examples/data-sources/snowflake_grants/data-source.tf index 1682092584..758f729eed 100644 --- a/examples/data-sources/snowflake_grants/data-source.tf +++ b/examples/data-sources/snowflake_grants/data-source.tf @@ -1,52 +1,150 @@ -# list all grants on account -data "snowflake_grants" "grants" { +################################## +### SHOW GRANTS ON ... +################################## + +# account +data "snowflake_grants" "example_on_account" { grants_on { account = true } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants2" { +# account object (e.g. database) +data "snowflake_grants" "example_on_account_object" { grants_on { - object_name = "\"tst\"" + object_name = "some_database" object_type = "DATABASE" } } -# list all grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants3" { +# database object (e.g. schema) +data "snowflake_grants" "example_on_database_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\"" + object_type = "SCHEMA" + } +} + +# schema object (e.g. table) +data "snowflake_grants" "example_on_schema_object" { + grants_on { + object_name = "\"some_database\".\"some_schema\".\"some_table\"" + object_type = "TABLE" + } +} + +################################## +### SHOW GRANTS TO ... +################################## + +# application +data "snowflake_grants" "example_to_application" { + grants_to { + application = "some_application" + } +} + +# application role +data "snowflake_grants" "example_to_application_role" { grants_to { - role = "ACCOUNTADMIN" + application_role = "\"some_application\".\"some_application_role\"" } } -# list all grants of role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants4" { +# account role +data "snowflake_grants" "example_to_role" { + grants_to { + account_role = "some_role" + } +} + +# database role +data "snowflake_grants" "example_to_database_role" { + grants_to { + database_role = "\"some_database\".\"some_database_role\"" + } +} + +# share +data "snowflake_grants" "example_to_share" { + grants_to { + share { + share_name = "some_share" + } + } +} + +# user +data "snowflake_grants" "example_to_user" { + grants_to { + user = "some_user" + } +} + +################################## +### SHOW GRANTS OF ... +################################## + +# application role +data "snowflake_grants" "example_of_application_role" { grants_of { - role = "ACCOUNTADMIN" + application_role = "\"some_application\".\"some_application_role\"" } } -# list all grants in database with name "tst" -data "snowflake_grants" "grants5" { +# database role +data "snowflake_grants" "example_of_database_role" { + grants_of { + database_role = "\"some_database\".\"some_database_role\"" + } +} + +# account role +data "snowflake_grants" "example_of_role" { + grants_of { + account_role = "some_role" + } +} + +# share +data "snowflake_grants" "example_of_share" { + grants_of { + share = "some_share" + } +} + +################################## +### SHOW FUTURE GRANTS IN ... +################################## + +# database +data "snowflake_grants" "example_future_in_database" { future_grants_in { - database = "\"tst\"" + database = "some_database" } } -# list all future grants in schema with name "mydatabase" and database with name "myschema" -data "snowflake_grants" "grants6" { +# schema +data "snowflake_grants" "example_future_in_schema" { future_grants_in { - schema { - database_name = "\"mydatabase\"" - schema_name = "\"myschema\"" - } + schema = "\"some_database\".\"some_schema\"" + } +} + +################################## +### SHOW FUTURE GRANTS TO ... +################################## + +# account role +data "snowflake_grants" "example_future_to_role" { + future_grants_to { + account_role = "some_role" } } -# list all future grants to role with name "ACCOUNTADMIN" -data "snowflake_grants" "grants7" { +# database role +data "snowflake_grants" "example_future_to_database_role" { future_grants_to { - role = "ACCOUNTADMIN" + database_role = "\"some_database\".\"some_database_role\"" } } diff --git a/pkg/datasources/grants.go b/pkg/datasources/grants.go index 2afbe494d7..ad2e465e6c 100644 --- a/pkg/datasources/grants.go +++ b/pkg/datasources/grants.go @@ -1,128 +1,206 @@ package datasources import ( + "context" + "fmt" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) var grantsSchema = map[string]*schema.Schema{ "grants_on": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_of", "grants_to", "future_grants_in", "future_grants_to"}, - Description: "Lists all privileges that have been granted on an object or account", - ExactlyOneOf: []string{"grants_on", "grants_of", "grants_to", "future_grants_in", "future_grants_to"}, + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Description: "Lists all privileges that have been granted on an object or on an account.", + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "object_name": { - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"grants_on.0.object_type"}, - Description: "Name of object to list privileges on", - ConflictsWith: []string{"grants_on.0.account"}, - AtLeastOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"grants_on.0.object_type"}, + ExactlyOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + Description: "Name of object to list privileges on.", }, "object_type": { - Type: schema.TypeString, - Optional: true, - RequiredWith: []string{"grants_on.0.object_name"}, - Description: "Type of object to list privileges on.", - ConflictsWith: []string{"grants_on.0.account"}, + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"grants_on.0.object_name"}, + Description: "Type of object to list privileges on.", }, "account": { - Type: schema.TypeBool, - Optional: true, - Description: "Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles.", - ConflictsWith: []string{"grants_on.0.object_name", "grants_on.0.object_type"}, - AtLeastOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, + Type: schema.TypeBool, + Optional: true, + Description: "Object hierarchy to list privileges on. The only valid value is: ACCOUNT. Setting this attribute lists all the account-level (i.e. global) privileges that have been granted to roles.", + ExactlyOneOf: []string{"grants_on.0.object_name", "grants_on.0.account"}, }, }, }, }, "grants_to": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_of", "future_grants_in", "future_grants_to"}, - Description: "Lists all privileges granted to the object", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, + Description: "Lists all privileges granted to the object.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "role": { + "application": { Type: schema.TypeString, Optional: true, - Description: "Lists all privileges and roles granted to the role", - ConflictsWith: []string{ + Description: "Lists all the privileges and roles granted to the application.", + ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.account_role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, + }, + "application_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all the privileges and roles granted to the application role. Must be a fully qualified name (\"<app_name>\".\"<app_role_name>\").", ExactlyOneOf: []string{ - "grants_to.0.role", + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.account_role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, - "user": { + "account_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed", - ConflictsWith: []string{ - "grants_to.0.role", + Description: "Lists all privileges and roles granted to the role.", + ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.account_role", + "grants_to.0.database_role", + "grants_to.0.user", "grants_to.0.share", }, + }, + "database_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all privileges and roles granted to the database role. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ - "grants_to.0.role", + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.account_role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, - "share": { + "user": { Type: schema.TypeString, Optional: true, - Description: "Lists all the privileges granted to the share", - ConflictsWith: []string{ - "grants_to.0.role", + Description: "Lists all the roles granted to the user. Note that the PUBLIC role, which is automatically available to every user, is not listed.", + ExactlyOneOf: []string{ + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.account_role", + "grants_to.0.database_role", "grants_to.0.user", + "grants_to.0.share", }, + }, + "share": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Lists all the privileges granted to the share.", ExactlyOneOf: []string{ - "grants_to.0.role", + "grants_to.0.application", + "grants_to.0.application_role", + "grants_to.0.account_role", + "grants_to.0.database_role", "grants_to.0.user", "grants_to.0.share", }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "share_name": { + Type: schema.TypeString, + Required: true, + Description: "Lists all of the privileges and roles granted to the specified share.", + }, + // TODO [SNOW-1284382]: Uncomment after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. + // "in_application_package": { + // Type: schema.TypeString, + // Optional: true, + // Description: "Lists all of the privileges and roles granted to a share in the specified application package.", + // }, + }, + }, }, }, }, }, "grants_of": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_to", "future_grants_in", "future_grants_to"}, - Description: "Lists all objects to which the given object has been granted", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, + Description: "Lists all objects to which the given object has been granted.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "role": { + "account_role": { Type: schema.TypeString, Optional: true, - Description: "Lists all users and roles to which the role has been granted", - ConflictsWith: []string{ + Description: "Lists all users and roles to which the account role has been granted.", + ExactlyOneOf: []string{ + "grants_of.0.account_role", + "grants_of.0.database_role", + "grants_of.0.application_role", "grants_of.0.share", }, + }, + "database_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all users and roles to which the database role has been granted. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", + ExactlyOneOf: []string{ + "grants_of.0.account_role", + "grants_of.0.database_role", + "grants_of.0.application_role", + "grants_of.0.share", + }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), + }, + "application_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all the users and roles to which the application role has been granted. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", ExactlyOneOf: []string{ - "grants_of.0.role", + "grants_of.0.account_role", + "grants_of.0.database_role", + "grants_of.0.application_role", "grants_of.0.share", }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, "share": { Type: schema.TypeString, Optional: true, Description: "Lists all the accounts for the share and indicates the accounts that are using the share.", - ConflictsWith: []string{ - "grants_of.0.role", - }, ExactlyOneOf: []string{ - "grants_of.0.role", + "grants_of.0.account_role", + "grants_of.0.database_role", + "grants_of.0.application_role", "grants_of.0.share", }, }, @@ -130,67 +208,61 @@ var grantsSchema = map[string]*schema.Schema{ }, }, "future_grants_in": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_of", "grants_to", "future_grants_to"}, - Description: "Lists all privileges on new (i.e. future) objects", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, + Description: "Lists all privileges on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "database": { Type: schema.TypeString, Optional: true, Description: "Lists all privileges on new (i.e. future) objects of a specified type in the database granted to a role.", - ConflictsWith: []string{ - "future_grants_in.0.schema", - }, ExactlyOneOf: []string{ "future_grants_in.0.database", "future_grants_in.0.schema", }, }, "schema": { - Type: schema.TypeList, - MaxItems: 1, + Type: schema.TypeString, Optional: true, - Description: "Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role.", - ConflictsWith: []string{ - "future_grants_in.0.database", - }, + Description: "Lists all privileges on new (i.e. future) objects of a specified type in the schema granted to a role. Schema must be a fully qualified name (\"<db_name>\".\"<schema_name>\").", ExactlyOneOf: []string{ "future_grants_in.0.database", "future_grants_in.0.schema", }, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "schema_name": { - Type: schema.TypeString, - Required: true, - Description: "The name of the schema to list all privileges of new (ie. future) objects granted to", - }, - "database_name": { - Type: schema.TypeString, - Optional: true, - Description: "The database in which the scehma resides. Optional when querying a schema in the current database.", - }, - }, - }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, }, }, }, "future_grants_to": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - ConflictsWith: []string{"grants_on", "grants_of", "grants_to", "future_grants_in"}, - Description: "Lists all privileges granted to the object on new (i.e. future) objects", + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ExactlyOneOf: []string{"grants_on", "grants_to", "grants_of", "future_grants_in", "future_grants_to"}, + Description: "Lists all privileges granted to the object on new (i.e. future) objects.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "role": { + "account_role": { + Type: schema.TypeString, + Optional: true, + Description: "Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the account role.", + ExactlyOneOf: []string{ + "future_grants_to.0.account_role", + "future_grants_to.0.database_role", + }, + }, + "database_role": { Type: schema.TypeString, - Required: true, - Description: "Lists all privileges on new (i.e. future) objects of a specified type in a database or schema granted to the role.", + Optional: true, + Description: "Lists all privileges on new (i.e. future) objects granted to the database role. Must be a fully qualified name (\"<db_name>\".\"<database_role_name>\").", + ExactlyOneOf: []string{ + "future_grants_to.0.account_role", + "future_grants_to.0.database_role", + }, + ValidateDiagFunc: resources.IsValidIdentifier[sdk.DatabaseObjectIdentifier](), }, }, }, @@ -203,42 +275,42 @@ var grantsSchema = map[string]*schema.Schema{ Schema: map[string]*schema.Schema{ "created_on": { Type: schema.TypeString, - Description: "The date and time the grant was created", + Description: "The date and time the grant was created.", Computed: true, }, "privilege": { Type: schema.TypeString, - Description: "The privilege granted", + Description: "The privilege granted.", Computed: true, }, "granted_on": { Type: schema.TypeString, - Description: "The object on which the privilege was granted", + Description: "The object on which the privilege was granted.", Computed: true, }, "name": { Type: schema.TypeString, - Description: "The name of the object on which the privilege was granted", + Description: "The name of the object on which the privilege was granted.", Computed: true, }, "granted_to": { Type: schema.TypeString, - Description: "The role to which the privilege was granted", + Description: "The role to which the privilege was granted.", Computed: true, }, "grantee_name": { Type: schema.TypeString, - Description: "The name of the role to which the privilege was granted", + Description: "The name of the role to which the privilege was granted.", Computed: true, }, "grant_option": { Type: schema.TypeBool, - Description: "Whether the grantee can grant the privilege to others", + Description: "Whether the grantee can grant the privilege to others.", Computed: true, }, "granted_by": { Type: schema.TypeString, - Description: "The role that granted the privilege", + Description: "The role that granted the privilege.", Computed: true, }, }, @@ -248,135 +320,193 @@ var grantsSchema = map[string]*schema.Schema{ func Grants() *schema.Resource { return &schema.Resource{ - Read: ReadGrants, - Schema: grantsSchema, + ReadContext: ReadGrants, + Schema: grantsSchema, } } -func ReadGrants(d *schema.ResourceData, meta interface{}) error { +func ReadGrants(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { client := meta.(*provider.Context).Client - db := client.GetConn().DB - var grantDetails []snowflake.GrantDetail + var opts *sdk.ShowGrantOptions var err error - if v, ok := d.GetOk("grants_on"); ok { - grantsOn := v.([]interface{})[0].(map[string]interface{}) - objectType := grantsOn["object_type"].(string) - objectName := grantsOn["object_name"].(string) - account := grantsOn["account"].(bool) + if grantsOn, ok := d.GetOk("grants_on"); ok { + opts, err = buildOptsForGrantsOn(grantsOn.([]any)[0].(map[string]any)) + } + if grantsTo, ok := d.GetOk("grants_to"); ok { + opts, err = buildOptsForGrantsTo(grantsTo.([]any)[0].(map[string]any)) + } + if grantsOf, ok := d.GetOk("grants_of"); ok { + opts, err = buildOptsForGrantsOf(grantsOf.([]any)[0].(map[string]any)) + } + if futureGrantsIn, ok := d.GetOk("future_grants_in"); ok { + opts, err = buildOptsForFutureGrantsIn(futureGrantsIn.([]any)[0].(map[string]any)) + } + if futureGrantsTo, ok := d.GetOk("future_grants_to"); ok { + opts, err = buildOptsForFutureGrantsTo(futureGrantsTo.([]any)[0].(map[string]any)) + } + if err != nil { + return diag.FromErr(err) + } - if account { - grantDetails, err = snowflake.ShowGrantsOnAccount(db) - if err != nil { - return err - } - } else if objectType != "" && objectName != "" { - grantDetails, err = snowflake.ShowGrantsOn(db, objectType, objectName) - if err != nil { - return err - } - } + grants, err := client.Grants.Show(ctx, opts) + if err != nil { + return diag.FromErr(err) } - if v, ok := d.GetOk("grants_to"); ok { - grantsTo := v.([]interface{})[0].(map[string]interface{}) - role := grantsTo["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "ROLE", role) - if err != nil { - return err - } + err = d.Set("grants", convertGrants(grants)) + if err != nil { + return diag.FromErr(err) + } + + d.SetId("grants") + return nil +} + +func buildOptsForGrantsOn(grantsOn map[string]any) (*sdk.ShowGrantOptions, error) { + opts := new(sdk.ShowGrantOptions) + + objectType := grantsOn["object_type"].(string) + objectName := grantsOn["object_name"].(string) + account := grantsOn["account"].(bool) + + if account { + opts.On = &sdk.ShowGrantsOn{ + Account: sdk.Bool(true), + } + } else { + if objectType == "" || objectName == "" { + return nil, fmt.Errorf("object_type (%s) or object_name (%s) missing", objectType, objectName) } - user := grantsTo["user"].(string) - if user != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "USER", user) - if err != nil { - return err - } + objectId, err := helpers.DecodeSnowflakeParameterID(objectName) + if err != nil { + return nil, err } - share := grantsTo["share"].(string) - if share != "" { - grantDetails, err = snowflake.ShowGrantsTo(db, "SHARE", share) - if err != nil { - return err - } + opts.On = &sdk.ShowGrantsOn{ + Object: &sdk.Object{ + ObjectType: sdk.ObjectType(objectType), + Name: objectId, + }, } } + return opts, nil +} - if v, ok := d.GetOk("grants_of"); ok { - grantsOf := v.([]interface{})[0].(map[string]interface{}) - role := grantsOf["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowGrantsOf(db, "ROLE", role) - if err != nil { - return err - } +func buildOptsForGrantsTo(grantsTo map[string]any) (*sdk.ShowGrantOptions, error) { + opts := new(sdk.ShowGrantOptions) + + if application := grantsTo["application"].(string); application != "" { + opts.To = &sdk.ShowGrantsTo{ + Application: sdk.NewAccountObjectIdentifier(application), } - share := grantsOf["share"].(string) - if share != "" { - grantDetails, err = snowflake.ShowGrantsOf(db, "SHARE", share) - if err != nil { - return err - } + } + if applicationRole := grantsTo["application_role"].(string); applicationRole != "" { + opts.To = &sdk.ShowGrantsTo{ + ApplicationRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(applicationRole), } } - - if v, ok := d.GetOk("future_grants_in"); ok { - futureGrantsIn := v.([]interface{})[0].(map[string]interface{}) - database := futureGrantsIn["database"].(string) - if database != "" { - grantDetails, err = snowflake.ShowFutureGrantsIn(db, "DATABASE", database) - if err != nil { - return err - } + if accountRole := grantsTo["account_role"].(string); accountRole != "" { + opts.To = &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(accountRole), } - schema := futureGrantsIn["schema"].([]interface{}) - if len(schema) > 0 { - schemaMap := schema[0].(map[string]interface{}) - schemaName := schemaMap["schema_name"].(string) - databaseName := schemaMap["database_name"].(string) - if databaseName != "" { - schemaName = databaseName + "." + schemaName - } + } + if databaseRole := grantsTo["database_role"].(string); databaseRole != "" { + opts.To = &sdk.ShowGrantsTo{ + DatabaseRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(databaseRole), + } + } + if user := grantsTo["user"].(string); user != "" { + opts.To = &sdk.ShowGrantsTo{ + User: sdk.NewAccountObjectIdentifier(user), + } + } + if share := grantsTo["share"]; share != nil && len(share.([]any)) > 0 { + shareMap := share.([]any)[0].(map[string]any) + opts.To = &sdk.ShowGrantsTo{ + Share: &sdk.ShowGrantsToShare{ + Name: sdk.NewAccountObjectIdentifier(shareMap["share_name"].(string)), + }, + } + // TODO [SNOW-1284382]: Uncomment after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. + // if inApplicationPackage := shareMap["in_application_package"]; inApplicationPackage != "" { + // opts.To.Share.InApplicationPackage = sdk.Pointer(sdk.NewAccountObjectIdentifier(inApplicationPackage.(string))) + // } + } + return opts, nil +} - grantDetails, err = snowflake.ShowFutureGrantsIn(db, "SCHEMA", schemaName) - if err != nil { - return err - } +func buildOptsForGrantsOf(grantsOf map[string]any) (*sdk.ShowGrantOptions, error) { + opts := new(sdk.ShowGrantOptions) + + if accountRole := grantsOf["account_role"].(string); accountRole != "" { + opts.Of = &sdk.ShowGrantsOf{ + Role: sdk.NewAccountObjectIdentifier(accountRole), + } + } + if databaseRole := grantsOf["database_role"].(string); databaseRole != "" { + opts.Of = &sdk.ShowGrantsOf{ + DatabaseRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(databaseRole), + } + } + if applicationRole := grantsOf["application_role"].(string); applicationRole != "" { + opts.Of = &sdk.ShowGrantsOf{ + ApplicationRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(applicationRole), } } + if share := grantsOf["share"].(string); share != "" { + opts.Of = &sdk.ShowGrantsOf{ + Share: sdk.NewAccountObjectIdentifier(share), + } + } + return opts, nil +} + +func buildOptsForFutureGrantsIn(futureGrantsIn map[string]any) (*sdk.ShowGrantOptions, error) { + opts := new(sdk.ShowGrantOptions) + opts.Future = sdk.Bool(true) - if v, ok := d.GetOk("future_grants_to"); ok { - futureGrantsTo := v.([]interface{})[0].(map[string]interface{}) - role := futureGrantsTo["role"].(string) - if role != "" { - grantDetails, err = snowflake.ShowFutureGrantsTo(db, "ROLE", role) - if err != nil { - return err - } + if db := futureGrantsIn["database"].(string); db != "" { + opts.In = &sdk.ShowGrantsIn{ + Database: sdk.Pointer(sdk.NewAccountObjectIdentifier(db)), } } + if sc := futureGrantsIn["schema"].(string); sc != "" { + opts.In = &sdk.ShowGrantsIn{ + Schema: sdk.Pointer(sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(sc)), + } + } + return opts, nil +} - err = d.Set("grants", flattenGrants(grantDetails)) - if err != nil { - return err +func buildOptsForFutureGrantsTo(futureGrantsTo map[string]any) (*sdk.ShowGrantOptions, error) { + opts := new(sdk.ShowGrantOptions) + opts.Future = sdk.Bool(true) + + if accountRole := futureGrantsTo["account_role"].(string); accountRole != "" { + opts.To = &sdk.ShowGrantsTo{ + Role: sdk.NewAccountObjectIdentifier(accountRole), + } } - d.SetId("grants") - return nil + if databaseRole := futureGrantsTo["database_role"].(string); databaseRole != "" { + opts.To = &sdk.ShowGrantsTo{ + DatabaseRole: sdk.NewDatabaseObjectIdentifierFromFullyQualifiedName(databaseRole), + } + } + return opts, nil } -func flattenGrants(grants []snowflake.GrantDetail) []map[string]interface{} { - grantDetails := make([]map[string]interface{}, len(grants)) +func convertGrants(grants []sdk.Grant) []map[string]any { + grantDetails := make([]map[string]any, len(grants)) for i, grant := range grants { - grantDetails[i] = map[string]interface{}{ - "created_on": grant.CreatedOn.String, - "privilege": grant.Privilege.String, - "granted_on": grant.GrantedOn.String, - "name": grant.Name.String, - "granted_to": grant.GrantedTo.String, - "grantee_name": grant.GranteeName.String, - "grant_option": grant.GrantOption.String == "true", - "granted_by": grant.GrantedBy.String, + grantDetails[i] = map[string]any{ + "created_on": grant.CreatedOn.String(), + "privilege": grant.Privilege, + "granted_on": grant.GrantedOn.String(), + "name": grant.Name.FullyQualifiedName(), + "granted_to": grant.GrantedTo.String(), + "grantee_name": grant.GranteeName.FullyQualifiedName(), + "grant_option": grant.GrantOption, + "granted_by": grant.GrantedBy.FullyQualifiedName(), } } return grantDetails diff --git a/pkg/datasources/grants_acceptance_test.go b/pkg/datasources/grants_acceptance_test.go index b17479faab..5389832530 100644 --- a/pkg/datasources/grants_acceptance_test.go +++ b/pkg/datasources/grants_acceptance_test.go @@ -1,39 +1,700 @@ package datasources_test import ( + "context" + "regexp" + "strings" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testprofiles" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/tfversion" + "github.com/stretchr/testify/require" ) -func TestAcc_Grants(t *testing.T) { +func TestAcc_Grants_On_Account(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: grantsAccount(), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/Account"), + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_AccountObject(t *testing.T) { + configVariables := config.Variables{ + "database": config.StringVariable(acc.TestDatabaseName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/AccountObject"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_DatabaseObject(t *testing.T) { + configVariables := config.Variables{ + "database": config.StringVariable(acc.TestDatabaseName), + "schema": config.StringVariable(acc.TestSchemaName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/DatabaseObject"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_SchemaObject(t *testing.T) { + tableName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(acc.TestDatabaseName), + "schema": config.StringVariable(acc.TestSchemaName), + "table": config.StringVariable(tableName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/SchemaObject"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_On_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_On_Invalid_MissingObjectType(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/On/Invalid/MissingObjectType"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Missing required argument"), + }, + }, + }) +} + +// TODO [SNOW-1284382]: Implement after snowflake_application and snowflake_application_role resources are introduced. +func TestAcc_Grants_To_Application(t *testing.T) { + t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") +} + +// TODO [SNOW-1284382]: Implement after snowflake_application and snowflake_application_role resources are introduced. +func TestAcc_Grants_To_ApplicationRole(t *testing.T) { + t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") +} + +func TestAcc_Grants_To_AccountRole(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/AccountRole"), + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_To_DatabaseRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "database_role": config.StringVariable(databaseRoleName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/DatabaseRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_To_User(t *testing.T) { + user := getCurrentUser(t) + configVariables := config.Variables{ + "user": config.StringVariable(user), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/User"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresentLimited(), + }, + }, + }) +} + +func TestAcc_Grants_To_Share(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "share": config.StringVariable(shareName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Share"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresent(), + }, + }, + }) +} + +// TODO [SNOW-1284382]: Implement after SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working. +func TestAcc_Grants_To_ShareWithApplicationPackage(t *testing.T) { + t.Skip("Skipped until SHOW GRANTS TO SHARE IN APPLICATION PACKAGE syntax starts working.") +} + +func TestAcc_Grants_To_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_To_Invalid_ShareNameMissing(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/ShareNameMissing"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Missing required argument"), + }, + }, + }) +} + +func TestAcc_Grants_To_Invalid_DatabaseRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + +func TestAcc_Grants_To_Invalid_ApplicationRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + +func TestAcc_Grants_Of_AccountRole(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/AccountRole"), + Check: checkAtLeastOneGrantPresentLimited(), + }, + }, + }) +} + +func TestAcc_Grants_Of_DatabaseRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "database_role": config.StringVariable(databaseRoleName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/DatabaseRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneGrantPresentLimited(), + }, + }, + }) +} + +// TODO [SNOW-1284382]: Implement after snowflake_application and snowflake_application_role resources are introduced. +func TestAcc_Grants_Of_ApplicationRole(t *testing.T) { + t.Skip("Skipped until snowflake_application and snowflake_application_role resources are introduced. Currently, behavior tested in application_roles_gen_integration_test.go.") +} + +// TODO [SNOW-1284394]: Unskip the test +func TestAcc_Grants_Of_Share(t *testing.T) { + t.Skip("TestAcc_Share are skipped") + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + shareName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + + getSecondaryAccountIdentifier := func(t *testing.T) *sdk.AccountIdentifier { + t.Helper() + + client, err := sdk.NewDefaultClient() + if err != nil { + t.Fatal(err) + } + cfg, err := sdk.ProfileConfig(testprofiles.Secondary) + if err != nil { + t.Fatal(err) + } + secondaryClient, err := sdk.NewClient(cfg) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + + replicationAccounts, err := client.ReplicationFunctions.ShowReplicationAccounts(ctx) + if err != nil { + t.Fatal(err) + } + for _, replicationAccount := range replicationAccounts { + if replicationAccount.AccountLocator == secondaryClient.GetAccountLocator() { + return sdk.Pointer(sdk.NewAccountIdentifier(replicationAccount.OrganizationName, replicationAccount.AccountName)) + } + } + return nil + } + + accountId := getSecondaryAccountIdentifier(t) + require.NotNil(t, accountId) + + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "share": config.StringVariable(shareName), + "account": config.StringVariable(accountId.FullyQualifiedName()), + } + datasourceName := "data.snowflake_grants.test" + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Share"), + ConfigVariables: configVariables, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.snowflake_grants.g", "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckNoResourceAttr(datasourceName, "grants.0.created_on"), ), }, }, }) } -func grantsAccount() string { - s := ` -data "snowflake_grants" "g" { - grants_on { - account = true +func TestAcc_Grants_Of_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_Of_Invalid_DatabaseRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + +func TestAcc_Grants_Of_Invalid_ApplicationRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + +func TestAcc_Grants_FutureIn_Database(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Database"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) } -` - return s + +func TestAcc_Grants_FutureIn_Schema(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + schemaName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "schema": config.StringVariable(schemaName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Schema"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureIn_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_FutureIn_Invalid_SchemaNameNotFullyQualified(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_AccountRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/AccountRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_DatabaseRole(t *testing.T) { + databaseName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + databaseRoleName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + configVariables := config.Variables{ + "database": config.StringVariable(databaseName), + "database_role": config.StringVariable(databaseRoleName), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/DatabaseRole"), + ConfigVariables: configVariables, + Check: checkAtLeastOneFutureGrantPresent(), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_Invalid_NoAttribute(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/Invalid/NoAttribute"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid combination of arguments"), + }, + }, + }) +} + +func TestAcc_Grants_FutureTo_Invalid_DatabaseRoleIdInvalid(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid"), + PlanOnly: true, + ExpectError: regexp.MustCompile("Error: Invalid identifier type"), + }, + }, + }) +} + +func checkAtLeastOneGrantPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), + ) +} + +func checkAtLeastOneFutureGrantPresent() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.privilege"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grantee_name"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.grant_option"), + ) +} + +func checkAtLeastOneGrantPresentLimited() resource.TestCheckFunc { + datasourceName := "data.snowflake_grants.test" + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(datasourceName, "grants.#"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.created_on"), + resource.TestCheckResourceAttrSet(datasourceName, "grants.0.granted_to"), + ) +} + +func getCurrentUser(t *testing.T) string { + t.Helper() + client, err := sdk.NewDefaultClient() + if err != nil { + t.Fatal(err) + } + user, err := client.ContextFunctions.CurrentUser(context.Background()) + if err != nil { + t.Fatal(err) + } + return user } diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf new file mode 100644 index 0000000000..5a7b657f0e --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/snowflake_grants_future_in_database.tf @@ -0,0 +1,22 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = data.snowflake_current_role.test.name + privileges = ["CREATE TABLE"] + + on_schema { + future_schemas_in_database = snowflake_database.test.name + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_account_role.test] + + future_grants_in { + database = snowflake_database.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf new file mode 100644 index 0000000000..bfdd9eeb3c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Database/variables.tf @@ -0,0 +1,3 @@ +variable "database" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf new file mode 100644 index 0000000000..d85dbce68d --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/NoAttribute/snowflake_grants_future_in_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + future_grants_in { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf new file mode 100644 index 0000000000..9204f6cd66 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Invalid/SchemaNameNotFullyQualified/snowflake_grants_future_in_invalid_schema_name_not_fully_qualified.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + future_grants_in { + schema = "schema" + } +} \ No newline at end of file diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf new file mode 100644 index 0000000000..5da1376697 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/snowflake_grants_future_in_schema.tf @@ -0,0 +1,30 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_schema" "test" { + name = var.schema + database = snowflake_database.test.name +} + +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = data.snowflake_current_role.test.name + privileges = ["INSERT"] + + on_schema_object { + future { + object_type_plural = "TABLES" + in_schema = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\"" + } + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_account_role.test] + + future_grants_in { + schema = "\"${snowflake_database.test.name}\".\"${snowflake_schema.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf new file mode 100644 index 0000000000..626dbab534 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureIn/Schema/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "schema" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/snowflake_grants_future_to_account_role.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/snowflake_grants_future_to_account_role.tf new file mode 100644 index 0000000000..51959e6ff9 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/snowflake_grants_future_to_account_role.tf @@ -0,0 +1,22 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_grant_privileges_to_account_role" "test" { + account_role_name = data.snowflake_current_role.test.name + privileges = ["CREATE TABLE"] + + on_schema { + future_schemas_in_database = snowflake_database.test.name + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_account_role.test] + + future_grants_to { + account_role = data.snowflake_current_role.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/variables.tf new file mode 100644 index 0000000000..bfdd9eeb3c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/AccountRole/variables.tf @@ -0,0 +1,3 @@ +variable "database" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf new file mode 100644 index 0000000000..e194007f22 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/snowflake_grants_future_to_database_role.tf @@ -0,0 +1,25 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_database_role" "test" { + name = var.database_role + database = snowflake_database.test.name +} + +resource "snowflake_grant_privileges_to_database_role" "test" { + database_role_name = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + privileges = ["CREATE TABLE"] + + on_schema { + future_schemas_in_database = snowflake_database.test.name + } +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_database_role.test] + + future_grants_to { + database_role = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf new file mode 100644 index 0000000000..447fa919c0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/DatabaseRole/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "database_role" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf new file mode 100644 index 0000000000..49f3275ce2 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/DatabaseRoleIdInvalid/snowflake_grants_future_to_invalid_database_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + future_grants_to { + database_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf new file mode 100644 index 0000000000..780fc9fd08 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/FutureTo/Invalid/NoAttribute/snowflake_grants_future_to_invalid_no_attribute.tf @@ -0,0 +1,3 @@ +data "snowflake_grants" "test" { + future_grants_to {} +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/AccountRole/snowflake_grants_of_account_role.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/AccountRole/snowflake_grants_of_account_role.tf new file mode 100644 index 0000000000..7a0815f229 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/AccountRole/snowflake_grants_of_account_role.tf @@ -0,0 +1,7 @@ +data "snowflake_current_role" "test" {} + +data "snowflake_grants" "test" { + grants_of { + account_role = data.snowflake_current_role.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf new file mode 100644 index 0000000000..2495cee7f9 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/snowflake_grants_of_database_role.tf @@ -0,0 +1,23 @@ +data "snowflake_current_role" "test" {} + +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_database_role" "test" { + name = var.database_role + database = snowflake_database.test.name +} + +resource "snowflake_grant_database_role" "test" { + database_role_name = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + parent_role_name = data.snowflake_current_role.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_database_role.test] + + grants_of { + database_role = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf new file mode 100644 index 0000000000..447fa919c0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/DatabaseRole/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "database_role" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf new file mode 100644 index 0000000000..0c25ac7b2a --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/ApplicationRoleIdInvalid/snowflake_grants_of_invalid_application_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_of { + application_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf new file mode 100644 index 0000000000..46b6c8caa7 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/DatabaseRoleIdInvalid/snowflake_grants_of_invalid_database_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_of { + database_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf new file mode 100644 index 0000000000..2b0e08d3e8 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Invalid/NoAttribute/snowflake_grants_of_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + grants_of { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf new file mode 100644 index 0000000000..001bb832d5 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/snowflake_grants_of_share.tf @@ -0,0 +1,24 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_share" "test" { + depends_on = [snowflake_database.test] + + name = var.share + accounts = [var.account] +} + +resource "snowflake_grant_privileges_to_share" "test" { + to_share = snowflake_share.test.name + privileges = ["USAGE"] + on_database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_share.test] + + grants_of { + share = snowflake_share.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf new file mode 100644 index 0000000000..f620845508 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/Of/Share/variables.tf @@ -0,0 +1,11 @@ +variable "database" { + type = string +} + +variable "share" { + type = string +} + +variable "account" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/Account/snowflake_grants_on_account.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Account/snowflake_grants_on_account.tf new file mode 100644 index 0000000000..4c66d5a46e --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/Account/snowflake_grants_on_account.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_on { + account = true + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/snowflake_grants_on_account_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/snowflake_grants_on_account_object.tf new file mode 100644 index 0000000000..cd93692366 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/snowflake_grants_on_account_object.tf @@ -0,0 +1,6 @@ +data "snowflake_grants" "test" { + grants_on { + object_name = var.database + object_type = "DATABASE" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/variables.tf new file mode 100644 index 0000000000..bfdd9eeb3c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/AccountObject/variables.tf @@ -0,0 +1,3 @@ +variable "database" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/snowflake_grants_on_database_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/snowflake_grants_on_database_object.tf new file mode 100644 index 0000000000..ffb7081367 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/snowflake_grants_on_database_object.tf @@ -0,0 +1,6 @@ +data "snowflake_grants" "test" { + grants_on { + object_name = "\"${var.database}\".\"${var.schema}\"" + object_type = "SCHEMA" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/variables.tf new file mode 100644 index 0000000000..626dbab534 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/DatabaseObject/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "schema" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf new file mode 100644 index 0000000000..d94be962db --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/MissingObjectType/snowflake_grants_on_invalid_missing_object_type.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_on { + object_name = "some_object" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/NoAttribute/snowflake_grants_on_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/NoAttribute/snowflake_grants_on_invalid_no_attribute.tf new file mode 100644 index 0000000000..4ec09a6115 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/Invalid/NoAttribute/snowflake_grants_on_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + grants_on { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/snowflake_grants_on_schema_object.tf b/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/snowflake_grants_on_schema_object.tf new file mode 100644 index 0000000000..cd40a0d18c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/snowflake_grants_on_schema_object.tf @@ -0,0 +1,17 @@ +resource "snowflake_table" "test" { + database = var.database + schema = var.schema + name = var.table + + column { + name = "id" + type = "NUMBER(38,0)" + } +} + +data "snowflake_grants" "test" { + grants_on { + object_name = "\"${snowflake_table.test.database}\".\"${snowflake_table.test.schema}\".\"${snowflake_table.test.name}\"" + object_type = "TABLE" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/variables.tf new file mode 100644 index 0000000000..92e06a8848 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/On/SchemaObject/variables.tf @@ -0,0 +1,11 @@ +variable "database" { + type = string +} + +variable "schema" { + type = string +} + +variable "table" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/AccountRole/snowflake_grants_to_account_role.tf b/pkg/datasources/testdata/TestAcc_Grants/To/AccountRole/snowflake_grants_to_account_role.tf new file mode 100644 index 0000000000..a5bb91a0b7 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/AccountRole/snowflake_grants_to_account_role.tf @@ -0,0 +1,7 @@ +data "snowflake_current_role" "test" {} + +data "snowflake_grants" "test" { + grants_to { + account_role = data.snowflake_current_role.test.name + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf new file mode 100644 index 0000000000..87183c9a9b --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/snowflake_grants_to_database_role.tf @@ -0,0 +1,14 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_database_role" "test" { + name = var.database_role + database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + grants_to { + database_role = "\"${snowflake_database.test.name}\".\"${snowflake_database_role.test.name}\"" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf new file mode 100644 index 0000000000..447fa919c0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/DatabaseRole/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "database_role" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf new file mode 100644 index 0000000000..d0815f4b1c --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ApplicationRoleIdInvalid/snowflake_grants_to_invalid_application_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + application_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf new file mode 100644 index 0000000000..c9365923ca --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/DatabaseRoleIdInvalid/snowflake_grants_to_invalid_database_role_id_invalid.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + database_role = "role" + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf new file mode 100644 index 0000000000..5762ea22f0 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/NoAttribute/snowflake_grants_to_invalid_no_attribute.tf @@ -0,0 +1,4 @@ +data "snowflake_grants" "test" { + grants_to { + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf new file mode 100644 index 0000000000..b535ec09de --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Invalid/ShareNameMissing/snowflake_grants_to_invalid_share_name_missing.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + share {} + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf new file mode 100644 index 0000000000..5fa9e34424 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Share/snowflake_grants_to_share.tf @@ -0,0 +1,25 @@ +resource "snowflake_database" "test" { + name = var.database +} + +resource "snowflake_share" "test" { + depends_on = [snowflake_database.test] + + name = var.share +} + +resource "snowflake_grant_privileges_to_share" "test" { + to_share = snowflake_share.test.name + privileges = ["USAGE"] + on_database = snowflake_database.test.name +} + +data "snowflake_grants" "test" { + depends_on = [snowflake_grant_privileges_to_share.test] + + grants_to { + share { + share_name = snowflake_share.test.name + } + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf new file mode 100644 index 0000000000..5de127b632 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/Share/variables.tf @@ -0,0 +1,7 @@ +variable "database" { + type = string +} + +variable "share" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf b/pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf new file mode 100644 index 0000000000..fa2cffc3ac --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/User/snowflake_grants_to_user.tf @@ -0,0 +1,5 @@ +data "snowflake_grants" "test" { + grants_to { + user = var.user + } +} diff --git a/pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf b/pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf new file mode 100644 index 0000000000..be0022a44a --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_Grants/To/User/variables.tf @@ -0,0 +1,3 @@ +variable "user" { + type = string +} diff --git a/pkg/resources/file_format_acceptance_test.go b/pkg/resources/file_format_acceptance_test.go index e2a0f7bc97..cb6281e911 100644 --- a/pkg/resources/file_format_acceptance_test.go +++ b/pkg/resources/file_format_acceptance_test.go @@ -70,10 +70,9 @@ func TestAcc_FileFormatCSV(t *testing.T) { }, // IMPORT { - ResourceName: "snowflake_file_format.test", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{}, + ResourceName: "snowflake_file_format.test", + ImportState: true, + ImportStateVerify: true, }, }, }) diff --git a/pkg/resources/grant_privileges_to_share_acceptance_test.go b/pkg/resources/grant_privileges_to_share_acceptance_test.go index c4a1a3b87c..15d92a5767 100644 --- a/pkg/resources/grant_privileges_to_share_acceptance_test.go +++ b/pkg/resources/grant_privileges_to_share_acceptance_test.go @@ -533,7 +533,9 @@ func testAccCheckSharePrivilegesRevoked() func(*terraform.State) error { id := sdk.NewExternalObjectIdentifierFromFullyQualifiedName(rs.Primary.Attributes["to_share"]) grants, err := client.Grants.Show(ctx, &sdk.ShowGrantOptions{ To: &sdk.ShowGrantsTo{ - Share: sdk.NewAccountObjectIdentifier(id.Name()), + Share: &sdk.ShowGrantsToShare{ + Name: sdk.NewAccountObjectIdentifier(id.Name()), + }, }, }) if err != nil { diff --git a/pkg/resources/share_acceptance_test.go b/pkg/resources/share_acceptance_test.go index eac0f0be6d..486ad449d7 100644 --- a/pkg/resources/share_acceptance_test.go +++ b/pkg/resources/share_acceptance_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) +// TODO [SNOW-1284394]: Unskip the test func TestAcc_Share(t *testing.T) { t.Skip("second and third account must be set for Share acceptance tests") var account2 string diff --git a/pkg/sdk/grants.go b/pkg/sdk/grants.go index 32134a84fe..906d6c7d8a 100644 --- a/pkg/sdk/grants.go +++ b/pkg/sdk/grants.go @@ -176,16 +176,24 @@ type ShowGrantsOn struct { } type ShowGrantsTo struct { - Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` - User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` - Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` - DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` + Application AccountObjectIdentifier `ddl:"identifier" sql:"APPLICATION"` + ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` + Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` + User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` + Share *ShowGrantsToShare `ddl:"-"` + DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` +} + +type ShowGrantsToShare struct { + Name AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` + InApplicationPackage *AccountObjectIdentifier `ddl:"identifier" sql:"IN APPLICATION PACKAGE"` } type ShowGrantsOf struct { - Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` - DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` - Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` + ApplicationRole DatabaseObjectIdentifier `ddl:"identifier" sql:"APPLICATION ROLE"` + Role AccountObjectIdentifier `ddl:"identifier" sql:"ROLE"` + DatabaseRole DatabaseObjectIdentifier `ddl:"identifier" sql:"DATABASE ROLE"` + Share AccountObjectIdentifier `ddl:"identifier" sql:"SHARE"` } type grantRow struct { diff --git a/pkg/sdk/grants_test.go b/pkg/sdk/grants_test.go index 69ccf6b44c..ad20aca09e 100644 --- a/pkg/sdk/grants_test.go +++ b/pkg/sdk/grants_test.go @@ -1041,12 +1041,28 @@ func TestGrantShow(t *testing.T) { shareID := RandomAccountObjectIdentifier() opts := &ShowGrantOptions{ To: &ShowGrantsTo{ - Share: shareID, + Share: &ShowGrantsToShare{ + Name: shareID, + }, }, } assertOptsValidAndSQLEquals(t, opts, "SHOW GRANTS TO SHARE %s", shareID.FullyQualifiedName()) }) + t.Run("to share in application package", func(t *testing.T) { + shareID := RandomAccountObjectIdentifier() + packageId := RandomAccountObjectIdentifier() + opts := &ShowGrantOptions{ + To: &ShowGrantsTo{ + Share: &ShowGrantsToShare{ + Name: shareID, + InApplicationPackage: &packageId, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "SHOW GRANTS TO SHARE %s IN APPLICATION PACKAGE %s", shareID.FullyQualifiedName(), packageId.FullyQualifiedName()) + }) + t.Run("of role", func(t *testing.T) { roleID := RandomAccountObjectIdentifier() opts := &ShowGrantOptions{ diff --git a/pkg/sdk/testint/application_roles_gen_integration_test.go b/pkg/sdk/testint/application_roles_gen_integration_test.go index dd712a5e85..393a3178c0 100644 --- a/pkg/sdk/testint/application_roles_gen_integration_test.go +++ b/pkg/sdk/testint/application_roles_gen_integration_test.go @@ -2,10 +2,12 @@ package testint import ( "context" + "fmt" "testing" "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" ) @@ -21,7 +23,7 @@ import ( func TestInt_ApplicationRoles(t *testing.T) { client := testClient(t) - stageName := "stage_name" + stageName := random.AlphaN(8) stage, cleanupStage := createStage(t, client, sdk.NewSchemaObjectIdentifier(TestDatabaseName, TestSchemaName, stageName)) t.Cleanup(cleanupStage) @@ -79,4 +81,79 @@ func TestInt_ApplicationRoles(t *testing.T) { assertApplicationRoles(t, appRoles, "app_role_1", "some comment") assertApplicationRoles(t, appRoles, "app_role_2", "some comment2") }) + + t.Run("show grants to application role", func(t *testing.T) { + name := "app_role_1" + id := sdk.NewDatabaseObjectIdentifier(appName, name) + ctx := context.Background() + + opts := new(sdk.ShowGrantOptions) + opts.To = &sdk.ShowGrantsTo{ + ApplicationRole: id, + } + grants, err := client.Grants.Show(ctx, opts) + require.NoError(t, err) + + require.NotEmpty(t, grants) + require.NotEmpty(t, grants[0].CreatedOn) + require.Equal(t, sdk.ObjectPrivilegeUsage.String(), grants[0].Privilege) + require.Equal(t, sdk.ObjectTypeDatabase, grants[0].GrantedOn) + require.Equal(t, sdk.ObjectTypeApplicationRole, grants[0].GrantedTo) + }) + + t.Run("show grants of application role", func(t *testing.T) { + name := "app_role_1" + id := sdk.NewDatabaseObjectIdentifier(appName, name) + ctx := context.Background() + + opts := new(sdk.ShowGrantOptions) + opts.Of = &sdk.ShowGrantsOf{ + ApplicationRole: id, + } + grants, err := client.Grants.Show(ctx, opts) + require.NoError(t, err) + + require.NotEmpty(t, grants) + require.NotEmpty(t, grants[0].CreatedOn) + require.Equal(t, sdk.ObjectTypeRole, grants[0].GrantedTo) + require.Equal(t, sdk.NewAccountObjectIdentifier(appName), grants[0].GrantedBy) + }) + + t.Run("show grants to application", func(t *testing.T) { + // Need second app to be able to grant application role to it. Cannot grant to parent application (098806 (0A000): Cannot grant an APPLICATION ROLE to the parent APPLICATION). + stageName2 := random.AlphaN(8) + stage2, cleanupStage2 := createStage(t, client, sdk.NewSchemaObjectIdentifier(TestDatabaseName, TestSchemaName, stageName2)) + t.Cleanup(cleanupStage2) + + putOnStage(t, client, stage2, "manifest2.yml") + putOnStage(t, client, stage2, "setup.sql") + + appName2 := "snowflake_app_2" + cleanupApp2 := createApplication(t, client, appName2, appPackageName, versionName) + t.Cleanup(cleanupApp2) + + name := "app_role_1" + id := sdk.NewDatabaseObjectIdentifier(appName, name) + ctx := context.Background() + + _, err := client.ExecForTests(ctx, fmt.Sprintf(`GRANT APPLICATION ROLE %s TO APPLICATION %s`, id.FullyQualifiedName(), sdk.NewAccountObjectIdentifier(appName2).FullyQualifiedName())) + require.NoError(t, err) + defer func() { + _, err := client.ExecForTests(ctx, fmt.Sprintf(`REVOKE APPLICATION ROLE %s FROM APPLICATION %s`, id.FullyQualifiedName(), sdk.NewAccountObjectIdentifier(appName2).FullyQualifiedName())) + require.NoError(t, err) + }() + + opts := new(sdk.ShowGrantOptions) + opts.To = &sdk.ShowGrantsTo{ + Application: sdk.NewAccountObjectIdentifier(appName2), + } + grants, err := client.Grants.Show(ctx, opts) + require.NoError(t, err) + + require.NotEmpty(t, grants) + require.NotEmpty(t, grants[0].CreatedOn) + require.Equal(t, sdk.ObjectPrivilegeUsage.String(), grants[0].Privilege) + require.Equal(t, sdk.ObjectTypeApplicationRole, grants[0].GrantedOn) + require.Equal(t, sdk.ObjectTypeApplication, grants[0].GrantedTo) + }) } diff --git a/pkg/sdk/testint/grants_integration_test.go b/pkg/sdk/testint/grants_integration_test.go index 1b1a908778..43cd92c10f 100644 --- a/pkg/sdk/testint/grants_integration_test.go +++ b/pkg/sdk/testint/grants_integration_test.go @@ -756,6 +756,31 @@ func TestInt_GrantPrivilegeToShare(t *testing.T) { require.NoError(t, err) assertGrant(t, grants, table.ID(), sdk.ObjectPrivilegeSelect) + _, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Share: &sdk.ShowGrantsToShare{ + Name: shareTest.ID(), + }, + }, + }) + require.NoError(t, err) + + appPackageName := random.AlphaN(8) + cleanupAppPackage := createApplicationPackage(t, client, appPackageName) + t.Cleanup(cleanupAppPackage) + // TODO [SNOW-1284382]: alter the test when the syntax starts working + // 2024/03/29 17:04:20 [DEBUG] sql-conn-query: [query SHOW GRANTS TO SHARE "0a8DMkl3NOx7" IN APPLICATION PACKAGE "hziiAtqY" err 001003 (42000): SQL compilation error: + // syntax error line 1 at position 39 unexpected 'APPLICATION'. duration 445.248042ms args {}] (IYA62698) + _, err = client.Grants.Show(ctx, &sdk.ShowGrantOptions{ + To: &sdk.ShowGrantsTo{ + Share: &sdk.ShowGrantsToShare{ + Name: shareTest.ID(), + InApplicationPackage: sdk.Pointer(sdk.NewAccountObjectIdentifier(appPackageName)), + }, + }, + }) + require.Error(t, err) + err = client.Grants.RevokePrivilegeFromShare(ctx, []sdk.ObjectPrivilege{sdk.ObjectPrivilegeSelect}, &sdk.ShareGrantOn{ Table: &sdk.OnTable{ AllInSchema: testSchema(t).ID(), diff --git a/pkg/sdk/testint/testdata/manifest2.yml b/pkg/sdk/testint/testdata/manifest2.yml new file mode 100644 index 0000000000..b951fb531b --- /dev/null +++ b/pkg/sdk/testint/testdata/manifest2.yml @@ -0,0 +1,5 @@ +manifest_version: 1 # required +version: + name: application_roles_test_app_2 + label: "v1.0" + comment: "This application is used by Snowflake Terraform Provider for application role integration tests" diff --git a/pkg/snowflake/grant.go b/pkg/snowflake/grant.go index 2ee39bc330..92f4596d5d 100644 --- a/pkg/snowflake/grant.go +++ b/pkg/snowflake/grant.go @@ -1,13 +1,8 @@ package snowflake import ( - "database/sql" - "errors" "fmt" - "log" "strings" - - "github.com/jmoiron/sqlx" ) type grantType string @@ -408,63 +403,3 @@ func (ge *CurrentGrantExecutable) RevokeOwnership(r string) []string { func (ge *CurrentGrantExecutable) Show() string { return fmt.Sprintf(`SHOW GRANTS OF %v "%v"`, ge.granteeType, ge.granteeName) } - -type GrantDetail struct { - CreatedOn sql.NullString `db:"created_on"` - Privilege sql.NullString `db:"privilege"` - GrantedOn sql.NullString `db:"granted_on"` - Name sql.NullString `db:"name"` - GrantedTo sql.NullString `db:"granted_to"` - GranteeName sql.NullString `db:"grantee_name"` - GrantOption sql.NullString `db:"grant_option"` - GrantedBy sql.NullString `db:"granted_by"` -} - -func queryGrants(db *sql.DB, stmt string) ([]GrantDetail, error) { - rows, err := Query(db, stmt) - if err != nil { - return nil, err - } - defer rows.Close() - - grantDetails := []GrantDetail{} - err = sqlx.StructScan(rows, &grantDetails) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - log.Println("[DEBUG] no grants found") - return nil, fmt.Errorf("unable to scan rows for %s, err = %w", stmt, err) - } - return grantDetails, err - } - return grantDetails, nil -} - -func ShowGrantsOn(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW GRANTS ON %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowGrantsOnAccount(db *sql.DB) ([]GrantDetail, error) { - stmt := `SHOW GRANTS ON ACCOUNT` - return queryGrants(db, stmt) -} - -func ShowGrantsTo(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW GRANTS TO %v "%v"`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowGrantsOf(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW GRANTS OF %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowFutureGrantsIn(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW FUTURE GRANTS IN %v %v`, objectType, objectName) - return queryGrants(db, stmt) -} - -func ShowFutureGrantsTo(db *sql.DB, objectType, objectName string) ([]GrantDetail, error) { - stmt := fmt.Sprintf(`SHOW FUTURE GRANTS TO %v %v`, objectType, objectName) - return queryGrants(db, stmt) -}