Improve authentication handling (#908)

This commit is contained in:
Tomas Della Vedova
2019-07-18 10:33:11 +02:00
committed by GitHub
parent 24e674469e
commit 0ebbd71e9a
12 changed files with 602 additions and 142 deletions

2
lib/Connection.d.ts vendored
View File

@ -21,6 +21,7 @@
import { URL } from 'url';
import { inspect, InspectOptions } from 'util';
import { ApiKeyAuth, BasicAuth } from './ConnectionPool'
import * as http from 'http';
import { ConnectionOptions as TlsConnectionOptions } from 'tls';
@ -34,6 +35,7 @@ interface ConnectionOptions {
agent?: AgentOptions | agentFn;
status?: string;
roles?: any;
auth?: BasicAuth | ApiKeyAuth;
}
interface RequestOptions extends http.ClientRequestArgs {

View File

@ -34,8 +34,7 @@ class Connection {
this.url = opts.url
this.ssl = opts.ssl || null
this.id = opts.id || stripAuth(opts.url.href)
this.headers = opts.headers || null
this.auth = opts.auth || { username: null, password: null }
this.headers = prepareHeaders(opts.headers, opts.auth)
this.deadCount = 0
this.resurrectTimeout = 0
@ -181,7 +180,6 @@ class Connection {
buildRequestObject (params) {
const url = this.url
const { username, password } = this.auth
const request = {
protocol: url.protocol,
hostname: url.hostname[0] === '['
@ -196,9 +194,6 @@ class Connection {
// https://github.com/elastic/elasticsearch-js/issues/843
port: url.port !== '' ? url.port : undefined,
headers: this.headers,
auth: username != null && password != null
? `${username}:${password}`
: undefined,
agent: this.agent
}
@ -230,10 +225,15 @@ class Connection {
// the logs very hard to read. The user can still
// access them with `instance.agent` and `instance.ssl`.
[inspect.custom] (depth, options) {
const {
authorization,
...headers
} = this.headers
return {
url: stripAuth(this.url.toString()),
id: this.id,
headers: this.headers,
headers,
deadCount: this.deadCount,
resurrectTimeout: this.resurrectTimeout,
_openRequests: this._openRequests,
@ -243,10 +243,15 @@ class Connection {
}
toJSON () {
const {
authorization,
...headers
} = this.headers
return {
url: stripAuth(this.url.toString()),
id: this.id,
headers: this.headers,
headers,
deadCount: this.deadCount,
resurrectTimeout: this.resurrectTimeout,
_openRequests: this._openRequests,
@ -302,4 +307,21 @@ function resolve (host, path) {
}
}
function prepareHeaders (headers = {}, auth) {
if (auth != null && headers.authorization == null) {
if (auth.username && auth.password) {
headers.authorization = 'Basic ' + Buffer.from(`${auth.username}:${auth.password}`).toString('base64')
}
if (auth.apiKey) {
if (typeof auth.apiKey === 'object') {
headers.authorization = 'ApiKey ' + Buffer.from(`${auth.apiKey.id}:${auth.apiKey.api_key}`).toString('base64')
} else {
headers.authorization = `ApiKey ${auth.apiKey}`
}
}
}
return headers
}
module.exports = Connection

View File

@ -26,6 +26,7 @@ import { nodeFilterFn, nodeSelectorFn } from './Transport';
interface ConnectionPoolOptions {
ssl?: SecureContextOptions;
agent?: AgentOptions;
auth: BasicAuth | ApiKeyAuth;
pingTimeout?: number;
Connection: typeof Connection;
resurrectStrategy?: string;
@ -36,6 +37,20 @@ export interface getConnectionOptions {
selector?: nodeSelectorFn;
}
export interface ApiKeyAuth {
apiKey:
| string
| {
id: string;
api_key: string;
}
}
export interface BasicAuth {
username: string;
password: string;
}
export interface resurrectOptions {
now?: number;
requestId: string;
@ -66,6 +81,7 @@ export default class ConnectionPool {
resurrectTimeout: number;
resurrectTimeoutCutoff: number;
pingTimeout: number;
auth: BasicAuth | ApiKeyAuth;
Connection: typeof Connection;
resurrectStrategy: number;
constructor(opts?: ConnectionPoolOptions);

View File

@ -30,7 +30,7 @@ class ConnectionPool {
this.connections = new Map()
this.dead = []
this.selector = opts.selector
this._auth = null
this.auth = opts.auth || null
this._ssl = opts.ssl
this._agent = opts.agent
// the resurrect timeout is 60s
@ -217,23 +217,14 @@ class ConnectionPool {
if (typeof opts === 'string') {
opts = this.urlToHost(opts)
}
// if a given node has auth data we store it in the connection pool,
// so if we add new nodes without auth data (after a sniff for example)
// we can add it to them once the connection instance has been created
if (opts.url.username !== '' && opts.url.password !== '') {
this._auth = {
opts.auth = {
username: decodeURIComponent(opts.url.username),
password: decodeURIComponent(opts.url.password)
}
opts.auth = this._auth
}
if (this._auth != null) {
if (opts.auth == null || (opts.auth.username == null && opts.auth.password == null)) {
opts.auth = this._auth
opts.url.username = this._auth.username
opts.url.password = this._auth.password
}
} else if (this.auth !== null) {
opts.auth = this.auth
}
if (opts.ssl == null) opts.ssl = this._ssl