Skip to content
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

Add support for Redis 6 auth pass [user] #1508

Merged
merged 15 commits into from Mar 17, 2021
3 changes: 2 additions & 1 deletion index.js
Expand Up @@ -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;
Expand Down Expand Up @@ -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_user, this.auth_pass, function (err) {
if (err && err.code !== 'UNCERTAIN_STATE') {
self.emit('error', err);
}
Expand Down
6 changes: 5 additions & 1 deletion lib/createClient.js
Expand Up @@ -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:') {
Expand Down
24 changes: 18 additions & 6 deletions lib/individualCommands.js
Expand Up @@ -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)) {
Expand All @@ -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;
}
Expand All @@ -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;
};

Expand Down
2 changes: 1 addition & 1 deletion test/auth.spec.js
Expand Up @@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leibale shouldn't we keep the original test too? for backward?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gkorland overriding a function on the client is not part of the API..

callback(null, 'retry worked');
};
tmp(new Error('ERR redis is still LOADING'));
Expand Down