vault/ui/app/models/mfa-login-enforcement.js
Chelsea Shaw 5c18a4e7a4
UI: Ember deprecation - addObject, removeObject (#25952)
* Update add-to-array and remove-from-array helpers

* remove search-select-has-many, moved logic directly into mfa-login-enforcement-form (see #16470)

* Replace add/remove object in MFA files - All MFA tests pass

* Replace in PKI components (pki tests all passing)

* Replace in core addon where applicable

* glimmerize console service -- console tests pass

* more replacements

* update string-list, add comment to vertical-bar-chart

* Refactor CSP Event service

- only used one place (auth-form) so simplified that usage
- glimmerize and refactor so that the tests work

* small updates

* more cleanup

* Fix tests

* Remove objectAt from console-helpers

* Address PR comments

* move commandIndex clearing back

* Remove extra model set
2024-03-25 18:31:31 +00:00

117 lines
3.9 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 { methods } from 'vault/helpers/mountable-auth-methods';
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';
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 = methods();
const mount = mountableMethods.find((method) => method.type === type);
return mount ? mount.glyph || mount.type : 'token';
}
}