-
Notifications
You must be signed in to change notification settings - Fork 79
Grant owner role to connected user for default privileges management #71
Conversation
Usage example possible now: https://gist.github.com/zytek/bd87ae7f52d25dc0743226a63d07c5d4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zytek Many thanks for your work on this issue and sorry for the response delay.
I added a few remarks.
For the tests, I didn't find yet a (not too ugly) way to connect as non-superuser like RDS and make the failing test which will pass thanks to this fix. I'll need to rework a bit the test provider for that.
But you can make similar two tests that I've done for the same behavior in database resource: https://github.com/terraform-providers/terraform-provider-postgresql/blob/master/postgresql/resource_postgresql_database_test.go#L186-L263
It tests that the temporary granted role is correctly revoked, or not revoked if connected user was actually already a member of the owner role.
@@ -147,6 +163,18 @@ func resourcePostgreSQLDefaultPrivilegesDelete(d *schema.ResourceData, meta inte | |||
} | |||
defer deferredRollback(txn) | |||
|
|||
// Needed in order to set the owner of the db if the connection user is not a | |||
// superuser | |||
ownerGranted, err := grantRoleMembership(client.DB(), owner, currentUser) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above.
@@ -110,6 +112,18 @@ func resourcePostgreSQLDefaultPrivilegesCreate(d *schema.ResourceData, meta inte | |||
} | |||
defer deferredRollback(txn) | |||
|
|||
// Needed in order to set the owner of the db if the connection user is not a | |||
// superuser | |||
ownerGranted, err := grantRoleMembership(client.DB(), owner, currentUser) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case I think it would be great to use txn
instead of creating a new connection (and so you can't use defer
to call revokeRoleMembership
but you can call it just before the txn.Commit();
Thanks to that this role will not be granted outside of the transaction.
You just need the type of the first parameter in both grantRoleMembership
and revokeRoleMembership
to accept sql.Tx
or sql.Db
.
I already wrote in another (not yet published) PR an interface for that (inspired from an issue in the database/sql
lib):
// QueryAble is common interface for sql.DB and sql.Tx
// (cf https://github.com/golang/go/issues/14468)
type QueryAble interface {
Exec(query string, args ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row
}
[...]
func grantRoleMembership(db QueryAble, [...])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it in the same way we already do that GRANT, I guess refactoring this to use txn
could be made in another PR ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's done like that for the postgresql_database
resource because databases cannot be created/dropped in a transaction, so there is no opened transactions where it's done.
But in this case all the queries we are doing are done in the transaction. So I would prefer using the transaction too for these queries.
But I can do it if you prefer.
@@ -38,7 +38,7 @@ func resourcePostgreSQLDefaultPrivileges() *schema.Resource { | |||
Type: schema.TypeString, | |||
Required: true, | |||
ForceNew: true, | |||
Description: "Role for which apply default privileges (You can change default privileges only for objects that will be created by yourself or by roles that you are a member of)", | |||
Description: "The name of an existing role of which the current (connected user) role is a member (you can change default privileges only for objects that will be created by yourself or by roles that you are a member of)", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
existing role of which the current (connected user) role is a member
Hmm actually no... (thanks to this PR)
My description was indeed not very clear but the connected user does not have to be a member of the owner role as we will, with this PR, temporary grant it just to apply default privilieges.
What the owner means:
If we have a database test
which owner is test_owner
and we have another role call readonly
, we will write:
resource postgresql_default_privileges "read_only_tables" {
role = "readonly"
database = "test"
schema = "public"
owner = "test_owner"
object_type = "table"
privileges = ["SELECT"]
}
So each time test_owner
will create a table in this database (in schema public
), readonly
user will automatically have SELECT
privileges on it.
But to apply this postgresql_default_privileges
, the provider can be connected as a user (let's say postgres
) which either is superuser
so will have rights to apply it anyway, or is not superuser
so the provider will temporary grant to postgres
the test_owner
role (if it's not already the case) to change these default privileges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied that desc from Postgres docs.. and it is correct, but in our case it is correct only for a brief moment :) We should probably describe this temp grant role process in the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's correct for the point of view of a user executing the query. But as a user reading the provider documentation, it makes me think that the role I configured in the provider connection options needs to be a member of owner.
Which is not the case anymore thanks to this PR.
But maybe it's my bad English level which fails my comprehension 🤷♂️
Could we move that to come kind of commons/helper functions that would be tested once? No need to test the same behaviour multiple times? |
Why not, I don't have an idea of how to do that yet, but if you have an idea for that feel free to propose 👍 |
Signed-off-by: Jakub Paweł Głazik <zytek@nuxi.pl>
Make sure connected user has proper permissions to manage default privileges. Fixed hashicorp#70 Signed-off-by: Jakub Paweł Głazik <zytek@nuxi.pl>
So in this way this temporary grant is not even seen outside the transaction. This also adds a test to verify that owner is correctly revoked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zytek So i added a commit to your PR which:
- Rebase on master.
- Use the existing transactions to grant / revoke the owner to connected user. So this temporary privilege is not even seen outside of the transaction.
- Add the little test that I talked about. It's not the best solution but at least it covered the code.
- Change again the description of the owner field. Actually I talked with a co-worker about this description and we thought that even the field name is not very clear. So for now I set this little description but in the future we probably need to rename the field and complete/enhance the description (and especially in the website documentation).
I will quickly test that on RDS again to be sure it works then merge this PR. (If you want to take a look and approve / comment / request changes before, feel free!)
Thanks again for your work 👍 !
@cyrilgdn hey Cyril, thanks for looking into it and moving it forward. I just did a recompile with your changes and ran a quick test on my TF setup. Works fine.
|
Make sure connected user has proper permissions to manage default
privileges.
Fixed #70
NOTE: please assist in running acc tests, I have tested it only with my dev RDS