vault/ui/app/models/mfa-login-enforcement.js
Dan Rivera 008835ba36
UI: Surface plugin version & cleanup utils (#31001)
* surface plugin version & removing mountable-auth-methods.js

* UI: Removing mountable-secret-engines.js (#30950)

* first pass, removing all related imports

* fix usage

* fix category

* fix typos

* fix more tests

* fix more tests pt2

* attempting WIF const removal

* fix wif tests, removing config consts

* fixing tests

* please

* removing fallback

* cleanup

* fix type ent test

* remove isaddon

* Revert "remove isaddon"

This reverts commit ee114197b7299711e35e3c8e5aca9694063726eb.

* adding tab click

* update case

* fix case, rename to isOnlyMountable

* fix backend form

* more test fix

* adding changelog

* pr comments

* renaming params, adding requiresADP

* updates

* updates and pr comments

* perhaps update the test
2025-06-19 15:10:09 -04:00

117 lines
4.0 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Model, { attr, hasMany } from '@ember-data/model';
import ArrayProxy from '@ember/array/proxy';
import PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
import { withModelValidations } from 'vault/decorators/model-validations';
import { isPresent } from '@ember/utils';
import { service } from '@ember/service';
import { addManyToArray, addToArray } from 'vault/helpers/add-to-array';
import { filterEnginesByMountCategory } from 'vault/utils/all-engines-metadata';
const validations = {
name: [{ type: 'presence', message: 'Name is required' }],
mfa_methods: [{ type: 'presence', message: 'At least one MFA method is required' }],
targets: [
{
validator(model) {
// avoid async fetch of records here and access relationship ids to check for presence
const entityIds = model.hasMany('identity_entities').ids();
const groupIds = model.hasMany('identity_groups').ids();
return (
isPresent(model.auth_method_accessors) ||
isPresent(model.auth_method_types) ||
isPresent(entityIds) ||
isPresent(groupIds)
);
},
message:
"At least one target is required. If you've selected one, click 'Add' to make sure it's added to this enforcement.",
},
],
};
@withModelValidations(validations)
export default class MfaLoginEnforcementModel extends Model {
@service store;
@attr('string') name;
@hasMany('mfa-method', { async: true, inverse: null }) mfa_methods;
@attr('string') namespace_id;
@attr('array', { defaultValue: () => [] }) auth_method_accessors; // ["auth_approle_17a552c6"]
@attr('array', { defaultValue: () => [] }) auth_method_types; // ["userpass"]
@hasMany('identity/entity', { async: true, inverse: null }) identity_entities;
@hasMany('identity/group', { async: true, inverse: null }) identity_groups;
get targets() {
return ArrayProxy.extend(PromiseProxyMixin).create({
promise: this.prepareTargets(),
});
}
async prepareTargets() {
let authMethods;
let targets = [];
if (this.auth_method_accessors.length || this.auth_method_types.length) {
// fetch all auth methods and lookup by accessor to get mount path and type
try {
const { data } = await this.store.adapterFor('auth-method').findAll();
authMethods = Object.keys(data).map((key) => ({ path: key, ...data[key] }));
} catch (error) {
// swallow this error
}
}
if (this.auth_method_accessors.length) {
const selectedAuthMethods = authMethods.filter((model) => {
return this.auth_method_accessors.includes(model.accessor);
});
targets = addManyToArray(
targets,
selectedAuthMethods.map((method) => ({
icon: this.iconForMount(method.type),
link: 'vault.cluster.access.method',
linkModels: [method.path.slice(0, -1)],
title: method.path,
subTitle: method.accessor,
}))
);
}
this.auth_method_types.forEach((type) => {
const icon = this.iconForMount(type);
const mountCount = authMethods.filterBy('type', type).length;
targets = addToArray(targets, {
key: 'auth_method_types',
icon,
title: type,
subTitle: `All ${type} mounts (${mountCount})`,
});
});
for (const key of ['identity_entities', 'identity_groups']) {
(await this[key]).forEach((model) => {
targets = addToArray(targets, {
key,
icon: 'user',
link: 'vault.cluster.access.identity.show',
linkModels: [key.split('_')[1], model.id, 'details'],
title: model.name,
subTitle: model.id,
});
});
}
return targets;
}
iconForMount(type) {
const mountableMethods = filterEnginesByMountCategory({ mountCategory: 'auth', isEnterprise: true });
const mount = mountableMethods.find((method) => method.type === type);
return mount ? mount.glyph || mount.type : 'token';
}
}