Skip to content

Commit 3d61320

Browse files
authored
Use and respect the passfile connection parameter (#1129)
* Use and respect the passfile connection parameter The postgres documentation[1] regarding the password file, states that: password file to use can be specified using the connection parameter passfile or the environment variable PGPASSFILE. The current implementation of lib/pq only respects the environment variable PGPASSFILE. This is not correct, but also limiting, as the PGPASSFILE is global and we might want to use different files for different clients in the same program. Fixing that is easy, by just checking the parameter passfile first, and if not, pull the value from PGPASSFILE. This also moves the parsing of PGPASSFILE to `parseEnviron`. Now the connection only checks the parameter passfile, that is populated by `parseEnviron`. [1] https://www.postgresql.org/docs/current/libpq-pgpass.html
1 parent 300ec9b commit 3d61320

File tree

3 files changed

+39
-10
lines changed

3 files changed

+39
-10
lines changed

conn.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ func (cn *conn) handlePgpass(o values) {
233233
if _, ok := o["password"]; ok {
234234
return
235235
}
236-
filename := os.Getenv("PGPASSFILE")
236+
// Get passfile from the options
237+
filename := o["passfile"]
237238
if filename == "" {
238239
// XXX this code doesn't work on Windows where the default filename is
239240
// XXX %APPDATA%\postgresql\pgpass.conf
@@ -2038,6 +2039,8 @@ func parseEnviron(env []string) (out map[string]string) {
20382039
accrue("user")
20392040
case "PGPASSWORD":
20402041
accrue("password")
2042+
case "PGPASSFILE":
2043+
accrue("passfile")
20412044
case "PGSERVICE", "PGSERVICEFILE", "PGREALM":
20422045
unsupported()
20432046
case "PGOPTIONS":

conn_test.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ func TestPgpass(t *testing.T) {
174174
}
175175
testAssert("", "ok", "missing .pgpass, unexpected error %#v")
176176
os.Setenv("PGPASSFILE", pgpassFile)
177+
defer os.Unsetenv("PGPASSFILE")
177178
testAssert("host=/tmp", "fail", ", unexpected error %#v")
179+
os.Unsetenv("PGPASSFILE")
180+
178181
os.Remove(pgpassFile)
179182
pgpass, err := os.OpenFile(pgpassFile, os.O_RDWR|os.O_CREATE, 0644)
180183
if err != nil {
@@ -189,6 +192,7 @@ localhost:*:*:*:pass_C
189192
if err != nil {
190193
t.Fatalf("Unexpected error writing pgpass file %#v", err)
191194
}
195+
defer os.Remove(pgpassFile)
192196
pgpass.Close()
193197

194198
assertPassword := func(extra values, expected string) {
@@ -211,19 +215,22 @@ localhost:*:*:*:pass_C
211215
t.Fatalf("For %v expected %s got %s", extra, expected, pw)
212216
}
213217
}
218+
// missing passfile means empty psasword
219+
assertPassword(values{"host": "server", "dbname": "some_db", "user": "some_user"}, "")
214220
// wrong permissions for the pgpass file means it should be ignored
215-
assertPassword(values{"host": "example.com", "user": "foo"}, "")
221+
assertPassword(values{"host": "example.com", "passfile": pgpassFile, "user": "foo"}, "")
216222
// fix the permissions and check if it has taken effect
217223
os.Chmod(pgpassFile, 0600)
218-
assertPassword(values{"host": "server", "dbname": "some_db", "user": "some_user"}, "pass_A")
219-
assertPassword(values{"host": "example.com", "user": "foo"}, "pass_fallback")
220-
assertPassword(values{"host": "example.com", "dbname": "some_db", "user": "some_user"}, "pass_B")
224+
225+
assertPassword(values{"host": "server", "passfile": pgpassFile, "dbname": "some_db", "user": "some_user"}, "pass_A")
226+
assertPassword(values{"host": "example.com", "passfile": pgpassFile, "user": "foo"}, "pass_fallback")
227+
assertPassword(values{"host": "example.com", "passfile": pgpassFile, "dbname": "some_db", "user": "some_user"}, "pass_B")
221228
// localhost also matches the default "" and UNIX sockets
222-
assertPassword(values{"host": "", "user": "some_user"}, "pass_C")
223-
assertPassword(values{"host": "/tmp", "user": "some_user"}, "pass_C")
224-
// cleanup
225-
os.Remove(pgpassFile)
226-
os.Setenv("PGPASSFILE", "")
229+
assertPassword(values{"host": "", "passfile": pgpassFile, "user": "some_user"}, "pass_C")
230+
assertPassword(values{"host": "/tmp", "passfile": pgpassFile, "user": "some_user"}, "pass_C")
231+
// passfile connection parameter takes precedence
232+
os.Setenv("PGPASSFILE", "/tmp")
233+
assertPassword(values{"host": "server", "passfile": pgpassFile, "dbname": "some_db", "user": "some_user"}, "pass_A")
227234
}
228235

229236
func TestExec(t *testing.T) {

connector_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"context"
88
"database/sql"
99
"database/sql/driver"
10+
"os"
1011
"testing"
1112
)
1213

@@ -66,3 +67,21 @@ func TestNewConnector_Driver(t *testing.T) {
6667
}
6768
txn.Rollback()
6869
}
70+
71+
func TestNewConnector_Environ(t *testing.T) {
72+
name := ""
73+
os.Setenv("PGPASSFILE", "/tmp/.pgpass")
74+
defer os.Unsetenv("PGPASSFILE")
75+
c, err := NewConnector(name)
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
for key, expected := range map[string]string{
80+
"passfile": "/tmp/.pgpass",
81+
} {
82+
if got := c.opts[key]; got != expected {
83+
t.Fatalf("Getting values from environment variables, for %v expected %s got %s", key, expected, got)
84+
}
85+
}
86+
87+
}

0 commit comments

Comments
 (0)