Skip to content

Commit

Permalink
feat(NODE-2938): add service host mechanism property (#3130)
Browse files Browse the repository at this point in the history
  • Loading branch information
durran committed Feb 10, 2022
1 parent 541e939 commit 46d5821
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 15 deletions.
5 changes: 4 additions & 1 deletion src/cmap/auth/gssapi.ts
Expand Up @@ -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;
};
Expand Down Expand Up @@ -72,6 +73,7 @@ export class GSSAPI extends AuthProvider {
});
}
}

function makeKerberosClient(authContext: AuthContext, callback: Callback<KerberosClient>): void {
const { hostAddress } = authContext.options;
const { credentials } = authContext;
Expand Down Expand Up @@ -102,7 +104,8 @@ function makeKerberosClient(authContext: AuthContext, callback: Callback<Kerbero
Object.assign(initOptions, { user: username, password: password });
}

let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${host}`;
const spnHost = mechanismProperties.SERVICE_HOST ?? host;
let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${spnHost}`;
if ('SERVICE_REALM' in mechanismProperties) {
spn = `${spn}@${mechanismProperties.SERVICE_REALM}`;
}
Expand Down
1 change: 1 addition & 0 deletions src/cmap/auth/mongo_credentials.ts
Expand Up @@ -27,6 +27,7 @@ function getDefaultAuthMechanism(hello?: Document): AuthMechanism {

/** @public */
export interface AuthMechanismProperties extends Document {
SERVICE_HOST?: string;
SERVICE_NAME?: string;
SERVICE_REALM?: string;
CANONICALIZE_HOST_NAME?: boolean;
Expand Down
46 changes: 45 additions & 1 deletion test/manual/kerberos.test.js
Expand Up @@ -41,6 +41,7 @@ describe('Kerberos', function () {
return;
}
let krb5Uri = process.env.MONGODB_URI;
const parts = krb5Uri.split('@', 2);

if (!process.env.KRB5_PRINCIPAL) {
console.error('skipping Kerberos tests, KRB5_PRINCIPAL environment variable is not defined');
Expand All @@ -52,7 +53,6 @@ describe('Kerberos', function () {
if (process.env.LDAPTEST_PASSWORD == null) {
throw new Error('The env parameter LDAPTEST_PASSWORD must be set');
}
const parts = krb5Uri.split('@', 2);
krb5Uri = `${parts[0]}:${process.env.LDAPTEST_PASSWORD}@${parts[1]}`;
}

Expand All @@ -65,6 +65,10 @@ describe('Kerberos', function () {
});

it('validate that gssapiCanonicalizeHostName can be passed in', function (done) {
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`
);
Expand All @@ -76,6 +80,10 @@ describe('Kerberos', function () {
});

it('validate that CANONICALIZE_HOST_NAME can be passed in', function (done) {
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`
);
Expand All @@ -97,6 +105,42 @@ describe('Kerberos', 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: {
SERVICE_HOST: 'example.com'
}
});

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.message).to.match(/GSS failure|UNKNOWN_SERVER/);
});
});

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`, {
Expand Down
7 changes: 4 additions & 3 deletions test/spec/auth/connection-string.json
Expand Up @@ -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",
Expand All @@ -89,7 +89,8 @@
"mechanism": "GSSAPI",
"mechanism_properties": {
"SERVICE_NAME": "other",
"CANONICALIZE_HOST_NAME": true
"CANONICALIZE_HOST_NAME": true,
"SERVICE_HOST": "example.com"
}
}
},
Expand Down Expand Up @@ -368,4 +369,4 @@
"valid": false
}
]
}
}
3 changes: 2 additions & 1 deletion test/spec/auth/connection-string.yml
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
5 changes: 3 additions & 2 deletions test/spec/connection-string/valid-auth.json
Expand Up @@ -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": [
Expand All @@ -282,7 +282,8 @@
"authmechanism": "GSSAPI",
"authmechanismproperties": {
"SERVICE_NAME": "other",
"CANONICALIZE_HOST_NAME": true
"CANONICALIZE_HOST_NAME": true,
"SERVICE_HOST": "example.com"
}
}
},
Expand Down
5 changes: 3 additions & 2 deletions test/spec/connection-string/valid-auth.yml
Expand Up @@ -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:
Expand All @@ -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"
Expand Down
5 changes: 3 additions & 2 deletions test/spec/uri-options/auth-options.json
Expand Up @@ -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,
Expand All @@ -11,7 +11,8 @@
"authMechanism": "GSSAPI",
"authMechanismProperties": {
"SERVICE_NAME": "other",
"CANONICALIZE_HOST_NAME": true
"CANONICALIZE_HOST_NAME": true,
"SERVICE_HOST": "example.com"
},
"authSource": "$external"
}
Expand Down
5 changes: 3 additions & 2 deletions 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: ~
Expand All @@ -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)"
Expand Down
3 changes: 2 additions & 1 deletion test/unit/connection_string.test.ts
Expand Up @@ -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
Expand Down

0 comments on commit 46d5821

Please sign in to comment.