UI: glimmerize generate credentials component (#27405)

This commit is contained in:
Chelsea Shaw
2024-06-10 12:49:05 -05:00
committed by GitHub
parent b0864e3f54
commit 7e70e3fd52
17 changed files with 444 additions and 396 deletions

View File

@@ -4,56 +4,49 @@
*/
import { service } from '@ember/service';
import { computed, set } from '@ember/object';
import Component from '@ember/component';
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
const MODEL_TYPES = {
'ssh-sign': {
model: 'ssh-sign',
},
'ssh-creds': {
const CREDENTIAL_TYPES = {
ssh: {
model: 'ssh-otp-credential',
title: 'Generate SSH Credentials',
formFields: ['username', 'ip'],
displayFields: ['username', 'ip', 'key', 'keyType', 'port'],
},
'aws-creds': {
aws: {
model: 'aws-credential',
title: 'Generate AWS Credentials',
backIsListLink: true,
displayFields: ['accessKey', 'secretKey', 'securityToken', 'leaseId', 'renewable', 'leaseDuration'],
// aws form fields are dynamic
formFields: (model) => {
return {
iam_user: ['credentialType'],
assumed_role: ['credentialType', 'ttl', 'roleArn'],
federation_token: ['credentialType', 'ttl'],
session_token: ['credentialType', 'ttl'],
}[model.credentialType];
},
},
};
export default Component.extend({
controlGroup: service(),
store: service(),
router: service(),
// set on the component
backendType: null,
backendPath: null,
roleName: null,
action: null,
export default class GenerateCredentials extends Component {
@service controlGroup;
@service store;
@service router;
model: null,
loading: false,
emptyData: '{\n}',
@tracked model;
@tracked loading = false;
@tracked hasGenerated = false;
emptyData = '{\n}';
modelForType() {
const type = this.options;
if (type) {
return type.model;
}
// if we don't have a mode for that type then redirect them back to the backend list
this.router.transitionTo('vault.cluster.secrets.backend.list-root', this.backendPath);
},
options: computed('action', 'backendType', function () {
const action = this.action || 'creds';
return MODEL_TYPES[`${this.backendType}-${action}`];
}),
init() {
this._super(...arguments);
this.createOrReplaceModel();
},
constructor() {
super(...arguments);
const modelType = this.modelForType();
this.model = this.generateNewModel(modelType);
}
willDestroy() {
// components are torn down after store is unloaded and will cause an error if attempt to unload record
@@ -61,20 +54,46 @@ export default Component.extend({
if (noTeardown && !this.model.isDestroyed && !this.model.isDestroying) {
this.model.unloadRecord();
}
this._super(...arguments);
},
super.willDestroy();
}
createOrReplaceModel() {
const modelType = this.modelForType();
const model = this.model;
const roleName = this.roleName;
const backendPath = this.backendPath;
modelForType() {
const type = this.options;
if (type) {
return type.model;
}
// if we don't have a mode for that type then redirect them back to the backend list
this.router.transitionTo('vault.cluster.secrets.backend.list-root', this.args.backendPath);
}
get helpText() {
if (this.options?.model === 'aws-credential') {
return 'For Vault roles of credential type iam_user, there are no inputs, just submit the form. Choose a type to change the input options.';
}
return '';
}
get options() {
return CREDENTIAL_TYPES[this.args.backendType];
}
get formFields() {
const typeOpts = this.options;
if (typeof typeOpts.formFields === 'function') {
return typeOpts.formFields(this.model);
}
return typeOpts.formFields;
}
get displayFields() {
return this.options.displayFields;
}
generateNewModel(modelType) {
if (!modelType) {
return;
}
if (model) {
model.unloadRecord();
}
const { roleName, backendPath, awsRoleType } = this.args;
const attrs = {
role: {
backend: backendPath,
@@ -82,44 +101,60 @@ export default Component.extend({
},
id: `${backendPath}-${roleName}`,
};
const newModel = this.store.createRecord(modelType, attrs);
this.set('model', newModel);
},
if (awsRoleType) {
// this is only set from route if backendType = aws
attrs.credentialType = awsRoleType;
}
return this.store.createRecord(modelType, attrs);
}
actions: {
create() {
const model = this.model;
this.set('loading', true);
this.model
.save()
.then(() => {
model.set('hasGenerated', true);
})
.catch((error) => {
// Handle control group AdapterError
if (error.message === 'Control Group encountered') {
this.controlGroup.saveTokenFromError(error);
const err = this.controlGroup.logFromError(error);
error.errors = [err.content];
}
throw error;
})
.finally(() => {
this.set('loading', false);
});
},
replaceModel() {
const modelType = this.modelForType();
if (!modelType) {
return;
}
if (this.model) {
this.model.unloadRecord();
}
this.model = this.generateNewModel(modelType);
}
codemirrorUpdated(attr, val, codemirror) {
codemirror.performLint();
const hasErrors = codemirror.state.lint.marked.length > 0;
@action
create(evt) {
evt.preventDefault();
this.loading = true;
this.model
.save()
.then(() => {
this.hasGenerated = true;
})
.catch((error) => {
// Handle control group AdapterError
if (error.message === 'Control Group encountered') {
this.controlGroup.saveTokenFromError(error);
const err = this.controlGroup.logFromError(error);
error.errors = [err.content];
}
throw error;
})
.finally(() => {
this.loading = false;
});
}
if (!hasErrors) {
set(this.model, attr, JSON.parse(val));
}
},
@action
codemirrorUpdated(attr, val, codemirror) {
codemirror.performLint();
const hasErrors = codemirror.state.lint.marked.length > 0;
newModel() {
this.createOrReplaceModel();
},
},
});
if (!hasErrors) {
this.model[attr] = JSON.parse(val);
}
}
@action
reset() {
this.hasGenerated = false;
this.replaceModel();
}
}