| [62b2964] | 1 | 'use strict';
|
|---|
| 2 |
|
|---|
| 3 | var path = require('path')
|
|---|
| 4 | , Stream = require('stream').Stream
|
|---|
| 5 | , split = require('split2')
|
|---|
| 6 | , util = require('util')
|
|---|
| 7 | , defaultPort = 5432
|
|---|
| 8 | , isWin = (process.platform === 'win32')
|
|---|
| 9 | , warnStream = process.stderr
|
|---|
| 10 | ;
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 | var S_IRWXG = 56 // 00070(8)
|
|---|
| 14 | , S_IRWXO = 7 // 00007(8)
|
|---|
| 15 | , S_IFMT = 61440 // 00170000(8)
|
|---|
| 16 | , S_IFREG = 32768 // 0100000(8)
|
|---|
| 17 | ;
|
|---|
| 18 | function isRegFile(mode) {
|
|---|
| 19 | return ((mode & S_IFMT) == S_IFREG);
|
|---|
| 20 | }
|
|---|
| 21 |
|
|---|
| 22 | var fieldNames = [ 'host', 'port', 'database', 'user', 'password' ];
|
|---|
| 23 | var nrOfFields = fieldNames.length;
|
|---|
| 24 | var passKey = fieldNames[ nrOfFields -1 ];
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 | function warn() {
|
|---|
| 28 | var isWritable = (
|
|---|
| 29 | warnStream instanceof Stream &&
|
|---|
| 30 | true === warnStream.writable
|
|---|
| 31 | );
|
|---|
| 32 |
|
|---|
| 33 | if (isWritable) {
|
|---|
| 34 | var args = Array.prototype.slice.call(arguments).concat("\n");
|
|---|
| 35 | warnStream.write( util.format.apply(util, args) );
|
|---|
| 36 | }
|
|---|
| 37 | }
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 | Object.defineProperty(module.exports, 'isWin', {
|
|---|
| 41 | get : function() {
|
|---|
| 42 | return isWin;
|
|---|
| 43 | } ,
|
|---|
| 44 | set : function(val) {
|
|---|
| 45 | isWin = val;
|
|---|
| 46 | }
|
|---|
| 47 | });
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 | module.exports.warnTo = function(stream) {
|
|---|
| 51 | var old = warnStream;
|
|---|
| 52 | warnStream = stream;
|
|---|
| 53 | return old;
|
|---|
| 54 | };
|
|---|
| 55 |
|
|---|
| 56 | module.exports.getFileName = function(rawEnv){
|
|---|
| 57 | var env = rawEnv || process.env;
|
|---|
| 58 | var file = env.PGPASSFILE || (
|
|---|
| 59 | isWin ?
|
|---|
| 60 | path.join( env.APPDATA || './' , 'postgresql', 'pgpass.conf' ) :
|
|---|
| 61 | path.join( env.HOME || './', '.pgpass' )
|
|---|
| 62 | );
|
|---|
| 63 | return file;
|
|---|
| 64 | };
|
|---|
| 65 |
|
|---|
| 66 | module.exports.usePgPass = function(stats, fname) {
|
|---|
| 67 | if (Object.prototype.hasOwnProperty.call(process.env, 'PGPASSWORD')) {
|
|---|
| 68 | return false;
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | if (isWin) {
|
|---|
| 72 | return true;
|
|---|
| 73 | }
|
|---|
| 74 |
|
|---|
| 75 | fname = fname || '<unkn>';
|
|---|
| 76 |
|
|---|
| 77 | if (! isRegFile(stats.mode)) {
|
|---|
| 78 | warn('WARNING: password file "%s" is not a plain file', fname);
|
|---|
| 79 | return false;
|
|---|
| 80 | }
|
|---|
| 81 |
|
|---|
| 82 | if (stats.mode & (S_IRWXG | S_IRWXO)) {
|
|---|
| 83 | /* If password file is insecure, alert the user and ignore it. */
|
|---|
| 84 | warn('WARNING: password file "%s" has group or world access; permissions should be u=rw (0600) or less', fname);
|
|---|
| 85 | return false;
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 | return true;
|
|---|
| 89 | };
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 | var matcher = module.exports.match = function(connInfo, entry) {
|
|---|
| 93 | return fieldNames.slice(0, -1).reduce(function(prev, field, idx){
|
|---|
| 94 | if (idx == 1) {
|
|---|
| 95 | // the port
|
|---|
| 96 | if ( Number( connInfo[field] || defaultPort ) === Number( entry[field] ) ) {
|
|---|
| 97 | return prev && true;
|
|---|
| 98 | }
|
|---|
| 99 | }
|
|---|
| 100 | return prev && (
|
|---|
| 101 | entry[field] === '*' ||
|
|---|
| 102 | entry[field] === connInfo[field]
|
|---|
| 103 | );
|
|---|
| 104 | }, true);
|
|---|
| 105 | };
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 | module.exports.getPassword = function(connInfo, stream, cb) {
|
|---|
| 109 | var pass;
|
|---|
| 110 | var lineStream = stream.pipe(split());
|
|---|
| 111 |
|
|---|
| 112 | function onLine(line) {
|
|---|
| 113 | var entry = parseLine(line);
|
|---|
| 114 | if (entry && isValidEntry(entry) && matcher(connInfo, entry)) {
|
|---|
| 115 | pass = entry[passKey];
|
|---|
| 116 | lineStream.end(); // -> calls onEnd(), but pass is set now
|
|---|
| 117 | }
|
|---|
| 118 | }
|
|---|
| 119 |
|
|---|
| 120 | var onEnd = function() {
|
|---|
| 121 | stream.destroy();
|
|---|
| 122 | cb(pass);
|
|---|
| 123 | };
|
|---|
| 124 |
|
|---|
| 125 | var onErr = function(err) {
|
|---|
| 126 | stream.destroy();
|
|---|
| 127 | warn('WARNING: error on reading file: %s', err);
|
|---|
| 128 | cb(undefined);
|
|---|
| 129 | };
|
|---|
| 130 |
|
|---|
| 131 | stream.on('error', onErr);
|
|---|
| 132 | lineStream
|
|---|
| 133 | .on('data', onLine)
|
|---|
| 134 | .on('end', onEnd)
|
|---|
| 135 | .on('error', onErr)
|
|---|
| 136 | ;
|
|---|
| 137 |
|
|---|
| 138 | };
|
|---|
| 139 |
|
|---|
| 140 |
|
|---|
| 141 | var parseLine = module.exports.parseLine = function(line) {
|
|---|
| 142 | if (line.length < 11 || line.match(/^\s+#/)) {
|
|---|
| 143 | return null;
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | var curChar = '';
|
|---|
| 147 | var prevChar = '';
|
|---|
| 148 | var fieldIdx = 0;
|
|---|
| 149 | var startIdx = 0;
|
|---|
| 150 | var endIdx = 0;
|
|---|
| 151 | var obj = {};
|
|---|
| 152 | var isLastField = false;
|
|---|
| 153 | var addToObj = function(idx, i0, i1) {
|
|---|
| 154 | var field = line.substring(i0, i1);
|
|---|
| 155 |
|
|---|
| 156 | if (! Object.hasOwnProperty.call(process.env, 'PGPASS_NO_DEESCAPE')) {
|
|---|
| 157 | field = field.replace(/\\([:\\])/g, '$1');
|
|---|
| 158 | }
|
|---|
| 159 |
|
|---|
| 160 | obj[ fieldNames[idx] ] = field;
|
|---|
| 161 | };
|
|---|
| 162 |
|
|---|
| 163 | for (var i = 0 ; i < line.length-1 ; i += 1) {
|
|---|
| 164 | curChar = line.charAt(i+1);
|
|---|
| 165 | prevChar = line.charAt(i);
|
|---|
| 166 |
|
|---|
| 167 | isLastField = (fieldIdx == nrOfFields-1);
|
|---|
| 168 |
|
|---|
| 169 | if (isLastField) {
|
|---|
| 170 | addToObj(fieldIdx, startIdx);
|
|---|
| 171 | break;
|
|---|
| 172 | }
|
|---|
| 173 |
|
|---|
| 174 | if (i >= 0 && curChar == ':' && prevChar !== '\\') {
|
|---|
| 175 | addToObj(fieldIdx, startIdx, i+1);
|
|---|
| 176 |
|
|---|
| 177 | startIdx = i+2;
|
|---|
| 178 | fieldIdx += 1;
|
|---|
| 179 | }
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | obj = ( Object.keys(obj).length === nrOfFields ) ? obj : null;
|
|---|
| 183 |
|
|---|
| 184 | return obj;
|
|---|
| 185 | };
|
|---|
| 186 |
|
|---|
| 187 |
|
|---|
| 188 | var isValidEntry = module.exports.isValidEntry = function(entry){
|
|---|
| 189 | var rules = {
|
|---|
| 190 | // host
|
|---|
| 191 | 0 : function(x){
|
|---|
| 192 | return x.length > 0;
|
|---|
| 193 | } ,
|
|---|
| 194 | // port
|
|---|
| 195 | 1 : function(x){
|
|---|
| 196 | if (x === '*') {
|
|---|
| 197 | return true;
|
|---|
| 198 | }
|
|---|
| 199 | x = Number(x);
|
|---|
| 200 | return (
|
|---|
| 201 | isFinite(x) &&
|
|---|
| 202 | x > 0 &&
|
|---|
| 203 | x < 9007199254740992 &&
|
|---|
| 204 | Math.floor(x) === x
|
|---|
| 205 | );
|
|---|
| 206 | } ,
|
|---|
| 207 | // database
|
|---|
| 208 | 2 : function(x){
|
|---|
| 209 | return x.length > 0;
|
|---|
| 210 | } ,
|
|---|
| 211 | // username
|
|---|
| 212 | 3 : function(x){
|
|---|
| 213 | return x.length > 0;
|
|---|
| 214 | } ,
|
|---|
| 215 | // password
|
|---|
| 216 | 4 : function(x){
|
|---|
| 217 | return x.length > 0;
|
|---|
| 218 | }
|
|---|
| 219 | };
|
|---|
| 220 |
|
|---|
| 221 | for (var idx = 0 ; idx < fieldNames.length ; idx += 1) {
|
|---|
| 222 | var rule = rules[idx];
|
|---|
| 223 | var value = entry[ fieldNames[idx] ] || '';
|
|---|
| 224 |
|
|---|
| 225 | var res = rule(value);
|
|---|
| 226 | if (!res) {
|
|---|
| 227 | return false;
|
|---|
| 228 | }
|
|---|
| 229 | }
|
|---|
| 230 |
|
|---|
| 231 | return true;
|
|---|
| 232 | };
|
|---|
| 233 |
|
|---|