Skip to content

Commit

Permalink
feat: HMAC System tests (#750)
Browse files Browse the repository at this point in the history
* npm run fix

* test: add second service account email for testing multiple SAs HMAC keys

* npm run fix

* test: add second service account email for testing multiple SAs HMAC keys

* fix tests

* use HMAC_PROJECT in env variable
  • Loading branch information
jkwlui committed Aug 13, 2019
1 parent b270054 commit a4c4a66
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .kokoro/setup-vars.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export GCN_STORAGE_2ND_PROJECT_KEY=${KOKORO_GFILE_DIR}/no-whitelist-key.json
export GOOGLE_CLOUD_KMS_KEY_ASIA="projects/long-door-651/locations/asia/keyRings/test-key-asia/cryptoKeys/test-key-asia"
export GOOGLE_CLOUD_KMS_KEY_US="projects/long-door-651/locations/us/keyRings/test-key-us/cryptoKeys/test-key-us"

# Second service account for testing HMAC feature
export HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT="gcs-hmac-system-test-alt@long-door-651.iam.gserviceaccount.com"
# TODO: Switch with service-account pool
export POOL_SAMPLES_PROJECT_ID=long-door-651
export POOL_SAMPLES_PROJECT_CREDENTIALS="${KOKORO_GFILE_DIR}/storage-hmac-samples-key.json"
Expand Down
121 changes: 121 additions & 0 deletions system-test/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2687,6 +2687,116 @@ describe('storage', () => {
});
});

describe('HMAC keys', () => {
// This is generally a valid service account for a project.
const ALTERNATE_SERVICE_ACCOUNT = `${process.env.PROJECT_ID}@appspot.gserviceaccount.com`;
const SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT || ALTERNATE_SERVICE_ACCOUNT;
const HMAC_PROJECT = process.env.HMAC_KEY_TEST_SERVICE_ACCOUNT ? process.env.HMAC_KEY_TEST_PROJECT : process.env.PROJECT_ID;
// Second service account to test listing HMAC keys from different accounts.
const SECOND_SERVICE_ACCOUNT = process.env.HMAC_KEY_TEST_SECOND_SERVICE_ACCOUNT;

let accessId: string;

before(async () => {
await deleteHmacKeys(SERVICE_ACCOUNT, HMAC_PROJECT!);
});

after(async () => {
await deleteHmacKeys(SERVICE_ACCOUNT, HMAC_PROJECT!);
});

it('should create an HMAC key for a service account', async () => {
const [hmacKey, secret] = await storage.createHmacKey(SERVICE_ACCOUNT, {projectId: HMAC_PROJECT});
// We should always get a 40 character secret, which is valid base64.
assert.strictEqual(secret.length, 40);
accessId = hmacKey.id!;
const metadata = hmacKey.metadata!;
assert.strictEqual(metadata.accessId, accessId);
assert.strictEqual(metadata.state, 'ACTIVE');
assert.strictEqual(metadata.projectId, HMAC_PROJECT);
assert.strictEqual(metadata.serviceAccountEmail, SERVICE_ACCOUNT);
assert(typeof metadata.etag === 'string');
assert(typeof metadata.timeCreated === 'string');
assert(typeof metadata.updated === 'string');
});

it('should get metadata for an HMAC key', async () => {
const hmacKey = storage.hmacKey(accessId, {projectId: HMAC_PROJECT});
const [metadata] = await hmacKey.getMetadata();
assert.strictEqual(metadata.accessId, accessId);
});

it('should show up from getHmacKeys() without serviceAccountEmail param', async () => {
const [hmacKeys] = await storage.getHmacKeys({projectId: HMAC_PROJECT});
assert(hmacKeys.length > 0);
assert(
hmacKeys.some((hmacKey) => hmacKey.id === accessId),
'created HMAC key not found from getHmacKeys result');
});

it('should make the key INACTIVE', async () => {
const hmacKey = storage.hmacKey(accessId, {projectId: HMAC_PROJECT});
let [metadata] = await hmacKey.setMetadata({state: 'INACTIVE'});
assert.strictEqual(metadata.state, 'INACTIVE');

[metadata] = await hmacKey.getMetadata();
assert.strictEqual(metadata.state, 'INACTIVE');
});

it('should delete the key', async () => {
const hmacKey = storage.hmacKey(accessId, {projectId: HMAC_PROJECT});
await hmacKey.delete();
const [metadata] = await hmacKey.getMetadata();
assert.strictEqual(metadata.state, 'DELETED');
assert.strictEqual(hmacKey.metadata!.state, 'DELETED');
});

it('deleted key should not show up from getHmacKeys() by default', async () => {
const [hmacKeys] = await storage.getHmacKeys({serviceAccountEmail: SERVICE_ACCOUNT, projectId: HMAC_PROJECT});
assert(Array.isArray(hmacKeys));
assert(
!hmacKeys.some((hmacKey) => hmacKey.id === accessId),
'deleted HMAC key is found from getHmacKeys result');
});

describe('second service account', () => {
before(async function () {
if (!SECOND_SERVICE_ACCOUNT) {
this.skip();
return;
}
await deleteHmacKeys(SECOND_SERVICE_ACCOUNT, HMAC_PROJECT!);
});

it('should create key for a second service account', async () => {
const _ = await storage.createHmacKey(SECOND_SERVICE_ACCOUNT!, {projectId: HMAC_PROJECT});
});

it('get HMAC keys for both service accounts', async () => {
// Create a key for the first service account
const _ = await storage.createHmacKey(SERVICE_ACCOUNT!, {projectId: HMAC_PROJECT});

const [hmacKeys] = await storage.getHmacKeys({projectId: HMAC_PROJECT});
assert(hmacKeys.some((hmacKey) =>
hmacKey.metadata!.serviceAccountEmail === SERVICE_ACCOUNT),
`Expected at least 1 key for service account: ${SERVICE_ACCOUNT}`
);
assert(hmacKeys.some((hmacKey) =>
hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT),
`Expected at least 1 key for service account: ${SECOND_SERVICE_ACCOUNT}`
);
});

it('filter by service account email', async () => {
const [hmacKeys] = await storage.getHmacKeys({serviceAccountEmail: SECOND_SERVICE_ACCOUNT, projectId: HMAC_PROJECT});
assert(hmacKeys.every((hmacKey) =>
hmacKey.metadata!.serviceAccountEmail === SECOND_SERVICE_ACCOUNT),
'HMAC key belonging to other service accounts unexpected'
);
});
});
});

describe('list files', () => {
const DIRECTORY_NAME = 'directory-name';

Expand Down Expand Up @@ -3235,6 +3345,17 @@ describe('storage', () => {
}
}

async function deleteHmacKeys(serviceAccountEmail: string, projectId: string) {
const [hmacKeys] = await storage.getHmacKeys({ serviceAccountEmail, projectId });
const limit = pLimit(10);
await Promise.all(hmacKeys.map((hmacKey) => limit(async () => {
if (hmacKey.metadata!.state === 'ACTIVE') {
await hmacKey.setMetadata({state:'INACTIVE' });
}
await hmacKey.delete();
})));
}

// tslint:disable-next-line no-any
function createFileAsync(fileObject: any) {
return fileObject.file.save(fileObject.contents);
Expand Down

0 comments on commit a4c4a66

Please sign in to comment.