369 lines
10 KiB
JavaScript
369 lines
10 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import Controller from '@ember/controller';
|
|
import { service } from '@ember/service';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { action } from '@ember/object';
|
|
import { debounce } from 'core/decorators/debounce';
|
|
import { loading } from 'ember-loading';
|
|
import { confirm } from 'core/decorators/confirm';
|
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
|
import { TYPES_TARGET } from 'api/models/target';
|
|
|
|
export default class ScopesScopeTargetsIndexController extends Controller {
|
|
// =services
|
|
|
|
@service intl;
|
|
@service can;
|
|
@service router;
|
|
@service confirm;
|
|
|
|
// =attributes
|
|
|
|
queryParams = [
|
|
'search',
|
|
{ availableSessions: { type: 'array' } },
|
|
{ types: { type: 'array' } },
|
|
'page',
|
|
'pageSize',
|
|
];
|
|
|
|
@tracked search;
|
|
@tracked scopes = [];
|
|
@tracked availableSessions = [];
|
|
@tracked types = [];
|
|
@tracked page = 1;
|
|
@tracked pageSize = 10;
|
|
@tracked selectedTarget;
|
|
|
|
get availableSessionOptions() {
|
|
return [
|
|
{
|
|
id: 'yes',
|
|
name: this.intl.t('resources.target.filter.active-sessions.yes'),
|
|
},
|
|
{
|
|
id: 'no',
|
|
name: this.intl.t('resources.target.filter.active-sessions.no'),
|
|
},
|
|
];
|
|
}
|
|
|
|
get filters() {
|
|
return {
|
|
allFilters: {
|
|
availableSessions: this.availableSessionOptions,
|
|
types: this.targetTypeOptions,
|
|
},
|
|
selectedFilters: {
|
|
availableSessions: this.availableSessions,
|
|
types: this.types,
|
|
},
|
|
};
|
|
}
|
|
|
|
get targetTypeOptions() {
|
|
return TYPES_TARGET.map((type) => ({
|
|
id: type,
|
|
name: this.intl.t(`resources.target.types.${type}`),
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* If can list (at least): return default welcome message.
|
|
* If can create (only): return create-but-not-list welcome message.
|
|
* If can neither list nor create: return neither-list-nor-create welcome message
|
|
* @type {string}
|
|
*/
|
|
get messageDescription() {
|
|
const canList = this.can.can('list model', this.scope, {
|
|
collection: 'targets',
|
|
});
|
|
const canCreate = this.can.can('create model', this.scope, {
|
|
collection: 'targets',
|
|
});
|
|
const resource = this.intl.t('resources.target.title_plural');
|
|
let description = 'descriptions.neither-list-nor-create';
|
|
|
|
if (canList) {
|
|
description = 'resources.target.description';
|
|
} else if (canCreate) {
|
|
description = 'descriptions.create-but-not-list';
|
|
}
|
|
|
|
return this.intl.t(description, { resource });
|
|
}
|
|
|
|
// =actions
|
|
|
|
/**
|
|
* Handles input on each keystroke and the search queryParam
|
|
* @param {object} event
|
|
*/
|
|
@action
|
|
@debounce(250)
|
|
handleSearchInput(event) {
|
|
const { value } = event.target;
|
|
this.search = value;
|
|
this.page = 1;
|
|
}
|
|
|
|
/**
|
|
* Sets a query param to the value of selectedItems
|
|
* and resets the page to 1.
|
|
* @param {string} paramKey
|
|
* @param {[string]} selectedItems
|
|
*/
|
|
@action
|
|
applyFilter(paramKey, selectedItems) {
|
|
this[paramKey] = [...selectedItems];
|
|
this.page = 1;
|
|
}
|
|
|
|
/**
|
|
* Rollback changes on a target.
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
cancel(target) {
|
|
const { isNew } = target;
|
|
target.rollbackAttributes();
|
|
if (isNew) this.router.transitionTo('scopes.scope.targets');
|
|
}
|
|
|
|
/**
|
|
* Saves the target and refreshes the list.
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
@loading
|
|
@notifyError(({ message }) => message)
|
|
@notifySuccess(({ isNew }) =>
|
|
isNew ? 'notifications.create-success' : 'notifications.save-success',
|
|
)
|
|
async save(target) {
|
|
await target.save();
|
|
if (this.can.can('read model', target)) {
|
|
await this.router.transitionTo('scopes.scope.targets.target', target);
|
|
} else {
|
|
await this.router.transitionTo('scopes.scope.targets');
|
|
}
|
|
await this.router.refresh();
|
|
}
|
|
|
|
/**
|
|
* Targets may have either an `address` _or_ host sources, but not both.
|
|
*
|
|
* In order to save a target with an `address`, any existing host sources
|
|
* must first be removed. Once host sources are removed, the target with
|
|
* `address` may be saved via the standard `save` action.
|
|
*
|
|
* If `address` and host sources are both present, the user is asked to
|
|
* confirm that they wish to remove all host sources. If the user declines,
|
|
* no changes are persisted; the form remains editable and unsaved.
|
|
*
|
|
* If `address` is set and the target has no host sources, the save proceeds
|
|
* as normal.
|
|
*
|
|
* If neither `address` nor host sources are set, the save proceeds as normal.
|
|
*
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
async saveWithAddress(target) {
|
|
if (target.address) {
|
|
const { address } = target;
|
|
// Remove host sources if necessary. This is cancelable by the user.
|
|
await this.removeHostSources(target);
|
|
// After removing host sources, the model is reset to an empty address,
|
|
// so we need to update the address with the previous value.
|
|
target.address = address;
|
|
}
|
|
|
|
// Proceed with standard save.
|
|
await this.save(target);
|
|
}
|
|
|
|
/**
|
|
* If the passed target has host sources:
|
|
* - Request confirmation from the user for host source removal.
|
|
* - Persist removal of all host sources.
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
@loading
|
|
@notifyError(({ message }) => message)
|
|
async removeHostSources(target) {
|
|
const hostSourceCount = target.host_sources?.length;
|
|
|
|
if (hostSourceCount) {
|
|
const hostSourceIDs = target.host_sources.map(
|
|
({ host_source_id }) => host_source_id,
|
|
);
|
|
const confirmMessage = this.intl.t(
|
|
'resources.target.questions.delete-host-sources.message',
|
|
{ hostSourceCount },
|
|
);
|
|
|
|
// Ask for confirmation
|
|
await this.confirm.confirm(confirmMessage, {
|
|
title: 'resources.target.questions.delete-host-sources.title',
|
|
confirm: 'actions.remove-resources',
|
|
});
|
|
// Remove host sources. This step is reached only if the user accepts.
|
|
await target.removeHostSources(hostSourceIDs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a target and redirects to targets index.
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
@loading
|
|
@confirm('questions.delete-confirm')
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.delete-success')
|
|
async delete(target) {
|
|
await target.destroyRecord();
|
|
this.router.replaceWith('scopes.scope.targets');
|
|
await this.router.refresh();
|
|
}
|
|
|
|
/**
|
|
* Update type of target
|
|
* @param {string} type
|
|
*/
|
|
@action
|
|
changeType(type) {
|
|
this.router.replaceWith({ queryParams: { type } });
|
|
}
|
|
|
|
/**
|
|
* Remove a credential source from the current target.
|
|
* @param {TargetModel} target
|
|
* @param {CredentialLibraryModel, credentialModel} credentialSource
|
|
*/
|
|
@action
|
|
@loading
|
|
@confirm('questions.remove-confirm')
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.remove-success')
|
|
async removeInjectedApplicationCredentialSource(target, credentialSource) {
|
|
await target.removeInjectedApplicationCredentialSource(credentialSource.id);
|
|
await this.router.refresh();
|
|
}
|
|
|
|
/**
|
|
* Remove a credential source from the current target.
|
|
* @param {TargetModel} target
|
|
* @param {CredentialLibraryModel, credentialModel} credentialSource
|
|
*/
|
|
@action
|
|
@loading
|
|
@confirm('questions.remove-confirm')
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.remove-success')
|
|
async removeBrokeredCredentialSource(target, credentialSource) {
|
|
await target.removeBrokeredCredentialSource(credentialSource.id);
|
|
await this.router.refresh();
|
|
}
|
|
|
|
/**
|
|
* Removes a host set from the current target and redirects to index.
|
|
* @param {TargetModel} target
|
|
* @param {HostSetModel} hostSet
|
|
*/
|
|
@action
|
|
@loading
|
|
@confirm('questions.remove-confirm')
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.remove-success')
|
|
async removeHostSource(target, hostSet) {
|
|
await target.removeHostSource(hostSet.id);
|
|
await this.router.refresh();
|
|
}
|
|
|
|
/**
|
|
* Delete an alias
|
|
* @param {AliasModel} alias
|
|
*/
|
|
@action
|
|
@loading
|
|
@confirm('resources.alias.messages.delete')
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.delete-success')
|
|
async deleteAlias(alias) {
|
|
await alias.destroyRecord();
|
|
await this.router.transitionTo('scopes.scope.targets.target');
|
|
await this.router.refresh('scopes.scope.targets.target');
|
|
}
|
|
|
|
/**
|
|
* Remove destaination_id from alias
|
|
* @param {AliasModel} alias
|
|
*/
|
|
@action
|
|
@loading
|
|
@confirm('questions.clear-confirm')
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.clear-success')
|
|
async clearAlias(alias) {
|
|
alias.destination_id = '';
|
|
await alias.save();
|
|
await this.router.transitionTo('scopes.scope.targets.target');
|
|
await this.router.refresh('scopes.scope.targets.target');
|
|
}
|
|
|
|
/**
|
|
* Handle save
|
|
* @param {AliasModel} alias
|
|
*/
|
|
@action
|
|
@loading
|
|
@notifyError(({ message }) => message)
|
|
@notifySuccess('notifications.save-success')
|
|
async saveAlias(alias) {
|
|
await alias.save();
|
|
await this.router.transitionTo('scopes.scope.targets.target');
|
|
await this.router.refresh();
|
|
}
|
|
|
|
/**
|
|
* Rollback changes on alias.
|
|
* @param {AliasModel} alias
|
|
*/
|
|
@action
|
|
async cancelAlias(alias) {
|
|
alias.rollbackAttributes();
|
|
await this.router.transitionTo('scopes.scope.targets.target');
|
|
}
|
|
|
|
/**
|
|
* Handle save
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
@loading
|
|
@notifyError(({ message }) => message, { catch: true })
|
|
@notifySuccess('notifications.add-success')
|
|
async saveWorkerFilter(target) {
|
|
await target.save();
|
|
await this.router.replaceWith('scopes.scope.targets.target.workers');
|
|
await this.router.refresh('scopes.scope.targets.target');
|
|
}
|
|
|
|
/**
|
|
* Rollback changes on target.
|
|
* @param {TargetModel} target
|
|
*/
|
|
@action
|
|
async cancelWorkerFilter(target) {
|
|
target.rollbackAttributes();
|
|
await this.router.replaceWith('scopes.scope.targets.target.workers');
|
|
}
|
|
}
|