Fix bug preventing multiline statemtents in rotation_statements for database static roles (#31442)

* added multiline unit test to replicate error & eventual fix

* create changelog

* move multiline statement test above the bad statements test for consistency.

* Add support for multiline statements in changeUserPassword

* Update expiration multi-line statements

* pr fixes
This commit is contained in:
Jaired Jawed 2025-08-11 16:48:47 -05:00 committed by GitHub
parent c88b3136a6
commit a0f8dab6a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 60 additions and 0 deletions

3
changelog/31442.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
secrets/database/postgresql: Support for multiline statements in the `rotation_statements` field.
```

View File

@ -270,6 +270,28 @@ func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, ch
defer tx.Rollback() defer tx.Rollback()
for _, stmt := range stmts { for _, stmt := range stmts {
if containsMultilineStatement(stmt) {
// Execute it as-is.
m := map[string]string{
"name": username,
"username": username,
"password": password,
}
if p.passwordAuthentication == passwordAuthenticationSCRAMSHA256 {
hashedPassword, err := scram.Hash(password)
if err != nil {
return fmt.Errorf("unable to scram-sha256 password: %w", err)
}
m["password"] = hashedPassword
}
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, stmt); err != nil {
return err
}
continue
}
// Otherwise, it's fine to split the statements on the semicolon.
for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") { for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") {
query = strings.TrimSpace(query) query = strings.TrimSpace(query)
if len(query) == 0 { if len(query) == 0 {
@ -337,6 +359,19 @@ func (p *PostgreSQL) changeUserExpiration(ctx context.Context, username string,
expirationStr := changeExp.NewExpiration.Format(expirationFormat) expirationStr := changeExp.NewExpiration.Format(expirationFormat)
for _, stmt := range renewStmts { for _, stmt := range renewStmts {
if containsMultilineStatement(stmt) {
// Execute it as-is.
m := map[string]string{
"name": username,
"username": username,
"expiration": expirationStr,
}
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, stmt); err != nil {
return err
}
continue
}
// Otherwise, it's fine to split the statements on the semicolon.
for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") { for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") {
query = strings.TrimSpace(query) query = strings.TrimSpace(query)
if len(query) == 0 { if len(query) == 0 {

View File

@ -1063,6 +1063,17 @@ func TestUpdateUser_Password(t *testing.T) {
expectErr: false, expectErr: false,
credsAssertion: assertCredsExist, credsAssertion: assertCredsExist,
}, },
"multi-line statements": {
statements: []string{
`DO $$ BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname='{{name}}')
THEN CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}';
ELSE ALTER ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}';
END IF; END $$`,
},
expectErr: false,
credsAssertion: assertCredsExist,
},
"bad statements": { "bad statements": {
statements: []string{`asdofyas8uf77asoiajv`}, statements: []string{`asdofyas8uf77asoiajv`},
expectErr: true, expectErr: true,
@ -1205,6 +1216,17 @@ func TestUpdateUser_Expiration(t *testing.T) {
statements: []string{`ALTER ROLE "{{username}}" VALID UNTIL '{{expiration}}';`}, statements: []string{`ALTER ROLE "{{username}}" VALID UNTIL '{{expiration}}';`},
expectErr: false, expectErr: false,
}, },
"multi-line statements": {
initialExpiration: now.Add(1 * time.Minute),
newExpiration: now.Add(5 * time.Minute),
expectedExpiration: now.Add(5 * time.Minute),
statements: []string{
`DO $$ BEGIN
ALTER ROLE "{{name}}" VALID UNTIL '{{expiration}}';
END $$`,
},
expectErr: false,
},
"bad statements": { "bad statements": {
initialExpiration: now.Add(1 * time.Minute), initialExpiration: now.Add(1 * time.Minute),
newExpiration: now.Add(5 * time.Minute), newExpiration: now.Add(5 * time.Minute),