Transit flaky test revisit (#25563)

* here we go...

* glimmerize model to help —maybe—with capabilities checks

* remove waitUntils
This commit is contained in:
Angel Garbarino
2024-02-21 14:18:12 -07:00
committed by GitHub
parent ef4adca32c
commit d1885ee558
3 changed files with 88 additions and 85 deletions

View File

@@ -6,14 +6,18 @@
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { action } from '@ember/object';
import { buildWaiter } from '@ember/test-waiters';
import errorMessage from 'vault/utils/error-message';
const waiter = buildWaiter('transit-form-show');
export default class TransitFormShow extends Component {
@service store;
@service router;
@service flashMessages;
@action async rotateKey() {
const waiterToken = waiter.beginAsync();
const { backend, id } = this.args.key;
try {
await this.store.adapterFor('transit-key').keyAction('rotate', { backend, id });
@@ -22,6 +26,8 @@ export default class TransitFormShow extends Component {
await this.router.refresh();
} catch (e) {
this.flashMessages.danger(errorMessage(e));
} finally {
waiter.endAsync(waiterToken);
}
}
}

View File

@@ -4,102 +4,113 @@
*/
import Model, { attr } from '@ember-data/model';
import { alias } from '@ember/object/computed';
import { set, get, computed } from '@ember/object';
import { set, get } from '@ember/object';
import clamp from 'vault/utils/clamp';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
const ACTION_VALUES = {
encrypt: {
isSupported: 'supportsEncryption',
description: 'Looks up wrapping properties for the given token',
description: 'Looks up wrapping properties for the given token.',
glyph: 'lock-fill',
},
decrypt: {
isSupported: 'supportsDecryption',
description: 'Decrypts the provided ciphertext using this key',
description: 'Decrypts the provided ciphertext using this key.',
glyph: 'mail-open',
},
datakey: {
isSupported: 'supportsEncryption',
description: 'Generates a new key and value encrypted with this key',
description: 'Generates a new key and value encrypted with this key.',
glyph: 'key',
},
rewrap: {
isSupported: 'supportsEncryption',
description: 'Rewraps the ciphertext using the latest version of the named key',
description: 'Rewraps the ciphertext using the latest version of the named key.',
glyph: 'reload',
},
sign: {
isSupported: 'supportsSigning',
description: 'Get the cryptographic signature of the given data',
description: 'Get the cryptographic signature of the given data.',
glyph: 'pencil-tool',
},
hmac: {
isSupported: true,
description: 'Generate a data digest using a hash algorithm',
description: 'Generate a data digest using a hash algorithm.',
glyph: 'shuffle',
},
verify: {
isSupported: true,
description: 'Validate the provided signature for the given data',
description: 'Validate the provided signature for the given data.',
glyph: 'check-circle',
},
export: {
isSupported: 'exportable',
description: 'Get the named key',
description: 'Get the named key.',
glyph: 'external-link',
},
};
export default Model.extend({
type: attr('string', {
export default class TransitKeyModel extends Model {
@attr('string') backend;
@attr('string', {
defaultValue: 'aes256-gcm96',
}),
name: attr('string', {
})
type;
@attr('string', {
label: 'Name',
readOnly: true,
}),
autoRotatePeriod: attr({
})
name;
@attr({
defaultValue: '0',
defaultShown: 'Key is not automatically rotated',
editType: 'ttl',
label: 'Auto-rotation period',
}),
deletionAllowed: attr('boolean'),
derived: attr('boolean'),
exportable: attr('boolean'),
minDecryptionVersion: attr('number', {
defaultValue: 1,
}),
minEncryptionVersion: attr('number', {
defaultValue: 0,
}),
latestVersion: attr('number'),
keys: attr('object'),
convergentEncryption: attr('boolean'),
convergentEncryptionVersion: attr('number'),
})
autoRotatePeriod;
supportsSigning: attr('boolean'),
supportsEncryption: attr('boolean'),
supportsDecryption: attr('boolean'),
supportsDerivation: attr('boolean'),
@attr('boolean') deletionAllowed;
@attr('boolean') derived;
@attr('boolean') exportable;
@attr('number', {
defaultValue: 1,
})
minDecryptionVersion;
@attr('number', {
defaultValue: 0,
})
minEncryptionVersion;
@attr('number') latestVersion;
@attr('object') keys;
@attr('boolean') convergentEncryption;
@attr('number') convergentEncryptionVersion;
@attr('boolean') supportsSigning;
@attr('boolean') supportsEncryption;
@attr('boolean') supportsDecryption;
@attr('boolean') supportsDerivation;
setConvergentEncryption(val) {
if (val === true) {
set(this, 'derived', val);
}
set(this, 'convergentEncryption', val);
},
}
setDerived(val) {
if (val === false) {
set(this, 'convergentEncryption', val);
}
set(this, 'derived', val);
},
}
supportedActions: computed('type', function () {
get supportedActions() {
return Object.keys(ACTION_VALUES)
.filter((name) => {
const { isSupported } = ACTION_VALUES[name];
@@ -109,14 +120,14 @@ export default Model.extend({
const { description, glyph } = ACTION_VALUES[name];
return { name, description, glyph };
});
}),
}
canDelete: computed('deletionAllowed', 'lastLoadTS', function () {
get canDelete() {
const deleteAttrChanged = Boolean(this.changedAttributes().deletionAllowed);
return this.deletionAllowed && deleteAttrChanged === false;
}),
}
keyVersions: computed('validKeyVersions', function () {
get keyVersions() {
let maxVersion = Math.max(...this.validKeyVersions);
const versions = [];
while (maxVersion > 0) {
@@ -124,25 +135,19 @@ export default Model.extend({
maxVersion--;
}
return versions;
}),
}
encryptionKeyVersions: computed(
'keyVerisons',
'keyVersions',
'latestVersion',
'minDecryptionVersion',
function () {
const { keyVersions, minDecryptionVersion } = this;
get encryptionKeyVersions() {
const { keyVersions, minDecryptionVersion } = this;
return keyVersions
.filter((version) => {
return version >= minDecryptionVersion;
})
.reverse();
}
),
return keyVersions
.filter((version) => {
return version >= minDecryptionVersion;
})
.reverse();
}
keysForEncryption: computed('minEncryptionVersion', 'latestVersion', function () {
get keysForEncryption() {
let { minEncryptionVersion, latestVersion } = this;
const minVersion = clamp(minEncryptionVersion - 1, 0, latestVersion);
const versions = [];
@@ -151,13 +156,13 @@ export default Model.extend({
latestVersion--;
}
return versions;
}),
}
validKeyVersions: computed('keys', function () {
get validKeyVersions() {
return Object.keys(this.keys);
}),
}
exportKeyTypes: computed('exportable', 'supportsEncryption', 'supportsSigning', 'type', function () {
get exportKeyTypes() {
const types = ['hmac'];
if (this.supportsSigning) {
types.unshift('signing');
@@ -166,13 +171,17 @@ export default Model.extend({
types.unshift('encryption');
}
return types;
}),
}
@lazyCapabilities(apiPath`${'backend'}/keys/${'id'}/rotate`, 'backend', 'id') rotatePath;
@lazyCapabilities(apiPath`${'backend'}/keys/${'id'}`, 'backend', 'id') secretPath;
backend: attr('string'),
rotatePath: lazyCapabilities(apiPath`${'backend'}/keys/${'id'}/rotate`, 'backend', 'id'),
canRotate: alias('rotatePath.canUpdate'),
secretPath: lazyCapabilities(apiPath`${'backend'}/keys/${'id'}`, 'backend', 'id'),
canRead: alias('secretPath.canUpdate'),
canEdit: alias('secretPath.canUpdate'),
});
get canRotate() {
return this.rotatePath.get('canUpdate') !== false;
}
get canRead() {
return this.secretPath.get('canUpdate') !== false;
}
get canEdit() {
return this.secretPath.get('canUpdate') !== false;
}
}

View File

@@ -3,7 +3,7 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { click, fillIn, find, currentURL, settled, visit, waitUntil, findAll } from '@ember/test-helpers';
import { click, fillIn, find, currentURL, settled, visit, findAll } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { v4 as uuidv4 } from 'uuid';
@@ -345,17 +345,14 @@ module('Acceptance | transit (flaky)', function (hooks) {
await click(SELECTORS.versionsTab);
assert.dom(SELECTORS.versionRow(1)).hasTextContaining('Version 1', `${name}: only one key version`);
await waitUntil(() => find(SELECTORS.rotate.trigger));
await click(SELECTORS.rotate.trigger);
await click(SELECTORS.rotate.confirm);
// wait for rotate call
await waitUntil(() => find(SELECTORS.versionRow(2)));
assert.dom(SELECTORS.versionRow(2)).exists('two key versions after rotate');
// navigate back to actions tab
await click(SELECTORS.actionsTab);
await waitUntil(() => find(SELECTORS.card('encrypt')));
assert.dom(SELECTORS.card('encrypt')).exists(`renders encrypt action card for ${name}`);
await click(SELECTORS.card('encrypt'));
assert
@@ -367,11 +364,6 @@ module('Acceptance | transit (flaky)', function (hooks) {
await testConvergentEncryption(assert, name);
});
/*
OLD FLAKY TESTS (skipped)
It's been a while since we've updated the transit engine
keeping these tests to run locally the next time we touch that secret engine
*/
const KEY_TYPE_COMBINATIONS = [
{
name: (uid) => `aes-${uid}`,
@@ -459,7 +451,7 @@ module('Acceptance | transit (flaky)', function (hooks) {
];
for (const key of KEY_TYPE_COMBINATIONS) {
test.skip(`transit backend: ${key.type}`, async function (assert) {
test(`transit backend: ${key.type}`, async function (assert) {
assert.expect(key.convergent ? 43 : 7);
const name = await this.generateTransitKey(key);
await visit(`vault/secrets/${this.path}/show/${name}`);
@@ -473,12 +465,9 @@ module('Acceptance | transit (flaky)', function (hooks) {
// wait for capabilities
assert.dom('[data-test-transit-version]').exists({ count: 1 }, `${name}: only one key version`);
await waitUntil(() => find(SELECTORS.rotate.trigger));
await click(SELECTORS.rotate.trigger);
await click(SELECTORS.rotate.confirm);
// wait for rotate call
await waitUntil(() => findAll('[data-test-transit-version]').length >= 2);
assert
.dom('[data-test-transit-version]')
.exists({ count: 2 }, `${name}: two key versions after rotate`);
@@ -491,7 +480,6 @@ module('Acceptance | transit (flaky)', function (hooks) {
);
const keyAction = key.supportsEncryption ? 'encrypt' : 'sign';
await waitUntil(() => find(`[data-test-transit-action-title=${keyAction}]`));
assert
.dom(`[data-test-transit-action-title=${keyAction}]`)