pg 7.7.1 → 7.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/SPONSORS.md +3 -0
- package/lib/client.js +24 -1
- package/lib/connection.js +63 -16
- package/lib/native/query.js +5 -1
- package/lib/query.js +4 -0
- package/lib/sasl.js +146 -0
- package/package.json +3 -3
- package/.travis.yml +0 -41
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ For richer information consult the commit log on github with referenced pull req
|
|
|
4
4
|
|
|
5
5
|
We do not include break-fix version release in this file.
|
|
6
6
|
|
|
7
|
+
### 7.9.0
|
|
8
|
+
|
|
9
|
+
- Add support for [sasl/scram authentication](https://github.com/brianc/node-postgres/pull/1835).
|
|
10
|
+
|
|
11
|
+
### 7.8.0
|
|
12
|
+
|
|
13
|
+
- Add support for passing [secureOptions](https://github.com/brianc/node-postgres/pull/1804) SSL config.
|
|
14
|
+
- Upgrade [pg-types](https://github.com/brianc/node-postgres/pull/1806) to 2.0.
|
|
15
|
+
|
|
7
16
|
### 7.7.0
|
|
8
17
|
|
|
9
18
|
- Add support for configurable [query timeout](https://github.com/brianc/node-postgres/pull/1760) on a client level.
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ The causes and solutions to common errors can be found among the [Frequently Ask
|
|
|
67
67
|
|
|
68
68
|
## License
|
|
69
69
|
|
|
70
|
-
Copyright (c) 2010-
|
|
70
|
+
Copyright (c) 2010-2019 Brian Carlson (brian.m.carlson@gmail.com)
|
|
71
71
|
|
|
72
72
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
73
73
|
of this software and associated documentation files (the "Software"), to deal
|
package/SPONSORS.md
CHANGED
|
@@ -2,6 +2,7 @@ node-postgres is made possible by the helpful contributors from the community we
|
|
|
2
2
|
|
|
3
3
|
# Leaders
|
|
4
4
|
- [MadKudu](https://www.madkudu.com) - [@madkudu](https://twitter.com/madkudu)
|
|
5
|
+
- [Third Iron](https://thirdiron.com/)
|
|
5
6
|
|
|
6
7
|
# Supporters
|
|
7
8
|
- John Fawcett
|
|
@@ -17,3 +18,5 @@ node-postgres is made possible by the helpful contributors from the community we
|
|
|
17
18
|
- Benjie Gillam
|
|
18
19
|
- David Hanson
|
|
19
20
|
- Franklin Davenport
|
|
21
|
+
- [Eventbot](https://geteventbot.com/)
|
|
22
|
+
- Chuck T
|
package/lib/client.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
var EventEmitter = require('events').EventEmitter
|
|
11
11
|
var util = require('util')
|
|
12
12
|
var utils = require('./utils')
|
|
13
|
+
var sasl = require('./sasl')
|
|
13
14
|
var pgPass = require('pgpass')
|
|
14
15
|
var TypeOverrides = require('./type-overrides')
|
|
15
16
|
|
|
@@ -126,6 +127,28 @@ Client.prototype._connect = function (callback) {
|
|
|
126
127
|
con.password(utils.postgresMd5PasswordHash(self.user, self.password, msg.salt))
|
|
127
128
|
}))
|
|
128
129
|
|
|
130
|
+
// password request handling (SASL)
|
|
131
|
+
var saslSession
|
|
132
|
+
con.on('authenticationSASL', checkPgPass(function (msg) {
|
|
133
|
+
saslSession = sasl.startSession(msg.mechanisms)
|
|
134
|
+
|
|
135
|
+
con.sendSASLInitialResponseMessage(saslSession.mechanism, saslSession.response)
|
|
136
|
+
}))
|
|
137
|
+
|
|
138
|
+
// password request handling (SASL)
|
|
139
|
+
con.on('authenticationSASLContinue', function (msg) {
|
|
140
|
+
sasl.continueSession(saslSession, self.password, msg.data)
|
|
141
|
+
|
|
142
|
+
con.sendSCRAMClientFinalMessage(saslSession.response)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// password request handling (SASL)
|
|
146
|
+
con.on('authenticationSASLFinal', function (msg) {
|
|
147
|
+
sasl.finalizeSession(saslSession, msg.data)
|
|
148
|
+
|
|
149
|
+
saslSession = null
|
|
150
|
+
})
|
|
151
|
+
|
|
129
152
|
con.once('backendKeyData', function (msg) {
|
|
130
153
|
self.processID = msg.processID
|
|
131
154
|
self.secretKey = msg.secretKey
|
|
@@ -276,7 +299,7 @@ Client.prototype._attachListeners = function (con) {
|
|
|
276
299
|
// it again on the same client
|
|
277
300
|
con.on('parseComplete', function (msg) {
|
|
278
301
|
if (self.activeQuery.name) {
|
|
279
|
-
con.parsedStatements[self.activeQuery.name] =
|
|
302
|
+
con.parsedStatements[self.activeQuery.name] = self.activeQuery.text
|
|
280
303
|
}
|
|
281
304
|
})
|
|
282
305
|
|
package/lib/connection.js
CHANGED
|
@@ -101,6 +101,7 @@ Connection.prototype.connect = function (port, host) {
|
|
|
101
101
|
key: self.ssl.key,
|
|
102
102
|
passphrase: self.ssl.passphrase,
|
|
103
103
|
cert: self.ssl.cert,
|
|
104
|
+
secureOptions: self.ssl.secureOptions,
|
|
104
105
|
NPNProtocols: self.ssl.NPNProtocols
|
|
105
106
|
})
|
|
106
107
|
self.attachListeners(self.stream)
|
|
@@ -190,6 +191,24 @@ Connection.prototype.password = function (password) {
|
|
|
190
191
|
this._send(0x70, this.writer.addCString(password))
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
Connection.prototype.sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
|
|
195
|
+
// 0x70 = 'p'
|
|
196
|
+
this.writer
|
|
197
|
+
.addCString(mechanism)
|
|
198
|
+
.addInt32(Buffer.byteLength(initialResponse))
|
|
199
|
+
.addString(initialResponse)
|
|
200
|
+
|
|
201
|
+
this._send(0x70)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
Connection.prototype.sendSCRAMClientFinalMessage = function (additionalData) {
|
|
205
|
+
// 0x70 = 'p'
|
|
206
|
+
this.writer
|
|
207
|
+
.addString(additionalData)
|
|
208
|
+
|
|
209
|
+
this._send(0x70)
|
|
210
|
+
}
|
|
211
|
+
|
|
193
212
|
Connection.prototype._send = function (code, more) {
|
|
194
213
|
if (!this.stream.writable) {
|
|
195
214
|
return false
|
|
@@ -420,25 +439,53 @@ Connection.prototype.parseMessage = function (buffer) {
|
|
|
420
439
|
}
|
|
421
440
|
|
|
422
441
|
Connection.prototype.parseR = function (buffer, length) {
|
|
423
|
-
var code =
|
|
442
|
+
var code = this.parseInt32(buffer)
|
|
443
|
+
|
|
424
444
|
var msg = new Message('authenticationOk', length)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
msg
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
msg.
|
|
437
|
-
|
|
438
|
-
|
|
445
|
+
|
|
446
|
+
switch (code) {
|
|
447
|
+
case 0: // AuthenticationOk
|
|
448
|
+
return msg
|
|
449
|
+
case 3: // AuthenticationCleartextPassword
|
|
450
|
+
if (msg.length === 8) {
|
|
451
|
+
msg.name = 'authenticationCleartextPassword'
|
|
452
|
+
return msg
|
|
453
|
+
}
|
|
454
|
+
break
|
|
455
|
+
case 5: // AuthenticationMD5Password
|
|
456
|
+
if (msg.length === 12) {
|
|
457
|
+
msg.name = 'authenticationMD5Password'
|
|
458
|
+
msg.salt = Buffer.alloc(4)
|
|
459
|
+
buffer.copy(msg.salt, 0, this.offset, this.offset + 4)
|
|
460
|
+
this.offset += 4
|
|
461
|
+
return msg
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
break
|
|
465
|
+
case 10: // AuthenticationSASL
|
|
466
|
+
msg.name = 'authenticationSASL'
|
|
467
|
+
msg.mechanisms = []
|
|
468
|
+
do {
|
|
469
|
+
var mechanism = this.parseCString(buffer)
|
|
470
|
+
|
|
471
|
+
if (mechanism) {
|
|
472
|
+
msg.mechanisms.push(mechanism)
|
|
473
|
+
}
|
|
474
|
+
} while (mechanism)
|
|
475
|
+
|
|
476
|
+
return msg
|
|
477
|
+
case 11: // AuthenticationSASLContinue
|
|
478
|
+
msg.name = 'authenticationSASLContinue'
|
|
479
|
+
msg.data = this.readString(buffer, length - 4)
|
|
480
|
+
|
|
481
|
+
return msg
|
|
482
|
+
case 12: // AuthenticationSASLFinal
|
|
483
|
+
msg.name = 'authenticationSASLFinal'
|
|
484
|
+
msg.data = this.readString(buffer, length - 4)
|
|
485
|
+
|
|
439
486
|
return msg
|
|
440
|
-
}
|
|
441
487
|
}
|
|
488
|
+
|
|
442
489
|
throw new Error('Unknown authenticationOk message type' + util.inspect(msg))
|
|
443
490
|
}
|
|
444
491
|
|
package/lib/native/query.js
CHANGED
|
@@ -139,12 +139,16 @@ NativeQuery.prototype.submit = function (client) {
|
|
|
139
139
|
// check if the client has already executed this named query
|
|
140
140
|
// if so...just execute it again - skip the planning phase
|
|
141
141
|
if (client.namedQueries[this.name]) {
|
|
142
|
+
if (this.text && client.namedQueries[this.name] !== this.text) {
|
|
143
|
+
const err = new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`)
|
|
144
|
+
return after(err)
|
|
145
|
+
}
|
|
142
146
|
return client.native.execute(this.name, values, after)
|
|
143
147
|
}
|
|
144
148
|
// plan the named query the first time, then execute it
|
|
145
149
|
return client.native.prepare(this.name, this.text, values.length, function (err) {
|
|
146
150
|
if (err) return after(err)
|
|
147
|
-
client.namedQueries[self.name] =
|
|
151
|
+
client.namedQueries[self.name] = self.text
|
|
148
152
|
return self.native.execute(self.name, values, after)
|
|
149
153
|
})
|
|
150
154
|
} else if (this.values) {
|
package/lib/query.js
CHANGED
|
@@ -148,6 +148,10 @@ Query.prototype.submit = function (connection) {
|
|
|
148
148
|
if (typeof this.text !== 'string' && typeof this.name !== 'string') {
|
|
149
149
|
return new Error('A query must have either text or a name. Supplying neither is unsupported.')
|
|
150
150
|
}
|
|
151
|
+
const previous = connection.parsedStatements[this.name]
|
|
152
|
+
if (this.text && previous && this.text !== previous) {
|
|
153
|
+
return new Error(`Prepared statements must be unique - '${this.name}' was used for a different statement`)
|
|
154
|
+
}
|
|
151
155
|
if (this.values && !Array.isArray(this.values)) {
|
|
152
156
|
return new Error('Query values must be an array')
|
|
153
157
|
}
|
package/lib/sasl.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const crypto = require('crypto')
|
|
2
|
+
|
|
3
|
+
function startSession (mechanisms) {
|
|
4
|
+
if (mechanisms.indexOf('SCRAM-SHA-256') === -1) {
|
|
5
|
+
throw new Error('SASL: Only mechanism SCRAM-SHA-256 is currently supported')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const clientNonce = crypto.randomBytes(18).toString('base64')
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
mechanism: 'SCRAM-SHA-256',
|
|
12
|
+
clientNonce,
|
|
13
|
+
response: 'n,,n=*,r=' + clientNonce,
|
|
14
|
+
message: 'SASLInitialResponse'
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function continueSession (session, password, serverData) {
|
|
19
|
+
if (session.message !== 'SASLInitialResponse') {
|
|
20
|
+
throw new Error('SASL: Last message was not SASLInitialResponse')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const sv = extractVariablesFromFirstServerMessage(serverData)
|
|
24
|
+
|
|
25
|
+
if (!sv.nonce.startsWith(session.clientNonce)) {
|
|
26
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var saltBytes = Buffer.from(sv.salt, 'base64')
|
|
30
|
+
|
|
31
|
+
var saltedPassword = Hi(password, saltBytes, sv.iteration)
|
|
32
|
+
|
|
33
|
+
var clientKey = createHMAC(saltedPassword, 'Client Key')
|
|
34
|
+
var storedKey = crypto.createHash('sha256').update(clientKey).digest()
|
|
35
|
+
|
|
36
|
+
var clientFirstMessageBare = 'n=*,r=' + session.clientNonce
|
|
37
|
+
var serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iteration
|
|
38
|
+
|
|
39
|
+
var clientFinalMessageWithoutProof = 'c=biws,r=' + sv.nonce
|
|
40
|
+
|
|
41
|
+
var authMessage = clientFirstMessageBare + ',' + serverFirstMessage + ',' + clientFinalMessageWithoutProof
|
|
42
|
+
|
|
43
|
+
var clientSignature = createHMAC(storedKey, authMessage)
|
|
44
|
+
var clientProofBytes = xorBuffers(clientKey, clientSignature)
|
|
45
|
+
var clientProof = clientProofBytes.toString('base64')
|
|
46
|
+
|
|
47
|
+
var serverKey = createHMAC(saltedPassword, 'Server Key')
|
|
48
|
+
var serverSignatureBytes = createHMAC(serverKey, authMessage)
|
|
49
|
+
|
|
50
|
+
session.message = 'SASLResponse'
|
|
51
|
+
session.serverSignature = serverSignatureBytes.toString('base64')
|
|
52
|
+
session.response = clientFinalMessageWithoutProof + ',p=' + clientProof
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function finalizeSession (session, serverData) {
|
|
56
|
+
if (session.message !== 'SASLResponse') {
|
|
57
|
+
throw new Error('SASL: Last message was not SASLResponse')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
var serverSignature
|
|
61
|
+
|
|
62
|
+
String(serverData).split(',').forEach(function (part) {
|
|
63
|
+
switch (part[0]) {
|
|
64
|
+
case 'v':
|
|
65
|
+
serverSignature = part.substr(2)
|
|
66
|
+
break
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
if (serverSignature !== session.serverSignature) {
|
|
71
|
+
throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not match')
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function extractVariablesFromFirstServerMessage (data) {
|
|
76
|
+
var nonce, salt, iteration
|
|
77
|
+
|
|
78
|
+
String(data).split(',').forEach(function (part) {
|
|
79
|
+
switch (part[0]) {
|
|
80
|
+
case 'r':
|
|
81
|
+
nonce = part.substr(2)
|
|
82
|
+
break
|
|
83
|
+
case 's':
|
|
84
|
+
salt = part.substr(2)
|
|
85
|
+
break
|
|
86
|
+
case 'i':
|
|
87
|
+
iteration = parseInt(part.substr(2), 10)
|
|
88
|
+
break
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
if (!nonce) {
|
|
93
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!salt) {
|
|
97
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!iteration) {
|
|
101
|
+
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
nonce,
|
|
106
|
+
salt,
|
|
107
|
+
iteration
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function xorBuffers (a, b) {
|
|
112
|
+
if (!Buffer.isBuffer(a)) a = Buffer.from(a)
|
|
113
|
+
if (!Buffer.isBuffer(b)) b = Buffer.from(b)
|
|
114
|
+
var res = []
|
|
115
|
+
if (a.length > b.length) {
|
|
116
|
+
for (var i = 0; i < b.length; i++) {
|
|
117
|
+
res.push(a[i] ^ b[i])
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
for (var j = 0; j < a.length; j++) {
|
|
121
|
+
res.push(a[j] ^ b[j])
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return Buffer.from(res)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createHMAC (key, msg) {
|
|
128
|
+
return crypto.createHmac('sha256', key).update(msg).digest()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function Hi (password, saltBytes, iterations) {
|
|
132
|
+
var ui1 = createHMAC(password, Buffer.concat([saltBytes, Buffer.from([0, 0, 0, 1])]))
|
|
133
|
+
var ui = ui1
|
|
134
|
+
for (var i = 0; i < iterations - 1; i++) {
|
|
135
|
+
ui1 = createHMAC(password, ui1)
|
|
136
|
+
ui = xorBuffers(ui, ui1)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return ui
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
startSession,
|
|
144
|
+
continueSession,
|
|
145
|
+
finalizeSession
|
|
146
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pg",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.9.0",
|
|
4
4
|
"description": "PostgreSQL client - pure javascript & libpq with the same API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"database",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"main": "./lib",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"buffer-writer": "2.0.0",
|
|
23
|
-
"packet-reader": "0.
|
|
23
|
+
"packet-reader": "1.0.0",
|
|
24
24
|
"pg-connection-string": "0.1.3",
|
|
25
25
|
"pg-pool": "^2.0.4",
|
|
26
|
-
"pg-types": "~
|
|
26
|
+
"pg-types": "~2.0.0",
|
|
27
27
|
"pgpass": "1.x",
|
|
28
28
|
"semver": "4.3.2"
|
|
29
29
|
},
|
package/.travis.yml
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
language: node_js
|
|
2
|
-
sudo: false
|
|
3
|
-
dist: trusty
|
|
4
|
-
before_script:
|
|
5
|
-
- node script/create-test-tables.js pg://postgres@127.0.0.1:5432/postgres
|
|
6
|
-
env:
|
|
7
|
-
- CC=clang CXX=clang++ npm_config_clang=1 PGUSER=postgres PGDATABASE=postgres
|
|
8
|
-
|
|
9
|
-
matrix:
|
|
10
|
-
include:
|
|
11
|
-
- node_js: "lts/boron"
|
|
12
|
-
addons:
|
|
13
|
-
postgresql: "9.6"
|
|
14
|
-
- node_js: "lts/argon"
|
|
15
|
-
addons:
|
|
16
|
-
postgresql: "9.6"
|
|
17
|
-
- node_js: "10"
|
|
18
|
-
addons:
|
|
19
|
-
postgresql: "9.6"
|
|
20
|
-
- node_js: "11"
|
|
21
|
-
addons:
|
|
22
|
-
postgresql: "9.6"
|
|
23
|
-
- node_js: "lts/carbon"
|
|
24
|
-
addons:
|
|
25
|
-
postgresql: "9.1"
|
|
26
|
-
dist: precise
|
|
27
|
-
- node_js: "lts/carbon"
|
|
28
|
-
addons:
|
|
29
|
-
postgresql: "9.2"
|
|
30
|
-
- node_js: "lts/carbon"
|
|
31
|
-
addons:
|
|
32
|
-
postgresql: "9.3"
|
|
33
|
-
- node_js: "lts/carbon"
|
|
34
|
-
addons:
|
|
35
|
-
postgresql: "9.4"
|
|
36
|
-
- node_js: "lts/carbon"
|
|
37
|
-
addons:
|
|
38
|
-
postgresql: "9.5"
|
|
39
|
-
- node_js: "lts/carbon"
|
|
40
|
-
addons:
|
|
41
|
-
postgresql: "9.6"
|