Skip to content

Commit

Permalink
execute automated external browser tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-akolodziejczyk committed Nov 22, 2024
1 parent bada542 commit baefeeb
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 93 deletions.
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
16 changes: 16 additions & 0 deletions ci/test_authentication.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/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 rmi -f nexus.int.snowflakecomputing.com:8086/docker/snowdrivers-test-external-browser:1
docker pull nexus.int.snowflakecomputing.com:8086/docker/snowdrivers-test-external-browser:1
docker run \
-v $(cd $THIS_DIR/.. && pwd):/mnt/host/auth \
-v $WORKSPACE:/mnt/workspace/auth \
--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

0 comments on commit baefeeb

Please sign in to comment.