Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(NODE-2905): support SERVICE_NAME authentication mechanism property #2857

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/cmap/auth/gssapi.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this as it was the last reference to gssapiServiceName and there is no test with that description anymore.

'Deprecated (or unknown) options are ignored if replacement exists'
];

describe('Connection String', function () {
Expand Down