vault/ui/app/components/transit-key-actions.js
hashicorp-copywrite[bot] 0b12cdcfd1
[COMPLIANCE] License changes (#22290)
* Adding explicit MPL license for sub-package.

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Adding explicit MPL license for sub-package.

This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository.

* Updating the license from MPL to Business Source License.

Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl.

* add missing license headers

* Update copyright file headers to BUS-1.1

* Fix test that expected exact offset on hcl file

---------

Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
Co-authored-by: Sarah Thompson <sthompson@hashicorp.com>
Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
2023-08-10 18:14:03 -07:00

255 lines
6.9 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { assign } from '@ember/polyfills';
import { copy } from 'ember-copy';
import { assert } from '@ember/debug';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { set, get, computed } from '@ember/object';
import { encodeString } from 'vault/utils/b64';
const TRANSIT_PARAMS = {
hash_algorithm: 'sha2-256',
algorithm: 'sha2-256',
signature_algorithm: 'pss',
bits: 256,
bytes: 32,
ciphertext: null,
context: null,
format: 'base64',
hmac: null,
input: null,
key_version: 0,
keys: null,
nonce: null,
param: 'wrapped',
prehashed: false,
plaintext: null,
random_bytes: null,
signature: null,
sum: null,
encodedBase64: false,
exportKeyType: null,
exportKeyVersion: null,
wrappedToken: null,
valid: null,
plaintextOriginal: null,
didDecode: false,
verification: 'Signature',
};
const PARAMS_FOR_ACTION = {
sign: ['input', 'hash_algorithm', 'key_version', 'prehashed', 'signature_algorithm'],
verify: ['input', 'hmac', 'signature', 'hash_algorithm', 'prehashed'],
hmac: ['input', 'algorithm', 'key_version'],
encrypt: ['plaintext', 'context', 'nonce', 'key_version'],
decrypt: ['ciphertext', 'context', 'nonce'],
rewrap: ['ciphertext', 'context', 'nonce', 'key_version'],
};
const SUCCESS_MESSAGE_FOR_ACTION = {
sign: 'Signed your data',
// the verify action doesn't trigger a success message
hmac: 'Created your hash output',
encrypt: 'Created a wrapped token for your data',
decrypt: 'Decrypted the data from your token',
rewrap: 'Created a new token for your data',
datakey: 'Generated your key',
export: 'Exported your key',
};
export default Component.extend(TRANSIT_PARAMS, {
store: service(),
flashMessages: service(),
// public attrs
selectedAction: null,
key: null,
isModalActive: false,
onRefresh() {},
init() {
this._super(...arguments);
// TODO figure out why get is needed here Ember Upgrade
// eslint-disable-next-line ember/no-get
if (this.selectedAction) {
return;
}
// 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() {
this._super(...arguments);
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';
}),
getModelInfo() {
const model = this.key || this.backend;
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) {
let props = {};
if (resp && resp.data) {
if (action === 'export' && resp.data.keys) {
const { keys, type, name } = resp.data;
resp.data.keys = { keys, type, name };
}
props = assign({}, props, resp.data);
}
if (options.wrapTTL) {
props = assign({}, props, { wrappedToken: resp.wrap_info.token });
}
if (!this.isDestroyed && !this.isDestroying) {
this.toggleProperty('isModalActive');
this.setProperties(props);
}
if (action === 'rotate') {
this.onRefresh();
}
this.triggerSuccessMessage(action);
},
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) => {
if (key === 'signature_algorithm' && !isRSA) {
return result;
}
if (data[key]) {
result[key] = data[key];
}
return result;
}, {});
},
actions: {
onActionChange(action) {
set(this, 'selectedAction', action);
this.checkAction();
},
onClear() {
this.resetParams(null, this.selectedAction);
},
clearParams(params) {
const arr = Array.isArray(params) ? params : [params];
arr.forEach((param) => this.set(param, null));
},
toggleModal(successMessage) {
if (!!successMessage && typeof successMessage === 'string') {
this.flashMessages.success(successMessage);
}
this.toggleProperty('isModalActive');
},
doSubmit(data, options = {}, maybeEvent) {
const event = options.type === 'submit' ? options : maybeEvent;
if (event) {
event.preventDefault();
}
const { backend, id } = this.getModelInfo();
const action = this.selectedAction;
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({
errors: null,
result: null,
});
this.store
.adapterFor('transit-key')
.keyAction(action, { backend, id, payload }, options)
.then(
(resp) => this.handleSuccess(resp, options, action),
(...errArgs) => this.handleError(...errArgs)
);
},
},
});