Skip to content

Commit c144dda

Browse files
elemountdougwilson
authored andcommitted
Support auth switch in change user flow
closes #1776
1 parent 776d843 commit c144dda

File tree

6 files changed

+132
-9
lines changed

6 files changed

+132
-9
lines changed

Changes.md

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ you spot any mistakes.
1919
* Add Amazon RDS GovCloud SSL certificates #1876
2020
* Add new error codes up to MySQL 5.7.21
2121
* Include connection ID in debug output
22+
* Support auth switch in change user flow #1776
2223
* Support Node.js 9.x
2324
* Support Node.js 10.x #2003 #2024 #2026 #2034
2425
* Update Amazon RDS SSL certificates

lib/protocol/Auth.js

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@ var Buffer = require('safe-buffer').Buffer;
22
var Crypto = require('crypto');
33
var Auth = exports;
44

5+
function auth(name, data, options) {
6+
options = options || {};
7+
8+
switch (name) {
9+
case 'mysql_native_password':
10+
return Auth.token(options.password, data.slice(0, 20));
11+
default:
12+
return undefined;
13+
}
14+
}
15+
Auth.auth = auth;
16+
517
function sha1(msg) {
618
var hash = Crypto.createHash('sha1');
719
hash.update(msg, 'binary');

lib/protocol/sequences/ChangeUser.js

+26
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ function ChangeUser(options, callback) {
1515
this._currentConfig = options.currentConfig;
1616
}
1717

18+
ChangeUser.prototype.determinePacket = function determinePacket(firstByte) {
19+
switch (firstByte) {
20+
case 0xfe: return Packets.AuthSwitchRequestPacket;
21+
case 0xff: return Packets.ErrorPacket;
22+
default: return undefined;
23+
}
24+
};
25+
1826
ChangeUser.prototype.start = function(handshakeInitializationPacket) {
1927
var scrambleBuff = handshakeInitializationPacket.scrambleBuff();
2028
scrambleBuff = Auth.token(this._password, scrambleBuff);
@@ -34,6 +42,24 @@ ChangeUser.prototype.start = function(handshakeInitializationPacket) {
3442
this.emit('packet', packet);
3543
};
3644

45+
ChangeUser.prototype['AuthSwitchRequestPacket'] = function (packet) {
46+
var name = packet.authMethodName;
47+
var data = Auth.auth(name, packet.authMethodData, {
48+
password: this._password
49+
});
50+
51+
if (data !== undefined) {
52+
this.emit('packet', new Packets.AuthSwitchResponsePacket({
53+
data: data
54+
}));
55+
} else {
56+
var err = new Error('MySQL is requesting the ' + name + ' authentication method, which is not supported.');
57+
err.code = 'UNSUPPORTED_AUTH_METHOD';
58+
err.fatal = true;
59+
this.end(err);
60+
}
61+
};
62+
3763
ChangeUser.prototype['ErrorPacket'] = function(packet) {
3864
var err = this._packetToError(packet);
3965
err.fatal = true;

lib/protocol/sequences/Handshake.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,19 @@ Handshake.prototype.determinePacket = function determinePacket(firstByte, parser
3434
};
3535

3636
Handshake.prototype['AuthSwitchRequestPacket'] = function (packet) {
37-
if (packet.authMethodName === 'mysql_native_password') {
38-
var challenge = packet.authMethodData.slice(0, 20);
37+
var name = packet.authMethodName;
38+
var data = Auth.auth(name, packet.authMethodData, {
39+
password: this._config.password
40+
});
3941

42+
if (data !== undefined) {
4043
this.emit('packet', new Packets.AuthSwitchResponsePacket({
41-
data: Auth.token(this._config.password, challenge)
44+
data: data
4245
}));
4346
} else {
44-
var err = new Error(
45-
'MySQL is requesting the ' + packet.authMethodName + ' authentication method, which is not supported.'
46-
);
47-
48-
err.code = 'UNSUPPORTED_AUTH_METHOD';
47+
var err = new Error('MySQL is requesting the ' + name + ' authentication method, which is not supported.');
48+
err.code = 'UNSUPPORTED_AUTH_METHOD';
4949
err.fatal = true;
50-
5150
this.end(err);
5251
}
5352
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
var assert = require('assert');
2+
var Buffer = require('safe-buffer').Buffer;
3+
var common = require('../../common');
4+
var connection = common.createConnection({
5+
port : common.fakeServerPort,
6+
user : 'user_1',
7+
password : 'pass_1'
8+
});
9+
10+
var server = common.createFakeServer();
11+
12+
server.listen(common.fakeServerPort, function(err) {
13+
assert.ifError(err);
14+
15+
connection.query('SELECT CURRENT_USER()', function (err, result) {
16+
assert.ifError(err);
17+
assert.strictEqual(result[0]['CURRENT_USER()'], 'user_1@localhost');
18+
19+
connection.changeUser({user: 'user_2', password: 'pass_2'}, function (err) {
20+
assert.ok(err);
21+
assert.equal(err.code, 'UNSUPPORTED_AUTH_METHOD');
22+
assert.equal(err.fatal, true);
23+
assert.ok(/foo_plugin_password/.test(err.message));
24+
25+
connection.destroy();
26+
server.destroy();
27+
});
28+
});
29+
});
30+
31+
server.on('connection', function (incomingConnection) {
32+
incomingConnection.on('changeUser', function () {
33+
this.authSwitchRequest({
34+
authMethodName : 'foo_plugin_password',
35+
authMethodData : Buffer.alloc(0)
36+
});
37+
});
38+
39+
incomingConnection.handshake();
40+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var assert = require('assert');
2+
var Crypto = require('crypto');
3+
var common = require('../../common');
4+
var connection = common.createConnection({
5+
port : common.fakeServerPort,
6+
user : 'user_1',
7+
password : 'pass_1'
8+
});
9+
10+
var random = Crypto.pseudoRandomBytes || Crypto.randomBytes; // Depends on node.js version
11+
var server = common.createFakeServer();
12+
13+
server.listen(common.fakeServerPort, function(err) {
14+
assert.ifError(err);
15+
16+
connection.query('SELECT CURRENT_USER()', function (err, result) {
17+
assert.ifError(err);
18+
assert.strictEqual(result[0]['CURRENT_USER()'], 'user_1@localhost');
19+
20+
connection.changeUser({user: 'user_2', password: 'pass_2'}, function (err) {
21+
assert.ifError(err);
22+
connection.destroy();
23+
server.destroy();
24+
});
25+
});
26+
});
27+
28+
server.on('connection', function (incomingConnection) {
29+
random(20, function (err, scramble) {
30+
assert.ifError(err);
31+
32+
incomingConnection.on('authSwitchResponse', function (packet) {
33+
this._sendAuthResponse(packet.data, common.Auth.token('pass_2', scramble));
34+
});
35+
36+
incomingConnection.on('changeUser', function () {
37+
this.authSwitchRequest({
38+
authMethodName : 'mysql_native_password',
39+
authMethodData : scramble
40+
});
41+
});
42+
43+
incomingConnection.handshake();
44+
});
45+
});

0 commit comments

Comments
 (0)