diff --git a/lib/client.js b/lib/client.js index b924fdbd..dc8edc74 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,6 +1,7 @@ var crypto = require('crypto'); var EventEmitter = require('events').EventEmitter; var util = require('util'); +var pgPass = require('pgpass'); var ConnectionParameters = require(__dirname + '/connection-parameters'); var Query = require(__dirname + '/query'); @@ -38,6 +39,7 @@ util.inherits(Client, EventEmitter); Client.prototype.connect = function(callback) { var self = this; var con = this.connection; + if(this.host && this.host.indexOf('/') === 0) { con.connect(this.host + '/.s.PGSQL.' + this.port); } else { @@ -64,18 +66,33 @@ Client.prototype.connect = function(callback) { }); }); - //password request handling - con.on('authenticationCleartextPassword', function() { - con.password(self.password); - }); + function checkPgPass(cb) { + return function(msg) { + if (null !== self.password) { + cb(msg); + } else { + pgPass(self.connectionParameters, function(pass){ + if (undefined !== pass) { + self.connectionParameters.password = self.password = pass; + } + cb(msg); + }); + } + }; + } //password request handling - con.on('authenticationMD5Password', function(msg) { + con.on('authenticationCleartextPassword', checkPgPass(function() { + con.password(self.password); + })); + + //password request handling + con.on('authenticationMD5Password', checkPgPass(function(msg) { var inner = Client.md5(self.password + self.user); var outer = Client.md5(inner + msg.salt.toString('binary')); var md5password = "md5" + outer; con.password(md5password); - }); + })); con.once('backendKeyData', function(msg) { self.processID = msg.processID; diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index e69fc5ba..1c3b0c30 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -85,8 +85,10 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { if(this.database) { params.push("dbname='" + this.database + "'"); } - if(this.isDomainSocket) { + if(this.host) { params.push("host=" + this.host); + } + if(this.isDomainSocket) { return cb(null, params.join(' ')); } if(this.client_encoding) { diff --git a/package.json b/package.json index eb69d96e..c18a8f3c 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "generic-pool": "2.0.3", "buffer-writer": "1.0.0", + "pgpass": "0.0.1", "nan": "~0.6.0" }, "devDependencies": { diff --git a/test/integration/client/heroku-pgpass-tests.js b/test/integration/client/heroku-pgpass-tests.js new file mode 100644 index 00000000..c0379233 --- /dev/null +++ b/test/integration/client/heroku-pgpass-tests.js @@ -0,0 +1,36 @@ +var helper = require(__dirname + '/../test-helper'); + +// Path to the password file +var passfile = __dirname + '/heroku.pgpass'; + +// Export the path to the password file +process.env.PGPASSFILE = passfile; + +// Do a chmod 660, because git doesn't track those permissions +require('fs').chmodSync(passfile, 384); + +var pg = helper.pg; + +var host = 'ec2-107-20-224-218.compute-1.amazonaws.com'; +var database = 'db6kfntl5qhp2'; +var user = 'kwdzdnqpdiilfs'; + +var config = { + host: host, + database: database, + user: user, + ssl: true +}; + +// connect & disconnect from heroku +pg.connect(config, assert.success(function(client, done) { + client.query('SELECT NOW() as time', assert.success(function(res) { + assert(res.rows[0].time.getTime()); + + // cleanup ... remove the env variable + delete process.env.PGPASSFILE; + + done(); + pg.end(); + })) +})); diff --git a/test/integration/client/heroku.pgpass b/test/integration/client/heroku.pgpass new file mode 100644 index 00000000..39bba52e --- /dev/null +++ b/test/integration/client/heroku.pgpass @@ -0,0 +1 @@ +ec2-107-20-224-218.compute-1.amazonaws.com:5432:db6kfntl5qhp2:kwdzdnqpdiilfs:uaZoSSHgi7mVM7kYaROtusClKu