diff --git a/README.md b/README.md index 3ade5da8..9f5bee95 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,13 @@ You can also specify connection options as a [`redis://` URL](http://www.iana.or ```javascript // Connect to 127.0.0.1:6380, db 4, using password "authpassword": new Redis("redis://:authpassword@127.0.0.1:6380/4"); + +// Username can also be passed via URI. +// It's worth to noticing that for compatibility reasons `allowUsernameInURI` +// need to be provided, otherwise the username part will be ignored. +new Redis( + "redis://username:authpassword@127.0.0.1:6380/4?allowUsernameInURI=true" +); ``` See [API Documentation](API.md#new_Redis) for all available options. diff --git a/lib/utils/index.ts b/lib/utils/index.ts index a71406ee..d9533fcd 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -255,10 +255,19 @@ export function parseURL(url) { parsed = urllibParse(url, true, true); } + const options = parsed.query || {}; + const allowUsernameInURI = + options.allowUsernameInURI && options.allowUsernameInURI !== "false"; + delete options.allowUsernameInURI; + const result: any = {}; if (parsed.auth) { - const index = parsed.auth.indexOf(":") - result.password = index === -1 ? '' : parsed.auth.slice(index + 1) + const index = parsed.auth.indexOf(":"); + if (allowUsernameInURI) { + result.username = + index === -1 ? parsed.auth : parsed.auth.slice(0, index); + } + result.password = index === -1 ? "" : parsed.auth.slice(index + 1); } if (parsed.pathname) { if (parsed.protocol === "redis:" || parsed.protocol === "rediss:") { @@ -275,7 +284,7 @@ export function parseURL(url) { if (parsed.port) { result.port = parsed.port; } - defaults(result, parsed.query); + defaults(result, options); return result; } diff --git a/test/functional/auth.ts b/test/functional/auth.ts index e9fa22cb..9739dc41 100644 --- a/test/functional/auth.ts +++ b/test/functional/auth.ts @@ -170,6 +170,25 @@ describe("auth", function () { redis = new Redis({ port: 17379, username, password }); }); + it("should handle auth with Redis URL string with username and password (Redis >=6) (redis://foo:bar@baz.com/) correctly", function (done) { + let username = "user"; + let password = "pass"; + let redis; + new MockServer(17379, function (argv) { + if ( + argv[0] === "auth" && + argv[1] === username && + argv[2] === password + ) { + redis.disconnect(); + done(); + } + }); + redis = new Redis( + `redis://user:pass@localhost:17379/?allowUsernameInURI=true` + ); + }); + it('should not emit "error" when the Redis >=6 server doesn\'t need auth', function (done) { new MockServer(17379, function (argv) { if (argv[0] === "auth" && argv[1] === "pass") { diff --git a/test/unit/utils.ts b/test/unit/utils.ts index 8a16b5ad..cdffd1f1 100644 --- a/test/unit/utils.ts +++ b/test/unit/utils.ts @@ -204,9 +204,7 @@ describe("utils", function () { password: "pass:word", key: "value", }); - expect( - utils.parseURL("redis://user@127.0.0.1:6380/4?key=value") - ).to.eql({ + expect(utils.parseURL("redis://user@127.0.0.1:6380/4?key=value")).to.eql({ host: "127.0.0.1", port: "6380", db: "4", @@ -226,6 +224,59 @@ describe("utils", function () { key: "value", }); }); + + it("supports allowUsernameInURI", function () { + expect( + utils.parseURL( + "redis://user:pass@127.0.0.1:6380/4?allowUsernameInURI=true" + ) + ).to.eql({ + host: "127.0.0.1", + port: "6380", + db: "4", + username: "user", + password: "pass", + }); + expect( + utils.parseURL( + "redis://user:pass@127.0.0.1:6380/4?allowUsernameInURI=false" + ) + ).to.eql({ + host: "127.0.0.1", + port: "6380", + db: "4", + password: "pass", + }); + expect( + utils.parseURL( + "redis://user:pass:word@127.0.0.1:6380/4?key=value&allowUsernameInURI=true" + ) + ).to.eql({ + host: "127.0.0.1", + port: "6380", + db: "4", + username: "user", + password: "pass:word", + key: "value", + }); + expect( + utils.parseURL( + "redis://user@127.0.0.1:6380/4?key=value&allowUsernameInURI=true" + ) + ).to.eql({ + host: "127.0.0.1", + port: "6380", + db: "4", + username: "user", + password: "", + key: "value", + }); + expect( + utils.parseURL("redis://127.0.0.1/?allowUsernameInURI=true") + ).to.eql({ + host: "127.0.0.1", + }); + }); }); describe(".sample", function () {