Skip to content

Commit

Permalink
fix: move kerberos client setup from prepare to auth (#2655)
Browse files Browse the repository at this point in the history
  • Loading branch information
emadum authored Dec 3, 2020
1 parent 3bcd393 commit 93ef9e8
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 81 deletions.
152 changes: 72 additions & 80 deletions src/cmap/auth/gssapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,117 +2,109 @@ import { AuthProvider, AuthContext } from './auth_provider';
import { MongoError } from '../../error';
import { Kerberos, KerberosClient } from '../../deps';
import type { Callback } from '../../utils';
import type { HandshakeDocument } from '../connect';
import type { Document } from '../../bson';

const kGssapiClient = Symbol('GSSAPI_CLIENT');

type MechanismProperties = {
gssapiCanonicalizeHostName?: boolean;
};

import * as dns from 'dns';

export class GSSAPI extends AuthProvider {
[kGssapiClient]: KerberosClient;
prepare(
handshakeDoc: HandshakeDocument,
authContext: AuthContext,
callback: Callback<HandshakeDocument>
): void {
const { host, port } = authContext.options;
const { credentials } = authContext;
if (!host || !port || !credentials) {
return callback(
new MongoError(
`Connection must specify: ${host ? 'host' : ''}, ${port ? 'port' : ''}, ${
credentials ? 'host' : 'credentials'
}.`
)
);
}

if ('kModuleError' in Kerberos) {
return callback(Kerberos['kModuleError']);
}

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

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

const initOptions = {};
if (password != null) {
Object.assign(initOptions, { user: username, password: password });
}

Kerberos.initializeClient(
`${serviceName}${process.platform === 'win32' ? '/' : '@'}${host}`,
initOptions,
(err: string, client: KerberosClient): void => {
if (err) return callback(new MongoError(err));
if (client == null) return callback();
this[kGssapiClient] = client;
callback(undefined, handshakeDoc);
}
);
}
);
}

auth(authContext: AuthContext, callback: Callback): void {
const { connection, credentials } = authContext;
if (credentials == null) return callback(new MongoError('credentials required'));
const { username } = credentials;
const client = this[kGssapiClient];
if (client == null) return callback(new MongoError('gssapi client missing'));
function externalCommand(
command: Document,
cb: Callback<{ payload: string; conversationId: any }>
) {
return connection.command('$external.$cmd', command, cb);
}
client.step('', (err, payload) => {
makeKerberosClient(authContext, (err, client) => {
if (err) return callback(err);

externalCommand(saslStart(payload), (err, result) => {
if (client == null) return callback(new MongoError('gssapi client missing'));
client.step('', (err, payload) => {
if (err) return callback(err);
if (result == null) return callback();
negotiate(client, 10, result.payload, (err, payload) => {
if (err) return callback(err);

externalCommand(saslContinue(payload, result.conversationId), (err, result) => {
externalCommand(saslStart(payload), (err, result) => {
if (err) return callback(err);
if (result == null) return callback();
negotiate(client, 10, result.payload, (err, payload) => {
if (err) return callback(err);
if (result == null) return callback();
finalize(client, username, result.payload, (err, payload) => {
if (err) return callback(err);

externalCommand(
{
saslContinue: 1,
conversationId: result.conversationId,
payload
},
(err, result) => {
if (err) return callback(err);

callback(undefined, result);
}
);
externalCommand(saslContinue(payload, result.conversationId), (err, result) => {
if (err) return callback(err);
if (result == null) return callback();
finalize(client, username, result.payload, (err, payload) => {
if (err) return callback(err);

externalCommand(
{
saslContinue: 1,
conversationId: result.conversationId,
payload
},
(err, result) => {
if (err) return callback(err);

callback(undefined, result);
}
);
});
});
});
});
});
});
}
}
function makeKerberosClient(authContext: AuthContext, callback: Callback<KerberosClient>): void {
const { host, port } = authContext.options;
const { credentials } = authContext;
if (!host || !port || !credentials) {
return callback(
new MongoError(
`Connection must specify: ${host ? 'host' : ''}, ${port ? 'port' : ''}, ${
credentials ? 'host' : 'credentials'
}.`
)
);
}

if ('kModuleError' in Kerberos) {
return callback(Kerberos['kModuleError']);
}

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

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

const initOptions = {};
if (password != null) {
Object.assign(initOptions, { user: username, password: password });
}

Kerberos.initializeClient(
`${serviceName}${process.platform === 'win32' ? '/' : '@'}${host}`,
initOptions,
(err: string, client: KerberosClient): void => {
if (err) return callback(new MongoError(err));
callback(undefined, client);
}
);
}
);
}

function saslStart(payload?: string): Document {
return {
saslStart: 1,
Expand Down
2 changes: 1 addition & 1 deletion test/unit/optional_require.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('optionalRequire', function () {
return this.skip();
}
const gssapi = new GSSAPI();
gssapi.prepare('', new AuthContext(null, true, { host: true, port: true }), error => {
gssapi.auth(new AuthContext(null, true, { host: true, port: true }), error => {
expect(error).to.exist;
expect(error.message).includes('not found');
});
Expand Down

0 comments on commit 93ef9e8

Please sign in to comment.