From 428e1c8a7b2322c2650294638cb1663ac5692728 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 18 Mar 2021 01:21:37 +0200 Subject: [PATCH] Add support for Redis 6 `auth pass [user]` (#1508) * Add support for `auth pass user` * fix lint issues * fix typo * fix more lint issues * more lints fixes * reverse password user order * update redis-commands * Update individualCommands.js Clean code * Update individualCommands.js * Update auth.spec.js * Update index.js Co-authored-by: Leibale Eidelman --- index.js | 3 ++- lib/createClient.js | 6 +++++- lib/individualCommands.js | 24 ++++++++++++++++++------ test/auth.spec.js | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/index.js b/index.js index 439c784c8d9..fe79c5f3935 100644 --- a/index.js +++ b/index.js @@ -109,6 +109,7 @@ function RedisClient (options, stream) { this.closing = false; this.server_info = {}; this.auth_pass = options.auth_pass || options.password; + this.auth_user = options.auth_user || options.user; this.selected_db = options.db; // Save the selected db here, used when reconnecting this.fire_strings = true; // Determine if strings or buffers should be written to the stream this.pipeline = false; @@ -240,7 +241,7 @@ RedisClient.prototype.create_stream = function () { if (this.auth_pass !== undefined) { this.ready = true; // Fail silently as we might not be able to connect - this.auth(this.auth_pass, function (err) { + this.auth(this.auth_pass, this.auth_user, function (err) { if (err && err.code !== 'UNCERTAIN_STATE') { self.emit('error', err); } diff --git a/lib/createClient.js b/lib/createClient.js index 1533d73d1b7..b03bb575399 100644 --- a/lib/createClient.js +++ b/lib/createClient.js @@ -29,7 +29,11 @@ module.exports = function createClient (port_arg, host_arg, options) { // [redis:]//[[user][:password]@][host][:port][/db-number][?db=db-number[&password=bar[&option=value]]] if (parsed.slashes) { // We require slashes if (parsed.auth) { - options.password = parsed.auth.slice(parsed.auth.indexOf(':') + 1); + var columnIndex = parsed.auth.indexOf(':'); + options.password = parsed.auth.slice(columnIndex + 1); + if (columnIndex > 0) { + options.user = parsed.auth.slice(0, columnIndex); + } } if (parsed.protocol) { if (parsed.protocol === 'rediss:') { diff --git a/lib/individualCommands.js b/lib/individualCommands.js index 44973324993..4d6f0a70942 100644 --- a/lib/individualCommands.js +++ b/lib/individualCommands.js @@ -180,7 +180,7 @@ Multi.prototype.info = Multi.prototype.INFO = function info (section, callback) return this; }; -function auth_callback (self, pass, callback) { +function auth_callback (self, pass, user, callback) { return function (err, res) { if (err) { if (no_password_is_set.test(err.message)) { @@ -191,7 +191,7 @@ function auth_callback (self, pass, callback) { // If redis is still loading the db, it will not authenticate and everything else will fail debug('Redis still loading, trying to authenticate later'); setTimeout(function () { - self.auth(pass, callback); + self.auth(user, pass, callback); }, 100); return; } @@ -200,25 +200,37 @@ function auth_callback (self, pass, callback) { }; } -RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, callback) { +RedisClient.prototype.auth = RedisClient.prototype.AUTH = function auth (pass, user, callback) { debug('Sending auth to ' + this.address + ' id ' + this.connection_id); + // Backward compatibility support for auth with password only + if (user instanceof Function) { + callback = user; + user = null; + } // Stash auth for connect and reconnect. this.auth_pass = pass; + this.auth_user = user; var ready = this.ready; this.ready = ready || this.offline_queue.length === 0; - var tmp = this.internal_send_command(new Command('auth', [pass], auth_callback(this, pass, callback))); + var tmp = this.internal_send_command(new Command('auth', user ? [user, pass] : [pass], auth_callback(this, pass, user, callback))); this.ready = ready; return tmp; }; // Only works with batch, not in a transaction -Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, callback) { +Multi.prototype.auth = Multi.prototype.AUTH = function auth (pass, user, callback) { debug('Sending auth to ' + this.address + ' id ' + this.connection_id); + // Backward compatibility support for auth with password only + if (user instanceof Function) { + callback = user; + user = null; + } // Stash auth for connect and reconnect. this.auth_pass = pass; - this.queue.push(new Command('auth', [pass], auth_callback(this._client, callback))); + this.auth_user = user; + this.queue.push(new Command('auth', user ? [user, pass] : [pass], auth_callback(this._client, pass, user, callback))); return this; }; diff --git a/test/auth.spec.js b/test/auth.spec.js index 995d98136b3..d1b596e5ae3 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -61,7 +61,7 @@ describe('client authentication', function () { }); var tmp = client.command_queue.get(0).callback; client.command_queue.get(0).callback = function (err, res) { - client.auth = function (pass, callback) { + client.auth = function (pass, user, callback) { callback(null, 'retry worked'); }; tmp(new Error('ERR redis is still LOADING'));