Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1799619 execute automated external browser tests #969

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/parameters_aws_auth_tests.json.gpg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Œ ÚÿPþ™tÿÒÀ°„än4í"û­¦KxÉ{3Ëq—¾rºÔÖÝ*V}hý I§V_FÕ-…я[‹‘ôqìéx·/ÝÒ,»0¸NÀ£j„°Äp:kŽ~±0öí‹Þ;0¶fpëttäõüQF)qÆv5ñÊsÙ¨»â°þÀ>P~TyG;Jÿb¶ë®àoŒÅ/S’Œú’Ç ãu¦ÕNa¿nêŽlNÿÛátG¥Ìîñz–-óTž–’”á§ìµõ<S릃Nbù³ãjbÇÏYle¢? 9‡¹ë¼½VçHÓ¡<y˜“
¤Æ D[jï‚sE÷?J^ßyžâùúuÒŸm_§kLË™èXv¾¾žâ=zYÖb^9Ù$2Æ‹cÇÕeCŸ]2¯!ªðÔ.®ô¼¢2YÇyû/þ¢H'uÁ.húÌo6³Ãj}ª3ÑÞIb<wÂC_rù¦3ý]ýgÃö2ÃþíAݹ³oF
éÔ™üýèä^Œ
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*.swp
.idea
.git
parameters.json
parameters*.json
snowflake-sdk-*.tgz
dist
junit*.xml
Expand Down
72 changes: 45 additions & 27 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,55 @@ import groovy.json.JsonOutput
timestamps {
node('regular-memory-node') {
stage('checkout') {
cleanWs()
scmInfo = checkout scm
println("${scmInfo}")
env.GIT_BRANCH = scmInfo.GIT_BRANCH
env.GIT_COMMIT = scmInfo.GIT_COMMIT
}

stage('Build') {
withCredentials([
usernamePassword(credentialsId: '063fc85b-62a6-4181-9d72-873b43488411', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'),
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c',variable: 'NEXUS_PASSWORD')
]) {
sh '''\
|#!/bin/bash -e
|export GIT_BRANCH=${GIT_BRANCH}
|export GIT_COMMIT=${GIT_COMMIT}
|$WORKSPACE/ci/build.sh
'''.stripMargin()
parallel(
'Build and Test': {
stage('Build') {
withCredentials([
usernamePassword(credentialsId: '063fc85b-62a6-4181-9d72-873b43488411', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY'),
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c', variable: 'NEXUS_PASSWORD')
]) {
sh '''\
|#!/bin/bash -e
|export GIT_BRANCH=${GIT_BRANCH}
|export GIT_COMMIT=${GIT_COMMIT}
|$WORKSPACE/ci/build.sh
'''.stripMargin()
}
}
stage('Test') {
def params = [
string(name: 'svn_revision', value: 'main'),
string(name: 'branch', value: 'main'),
string(name: 'client_git_commit', value: scmInfo.GIT_COMMIT),
string(name: 'client_git_branch', value: scmInfo.GIT_BRANCH),
string(name: 'TARGET_DOCKER_TEST_IMAGE', value: 'nodejs-chainguard-node18'),
string(name: 'parent_job', value: env.JOB_NAME),
string(name: 'parent_build_number', value: env.BUILD_NUMBER)
]
build job: 'RT-LanguageNodeJS-PC', parameters: params
}
},
'Test Authentication': {
stage('Test Authentication') {
withCredentials([
string(credentialsId: 'a791118f-a1ea-46cd-b876-56da1b9bc71c', variable: 'NEXUS_PASSWORD'),
string(credentialsId: 'sfctest0-parameters-secret', variable: 'PARAMETERS_SECRET')
]) {
sh '''\
|#!/bin/bash -e
|$WORKSPACE/ci/test_authentication.sh
'''.stripMargin()
}
}
}
}
params = [
string(name: 'svn_revision', value: 'main'),
string(name: 'branch', value: 'main'),
string(name: 'client_git_commit', value: scmInfo.GIT_COMMIT),
string(name: 'client_git_branch', value: scmInfo.GIT_BRANCH),
string(name: 'TARGET_DOCKER_TEST_IMAGE', value: 'nodejs-chainguard-node18'),
string(name: 'parent_job', value: env.JOB_NAME),
string(name: 'parent_build_number', value: env.BUILD_NUMBER)
]
stage('Test') {
build job: 'RT-LanguageNodeJS-PC',parameters: params
}
)
}
}

Expand All @@ -61,7 +79,7 @@ pipeline {
}

def wgetUpdateGithub(String state, String folder, String targetUrl, String seconds) {
def ghURL = "https://api.github.com/repos/snowflakedb/snowflake-connector-nodejs/statuses/$COMMIT_SHA_LONG"
def data = JsonOutput.toJson([state: "${state}", context: "jenkins/${folder}",target_url: "${targetUrl}"])
sh "wget ${ghURL} --spider -q --header='Authorization: token $GIT_PASSWORD' --post-data='${data}'"
def ghURL = "https://api.github.com/repos/snowflakedb/snowflake-connector-nodejs/statuses/$COMMIT_SHA_LONG"
def data = JsonOutput.toJson([state: "${state}", context: "jenkins/${folder}", target_url: "${targetUrl}"])
sh "wget ${ghURL} --spider -q --header='Authorization: token $GIT_PASSWORD' --post-data='${data}'"
}
9 changes: 9 additions & 0 deletions ci/container/test_authentication.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash -e

set -o pipefail

AUTH_PARAMETER_FILE=./.github/workflows/parameters_aws_auth_tests.json
eval $(jq -r '.authtestparams | to_entries | map("export \(.key)=\(.value|tostring)")|.[]' $AUTH_PARAMETER_FILE)

npm install
npm run test:authentication
14 changes: 14 additions & 0 deletions ci/test_authentication.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash -e

set -o pipefail
THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export WORKSPACE=${WORKSPACE:-/tmp}

gpg --quiet --batch --yes --decrypt --passphrase="$PARAMETERS_SECRET" --output $THIS_DIR/../.github/workflows/parameters_aws_auth_tests.json "$THIS_DIR/../.github/workflows/parameters_aws_auth_tests.json.gpg"

docker run \
-v $(cd $THIS_DIR/.. && pwd):/mnt/host \
-v $WORKSPACE:/mnt/workspace \
--rm \
nexus.int.snowflakecomputing.com:8086/docker/snowdrivers-test-external-browser:1 \
"/mnt/host/ci/container/test_authentication.sh"
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"lint:check:all:errorsOnly": "npm run lint:check:all -- --quiet",
"lint:fix": "eslint --fix",
"test": "mocha -timeout 180000 --recursive --full-trace test/unit/**/*.js test/unit/*.js",
"test:authentication": "mocha --exit -timeout 180000 --recursive --full-trace test/authentication/**/*.js test/authentication/*.js",
"test:integration": "mocha -timeout 180000 --recursive --full-trace test/integration/**/*.js test/integration/*.js",
"test:single": "mocha -timeout 180000 --full-trace",
"test:system": "mocha -timeout 180000 --recursive --full-trace system_test/*.js",
Expand Down
30 changes: 30 additions & 0 deletions test/authentication/connectionParameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const snowflakeAuthTestProtocol = process.env.SNOWFLAKE_AUTH_TEST_PROTOCOL;
const snowflakeAuthTestHost = process.env.SNOWFLAKE_AUTH_TEST_HOST;
const snowflakeAuthTestPort = process.env.SNOWFLAKE_AUTH_TEST_PORT;
const snowflakeAuthTestAccount = process.env.SNOWFLAKE_AUTH_TEST_ACCOUNT;
const snowflakeAuthTestRole = process.env.SNOWFLAKE_AUTH_TEST_ROLE;
const snowflakeTestBrowserUser = process.env.SNOWFLAKE_AUTH_TEST_BROWSER_USER;
const snowflakeAuthTestOktaPass = process.env.SNOWFLAKE_AUTH_TEST_OKTA_PASS;
const snowflakeAuthTestDatabase = process.env.SNOWFLAKE_AUTH_TEST_DATABASE;
const snowflakeAuthTestWarehouse = process.env.SNOWFLAKE_AUTH_TEST_WAREHOUSE;
const snowflakeAuthTestSchema = process.env.SNOWFLAKE_AUTH_TEST_SCHEMA;

const accessUrlAuthTests = snowflakeAuthTestProtocol + '://' + snowflakeAuthTestHost + ':' +
snowflakeAuthTestPort;

const externalBrowser =
{
accessUrl: accessUrlAuthTests,
username: snowflakeTestBrowserUser,
account: snowflakeAuthTestAccount,
role: snowflakeAuthTestRole,
host: snowflakeAuthTestHost,
warehouse: snowflakeAuthTestWarehouse,
database: snowflakeAuthTestDatabase,
schema: snowflakeAuthTestSchema,
authenticator: 'EXTERNALBROWSER'
};

exports.externalBrowser = externalBrowser;
exports.snowflakeTestBrowserUser = snowflakeTestBrowserUser;
exports.snowflakeAuthTestOktaPass = snowflakeAuthTestOktaPass;
116 changes: 116 additions & 0 deletions test/authentication/testExternalBrowser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const snowflake = require('../../lib/snowflake');
const assert = require('assert');
const testUtil = require('../integration/testUtil');
const connParameters = require('./connectionParameters');
const { exec, spawn } = require('child_process');

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


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

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

it('Successful connection', async () => {
const connectionOption = { ...connParameters.externalBrowser, clientStoreTemporaryCredential: false };
const connection = await snowflake.createConnection(connectionOption);
const connectAsyncPromise = connection.connectAsync(function (err, connection) {
connectionHandler(err, connection);
});
const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'success', login, password], 15000);
await Promise.all([connectAsyncPromise, provideCredentialsPromise]);
});

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

connection.connectAsync(function (err, connection) {
connectionHandler(err, connection);
});
const provideCredentialsPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'fail', login, password]);
await Promise.all([provideCredentialsPromise]);

});

it('External browser timeout', async () => {
const connectionOption = { ...connParameters.externalBrowser, browserActionTimeout: 100, clientStoreTemporaryCredential: false };
const connection = await snowflake.createConnection(connectionOption);

const connectAsyncPromise = connection.connectAsync(function (err, connection) {
timeoutConnectionHandler(err, connection);
});

const connectToBrowserPromise = execWithTimeout('node', [provideBrowserCredentialsPath, 'timeout']);
await Promise.all([connectAsyncPromise, connectToBrowserPromise]);
});
});
});

async function timeoutConnectionHandler(err, timeout) {
try {
assert.ok(err, `Browser action timed out after ${timeout} ms.`);
} catch (err){
await assert.fail(err);
}
}

async function cleanBrowserProcesses() {
exec('pkill -f chromium');
exec('pkill -f xdg-open');
}

function connectionHandler(err, connection) {
assert.ok(connection.isUp(), 'Connection is not active');
testUtil.destroyConnection(connection, function () {
try {
assert.ok(!connection.isUp(), 'Connection is not closed');
} catch (err) {
assert.fail(err);
}
});
}

function execWithTimeout(command, args, timeout = 5000) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { shell: true });

let stdout = '';
let stderr = '';

child.stdout.on('data', (data) => {
stdout += data;
});

child.stderr.on('data', (data) => {
stderr += data;
});

child.on('error', (err) => {
reject(err);
});

child.on('close', (code) => {
if (code !== 0) {
reject(new Error(`Provide browser credentials process exited with code: ${code}, error: ${stderr}`));
} else {
resolve({ stdout, stderr });
}
});

setTimeout(() => {
child.kill();
reject(new Error('Provide browser credentials process timed out'));
}, timeout);
});
}
65 changes: 0 additions & 65 deletions test/integration/testManualConnection.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,71 +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 test - external browser', function () {
it('Simple Connect', function (done) {
const connection = snowflake.createConnection(
connOption.externalBrowser
);

connection.connectAsync(function (err, connection) {
try {
assert.ok(connection.isUp(), 'not active');
testUtil.destroyConnection(connection, function () {
try {
assert.ok(!connection.isUp(), 'not active');
done();
} catch (err) {
done(err);
}
});
} catch (err) {
done(err);
}
});
});

it('Connect - external browser timeout', function (done) {
const connection = snowflake.createConnection(
connOption.externalBrowserWithShortTimeout
);

connection.connectAsync(function (err) {
try {
const browserActionTimeout =
connOption.externalBrowserWithShortTimeout.browserActionTimeout;
assert.ok(
err,
`Browser action timed out after ${browserActionTimeout} ms.`
);
done();
} catch (err) {
done(err);
}
});
});

it('Mismatched Username', function (done) {
const connection = snowflake.createConnection(
connOption.externalBrowserMismatchUser
);
connection.connectAsync(function (err) {
try {
assert.ok(
err,
'Logged in with different user than one on connection string'
);
assert.equal(
'The user you were trying to authenticate as differs from the user currently logged in at the IDP.',
err['message']
);
done();
} catch (err) {
done(err);
}
});
});
});

describe('Connection - ID Token authenticator', function () {
const connectionOption = { ...connOption.externalBrowser, clientStoreTemporaryCredential: true };
const key = Util.buildCredentialCacheKey(connectionOption.host, connectionOption.username, 'ID_TOKEN');
Expand Down
Loading