mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 10:37:56 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			195 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			6.0 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) {
 | |
|     const mount = this.mountModel;
 | |
|     const currentPath = mount.path;
 | |
|     const 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
 | |
|     const 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;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const changedAttrKeys = Object.keys(mountModel.changedAttributes());
 | |
|     const 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) {
 | |
|         const 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);
 | |
|     }
 | |
|   }
 | |
| }
 | 
