From c7e86b1ada9b9c30d9eb7378337aae2bf39b0e75 Mon Sep 17 00:00:00 2001 From: Michael Rose Date: Fri, 25 Jun 2021 16:54:42 +0200 Subject: [PATCH] fix(NODE-2905): support SERVICE_NAME authentication mechanism property (#2857) --- src/cmap/auth/gssapi.ts | 12 ++-- test/manual/README.md | 19 +++++ test/manual/kerberos.test.js | 88 ++++++++++++++---------- test/unit/core/connection_string.test.js | 7 +- 4 files changed, 80 insertions(+), 46 deletions(-) create mode 100644 test/manual/README.md diff --git a/src/cmap/auth/gssapi.ts b/src/cmap/auth/gssapi.ts index 17f8b09d6bc..27fc5c1a271 100644 --- a/src/cmap/auth/gssapi.ts +++ b/src/cmap/auth/gssapi.ts @@ -6,6 +6,7 @@ import type { Document } from '../../bson'; type MechanismProperties = { gssapiCanonicalizeHostName?: boolean; + SERVICE_NAME?: string; }; import * as dns from 'dns'; @@ -72,15 +73,14 @@ function makeKerberosClient(authContext: AuthContext, callback: Callback { if (err) return callback(err); diff --git a/test/manual/README.md b/test/manual/README.md new file mode 100644 index 00000000000..0d7f7e2f598 --- /dev/null +++ b/test/manual/README.md @@ -0,0 +1,19 @@ +# Manual Tests + +This directory contains a set of manual tests that require setup of additional systems and thus cannot _just_ be run as part of the other unit tests. + +See the individual test files for details - only some will be described in this document. + +## Kerberos Tests +The Kerberos tests are defined in [`kerberos.test.js](./kerberos.test.js). The following environment variables must be set up for the test to work properly: + +* `MONGODB_URI`: The full connection string including a valid User Principal Name to connect to a Kerberos-enabled MongoDB server (must include `authMechanism=GSSAPI`) +* `KRB5_PRINCIPAL`: The User Principal Name specified in the connection string (i.e. `MONGODB_URI`) + +> Note: You have to initialize Kerberos locally before running the tests, e.g. by running `kinit` in order to acquire a valid TGT from the KDC. + +The test also requires a database `kerberos` to be present with a single document in the `test` collection having a `kerberos` property of boolean value `true`, e.g.: + +``` +use kerberos; db.test.insertOne({ kerberos: true }) +``` \ No newline at end of file diff --git a/test/manual/kerberos.test.js b/test/manual/kerberos.test.js index b8ae26437f6..d3025937213 100644 --- a/test/manual/kerberos.test.js +++ b/test/manual/kerberos.test.js @@ -3,12 +3,36 @@ const { MongoClient } = require('../../src'); const chai = require('chai'); const expect = chai.expect; +function verifyKerberosAuthentication(client, done) { + client + .db('kerberos') + .collection('test') + .find() + .toArray(function (err, docs) { + let expectError; + try { + expect(err).to.not.exist; + expect(docs).to.have.length(1); + expect(docs[0].kerberos).to.be.true; + } catch (e) { + expectError = e; + } + client.close(e => done(expectError || e)); + }); +} + describe('Kerberos', function () { if (process.env.MONGODB_URI == null) { console.error('skipping Kerberos tests, MONGODB_URI environment variable is not defined'); return; } let krb5Uri = process.env.MONGODB_URI; + + if (!process.env.KRB5_PRINCIPAL) { + console.error('skipping Kerberos tests, KRB5_PRINCIPAL environment variable is not defined'); + return; + } + if (process.platform === 'win32') { console.error('Win32 run detected'); if (process.env.LDAPTEST_PASSWORD == null) { @@ -22,54 +46,48 @@ describe('Kerberos', function () { const client = new MongoClient(krb5Uri); client.connect(function (err, client) { expect(err).to.not.exist; - client - .db('kerberos') - .collection('test') - .find() - .toArray(function (err, docs) { - expect(err).to.not.exist; - expect(docs[0].kerberos).to.be.true; - - client.close(done); - }); + verifyKerberosAuthentication(client, done); }); }); + // TODO: this test only tests that these properties do not crash anything - but not that they actually have an effect it('validate that SERVICE_REALM and CANONICALIZE_HOST_NAME can be passed in', function (done) { const client = new MongoClient( `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:false,SERVICE_REALM:windows&maxPoolSize=1` ); client.connect(function (err, client) { expect(err).to.not.exist; - client - .db('kerberos') - .collection('test') - .find() - .toArray(function (err, docs) { - expect(err).to.not.exist; - expect(docs[0].kerberos).to.be.true; - - client.close(done); - }); + verifyKerberosAuthentication(client, done); }); }); - it('should authenticate with additional authentication properties', function (done) { - const client = new MongoClient( - `${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:false&maxPoolSize=1` - ); - client.connect(function (err, client) { - expect(err).to.not.exist; - client - .db('kerberos') - .collection('test') - .find() - .toArray(function (err, docs) { - expect(err).to.not.exist; - expect(docs[0].kerberos).to.be.true; + 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`, { + authMechanismProperties: { + SERVICE_NAME: 'alternate' + } + }); + client.connect(function (err) { + expect(err).to.exist; + expect(err.message).to.match( + /(Error from KDC: LOOKING_UP_SERVER)|(not found in Kerberos database)/ + ); + done(); + }); + }); - client.close(done); - }); + it('as part of the query string parameters', function (done) { + const client = new MongoClient( + `${krb5Uri}&authMechanismProperties=SERVICE_NAME:alternate&maxPoolSize=1` + ); + client.connect(function (err) { + expect(err).to.exist; + expect(err.message).to.match( + /(Error from KDC: LOOKING_UP_SERVER)|(not found in Kerberos database)/ + ); + done(); + }); }); }); diff --git a/test/unit/core/connection_string.test.js b/test/unit/core/connection_string.test.js index bcde175d4b9..c1f51322889 100644 --- a/test/unit/core/connection_string.test.js +++ b/test/unit/core/connection_string.test.js @@ -8,7 +8,7 @@ const { AuthMechanism } = require('../../../src/cmap/auth/defaultAuthProviders') const expect = chai.expect; chai.use(require('chai-subset')); -// NOTE: These are cases we could never check for unless we write out own +// NOTE: These are cases we could never check for unless we write our own // url parser. The node parser simply won't let these through, so we // are safe skipping them. const skipTests = [ @@ -22,10 +22,7 @@ const skipTests = [ 'Unsupported option values are ignored', // We don't actually support `wtimeoutMS` which this test depends upon - 'Deprecated (or unknown) options are ignored if replacement exists', - - // We already handle this case in different ways - 'may support deprecated gssapiServiceName option (GSSAPI)' + 'Deprecated (or unknown) options are ignored if replacement exists' ]; describe('Connection String', function () {