152 lines
3.7 KiB
JavaScript
152 lines
3.7 KiB
JavaScript
'use strict';
|
|
const dns = require('dns');
|
|
const kerberos = require('../kerberos');
|
|
|
|
class MongoAuthProcess {
|
|
constructor(host, port, serviceName, options) {
|
|
options = options || {};
|
|
this.host = host;
|
|
this.port = port;
|
|
|
|
// Set up service name
|
|
this.serviceName = serviceName || options.gssapiServiceName || 'mongodb';
|
|
|
|
// Options
|
|
this.canonicalizeHostName =
|
|
typeof options.gssapiCanonicalizeHostName === 'boolean'
|
|
? options.gssapiCanonicalizeHostName
|
|
: false;
|
|
|
|
// Set up first transition
|
|
this._transition = firstTransition(this);
|
|
|
|
// Number of retries
|
|
this.retries = 10;
|
|
}
|
|
|
|
init(username, password, callback) {
|
|
const self = this;
|
|
this.username = username;
|
|
this.password = password;
|
|
|
|
// Canonicialize host name if needed
|
|
function performGssapiCanonicalizeHostName(canonicalizeHostName, host, callback) {
|
|
if (!canonicalizeHostName) return callback();
|
|
|
|
// Attempt to resolve the host name
|
|
dns.resolveCname(host, (err, r) => {
|
|
if (err) return callback(err);
|
|
|
|
// Get the first resolve host id
|
|
if (Array.isArray(r) && r.length > 0) {
|
|
self.host = r[0];
|
|
}
|
|
|
|
callback();
|
|
});
|
|
}
|
|
|
|
// Canonicialize host name if needed
|
|
performGssapiCanonicalizeHostName(this.canonicalizeHostName, this.host, err => {
|
|
if (err) return callback(err);
|
|
|
|
const initOptions = {};
|
|
if (password != null) {
|
|
Object.assign(initOptions, { user: username, password });
|
|
}
|
|
|
|
const service =
|
|
process.platform === 'win32'
|
|
? `${this.serviceName}/${this.host}`
|
|
: `${this.serviceName}@${this.host}`;
|
|
|
|
kerberos.initializeClient(service, initOptions, (err, client) => {
|
|
if (err) return callback(err, null);
|
|
|
|
self.client = client;
|
|
callback(null, client);
|
|
});
|
|
});
|
|
}
|
|
|
|
transition(payload, callback) {
|
|
if (this._transition == null) {
|
|
return callback(new Error('Transition finished'));
|
|
}
|
|
|
|
this._transition(payload, callback);
|
|
}
|
|
}
|
|
|
|
function firstTransition(auth) {
|
|
return (payload, callback) => {
|
|
auth.client.step('', (err, response) => {
|
|
if (err) return callback(err);
|
|
|
|
// Set up the next step
|
|
auth._transition = secondTransition(auth);
|
|
|
|
// Return the payload
|
|
callback(null, response);
|
|
});
|
|
};
|
|
}
|
|
|
|
function secondTransition(auth) {
|
|
return (payload, callback) => {
|
|
auth.client.step(payload, (err, response) => {
|
|
if (err && auth.retries === 0) return callback(err);
|
|
|
|
// Attempt to re-establish a context
|
|
if (err) {
|
|
// Adjust the number of retries
|
|
auth.retries = auth.retries - 1;
|
|
|
|
// Call same step again
|
|
return auth.transition(payload, callback);
|
|
}
|
|
|
|
// Set up the next step
|
|
auth._transition = thirdTransition(auth);
|
|
|
|
// Return the payload
|
|
callback(null, response || '');
|
|
});
|
|
};
|
|
}
|
|
|
|
function thirdTransition(auth) {
|
|
return (payload, callback) => {
|
|
// GSS Client Unwrap
|
|
auth.client.unwrap(payload, (err, response) => {
|
|
if (err) return callback(err, false);
|
|
|
|
// Wrap the response
|
|
auth.client.wrap(response, { user: auth.username }, (err, wrapped) => {
|
|
if (err) return callback(err, false);
|
|
|
|
// Set up the next step
|
|
auth._transition = fourthTransition(auth);
|
|
|
|
// Return the payload
|
|
callback(null, wrapped);
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
function fourthTransition(auth) {
|
|
return (payload, callback) => {
|
|
// Set the transition to null
|
|
auth._transition = null;
|
|
|
|
// Callback with valid authentication
|
|
callback(null, true);
|
|
};
|
|
}
|
|
|
|
// Set the process
|
|
module.exports = {
|
|
MongoAuthProcess
|
|
};
|