FIX (backups): Escape password over connection check to allow whitespaces

This commit is contained in:
Rostislav Dugin
2026-01-04 20:49:22 +03:00
parent 722560e824
commit 52c0f53608
4 changed files with 103 additions and 4 deletions

View File

@@ -695,16 +695,22 @@ func buildConnectionStringForDB(p *PostgresqlDatabase, dbName string, password s
}
return fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s default_query_exec_mode=simple_protocol standard_conforming_strings=on client_encoding=UTF8",
"host=%s port=%d user=%s password='%s' dbname=%s sslmode=%s default_query_exec_mode=simple_protocol standard_conforming_strings=on client_encoding=UTF8",
p.Host,
p.Port,
p.Username,
password,
escapeConnectionStringValue(password),
dbName,
sslMode,
)
}
func escapeConnectionStringValue(value string) string {
value = strings.ReplaceAll(value, `\`, `\\`)
value = strings.ReplaceAll(value, `'`, `\'`)
return value
}
func decryptPasswordIfNeeded(
password string,
encryptor encryption.FieldEncryptor,

View File

@@ -0,0 +1,94 @@
package postgresql
import (
"fmt"
"log/slog"
"os"
"strconv"
"testing"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"github.com/stretchr/testify/assert"
"databasus-backend/internal/config"
"databasus-backend/internal/util/tools"
)
func Test_TestConnection_PasswordContainingSpaces_TestedSuccessfully(t *testing.T) {
env := config.GetEnv()
container := connectToTestPostgresContainer(t, env.TestPostgres16Port)
defer container.DB.Close()
passwordWithSpaces := "test password with spaces"
usernameWithSpaces := fmt.Sprintf("testuser_spaces_%s", uuid.New().String()[:8])
_, err := container.DB.Exec(fmt.Sprintf(
`CREATE USER "%s" WITH PASSWORD '%s' LOGIN`,
usernameWithSpaces,
passwordWithSpaces,
))
assert.NoError(t, err)
_, err = container.DB.Exec(fmt.Sprintf(
`GRANT CONNECT ON DATABASE "%s" TO "%s"`,
container.Database,
usernameWithSpaces,
))
assert.NoError(t, err)
defer func() {
_, _ = container.DB.Exec(fmt.Sprintf(`DROP USER IF EXISTS "%s"`, usernameWithSpaces))
}()
pgModel := &PostgresqlDatabase{
Version: tools.GetPostgresqlVersionEnum("16"),
Host: container.Host,
Port: container.Port,
Username: usernameWithSpaces,
Password: passwordWithSpaces,
Database: &container.Database,
IsHttps: false,
CpuCount: 1,
}
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
err = pgModel.TestConnection(logger, nil, uuid.New())
assert.NoError(t, err)
}
type testPostgresContainer struct {
Host string
Port int
Username string
Password string
Database string
DB *sqlx.DB
}
func connectToTestPostgresContainer(t *testing.T, port string) *testPostgresContainer {
dbName := "testdb"
password := "testpassword"
username := "testuser"
host := "localhost"
portInt, err := strconv.Atoi(port)
assert.NoError(t, err)
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
host, portInt, username, password, dbName)
db, err := sqlx.Connect("postgres", dsn)
assert.NoError(t, err)
return &testPostgresContainer{
Host: host,
Port: portInt,
Username: username,
Password: password,
Database: dbName,
DB: db,
}
}

View File

@@ -131,7 +131,6 @@ func (uc *RestorePostgresqlBackupUsecase) restoreViaStdin(
"--if-exists",
"--no-owner",
"--no-acl",
"--no-comments",
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)

View File

@@ -163,7 +163,7 @@ export const RestoresComponent = ({ database, backup }: Props) => {
loading={isRestoreInProgress}
onClick={() => setIsShowRestore(true)}
>
Restore from backup
Select database to restore to
</Button>
{restores.length === 0 && (