Skip to content

Commit 46d5821

Browse files
authored
feat(NODE-2938): add service host mechanism property (#3130)
1 parent 541e939 commit 46d5821

File tree

10 files changed

+70
-15
lines changed

10 files changed

+70
-15
lines changed

src/cmap/auth/gssapi.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type MechanismProperties = {
1616
/** @deprecated use `CANONICALIZE_HOST_NAME` instead */
1717
gssapiCanonicalizeHostName?: boolean;
1818
CANONICALIZE_HOST_NAME?: boolean;
19+
SERVICE_HOST?: string;
1920
SERVICE_NAME?: string;
2021
SERVICE_REALM?: string;
2122
};
@@ -72,6 +73,7 @@ export class GSSAPI extends AuthProvider {
7273
});
7374
}
7475
}
76+
7577
function makeKerberosClient(authContext: AuthContext, callback: Callback<KerberosClient>): void {
7678
const { hostAddress } = authContext.options;
7779
const { credentials } = authContext;
@@ -102,7 +104,8 @@ function makeKerberosClient(authContext: AuthContext, callback: Callback<Kerbero
102104
Object.assign(initOptions, { user: username, password: password });
103105
}
104106

105-
let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${host}`;
107+
const spnHost = mechanismProperties.SERVICE_HOST ?? host;
108+
let spn = `${serviceName}${process.platform === 'win32' ? '/' : '@'}${spnHost}`;
106109
if ('SERVICE_REALM' in mechanismProperties) {
107110
spn = `${spn}@${mechanismProperties.SERVICE_REALM}`;
108111
}

src/cmap/auth/mongo_credentials.ts

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function getDefaultAuthMechanism(hello?: Document): AuthMechanism {
2727

2828
/** @public */
2929
export interface AuthMechanismProperties extends Document {
30+
SERVICE_HOST?: string;
3031
SERVICE_NAME?: string;
3132
SERVICE_REALM?: string;
3233
CANONICALIZE_HOST_NAME?: boolean;

test/manual/kerberos.test.js

+45-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('Kerberos', function () {
4141
return;
4242
}
4343
let krb5Uri = process.env.MONGODB_URI;
44+
const parts = krb5Uri.split('@', 2);
4445

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

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

6767
it('validate that gssapiCanonicalizeHostName can be passed in', function (done) {
68+
if (process.platform === 'darwin') {
69+
this.test.skipReason = 'DNS does not resolve with proper CNAME record on evergreen MacOS';
70+
this.skip();
71+
}
6872
const client = new MongoClient(
6973
`${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,gssapiCanonicalizeHostName:true&maxPoolSize=1`
7074
);
@@ -76,6 +80,10 @@ describe('Kerberos', function () {
7680
});
7781

7882
it('validate that CANONICALIZE_HOST_NAME can be passed in', function (done) {
83+
if (process.platform === 'darwin') {
84+
this.test.skipReason = 'DNS does not resolve with proper CNAME record on evergreen MacOS';
85+
this.skip();
86+
}
7987
const client = new MongoClient(
8088
`${krb5Uri}&authMechanismProperties=SERVICE_NAME:mongodb,CANONICALIZE_HOST_NAME:true&maxPoolSize=1`
8189
);
@@ -97,6 +105,42 @@ describe('Kerberos', function () {
97105
});
98106
});
99107

108+
context('when passing SERVICE_HOST as an auth mech option', function () {
109+
context('when the SERVICE_HOST is invalid', function () {
110+
const client = new MongoClient(`${krb5Uri}&maxPoolSize=1`, {
111+
authMechanismProperties: {
112+
SERVICE_HOST: 'example.com'
113+
}
114+
});
115+
116+
it('fails to authenticate', async function () {
117+
let expectedError;
118+
await client.connect().catch(e => {
119+
expectedError = e;
120+
});
121+
if (!expectedError) {
122+
expect.fail('Expected connect with invalid SERVICE_HOST to fail');
123+
}
124+
expect(expectedError.message).to.match(/GSS failure|UNKNOWN_SERVER/);
125+
});
126+
});
127+
128+
context('when the SERVICE_HOST is valid', function () {
129+
const client = new MongoClient(`${krb5Uri}&maxPoolSize=1`, {
130+
authMechanismProperties: {
131+
SERVICE_HOST: 'ldaptest.10gen.cc'
132+
}
133+
});
134+
135+
it('authenticates', function (done) {
136+
client.connect(function (err, client) {
137+
expect(err).to.not.exist;
138+
verifyKerberosAuthentication(client, done);
139+
});
140+
});
141+
});
142+
});
143+
100144
describe('should use the SERVICE_NAME property', function () {
101145
it('as an option handed to the MongoClient', function (done) {
102146
const client = new MongoClient(`${krb5Uri}&maxPoolSize=1`, {

test/spec/auth/connection-string.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
},
8181
{
8282
"description": "should accept generic mechanism property (GSSAPI)",
83-
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true",
83+
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com",
8484
"valid": true,
8585
"credential": {
8686
"username": "[email protected]",
@@ -89,7 +89,8 @@
8989
"mechanism": "GSSAPI",
9090
"mechanism_properties": {
9191
"SERVICE_NAME": "other",
92-
"CANONICALIZE_HOST_NAME": true
92+
"CANONICALIZE_HOST_NAME": true,
93+
"SERVICE_HOST": "example.com"
9394
}
9495
}
9596
},
@@ -368,4 +369,4 @@
368369
"valid": false
369370
}
370371
]
371-
}
372+
}

test/spec/auth/connection-string.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ tests:
6464
SERVICE_NAME: "mongodb"
6565
-
6666
description: "should accept generic mechanism property (GSSAPI)"
67-
uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true"
67+
uri: "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com"
6868
valid: true
6969
credential:
7070
username: "[email protected]"
@@ -74,6 +74,7 @@ tests:
7474
mechanism_properties:
7575
SERVICE_NAME: "other"
7676
CANONICALIZE_HOST_NAME: true
77+
SERVICE_HOST: "example.com"
7778
-
7879
description: "should accept the password (GSSAPI)"
7980
uri: "mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external"

test/spec/connection-string/valid-auth.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@
263263
},
264264
{
265265
"description": "Escaped username (GSSAPI)",
266-
"uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI",
266+
"uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authMechanism=GSSAPI",
267267
"valid": true,
268268
"warning": false,
269269
"hosts": [
@@ -282,7 +282,8 @@
282282
"authmechanism": "GSSAPI",
283283
"authmechanismproperties": {
284284
"SERVICE_NAME": "other",
285-
"CANONICALIZE_HOST_NAME": true
285+
"CANONICALIZE_HOST_NAME": true,
286+
"SERVICE_HOST": "example.com"
286287
}
287288
}
288289
},

test/spec/connection-string/valid-auth.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ tests:
206206
authmechanism: "MONGODB-X509"
207207
-
208208
description: "Escaped username (GSSAPI)"
209-
uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI"
209+
uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authMechanism=GSSAPI"
210210
valid: true
211211
warning: false
212212
hosts:
@@ -222,7 +222,8 @@ tests:
222222
authmechanism: "GSSAPI"
223223
authmechanismproperties:
224224
SERVICE_NAME: "other"
225-
CANONICALIZE_HOST_NAME: true
225+
CANONICALIZE_HOST_NAME: true,
226+
SERVICE_HOST: "example.com"
226227
-
227228
description: "At-signs in options aren't part of the userinfo"
228229
uri: "mongodb://alice:[email protected]/admin?replicaset=my@replicaset"

test/spec/uri-options/auth-options.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"tests": [
33
{
44
"description": "Valid auth options are parsed correctly (GSSAPI)",
5-
"uri": "mongodb://foo:[email protected]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authSource=$external",
5+
"uri": "mongodb://foo:[email protected]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authSource=$external",
66
"valid": true,
77
"warning": false,
88
"hosts": null,
@@ -11,7 +11,8 @@
1111
"authMechanism": "GSSAPI",
1212
"authMechanismProperties": {
1313
"SERVICE_NAME": "other",
14-
"CANONICALIZE_HOST_NAME": true
14+
"CANONICALIZE_HOST_NAME": true,
15+
"SERVICE_HOST": "example.com"
1516
},
1617
"authSource": "$external"
1718
}

test/spec/uri-options/auth-options.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
tests:
22
-
33
description: "Valid auth options are parsed correctly (GSSAPI)"
4-
uri: "mongodb://foo:[email protected]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authSource=$external"
4+
uri: "mongodb://foo:[email protected]/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authSource=$external"
55
valid: true
66
warning: false
77
hosts: ~
@@ -10,7 +10,8 @@ tests:
1010
authMechanism: "GSSAPI"
1111
authMechanismProperties:
1212
SERVICE_NAME: "other"
13-
CANONICALIZE_HOST_NAME: true
13+
CANONICALIZE_HOST_NAME: true,
14+
SERVICE_HOST: "example.com"
1415
authSource: "$external"
1516
-
1617
description: "Valid auth options are parsed correctly (SCRAM-SHA-1)"

test/unit/connection_string.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ describe('Connection String', function () {
7777

7878
it('should parse `authMechanismProperties`', function () {
7979
const options = parseOptions(
80-
'mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,SERVICE_REALM:blah,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI'
80+
'mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,SERVICE_REALM:blah,CANONICALIZE_HOST_NAME:true,SERVICE_HOST:example.com&authMechanism=GSSAPI'
8181
);
8282
expect(options.credentials.mechanismProperties).to.deep.include({
83+
SERVICE_HOST: 'example.com',
8384
SERVICE_NAME: 'other',
8485
SERVICE_REALM: 'blah',
8586
CANONICALIZE_HOST_NAME: true

0 commit comments

Comments
 (0)