Skip to content

Commit

Permalink
add tests for token authentication, more detailed assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-akolodziejczyk committed Nov 26, 2024
1 parent 89979c4 commit 61489ad
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 151 deletions.
121 changes: 102 additions & 19 deletions test/authentication/testExternalBrowser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,54 @@ const assert = require('assert');
const testUtil = require('../integration/testUtil');
const connParameters = require('./connectionParameters');
const { exec, spawn } = require('child_process');
const Util = require('../../lib/util');
const JsonCredentialManager = require('../../lib/authentication/secure_storage/json_credential_manager');

describe('External browser authentication tests', function () {
const provideBrowserCredentialsPath = '/externalbrowser/provideBrowserCredentials.js';
const login = connParameters.snowflakeTestBrowserUser;
const password = connParameters.snowflakeAuthTestOktaPass;
let connection;
let connection, error;

describe('External browser tests', async () => {
before(async () => {
await cleanBrowserProcesses();
});
before(async () => {
await cleanBrowserProcesses();
});

afterEach(async () => {
await cleanBrowserProcesses();
if (connection !== undefined && connection.isUp()) {
await testUtil.destroyConnectionAsync(connection);
}
});
afterEach(async () => {
await cleanBrowserProcesses();
await destroyConnection(connection);
error = undefined;
});

describe('External browser tests', async () => {
it('Successful connection', async () => {
const connectionOption = { ...connParameters.externalBrowser, clientStoreTemporaryCredential: false };
connection = await snowflake.createConnection(connectionOption);
const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'success', login, password], 15000);
await Promise.allSettled([connection.connectAsync(function () {}), provideCredentialsPromise]);
await Promise.allSettled([connection.connectAsync(connectAsyncCallback()), provideCredentialsPromise]);
verifyNoErrorWasThrown();
await verifyConnectionIsUp(connection);
});

it('Mismatched Username', async () => {
const connectionOption = { ...connParameters.externalBrowser, username: 'differentUsername', clientStoreTemporaryCredential: false };
connection = await snowflake.createConnection(connectionOption);
const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'success', login, password], 15000);
await Promise.allSettled([connection.connectAsync(connectAsyncCallback()), provideCredentialsPromise]);
assert.strictEqual(error?.message, 'The user you were trying to authenticate as differs from the user currently logged in at the IDP.');
await verifyConnectionIsNotUp(connection, 'Unable to perform operation using terminated connection.');
});

it('Wrong credentials', async () => {
const login = 'itsnotanaccount.com';
const password = 'fakepassword';
const connectionOption = { ...connParameters.externalBrowser, browserActionTimeout: 5000, clientStoreTemporaryCredential: false };
const connectionOption = { ...connParameters.externalBrowser, browserActionTimeout: 10000, clientStoreTemporaryCredential: false };
connection = await snowflake.createConnection(connectionOption);

const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'fail', login, password]);
await Promise.allSettled([connection.connectAsync(function () {}), provideCredentialsPromise]);
const results = await Promise.allSettled([connection.connectAsync(connectAsyncCallback()), provideCredentialsPromise]);
assert.strictEqual(results[1].status, 'fulfilled');
assert.strictEqual(error?.message, 'Error while getting SAML token: Browser action timed out after 10000 ms.');
await verifyConnectionIsNotUp(connection);
});

Expand All @@ -46,10 +59,72 @@ describe('External browser authentication tests', function () {
connection = await snowflake.createConnection(connectionOption);

const connectToBrowserPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'timeout']);
await Promise.allSettled([connection.connectAsync(function () {}), connectToBrowserPromise]);
await Promise.allSettled([connection.connectAsync(connectAsyncCallback()), connectToBrowserPromise]);
assert.strictEqual(error?.message, 'Error while getting SAML token: Browser action timed out after 100 ms.');
await verifyConnectionIsNotUp(connection);
});
});

describe('ID Token authentication tests', async () => {
const connectionOption = { ...connParameters.externalBrowser, clientStoreTemporaryCredential: true };
const key = Util.buildCredentialCacheKey(connectionOption.host, connectionOption.username, 'ID_TOKEN');
const defaultCredentialManager = new JsonCredentialManager();
let firstIdToken;

before(async () => {
await defaultCredentialManager.remove(key);
await cleanBrowserProcesses();
});

afterEach(async () => {
await cleanBrowserProcesses();
await destroyConnection(connection);
});

it('obtains the id token from the server and saves it on the local storage', async function () {
connection = snowflake.createConnection(connectionOption);
const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'success', login, password], 15000);
await Promise.allSettled([connection.connectAsync(connectAsyncCallback()), provideCredentialsPromise]);
verifyNoErrorWasThrown();
await verifyConnectionIsUp(connection);
});

it('the token is saved in the credential manager', async function () {
firstIdToken = await defaultCredentialManager.read(key);
assert.notStrictEqual(firstIdToken, null);
});

it('authenticates by token, browser credentials not needed', async function () {
connection = snowflake.createConnection(connectionOption);
await connection.connectAsync(connectAsyncCallback());
verifyNoErrorWasThrown();
await verifyConnectionIsUp(connection);
});

it('opens browser okta authentication again when token is incorrect', async function () {
await defaultCredentialManager.write(key, '1234');
connection = snowflake.createConnection(connectionOption);
const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'success', login, password], 15000);
await Promise.allSettled([connection.connectAsync(connectAsyncCallback()), provideCredentialsPromise]);
verifyNoErrorWasThrown();
await verifyConnectionIsUp(connection);
});

it('refreshes the token for credential cache key', async function () {
const newToken = await defaultCredentialManager.read(key);
assert.notStrictEqual(firstIdToken, newToken);
});
});

function connectAsyncCallback() {
return function (err) {
error = err;
};
}

function verifyNoErrorWasThrown() {
assert.equal(error, null);
}
});

async function cleanBrowserProcesses() {
Expand All @@ -58,17 +133,25 @@ async function cleanBrowserProcesses() {
}

async function verifyConnectionIsUp(connection) {
assert.ok(await connection.isValidAsync());
assert.ok(connection.isUp(), 'Connection is not up');
assert.ok(await connection.isValidAsync(), 'Connection is not valid');
await testUtil.executeCmdAsync(connection, 'Select 1');
}

async function verifyConnectionIsNotUp(connection) {
async function verifyConnectionIsNotUp(connection, message = 'Unable to perform operation because a connection was never established.') {
assert.ok(!(connection.isUp()), 'Connection should not be up');
assert.ok(!(await connection.isValidAsync()), 'Connection should not be valid');
try {
assert(!(await connection.isValidAsync()));
await testUtil.executeCmdAsync(connection, 'Select 1');
assert.fail('Expected error was not thrown');
} catch (error) {
assert.strictEqual(error.message, 'Unable to perform operation because a connection was never established.');
assert.strictEqual(error.message, message);
}
}

async function destroyConnection(connection) {
if (connection !== undefined && connection.isUp() && connection.isValidAsync()) {
await testUtil.destroyConnectionAsync(connection);
}
}

Expand Down
30 changes: 0 additions & 30 deletions test/integration/connectionOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const snowflakeTestRole = process.env.SNOWFLAKE_TEST_ROLE;
const snowflakeTestPassword = process.env.SNOWFLAKE_TEST_PASSWORD;
const snowflakeTestAdminUser = process.env.SNOWFLAKE_TEST_ADMIN_USER;
const snowflakeTestAdminPassword = process.env.SNOWFLAKE_TEST_ADMIN_PASSWORD;
const snowflakeTestBrowserUser = process.env.SNOWFLAKE_TEST_BROWSER_USER;
const snowflakeTestPrivateKeyUser = process.env.SNOWFLAKE_JWT_TEST_USER;
const snowflakeTestPrivateKey = process.env.SNOWFLAKE_TEST_PRIVATE_KEY;
const snowflakeTestPrivateKeyPath = process.env.SNOWFLAKE_TEST_PRIVATE_KEY_PATH;
Expand Down Expand Up @@ -89,32 +88,6 @@ const wrongPwd =
account: snowflakeTestAccount
};

const externalBrowser =
{
accessUrl: accessUrl,
username: snowflakeTestBrowserUser,
account: snowflakeTestAccount,
warehouse: snowflakeTestWarehouse,
database: snowflakeTestDatabase,
schema: snowflakeTestSchema,
role: snowflakeTestRole,
host: snowflakeTestHost,
authenticator: 'EXTERNALBROWSER'
};

const externalBrowserWithShortTimeout = {
...externalBrowser,
browserActionTimeout: 100,
};

const externalBrowserMismatchUser =
{
accessUrl: accessUrl,
username: 'node',
account: snowflakeTestAccount,
authenticator: 'EXTERNALBROWSER'
};

const keypairPrivateKey =
{
accessUrl: accessUrl,
Expand Down Expand Up @@ -233,9 +206,6 @@ exports.wrongUserName = wrongUserName;
exports.wrongPwd = wrongPwd;
exports.accessUrl = accessUrl;
exports.account = snowflakeTestAccount;
exports.externalBrowser = externalBrowser;
exports.externalBrowserWithShortTimeout = externalBrowserWithShortTimeout;
exports.externalBrowserMismatchUser = externalBrowserMismatchUser;
exports.keypairPrivateKey = keypairPrivateKey;
exports.keypairPathEncrypted = keypairPathEncrypted;
exports.keypairPathUnencrypted = keypairPathUnencrypted;
Expand Down
102 changes: 0 additions & 102 deletions test/integration/testManualConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,78 +13,6 @@ const JsonCredentialManager = require('../../lib/authentication/secure_storage/j

if (process.env.RUN_MANUAL_TESTS_ONLY === 'true') {
describe('Run manual tests', function () {
describe('Connection - ID Token authenticator', function () {
const connectionOption = { ...connOption.externalBrowser, clientStoreTemporaryCredential: true };
const key = Util.buildCredentialCacheKey(connectionOption.host, connectionOption.username, 'ID_TOKEN');
const defaultCredentialManager = new JsonCredentialManager();
let oldToken;
before( async () => {
await defaultCredentialManager.remove(key);
});

it('test - obtain the id token from the server and save it on the local storage', function (done) {
const connection = snowflake.createConnection(connectionOption);
connection.connectAsync(function (err) {
try {
assert.ok(!err);
done();
} catch (err){
done(err);
}
});
});

it('test - the token is saved in the credential manager correctly', function (done) {
defaultCredentialManager.read(key).then((idToken) => {
try {
oldToken = idToken;
assert.notStrictEqual(idToken, null);
done();
} catch (err){
done(err);
}
});
});


// Web Browser should not be open.
it('test - id token authentication', function (done) {
const idTokenConnection = snowflake.createConnection(connectionOption);
try {
idTokenConnection.connectAsync(function (err) {
assert.ok(!err);
done();
});
} catch (err) {
done(err);
}
});

// Web Browser should be open.
it('test - id token reauthentication', function (done) {
defaultCredentialManager.write(key, '1234').then(() => {
const wrongTokenConnection = snowflake.createConnection(connectionOption);
wrongTokenConnection.connectAsync(function (err) {
assert.ok(!err);
done();
});
});
});

//Compare two idToken. Those two should be different.
it('test - the token is refreshed', function (done) {
oldToken = undefined;
defaultCredentialManager.read(key).then((idToken) => {
try {
assert.notStrictEqual(idToken, oldToken);
done();
} catch (err) {
done(err);
}
});
});
});

describe('Connection test - oauth', function () {
it('Simple Connect', function (done) {
const connection = snowflake.createConnection(connOption.oauth);
Expand Down Expand Up @@ -136,36 +64,6 @@ if (process.env.RUN_MANUAL_TESTS_ONLY === 'true') {
});
});

describe('Connection test - okta', function () {
it('Simple Connect', function (done) {
const connection = snowflake.createConnection(connOption.okta);

async.series([
function (callback) {
connection.connectAsync(function (err) {
done(err);
assert.ok(!err, JSON.stringify(err));
callback();
});
},
function (callback) {
assert.ok(connection.isUp(), 'not active');
callback();
},
function (callback) {
connection.destroy(function (err) {
assert.ok(!err, JSON.stringify(err));
callback();
});
},
function (callback) {
assert.ok(!connection.isUp(), 'still active');
callback();
},
]);
});
});

describe('Connection - MFA authenticator with DUO', function () {
const connectionOption = connOption.MFA;

Expand Down

0 comments on commit 61489ad

Please sign in to comment.