Skip to content

Commit

Permalink
Merge pull request #9 from Leafly-com/allow-grant-updates
Browse files Browse the repository at this point in the history
Allow grant updates
  • Loading branch information
wilsonjackson authored Nov 2, 2022
2 parents 9ada18f + 3944781 commit 7f7cdc0
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 14 deletions.
14 changes: 14 additions & 0 deletions postgresql/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,20 @@ func validatePrivileges(d *schema.ResourceData) error {
return nil
}

// getDataForRevoke returns a value for a REVOKE statement from the given resource data key.
//
// If the key has changes, the "old" value, which incorporates both previous Terraform state and any drift detected
// during the planning phase, will be returned.
//
// If the key does not have changes, the current value is returned.
func getDataForRevoke(d *schema.ResourceData, key string) interface{} {
if d.HasChange(key) {
oldValue, _ := d.GetChange(key)
return oldValue
}
return d.Get(key)
}

func pgArrayToSet(arr pq.ByteaArray) *schema.Set {
s := make([]interface{}, len(arr))
for i, v := range arr {
Expand Down
39 changes: 26 additions & 13 deletions postgresql/resource_postgresql_grant.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package postgresql

import (
"context"
"database/sql"
"fmt"
"log"
Expand Down Expand Up @@ -36,13 +37,23 @@ var objectTypes = map[string]string{

func resourcePostgreSQLGrant() *schema.Resource {
return &schema.Resource{
Create: PGResourceFunc(resourcePostgreSQLGrantCreate),
// Since all of this resource's arguments force a recreation
// there's no need for an Update function
// Update:
Create: PGResourceFunc(resourcePostgreSQLGrantCreateOrUpdate),
Update: PGResourceFunc(resourcePostgreSQLGrantCreateOrUpdate),
Read: PGResourceFunc(resourcePostgreSQLGrantRead),
Delete: PGResourceFunc(resourcePostgreSQLGrantDelete),

CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, i interface{}) error {
// Object types that only support a single object always force recreation
objectType := strings.ToUpper(diff.Get("object_type").(string))
if (diff.HasChange("permissions") || diff.HasChange("objects")) &&
(objectType == "FOREIGN_DATA_WRAPPER" || objectType == "FOREIGN_SERVER") {
if err := diff.ForceNew("objects"); err != nil {
return err
}
}
return nil
},

Schema: map[string]*schema.Schema{
"role": {
Type: schema.TypeString,
Expand Down Expand Up @@ -72,23 +83,23 @@ func resourcePostgreSQLGrant() *schema.Resource {
"objects": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Description: "The specific objects to grant privileges on for this role (empty means all objects of the requested type)",
},
"columns": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Description: "The specific columns to grant privileges on for this role",
},
"privileges": {
Type: schema.TypeSet,
Required: true,
ForceNew: true,
ForceNew: false,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
Description: "The list of privileges to grant",
Expand Down Expand Up @@ -128,7 +139,7 @@ func resourcePostgreSQLGrantRead(db *DBConnection, d *schema.ResourceData) error
return readRolePrivileges(txn, d)
}

func resourcePostgreSQLGrantCreate(db *DBConnection, d *schema.ResourceData) error {
func resourcePostgreSQLGrantCreateOrUpdate(db *DBConnection, d *schema.ResourceData) error {
if err := validateFeatureSupport(db, d); err != nil {
return fmt.Errorf("feature is not supported: %v", err)
}
Expand Down Expand Up @@ -606,9 +617,9 @@ func createRevokeQuery(d *schema.ResourceData) string {
pq.QuoteIdentifier(d.Get("role").(string)),
)
case "COLUMN":
objects := d.Get("objects").(*schema.Set)
columns := d.Get("columns").(*schema.Set)
privileges := d.Get("privileges").(*schema.Set)
objects := getDataForRevoke(d, "objects").(*schema.Set)
columns := getDataForRevoke(d, "columns").(*schema.Set)
privileges := getDataForRevoke(d, "privileges").(*schema.Set)
if privileges.Len() == 0 || columns.Len() == 0 {
// No privileges to revoke, so don't revoke anything
query = "SELECT NULL"
Expand All @@ -622,8 +633,8 @@ func createRevokeQuery(d *schema.ResourceData) string {
)
}
case "TABLE", "SEQUENCE", "FUNCTION", "PROCEDURE", "ROUTINE":
objects := d.Get("objects").(*schema.Set)
privileges := d.Get("privileges").(*schema.Set)
objects := getDataForRevoke(d, "objects").(*schema.Set)
privileges := getDataForRevoke(d, "privileges").(*schema.Set)
if objects.Len() > 0 {
if privileges.Len() > 0 {
// Revoking specific privileges instead of all privileges
Expand Down Expand Up @@ -668,13 +679,15 @@ func grantRolePrivileges(txn *sql.Tx, d *schema.ResourceData) error {
}

query := createGrantQuery(d, privileges)
log.Printf("[DEBUG] grantRolePrivileges: %s", query)

_, err := txn.Exec(query)
return err
}

func revokeRolePrivileges(txn *sql.Tx, d *schema.ResourceData) error {
query := createRevokeQuery(d)
log.Printf("[DEBUG] revokeRolePrivileges: %s", query)
if len(query) == 0 {
// Query is empty, don't run anything
return nil
Expand Down
28 changes: 27 additions & 1 deletion postgresql/resource_postgresql_grant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func TestCreateRevokeQuery(t *testing.T) {

cases := []struct {
resource *schema.ResourceData
diff map[string]interface{}
expected string
}{
{
Expand Down Expand Up @@ -261,6 +262,20 @@ func TestCreateRevokeQuery(t *testing.T) {
}),
expected: fmt.Sprintf(`REVOKE UPDATE,INSERT ON TABLE %[1]s."o2",%[1]s."o1" FROM %s`, pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)),
},
// Haven't been able to figure out how to mock old/new state in a Terraform ResourceData instance
//{
// resource: schema.TestResourceDataRaw(t, resourcePostgreSQLGrant().Schema, map[string]interface{}{
// "object_type": "table",
// "objects": tableObjects,
// "schema": databaseName,
// "role": roleName,
// "privileges": []interface{}{"INSERT", "UPDATE"},
// }),
// diff: map[string]interface{}{
// "objects": []interface{}{"o1", "o3"},
// },
// expected: fmt.Sprintf(`REVOKE UPDATE,INSERT ON TABLE %[1]s."o2",%[1]s."o1" FROM %s`, pq.QuoteIdentifier(databaseName), pq.QuoteIdentifier(roleName)),
//},
{
resource: schema.TestResourceDataRaw(t, resourcePostgreSQLGrant().Schema, map[string]interface{}{
"object_type": "column",
Expand Down Expand Up @@ -291,7 +306,18 @@ func TestCreateRevokeQuery(t *testing.T) {
}

for _, c := range cases {
out := createRevokeQuery(c.resource)
d := c.resource
d.SetId("fakeID")
d = resourcePostgreSQLGrant().Data(d.State())
// Attempt (failed) at mocking new/old instance state
//if len(c.diff) > 0 {
// for k, v := range c.diff {
// if err := d.Set(k, v); err != nil {
// t.Fatalf(err.Error())
// }
// }
//}
out := createRevokeQuery(d)
if out != c.expected {
t.Fatalf("Error matching output and expected: %#v vs %#v", out, c.expected)
}
Expand Down

0 comments on commit 7f7cdc0

Please sign in to comment.