Skip to content

Commit a0ba6ed

Browse files
Add new resource to support ALTER ROLE
1 parent 4c52d3a commit a0ba6ed

File tree

5 files changed

+405
-1
lines changed

5 files changed

+405
-1
lines changed

postgresql/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func Provider() *schema.Provider {
166166
"postgresql_schema": resourcePostgreSQLSchema(),
167167
"postgresql_role": resourcePostgreSQLRole(),
168168
"postgresql_function": resourcePostgreSQLFunction(),
169+
"postgresql_alter_role": resourcePostgreSQLAlterRole(),
169170
},
170171

171172
ConfigureFunc: providerConfigure,
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package postgresql
2+
3+
import (
4+
"database/sql"
5+
// "encoding/json"
6+
"fmt"
7+
"log"
8+
"strings"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/lib/pq"
12+
)
13+
14+
const (
15+
getAlterRoleQuery = `
16+
SELECT
17+
rolname as role,
18+
rolconfig as role_parameters
19+
FROM
20+
pg_catalog.pg_roles
21+
WHERE
22+
rolname = $1
23+
`
24+
)
25+
26+
func resourcePostgreSQLAlterRole() *schema.Resource {
27+
return &schema.Resource{
28+
Create: PGResourceFunc(resourcePostgreSQLAlterRoleCreate),
29+
Read: PGResourceFunc(resourcePostgreSQLAlterRoleRead),
30+
Delete: PGResourceFunc(resourcePostgreSQLAlterRoleDelete),
31+
32+
Schema: map[string]*schema.Schema{
33+
"role_name": {
34+
Type: schema.TypeString,
35+
Required: true,
36+
ForceNew: true,
37+
Description: "The name of the role to alter the attributes of",
38+
},
39+
"parameter_key": {
40+
Type: schema.TypeString,
41+
Required: true,
42+
ForceNew: true,
43+
Description: "The name of the parameter to alter on the role",
44+
},
45+
"parameter_value": {
46+
Type: schema.TypeString,
47+
Required: true,
48+
ForceNew: true,
49+
Description: "The value of the parameter which is being set",
50+
},
51+
},
52+
}
53+
}
54+
55+
func resourcePostgreSQLAlterRoleRead(db *DBConnection, d *schema.ResourceData) error {
56+
if !db.featureSupported(featurePrivileges) {
57+
return fmt.Errorf(
58+
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
59+
db.version,
60+
)
61+
}
62+
63+
return readAlterRole(db, d)
64+
}
65+
66+
func resourcePostgreSQLAlterRoleCreate(db *DBConnection, d *schema.ResourceData) error {
67+
if !db.featureSupported(featurePrivileges) {
68+
return fmt.Errorf(
69+
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
70+
db.version,
71+
)
72+
}
73+
74+
txn, err := startTransaction(db.client, "")
75+
if err != nil {
76+
return err
77+
}
78+
defer deferredRollback(txn)
79+
80+
// Reset the role alterations before altering them again.
81+
if err = resetAlterRole(txn, d); err != nil {
82+
return err
83+
}
84+
85+
if err = alterRole(txn, d); err != nil {
86+
return err
87+
}
88+
89+
if err = txn.Commit(); err != nil {
90+
return fmt.Errorf("could not commit transaction: %w", err)
91+
}
92+
93+
d.SetId(generateAlterRoleID(d))
94+
95+
return readAlterRole(db, d)
96+
}
97+
98+
func resourcePostgreSQLAlterRoleDelete(db *DBConnection, d *schema.ResourceData) error {
99+
if !db.featureSupported(featurePrivileges) {
100+
return fmt.Errorf(
101+
"postgresql_alter_role resource is not supported for this Postgres version (%s)",
102+
db.version,
103+
)
104+
}
105+
106+
txn, err := startTransaction(db.client, "")
107+
if err != nil {
108+
return err
109+
}
110+
defer deferredRollback(txn)
111+
112+
if err = resetAlterRole(txn, d); err != nil {
113+
return err
114+
}
115+
116+
if err = txn.Commit(); err != nil {
117+
return fmt.Errorf("could not commit transaction: %w", err)
118+
}
119+
120+
return nil
121+
}
122+
123+
func readAlterRole(db QueryAble, d *schema.ResourceData) error {
124+
var (
125+
roleName string
126+
roleParameters pq.ByteaArray
127+
)
128+
129+
alterRoleID := d.Id()
130+
alterParameterKey := d.Get("parameter_key")
131+
132+
values := []interface{}{
133+
&roleName,
134+
&roleParameters,
135+
}
136+
137+
err := db.QueryRow(getAlterRoleQuery, d.Get("role_name")).Scan(values...)
138+
switch {
139+
case err == sql.ErrNoRows:
140+
log.Printf("[WARN] PostgreSQL alter role (%q) not found", alterRoleID)
141+
d.SetId("")
142+
return nil
143+
case err != nil:
144+
return fmt.Errorf("error reading alter role: %w", err)
145+
}
146+
147+
d.Set("parameter_key", alterParameterKey)
148+
d.Set("parameter_value", "")
149+
d.Set("role_name", roleName)
150+
d.SetId(generateAlterRoleID(d))
151+
152+
for _, v := range roleParameters {
153+
parameter := string(v)
154+
parameterKey := strings.Split(parameter, "=")[0]
155+
parameterValue := strings.Split(parameter, "=")[1]
156+
if parameterKey == alterParameterKey {
157+
d.Set("parameter_key", parameterKey)
158+
d.Set("parameter_value", parameterValue)
159+
}
160+
}
161+
162+
return nil
163+
}
164+
165+
func createAlterRoleQuery(d *schema.ResourceData) string {
166+
alterRole, _ := d.Get("role_name").(string)
167+
alterParameterKey, _ := d.Get("parameter_key").(string)
168+
alterParameterValue, _ := d.Get("parameter_value").(string)
169+
170+
query := fmt.Sprintf(
171+
"ALTER ROLE %s SET %s TO %s",
172+
pq.QuoteIdentifier(alterRole),
173+
pq.QuoteIdentifier(alterParameterKey),
174+
pq.QuoteIdentifier(alterParameterValue),
175+
)
176+
177+
return query
178+
}
179+
180+
func createResetAlterRoleQuery(d *schema.ResourceData) string {
181+
alterRole, _ := d.Get("role_name").(string)
182+
alterParameterKey, _ := d.Get("parameter_key").(string)
183+
184+
return fmt.Sprintf(
185+
"ALTER ROLE %s RESET %s",
186+
pq.QuoteIdentifier(alterRole),
187+
pq.QuoteIdentifier(alterParameterKey),
188+
)
189+
}
190+
191+
func alterRole(txn *sql.Tx, d *schema.ResourceData) error {
192+
query := createAlterRoleQuery(d)
193+
log.Println(query)
194+
if _, err := txn.Exec(query); err != nil {
195+
return fmt.Errorf("could not execute alter query testing message: %w", err)
196+
}
197+
return nil
198+
}
199+
200+
func resetAlterRole(txn *sql.Tx, d *schema.ResourceData) error {
201+
query := createResetAlterRoleQuery(d)
202+
fmt.Println(query)
203+
if _, err := txn.Exec(query); err != nil {
204+
return fmt.Errorf("could not execute alter reset query (%s): %w", query, err)
205+
}
206+
return nil
207+
}
208+
209+
func generateAlterRoleID(d *schema.ResourceData) string {
210+
return strings.Join([]string{d.Get("role_name").(string), d.Get("parameter_key").(string), d.Get("parameter_value").(string)}, "_")
211+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package postgresql
2+
3+
import (
4+
"database/sql"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
"github.com/lib/pq"
12+
)
13+
14+
func TestCreateAlterRoleQuery(t *testing.T) {
15+
var roleName = "foo"
16+
var parameterKey = "bar"
17+
var parameterValue = "foo"
18+
19+
cases := []struct {
20+
resource map[string]interface{}
21+
expected string
22+
}{
23+
{
24+
resource: map[string]interface{}{
25+
"role_name": roleName,
26+
"parameter_key": parameterKey,
27+
"parameter_value": parameterValue,
28+
},
29+
expected: fmt.Sprintf("ALTER ROLE %s SET %s TO %s",
30+
pq.QuoteIdentifier(roleName),
31+
pq.QuoteIdentifier(parameterKey),
32+
pq.QuoteIdentifier(parameterValue)),
33+
},
34+
}
35+
36+
for _, c := range cases {
37+
out := createAlterRoleQuery(schema.TestResourceDataRaw(t, resourcePostgreSQLAlterRole().Schema, c.resource))
38+
if out != c.expected {
39+
t.Fatalf("Error matching output and expected: %#v vs %#v", out, c.expected)
40+
}
41+
}
42+
}
43+
44+
func TestResetRoleQuery(t *testing.T) {
45+
var roleName = "foo"
46+
var parameterKey = "pgaudit.role"
47+
48+
expected := fmt.Sprintf("ALTER ROLE %s RESET %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(parameterKey))
49+
50+
cases := []struct {
51+
resource map[string]interface{}
52+
}{
53+
{
54+
resource: map[string]interface{}{
55+
"role_name": roleName,
56+
"parameter_key": parameterKey,
57+
},
58+
},
59+
}
60+
61+
for _, c := range cases {
62+
out := createResetAlterRoleQuery(schema.TestResourceDataRaw(t, resourcePostgreSQLAlterRole().Schema, c.resource))
63+
if out != expected {
64+
t.Fatalf("Error matching output and expected: %#v vs %#v", out, expected)
65+
}
66+
}
67+
}
68+
69+
func TestAccPostgresqlAlterRole(t *testing.T) {
70+
skipIfNotAcc(t)
71+
72+
config := getTestConfig(t)
73+
dsn := config.connStr("postgres")
74+
75+
dbSuffix, teardown := setupTestDatabase(t, false, true)
76+
defer teardown()
77+
78+
_, roleName := getTestDBNames(dbSuffix)
79+
80+
parameterKey := "pgaudit.log"
81+
parameterValue := "ALL"
82+
83+
testAccPostgresqlAlterRoleResources := fmt.Sprintf(`
84+
resource "postgresql_alter_role" "alter_role" {
85+
role_name = "%s"
86+
parameter_key = "%s"
87+
parameter_value = "%s"
88+
}
89+
`, roleName, parameterKey, parameterValue)
90+
91+
resource.Test(t, resource.TestCase{
92+
PreCheck: func() {
93+
testAccPreCheck(t)
94+
testCheckCompatibleVersion(t, featurePrivileges)
95+
},
96+
Providers: testAccProviders,
97+
Steps: []resource.TestStep{
98+
{
99+
Config: testAccPostgresqlAlterRoleResources,
100+
Check: resource.ComposeTestCheckFunc(
101+
resource.TestCheckResourceAttr(
102+
"postgresql_alter_role.alter_role", "role_name", roleName),
103+
resource.TestCheckResourceAttr(
104+
"postgresql_alter_role.alter_role", "parameter_key", parameterKey),
105+
resource.TestCheckResourceAttr(
106+
"postgresql_alter_role.alter_role", "parameter_value", parameterValue),
107+
checkAlterRole(t, dsn, roleName, parameterKey, parameterValue),
108+
),
109+
},
110+
},
111+
})
112+
}
113+
114+
func checkAlterRole(t *testing.T, dsn, role string, parameterKey string, parameterValue string) resource.TestCheckFunc {
115+
return func(s *terraform.State) error {
116+
db, err := sql.Open("postgres", dsn)
117+
if err != nil {
118+
t.Fatalf("could to create connection pool: %v", err)
119+
}
120+
defer db.Close()
121+
122+
roleParameter := fmt.Sprintf("%s=%s", parameterKey, parameterValue)
123+
var _rez int
124+
err = db.QueryRow(`
125+
SELECT 1
126+
FROM pg_catalog.pg_roles
127+
WHERE rolname = $1
128+
AND $2=ANY(rolconfig)
129+
`, role, roleParameter).Scan(&_rez)
130+
131+
switch {
132+
case err == sql.ErrNoRows:
133+
return fmt.Errorf(
134+
"Role %s does not have the following attribute assigned %s",
135+
role, roleParameter,
136+
)
137+
138+
case err != nil:
139+
t.Fatalf("could not check roles attributes: %v", err)
140+
}
141+
142+
return nil
143+
}
144+
}

0 commit comments

Comments
 (0)