Skip to content

Commit c27b5ad

Browse files
committed
create external_auth_source_mechanisms set typing, add external_auth_source_mechanism validation, update/add tests
1 parent 3c041f4 commit c27b5ad

6 files changed

+130
-9
lines changed

src/cmap/auth/defaultAuthProviders.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ export const AuthMechanism = Object.freeze({
1818
MONGODB_X509: 'MONGODB-X509'
1919
} as const);
2020

21+
/** @public */
22+
export const $EXTERNAL_AUTH_SOURCE_MECHANISMS = new Set<string | undefined>([
23+
AuthMechanism.MONGODB_GSSAPI,
24+
AuthMechanism.MONGODB_AWS,
25+
AuthMechanism.MONGODB_X509
26+
]);
27+
2128
/** @public */
2229
export type AuthMechanism = typeof AuthMechanism[keyof typeof AuthMechanism];
2330

src/cmap/auth/mongo_credentials.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import type { Document } from '../../bson';
44
import { MongoAPIError, MongoMissingCredentialsError } from '../../error';
5-
import { AuthMechanism } from './defaultAuthProviders';
5+
import { $EXTERNAL_AUTH_SOURCE_MECHANISMS, AuthMechanism } from './defaultAuthProviders';
66

77
// https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst
88
function getDefaultAuthMechanism(ismaster?: Document): AuthMechanism {
@@ -136,11 +136,7 @@ export class MongoCredentials {
136136
throw new MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`);
137137
}
138138

139-
if (
140-
this.mechanism === AuthMechanism.MONGODB_GSSAPI ||
141-
this.mechanism === AuthMechanism.MONGODB_AWS ||
142-
this.mechanism === AuthMechanism.MONGODB_X509
143-
) {
139+
if ($EXTERNAL_AUTH_SOURCE_MECHANISMS.has(this.mechanism)) {
144140
if (this.source != null && this.source !== '$external') {
145141
// TODO(NODE-3485): Replace this with a MongoAuthValidationError
146142
throw new MongoAPIError(

src/connection_string.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as dns from 'dns';
22
import * as fs from 'fs';
33
import ConnectionString from 'mongodb-connection-string-url';
44
import { URLSearchParams } from 'url';
5-
import { AuthMechanism } from './cmap/auth/defaultAuthProviders';
5+
import { $EXTERNAL_AUTH_SOURCE_MECHANISMS, AuthMechanism } from './cmap/auth/defaultAuthProviders';
66
import { ReadPreference, ReadPreferenceMode } from './read_preference';
77
import { ReadConcern, ReadConcernLevel } from './read_concern';
88
import { W, WriteConcern } from './write_concern';
@@ -124,7 +124,11 @@ export function resolveSRVRecord(options: MongoOptions, callback: Callback<HostA
124124
const replicaSet = txtRecordOptions.get('replicaSet') ?? undefined;
125125
const loadBalanced = txtRecordOptions.get('loadBalanced') ?? undefined;
126126

127-
if (!options.userSpecifiedAuthSource && source) {
127+
if (
128+
!options.userSpecifiedAuthSource &&
129+
source &&
130+
!$EXTERNAL_AUTH_SOURCE_MECHANISMS.has(options.credentials?.mechanism)
131+
) {
128132
options.credentials = MongoCredentials.merge(options.credentials, { source });
129133
}
130134

test/unit/connection_string.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ describe('Connection String', function () {
9494
expect(options.credentials.mechanism).to.equal(AuthMechanism.MONGODB_GSSAPI);
9595
});
9696

97+
it('should provide default authSource when valid AuthMechanism provided', function () {
98+
// const options = parseOptions(
99+
// 'mongodb+srv://jira-sync.pw0q4.mongodb.net/testDB?authMechanism=MONGODB-AWS&retryWrites=true&w=majority'
100+
// );
101+
const options = parseOptions(
102+
'mongodb+srv://jira-sync.pw0q4.mongodb.net/testDB?authMechanism=MONGODB-AWS&retryWrites=true&w=majority'
103+
);
104+
expect(options.credentials.source).to.equal('$external');
105+
});
106+
97107
it('should parse a numeric authSource with variable width', function () {
98108
const options = parseOptions('mongodb://test@localhost/?authSource=0001');
99109
expect(options.credentials.source).to.equal('0001');

test/unit/polling_srv_records_for_mongos_discovery.prose.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as sinon from 'sinon';
33
import { expect } from 'chai';
44
import { MongoClient } from '../../src';
55
import { processTick } from '../tools/utils';
6-
import { it } from 'mocha';
76
import * as mock from '../tools/mongodb-mock/index';
87

98
/*

test/unit/srv_option_handling.test.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import * as dns from 'dns';
2+
import * as sinon from 'sinon';
3+
import { expect } from 'chai';
4+
import { MongoCredentials } from '../../src/cmap/auth/mongo_credentials';
5+
import { resolveSRVRecord } from '../../src/connection_string';
6+
import { AuthMechanism } from '../../src/cmap/auth/defaultAuthProviders';
7+
8+
describe('Srv Option Handling', () => {
9+
let resolveSrvStub: sinon.SinonStub;
10+
let resolveTxtStub: sinon.SinonStub;
11+
let resolveSRVRecordStub: sinon.SinonStub;
12+
let lookupStub: sinon.SinonStub;
13+
14+
afterEach(async () => {
15+
if (resolveSrvStub) {
16+
resolveSrvStub.restore();
17+
resolveSrvStub = undefined;
18+
}
19+
if (resolveTxtStub) {
20+
resolveTxtStub.restore();
21+
resolveTxtStub = undefined;
22+
}
23+
if (lookupStub) {
24+
lookupStub.restore();
25+
lookupStub = undefined;
26+
}
27+
if (resolveSRVRecordStub) {
28+
resolveSRVRecordStub.restore();
29+
resolveSRVRecordStub = undefined;
30+
}
31+
});
32+
33+
function makeStub(txtRecord: string) {
34+
const mockAddress = [
35+
{
36+
name: 'localhost.test.mock.test.build.10gen.cc',
37+
port: 2017,
38+
weight: 0,
39+
priority: 0
40+
}
41+
];
42+
43+
const mockRecord: string[][] = [[txtRecord]];
44+
45+
// first call is for the driver initial connection
46+
// second call will check the poller
47+
resolveSrvStub = sinon.stub(dns, 'resolveSrv').callsFake((address, callback) => {
48+
return process.nextTick(callback, null, mockAddress);
49+
});
50+
51+
resolveTxtStub = sinon.stub(dns, 'resolveTxt').callsFake((address, thisIsWhatWeAreTesting) => {
52+
thisIsWhatWeAreTesting(null, mockRecord);
53+
});
54+
}
55+
56+
for (const iterator of [
57+
{
58+
mechanism: AuthMechanism.MONGODB_AWS,
59+
source: '',
60+
userSpecifiedAuthSource: false,
61+
expected: 'succeed'
62+
},
63+
{
64+
mechanism: AuthMechanism.MONGODB_AWS,
65+
source: 'admin',
66+
userSpecifiedAuthSource: true,
67+
expected: 'succeed'
68+
},
69+
{
70+
mechanism: null,
71+
source: 'admin',
72+
userSpecifiedAuthSource: false,
73+
expected: 'fail'
74+
}
75+
]) {
76+
it(`should ${iterator.expected} for ${iterator.mechanism} mechanism and ${
77+
iterator.userSpecifiedAuthSource ? '' : 'non-'
78+
}user-specified source: ${iterator.source}`, function () {
79+
makeStub('authSource=admin');
80+
81+
const options = {
82+
credentials: new MongoCredentials({
83+
source: '$external',
84+
mechanism: iterator.mechanism,
85+
username: 'username',
86+
password: 'password',
87+
mechanismProperties: {}
88+
}),
89+
srvHost: 'host',
90+
srvServiceName: 'mongodb',
91+
userSpecifiedAuthSource: iterator.userSpecifiedAuthSource
92+
};
93+
94+
resolveSRVRecord(options as any, (err, hostAddress) => {
95+
if (iterator.expected === 'succeed') {
96+
expect(options).to.have.nested.property('credentials.source', '$external');
97+
} else {
98+
expect(options).to.not.have.nested.property('credentials.source', '$external');
99+
}
100+
});
101+
102+
// expect(client).to.have.nested.property('options.credentials.source', '$external');
103+
});
104+
}
105+
});

0 commit comments

Comments
 (0)