Repository: tinkerpop Updated Branches: refs/heads/TINKERPOP-1977 [created] e61fcf1c6
Implementation of Sasl authentication. Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/2a8b4b4f Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/2a8b4b4f Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/2a8b4b4f Branch: refs/heads/TINKERPOP-1977 Commit: 2a8b4b4ff5cf55b82fe2af8f8390724c6688467f Parents: 675c077 Author: Matthew Allen <matt.al...@runbox.com> Authored: Fri Jul 6 20:49:54 2018 +0100 Committer: Matthew Allen <matt.al...@runbox.com> Committed: Thu Aug 23 06:33:51 2018 +0100 ---------------------------------------------------------------------- .../lib/driver/authenticator.js | 14 ++++++ .../lib/driver/driver-remote-connection.js | 44 ++++++++----------- .../lib/driver/sasl-authenticator.js | 45 ++++++++++++++++++++ .../javascript/gremlin-javascript/lib/utils.js | 23 +++++++++- 4 files changed, 99 insertions(+), 27 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2a8b4b4f/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/authenticator.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/authenticator.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/authenticator.js new file mode 100644 index 0000000..053aecd --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/authenticator.js @@ -0,0 +1,14 @@ +'use strict'; + +/** @abstract */ +class Authenticator { + constructor(credentials) { + this._credentials = credentials; + } + + evaluateChallenge(ws, header) { + throw new Error("evaluateChallenge should be implemented"); + } +} + +module.exports = Authenticator; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2a8b4b4f/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js index 0f7cedb..153c278 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js @@ -22,7 +22,6 @@ */ 'use strict'; -const crypto = require('crypto'); const WebSocket = require('ws'); const util = require('util'); const RemoteConnection = require('./remote-connection').RemoteConnection; @@ -31,7 +30,8 @@ const serializer = require('../structure/io/graph-serializer'); const responseStatusCode = { success: 200, noContent: 204, - partialContent: 206 + partialContent: 206, + authenticationChallenge: 407, }; const defaultMimeType = 'application/vnd.gremlin-v2.0+json'; @@ -48,6 +48,7 @@ class DriverRemoteConnection extends RemoteConnection { * @param {Boolean} [options.rejectUnauthorized] Determines whether to verify or not the server certificate. * @param {String} [options.traversalSource] The traversal source. Defaults to: 'g'. * @param {GraphSONWriter} [options.writer] The writer to use. + * @param {Authenticator} [options.authenticator] The authentication handler to use. * @constructor */ constructor(url, options) { @@ -76,7 +77,13 @@ class DriverRemoteConnection extends RemoteConnection { const mimeType = options.mimeType || defaultMimeType; this._header = String.fromCharCode(mimeType.length) + mimeType; this.isOpen = false; + this.connectionError = false; + this.connectionErrorMessage = ''; this.traversalSource = options.traversalSource || 'g'; + + if (options.authenticator) { + this._authenticator = options.authenticator; + } } /** @@ -102,7 +109,7 @@ class DriverRemoteConnection extends RemoteConnection { /** @override */ submit(bytecode) { return this.open().then(() => new Promise((resolve, reject) => { - const requestId = getUuid(); + const requestId = utils.getUuid(); this._responseHandlers[requestId] = { callback: (err, result) => err ? reject(err) : resolve(result), result: null @@ -112,12 +119,12 @@ class DriverRemoteConnection extends RemoteConnection { })); } - _getRequest(id, bytecode) { + _getRequest(id, bytecode, op, args) { return ({ 'requestId': { '@type': 'g:UUID', '@value': id }, - 'op': 'bytecode', + 'op': op || 'bytecode', 'processor': 'traversal', - 'args': { + 'args': args || { 'gremlin': this._writer.adaptObject(bytecode), 'aliases': { 'g': this.traversalSource } } @@ -151,7 +158,11 @@ class DriverRemoteConnection extends RemoteConnection { return; } - if (response.status.code >= 400) { + if (response.status.code === responseStatusCode.authenticationChallenge && this._authenticator) { + this._authenticator.evaluateChallenge(this._ws, this._header); + return; + } + else if (response.status.code >= 400) { // callback in error return handler.callback( new Error(util.format('Server error: %s (%d)', response.status.message, response.status.code))); @@ -203,25 +214,6 @@ class DriverRemoteConnection extends RemoteConnection { } } -function getUuid() { - const buffer = crypto.randomBytes(16); - //clear the version - buffer[6] &= 0x0f; - //set the version 4 - buffer[6] |= 0x40; - //clear the variant - buffer[8] &= 0x3f; - //set the IETF variant - buffer[8] |= 0x80; - const hex = buffer.toString('hex'); - return ( - hex.substr(0, 8) + '-' + - hex.substr(8, 4) + '-' + - hex.substr(12, 4) + '-' + - hex.substr(16, 4) + '-' + - hex.substr(20, 12)); -} - const bufferFromString = (Int8Array.from !== Buffer.from && Buffer.from) || function newBuffer(text) { return new Buffer(text, 'utf8'); }; http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2a8b4b4f/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/sasl-authenticator.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/sasl-authenticator.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/sasl-authenticator.js new file mode 100644 index 0000000..1f08b7d --- /dev/null +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/sasl-authenticator.js @@ -0,0 +1,45 @@ +'use strict'; + +const Authenticator = require('./authenticator'); +const utils = require('../utils'); + +class SaslAuthenticator extends Authenticator { + /** + * Creates a new instance of SaslAuthenticator. + * @param {Object} [credentials] The authentication credential options. + * @param {String} [credentials.username] The user for the authentication response. + * @param {String} [credentials.password] The plaintext password for authentication response. + * @constructor + */ + constructor(credentials) { + super(credentials); + } + + evaluateChallenge(ws, header) { + const message = bufferFromString(header + JSON.stringify({ + 'requestId': { '@type': 'g:UUID', '@value': utils.getUuid() }, + 'op': 'authentication', + 'processor': 'traversal', + 'args': { + 'sasl': this.saslArgument() + } + })); + + return ws.send(message); + } + + saslArgument() { + if (this._credentials.username === null || this._credentials.username.length === 0 + || this._credentials.password === null || this._credentials.password.length === 0 ) { + return ''; + } + return new Buffer(`\0${this._credentials.username}\0${this._credentials.password}`).toString('base64'); + } +} + + +const bufferFromString = (Int8Array.from !== Buffer.from && Buffer.from) || function newBuffer(text) { + return new Buffer(text, 'utf8'); +}; + +module.exports = SaslAuthenticator; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/2a8b4b4f/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js ---------------------------------------------------------------------- diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js index e3a001c..7abc5fe 100644 --- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js +++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js @@ -23,6 +23,8 @@ */ 'use strict'; +const crypto = require('crypto'); + exports.toLong = function toLong(value) { return new Long(value); }; @@ -32,4 +34,23 @@ const Long = exports.Long = function Long(value) { throw new TypeError('Ty') } this.value = value.toString(); -}; \ No newline at end of file +}; + +exports.getUuid = function getUuid() { + const buffer = crypto.randomBytes(16); + //clear the version + buffer[6] &= 0x0f; + //set the version 4 + buffer[6] |= 0x40; + //clear the variant + buffer[8] &= 0x3f; + //set the IETF variant + buffer[8] |= 0x80; + const hex = buffer.toString('hex'); + return ( + hex.substr(0, 8) + '-' + + hex.substr(8, 4) + '-' + + hex.substr(12, 4) + '-' + + hex.substr(16, 4) + '-' + + hex.substr(20, 12)); +} \ No newline at end of file