feat: Add support for loading db password from file via PASSD_URI (#10421)

Adds a new PASSWD_URI ini setting similar to all the other secrets

Fixes #9365
Fixes #6530

Co-authored-by: Daniel Tschinder <231804+danez@users.noreply.github.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10421
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: danez <danez@noreply.codeberg.org>
Co-committed-by: danez <danez@noreply.codeberg.org>
This commit is contained in:
danez 2025-12-18 20:55:56 +01:00 committed by Gusted
commit 001ebb8e39
3 changed files with 57 additions and 1 deletions

View file

@ -373,6 +373,8 @@ DB_TYPE = sqlite3
;NAME = gitea
;USER = root
;PASSWD = ;Use PASSWD = `your password` for quoting if you use special characters in the password.
;; Alternative location to specify mysql password. You cannot specify both this and PASSWD, and must pick one
;PASSWD_URI = file:/etc/forgejo/db_passwd
;SSL_MODE = false ; either "false" (default), "true", or "skip-verify"
;CHARSET_COLLATION = ; Empty as default, Forgejo will try to find a case-sensitive collation. Don't change it unless you clearly know what you need.
;;
@ -385,6 +387,8 @@ DB_TYPE = sqlite3
;NAME = gitea
;USER = root
;PASSWD =
;; Alternative location to specify postgres password. You cannot specify both this and PASSWD, and must pick one
;PASSWD_URI = file:/etc/forgejo/db_passwd
;SCHEMA =
;SSL_MODE=disable ;either "disable" (default), "require", or "verify-full"
;;

View file

@ -79,7 +79,7 @@ func loadDBSetting(rootCfg ConfigProvider) {
Database.Name = sec.Key("NAME").String()
Database.User = sec.Key("USER").String()
if len(Database.Passwd) == 0 {
Database.Passwd = sec.Key("PASSWD").String()
Database.Passwd = loadSecret(sec, "PASSWD_URI", "PASSWD")
}
Database.Schema = sec.Key("SCHEMA").String()
Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")

View file

@ -4,10 +4,16 @@
package setting
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_parsePostgreSQLHostPort(t *testing.T) {
@ -253,3 +259,49 @@ func Test_getPostgreSQLEngineGroupConnectionStrings(t *testing.T) {
assert.Equal(t, test.outputReplicas, replicas)
}
}
func Test_loadDBSetting(t *testing.T) {
defer test.MockProtect(&Database)()
t.Run("Does not overwrite Passwd", func(t *testing.T) {
expectedPassword := "already_set"
cfg, _ := NewConfigProviderFromData(`
[database]
PASSWD="new password"
`)
Database.Passwd = expectedPassword
loadDBSetting(cfg)
assert.Equal(t, expectedPassword, Database.Passwd)
})
t.Run("uses PASSWD", func(t *testing.T) {
expectedPassword := "testpassword"
cfg, _ := NewConfigProviderFromData(fmt.Sprintf(`
[database]
PASSWD="%s"
`, expectedPassword))
Database.Passwd = ""
loadDBSetting(cfg)
assert.Equal(t, expectedPassword, Database.Passwd)
})
t.Run("Uses PASSWD_URI", func(t *testing.T) {
expectedPassword := "testpassworduri"
uri := filepath.Join(t.TempDir(), "db_passwd")
require.NoError(t, os.WriteFile(uri, []byte(expectedPassword), 0o644))
cfg, _ := NewConfigProviderFromData(fmt.Sprintf(`
[database]
PASSWD_URI="file:%s"
`, uri))
Database.Passwd = ""
loadDBSetting(cfg)
assert.Equal(t, expectedPassword, Database.Passwd)
})
}