Skip to content

Commit

Permalink
fix(NODE-2905): support SERVICE_NAME authentication mechanism property (
Browse files Browse the repository at this point in the history
  • Loading branch information
rose-m committed Jun 25, 2021
1 parent ac4aabc commit dfb91b8
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 46 deletions.
12 changes: 6 additions & 6 deletions src/cmap/auth/gssapi.ts
Expand Up @@ -6,6 +6,7 @@ import type { Document } from '../../bson';

type MechanismProperties = {
gssapiCanonicalizeHostName?: boolean;
SERVICE_NAME?: string;
};

import * as dns from 'dns';
Expand Down Expand Up @@ -72,15 +73,14 @@ function makeKerberosClient(authContext: AuthContext, callback: Callback<Kerbero
return callback(Kerberos['kModuleError']);
}

const { username, password, mechanismProperties } = credentials;
const serviceName =
mechanismProperties['gssapiservicename'] ||
mechanismProperties['gssapiServiceName'] ||
'mongodb';
const { username, password } = credentials;
const mechanismProperties = credentials.mechanismProperties as MechanismProperties;

const serviceName = mechanismProperties.SERVICE_NAME ?? 'mongodb';

performGssapiCanonicalizeHostName(
hostAddress.host,
mechanismProperties as MechanismProperties,
mechanismProperties,
(err?: Error | MongoError, host?: string) => {
if (err) return callback(err);

Expand Down
19 changes: 19 additions & 0 deletions 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 })
```
88 changes: 53 additions & 35 deletions test/manual/kerberos.test.js
Expand Up @@ -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) {
Expand All @@ -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();
});
});
});

Expand Down
7 changes: 2 additions & 5 deletions test/unit/core/connection_string.test.js
Expand Up @@ -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 = [
Expand All @@ -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 () {
Expand Down

0 comments on commit dfb91b8

Please sign in to comment.