diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index 561e3130c..520406f00 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -60,6 +60,7 @@ questions: remove-confirm: Are you sure you want to remove this association? abandon-confirm: Are you sure you want to navigate away? Your unsaved changes will be lost. retry-confirm: Would you like to retry? + clear-confirm: Are you sure you want to clear the resource id? themes: system: System Default light: Light diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 26c8421e0..f7b85aeed 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -1024,8 +1024,7 @@ alias: title: Welcome to Aliases description: No Aliases yet link: Create a new alias - clear-or-delete: Deleting this alias may break any workflows using it. You can also clear the alias of the resource it points to without deleting it and then edit it to point it to a new resource. - delete: Deleting this alias may break any workflows using it. + delete: Deleting this alias may break any workflows using it. You can also clear the alias of the resource it points to without deleting it and then edit it to point it to a new resource. form: options: label: Alias options diff --git a/ui/admin/app/components/form/alias/index.hbs b/ui/admin/app/components/form/alias/index.hbs index d24c9178e..e69d455b8 100644 --- a/ui/admin/app/components/form/alias/index.hbs +++ b/ui/admin/app/components/form/alias/index.hbs @@ -92,6 +92,7 @@ @isOptional={{true}} @value={{@model.destination_id}} @isInvalid={{@model.errors.destination_id}} + readonly={{@hideTargetId}} disabled={{form.disabled}} {{on 'input' (set-from-event @model 'destination_id')}} as |F| diff --git a/ui/admin/app/controllers/scopes/scope/aliases/index.js b/ui/admin/app/controllers/scopes/scope/aliases/index.js index c3537ee0c..7ea18d9fd 100644 --- a/ui/admin/app/controllers/scopes/scope/aliases/index.js +++ b/ui/admin/app/controllers/scopes/scope/aliases/index.js @@ -8,17 +8,13 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { loading } from 'ember-loading'; import { notifySuccess, notifyError } from 'core/decorators/notify'; -import { tracked } from '@glimmer/tracking'; +import { confirm } from 'core/decorators/confirm'; export default class ScopesScopeAliasesIndexController extends Controller { // =services @service can; @service router; - // =attributes - @tracked showModal = false; - @tracked selectedAlias; - @tracked hasDestinationId; // =actions /** @@ -52,32 +48,19 @@ export default class ScopesScopeAliasesIndexController extends Controller { await this.router.refresh(); } - /** - * Handles modal visibility and sets the destination_id to a tracked variable so we can handle the clear button visibility on the modal - */ - @action - toggleModal(alias) { - if (alias?.destination_id) { - this.hasDestinationId = true; - } - this.showModal = !this.showModal; - this.selectedAlias = alias; - } - /** * 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 = ''; - this.hasDestinationId = false; await alias.save(); await this.router.refresh(); - this.showModal = false; } /** @@ -86,11 +69,12 @@ export default class ScopesScopeAliasesIndexController extends Controller { */ @action @loading + @confirm('resources.alias.messages.delete') @notifyError(({ message }) => message, { catch: true }) @notifySuccess('notifications.delete-success') async deleteAlias(alias) { await alias.destroyRecord(); - this.showModal = false; + await this.router.replaceWith('scopes.scope.aliases'); await this.router.refresh(); } } diff --git a/ui/admin/app/routes/scopes/scope/targets.js b/ui/admin/app/routes/scopes/scope/targets.js index e420fd1b7..1136d15ae 100644 --- a/ui/admin/app/routes/scopes/scope/targets.js +++ b/ui/admin/app/routes/scopes/scope/targets.js @@ -147,6 +147,7 @@ export default class ScopesScopeTargetsRoute extends Route { */ @action @loading + @confirm('resources.alias.messages.delete') @notifyError(({ message }) => message, { catch: true }) @notifySuccess('notifications.delete-success') async deleteAlias(alias) { @@ -161,6 +162,7 @@ export default class ScopesScopeTargetsRoute extends Route { */ @action @loading + @confirm('questions.clear-confirm') @notifyError(({ message }) => message, { catch: true }) @notifySuccess('notifications.clear-success') async clearAlias(alias) { diff --git a/ui/admin/app/routes/scopes/scope/targets/target/create-alias.js b/ui/admin/app/routes/scopes/scope/targets/target/create-alias.js index f4543c194..a409ac383 100644 --- a/ui/admin/app/routes/scopes/scope/targets/target/create-alias.js +++ b/ui/admin/app/routes/scopes/scope/targets/target/create-alias.js @@ -23,8 +23,10 @@ export default class ScopesScopeTargetsTargetCreateAliasRoute extends Route { */ model() { const scopeModel = this.store.peekRecord('scope', 'global'); + const { id } = this.modelFor('scopes.scope.targets.target'); const record = this.store.createRecord('alias', { type: TYPE_ALIAS_TARGET, + destination_id: id, }); record.scopeModel = scopeModel; return record; diff --git a/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs b/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs index 80d3315b3..3f8732416 100644 --- a/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs +++ b/ui/admin/app/templates/scopes/scope/aliases/alias/index.hbs @@ -10,7 +10,7 @@

{{t 'resources.alias.title'}} - {{! TODO: DOC LINK }} +

{{t 'resources.alias.description'}}

@@ -23,7 +23,37 @@ + {{#let + (hash + canUpdate=(can 'update model' @model) + canDelete=(can 'delete model' @model) + ) + as |perms| + }} + + {{#if (or perms.canUpdate perms.canDelete)}} + + + {{#if (and perms.canUpdate @model.destination_id)}} + + {{/if}} + {{#if perms.canDelete}} + + {{/if}} + + + {{/if}} + + {{/let}} - {{B.data.alias.displayName}} + {{B.data.alias.value}} {{else}} - {{B.data.alias.displayName}} + {{B.data.alias.value}} {{/if}} {{#if B.data.alias.description}} @@ -92,12 +92,12 @@ {{#let (hash - canRead=(can 'read model' B.data.alias) + canUpdate=(can 'update model' B.data.alias) canDelete=(can 'delete model' B.data.alias) ) as |perms| }} - {{#if (or perms.canRead perms.canDelete)}} + {{#if (or perms.canUpdate perms.canDelete)}} - {{#if perms.canRead}} + {{#if perms.canUpdate}} + {{#if B.data.alias.destination_id}} + + {{/if}} {{/if}} - {{#if (and perms.canRead perms.canDelete)}} - - {{/if}} + {{#if perms.canDelete}} {{/if}} @@ -163,54 +168,6 @@ {{/if}} - {{#if this.showModal}} - - - {{t 'questions.delete-confirm'}} - - - -

- {{#if this.hasDestinationId}} - {{t 'resources.alias.messages.clear-or-delete'}} - {{else}} - {{t 'resources.alias.messages.delete'}} - {{/if}} -

- -
- - - - - {{#if this.hasDestinationId}} - - {{/if}} - - - - -
- {{/if}}
\ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/targets/target/create-alias.hbs b/ui/admin/app/templates/scopes/scope/targets/target/create-alias.hbs index bb35e3968..ba79ae987 100644 --- a/ui/admin/app/templates/scopes/scope/targets/target/create-alias.hbs +++ b/ui/admin/app/templates/scopes/scope/targets/target/create-alias.hbs @@ -25,6 +25,7 @@ diff --git a/ui/admin/app/templates/scopes/scope/targets/target/manage-alias.hbs b/ui/admin/app/templates/scopes/scope/targets/target/manage-alias.hbs index 20887ed0e..61bdff1e4 100644 --- a/ui/admin/app/templates/scopes/scope/targets/target/manage-alias.hbs +++ b/ui/admin/app/templates/scopes/scope/targets/target/manage-alias.hbs @@ -14,36 +14,45 @@

{{t 'resources.alias.title'}} - {{! TODO: DOC LINK }} +

{{t 'resources.alias.description'}}

- + {{#let + (hash + canUpdate=(can 'update model' @model) + canDelete=(can 'delete model' @model) + ) + as |perms| + }} + + {{#if (or perms.canUpdate perms.canDelete)}} - - - - - {{#if (can 'delete model' @model)}} - - + + + {{#if (and perms.canUpdate @model.destination_id)}} + + {{/if}} + {{#if perms.canDelete}} + + + {{/if}} + {{/if}} - - - + + {{/let}} this.server.schema.aliases.all().models.length; }); - test('users can delete an alias', async function (assert) { + test('users can delete an alias with proper authorization', async function (assert) { const count = aliasCount(); assert.true(instances.alias.authorized_actions.includes('delete')); - await visit(urls.globalScope); - urls.alias = `${urls.aliases}/${instances.alias.id}`; - - await click(`[href="${urls.aliases}"]`); + await visit(urls.aliases); await click(DROPDOWN_BUTTON_SELECTOR); + await click(DELETE_DROPDOWN_SELECTOR); - assert.dom(DROPDOWN_ITEM_SELECTOR).exists(); - assert.dom(DROPDOWN_ITEM_SELECTOR).hasText('Delete'); - await click(DROPDOWN_ITEM_SELECTOR); - assert.dom(MODAL_SELECTOR); - await click(DELETE_BUTTON_SELECTOR); assert.strictEqual(aliasCount(), count - 1); }); @@ -93,26 +84,6 @@ module('Acceptance | aliases | delete', function (hooks) { await click(`[href="${urls.aliases}"]`); await click(DROPDOWN_BUTTON_SELECTOR); - assert.dom(DROPDOWN_ITEM_SELECTOR).isNotVisible(); - }); - - test('users have the option to clear an alias instead of deleting it', async function (assert) { - const count = aliasCount(); - assert.true(instances.alias.authorized_actions.includes('delete')); - - await visit(urls.globalScope); - - urls.alias = `${urls.aliases}/${instances.alias.id}`; - - await click(`[href="${urls.aliases}"]`); - await click(DROPDOWN_BUTTON_SELECTOR); - - assert.dom(DROPDOWN_ITEM_SELECTOR).exists(); - assert.dom(DROPDOWN_ITEM_SELECTOR).hasText('Delete'); - await click(DROPDOWN_ITEM_SELECTOR); - assert.dom(MODAL_SELECTOR); - await click(CLEAR_BUTTON_SELECTOR); - // await click(`[href="${urls.aliases}"]`); - assert.strictEqual(aliasCount(), count); + assert.dom(DELETE_DROPDOWN_SELECTOR).doesNotExist(); }); }); diff --git a/ui/admin/tests/acceptance/aliases/update-test.js b/ui/admin/tests/acceptance/aliases/update-test.js index fd77380bd..b3e11fd16 100644 --- a/ui/admin/tests/acceptance/aliases/update-test.js +++ b/ui/admin/tests/acceptance/aliases/update-test.js @@ -7,12 +7,16 @@ import { module, test } from 'qunit'; import { visit, click, fillIn } from '@ember/test-helpers'; import { setupApplicationTest } from 'admin/tests/helpers'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { setupIndexedDb } from 'api/test-support/helpers/indexed-db'; import { authenticateSession } from 'ember-simple-auth/test-support'; module('Acceptance | aliases | update', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + setupIndexedDb(hooks); + + let aliasCount; const SAVE_BTN_SELECTOR = '.rose-form-actions [type="submit"]'; @@ -21,6 +25,11 @@ module('Acceptance | aliases | update', function (hooks) { const NAME_FIELD_TEXT = 'random string'; const BUTTON_SELECTOR = '.rose-form-actions [type="button"]'; + const CLEAR_DROPDOWN_SELECTOR = + '.hds-dropdown-list-item--color-action:nth-child(2)'; + + const DROPDOWN_BUTTON_SELECTOR = '.hds-dropdown-toggle-icon'; + const DROPDOWN_ITEM_SELECTOR = '.hds-dropdown-list-item'; const instances = { scopes: { @@ -29,12 +38,14 @@ module('Acceptance | aliases | update', function (hooks) { project: null, }, alias: null, + target: null, }; const urls = { globalScope: null, aliases: null, alias: null, + target: null, }; hooks.beforeEach(function () { @@ -43,13 +54,17 @@ module('Acceptance | aliases | update', function (hooks) { type: 'org', scope: { id: 'global', type: 'global' }, }); - + instances.target = this.server.create('target', { + scope: instances.scopes.project, + }); instances.alias = this.server.create('alias', { scope: instances.scopes.global, + destination_id: instances.target.id, }); urls.globalScope = `/scopes/global`; urls.aliases = `${urls.globalScope}/aliases`; urls.alias = `${urls.aliases}/${instances.alias.id}`; + aliasCount = () => this.server.schema.aliases.all().models.length; authenticateSession({}); }); @@ -78,4 +93,34 @@ module('Acceptance | aliases | update', function (hooks) { assert.dom(NAME_FIELD_SELECTOR).hasValue(`${name}`); assert.strictEqual(instances.alias.name, name); }); + + test('users have the option to clear an alias', async function (assert) { + const count = aliasCount(); + assert.true(instances.alias.authorized_actions.includes('update')); + await visit(urls.globalScope); + + urls.alias = `${urls.aliases}/${instances.alias.id}`; + + await click(`[href="${urls.aliases}"]`); + await click(DROPDOWN_BUTTON_SELECTOR); + + assert.dom(DROPDOWN_ITEM_SELECTOR).exists(); + assert.dom(CLEAR_DROPDOWN_SELECTOR).exists(); + await click(CLEAR_DROPDOWN_SELECTOR); + assert.strictEqual(aliasCount(), count); + }); + + test('users can not see the option to clear an alias without proper authorization', async function (assert) { + instances.alias.authorized_actions = + instances.alias.authorized_actions.filter((item) => item !== 'update'); + await visit(urls.globalScope); + + assert.false(instances.alias.authorized_actions.includes('update')); + + urls.alias = `${urls.aliases}/${instances.alias.id}`; + await click(`[href="${urls.aliases}"]`); + await click(DROPDOWN_BUTTON_SELECTOR); + assert.dom(DROPDOWN_ITEM_SELECTOR).exists(); + assert.dom(CLEAR_DROPDOWN_SELECTOR).doesNotExist(); + }); }); diff --git a/ui/admin/tests/acceptance/targets/create-alias-test.js b/ui/admin/tests/acceptance/targets/create-alias-test.js index 89442c477..cd857a10c 100644 --- a/ui/admin/tests/acceptance/targets/create-alias-test.js +++ b/ui/admin/tests/acceptance/targets/create-alias-test.js @@ -4,7 +4,7 @@ */ import { module, test } from 'qunit'; -import { visit, click, fillIn } from '@ember/test-helpers'; +import { visit, click, fillIn, getContext } from '@ember/test-helpers'; import { setupApplicationTest } from 'admin/tests/helpers'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; @@ -20,6 +20,7 @@ module('Acceptance | targets | create-alias', function (hooks) { setupIndexedDb(hooks); let getAliasCount; + let featuresService; const SAVE_BTN_SELECTOR = '[type="submit"]'; const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]'; @@ -37,6 +38,8 @@ module('Acceptance | targets | create-alias', function (hooks) { global: null, org: null, }, + target: null, + alias: null, }; const urls = { @@ -47,6 +50,8 @@ module('Acceptance | targets | create-alias', function (hooks) { }; hooks.beforeEach(function () { + const { owner } = getContext(); + featuresService = owner.lookup('service:features'); instances.scopes.global = this.server.create('scope', { id: 'global' }); instances.scopes.org = this.server.create('scope', { type: 'org', @@ -61,7 +66,9 @@ module('Acceptance | targets | create-alias', function (hooks) { scope: instances.scopes.project, type: TYPE_TARGET_TCP, }); - + instances.alias = this.server.create('alias', { + scope: instances.scopes.global, + }); urls.globalScope = `/scopes/global`; urls.projectScope = `/scopes/${instances.scopes.project.id}`; @@ -83,18 +90,47 @@ module('Acceptance | targets | create-alias', function (hooks) { await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT); await fillIn(ALIAS_FIELD_SELECTOR, ALIAS_VALUE_TEXT); - await fillIn(DEST_FIELD_SELECTOR, instances.target.id); await click(SAVE_BTN_SELECTOR); const alias = this.server.schema.aliases.findBy({ name: NAME_FIELD_TEXT, }); assert.strictEqual(alias.name, NAME_FIELD_TEXT); + assert.strictEqual(alias.destinationId, instances.target.id); assert.strictEqual(alias.scopeId, 'global'); assert.strictEqual(getAliasCount(), aliasCount + 1); }); test('users can create a new alias for a target of SSH type', async function (assert) { + featuresService.enable('ssh-target'); + + instances.target.update({ + type: TYPE_TARGET_SSH, + }); + + const aliasCount = getAliasCount(); + + await visit(urls.targets); + + await click(`[href="${urls.target}"]`); + + await click(ADD_AN_ALIAS_SELECTOR); + + await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT); + await fillIn(ALIAS_FIELD_SELECTOR, ALIAS_VALUE_TEXT); + + await click(SAVE_BTN_SELECTOR); + const alias = this.server.schema.aliases.findBy({ + name: NAME_FIELD_TEXT, + }); + + assert.strictEqual(alias.name, NAME_FIELD_TEXT); + assert.strictEqual(alias.destinationId, instances.target.id); + assert.strictEqual(alias.scopeId, 'global'); + assert.strictEqual(getAliasCount(), aliasCount + 1); + }); + + test('destination id should be readonly', async function (assert) { instances.target = this.server.create('target', { scope: instances.scopes.project, type: TYPE_TARGET_SSH, @@ -109,8 +145,7 @@ module('Acceptance | targets | create-alias', function (hooks) { await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT); await fillIn(ALIAS_FIELD_SELECTOR, ALIAS_VALUE_TEXT); - await fillIn(DEST_FIELD_SELECTOR, instances.target.id); - + assert.dom(DEST_FIELD_SELECTOR).hasAttribute('readOnly'); await click(SAVE_BTN_SELECTOR); const alias = this.server.schema.aliases.findBy({ name: NAME_FIELD_TEXT, diff --git a/ui/admin/tests/acceptance/targets/manage-alias-test.js b/ui/admin/tests/acceptance/targets/manage-alias-test.js index c1cd49db2..87b4255f5 100644 --- a/ui/admin/tests/acceptance/targets/manage-alias-test.js +++ b/ui/admin/tests/acceptance/targets/manage-alias-test.js @@ -23,6 +23,7 @@ module('Acceptance | targets | manage-alias', function (hooks) { const CLEAR_BTN_SELECTOR = '.hds-dropdown-list-item--color-action button'; const DELETE_BTN_SELECTOR = '.hds-dropdown-list-item--color-critical button'; const LINK_TO_NEW_ALIAS = '.target-sidebar-aliases .hds-button'; + const DEST_FIELD_SELECTOR = '[name="destination_id"]'; const instances = { scopes: { @@ -149,6 +150,8 @@ module('Acceptance | targets | manage-alias', function (hooks) { assert.dom(DROPDOWN_ITEM_SELECTOR).hasText('Manage'); await click(DROPDOWN_ITEM_SELECTOR); + assert.dom(DEST_FIELD_SELECTOR).doesNotHaveAttribute('readOnly'); + assert.strictEqual(currentURL(), urls.tcpAlias); await click(DROPDOWN_ACTION); diff --git a/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs b/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs index c402ab5b9..1c3f8fd53 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/target.hbs @@ -48,25 +48,27 @@ -
-
- {{t 'resources.alias.details'}} + {{#if @model.target.aliases}} +
+
+ {{t 'resources.alias.details'}} +
+ + {{#each @model.target.aliases as |alias|}} + + + + + {{/each}} + +
- - {{#each @model.target.aliases as |alias|}} - - - - - {{/each}} - - -
+ {{/if}}