Skip to content

Commit

Permalink
Merge branch 'improvement/ZENKO-3925-add-e2e-iam-policies-tests-assum…
Browse files Browse the repository at this point in the history
…rRoleWII' into q/2.3
  • Loading branch information
bert-e committed Feb 18, 2022
2 parents e2a6252 + 915a3b8 commit d7f2202
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 2 deletions.
14 changes: 14 additions & 0 deletions eve/workers/end2end/scripts/run-e2e-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ ZENKO_ACCESS_KEY=$(kubectl get secret end2end-account-zenko -o jsonpath='{.data.
ZENKO_SECRET_KEY=$(kubectl get secret end2end-account-zenko -o jsonpath='{.data.SecretAccessKey}' | base64 -d)
ZENKO_SESSION_TOKEN=$(kubectl get secret end2end-account-zenko -o jsonpath='{.data.SessionToken}' | base64 -d)
OIDC_FULLNAME="${OIDC_FIRST_NAME} ${OIDC_LAST_NAME}"
KEYCLOAK_TEST_USER="${OIDC_USERNAME}-norights"
KEYCLOAK_TEST_PASSWORD=${OIDC_PASSWORD}
KEYCLOAK_TEST_HOST=${OIDC_ENDPOINT}
KEYCLOAK_TEST_PORT="80"
KEYCLOAK_TEST_REALM_NAME=${OIDC_REALM}
KEYCLOAK_TEST_CLIENT_ID=${OIDC_CLIENT_ID}
KEYCLOAK_TEST_GRANT_TYPE="password"

run_e2e_test() {
kubectl run ${1} ${POD_NAME} \
Expand Down Expand Up @@ -75,6 +82,13 @@ run_e2e_test() {
--env=RING_S3C_ENDPOINT=${RING_S3C_ENDPOINT} \
--env=RING_S3C_BACKEND_SOURCE_LOCATION=${RING_S3C_BACKEND_SOURCE_LOCATION} \
--env=RING_S3C_INGESTION_SRC_BUCKET_NAME=${RING_S3C_INGESTION_SRC_BUCKET_NAME} \
--env=KEYCLOAK_TEST_USER=${KEYCLOAK_TEST_USER} \
--env=KEYCLOAK_TEST_PASSWORD=${KEYCLOAK_TEST_PASSWORD} \
--env=KEYCLOAK_TEST_HOST=${KEYCLOAK_TEST_HOST} \
--env=KEYCLOAK_TEST_PORT=${KEYCLOAK_TEST_PORT} \
--env=KEYCLOAK_TEST_REALM_NAME=${KEYCLOAK_TEST_REALM_NAME} \
--env=KEYCLOAK_TEST_CLIENT_ID=${KEYCLOAK_TEST_CLIENT_ID} \
--env=KEYCLOAK_TEST_GRANT_TYPE=${KEYCLOAK_TEST_GRANT_TYPE} \
--command -- sh -c "${2}"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,10 @@ describe('iam policies - cloudserver - AssumeRole - Metadata', () => {
secretAccessKey: res.Credentials.SecretAccessKey,
sessionToken: res.Credentials.SessionToken,
};
return async.eachOf(test.buckets, (path, idx, eachCb) => {
return async.eachOf(test.buckets, (bucket, idx, eachCb) => {
// make metadataSearch request on specific buckets using session user's credentials
// and see if can get the correct response
metadataSearchResponseCode(sessionUserCredentials, test.buckets[idx], (err, res) => {
metadataSearchResponseCode(sessionUserCredentials, bucket, (err, res) => {
if (err) {
assert.ifError(err);
return done(err);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const assert = require('assert');
const async = require('async');
const VaultClient = require('../../VaultClient');
const { getS3Client } = require('../../s3SDK');
const { getSTSClient } = require('../../stsSDK');
const { getTokenForIdentity } = require('../../utils/getWebIdentityToken');
const { metadataSearchResponseCode } = require('./utils');

let iamClient = null;
let stsClient = null;
let s3Client = null;

const clientAdmin = VaultClient.getAdminClient();
const accountName = 'AccountTest';
const accountInfo = {
email: `${accountName}@test.com`,
password: 'test',
};
const externalAccessKey = 'DZMMJUPWIUK8IWXRP0HQ';
const externalSecretKey = 'iTuJdlidzrLipymvAGrLP66Yxghl4NQxLZR3cLlu';

const duration = '1000';

const bucket1 = 'bucket1';

const storageManagerName = 'storage_manager';
const storageAccountOwnerName = 'storage_account_owner';
const dataConsumerName = 'data_consumer';
const storageManagerRole = 'storage-manager-role';
const storageAccountOwnerRole = 'storage-account-owner-role';
const dataConsumerRole = 'data-consumer-role';

describe('iam policies - cloudserver - AssumeRoleWithWebIdentity - Metadata', () => {

before(done => {
async.series([
// create an account, generateAccountAccessKey for it
// get iam client, sts client and s3 client of this account
next => clientAdmin.createAccount(accountName, accountInfo, next),
next => clientAdmin.generateAccountAccessKey(accountName, next, { externalAccessKey, externalSecretKey }),
next => {
iamClient = VaultClient.getIamClient(externalAccessKey, externalSecretKey);
stsClient = getSTSClient(externalAccessKey, externalSecretKey);
s3Client = getS3Client(externalAccessKey, externalSecretKey);
next();
},
// use s3 client to create a bucket and put 2 objects
next => {
async.series([
next => s3Client.createBucket({ Bucket: bucket1 }, next),
next => s3Client.putObject({ Bucket: bucket1, Key: 'file1' }, next),
next => s3Client.putObject({ Bucket: bucket1, Key: 'file2' }, next),
], next);
},
], done);
});

after(done => {
async.series([
next => s3Client.deleteObjects({
Bucket: bucket1,
Delete: {
Objects: [{ Key: 'file1' }, { Key: 'file2' }],
Quiet: false,
},
}, next),
next => s3Client.deleteBucket({ Bucket: bucket1 }, next),
next => clientAdmin.deleteAccount(accountName, next),
], done);
});


const tests = [
{
name: 'should be able to perform metadata search on all buckets for storage manager role',
oidcIdentity: storageManagerName,
roleName: storageManagerRole,
assertion: result => assert.strictEqual(result.statusCode, 200),
},
{
name: 'should be able to perform metadata search on all buckets for storage account owner role',
oidcIdentity: storageAccountOwnerName,
roleName: storageAccountOwnerRole,
assertion: result => assert.strictEqual(result.statusCode, 200),
},
{
name: 'should be able to perform metadata search on all buckets for data consumer role',
oidcIdentity: dataConsumerName,
roleName: dataConsumerRole,
assertion: result => assert.strictEqual(result.statusCode, 200),
},
];

tests.forEach((test, i) => {
it(test.name, done => {
let jwtToken = null;
let roleArn = null;
async.waterfall([
next => getTokenForIdentity(test.oidcIdentity, (err, token) => {
assert.ifError(err);
jwtToken = token;
next();
}),
next => iamClient.getRole({ RoleName: test.roleName }, (err, res) => {
assert.ifError(err);
roleArn = res.Role.Arn;
next();
}),
next => stsClient.assumeRoleWithWebIdentity({
RoleArn: roleArn,
DurationSeconds: duration,
WebIdentityToken: jwtToken,
RoleSessionName: `session-name-test-${i}`,
}, (err, res) => {
assert.ifError(err);
return next(null, res);
}),
(res, next) => {
const sessionUserCredentials = {
accessKeyId: res.Credentials.AccessKeyId,
secretAccessKey: res.Credentials.SecretAccessKey,
sessionToken: res.Credentials.SessionToken,
};
// make metadataSearch request using session user's credentials
// and see if can get the correct response
metadataSearchResponseCode(sessionUserCredentials, bucket1, (err, res) => {
if (err) {
assert.ifError(err);
return done(err);
}
test.assertion(res);
return next();
});
}], done);
});
});
});
85 changes: 85 additions & 0 deletions tests/zenko_tests/node_tests/utils/getWebIdentityToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const querystring = require("querystring");
const http = require("http");
const assert = require('assert');

const USER_1_PASSWORD = process.env.KEYCLOAK_TEST_PASSWORD || '123';
const HOST_1_URL = process.env.KEYCLOAK_TEST_HOST || 'http://keycloak.zenko.local';
const HOST_1_PORT = parseInt(process.env.KEYCLOAK_TEST_PORT, 10) || 80;
const REALM_NAME = process.env.KEYCLOAK_TEST_REALM_NAME || 'zenko';
const KEYCLOAK_PATH = `/auth/realms/${REALM_NAME}/protocol/openid-connect/token`;
const CLIENT_ID = process.env.KEYCLOAK_TEST_CLIENT_ID || 'zenko-ui';
const GRANT_TYPE = process.env.KEYCLOAK_TEST_GRANT_TYPE || 'password';


function getTokenForIdentity(identity, callback) {
getWebIdentityToken(identity, USER_1_PASSWORD, HOST_1_URL,
HOST_1_PORT, KEYCLOAK_PATH, CLIENT_ID, GRANT_TYPE, (err, token) => {
assert(err === null);
callback(err, token);
});
}


/**
* HTTP client to request JWT token given the username and password.
*
* @param {string} username - username of user requesting token
* @param {string} password - password of user requesting token
* @param {string} host - host URL of keycloak service
* @param {number} port - port of keycloak service
* @param {string} path - path of keycloak service authentication API
* @param {string} client_id - id of the client of the user
* @param {string} grant_type - grant of the user
* @param {function} callback - callback function called with error or result
* @returns {undefined} undefined
*/
function getWebIdentityToken(username, password, host, port, path,
client_id, grant_type, callback) {
// In Zenko, we are using an endpoint as the `KEYCLOAK_TEST_HOST` env variable
// So we should remove any existing http of https prefix in HOST_1_URL.
host = host.replace('https://', '').replace('http://', '');
const userData = querystring.stringify({
username,
password,
client_id,
grant_type,
});
const options = {
host,
port,
method: 'POST',
path,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': userData.length,
},
rejectUnauthorized: false,
};
const req = http.request(options, response => {
if (response.statusCode < 200 || response.statusCode >= 300) {
response.resume();
return callback(new Error(`Status Code: ${response.statusCode}`));
}
const data = [];
return response
.on('data', chunk => data.push(chunk))
.on('end', () => {
let accessToken = null;
let error = null;
try {
accessToken = (JSON.parse(Buffer.concat(data))).access_token;
} catch (err) {
error = err;
}
return callback(error, accessToken);
})
.on('error', err => callback(err));
});
req.on('error', err => callback(err));
req.write(userData);
req.end();
}

module.exports = {
getTokenForIdentity,
}

0 comments on commit d7f2202

Please sign in to comment.