mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-18 12:37:02 +02:00
* Remove component: diff version selector * delete SecretVersionMenu * remove secret logic from GetCredentialsCard * remove DiffVersionSelector hbs file and references * delete more css for diff version view * remove diff route * fix credential card selector * ui: refactor SecretFormShow (#22723) * refactor secret form show * fix selector typo * remove version route (#22738) * Remove old KV2 delete things (#23015) * remove kv2 old delete things * comment * Remove old metadata (#22747) * wip to remove metadata * review comments * UI/remove kv2 secret create or update (#23039) * remove is v2 param * permissions clean up * remove version things * remove excess from form show * clean up * created time was never a thing for cubbyhole, confirmed on api * update tune test * fix control group tests: * Remove kv v2 models (#23087) * remove is v2 param * permissions clean up * remove version things * remove excess from form show * clean up * created time was never a thing for cubbyhole, confirmed on api * update tune test * fix control group tests: * remove models * Update ui/app/models/secret-engine.js Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> * blah prettier --------- Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> * UI/config update (#23111) * sweep through clean up * remove component * remove unused selectors * remove unncessary --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Co-authored-by: clairebontempo@gmail.com <clairebontempo@gmail.com> Co-authored-by: Angel Garbarino <Monkeychip@users.noreply.github.com> Co-authored-by: Angel Garbarino <angel@hashicorp.com>
222 lines
6.4 KiB
JavaScript
222 lines
6.4 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
/**
|
|
* @module SecretCreateOrUpdate
|
|
* SecretCreateOrUpdate component displays either the form for creating a new secret or creating a new version of the secret
|
|
*
|
|
* @example
|
|
* ```js
|
|
* <SecretCreateOrUpdate
|
|
* @mode="create"
|
|
* @model={{model}}
|
|
* @showAdvancedMode=true
|
|
* @modelForData={{@modelForData}}
|
|
* @secretData={{@secretData}}
|
|
* @buttonDisabled={{this.saving}}
|
|
* />
|
|
* ```
|
|
* @param {string} mode - create, edit, show determines what view to display
|
|
* @param {object} model - the route model
|
|
* @param {boolean} showAdvancedMode - whether or not to show the JSON editor
|
|
* @param {object} modelForData - a class that helps track secret data, defined in secret-edit
|
|
* @param {object} secretData - class that is created in secret-edit
|
|
* @param {boolean} buttonDisabled - if true, disables the submit button on the create/update form
|
|
*/
|
|
|
|
import Component from '@glimmer/component';
|
|
import ControlGroupError from 'vault/lib/control-group-error';
|
|
import Ember from 'ember';
|
|
import keys from 'core/utils/key-codes';
|
|
import { action, set } from '@ember/object';
|
|
import { inject as service } from '@ember/service';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { isBlank, isNone } from '@ember/utils';
|
|
import { task, waitForEvent } from 'ember-concurrency';
|
|
|
|
const LIST_ROUTE = 'vault.cluster.secrets.backend.list';
|
|
const LIST_ROOT_ROUTE = 'vault.cluster.secrets.backend.list-root';
|
|
const SHOW_ROUTE = 'vault.cluster.secrets.backend.show';
|
|
|
|
export default class SecretCreateOrUpdate extends Component {
|
|
@tracked codemirrorString = null;
|
|
@tracked error = null;
|
|
@tracked secretPaths = null;
|
|
@tracked pathWhiteSpaceWarning = false;
|
|
@tracked validationErrorCount = 0;
|
|
@tracked validationMessages = null;
|
|
|
|
@service controlGroup;
|
|
@service flashMessages;
|
|
@service router;
|
|
@service store;
|
|
|
|
@action
|
|
setup(elem, [secretData, mode]) {
|
|
this.codemirrorString = secretData.toJSONString();
|
|
this.validationMessages = {
|
|
path: '',
|
|
};
|
|
// for validation, return array of path names already assigned
|
|
if (Ember.testing) {
|
|
this.secretPaths = ['beep', 'bop', 'boop'];
|
|
}
|
|
this.checkRows();
|
|
|
|
if (mode === 'edit') {
|
|
this.addRow();
|
|
}
|
|
}
|
|
checkRows() {
|
|
if (this.args.secretData.length === 0) {
|
|
this.addRow();
|
|
}
|
|
}
|
|
checkValidation(name, value) {
|
|
if (name === 'path') {
|
|
// check for whitespace
|
|
this.pathHasWhiteSpace(value);
|
|
!value
|
|
? set(this.validationMessages, name, `${name} can't be blank.`)
|
|
: set(this.validationMessages, name, '');
|
|
}
|
|
const values = Object.values(this.validationMessages);
|
|
this.validationErrorCount = values.filter(Boolean).length;
|
|
}
|
|
onEscape(e) {
|
|
if (e.keyCode !== keys.ESC || this.args.mode !== 'show') {
|
|
return;
|
|
}
|
|
const parentKey = this.args.model.parentKey;
|
|
if (parentKey) {
|
|
this.transitionToRoute(LIST_ROUTE, parentKey);
|
|
} else {
|
|
this.transitionToRoute(LIST_ROOT_ROUTE);
|
|
}
|
|
}
|
|
pathHasWhiteSpace(value) {
|
|
const validation = new RegExp('\\s', 'g'); // search for whitespace
|
|
this.pathWhiteSpaceWarning = validation.test(value);
|
|
}
|
|
// successCallback is called in the context of the component
|
|
persistKey(successCallback) {
|
|
const secret = this.args.model;
|
|
const secretData = this.args.modelForData;
|
|
|
|
let key = secretData.get('path') || secret.id;
|
|
|
|
if (key.startsWith('/')) {
|
|
key = key.replace(/^\/+/g, '');
|
|
secretData.set(secretData.pathAttr, key);
|
|
}
|
|
|
|
return secretData
|
|
.save()
|
|
.then(() => {
|
|
if (!secretData.isError) {
|
|
this.saveComplete(successCallback, key);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
if (error instanceof ControlGroupError) {
|
|
const errorMessage = this.controlGroup.logFromError(error);
|
|
this.error = errorMessage.content;
|
|
this.controlGroup.saveTokenFromError(error);
|
|
}
|
|
throw error;
|
|
});
|
|
}
|
|
saveComplete(callback, key) {
|
|
callback(key);
|
|
}
|
|
transitionToRoute() {
|
|
return this.router.transitionTo(...arguments);
|
|
}
|
|
|
|
@(task(function* (name, value) {
|
|
this.checkValidation(name, value);
|
|
while (true) {
|
|
const event = yield waitForEvent(document.body, 'keyup');
|
|
this.onEscape(event);
|
|
}
|
|
})
|
|
.on('didInsertElement')
|
|
.cancelOn('willDestroyElement'))
|
|
waitForKeyUp;
|
|
|
|
@action
|
|
addRow() {
|
|
const data = this.args.secretData;
|
|
// fired off on init
|
|
if (isNone(data.findBy('name', ''))) {
|
|
data.pushObject({ name: '', value: '' });
|
|
this.handleChange();
|
|
}
|
|
this.checkRows();
|
|
}
|
|
@action
|
|
codemirrorUpdated(val, codemirror) {
|
|
this.error = null;
|
|
codemirror.performLint();
|
|
const noErrors = codemirror.state.lint.marked.length === 0;
|
|
if (noErrors) {
|
|
try {
|
|
this.args.secretData.fromJSONString(val);
|
|
set(this.args.modelForData, 'secretData', this.args.secretData.toJSON());
|
|
} catch (e) {
|
|
this.error = e.message;
|
|
}
|
|
}
|
|
this.codemirrorString = val;
|
|
}
|
|
@action
|
|
createOrUpdateKey(type, event) {
|
|
event.preventDefault();
|
|
if (type === 'create' && isBlank(this.args.modelForData.path || this.args.modelForData.id)) {
|
|
this.checkValidation('path', '');
|
|
return;
|
|
}
|
|
|
|
const secretPath = type === 'create' ? this.args.modelForData.path : this.args.model.id;
|
|
this.persistKey(() => {
|
|
// Show flash message in case there's a control group on read
|
|
this.flashMessages.success(
|
|
`Secret ${secretPath} ${type === 'create' ? 'created' : 'updated'} successfully.`
|
|
);
|
|
this.transitionToRoute(SHOW_ROUTE, secretPath);
|
|
});
|
|
}
|
|
@action
|
|
deleteRow(name) {
|
|
const data = this.args.secretData;
|
|
const item = data.findBy('name', name);
|
|
if (isBlank(item.name)) {
|
|
return;
|
|
}
|
|
data.removeObject(item);
|
|
this.checkRows();
|
|
this.handleChange();
|
|
}
|
|
@action
|
|
formatJSON() {
|
|
this.codemirrorString = this.args.secretData.toJSONString(true);
|
|
}
|
|
@action
|
|
handleMaskedInputChange(secret, index, value) {
|
|
const row = { ...secret, value };
|
|
set(this.args.secretData, index, row);
|
|
this.handleChange();
|
|
}
|
|
@action
|
|
handleChange() {
|
|
this.codemirrorString = this.args.secretData.toJSONString(true);
|
|
set(this.args.modelForData, 'secretData', this.args.secretData.toJSON());
|
|
}
|
|
@action
|
|
updateValidationErrorCount(errorCount) {
|
|
this.validationErrorCount = errorCount;
|
|
}
|
|
}
|