Skip to content

Commit

Permalink
PLAT-1484 Add new resource for ALTER ROLE
Browse files Browse the repository at this point in the history
  • Loading branch information
bmedlock-depop committed May 17, 2022
1 parent 4c52d3a commit 806b1cc
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 1 deletion.
1 change: 1 addition & 0 deletions postgresql/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func Provider() *schema.Provider {
"postgresql_schema": resourcePostgreSQLSchema(),
"postgresql_role": resourcePostgreSQLRole(),
"postgresql_function": resourcePostgreSQLFunction(),
"postgresql_alter_role": resourcePostgreSQLAlterRole(),
},

ConfigureFunc: providerConfigure,
Expand Down
211 changes: 211 additions & 0 deletions postgresql/resource_postgresql_alter_role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package postgresql

import (
"database/sql"
// "encoding/json"
"fmt"
"log"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/lib/pq"
)

const (
getAlterRoleQuery = `
SELECT
rolname as role,
rolconfig as role_parameters
FROM
pg_catalog.pg_roles
WHERE
rolname = $1
`
)

func resourcePostgreSQLAlterRole() *schema.Resource {
return &schema.Resource{
Create: PGResourceFunc(resourcePostgreSQLAlterRoleCreate),
Read: PGResourceFunc(resourcePostgreSQLAlterRoleRead),
Delete: PGResourceFunc(resourcePostgreSQLAlterRoleDelete),

Schema: map[string]*schema.Schema{
"role_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the role to alter the attributes of",
},
"parameter_key": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The name of the parameter to alter on the role",
},
"parameter_value": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The value of the parameter which is being set",
},
},
}
}

func resourcePostgreSQLAlterRoleRead(db *DBConnection, d *schema.ResourceData) error {
if !db.featureSupported(featurePrivileges) {
return fmt.Errorf(
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
db.version,
)
}

return readAlterRole(db, d)
}

func resourcePostgreSQLAlterRoleCreate(db *DBConnection, d *schema.ResourceData) error {
if !db.featureSupported(featurePrivileges) {
return fmt.Errorf(
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
db.version,
)
}

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

// Reset the role alterations before altering them again.
if err = resetAlterRole(txn, d); err != nil {
return err
}

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

if err = txn.Commit(); err != nil {
return fmt.Errorf("could not commit transaction: %w", err)
}

d.SetId(generateAlterRoleID(d))

return readAlterRole(db, d)
}

func resourcePostgreSQLAlterRoleDelete(db *DBConnection, d *schema.ResourceData) error {
if !db.featureSupported(featurePrivileges) {
return fmt.Errorf(
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
db.version,
)
}

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

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

if err = txn.Commit(); err != nil {
return fmt.Errorf("could not commit transaction: %w", err)
}

return nil
}

func readAlterRole(db QueryAble, d *schema.ResourceData) error {
var (
roleName string
roleParameters pq.ByteaArray
)

alterRoleID := d.Id()
alterParameterKey := d.Get("parameter_key")

values := []interface{}{
&roleName,
&roleParameters,
}

err := db.QueryRow(getAlterRoleQuery, d.Get("role_name")).Scan(values...)
switch {
case err == sql.ErrNoRows:
log.Printf("[WARN] PostgreSQL alter role (%q) not found", alterRoleID)
d.SetId("")
return nil
case err != nil:
return fmt.Errorf("error reading alter role: %w", err)
}

d.Set("parameter_key", alterParameterKey)
d.Set("parameter_value", "")
d.Set("role_name", roleName)
d.SetId(generateAlterRoleID(d))

for _, v := range roleParameters {
parameter := string(v)
parameterKey := strings.Split(parameter, "=")[0]
parameterValue := strings.Split(parameter, "=")[1]
if parameterKey == alterParameterKey {
d.Set("parameter_key", parameterKey)
d.Set("parameter_value", parameterValue)
}
}

return nil
}

func createAlterRoleQuery(d *schema.ResourceData) string {
alterRole, _ := d.Get("role_name").(string)
alterParameterKey, _ := d.Get("parameter_key").(string)
alterParameterValue, _ := d.Get("parameter_value").(string)

query := fmt.Sprintf(
"ALTER ROLE %s SET %s TO %s",
pq.QuoteIdentifier(alterRole),
pq.QuoteIdentifier(alterParameterKey),
pq.QuoteIdentifier(alterParameterValue),
)

return query
}

func createResetAlterRoleQuery(d *schema.ResourceData) string {
alterRole, _ := d.Get("role_name").(string)
alterParameterKey, _ := d.Get("parameter_key").(string)

return fmt.Sprintf(
"ALTER ROLE %s RESET %s",
pq.QuoteIdentifier(alterRole),
pq.QuoteIdentifier(alterParameterKey),
)
}

func alterRole(txn *sql.Tx, d *schema.ResourceData) error {
query := createAlterRoleQuery(d)
log.Println(query)
if _, err := txn.Exec(query); err != nil {
return fmt.Errorf("could not execute alter query testing message: %w", err)
}
return nil
}

func resetAlterRole(txn *sql.Tx, d *schema.ResourceData) error {
query := createResetAlterRoleQuery(d)
fmt.Println(query)
if _, err := txn.Exec(query); err != nil {
return fmt.Errorf("could not execute alter reset query (%s): %w", query, err)
}
return nil
}

func generateAlterRoleID(d *schema.ResourceData) string {
return strings.Join([]string{d.Get("role_name").(string), d.Get("parameter_key").(string), d.Get("parameter_value").(string)}, "_")
}
144 changes: 144 additions & 0 deletions postgresql/resource_postgresql_alter_role_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package postgresql

import (
"database/sql"
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/lib/pq"
)

func TestCreateAlterRoleQuery(t *testing.T) {
var roleName = "foo"
var parameterKey = "bar"
var parameterValue = "foo"

cases := []struct {
resource map[string]interface{}
expected string
}{
{
resource: map[string]interface{}{
"role_name": roleName,
"parameter_key": parameterKey,
"parameter_value": parameterValue,
},
expected: fmt.Sprintf("ALTER ROLE %s SET %s TO %s",
pq.QuoteIdentifier(roleName),
pq.QuoteIdentifier(parameterKey),
pq.QuoteIdentifier(parameterValue)),
},
}

for _, c := range cases {
out := createAlterRoleQuery(schema.TestResourceDataRaw(t, resourcePostgreSQLAlterRole().Schema, c.resource))
if out != c.expected {
t.Fatalf("Error matching output and expected: %#v vs %#v", out, c.expected)
}
}
}

func TestResetRoleQuery(t *testing.T) {
var roleName = "foo"
var parameterKey = "pgaudit.role"

expected := fmt.Sprintf("ALTER ROLE %s RESET %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(parameterKey))

cases := []struct {
resource map[string]interface{}
}{
{
resource: map[string]interface{}{
"role_name": roleName,
"parameter_key": parameterKey,
},
},
}

for _, c := range cases {
out := createResetAlterRoleQuery(schema.TestResourceDataRaw(t, resourcePostgreSQLAlterRole().Schema, c.resource))
if out != expected {
t.Fatalf("Error matching output and expected: %#v vs %#v", out, expected)
}
}
}

func TestAccPostgresqlAlterRole(t *testing.T) {
skipIfNotAcc(t)

config := getTestConfig(t)
dsn := config.connStr("postgres")

dbSuffix, teardown := setupTestDatabase(t, false, true)
defer teardown()

_, roleName := getTestDBNames(dbSuffix)

parameterKey := "pgaudit.log"
parameterValue := "ALL"

testAccPostgresqlAlterRoleResources := fmt.Sprintf(`
resource "postgresql_alter_role" "alter_role" {
role_name = "%s"
parameter_key = "%s"
parameter_value = "%s"
}
`, roleName, parameterKey, parameterValue)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testCheckCompatibleVersion(t, featurePrivileges)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccPostgresqlAlterRoleResources,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"postgresql_alter_role.alter_role", "role_name", roleName),
resource.TestCheckResourceAttr(
"postgresql_alter_role.alter_role", "parameter_key", parameterKey),
resource.TestCheckResourceAttr(
"postgresql_alter_role.alter_role", "parameter_value", parameterValue),
checkAlterRole(t, dsn, roleName, parameterKey, parameterValue),
),
},
},
})
}

func checkAlterRole(t *testing.T, dsn, role string, parameterKey string, parameterValue string) resource.TestCheckFunc {
return func(s *terraform.State) error {
db, err := sql.Open("postgres", dsn)
if err != nil {
t.Fatalf("could to create connection pool: %v", err)
}
defer db.Close()

roleParameter := fmt.Sprintf("%s=%s", parameterKey, parameterValue)
var _rez int
err = db.QueryRow(`
SELECT 1
FROM pg_catalog.pg_roles
WHERE rolname = $1
AND $2=ANY(rolconfig)
`, role, roleParameter).Scan(&_rez)

switch {
case err == sql.ErrNoRows:
return fmt.Errorf(
"Role %s does not have the following attribute assigned %s",
role, roleParameter,
)

case err != nil:
t.Fatalf("could not check roles attributes: %v", err)
}

return nil
}
}
Loading

0 comments on commit 806b1cc

Please sign in to comment.