diff --git a/postgresql/resource_postgresql_role.go b/postgresql/resource_postgresql_role.go index 5b346f3b..aff6bcdb 100644 --- a/postgresql/resource_postgresql_role.go +++ b/postgresql/resource_postgresql_role.go @@ -32,6 +32,7 @@ const ( roleSuperuserAttr = "superuser" roleValidUntilAttr = "valid_until" roleRolesAttr = "roles" + rolePgAuditLogAttr = "pgaudit_log" roleSearchPathAttr = "search_path" roleStatementTimeoutAttr = "statement_timeout" roleAssumeRoleAttr = "assume_role" @@ -173,6 +174,11 @@ func resourcePostgreSQLRole() *schema.Resource { Optional: true, Description: "Role to switch to at login", }, + rolePgAuditLogAttr: { + Type: schema.TypeString, + Optional: true, + Description: "Controls the behavior of the pgaudit extension by setting pgaudit.log. See https://github.com/pgaudit/pgaudit/blob/main/README.md#pgauditlog", + }, }, } } @@ -311,6 +317,10 @@ func resourcePostgreSQLRoleCreate(db *DBConnection, d *schema.ResourceData) erro return err } + if err = setPgAuditLog(txn, d); err != nil { + return err + } + if err = txn.Commit(); err != nil { return fmt.Errorf("could not commit transaction: %w", err) } @@ -457,6 +467,7 @@ func resourcePostgreSQLRoleReadImpl(db *DBConnection, d *schema.ResourceData) er d.Set(roleRolesAttr, pgArrayToSet(roleRoles)) d.Set(roleSearchPathAttr, readSearchPath(roleConfig)) d.Set(roleAssumeRoleAttr, readAssumeRole(roleConfig)) + d.Set(rolePgAuditLogAttr, readPgAuditLog(roleConfig)) statementTimeout, err := readStatementTimeout(roleConfig) if err != nil { @@ -547,6 +558,19 @@ func readAssumeRole(roleConfig pq.ByteaArray) string { return res } +// readPgAuditLog searches for a pgaudit.log entry in the rolconfig array. +// In case no such value is present, it returns empty string. +func readPgAuditLog(roleConfig pq.ByteaArray) string { + var pgAuditLogAttr = "pgaudit.log" + for _, v := range roleConfig { + config := string(v) + if strings.HasPrefix(config, pgAuditLogAttr) { + return strings.TrimPrefix(config, pgAuditLogAttr+"=") + } + } + return "" +} + // readRolePassword reads password either from Postgres if admin user is a superuser // or only from Terraform state. func readRolePassword(db *DBConnection, d *schema.ResourceData, roleCanLogin bool) (string, error) { @@ -689,6 +713,10 @@ func resourcePostgreSQLRoleUpdate(db *DBConnection, d *schema.ResourceData) erro return err } + if err = setPgAuditLog(txn, d); err != nil { + return err + } + if err = txn.Commit(); err != nil { return fmt.Errorf("could not commit transaction: %w", err) } @@ -1062,3 +1090,28 @@ func setAssumeRole(txn *sql.Tx, d *schema.ResourceData) error { } return nil } + +func setPgAuditLog(txn *sql.Tx, d *schema.ResourceData) error { + if !d.HasChange(rolePgAuditLogAttr) { + return nil + } + + roleName := d.Get(roleNameAttr).(string) + pgAuditLog := d.Get(rolePgAuditLogAttr).(string) + if pgAuditLog != "" { + sql := fmt.Sprintf( + "ALTER ROLE %s SET pgaudit.log TO '%s'", pq.QuoteIdentifier(roleName), pqQuoteLiteral(pgAuditLog), + ) + if _, err := txn.Exec(sql); err != nil { + return fmt.Errorf("could not set pgaudit.log %s for %s: %w", pgAuditLog, roleName, err) + } + } else { + sql := fmt.Sprintf( + "ALTER ROLE %s RESET pgaudit.log", pq.QuoteIdentifier(roleName), + ) + if _, err := txn.Exec(sql); err != nil { + return fmt.Errorf("could not reset pgaudit.log for %s: %w", roleName, err) + } + } + return nil +} diff --git a/postgresql/resource_postgresql_role_test.go b/postgresql/resource_postgresql_role_test.go index fc958840..b9fa96e6 100644 --- a/postgresql/resource_postgresql_role_test.go +++ b/postgresql/resource_postgresql_role_test.go @@ -58,6 +58,9 @@ func TestAccPostgresqlRole_Basic(t *testing.T) { resource.TestCheckResourceAttr("postgresql_role.sub_role", "roles.1", "role_simple"), testAccCheckPostgresqlRoleExists("role_with_search_path", nil, []string{"bar", "foo-with-hyphen"}), + + testAccCheckPostgresqlRoleExists("role_with_pgaudit_log", nil, nil), + resource.TestCheckResourceAttr("postgresql_role.role_with_pgaudit_log", "pgaudit_log", "all"), ), }, }, @@ -122,6 +125,7 @@ resource "postgresql_role" "update_role" { statement_timeout = 30000 idle_in_transaction_session_timeout = 60000 assume_role = "${postgresql_role.group_role.name}" + pgaudit_log = "all, -read" } ` resource.Test(t, resource.TestCase{ @@ -146,6 +150,7 @@ resource "postgresql_role" "update_role" { resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""), + resource.TestCheckResourceAttr("postgresql_role.update_role", "pgaudit_log", ""), testAccCheckRoleCanLogin(t, "update_role", "toto"), ), }, @@ -167,6 +172,7 @@ resource "postgresql_role" "update_role" { resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "30000"), resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "60000"), resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", "group_role"), + resource.TestCheckResourceAttr("postgresql_role.update_role", "pgaudit_log", "all, -read"), testAccCheckRoleCanLogin(t, "update_role2", "titi"), ), }, @@ -185,6 +191,7 @@ resource "postgresql_role" "update_role" { resource.TestCheckResourceAttr("postgresql_role.update_role", "statement_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "idle_in_transaction_session_timeout", "0"), resource.TestCheckResourceAttr("postgresql_role.update_role", "assume_role", ""), + resource.TestCheckResourceAttr("postgresql_role.update_role", "pgaudit_log", ""), testAccCheckRoleCanLogin(t, "update_role", "toto"), ), }, @@ -446,4 +453,9 @@ resource "postgresql_role" "role_with_search_path" { name = "role_with_search_path" search_path = ["bar", "foo-with-hyphen"] } + +resource "postgresql_role" "role_with_pgaudit_log" { + name = "role_with_pgaudit_log" + pgaudit_log = "all" +} ` diff --git a/website/docs/r/postgresql_role.html.markdown b/website/docs/r/postgresql_role.html.markdown index 2c9431be..10150b9f 100644 --- a/website/docs/r/postgresql_role.html.markdown +++ b/website/docs/r/postgresql_role.html.markdown @@ -118,6 +118,8 @@ resource "postgresql_role" "my_replication_role" { * `assume_role` - (Optional) Defines the role to switch to at login via [`SET ROLE`](https://www.postgresql.org/docs/current/sql-set-role.html). +* `pgaudit_log` - (Optional) Controls the behavior of the [`pgaudit`](https://github.com/pgaudit/pgaudit) extension by setting the [`pgaudit.log`](https://github.com/pgaudit/pgaudit/blob/master/README.md#pgauditlog) parameter. + ## Import Example `postgresql_role` supports importing resources. Supposing the following