import Ember from 'ember'; import { inject as service } from '@ember/service'; import { computed, set } from '@ember/object'; import Component from '@ember/component'; import { task } from 'ember-concurrency'; import { methods } from 'vault/helpers/mountable-auth-methods'; import { engines, KMIP, TRANSFORM } from 'vault/helpers/mountable-secret-engines'; import { waitFor } from '@ember/test-waiters'; const METHODS = methods(); const ENGINES = engines(); export default Component.extend({ store: service(), wizard: service(), flashMessages: service(), version: service(), /* * @param Function * @public * * Optional param to call a function upon successfully mounting a backend * */ onMountSuccess() {}, /* * @param String * @public * the type of backend we want to mount * defaults to `auth` * */ mountType: 'auth', /* * * @param DS.Model * @private * Ember Data model corresponding to the `mountType`. * Created and set during `init` * */ mountModel: null, showEnable: false, // cp-validation related properties validationMessages: null, isFormInvalid: false, mountIssue: false, init() { this._super(...arguments); const type = this.mountType; const modelType = type === 'secret' ? 'secret-engine' : 'auth-method'; const model = this.store.createRecord(modelType); this.set('mountModel', model); this.set('validationMessages', { path: '', }); }, mountTypes: computed('engines', 'mountType', function () { return this.mountType === 'secret' ? this.engines : METHODS; }), engines: computed('version.{features[],isEnterprise}', function () { if (this.version.isEnterprise) { return ENGINES.concat([KMIP, TRANSFORM]); } return ENGINES; }), willDestroy() { this._super(...arguments); // if unsaved, we want to unload so it doesn't show up in the auth mount list 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.set('path', type); } }, mountBackend: task( waitFor(function* () { const mountModel = this.mountModel; const { type, path } = mountModel; 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.onMountSuccess(type, path); return; } else { throw err; } } if (!capabilities.get('canUpdate')) { // if there is no sys/mount issue then error is config endpoint. 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, ]; } try { yield mountModel.save(); } catch (err) { if (err.message === 'mountIssue') { this.mountIssue = true; this.set('isFormInvalid', this.mountIssue); this.flashMessages.danger( 'You do not have access to the sys/mounts endpoint. The secret engine was not mounted.' ); return; } this.set('errorMessage', 'This mount path already exist.'); return; } let mountType = this.mountType; mountType = mountType === 'secret' ? `${mountType}s engine` : `${mountType} method`; this.flashMessages.success(`Successfully mounted the ${type} ${mountType} at ${path}.`); yield this.onMountSuccess(type, path); return; }) ).drop(), actions: { onKeyUp(name, value) { // validate path if (name === 'path') { this.mountModel.set('path', value); this.mountModel.validations.attrs.path.isValid ? set(this.validationMessages, 'path', '') : set(this.validationMessages, 'path', this.mountModel.validations.attrs.path.message); } // check maxVersions is a number if (name === 'maxVersions') { this.mountModel.set('maxVersions', value); this.mountModel.validations.attrs.maxVersions.isValid ? set(this.validationMessages, 'maxVersions', '') : set( this.validationMessages, 'maxVersions', this.mountModel.validations.attrs.maxVersions.message ); } this.mountModel.validate().then(({ validations }) => { this.set('isFormInvalid', !validations.isValid); }); }, onTypeChange(path, value) { if (path === 'type') { this.wizard.set('componentState', value); this.checkPathChange(value); } }, toggleShowEnable(value) { this.set('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); } }, }, });