You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
5.4 KiB
234 lines
5.4 KiB
3 years ago
|
'use strict';
|
||
|
|
||
|
var path = require('path')
|
||
|
, Stream = require('stream').Stream
|
||
|
, split = require('split2')
|
||
|
, util = require('util')
|
||
|
, defaultPort = 5432
|
||
|
, isWin = (process.platform === 'win32')
|
||
|
, warnStream = process.stderr
|
||
|
;
|
||
|
|
||
|
|
||
|
var S_IRWXG = 56 // 00070(8)
|
||
|
, S_IRWXO = 7 // 00007(8)
|
||
|
, S_IFMT = 61440 // 00170000(8)
|
||
|
, S_IFREG = 32768 // 0100000(8)
|
||
|
;
|
||
|
function isRegFile(mode) {
|
||
|
return ((mode & S_IFMT) == S_IFREG);
|
||
|
}
|
||
|
|
||
|
var fieldNames = [ 'host', 'port', 'database', 'user', 'password' ];
|
||
|
var nrOfFields = fieldNames.length;
|
||
|
var passKey = fieldNames[ nrOfFields -1 ];
|
||
|
|
||
|
|
||
|
function warn() {
|
||
|
var isWritable = (
|
||
|
warnStream instanceof Stream &&
|
||
|
true === warnStream.writable
|
||
|
);
|
||
|
|
||
|
if (isWritable) {
|
||
|
var args = Array.prototype.slice.call(arguments).concat("\n");
|
||
|
warnStream.write( util.format.apply(util, args) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
Object.defineProperty(module.exports, 'isWin', {
|
||
|
get : function() {
|
||
|
return isWin;
|
||
|
} ,
|
||
|
set : function(val) {
|
||
|
isWin = val;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
module.exports.warnTo = function(stream) {
|
||
|
var old = warnStream;
|
||
|
warnStream = stream;
|
||
|
return old;
|
||
|
};
|
||
|
|
||
|
module.exports.getFileName = function(rawEnv){
|
||
|
var env = rawEnv || process.env;
|
||
|
var file = env.PGPASSFILE || (
|
||
|
isWin ?
|
||
|
path.join( env.APPDATA || './' , 'postgresql', 'pgpass.conf' ) :
|
||
|
path.join( env.HOME || './', '.pgpass' )
|
||
|
);
|
||
|
return file;
|
||
|
};
|
||
|
|
||
|
module.exports.usePgPass = function(stats, fname) {
|
||
|
if (Object.prototype.hasOwnProperty.call(process.env, 'PGPASSWORD')) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (isWin) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
fname = fname || '<unkn>';
|
||
|
|
||
|
if (! isRegFile(stats.mode)) {
|
||
|
warn('WARNING: password file "%s" is not a plain file', fname);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (stats.mode & (S_IRWXG | S_IRWXO)) {
|
||
|
/* If password file is insecure, alert the user and ignore it. */
|
||
|
warn('WARNING: password file "%s" has group or world access; permissions should be u=rw (0600) or less', fname);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
|
||
|
var matcher = module.exports.match = function(connInfo, entry) {
|
||
|
return fieldNames.slice(0, -1).reduce(function(prev, field, idx){
|
||
|
if (idx == 1) {
|
||
|
// the port
|
||
|
if ( Number( connInfo[field] || defaultPort ) === Number( entry[field] ) ) {
|
||
|
return prev && true;
|
||
|
}
|
||
|
}
|
||
|
return prev && (
|
||
|
entry[field] === '*' ||
|
||
|
entry[field] === connInfo[field]
|
||
|
);
|
||
|
}, true);
|
||
|
};
|
||
|
|
||
|
|
||
|
module.exports.getPassword = function(connInfo, stream, cb) {
|
||
|
var pass;
|
||
|
var lineStream = stream.pipe(split());
|
||
|
|
||
|
function onLine(line) {
|
||
|
var entry = parseLine(line);
|
||
|
if (entry && isValidEntry(entry) && matcher(connInfo, entry)) {
|
||
|
pass = entry[passKey];
|
||
|
lineStream.end(); // -> calls onEnd(), but pass is set now
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var onEnd = function() {
|
||
|
stream.destroy();
|
||
|
cb(pass);
|
||
|
};
|
||
|
|
||
|
var onErr = function(err) {
|
||
|
stream.destroy();
|
||
|
warn('WARNING: error on reading file: %s', err);
|
||
|
cb(undefined);
|
||
|
};
|
||
|
|
||
|
stream.on('error', onErr);
|
||
|
lineStream
|
||
|
.on('data', onLine)
|
||
|
.on('end', onEnd)
|
||
|
.on('error', onErr)
|
||
|
;
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
var parseLine = module.exports.parseLine = function(line) {
|
||
|
if (line.length < 11 || line.match(/^\s+#/)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var curChar = '';
|
||
|
var prevChar = '';
|
||
|
var fieldIdx = 0;
|
||
|
var startIdx = 0;
|
||
|
var endIdx = 0;
|
||
|
var obj = {};
|
||
|
var isLastField = false;
|
||
|
var addToObj = function(idx, i0, i1) {
|
||
|
var field = line.substring(i0, i1);
|
||
|
|
||
|
if (! Object.hasOwnProperty.call(process.env, 'PGPASS_NO_DEESCAPE')) {
|
||
|
field = field.replace(/\\([:\\])/g, '$1');
|
||
|
}
|
||
|
|
||
|
obj[ fieldNames[idx] ] = field;
|
||
|
};
|
||
|
|
||
|
for (var i = 0 ; i < line.length-1 ; i += 1) {
|
||
|
curChar = line.charAt(i+1);
|
||
|
prevChar = line.charAt(i);
|
||
|
|
||
|
isLastField = (fieldIdx == nrOfFields-1);
|
||
|
|
||
|
if (isLastField) {
|
||
|
addToObj(fieldIdx, startIdx);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (i >= 0 && curChar == ':' && prevChar !== '\\') {
|
||
|
addToObj(fieldIdx, startIdx, i+1);
|
||
|
|
||
|
startIdx = i+2;
|
||
|
fieldIdx += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
obj = ( Object.keys(obj).length === nrOfFields ) ? obj : null;
|
||
|
|
||
|
return obj;
|
||
|
};
|
||
|
|
||
|
|
||
|
var isValidEntry = module.exports.isValidEntry = function(entry){
|
||
|
var rules = {
|
||
|
// host
|
||
|
0 : function(x){
|
||
|
return x.length > 0;
|
||
|
} ,
|
||
|
// port
|
||
|
1 : function(x){
|
||
|
if (x === '*') {
|
||
|
return true;
|
||
|
}
|
||
|
x = Number(x);
|
||
|
return (
|
||
|
isFinite(x) &&
|
||
|
x > 0 &&
|
||
|
x < 9007199254740992 &&
|
||
|
Math.floor(x) === x
|
||
|
);
|
||
|
} ,
|
||
|
// database
|
||
|
2 : function(x){
|
||
|
return x.length > 0;
|
||
|
} ,
|
||
|
// username
|
||
|
3 : function(x){
|
||
|
return x.length > 0;
|
||
|
} ,
|
||
|
// password
|
||
|
4 : function(x){
|
||
|
return x.length > 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
for (var idx = 0 ; idx < fieldNames.length ; idx += 1) {
|
||
|
var rule = rules[idx];
|
||
|
var value = entry[ fieldNames[idx] ] || '';
|
||
|
|
||
|
var res = rule(value);
|
||
|
if (!res) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
};
|
||
|
|