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.
233 lines
5.4 KiB
233 lines
5.4 KiB
'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; |
|
}; |
|
|
|
|