Skip to content

Commit 5e17463

Browse files
Support of ALTER ROLE ... SET ROLE ... (#209)
1 parent 924be53 commit 5e17463

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

postgresql/resource_postgresql_role.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const (
3434
roleRolesAttr = "roles"
3535
roleSearchPathAttr = "search_path"
3636
roleStatementTimeoutAttr = "statement_timeout"
37+
roleAssumeRoleAttr = "assume_role"
3738

3839
// Deprecated options
3940
roleDepEncryptedAttr = "encrypted"
@@ -167,6 +168,11 @@ func resourcePostgreSQLRole() *schema.Resource {
167168
Description: "Abort any statement that takes more than the specified number of milliseconds",
168169
ValidateFunc: validation.IntAtLeast(0),
169170
},
171+
roleAssumeRoleAttr: {
172+
Type: schema.TypeString,
173+
Optional: true,
174+
Description: "Role to switch to at login",
175+
},
170176
},
171177
}
172178
}
@@ -301,6 +307,10 @@ func resourcePostgreSQLRoleCreate(db *DBConnection, d *schema.ResourceData) erro
301307
return err
302308
}
303309

310+
if err = setAssumeRole(txn, d); err != nil {
311+
return err
312+
}
313+
304314
if err = txn.Commit(); err != nil {
305315
return fmt.Errorf("could not commit transaction: %w", err)
306316
}
@@ -446,6 +456,7 @@ func resourcePostgreSQLRoleReadImpl(db *DBConnection, d *schema.ResourceData) er
446456
d.Set(roleBypassRLSAttr, roleBypassRLS)
447457
d.Set(roleRolesAttr, pgArrayToSet(roleRoles))
448458
d.Set(roleSearchPathAttr, readSearchPath(roleConfig))
459+
d.Set(roleAssumeRoleAttr, readAssumeRole(roleConfig))
449460

450461
statementTimeout, err := readStatementTimeout(roleConfig)
451462
if err != nil {
@@ -522,6 +533,20 @@ func readStatementTimeout(roleConfig pq.ByteaArray) (int, error) {
522533
return 0, nil
523534
}
524535

536+
// readAssumeRole searches for a role entry in the rolconfig array.
537+
// In case no such value is present, it returns empty string.
538+
func readAssumeRole(roleConfig pq.ByteaArray) string {
539+
var res string
540+
var assumeRoleAttr = "role"
541+
for _, v := range roleConfig {
542+
config := string(v)
543+
if strings.HasPrefix(config, assumeRoleAttr) {
544+
res = strings.TrimPrefix(config, assumeRoleAttr+"=")
545+
}
546+
}
547+
return res
548+
}
549+
525550
// readRolePassword reads password either from Postgres if admin user is a superuser
526551
// or only from Terraform state.
527552
func readRolePassword(db *DBConnection, d *schema.ResourceData, roleCanLogin bool) (string, error) {
@@ -660,6 +685,10 @@ func resourcePostgreSQLRoleUpdate(db *DBConnection, d *schema.ResourceData) erro
660685
return err
661686
}
662687

688+
if err = setAssumeRole(txn, d); err != nil {
689+
return err
690+
}
691+
663692
if err = txn.Commit(); err != nil {
664693
return fmt.Errorf("could not commit transaction: %w", err)
665694
}
@@ -1008,3 +1037,28 @@ func setIdleInTransactionSessionTimeout(txn *sql.Tx, d *schema.ResourceData) err
10081037
}
10091038
return nil
10101039
}
1040+
1041+
func setAssumeRole(txn *sql.Tx, d *schema.ResourceData) error {
1042+
if !d.HasChange(roleAssumeRoleAttr) {
1043+
return nil
1044+
}
1045+
1046+
roleName := d.Get(roleNameAttr).(string)
1047+
assumeRole := d.Get(roleAssumeRoleAttr).(string)
1048+
if assumeRole != "" {
1049+
sql := fmt.Sprintf(
1050+
"ALTER ROLE %s SET ROLE TO %s", pq.QuoteIdentifier(roleName), pq.QuoteIdentifier(assumeRole),
1051+
)
1052+
if _, err := txn.Exec(sql); err != nil {
1053+
return fmt.Errorf("could not set role %s for %s: %w", assumeRole, roleName, err)
1054+
}
1055+
} else {
1056+
sql := fmt.Sprintf(
1057+
"ALTER ROLE %s RESET ROLE", pq.QuoteIdentifier(roleName),
1058+
)
1059+
if _, err := txn.Exec(sql); err != nil {
1060+
return fmt.Errorf("could not reset role for %s: %w", roleName, err)
1061+
}
1062+
}
1063+
return nil
1064+
}

postgresql/resource_postgresql_role_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestAccPostgresqlRole_Basic(t *testing.T) {
4646
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "skip_reassign_owned", "false"),
4747
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "statement_timeout", "0"),
4848
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "idle_in_transaction_session_timeout", "0"),
49+
resource.TestCheckResourceAttr("postgresql_role.role_with_defaults", "assume_role", ""),
4950

5051
resource.TestCheckResourceAttr("postgresql_role.role_with_create_database", "name", "role_with_create_database"),
5152
resource.TestCheckResourceAttr("postgresql_role.role_with_create_database", "create_database", "true"),
@@ -120,6 +121,7 @@ resource "postgresql_role" "update_role" {
120121
search_path = ["mysearchpath"]
121122
statement_timeout = 30000
122123
idle_in_transaction_session_timeout = 60000
124+
assume_role = "${postgresql_role.group_role.name}"
123125
}
124126
`
125127
resource.Test(t, resource.TestCase{
@@ -143,6 +145,7 @@ resource "postgresql_role" "update_role" {
143145
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"),
144146
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"),
145147
resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"),
148+
resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""),
146149
testAccCheckRoleCanLogin(t, "update_role", "toto"),
147150
),
148151
},
@@ -163,6 +166,7 @@ resource "postgresql_role" "update_role" {
163166
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.0", "mysearchpath"),
164167
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "30000"),
165168
resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "60000"),
169+
resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", "group_role"),
166170
testAccCheckRoleCanLogin(t, "update_role2", "titi"),
167171
),
168172
},
@@ -180,6 +184,7 @@ resource "postgresql_role" "update_role" {
180184
resource.TestCheckResourceAttr("postgresql_role.update_role", "search_path.#", "0"),
181185
resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"),
182186
resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"),
187+
resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""),
183188
testAccCheckRoleCanLogin(t, "update_role", "toto"),
184189
),
185190
},
@@ -412,6 +417,7 @@ resource "postgresql_role" "role_with_defaults" {
412417
valid_until = "infinity"
413418
statement_timeout = 0
414419
idle_in_transaction_session_timeout = 0
420+
assume_role = ""
415421
}
416422
417423
resource "postgresql_role" "role_with_create_database" {

website/docs/r/postgresql_role.html.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ resource "postgresql_role" "my_replication_role" {
116116

117117
* `statement_timeout` - (Optional) Defines [`statement_timeout`](https://www.postgresql.org/docs/current/runtime-config-client.html#RUNTIME-CONFIG-CLIENT-STATEMENT) setting for this role which allows to abort any statement that takes more than the specified amount of time.
118118

119+
* `assume_role` - (Optional) Defines the role to switch to at login via [`SET ROLE`](https://www.postgresql.org/docs/current/sql-set-role.html).
120+
119121
## Import Example
120122

121123
`postgresql_role` supports importing resources. Supposing the following

0 commit comments

Comments
 (0)