open-vault/ui/app/components/mount-backend-form.js
Jordan Reimer be632db682
Ember Upgrade to 4.4 (#17086)
* runs ember-cli-update to 4.4.0

* updates yarn.lock

* updates dependencies causing runtime errors (#17135)

* Inject Store Service When Accessed Implicitly (#17345)

* adds codemod for injecting store service

* adds custom babylon parser with decorators-legacy plugin for jscodeshift transforms

* updates inject-store-service codemod to only look for .extend object expressions and adds recast options

* runs inject-store-service codemod on js files

* replace query-params helper with hash (#17404)

* Updates/removes dependencies throwing errors in Ember 4.4 (#17396)

* updates ember-responsive to latest

* updates ember-composable-helpers to latest and uses includes helper since contains was removed

* updates ember-concurrency to latest

* updates ember-cli-clipboard to latest

* temporary workaround for toolbar-link component throwing errors for using params arg with LinkTo

* adds missing store injection to auth configure route

* fixes issue with string-list component throwing error for accessing prop in same computation

* fixes non-iterable query params issue in mfa methods controller

* refactors field-to-attrs to handle belongsTo rather than fragments

* converts mount-config fragment to belongsTo on auth-method model

* removes ember-api-actions and adds tune method to auth-method adapter

* converts cluster replication attributes from fragment to relationship

* updates ember-data, removes ember-data-fragments and updates yarn to latest

* removes fragments from secret-engine model

* removes fragment from test-form-model

* removes commented out code

* minor change to inject-store-service codemod and runs again on js files

* Remove LinkTo positional params (#17421)

* updates ember-cli-page-object to latest version

* update toolbar-link to support link-to args and not positional params

* adds replace arg to toolbar-link component

* Clean up js lint errors (#17426)

* replaces assert.equal to assert.strictEqual

* update eslint no-console to error and disables invididual intended uses of console

* cleans up hbs lint warnings (#17432)

* Upgrade bug and test fixes (#17500)

* updates inject-service codemod to take arg for service name and runs for flashMessages service

* fixes hbs lint error after merging main

* fixes flash messages

* updates more deps

* bug fixes

* test fixes

* updates ember-cli-content-security-policy and prevents default form submission throwing errors

* more bug and test fixes

* removes commented out code

* fixes issue with code-mirror modifier sending change event on setup causing same computation error

* Upgrade Clean Up (#17543)

* updates deprecation workflow and filter

* cleans up build errors, removes unused ivy-codemirror and sass and updates ember-cli-sass and node-sass to latest

* fixes control groups test that was skipped after upgrade

* updates control group service tests

* addresses review feedback

* updates control group service handleError method to use router.currentURL rather that transition.intent.url

* adds changelog entry
2022-10-18 09:46:02 -06:00

195 lines
6 KiB
JavaScript

import Ember from 'ember';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { action, setProperties } from '@ember/object';
import { task } from 'ember-concurrency';
import { methods } from 'vault/helpers/mountable-auth-methods';
import { engines, KMIP, TRANSFORM, KEYMGMT } from 'vault/helpers/mountable-secret-engines';
import { waitFor } from '@ember/test-waiters';
/**
* @module MountBackendForm
* The `MountBackendForm` is used to mount either a secret or auth backend.
*
* @example ```js
* <MountBackendForm @mountType="secret" @onMountSuccess={{this.onMountSuccess}} />```
*
* @param {function} onMountSuccess - A function that transitions once the Mount has been successfully posted.
* @param {string} [mountType=auth] - The type of backend we want to mount.
*
*/
const METHODS = methods();
const ENGINES = engines();
export default class MountBackendForm extends Component {
@service store;
@service wizard;
@service flashMessages;
@service version;
get mountType() {
return this.args.mountType || 'auth';
}
@tracked mountModel = null;
@tracked showEnable = false;
// validation related properties
@tracked modelValidations = null;
@tracked invalidFormAlert = null;
@tracked mountIssue = false;
@tracked errors = '';
@tracked errorMessage = '';
constructor() {
super(...arguments);
const type = this.args.mountType || 'auth';
const modelType = type === 'secret' ? 'secret-engine' : 'auth-method';
const model = this.store.createRecord(modelType);
model.set('config', this.store.createRecord('mount-config'));
this.mountModel = model;
}
get mountTypes() {
return this.mountType === 'secret' ? this.engines : METHODS;
}
get engines() {
if (this.version.isEnterprise) {
return ENGINES.concat([KMIP, TRANSFORM, KEYMGMT]);
}
return ENGINES;
}
willDestroy() {
// if unsaved, we want to unload so it doesn't show up in the auth mount list
super.willDestroy(...arguments);
this.mountModel.rollbackAttributes();
}
checkPathChange(type) {
let mount = this.mountModel;
let currentPath = mount.path;
let list = this.mountTypes;
// if the current path matches a type (meaning the user hasn't altered it),
// change it here to match the new type
let isUnchanged = list.findBy('type', currentPath);
if (!currentPath || isUnchanged) {
mount.path = type;
}
}
checkModelValidity(model) {
const { isValid, state, invalidFormMessage } = model.validate();
setProperties(this, {
modelValidations: state,
invalidFormAlert: invalidFormMessage,
});
return isValid;
}
@task
@waitFor
*mountBackend(event) {
event.preventDefault();
const mountModel = this.mountModel;
const { type, path } = mountModel;
// only submit form if validations pass
if (!this.checkModelValidity(mountModel)) {
return;
}
let capabilities = null;
try {
capabilities = yield this.store.findRecord('capabilities', `${path}/config`);
} catch (err) {
if (Ember.testing) {
//captures mount-backend-form component test
yield mountModel.save();
let mountType = this.mountType;
mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`;
this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`);
yield this.args.onMountSuccess(type, path);
return;
} else {
throw err;
}
}
let changedAttrKeys = Object.keys(mountModel.changedAttributes());
let updatesConfig =
changedAttrKeys.includes('casRequired') ||
changedAttrKeys.includes('deleteVersionAfter') ||
changedAttrKeys.includes('maxVersions');
try {
yield mountModel.save();
} catch (err) {
if (err.httpStatus === 403) {
this.mountIssue = true;
this.flashMessages.danger(
'You do not have access to the sys/mounts endpoint. The secret engine was not mounted.'
);
return;
}
if (err.errors) {
let errors = err.errors.map((e) => {
if (typeof e === 'object') return e.title || e.message || JSON.stringify(e);
return e;
});
this.errors = errors;
} else if (err.message) {
this.errorMessage = err.message;
} else {
this.errorMessage = 'An error occurred, check the vault logs.';
}
return;
}
// mountModel must be after the save
if (mountModel.isV2KV && updatesConfig && !capabilities.get('canUpdate')) {
// config error is not thrown from secret-engine adapter, so handling here
this.flashMessages.warning(
'You do not have access to the config endpoint. The secret engine was mounted, but the configuration settings were not saved.'
);
// remove the config data from the model otherwise it will save it even if the network request failed.
[this.mountModel.maxVersions, this.mountModel.casRequired, this.mountModel.deleteVersionAfter] = [
0,
false,
0,
];
}
let mountType = this.mountType;
mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`;
this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`);
yield this.args.onMountSuccess(type, path);
return;
}
@action
onKeyUp(name, value) {
this.mountModel.set(name, value);
}
@action
onTypeChange(path, value) {
if (path === 'type') {
this.wizard.set('componentState', value);
this.checkPathChange(value);
}
}
@action
toggleShowEnable(value) {
this.showEnable = value;
if (value === true && this.wizard.featureState === 'idle') {
this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', this.mountModel.type);
} else {
this.wizard.transitionFeatureMachine(this.wizard.featureState, 'RESET', this.mountModel.type);
}
}
}