Skip to content
This repository has been archived by the owner on Nov 14, 2020. It is now read-only.

Add postgresql_grant_role resource #189

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b030d17
feat: [resource_postgresql_grant_role] add grant role resource
Vince-Chenal Jul 7, 2020
ed22ce1
feat: [resource_postgresql_grant_role] add a test file
Vince-Chenal Jul 7, 2020
ae00dbf
feat: [resource_postgresql_grant_role] add doc
Vince-Chenal Jul 7, 2020
63ee22a
Merge branch 'master' into add-grant-role-resource
dvdliao Sep 25, 2020
4993411
minor
dvdliao Sep 25, 2020
bbe8b0e
update website, fix tests
dvdliao Sep 26, 2020
2d3e085
use previous query
dvdliao Sep 26, 2020
330c3a0
add to changelog
dvdliao Sep 26, 2020
5e5fb65
check version in test
dvdliao Sep 26, 2020
a0f93d8
Update postgresql/resource_postgresql_grant_role.go
dvdliao Sep 29, 2020
93dae89
Update postgresql/resource_postgresql_grant_role.go
dvdliao Sep 29, 2020
8b1cd3e
Update postgresql/resource_postgresql_grant_role.go
dvdliao Sep 29, 2020
36539f2
Update website/docs/r/postgresql_grant_role.html.markdown
dvdliao Sep 29, 2020
a73177d
protect against panic
dvdliao Sep 29, 2020
406d3e2
add more notes to docs
dvdliao Oct 2, 2020
949f81c
correctly save quoted role searchpath
lovromazgon Nov 4, 2020
2392096
Merge branch 'master' into add-grant-role-resource
dvdliao Nov 11, 2020
6c1fd97
Set up Github Workflows
thenonameguy Nov 19, 2020
acaaf19
travis: Remove make website command
cyrilgdn Nov 26, 2020
3e0ab6d
Merge pull request #3 from thenonameguy/master
cyrilgdn Nov 26, 2020
46715a5
Prepare CHANGELOG for v1.8.0
cyrilgdn Nov 26, 2020
7da0aeb
Release v1.8.0
cyrilgdn Nov 26, 2020
d88b13b
Revert "Merge pull request #199 from estahn/lazy-connections"
cyrilgdn Nov 26, 2020
4465ed5
Bugfix release: 1.8.1
cyrilgdn Nov 26, 2020
a3e8c91
README: Update documentation link.
cyrilgdn Nov 27, 2020
3765f18
Merge pull request #1 from lovromazgon/searchpath-with-hyphen
cyrilgdn Nov 30, 2020
6383ee2
merge
dvdliao Nov 30, 2020
906dfb8
code review
dvdliao Dec 14, 2020
f1ea383
code review
dvdliao Dec 14, 2020
4c9f394
minor
dvdliao Dec 14, 2020
021f001
minor
dvdliao Dec 14, 2020
b9b2d65
cr
dvdliao Dec 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## 1.8.0 (Unreleased)

FEATURES:
* `postgresql_grant_role`: Non-authoritative. Grant role to another role.

## 1.7.1 (July 30, 2020)

BUG FIXES:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.14

require (
github.com/blang/semver v3.5.1+incompatible
github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/terraform-plugin-sdk v1.0.0
github.com/lib/pq v1.3.0
github.com/sean-/postgresql-acl v0.0.0-20161225120419-d10489e5d217
Expand Down
1 change: 1 addition & 0 deletions postgresql/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func Provider() terraform.ResourceProvider {
"postgresql_default_privileges": resourcePostgreSQLDefaultPrivileges(),
"postgresql_extension": resourcePostgreSQLExtension(),
"postgresql_grant": resourcePostgreSQLGrant(),
"postgresql_grant_role": resourcePostgreSQLGrantRole(),
"postgresql_schema": resourcePostgreSQLSchema(),
"postgresql_role": resourcePostgreSQLRole(),
},
Expand Down
222 changes: 222 additions & 0 deletions postgresql/resource_postgresql_grant_role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package postgresql

import (
"database/sql"
"fmt"
"log"
"strconv"
"strings"

"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

"github.com/lib/pq"
)

const (
// This returns the role membership for role, grant_role
getGrantRoleQuery = `
SELECT pg_get_userbyid(member) as role, pg_get_userbyid(roleid) as grant_role, admin_option
FROM pg_auth_members
WHERE pg_get_userbyid(member) = $1
AND pg_get_userbyid(roleid) = $2;
dvdliao marked this conversation as resolved.
Show resolved Hide resolved
`
)

func resourcePostgreSQLGrantRole() *schema.Resource {
return &schema.Resource{
Create: resourcePostgreSQLGrantRoleCreate,
Read: resourcePostgreSQLGrantRoleRead,
Delete: resourcePostgreSQLGrantRoleDelete,

Schema: map[string]*schema.Schema{
"role": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the role to grant grant_role",
},
"grant_role": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the role to is granted to role",
dvdliao marked this conversation as resolved.
Show resolved Hide resolved
},
"with_admin_option": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Default: false,
Description: "Permit the grant recipient to grant it to others",
},
},
}
}

func resourcePostgreSQLGrantRoleRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there not a worry of a panic if the type assertion fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've typically seen providers implemented without a worry here


if !client.featureSupported(featurePrivileges) {
return fmt.Errorf(
"postgresql_grant_role resource is not supported for this Postgres version (%s)",
client.version,
)
}

client.catalogLock.RLock()
defer client.catalogLock.RUnlock()

txn, err := startTransaction(client, "")
if err != nil {
return err
}
defer deferredRollback(txn)

return readGrantRole(txn, d)
}

func resourcePostgreSQLGrantRoleCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)

if !client.featureSupported(featurePrivileges) {
return fmt.Errorf(
"postgresql_grant_role resource is not supported for this Postgres version (%s)",
client.version,
)
}

client.catalogLock.Lock()
defer client.catalogLock.Unlock()

txn, err := startTransaction(client, "")
if err != nil {
return err
}
defer deferredRollback(txn)

// Revoke the granted roles before granting them again.
if err = revokeRole(txn, d); err != nil {
return err
}

if err = grantRole(txn, d); err != nil {
return err
}

if err = txn.Commit(); err != nil {
return errwrap.Wrapf("could not commit transaction: {{err}}", err)
}

d.SetId(generateGrantRoleID(d))

txn, err = startTransaction(client, "")
if err != nil {
return err
}
defer deferredRollback(txn)

return readGrantRole(txn, d)
}

func resourcePostgreSQLGrantRoleDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Client)

if !client.featureSupported(featurePrivileges) {
return fmt.Errorf(
"postgresql_grant_role resource is not supported for this Postgres version (%s)",
client.version,
)
}

client.catalogLock.Lock()
defer client.catalogLock.Unlock()

txn, err := startTransaction(client, "")
if err != nil {
return err
}
defer deferredRollback(txn)

if err = revokeRole(txn, d); err != nil {
return err
}

if err = txn.Commit(); err != nil {
return errwrap.Wrapf("could not commit transaction: {{err}}", err)
}

return nil
}

func readGrantRole(txn *sql.Tx, d *schema.ResourceData) error {
var roleName, grantRoleName string
var withAdminOption bool

grantRoleId := d.Id()

values := []interface{}{
&roleName,
&grantRoleName,
&withAdminOption,
}

err := txn.QueryRow(getGrantRoleQuery, d.Get("role"), d.Get("grant_role")).Scan(values...)
switch {
case err == sql.ErrNoRows:
log.Printf("[WARN] PostgreSQL grant role (%q) not found", grantRoleId)
d.SetId("")
return nil
case err != nil:
return errwrap.Wrapf("Error reading grant role: {{err}}", err)
}

d.Set("role", roleName)
d.Set("grant_role", grantRoleName)
d.Set("with_admin_option", withAdminOption)

d.SetId(generateGrantRoleID(d))

return nil
}

func createGrantRoleQuery(d *schema.ResourceData) string {
var query string
query = fmt.Sprintf(
"GRANT %s TO %s",
pq.QuoteIdentifier(d.Get("grant_role").(string)),
pq.QuoteIdentifier(d.Get("role").(string)),
dvdliao marked this conversation as resolved.
Show resolved Hide resolved
)
if d.Get("with_admin_option").(bool) == true {
dvdliao marked this conversation as resolved.
Show resolved Hide resolved
query = query + " WITH ADMIN OPTION"
}

return query
}

func createRevokeRoleQuery(d *schema.ResourceData) string {
return fmt.Sprintf(
"REVOKE %s FROM %s",
pq.QuoteIdentifier(d.Get("grant_role").(string)),
pq.QuoteIdentifier(d.Get("role").(string)),
)
}

func grantRole(txn *sql.Tx, d *schema.ResourceData) error {
query := createGrantRoleQuery(d)
if _, err := txn.Exec(query); err != nil {
return errwrap.Wrapf("could not execute grant query: {{err}}", err)
}
return nil
}

func revokeRole(txn *sql.Tx, d *schema.ResourceData) error {
query := createRevokeRoleQuery(d)
if _, err := txn.Exec(query); err != nil {
return errwrap.Wrapf("could not execute revoke query: {{err}}", err)
}
return nil
}

func generateGrantRoleID(d *schema.ResourceData) string {
return strings.Join([]string{d.Get("role").(string), d.Get("grant_role").(string), strconv.FormatBool(d.Get("with_admin_option").(bool))}, "_")
}
Loading