diff --git a/lib/authentication/auth_web.js b/lib/authentication/auth_web.js index 3b822d5a4..2f383affe 100644 --- a/lib/authentication/auth_web.js +++ b/lib/authentication/auth_web.js @@ -7,6 +7,7 @@ const net = require('net'); const querystring = require('querystring'); const URLUtil = require('./../../lib/url_util'); const Util = require('./../../lib/util'); +var SsoUrlProvider = require('../authentication/sso_url_provider'); /** * Creates an external browser authenticator. @@ -18,10 +19,11 @@ const Util = require('./../../lib/util'); * @returns {Object} * @constructor */ -function auth_web(connectionConfig, ssoUrlProvider, webbrowser) { +function auth_web(connectionConfig, httpClient, webbrowser) { const host = connectionConfig.host; const browserActionTimeout = connectionConfig.getBrowserActionTimeout(); + const ssoUrlProvider = new SsoUrlProvider(connectionConfig, httpClient); if (!Util.exists(host)) { throw new Error(`Invalid value for host: ${host}`); @@ -32,7 +34,6 @@ function auth_web(connectionConfig, ssoUrlProvider, webbrowser) { const open = typeof webbrowser !== "undefined" ? webbrowser : require('open'); - let ssoURL; let proofKey; let token; @@ -81,10 +82,11 @@ function auth_web(connectionConfig, ssoUrlProvider, webbrowser) { server.address().port, username, host); - ssoURL = ssoData['ssoUrl']; + proofKey = ssoData['proofKey']; // Step 2: validate URL + let ssoURL = ssoData['ssoUrl']; if (!URLUtil.isValidURL(ssoURL)) { throw new Error(util.format("Invalid SSO URL found - %s ", ssoURL)); } diff --git a/lib/authentication/authentication.js b/lib/authentication/authentication.js index 94ade0831..9ef098841 100644 --- a/lib/authentication/authentication.js +++ b/lib/authentication/authentication.js @@ -2,13 +2,13 @@ * Copyright (c) 2015-2021 Snowflake Computing Inc. All rights reserved. */ -var auth_default = require('./auth_default'); -var auth_web = require('./auth_web'); -var auth_keypair = require('./auth_keypair'); -var auth_oauth = require('./auth_oauth'); -var auth_okta = require('./auth_okta'); +const auth_default = require('./auth_default'); +const auth_web = require('./auth_web'); +const auth_keypair = require('./auth_keypair'); +const auth_oauth = require('./auth_oauth'); +const auth_okta = require('./auth_okta'); -var authenticationTypes = +const authenticationTypes = { DEFAULT_AUTHENTICATOR: 'SNOWFLAKE', // default authenticator name EXTERNAL_BROWSER_AUTHENTICATOR: 'EXTERNALBROWSER', @@ -37,9 +37,8 @@ exports.formAuthJSON = function formAuthJSON( clientType, clientVersion, clientEnv -) -{ - var body = +) { + const body = { data: { @@ -67,20 +66,19 @@ exports.formAuthJSON = function formAuthJSON( * * @returns {Object} the authenticator. */ -exports.getAuthenticator = function getAuthenticator(connectionConfig, ssoUrlProvider) -{ - var auth = connectionConfig.getAuthenticator(); +exports.getAuthenticator = function getAuthenticator(connectionConfig, httpClient) { + const auth = connectionConfig.getAuthenticator(); - if (auth == authenticationTypes.DEFAULT_AUTHENTICATOR) { + if (auth === authenticationTypes.DEFAULT_AUTHENTICATOR) { return new auth_default(connectionConfig.password); - } else if (auth == authenticationTypes.EXTERNAL_BROWSER_AUTHENTICATOR) { - return new auth_web(connectionConfig, ssoUrlProvider); + } else if (auth === authenticationTypes.EXTERNAL_BROWSER_AUTHENTICATOR) { + return new auth_web(connectionConfig, httpClient); } - if (auth == authenticationTypes.KEY_PAIR_AUTHENTICATOR) { + if (auth === authenticationTypes.KEY_PAIR_AUTHENTICATOR) { return new auth_keypair(connectionConfig.getPrivateKey(), connectionConfig.getPrivateKeyPath(), connectionConfig.getPrivateKeyPass()); - } else if (auth == authenticationTypes.OAUTH_AUTHENTICATOR) { + } else if (auth === authenticationTypes.OAUTH_AUTHENTICATOR) { return new auth_oauth(connectionConfig.getToken()); } else if (auth.startsWith('HTTPS://')) { return new auth_okta(connectionConfig.password, diff --git a/lib/services/sso_url_provider.js b/lib/authentication/sso_url_provider.js similarity index 63% rename from lib/services/sso_url_provider.js rename to lib/authentication/sso_url_provider.js index c249d7b2e..862f52a7b 100644 --- a/lib/services/sso_url_provider.js +++ b/lib/authentication/sso_url_provider.js @@ -4,19 +4,17 @@ const Util = require('../util'); const Errors = require('../errors'); -const HttpClient = require("../http/node"); -const axios = require("axios"); -const {rest} = require("../global_config"); +const { rest } = require('../global_config'); /** - * Creates a new instance of an LargeResultSetService. + * Creates a new instance of an SsoUrlProvider. * * @param {Object} connectionConfig * @param {Object} httpClient * @constructor */ function SsoUrlProvider(connectionConfig, httpClient) { - // validate input + Errors.assertInternal(Util.isObject(connectionConfig)); Errors.assertInternal(Util.isObject(httpClient)); @@ -29,49 +27,45 @@ function SsoUrlProvider(connectionConfig, httpClient) { * @param {String} authenticator * @param {String} serviceName * @param {String} account - * @param {Number} callback_port + * @param {Number} callbackPort * @param {String} user * @param {String} host * * @returns {String} the SSO URL. */ - this.getSSOURL = function (authenticator, serviceName, account, callback_port, user, host) { + this.getSSOURL = function (authenticator, serviceName, account, callbackPort, user, host) { // Create URL to send POST request to - const url = protocol + '://' + host + "/session/authenticator-request"; + const url = protocol + '://' + host + '/session/authenticator-request'; let header; if (serviceName) { header = { 'HTTP_HEADER_SERVICE_NAME': serviceName - } + }; } const body = { - "data": { - "ACCOUNT_NAME": account, - "LOGIN_NAME": user, - "PORT": port, - "PROTOCOL": protocol, - "AUTHENTICATOR": authenticator, - "BROWSER_MODE_REDIRECT_PORT": callback_port.toString() + 'data': { + 'ACCOUNT_NAME': account, + 'LOGIN_NAME': user, + 'PORT': port, + 'PROTOCOL': protocol, + 'AUTHENTICATOR': authenticator, + 'BROWSER_MODE_REDIRECT_PORT': callbackPort.toString() } }; - const httpsClient = new HttpClient(connectionConfig) - const agent = httpsClient.getAgent(url, connectionConfig.getProxy()); + const agent = httpClient.getAgent(url, connectionConfig.getProxy()); const requestOptions = { - method: 'post', - url: url, headers: header, - data: body, requestOCSP: false, rejectUnauthorized: true, httpsAgent: agent }; // Post request to get the SSO URL - return axios.request(requestOptions) + return httpClient.post(url, body, requestOptions) .then((response) => { const data = response['data']['data']; return data; diff --git a/lib/connection/connection.js b/lib/connection/connection.js index 5bda06ed1..3f0733c85 100644 --- a/lib/connection/connection.js +++ b/lib/connection/connection.js @@ -297,7 +297,7 @@ function Connection(context) var self = this; // Get authenticator to use - var auth = Authenticator.getAuthenticator(connectionConfig, context.getServices().ssoUrlProvider); + var auth = Authenticator.getAuthenticator(connectionConfig, context.getHttpClient()); try { diff --git a/lib/connection/connection_context.js b/lib/connection/connection_context.js index 93c65970f..e6df0f0d5 100644 --- a/lib/connection/connection_context.js +++ b/lib/connection/connection_context.js @@ -6,7 +6,6 @@ var Util = require('../util'); var Errors = require('../errors'); var SfService = require('../services/sf'); var LargeResultSetService = require('../services/large_result_set'); -var SsoUrlProvider = require('../services/sso_url_provider'); /** * Creates a new ConnectionContext. @@ -40,7 +39,6 @@ function ConnectionContext(connectionConfig, httpClient, config) { sf: new SfService(connectionConfig, httpClient, sfServiceConfig), largeResultSet: new LargeResultSetService(connectionConfig, httpClient), - ssoUrlProvider: new SsoUrlProvider(connectionConfig, httpClient) }; /** @@ -79,6 +77,15 @@ function ConnectionContext(connectionConfig, httpClient, config) } }; }; + /** + * Returns instance of httpClient + * + * @returns {NodeHttpClient} + */ + this.getHttpClient = function () + { + return httpClient; + }; } module.exports = ConnectionContext; \ No newline at end of file diff --git a/lib/http/base.js b/lib/http/base.js index e4c0ebf5d..1793fa01b 100644 --- a/lib/http/base.js +++ b/lib/http/base.js @@ -192,6 +192,8 @@ HttpClient.prototype.getAgent = function (url, proxy, mock) { return null; }; +HttpClient.prototype.post = axios.post; + module.exports = HttpClient; /** diff --git a/test/unit/authentication/authentication_test.js b/test/unit/authentication/authentication_test.js index 6d91c8218..add03a563 100644 --- a/test/unit/authentication/authentication_test.js +++ b/test/unit/authentication/authentication_test.js @@ -13,6 +13,7 @@ var auth_keypair = require('./../../../lib/authentication/auth_keypair'); var auth_oauth = require('./../../../lib/authentication/auth_oauth'); var auth_okta = require('./../../../lib/authentication/auth_okta'); var authenticationTypes = require('./../../../lib/authentication/authentication').authenticationTypes; +const HttpAgent = require('https').Agent; var MockTestUtil = require('./../mock/mock_test_util'); @@ -74,6 +75,7 @@ describe('external browser authentication', function () const BROWSER_ACTION_TIMEOUT = 10000; const connectionConfig= { getBrowserActionTimeout: () => BROWSER_ACTION_TIMEOUT, + getProxy: () => {}, host: 'fakehost' } @@ -89,25 +91,35 @@ describe('external browser authentication', function () return; } }); - mock('ssoUrlProvider', { - getSSOURL: async function (authenticator, serviceName, account, callback_port, user, host) { + mock('httpclient', { + getAgent: function (url, body, header) + { + return new HttpAgent(); + }, + post: async function (url, body, header) + { const data = { - ssoUrl: mockSsoURL, - proofKey: mockProofKey + data: { + data: + { + ssoUrl: mockSsoURL, + proofKey: mockProofKey + } + } } - browserRedirectPort = callback_port.toString(); + browserRedirectPort = body['data']['BROWSER_MODE_REDIRECT_PORT']; return data; } }); webbrowser = require('webbrowser'); - ssoUrlProvider = require('ssoUrlProvider'); + httpclient = require('httpclient'); }); it('external browser - authenticate method is thenable', done => { - const auth = new auth_web(connectionConfig, ssoUrlProvider, webbrowser.open); + const auth = new auth_web(connectionConfig, httpclient, webbrowser.open); auth.authenticate(credentials.authenticator, '', credentials.account, credentials.username, credentials.host) .then(done) @@ -116,7 +128,7 @@ describe('external browser authentication', function () it('external browser - get success', async function () { - const auth = new auth_web(connectionConfig, ssoUrlProvider, webbrowser.open); + const auth = new auth_web(connectionConfig, httpclient, webbrowser.open); await auth.authenticate(credentials.authenticator, '', credentials.account, credentials.username, credentials.host); var body = { data: {} }; @@ -138,21 +150,32 @@ describe('external browser authentication', function () return; } }); - mock('ssoUrlProvider', { - getSSOURL: async function (authenticator, serviceName, account, callback_port, user, host) { + + mock('httpclient', { + getAgent: function (url, body, header) + { + return new HttpAgent(); + }, + post: async function (url, body, header) + { const data = { - ssoUrl: mockSsoURL + data: { + data: + { + ssoUrl: mockSsoURL + } + } } - browserRedirectPort = callback_port.toString(); + browserRedirectPort = body['data']['BROWSER_MODE_REDIRECT_PORT']; return data; } }); webbrowser = require('webbrowser'); - ssoUrlProvider = require('ssoUrlProvider'); + httpclient = require('httpclient'); - const auth = new auth_web(connectionConfig, ssoUrlProvider, webbrowser.open); + const auth = new auth_web(connectionConfig, httpclient, webbrowser.open); await auth.authenticate(credentials.authenticator, '', credentials.account, credentials.username, credentials.host); var body = { data: {} };