-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Support libpq's channel_binding and require_auth (and enforce them against every authentication request)
#3746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
b93c6ea
9febd2a
aea2cb9
3124258
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,16 @@ import { ClientConfig } from 'pg' | |
|
|
||
| export function parse(connectionString: string, options?: Options): ConnectionOptions | ||
|
|
||
| // Use of SCRAM channel binding, as libpq's channel_binding parameter defines it | ||
| export type ChannelBinding = 'disable' | 'prefer' | 'require' | ||
|
|
||
| export interface Options { | ||
| // Use libpq semantics when interpreting the connection string | ||
| useLibpqCompat?: boolean | ||
| // The channel binding setting held by the caller, for cases where it was not | ||
| // given in the connection string. A value of 'require' suppresses the sslmode | ||
| // deprecation warning, since the server is then authenticated by the binding. | ||
| channelBinding?: ChannelBinding | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We introduced Setting If someone wants to add channel binding, they could parse and then add it to the resulting config object: const config = parse('...');
config.channel_binding = 'require';If it is to prevent the warning, then: const url = new URL('pg:///?sslmode=require')
if (url.searchParams.has('channel_binding') === false) {
url.searchParams.append('channel_binding', 'require')
}
const config = parse(url.toString()); |
||
| } | ||
|
|
||
| interface SSLConfig { | ||
|
|
@@ -23,6 +30,10 @@ export interface ConnectionOptions { | |
| client_encoding?: string | ||
| ssl?: boolean | string | SSLConfig | ||
| sslnegotiation?: 'postgres' | 'direct' | ||
| channel_binding?: ChannelBinding | ||
| // The authentication method(s) the server may ask for, as libpq's require_auth | ||
| // parameter defines them: a comma-separated list, optionally negated with '!' | ||
| require_auth?: string | ||
|
|
||
| application_name?: string | ||
| fallback_application_name?: string | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||
| 'use strict' | ||||||||||
|
|
||||||||||
| // Support for libpq's channel_binding parameter, which says whether SCRAM authentication | ||||||||||
| // has to be bound to the server's certificate: | ||||||||||
| // https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-CHANNEL-BINDING | ||||||||||
|
|
||||||||||
| const defaults = require('./defaults') | ||||||||||
|
|
||||||||||
| const channelBindingLevels = ['disable', 'prefer', 'require'] | ||||||||||
|
|
||||||||||
| // Accepts the levels libpq's channel_binding parameter defines, plus the booleans that | ||||||||||
| // pg's original enableChannelBinding option took. Any other non-string keeps its | ||||||||||
| // historical truthiness, so previously working configs keep working. A string that is not | ||||||||||
| // a level is refused rather than read as the weakest one that resembles it. | ||||||||||
| const normalizeChannelBinding = function (value) { | ||||||||||
| if (typeof value !== 'string') { | ||||||||||
| return value ? 'prefer' : 'disable' | ||||||||||
| } | ||||||||||
| if (!channelBindingLevels.includes(value)) { | ||||||||||
| throw new Error( | ||||||||||
| `Invalid channel_binding value: "${value}". Valid values are "disable", "prefer" and "require" (or a boolean).` | ||||||||||
| ) | ||||||||||
| } | ||||||||||
| return value | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // channel_binding, being libpq's own spelling, wins over the older | ||||||||||
| // enableChannelBinding option, then the environment, then the default. | ||||||||||
| const resolveChannelBinding = function (channelBinding, enableChannelBinding) { | ||||||||||
| const value = [channelBinding, enableChannelBinding, process.env.PGCHANNELBINDING, defaults.channel_binding].find( | ||||||||||
| (candidate) => candidate !== undefined && candidate !== null | ||||||||||
| ) | ||||||||||
|
Comment on lines
+30
to
+32
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
but shouldn’t const value = channelBinding ?? convertBoolean(enableChannelBinding) ?? process.env.PGCHANNELBINDING ?? defaults.channel_binding
if (!channelBindingLevels.includes(value)) {
throw …
}
return value(where |
||||||||||
| return normalizeChannelBinding(value) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| module.exports = { channelBindingLevels, normalizeChannelBinding, resolveChannelBinding } | ||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.