mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Glimmerize Transit Key Actions (#25365)
* wip, all actions wired up, not finished or tested. * move the rotate action to it's own component to clarify scope * clean up and refresh component on queryParam change without forcing a refresh from component as before * solve issue of carrying over props we want to keep * clean up and add clearProps action * transit key actions passing * update assert and doc * remove unecessary changes * Address pr comments * replace perform in submit action and instead pass it in as perform * address claire's pr comments * welp * trying to rearrange closer to original * addressing pr comments * move things around * pr comments * remove ciphertext when last action was rewrap * add args and istruncated and isfullwidth
This commit is contained in:
27
ui/app/components/transit-form-show.js
Normal file
27
ui/app/components/transit-form-show.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import errorMessage from 'vault/utils/error-message';
|
||||||
|
|
||||||
|
export default class TransitFormShow extends Component {
|
||||||
|
@service store;
|
||||||
|
@service router;
|
||||||
|
@service flashMessages;
|
||||||
|
|
||||||
|
@action async rotateKey() {
|
||||||
|
const { backend, id } = this.args.key;
|
||||||
|
try {
|
||||||
|
await this.store.adapterFor('transit-key').keyAction('rotate', { backend, id });
|
||||||
|
this.flashMessages.success('Key rotated.');
|
||||||
|
// must refresh to see the updated versions, a model refresh does not trigger the change.
|
||||||
|
await this.router.refresh();
|
||||||
|
} catch (e) {
|
||||||
|
this.flashMessages.danger(errorMessage(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -7,8 +7,5 @@ import Component from '@glimmer/component';
|
|||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
export default class ExportComponent extends Component {
|
export default class ExportComponent extends Component {
|
||||||
@tracked
|
@tracked exportVersion = false;
|
||||||
wrapTTL = null;
|
|
||||||
@tracked
|
|
||||||
exportVersion = false;
|
|
||||||
}
|
}
|
||||||
|
134
ui/app/components/transit-key-actions.hbs
Normal file
134
ui/app/components/transit-key-actions.hbs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
|
~}}
|
||||||
|
|
||||||
|
<div {{did-update this.updateProps @selectedAction}}>
|
||||||
|
<MessageError @errorMessage={{this.errors}} />
|
||||||
|
|
||||||
|
{{#if (eq @selectedAction "encrypt")}}
|
||||||
|
<TransitKeyAction::Encrypt
|
||||||
|
@key={{@key}}
|
||||||
|
@param={{this.props.param}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@nonce={{this.props.nonce}}
|
||||||
|
@bits={{this.props.bits}}
|
||||||
|
@key_version={{this.props.key_version}}
|
||||||
|
@encodedBase64={{this.props.encodedBase64}}
|
||||||
|
@toggleEncodeBase64={{this.toggleEncodeBase64}}
|
||||||
|
@plaintext={{this.props.plaintext}}
|
||||||
|
@ciphertext={{this.props.ciphertext}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (eq @selectedAction "decrypt")}}
|
||||||
|
<TransitKeyAction::Decrypt
|
||||||
|
@key={{@key}}
|
||||||
|
@ciphertext={{this.props.ciphertext}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@nonce={{this.props.nonce}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
@plaintext={{this.props.plaintext}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (eq @selectedAction "datakey")}}
|
||||||
|
<TransitKeyAction::Datakey
|
||||||
|
@key={{@key}}
|
||||||
|
@param={{this.props.param}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@nonce={{this.props.nonce}}
|
||||||
|
@bits={{this.props.bits}}
|
||||||
|
@plaintext={{this.props.plaintext}}
|
||||||
|
@ciphertext={{this.props.ciphertext}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (eq @selectedAction "rewrap")}}
|
||||||
|
<TransitKeyAction::Rewrap
|
||||||
|
@key={{@key}}
|
||||||
|
@param={{this.props.param}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@nonce={{this.props.nonce}}
|
||||||
|
@key_version={{this.props.key_version}}
|
||||||
|
@ciphertext={{this.props.ciphertext}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (eq @selectedAction "hmac")}}
|
||||||
|
<TransitKeyAction::Hmac
|
||||||
|
@key={{@key}}
|
||||||
|
@trackedInput={{this.props.input}}
|
||||||
|
@algorithm={{this.props.algorithm}}
|
||||||
|
@key_version={{this.props.key_version}}
|
||||||
|
@encodedBase64={{this.props.encodedBase64}}
|
||||||
|
@toggleEncodeBase64={{this.toggleEncodeBase64}}
|
||||||
|
@hmac={{this.props.hmac}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (eq @selectedAction "verify")}}
|
||||||
|
<TransitKeyAction::Verify
|
||||||
|
@key={{@key}}
|
||||||
|
@trackedInput={{this.props.input}}
|
||||||
|
@signature={{this.props.signature}}
|
||||||
|
@signature_algorithm={{this.props.signature_algorithm}}
|
||||||
|
@hmac={{this.props.hmac}}
|
||||||
|
@hash_algorithm={{this.props.hash_algorithm}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@prehashed={{this.props.prehashed}}
|
||||||
|
@encodedBase64={{this.props.encodedBase64}}
|
||||||
|
@verification={{this.props.verification}}
|
||||||
|
@valid={{this.props.valid}}
|
||||||
|
@toggleEncodeBase64={{this.toggleEncodeBase64}}
|
||||||
|
@keyIsRSA={{this.keyIsRSA}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
@submitIsRunning={{this.doSubmit.isRunning}}
|
||||||
|
@clearSpecificProps={{this.clearSpecificProps}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (eq @selectedAction "sign")}}
|
||||||
|
<TransitKeyAction::Sign
|
||||||
|
@key={{@key}}
|
||||||
|
@trackedInput={{this.props.input}}
|
||||||
|
@hash_algorithm={{this.props.hash_algorithm}}
|
||||||
|
@signature={{this.props.signature}}
|
||||||
|
@signature_algorithm={{this.props.signature_algorithm}}
|
||||||
|
@key_version={{this.props.key_version}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@prehashed={{this.props.prehashed}}
|
||||||
|
@encodedBase64={{this.props.encodedBase64}}
|
||||||
|
@toggleEncodeBase64={{this.toggleEncodeBase64}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
@submitIsRunning={{this.doSubmit.isRunning}}
|
||||||
|
data-test-transit-action={{@selectedAction}}
|
||||||
|
/>
|
||||||
|
{{else if (or (eq @selectedAction "export") (eq @key.supportedActions.firstObject "export"))}}
|
||||||
|
<TransitKeyAction::Export
|
||||||
|
@key={{@key}}
|
||||||
|
@keys={{this.props.keys}}
|
||||||
|
@trackedInput={{this.props.input}}
|
||||||
|
@hash_algorithm={{this.props.hash_algorithm}}
|
||||||
|
@signature={{this.props.signature}}
|
||||||
|
@signature_algorithm={{this.props.signature_algorithm}}
|
||||||
|
@key_version={{this.props.key_version}}
|
||||||
|
@context={{this.props.context}}
|
||||||
|
@prehashed={{this.props.prehashed}}
|
||||||
|
@encodedBase64={{this.props.encodedBase64}}
|
||||||
|
@exportKeyType={{this.props.exportKeyType}}
|
||||||
|
@exportKeyVersion={{this.props.exportKeyVersion}}
|
||||||
|
@wrappedToken={{this.props.wrappedToken}}
|
||||||
|
@wrappedTTL={{this.props.wrappedTTL}}
|
||||||
|
@toggleEncodeBase64={{this.toggleEncodeBase64}}
|
||||||
|
@isModalActive={{this.isModalActive}}
|
||||||
|
@doSubmit={{perform this.doSubmit}}
|
||||||
|
data-test-transit-action="export"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
@@ -3,15 +3,31 @@
|
|||||||
* SPDX-License-Identifier: BUSL-1.1
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { assign } from '@ember/polyfills';
|
import Component from '@glimmer/component';
|
||||||
import { copy } from 'ember-copy';
|
|
||||||
import { assert } from '@ember/debug';
|
|
||||||
import { service } from '@ember/service';
|
import { service } from '@ember/service';
|
||||||
import Component from '@ember/component';
|
import { action } from '@ember/object';
|
||||||
import { set, get, computed } from '@ember/object';
|
import { assert } from '@ember/debug';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
import { task } from 'ember-concurrency';
|
||||||
|
import { waitFor } from '@ember/test-waiters';
|
||||||
import { encodeString } from 'vault/utils/b64';
|
import { encodeString } from 'vault/utils/b64';
|
||||||
|
import errorMessage from 'vault/utils/error-message';
|
||||||
|
|
||||||
const TRANSIT_PARAMS = {
|
/**
|
||||||
|
* @module TransitKeyActions
|
||||||
|
* TransitKeyActions component handles the actions a user can take on a transit key model. The model and props are updated on every tab change
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <TransitKeyActions
|
||||||
|
* @key={{this.model}}
|
||||||
|
* @selectedAction="hmac"
|
||||||
|
* />
|
||||||
|
*
|
||||||
|
* @param {string} selectedAction - This is the query param "action" value. Ex: hmac, verify, decrypt, etc. The only time this param can be empty is if a user is exporting a key
|
||||||
|
* @param {object} key - This is the transit key model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const STARTING_TRANSIT_PROPS = {
|
||||||
hash_algorithm: 'sha2-256',
|
hash_algorithm: 'sha2-256',
|
||||||
algorithm: 'sha2-256',
|
algorithm: 'sha2-256',
|
||||||
signature_algorithm: 'pss',
|
signature_algorithm: 'pss',
|
||||||
@@ -35,161 +51,101 @@ const TRANSIT_PARAMS = {
|
|||||||
exportKeyType: null,
|
exportKeyType: null,
|
||||||
exportKeyVersion: null,
|
exportKeyVersion: null,
|
||||||
wrappedToken: null,
|
wrappedToken: null,
|
||||||
|
wrappedTTL: '30m',
|
||||||
valid: null,
|
valid: null,
|
||||||
plaintextOriginal: null,
|
plaintextOriginal: null,
|
||||||
didDecode: false,
|
didDecode: false,
|
||||||
verification: 'Signature',
|
verification: 'Signature',
|
||||||
};
|
};
|
||||||
const PARAMS_FOR_ACTION = {
|
|
||||||
|
const PROPS_TO_KEEP = {
|
||||||
|
encrypt: ['plaintext', 'context', 'nonce', 'key_version'],
|
||||||
|
decrypt: ['ciphertext', 'context', 'nonce'],
|
||||||
sign: ['input', 'hash_algorithm', 'key_version', 'prehashed', 'signature_algorithm'],
|
sign: ['input', 'hash_algorithm', 'key_version', 'prehashed', 'signature_algorithm'],
|
||||||
verify: ['input', 'hmac', 'signature', 'hash_algorithm', 'prehashed'],
|
verify: ['input', 'hmac', 'signature', 'hash_algorithm', 'prehashed'],
|
||||||
hmac: ['input', 'algorithm', 'key_version'],
|
hmac: ['input', 'algorithm', 'key_version'],
|
||||||
encrypt: ['plaintext', 'context', 'nonce', 'key_version'],
|
|
||||||
decrypt: ['ciphertext', 'context', 'nonce'],
|
|
||||||
rewrap: ['ciphertext', 'context', 'nonce', 'key_version'],
|
rewrap: ['ciphertext', 'context', 'nonce', 'key_version'],
|
||||||
|
datakey: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const SUCCESS_MESSAGE_FOR_ACTION = {
|
const SUCCESS_MESSAGE_FOR_ACTION = {
|
||||||
sign: 'Signed your data',
|
sign: 'Signed your data.',
|
||||||
// the verify action doesn't trigger a success message
|
// the verify action doesn't trigger a success message
|
||||||
hmac: 'Created your hash output',
|
hmac: 'Created your hash output.',
|
||||||
encrypt: 'Created a wrapped token for your data',
|
encrypt: 'Created a wrapped token for your data.',
|
||||||
decrypt: 'Decrypted the data from your token',
|
decrypt: 'Decrypted the data from your token.',
|
||||||
rewrap: 'Created a new token for your data',
|
rewrap: 'Created a new token for your data.',
|
||||||
datakey: 'Generated your key',
|
datakey: 'Generated your key.',
|
||||||
export: 'Exported your key',
|
export: 'Exported your key.',
|
||||||
};
|
};
|
||||||
export default Component.extend(TRANSIT_PARAMS, {
|
|
||||||
store: service(),
|
|
||||||
flashMessages: service(),
|
|
||||||
|
|
||||||
// public attrs
|
export default class TransitKeyActions extends Component {
|
||||||
selectedAction: null,
|
@service store;
|
||||||
key: null,
|
@service flashMessages;
|
||||||
isModalActive: false,
|
@service router;
|
||||||
|
|
||||||
onRefresh() {},
|
@tracked isModalActive = false;
|
||||||
init() {
|
@tracked errors = null;
|
||||||
this._super(...arguments);
|
@tracked props = { ...STARTING_TRANSIT_PROPS }; // Shallow copy of the object. We don't want to mutate the original.
|
||||||
// TODO figure out why get is needed here Ember Upgrade
|
|
||||||
// eslint-disable-next-line ember/no-get
|
constructor() {
|
||||||
if (this.selectedAction) {
|
super(...arguments);
|
||||||
return;
|
assert('@key is required for TransitKeyActions components', this.args.key);
|
||||||
|
|
||||||
|
if (this.firstSupportedAction === 'export' || this.args.selectedAction === 'export') {
|
||||||
|
this.props.exportKeyType = this.args.key.exportKeyTypes[0];
|
||||||
|
this.props.exportKeyVersion = this.args.key.validKeyVersions.lastObject;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line ember/no-get
|
}
|
||||||
set(this, 'selectedAction', get(this, 'key.supportedActions.firstObject'));
|
|
||||||
assert('`key` is required for `' + this.toString() + '`.', this.getModelInfo());
|
|
||||||
},
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
get keyIsRSA() {
|
||||||
this._super(...arguments);
|
const { type } = this.args.key;
|
||||||
this.checkAction();
|
|
||||||
if (this.selectedAction === 'export') {
|
|
||||||
this.setExportKeyDefaults();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setExportKeyDefaults() {
|
|
||||||
const exportKeyType = this.key.exportKeyTypes.firstObject;
|
|
||||||
const exportKeyVersion = this.key.validKeyVersions.lastObject;
|
|
||||||
this.setProperties({
|
|
||||||
exportKeyType,
|
|
||||||
exportKeyVersion,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
keyIsRSA: computed('key.type', function () {
|
|
||||||
const type = this.key.type;
|
|
||||||
return type === 'rsa-2048' || type === 'rsa-3072' || type === 'rsa-4096';
|
return type === 'rsa-2048' || type === 'rsa-3072' || type === 'rsa-4096';
|
||||||
}),
|
}
|
||||||
|
|
||||||
getModelInfo() {
|
get firstSupportedAction() {
|
||||||
const model = this.key || this.backend;
|
return this.args.key.supportedActions[0];
|
||||||
if (!model) {
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const backend = model.backend || model.id;
|
|
||||||
const id = model.id;
|
|
||||||
|
|
||||||
return {
|
|
||||||
backend,
|
|
||||||
id,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
checkAction() {
|
|
||||||
const currentAction = this.selectedAction;
|
|
||||||
const oldAction = this.oldSelectedAction;
|
|
||||||
|
|
||||||
this.resetParams(oldAction, currentAction);
|
|
||||||
set(this, 'oldSelectedAction', currentAction);
|
|
||||||
},
|
|
||||||
|
|
||||||
resetParams(oldAction, action) {
|
|
||||||
const params = copy(TRANSIT_PARAMS);
|
|
||||||
let paramsToKeep;
|
|
||||||
const clearWithoutCheck =
|
|
||||||
!oldAction ||
|
|
||||||
// don't save values from datakey
|
|
||||||
oldAction === 'datakey' ||
|
|
||||||
// can rewrap signatures — using that as a ciphertext later would be problematic
|
|
||||||
(oldAction === 'rewrap' && !this.key.supportsEncryption);
|
|
||||||
|
|
||||||
if (!clearWithoutCheck && action) {
|
|
||||||
paramsToKeep = PARAMS_FOR_ACTION[action];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paramsToKeep) {
|
|
||||||
paramsToKeep.forEach((param) => delete params[param]);
|
|
||||||
}
|
|
||||||
//resets params still left in the object to defaults
|
|
||||||
this.clearErrors();
|
|
||||||
this.setProperties(params);
|
|
||||||
if (action === 'export') {
|
|
||||||
this.setExportKeyDefaults();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleError(e) {
|
|
||||||
this.set('errors', e.errors);
|
|
||||||
},
|
|
||||||
|
|
||||||
clearErrors() {
|
|
||||||
this.set('errors', null);
|
|
||||||
},
|
|
||||||
|
|
||||||
triggerSuccessMessage(action) {
|
|
||||||
const message = SUCCESS_MESSAGE_FOR_ACTION[action];
|
|
||||||
if (!message) return;
|
|
||||||
this.flashMessages.success(message);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleSuccess(resp, options, action) {
|
handleSuccess(resp, options, action) {
|
||||||
let props = {};
|
|
||||||
if (resp && resp.data) {
|
if (resp && resp.data) {
|
||||||
if (action === 'export' && resp.data.keys) {
|
if (action === 'export' && resp.data.keys) {
|
||||||
const { keys, type, name } = resp.data;
|
const { keys, type, name } = resp.data;
|
||||||
resp.data.keys = { keys, type, name };
|
resp.data.keys = { keys, type, name };
|
||||||
}
|
}
|
||||||
props = assign({}, props, resp.data);
|
this.props = { ...this.props, ...resp.data };
|
||||||
|
|
||||||
|
// While we do not pass ciphertext as a value to the JsonEditor, so that navigating from rewrap to decrypt will not show ciphertext in the editor, we still want to clear it from the props after rewrapping.
|
||||||
|
if (action === 'rewrap' && !this.args.key.supportsEncryption) {
|
||||||
|
this.props.ciphertext = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (options.wrapTTL) {
|
if (options.wrapTTL) {
|
||||||
props = assign({}, props, { wrappedToken: resp.wrap_info.token });
|
this.props = { ...this.props, ...{ wrappedToken: resp.wrap_info.token } };
|
||||||
}
|
}
|
||||||
if (!this.isDestroyed && !this.isDestroying) {
|
this.isModalActive = true;
|
||||||
this.toggleProperty('isModalActive');
|
// verify doesn't trigger a success message
|
||||||
this.setProperties(props);
|
if (this.args.selectedAction !== 'verify') {
|
||||||
|
this.flashMessages.success(SUCCESS_MESSAGE_FOR_ACTION[action]);
|
||||||
}
|
}
|
||||||
if (action === 'rotate') {
|
}
|
||||||
this.onRefresh();
|
|
||||||
}
|
@action updateProps() {
|
||||||
this.triggerSuccessMessage(action);
|
this.errors = null;
|
||||||
},
|
// There are specific props we want to carry over from the previous tab.
|
||||||
|
// Ex: carrying over this.props.context from the encrypt tab to the decrypt tab, but not carrying over this.props.plaintext.
|
||||||
|
// To do this, we make a new object that contains the old this.props key/values from the previous tab that we want to keep. We then merge that new object into the STARTING_TRANSIT_PROPS object to come up with our new this.props tracked property.
|
||||||
|
// This action is passed to did-update in the component.
|
||||||
|
const transferredProps = PROPS_TO_KEEP[this.args.selectedAction]?.reduce(
|
||||||
|
(obj, key) => ({ ...obj, [key]: this.props[key] }),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
this.props = { ...STARTING_TRANSIT_PROPS, ...transferredProps };
|
||||||
|
}
|
||||||
|
|
||||||
compactData(data) {
|
compactData(data) {
|
||||||
const type = this.key.type;
|
|
||||||
const isRSA = type === 'rsa-2048' || type === 'rsa-3072' || type === 'rsa-4096';
|
|
||||||
return Object.keys(data).reduce((result, key) => {
|
return Object.keys(data).reduce((result, key) => {
|
||||||
if (key === 'signature_algorithm' && !isRSA) {
|
if (key === 'signature_algorithm' && !this.keyIsRSA) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (data[key]) {
|
if (data[key]) {
|
||||||
@@ -197,51 +153,45 @@ export default Component.extend(TRANSIT_PARAMS, {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
},
|
}
|
||||||
|
|
||||||
actions: {
|
@action toggleEncodeBase64() {
|
||||||
onActionChange(action) {
|
this.props.encodedBase64 = !this.props.encodedBase64;
|
||||||
set(this, 'selectedAction', action);
|
}
|
||||||
this.checkAction();
|
|
||||||
},
|
|
||||||
|
|
||||||
onClear() {
|
@action clearSpecificProps(arrayToClear) {
|
||||||
this.resetParams(null, this.selectedAction);
|
arrayToClear.forEach((prop) => (this.props[prop] = null));
|
||||||
},
|
}
|
||||||
|
|
||||||
clearParams(params) {
|
@task
|
||||||
const arr = Array.isArray(params) ? params : [params];
|
@waitFor
|
||||||
arr.forEach((param) => this.set(param, null));
|
*doSubmit(data, options = {}, maybeEvent) {
|
||||||
},
|
this.errors = null;
|
||||||
|
const event = options.type === 'submit' ? options : maybeEvent;
|
||||||
doSubmit(data, options = {}, maybeEvent) {
|
if (event) {
|
||||||
const event = options.type === 'submit' ? options : maybeEvent;
|
event.preventDefault();
|
||||||
if (event) {
|
}
|
||||||
event.preventDefault();
|
const { backend, id } = this.args.key;
|
||||||
|
const action = this.args.selectedAction || this.firstSupportedAction;
|
||||||
|
const { ...formData } = data || {};
|
||||||
|
if (!this.props.encodedBase64) {
|
||||||
|
if (action === 'encrypt' && !!formData.plaintext) {
|
||||||
|
formData.plaintext = encodeString(formData.plaintext);
|
||||||
}
|
}
|
||||||
const { backend, id } = this.getModelInfo();
|
if ((action === 'hmac' || action === 'verify' || action === 'sign') && !!formData.input) {
|
||||||
const action = this.selectedAction;
|
formData.input = encodeString(formData.input);
|
||||||
const { encodedBase64, ...formData } = data || {};
|
|
||||||
if (!encodedBase64) {
|
|
||||||
if (action === 'encrypt' && !!formData.plaintext) {
|
|
||||||
formData.plaintext = encodeString(formData.plaintext);
|
|
||||||
}
|
|
||||||
if ((action === 'hmac' || action === 'verify' || action === 'sign') && !!formData.input) {
|
|
||||||
formData.input = encodeString(formData.input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const payload = formData ? this.compactData(formData) : null;
|
}
|
||||||
this.setProperties({
|
const payload = formData ? this.compactData(formData) : null;
|
||||||
errors: null,
|
|
||||||
result: null,
|
try {
|
||||||
});
|
const resp = yield this.store
|
||||||
this.store
|
|
||||||
.adapterFor('transit-key')
|
.adapterFor('transit-key')
|
||||||
.keyAction(action, { backend, id, payload }, options)
|
.keyAction(action, { backend, id, payload }, options);
|
||||||
.then(
|
|
||||||
(resp) => this.handleSuccess(resp, options, action),
|
this.handleSuccess(resp, options, action);
|
||||||
(...errArgs) => this.handleError(...errArgs)
|
} catch (e) {
|
||||||
);
|
this.errors = errorMessage(e);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
@@ -47,8 +47,17 @@
|
|||||||
{{#if (not-eq @tab "actions")}}
|
{{#if (not-eq @tab "actions")}}
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<ToolbarActions>
|
<ToolbarActions>
|
||||||
{{#if (eq @tab "versions")}}
|
{{#if (and (eq @tab "versions") @key.canRotate)}}
|
||||||
<TransitKeyActions @key={{@key}} @selectedAction="rotate" @capabilities={{@capabilities}} @onRefresh={{@refresh}} />
|
<ConfirmAction
|
||||||
|
@buttonText="Rotate encryption key"
|
||||||
|
class="toolbar-button"
|
||||||
|
@buttonColor="secondary"
|
||||||
|
@confirmTitle="Rotate this key?"
|
||||||
|
@confirmMessage="After rotation, all key actions will default to using the newest version of the key."
|
||||||
|
@modalColor="warning"
|
||||||
|
@onConfirmAction={{this.rotateKey}}
|
||||||
|
data-test-transit-key-rotate
|
||||||
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq @mode "show")}}
|
{{#if (eq @mode "show")}}
|
||||||
{{#if (or @capabilities.canUpdate @capabilities.canDelete)}}
|
{{#if (or @capabilities.canUpdate @capabilities.canDelete)}}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
SPDX-License-Identifier: BUSL-1.1
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form onsubmit={{action @doSubmit (hash param=@param context=@context nonce=@nonce bits=@bits)}}>
|
<form {{on "submit" (fn @doSubmit (hash param=@param context=@context nonce=@nonce bits=@bits))}} ...attributes>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<NamespaceReminder @mode="perform" @noun="datakey creation" />
|
<NamespaceReminder @mode="perform" @noun="datakey creation" />
|
||||||
<div class="content has-bottom-margin-l">
|
<div class="content has-bottom-margin-l">
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
Copy your generated key
|
Copy your generated key
|
||||||
</M.Header>
|
</M.Header>
|
||||||
<M.Body>
|
<M.Body>
|
||||||
{{#if (eq @param "plaintext")}}
|
{{#if @plaintext}}
|
||||||
<h2 class="has-text-weight-semibold is-6">Plaintext</h2>
|
<h2 class="has-text-weight-semibold is-6">Plaintext</h2>
|
||||||
<p class="sub-text">Plaintext is base64 encoded</p>
|
<p class="sub-text">Plaintext is base64 encoded</p>
|
||||||
<Hds::Copy::Snippet
|
<Hds::Copy::Snippet
|
||||||
@@ -86,6 +86,8 @@
|
|||||||
@textToCopy={{@plaintext}}
|
@textToCopy={{@plaintext}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-datakey-modal"
|
@container="#transit-datakey-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
@@ -96,6 +98,8 @@
|
|||||||
@textToCopy={{@ciphertext}}
|
@textToCopy={{@ciphertext}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-datakey-modal"
|
@container="#transit-datakey-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
SPDX-License-Identifier: BUSL-1.1
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form onsubmit={{action @doSubmit (hash ciphertext=@ciphertext context=@context nonce=@nonce)}}>
|
<form {{on "submit" (fn @doSubmit (hash ciphertext=@ciphertext context=@context nonce=@nonce))}} ...attributes>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<div class="content has-bottom-margin-l">
|
<div class="content has-bottom-margin-l">
|
||||||
<p>You can decrypt ciphertext using <code>{{@key.name}}</code> as the encryption key.</p>
|
<p>You can decrypt ciphertext using <code>{{@key.name}}</code> as the encryption key.</p>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<div id="ciphertext-control" class="control">
|
<div id="ciphertext-control" class="control">
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title="Ciphertext"
|
@title="Ciphertext"
|
||||||
@valueUpdated={{action (mut @ciphertext)}}
|
@valueUpdated={{fn (mut @ciphertext)}}
|
||||||
@mode="ruby"
|
@mode="ruby"
|
||||||
@data-test-transit-input="ciphertext"
|
@data-test-transit-input="ciphertext"
|
||||||
/>
|
/>
|
||||||
@@ -60,11 +60,13 @@
|
|||||||
</M.Header>
|
</M.Header>
|
||||||
<M.Body>
|
<M.Body>
|
||||||
<h2 class="has-text-weight-semibold is-6">Plaintext</h2>
|
<h2 class="has-text-weight-semibold is-6">Plaintext</h2>
|
||||||
<p class="sub-text">Plaintext is base64 encoded</p>
|
<p class="sub-text">Plaintext is base64 encoded.</p>
|
||||||
<Hds::Copy::Snippet
|
<Hds::Copy::Snippet
|
||||||
@textToCopy={{@plaintext}}
|
@textToCopy={{@plaintext}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-decrypt-modal"
|
@container="#transit-decrypt-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -4,30 +4,34 @@
|
|||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form
|
<form
|
||||||
onsubmit={{action
|
{{on "submit" (fn @doSubmit (hash plaintext=@plaintext context=@context nonce=@nonce key_version=@key_version))}}
|
||||||
@doSubmit
|
...attributes
|
||||||
(hash plaintext=@plaintext context=@context nonce=@nonce key_version=@key_version encodedBase64=@encodedBase64)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<NamespaceReminder @mode="perform" @noun="encryption" />
|
<NamespaceReminder @mode="perform" @noun="encryption" />
|
||||||
<div class="content has-bottom-margin-l">
|
<div class="content has-bottom-margin-l">
|
||||||
<p>You can encrypt plaintext data using <code>{{@key.name}}</code> as the encryption key.</p>
|
<p>You can encrypt plaintext data using <code>{{@key.name}}</code> as the encryption key.</p>
|
||||||
</div>
|
</div>
|
||||||
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
<KeyVersionSelect @key={{@key}} @onVersionChange={{fn (mut @key_version)}} @key_version={{@key_version}} />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div id="plaintext-control" class="control is-relative">
|
<div id="plaintext-control" class="control is-relative">
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title="Plaintext"
|
@title="Plaintext"
|
||||||
@value={{@plaintext}}
|
@value={{@plaintext}}
|
||||||
@valueUpdated={{action (mut @plaintext)}}
|
@valueUpdated={{fn (mut @plaintext)}}
|
||||||
@mode="ruby"
|
@mode="ruby"
|
||||||
@data-test-transit-input="plaintext"
|
@data-test-transit-input="plaintext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Input @type="checkbox" id="encodedBase64" @checked={{@encodedBase64}} data-test-transit-input="encodedBase64" />
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
id="encodedBase64"
|
||||||
|
@checked={{@encodedBase64}}
|
||||||
|
{{on "change" @toggleEncodeBase64}}
|
||||||
|
data-test-transit-input="encodedBase64"
|
||||||
|
/>
|
||||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||||
</div>
|
</div>
|
||||||
{{#if @key.derived}}
|
{{#if @key.derived}}
|
||||||
@@ -78,6 +82,8 @@
|
|||||||
@textToCopy={{@ciphertext}}
|
@textToCopy={{@ciphertext}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-encrypt-modal"
|
@container="#transit-encrypt-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -4,11 +4,15 @@
|
|||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form
|
<form
|
||||||
onsubmit={{action
|
{{on
|
||||||
@doSubmit
|
"submit"
|
||||||
(hash param=(compact (array @exportKeyType (if this.exportVersion @exportKeyVersion))))
|
(fn
|
||||||
(hash wrapTTL=this.wrapTTL)
|
@doSubmit
|
||||||
|
(hash param=(compact (array @exportKeyType (if this.exportVersion @exportKeyVersion))))
|
||||||
|
(hash wrapTTL=@wrappedTTL)
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
|
...attributes
|
||||||
>
|
>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -55,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<WrapTtl @onChange={{action (mut this.wrapTTL)}} />
|
<WrapTtl @onChange={{fn (mut @wrappedTTL)}} />
|
||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
@@ -70,12 +74,14 @@
|
|||||||
</M.Header>
|
</M.Header>
|
||||||
<M.Body>
|
<M.Body>
|
||||||
<h2 class="title is-6">Wrapped key</h2>
|
<h2 class="title is-6">Wrapped key</h2>
|
||||||
{{#if this.wrapTTL}}
|
{{#if @wrappedTTL}}
|
||||||
<Hds::Copy::Snippet
|
<Hds::Copy::Snippet
|
||||||
class="has-bottom-margin-m"
|
class="has-bottom-margin-m"
|
||||||
@textToCopy={{@wrappedToken}}
|
@textToCopy={{@wrappedToken}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-export-modal"
|
@container="#transit-export-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -3,12 +3,7 @@
|
|||||||
SPDX-License-Identifier: BUSL-1.1
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form
|
<form {{on "submit" (fn @doSubmit (hash input=@trackedInput algorithm=@algorithm key_version=@key_version))}} ...attributes>
|
||||||
onsubmit={{action
|
|
||||||
@doSubmit
|
|
||||||
(hash input=@input algorithm=@algorithm key_version=@key_version encodedBase64=@encodedBase64)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<NamespaceReminder @mode="perform" @noun="HMAC creation" />
|
<NamespaceReminder @mode="perform" @noun="HMAC creation" />
|
||||||
<div class="content has-bottom-margin-l">
|
<div class="content has-bottom-margin-l">
|
||||||
@@ -18,14 +13,20 @@
|
|||||||
as the named key.
|
as the named key.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
<KeyVersionSelect @key={{@key}} @onVersionChange={{fn (mut @key_version)}} @key_version={{@key_version}} />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div id="input-control" class="control is-relative">
|
<div id="input-control" class="control is-relative">
|
||||||
<JsonEditor @title="Input" @valueUpdated={{action (mut @input)}} @mode="ruby" @data-test-transit-input="input" />
|
<JsonEditor @title="Input" @valueUpdated={{fn (mut @trackedInput)}} @mode="ruby" @data-test-transit-input="input" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Input @type="checkbox" id="encodedBase64" @checked={{@encodedBase64}} data-test-transit-input="encodedBase64" />
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
id="encodedBase64"
|
||||||
|
@checked={{@encodedBase64}}
|
||||||
|
{{on "change" @toggleEncodeBase64}}
|
||||||
|
data-test-transit-input="encodedBase64"
|
||||||
|
/>
|
||||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -60,6 +61,8 @@
|
|||||||
@textToCopy={{@hmac}}
|
@textToCopy={{@hmac}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-hmac-modal"
|
@container="#transit-hmac-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -3,7 +3,10 @@
|
|||||||
SPDX-License-Identifier: BUSL-1.1
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form onsubmit={{action @doSubmit (hash ciphertext=@ciphertext context=@context nonce=@nonce key_version=@key_version)}}>
|
<form
|
||||||
|
{{on "submit" (fn @doSubmit (hash ciphertext=@ciphertext context=@context nonce=@nonce key_version=@key_version))}}
|
||||||
|
...attributes
|
||||||
|
>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<NamespaceReminder @mode="perform" @noun="rewrap" />
|
<NamespaceReminder @mode="perform" @noun="rewrap" />
|
||||||
<div class="content has-bottom-margin-l">
|
<div class="content has-bottom-margin-l">
|
||||||
@@ -13,10 +16,10 @@
|
|||||||
as the encryption key.
|
as the encryption key.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
<KeyVersionSelect @key={{@key}} @onVersionChange={{fn (mut @key_version)}} @key_version={{@key_version}} />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control is-expanded">
|
<div class="control is-expanded">
|
||||||
<JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @mode="ruby" />
|
<JsonEditor @title="Ciphertext" @valueUpdated={{fn (mut @ciphertext)}} @mode="ruby" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if @key.derived}}
|
{{#if @key.derived}}
|
||||||
@@ -72,6 +75,8 @@
|
|||||||
@textToCopy={{@ciphertext}}
|
@textToCopy={{@ciphertext}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-rewrap-modal"
|
@container="#transit-rewrap-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -4,18 +4,22 @@
|
|||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form
|
<form
|
||||||
onsubmit={{action
|
{{on
|
||||||
@doSubmit
|
"submit"
|
||||||
(hash
|
(fn
|
||||||
input=@input
|
@doSubmit
|
||||||
hash_algorithm=@hash_algorithm
|
(hash
|
||||||
signature_algorithm=@signature_algorithm
|
input=@trackedInput
|
||||||
key_version=@key_version
|
hash_algorithm=@hash_algorithm
|
||||||
context=@context
|
signature_algorithm=@signature_algorithm
|
||||||
prehashed=@prehashed
|
key_version=@key_version
|
||||||
encodedBase64=@encodedBase64
|
context=@context
|
||||||
|
prehashed=@prehashed
|
||||||
|
encodedBase64=@encodedBase64
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
...attributes
|
||||||
>
|
>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<NamespaceReminder @mode="perform" @noun="signing" />
|
<NamespaceReminder @mode="perform" @noun="signing" />
|
||||||
@@ -26,20 +30,26 @@
|
|||||||
as the encryption key and the specified hash algorithm.
|
as the encryption key and the specified hash algorithm.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
|
<KeyVersionSelect @key={{@key}} @onVersionChange={{fn (mut @key_version)}} @key_version={{@key_version}} />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control is-relative">
|
<div class="control is-relative">
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title="Input"
|
@title="Input"
|
||||||
@value={{@input}}
|
@value={{@trackedInput}}
|
||||||
@valueUpdated={{action (mut @input)}}
|
@valueUpdated={{fn (mut @trackedInput)}}
|
||||||
@mode="ruby"
|
@mode="ruby"
|
||||||
@data-test-transit-input="input"
|
@data-test-transit-input="input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Input @type="checkbox" id="encodedBase64" @checked={{@encodedBase64}} data-test-transit-input="encodedBase64" />
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
id="encodedBase64"
|
||||||
|
@checked={{@encodedBase64}}
|
||||||
|
{{on "change" @toggleEncodeBase64}}
|
||||||
|
data-test-transit-input="encodedBase64"
|
||||||
|
/>
|
||||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||||
</div>
|
</div>
|
||||||
{{#if @key.derived}}
|
{{#if @key.derived}}
|
||||||
@@ -114,7 +124,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<Hds::Button @text="Sign" @icon={{if @loading "loading"}} type="submit" disabled={{@loading}} />
|
<Hds::Button @text="Sign" @icon={{if @submitIsRunning "loading"}} type="submit" disabled={{@submitIsRunning}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -129,6 +139,8 @@
|
|||||||
@textToCopy={{@signature}}
|
@textToCopy={{@signature}}
|
||||||
@color="secondary"
|
@color="secondary"
|
||||||
@container="#transit-sign-modal"
|
@container="#transit-sign-modal"
|
||||||
|
@isFullWidth={{true}}
|
||||||
|
@isTruncated={{true}}
|
||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
|
@@ -4,19 +4,23 @@
|
|||||||
~}}
|
~}}
|
||||||
|
|
||||||
<form
|
<form
|
||||||
onsubmit={{action
|
{{on
|
||||||
@doSubmit
|
"submit"
|
||||||
(hash
|
(fn
|
||||||
input=@input
|
@doSubmit
|
||||||
signature=@signature
|
(hash
|
||||||
signature_algorithm=@signature_algorithm
|
input=@trackedInput
|
||||||
hmac=@hmac
|
signature=@signature
|
||||||
hash_algorithm=@hash_algorithm
|
signature_algorithm=@signature_algorithm
|
||||||
context=@context
|
hmac=@hmac
|
||||||
prehashed=@prehashed
|
hash_algorithm=@hash_algorithm
|
||||||
encodedBase64=@encodedBase64
|
context=@context
|
||||||
|
prehashed=@prehashed
|
||||||
|
encodedBase64=@encodedBase64
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
...attributes
|
||||||
>
|
>
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<div class="content has-bottom-margin-l">
|
<div class="content has-bottom-margin-l">
|
||||||
@@ -26,15 +30,21 @@
|
|||||||
<div class="control is-relative">
|
<div class="control is-relative">
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title="Input"
|
@title="Input"
|
||||||
@value={{@input}}
|
@value={{@trackedInput}}
|
||||||
@valueUpdated={{action (mut @input)}}
|
@valueUpdated={{fn (mut @trackedInput)}}
|
||||||
@mode="ruby"
|
@mode="ruby"
|
||||||
@data-test-transit-input="input"
|
@data-test-transit-input="input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Input @type="checkbox" id="encodedBase64" @checked={{@encodedBase64}} data-test-transit-input="encodedBase64" />
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
id="encodedBase64"
|
||||||
|
@checked={{@encodedBase64}}
|
||||||
|
{{on "change" @toggleEncodeBase64}}
|
||||||
|
data-test-transit-input="encodedBase64"
|
||||||
|
/>
|
||||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||||
</div>
|
</div>
|
||||||
{{#if (and @key.supportsSigning @key.derived (not @hmac))}}
|
{{#if (and @key.supportsSigning @key.derived (not @hmac))}}
|
||||||
@@ -64,7 +74,7 @@
|
|||||||
id="verification"
|
id="verification"
|
||||||
onchange={{queue
|
onchange={{queue
|
||||||
(action (mut @verification) value="target.value")
|
(action (mut @verification) value="target.value")
|
||||||
(action @clearParams (array "hmac" "signature"))
|
(fn @clearSpecificProps (array "hmac" "signature"))
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{{#each (array "Signature" "HMAC") as |type|}}
|
{{#each (array "Signature" "HMAC") as |type|}}
|
||||||
@@ -145,18 +155,13 @@
|
|||||||
{{#if (or (and @verification (eq @verification "HMAC")) @hmac)}}
|
{{#if (or (and @verification (eq @verification "HMAC")) @hmac)}}
|
||||||
<div class="field is-flex-column is-flex-grow-1">
|
<div class="field is-flex-column is-flex-grow-1">
|
||||||
<div class="control is-flex-column is-flex-grow-1">
|
<div class="control is-flex-column is-flex-grow-1">
|
||||||
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
|
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{fn (mut @hmac)}} @mode="ruby" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="field is-flex-column is-flex-grow-1">
|
<div class="field is-flex-column is-flex-grow-1">
|
||||||
<div class="control is-flex-column is-flex-grow-1">
|
<div class="control is-flex-column is-flex-grow-1">
|
||||||
<JsonEditor
|
<JsonEditor @title="Signature" @value={{@signature}} @valueUpdated={{fn (mut @signature)}} @mode="ruby" />
|
||||||
@title="Signature"
|
|
||||||
@value={{@signature}}
|
|
||||||
@valueUpdated={{action (mut @signature)}}
|
|
||||||
@mode="ruby"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -165,7 +170,7 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
|
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{fn (mut @hmac)}} @mode="ruby" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -186,7 +191,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<Hds::Button @text="Verify" @icon={{if @loading "loading"}} type="submit" disabled={{@loading}} />
|
<Hds::Button @text="Verify" @icon={{if @submitIsRunning "loading"}} type="submit" disabled={{@submitIsRunning}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@@ -1,55 +0,0 @@
|
|||||||
{{!
|
|
||||||
Copyright (c) HashiCorp, Inc.
|
|
||||||
SPDX-License-Identifier: BUSL-1.1
|
|
||||||
~}}
|
|
||||||
|
|
||||||
{{#if (eq this.selectedAction "rotate")}}
|
|
||||||
{{#if this.key.canRotate}}
|
|
||||||
<ConfirmAction
|
|
||||||
@buttonText="Rotate encryption key"
|
|
||||||
class="toolbar-button"
|
|
||||||
@buttonColor="secondary"
|
|
||||||
@confirmTitle="Rotate this key?"
|
|
||||||
@confirmMessage="After rotation, all key actions will default to using the newest version of the key."
|
|
||||||
@modalColor="warning"
|
|
||||||
@onConfirmAction={{action "doSubmit"}}
|
|
||||||
data-test-transit-key-rotate
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<MessageError @errors={{this.errors}} />
|
|
||||||
{{#if this.selectedAction}}
|
|
||||||
<div data-test-transit-action={{this.selectedAction}}>
|
|
||||||
{{! template-lint-disable no-passed-in-event-handlers }}
|
|
||||||
{{component
|
|
||||||
(concat "transit-key-action/" this.selectedAction)
|
|
||||||
key=this.key
|
|
||||||
keys=this.keys
|
|
||||||
keyIsRSA=this.keyIsRSA
|
|
||||||
verification=this.verification
|
|
||||||
hmac=this.hmac
|
|
||||||
param=this.param
|
|
||||||
key_version=this.key_version
|
|
||||||
isModalActive=this.isModalActive
|
|
||||||
ciphertext=this.ciphertext
|
|
||||||
plaintext=this.plaintext
|
|
||||||
context=this.context
|
|
||||||
nonce=this.nonce
|
|
||||||
input=this.input
|
|
||||||
bits=this.bits
|
|
||||||
algorithm=this.algorithm
|
|
||||||
signature=this.signature
|
|
||||||
signature_algorithm=this.signature_algorithm
|
|
||||||
hash_algorithm=this.hash_algorithm
|
|
||||||
prehashed=this.prehashed
|
|
||||||
wrappedToken=this.wrappedToken
|
|
||||||
exportKeyType=this.exportKeyType
|
|
||||||
exportKeyVersion=this.exportKeyVersion
|
|
||||||
encodedBase64=this.encodedBase64
|
|
||||||
valid=this.valid
|
|
||||||
doSubmit=(action "doSubmit")
|
|
||||||
clearParams=(action "clearParams")
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
@@ -57,12 +57,6 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TransitKeyActions
|
<TransitKeyActions @selectedAction={{this.selectedAction}} @key={{this.model}} />
|
||||||
@selectedAction={{this.selectedAction}}
|
|
||||||
@backend={{this.backend}}
|
|
||||||
@key={{this.model}}
|
|
||||||
@capabilities={{this.capabilities}}
|
|
||||||
@onRefresh={{action "refresh"}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@@ -23,7 +23,7 @@ const SELECTORS = {
|
|||||||
form: (item) => `[data-test-transit-key="${item}"]`,
|
form: (item) => `[data-test-transit-key="${item}"]`,
|
||||||
versionRow: (version) => `[data-test-transit-version="${version}"]`,
|
versionRow: (version) => `[data-test-transit-version="${version}"]`,
|
||||||
rotate: {
|
rotate: {
|
||||||
trigger: '[data-test-confirm-action-trigger]',
|
trigger: '[data-test-transit-key-rotate]',
|
||||||
confirm: '[data-test-confirm-button]',
|
confirm: '[data-test-confirm-button]',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -344,6 +344,7 @@ module('Acceptance | transit (flaky)', function (hooks) {
|
|||||||
|
|
||||||
await click(SELECTORS.versionsTab);
|
await click(SELECTORS.versionsTab);
|
||||||
assert.dom(SELECTORS.versionRow(1)).hasTextContaining('Version 1', `${name}: only one key version`);
|
assert.dom(SELECTORS.versionRow(1)).hasTextContaining('Version 1', `${name}: only one key version`);
|
||||||
|
|
||||||
await waitUntil(() => find(SELECTORS.rotate.trigger));
|
await waitUntil(() => find(SELECTORS.rotate.trigger));
|
||||||
await click(SELECTORS.rotate.trigger);
|
await click(SELECTORS.rotate.trigger);
|
||||||
await click(SELECTORS.rotate.confirm);
|
await click(SELECTORS.rotate.confirm);
|
||||||
|
@@ -60,7 +60,7 @@ module('Integration | Component | transit key actions', function (hooks) {
|
|||||||
render(hbs`
|
render(hbs`
|
||||||
<TransitKeyActions />`);
|
<TransitKeyActions />`);
|
||||||
const err = await promise;
|
const err = await promise;
|
||||||
assert.ok(err.message.includes('`key` is required for'), 'asserts without key');
|
assert.ok(err.message.includes('@key is required for'), 'asserts without key');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders', async function (assert) {
|
test('it renders', async function (assert) {
|
||||||
@@ -102,19 +102,6 @@ module('Integration | Component | transit key actions', function (hooks) {
|
|||||||
.exists({ count: 1 }, 'renders signature_algorithm field on verify with rsa key');
|
.exists({ count: 1 }, 'renders signature_algorithm field on verify with rsa key');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders: rotate', async function (assert) {
|
|
||||||
this.set('key', { backend: 'transit', id: 'akey', supportedActions: ['rotate'] });
|
|
||||||
await render(hbs`
|
|
||||||
<TransitKeyActions @selectedAction="rotate" @key={{this.key}} />`);
|
|
||||||
|
|
||||||
assert.dom('*').hasText('', 'renders an empty div');
|
|
||||||
|
|
||||||
this.set('key.canRotate', true);
|
|
||||||
assert
|
|
||||||
.dom('button')
|
|
||||||
.hasText('Rotate encryption key', 'renders confirm-button when key.canRotate is true');
|
|
||||||
});
|
|
||||||
|
|
||||||
async function doEncrypt(assert, actions = [], keyattrs = {}) {
|
async function doEncrypt(assert, actions = [], keyattrs = {}) {
|
||||||
const keyDefaults = { backend: 'transit', id: 'akey', supportedActions: ['encrypt'].concat(actions) };
|
const keyDefaults = { backend: 'transit', id: 'akey', supportedActions: ['encrypt'].concat(actions) };
|
||||||
|
|
||||||
@@ -312,7 +299,7 @@ module('Integration | Component | transit key actions', function (hooks) {
|
|||||||
validKeyVersions: [1],
|
validKeyVersions: [1],
|
||||||
});
|
});
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<TransitKeyActions @key={{this.key}} />`);
|
<TransitKeyActions @key={{this.key}} @selectedAction="hmac" />`);
|
||||||
await fillIn('#algorithm', 'sha2-384');
|
await fillIn('#algorithm', 'sha2-384');
|
||||||
await blur('#algorithm');
|
await blur('#algorithm');
|
||||||
await fillIn('[data-test-component="code-mirror-modifier"] textarea', 'plaintext');
|
await fillIn('[data-test-component="code-mirror-modifier"] textarea', 'plaintext');
|
||||||
|
Reference in New Issue
Block a user