From d51e903e70adacad94c8b3a7bea2c1ec58a80c72 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 1 Feb 2022 11:46:29 +0100 Subject: [PATCH 1/5] feat(NODE-2938): add service host mechanism property --- src/cmap/auth/gssapi.ts | 5 +++- src/cmap/auth/mongo_credentials.ts | 1 + test/manual/kerberos.test.js | 33 ++++++++++++++++++++- test/spec/auth/connection-string.json | 7 +++-- test/spec/auth/connection-string.yml | 3 +- test/spec/connection-string/valid-auth.json | 5 ++-- test/spec/connection-string/valid-auth.yml | 5 ++-- test/spec/uri-options/auth-options.json | 5 ++-- test/spec/uri-options/auth-options.yml | 5 ++-- test/unit/connection_string.test.ts | 3 +- 10 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/cmap/auth/gssapi.ts b/src/cmap/auth/gssapi.ts index c55acacb07..56d5206cce 100644 --- a/src/cmap/auth/gssapi.ts +++ b/src/cmap/auth/gssapi.ts @@ -16,6 +16,7 @@ type MechanismProperties = { /** @deprecated use `CANONICALIZE_HOST_NAME` instead */ gssapiCanonicalizeHostName?: boolean; CANONICALIZE_HOST_NAME?: boolean; + SERVICE_HOST?: string; SERVICE_NAME?: string; SERVICE_REALM?: string; }; @@ -72,6 +73,7 @@ export class GSSAPI extends AuthProvider { }); } } + function makeKerberosClient(authContext: AuthContext, callback: Callback): void { const { hostAddress } = authContext.options; const { credentials } = authContext; @@ -102,7 +104,8 @@ function makeKerberosClient(authContext: AuthContext, callback: Callback { + expect(e).to.exist; + }); + }); + }); + + context('when the SERVICE_HOST is valid', function () { + const client = new MongoClient(`${krb5Uri}&maxPoolSize=1`, { + authMechanismProperties: { + SERVICE_HOST: 'ldaptest.10gen.cc' + } + }); + + it('authenticates', function (done) { + client.connect(function (err, client) { + expect(err).to.not.exist; + verifyKerberosAuthentication(client, done); + }); + }); + }); + }); + describe('should use the SERVICE_NAME property', function () { it('as an option handed to the MongoClient', function (done) { const client = new MongoClient(`${krb5Uri}&maxPoolSize=1`, { diff --git a/test/spec/auth/connection-string.json b/test/spec/auth/connection-string.json index 7fc3c5c45f..ea2bdf0eba 100644 --- a/test/spec/auth/connection-string.json +++ b/test/spec/auth/connection-string.json @@ -80,7 +80,7 @@ }, { "description": "should accept generic mechanism property (GSSAPI)", - "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true", + "uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com", "valid": true, "credential": { "username": "user@DOMAIN.COM", @@ -89,7 +89,8 @@ "mechanism": "GSSAPI", "mechanism_properties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true + "CANONICALIZE_HOST_NAME": true, + "SERVICE_HOST": "example.com" } } }, @@ -368,4 +369,4 @@ "valid": false } ] -} \ No newline at end of file +} diff --git a/test/spec/auth/connection-string.yml b/test/spec/auth/connection-string.yml index c4f34def63..70a76afe9b 100644 --- a/test/spec/auth/connection-string.yml +++ b/test/spec/auth/connection-string.yml @@ -64,7 +64,7 @@ tests: SERVICE_NAME: "mongodb" - description: "should accept generic mechanism property (GSSAPI)" - uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true" + uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com" valid: true credential: username: "user@DOMAIN.COM" @@ -74,6 +74,7 @@ tests: mechanism_properties: SERVICE_NAME: "other" CANONICALIZE_HOST_NAME: true + SERVICE_HOST: "example.com" - description: "should accept the password (GSSAPI)" uri: "mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external" diff --git a/test/spec/connection-string/valid-auth.json b/test/spec/connection-string/valid-auth.json index 672777ff84..3b00b0e503 100644 --- a/test/spec/connection-string/valid-auth.json +++ b/test/spec/connection-string/valid-auth.json @@ -263,7 +263,7 @@ }, { "description": "Escaped username (GSSAPI)", - "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authMechanism=GSSAPI", "valid": true, "warning": false, "hosts": [ @@ -282,7 +282,8 @@ "authmechanism": "GSSAPI", "authmechanismproperties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true + "CANONICALIZE_HOST_NAME": true, + "SERVICE_HOST": "example.com" } } }, diff --git a/test/spec/connection-string/valid-auth.yml b/test/spec/connection-string/valid-auth.yml index 1319a41888..2f8dc4da0f 100644 --- a/test/spec/connection-string/valid-auth.yml +++ b/test/spec/connection-string/valid-auth.yml @@ -206,7 +206,7 @@ tests: authmechanism: "MONGODB-X509" - description: "Escaped username (GSSAPI)" - uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI" + uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authMechanism=GSSAPI" valid: true warning: false hosts: @@ -222,7 +222,8 @@ tests: authmechanism: "GSSAPI" authmechanismproperties: SERVICE_NAME: "other" - CANONICALIZE_HOST_NAME: true + CANONICALIZE_HOST_NAME: true, + SERVICE_HOST: "example.com" - description: "At-signs in options aren't part of the userinfo" uri: "mongodb://alice:secret@example.com/admin?replicaset=my@replicaset" diff --git a/test/spec/uri-options/auth-options.json b/test/spec/uri-options/auth-options.json index fadbac35d2..612d9144fb 100644 --- a/test/spec/uri-options/auth-options.json +++ b/test/spec/uri-options/auth-options.json @@ -2,7 +2,7 @@ "tests": [ { "description": "Valid auth options are parsed correctly (GSSAPI)", - "uri": "mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authSource=$external", + "uri": "mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authSource=$external", "valid": true, "warning": false, "hosts": null, @@ -11,7 +11,8 @@ "authMechanism": "GSSAPI", "authMechanismProperties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true + "CANONICALIZE_HOST_NAME": true, + "SERVICE_HOST": "example.com" }, "authSource": "$external" } diff --git a/test/spec/uri-options/auth-options.yml b/test/spec/uri-options/auth-options.yml index cd63b0738d..45ac1e7915 100644 --- a/test/spec/uri-options/auth-options.yml +++ b/test/spec/uri-options/auth-options.yml @@ -1,7 +1,7 @@ tests: - description: "Valid auth options are parsed correctly (GSSAPI)" - uri: "mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authSource=$external" + uri: "mongodb://foo:bar@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authSource=$external" valid: true warning: false hosts: ~ @@ -10,7 +10,8 @@ tests: authMechanism: "GSSAPI" authMechanismProperties: SERVICE_NAME: "other" - CANONICALIZE_HOST_NAME: true + CANONICALIZE_HOST_NAME: true, + SERVICE_HOST: "example.com" authSource: "$external" - description: "Valid auth options are parsed correctly (SCRAM-SHA-1)" diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index c051a95221..9de26fd23b 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -77,9 +77,10 @@ describe('Connection String', function () { it('should parse `authMechanismProperties`', function () { const options = parseOptions( - 'mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,SERVICE_REALM:blah,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI' + 'mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,SERVICE_REALM:blah,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authMechanism=GSSAPI' ); expect(options.credentials.mechanismProperties).to.deep.include({ + SERVICE_HOST: 'example.com', SERVICE_NAME: 'other', SERVICE_REALM: 'blah', CANONICALIZE_HOST_NAME: true From 1c3932b01ef37e90f65059bf2a9e81d8a6d2ebe8 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 9 Feb 2022 21:55:24 +0100 Subject: [PATCH 2/5] test(NODE-2938): minor test fixes --- test/manual/kerberos.test.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/manual/kerberos.test.js b/test/manual/kerberos.test.js index 4650f06735..c2e3cdc525 100644 --- a/test/manual/kerberos.test.js +++ b/test/manual/kerberos.test.js @@ -97,7 +97,7 @@ describe('Kerberos', function () { }); }); - context('when passsing SERVICE_HOST', function () { + context('when passing SERVICE_HOST as an auth mech option', function () { context('when the SERVICE_HOST is invalid', function () { const client = new MongoClient(`${krb5Uri}&maxPoolSize=1`, { authMechanismProperties: { @@ -105,10 +105,15 @@ describe('Kerberos', function () { } }); - it('fails to authenticate', function () { - return client.connect().catch(e => { - expect(e).to.exist; + it('fails to authenticate', async function () { + let expectedError; + await client.connect().catch(e => { + expectedError = e; }); + if (!expectedError) { + expect.fail('Expected connect with invalid SERVICE_HOST to fail'); + } + expect(expectedError).property('message').to.include('Authentication failed'); }); }); From 8870edc9250cb120b40f63ecf8dea83a8513c2ab Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Feb 2022 13:25:14 +0100 Subject: [PATCH 3/5] test(NODE-2938): fix failure message --- test/manual/kerberos.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/manual/kerberos.test.js b/test/manual/kerberos.test.js index c2e3cdc525..0e51de5a29 100644 --- a/test/manual/kerberos.test.js +++ b/test/manual/kerberos.test.js @@ -113,7 +113,7 @@ describe('Kerberos', function () { if (!expectedError) { expect.fail('Expected connect with invalid SERVICE_HOST to fail'); } - expect(expectedError).property('message').to.include('Authentication failed'); + expect(expectedError).property('message').to.include('GSS failure'); }); }); From a52af111ee5ba734a07cdef0d7ed44730add1ab5 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Feb 2022 14:35:47 +0100 Subject: [PATCH 4/5] test(NODE-2938): fix kerberos tests --- test/manual/kerberos.test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/manual/kerberos.test.js b/test/manual/kerberos.test.js index 0e51de5a29..4d92e8f72c 100644 --- a/test/manual/kerberos.test.js +++ b/test/manual/kerberos.test.js @@ -65,6 +65,8 @@ describe('Kerberos', function () { }); it('validate that gssapiCanonicalizeHostName can be passed in', function (done) { + // CNAME resolution on evg macos boxes resolves to a ec2 CNAME which is not associated with the KDC. + if (process.platform === 'darwin') this.skip(); const client = new MongoClient( `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,gssapiCanonicalizeHostName:true&maxPoolSize=1` ); @@ -76,6 +78,8 @@ describe('Kerberos', function () { }); it('validate that CANONICALIZE_HOST_NAME can be passed in', function (done) { + // CNAME resolution on evg macos boxes resolves to a ec2 CNAME which is not associated with the KDC. + if (process.platform === 'darwin') this.skip(); const client = new MongoClient( `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:true&maxPoolSize=1` ); @@ -113,7 +117,8 @@ describe('Kerberos', function () { if (!expectedError) { expect.fail('Expected connect with invalid SERVICE_HOST to fail'); } - expect(expectedError).property('message').to.include('GSS failure'); + console.log(expectedError); + expect(expectedError.message).to.match(/GSS failure|UNKNOWN_SERVER/); }); }); From 4f111d76d21723687c0f0758223c47dd034a35b7 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Feb 2022 18:34:48 +0100 Subject: [PATCH 5/5] test(NODE-3777): add skip reason in tests --- test/manual/kerberos.test.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/manual/kerberos.test.js b/test/manual/kerberos.test.js index 4d92e8f72c..f60bde8140 100644 --- a/test/manual/kerberos.test.js +++ b/test/manual/kerberos.test.js @@ -65,8 +65,10 @@ describe('Kerberos', function () { }); it('validate that gssapiCanonicalizeHostName can be passed in', function (done) { - // CNAME resolution on evg macos boxes resolves to a ec2 CNAME which is not associated with the KDC. - if (process.platform === 'darwin') this.skip(); + if (process.platform === 'darwin') { + this.test.skipReason = 'DNS does not resolve with proper CNAME record on evergreen MacOS'; + this.skip(); + } const client = new MongoClient( `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,gssapiCanonicalizeHostName:true&maxPoolSize=1` ); @@ -78,8 +80,10 @@ describe('Kerberos', function () { }); it('validate that CANONICALIZE_HOST_NAME can be passed in', function (done) { - // CNAME resolution on evg macos boxes resolves to a ec2 CNAME which is not associated with the KDC. - if (process.platform === 'darwin') this.skip(); + if (process.platform === 'darwin') { + this.test.skipReason = 'DNS does not resolve with proper CNAME record on evergreen MacOS'; + this.skip(); + } const client = new MongoClient( `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:true&maxPoolSize=1` ); @@ -117,7 +121,6 @@ describe('Kerberos', function () { if (!expectedError) { expect.fail('Expected connect with invalid SERVICE_HOST to fail'); } - console.log(expectedError); expect(expectedError.message).to.match(/GSS failure|UNKNOWN_SERVER/); }); });