mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-09 04:41:10 +01:00
* [VAULT-37521] UI: decouple auth and secret engines * add copyright header * address acceptance test failure Co-authored-by: Shannon Roberts (Beagin) <beagins@users.noreply.github.com>
157 lines
5.1 KiB
TypeScript
157 lines
5.1 KiB
TypeScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import Component from '@glimmer/component';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { service } from '@ember/service';
|
|
import { action, set } from '@ember/object';
|
|
import { task } from 'ember-concurrency';
|
|
import { waitFor } from '@ember/test-waiters';
|
|
import { filterEnginesByMountCategory } from 'vault/utils/all-engines-metadata';
|
|
import { MOUNT_CATEGORIES } from 'vault/utils/plugin-catalog-helpers';
|
|
|
|
import type FlashMessageService from 'vault/services/flash-messages';
|
|
import type Store from '@ember-data/store';
|
|
import type AuthMethodForm from 'vault/forms/auth/method';
|
|
import type CapabilitiesService from 'vault/services/capabilities';
|
|
import type ApiService from 'vault/services/api';
|
|
import type { ApiError } from '@ember-data/adapter/error';
|
|
import type { ValidationMap } from 'vault/vault/app-types';
|
|
|
|
/**
|
|
* @module MountBackendForm
|
|
* The `MountBackendForm` is used to mount authentication methods.
|
|
*
|
|
* @example ```js
|
|
* <MountBackendForm @mountModel={{this.model}} @onMountSuccess={{this.onMountSuccess}} />```
|
|
*
|
|
* @param {AuthMethodForm} mountModel - The authentication method form.
|
|
* @param {function} onMountSuccess - A function that transitions once the Mount has been successfully posted.
|
|
*
|
|
*/
|
|
|
|
interface Args {
|
|
mountModel: AuthMethodForm;
|
|
onMountSuccess: (type: string, path: string, useEngineRoute: boolean) => void;
|
|
}
|
|
|
|
const AUTH_MOUNT_CATEGORY = MOUNT_CATEGORIES.AUTH;
|
|
|
|
export default class MountBackendForm extends Component<Args> {
|
|
@service declare readonly store: Store;
|
|
@service declare readonly flashMessages: FlashMessageService;
|
|
@service declare readonly capabilities: CapabilitiesService;
|
|
@service declare readonly api: ApiService;
|
|
|
|
// validation related properties
|
|
@tracked modelValidations: ValidationMap | null = null;
|
|
@tracked invalidFormAlert: string | null = null;
|
|
|
|
@tracked errorMessage: string | string[] = '';
|
|
|
|
get mountForm(): AuthMethodForm {
|
|
return this.args.mountModel;
|
|
}
|
|
|
|
get showEnable(): boolean {
|
|
return !!this.mountForm.type;
|
|
}
|
|
|
|
constructor(owner: unknown, args: Args) {
|
|
super(owner, args);
|
|
}
|
|
|
|
checkPathChange(backendType: string) {
|
|
if (!backendType) return;
|
|
const { data } = this.mountForm;
|
|
// Always use auth mount category since this component only handles auth methods
|
|
const mountsByType = filterEnginesByMountCategory({
|
|
mountCategory: AUTH_MOUNT_CATEGORY,
|
|
isEnterprise: true,
|
|
}).map((engine) => engine.type);
|
|
|
|
// if the current path has not been altered by user (is empty or matches a default mount type),
|
|
// change it here to match the new type
|
|
if (!data.path || mountsByType.includes(data.path)) {
|
|
data.path = backendType;
|
|
}
|
|
}
|
|
|
|
checkModelWarnings() {
|
|
// check for warnings on change
|
|
// since we only show errors on submit we need to clear those out and only send warning state
|
|
const mountModel = this.mountForm;
|
|
const { state } = mountModel.toJSON();
|
|
for (const key in state) {
|
|
if (state[key]) {
|
|
state[key].errors = [];
|
|
}
|
|
}
|
|
this.modelValidations = state;
|
|
this.invalidFormAlert = null;
|
|
}
|
|
|
|
async onMountError(status: number, errors: ApiError[], message: string) {
|
|
if (status === 403) {
|
|
this.flashMessages.danger(
|
|
'You do not have access to the sys/auth endpoint. The auth method was not mounted.'
|
|
);
|
|
} else if (errors) {
|
|
this.errorMessage = errors.map((e) => {
|
|
if (typeof e === 'object') return e.title || e.message || JSON.stringify(e);
|
|
return e;
|
|
});
|
|
} else if (message) {
|
|
this.errorMessage = message;
|
|
} else {
|
|
this.errorMessage = 'An error occurred, check the vault logs.';
|
|
}
|
|
}
|
|
|
|
@task
|
|
@waitFor
|
|
*mountBackend(event: Event) {
|
|
event.preventDefault();
|
|
const mountModel = this.mountForm;
|
|
const { type } = mountModel;
|
|
const { path } = mountModel.data;
|
|
// only submit form if validations pass
|
|
const { isValid, state, invalidFormMessage, data } = mountModel.toJSON();
|
|
if (!isValid) {
|
|
this.modelValidations = state;
|
|
this.invalidFormAlert = invalidFormMessage;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
yield this.api.sys.authEnableMethod(path, data);
|
|
this.flashMessages.success(`Successfully mounted the ${mountModel.type} auth method at ${path}.`);
|
|
this.args.onMountSuccess(type, path, false);
|
|
} catch (error) {
|
|
const { status, response, message } = yield this.api.parseError(error);
|
|
this.onMountError(status, response.errors, message);
|
|
}
|
|
}
|
|
|
|
@action
|
|
onKeyUp(name: string, value: string) {
|
|
set(this.mountForm.data, name, value);
|
|
this.checkModelWarnings();
|
|
}
|
|
|
|
@action
|
|
setMountType(value: string) {
|
|
this.mountForm.type = value;
|
|
this.checkPathChange(value);
|
|
}
|
|
|
|
@action
|
|
handleIdentityTokenKeyChange(value: string[] | string): void {
|
|
// if array, it's coming from the search-select component, otherwise it hit the fallback component and will come in as a string.
|
|
const { config } = this.mountForm.data;
|
|
config.identity_token_key = Array.isArray(value) ? value[0] : value;
|
|
}
|
|
}
|