mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
UI: Replace popup-menu on list items (#25588)
This commit is contained in:
3
changelog/25588.txt
Normal file
3
changelog/25588.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
ui: replace popup menu on list items (namespaces, auth items, KMIP, K8S, LDAP)
|
||||||
|
```
|
||||||
@@ -7,6 +7,7 @@ import { service } from '@ember/service';
|
|||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module GeneratedItemList
|
* @module GeneratedItemList
|
||||||
@@ -26,6 +27,7 @@ import { getOwner } from '@ember/application';
|
|||||||
export default class GeneratedItemList extends Component {
|
export default class GeneratedItemList extends Component {
|
||||||
@service router;
|
@service router;
|
||||||
@service store;
|
@service store;
|
||||||
|
@tracked showConfirmModal = false;
|
||||||
|
|
||||||
get model() {
|
get model() {
|
||||||
return this.args.model || null;
|
return this.args.model || null;
|
||||||
|
|||||||
@@ -176,3 +176,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match HDS styling for namespace ink
|
||||||
|
a.ns-dropdown-item {
|
||||||
|
color: var(--token-color-foreground-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 7px 9px 7px 0px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
outline-style: solid;
|
||||||
|
outline-color: transparent;
|
||||||
|
text-decoration: none;
|
||||||
|
&:hover {
|
||||||
|
color: var(--token-color-foreground-action-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,21 +72,36 @@
|
|||||||
<Icon @name="folder" class="has-text-grey-light" />{{list.item.id}}
|
<Icon @name="folder" class="has-text-grey-light" />{{list.item.id}}
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
<li class="action">
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<LinkTo @route="vault.cluster.access.method.item.show" @model={{list.item.id}} class="is-block">
|
<dd.ToggleIcon
|
||||||
View
|
@icon="more-horizontal"
|
||||||
{{singularize this.itemType}}
|
@text="Overflow options"
|
||||||
</LinkTo>
|
@hasChevron={{false}}
|
||||||
</li>
|
data-test-popup-menu-trigger
|
||||||
<li class="action">
|
/>
|
||||||
<LinkTo @route="vault.cluster.access.method.item.edit" @model={{list.item.id}} class="is-block">
|
<dd.Interactive
|
||||||
Edit
|
@text="View {{singularize this.itemType}}"
|
||||||
{{singularize this.itemType}}
|
@route="vault.cluster.access.method.item.show"
|
||||||
</LinkTo>
|
@model={{list.item.id}}
|
||||||
</li>
|
/>
|
||||||
<ConfirmAction
|
<dd.Interactive
|
||||||
@isInDropdown={{true}}
|
@text="Edit {{singularize this.itemType}}"
|
||||||
@onConfirmAction={{action
|
@route="vault.cluster.access.method.item.edit"
|
||||||
|
@model={{list.item.id}}
|
||||||
|
/>
|
||||||
|
<dd.Interactive
|
||||||
|
@text="Delete {{singularize this.itemType}}"
|
||||||
|
@color="critical"
|
||||||
|
{{on "click" (fn (mut this.showConfirmModal) true)}}
|
||||||
|
/>
|
||||||
|
</Hds::Dropdown>
|
||||||
|
</Item.menu>
|
||||||
|
|
||||||
|
{{#if this.showConfirmModal}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
|
@onClose={{fn (mut this.showConfirmModal) false}}
|
||||||
|
@onConfirm={{action
|
||||||
(perform
|
(perform
|
||||||
Item.callMethod
|
Item.callMethod
|
||||||
"destroyRecord"
|
"destroyRecord"
|
||||||
@@ -98,9 +113,8 @@
|
|||||||
}}
|
}}
|
||||||
@confirmTitle="Delete {{singularize this.itemType}}?"
|
@confirmTitle="Delete {{singularize this.itemType}}?"
|
||||||
@confirmMessage="Are you sure you want to delete {{singularize this.itemType}} {{list.item.id}}?"
|
@confirmMessage="Are you sure you want to delete {{singularize this.itemType}} {{list.item.id}}?"
|
||||||
@buttonText="Delete {{singularize this.itemType}}"
|
|
||||||
/>
|
/>
|
||||||
</Item.menu>
|
{{/if}}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ListView>
|
</ListView>
|
||||||
@@ -35,31 +35,37 @@
|
|||||||
{{list.item.id}}
|
{{list.item.id}}
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
{{#let (concat this.currentNamespace (if this.currentNamespace "/") list.item.id) as |targetNamespace|}}
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
{{#if (includes targetNamespace this.accessibleNamespaces)}}
|
<dd.ToggleIcon @icon="more-horizontal" @text="More options" @hasChevron={{false}} data-test-popup-menu-trigger />
|
||||||
<li class="action">
|
{{#let (concat this.currentNamespace (if this.currentNamespace "/") list.item.id) as |targetNamespace|}}
|
||||||
<NamespaceLink @targetNamespace={{targetNamespace}} @unparsed={{true}} @class="is-block">
|
{{#if (includes targetNamespace this.accessibleNamespaces)}}
|
||||||
Switch to Namespace
|
<dd.Generic>
|
||||||
</NamespaceLink>
|
<NamespaceLink @targetNamespace={{targetNamespace}} @unparsed={{true}} @class="ns-dropdown-item">
|
||||||
</li>
|
Switch to Namespace
|
||||||
{{/if}}
|
</NamespaceLink>
|
||||||
{{/let}}
|
</dd.Generic>
|
||||||
<ConfirmAction
|
{{/if}}
|
||||||
@isInDropdown={{true}}
|
{{/let}}
|
||||||
@buttonText="Delete"
|
<dd.Interactive @text="Delete" @color="critical" {{on "click" (fn (mut this.showConfirmModal) true)}} />
|
||||||
@confirmTitle="Delete this namespace?"
|
</Hds::Dropdown>
|
||||||
@confirmMessage="Any engines or mounts in this namespace will also be removed."
|
{{#if this.showConfirmModal}}
|
||||||
@onConfirmAction={{action
|
<ConfirmModal
|
||||||
(perform
|
@color="critical"
|
||||||
Item.callMethod
|
@onClose={{fn (mut this.showConfirmModal) false}}
|
||||||
"destroyRecord"
|
@onConfirm={{action
|
||||||
list.item
|
(perform
|
||||||
(concat "Successfully deleted namespace: " list.item.id)
|
Item.callMethod
|
||||||
"There was an error deleting this namespace: "
|
"destroyRecord"
|
||||||
(action "refreshNamespaceList")
|
list.item
|
||||||
)
|
(concat "Successfully deleted namespace: " list.item.id)
|
||||||
}}
|
"There was an error deleting this namespace: "
|
||||||
/>
|
(action "refreshNamespaceList")
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@confirmTitle="Delete this namespace?"
|
||||||
|
@confirmMessage="Any engines or mounts in this namespace will also be removed."
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
</Item.menu>
|
</Item.menu>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -3,14 +3,4 @@
|
|||||||
SPDX-License-Identifier: BUSL-1.1
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
~}}
|
~}}
|
||||||
|
|
||||||
{{#if this.hasMenu}}
|
{{yield this.item}}
|
||||||
<PopupMenu>
|
|
||||||
<nav class="menu">
|
|
||||||
<ul class="menu-list">
|
|
||||||
{{yield}}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</PopupMenu>
|
|
||||||
{{else}}
|
|
||||||
{{yield this.item}}
|
|
||||||
{{/if}}
|
|
||||||
@@ -63,22 +63,33 @@
|
|||||||
<Icon @name="file-text" class="has-text-grey-light" />{{list.item.id}}
|
<Icon @name="file-text" class="has-text-grey-light" />{{list.item.id}}
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
<li class="action">
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<LinkTo @route="credentials.show" @models={{array this.scope this.role list.item.id}} class="is-block">
|
<dd.ToggleIcon @icon="more-horizontal" @text="More options" @hasChevron={{false}} data-test-popup-menu-trigger />
|
||||||
View credentials
|
<dd.Interactive
|
||||||
</LinkTo>
|
@text="View credentials"
|
||||||
</li>
|
@route="credentials.show"
|
||||||
{{#if list.item.deletePath.isPending}}
|
@models={{array this.scope this.role list.item.id}}
|
||||||
<li class="action">
|
/>
|
||||||
<LoadingDropdownOption />
|
{{#if list.item.deletePath.isPending}}
|
||||||
</li>
|
<dd.Generic>
|
||||||
{{else if list.item.deletePath.canDelete}}
|
<LoadingDropdownOption />
|
||||||
<ConfirmAction
|
</dd.Generic>
|
||||||
@isInDropdown={{true}}
|
{{else if list.item.deletePath.canDelete}}
|
||||||
@buttonText="Revoke credentials"
|
<dd.Interactive
|
||||||
|
@text="Revoke credentials"
|
||||||
|
@color="critical"
|
||||||
|
{{on "click" (fn (mut this.showConfirmModal) true)}}
|
||||||
|
data-test-confirm-action-trigger
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</Hds::Dropdown>
|
||||||
|
{{#if this.showConfirmModal}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
|
@onClose={{fn (mut this.showConfirmModal) false}}
|
||||||
@confirmTitle="Revoke this?"
|
@confirmTitle="Revoke this?"
|
||||||
@confirmMessage="Any client using these credentials will no longer be able to."
|
@confirmMessage="Any client using these credentials will no longer be able to."
|
||||||
@onConfirmAction={{action
|
@onConfirm={{action
|
||||||
(perform
|
(perform
|
||||||
Item.callMethod
|
Item.callMethod
|
||||||
"destroyRecord"
|
"destroyRecord"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
@onError={{(fn
|
@onError={{(fn
|
||||||
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
(set-flash-message "Clipboard copy failed. Please make sure the browser Clipboard API is allowed." "danger")
|
||||||
)}}
|
)}}
|
||||||
class="toolbar-link is-flex-center"
|
class="toolbar-button"
|
||||||
data-test-copy-button
|
data-test-copy-button
|
||||||
/>
|
/>
|
||||||
</ToolbarActions>
|
</ToolbarActions>
|
||||||
|
|||||||
@@ -69,44 +69,44 @@
|
|||||||
<Icon @name="user" class="has-text-grey-light" />{{list.item.id}}
|
<Icon @name="user" class="has-text-grey-light" />{{list.item.id}}
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
<li class="action">
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<LinkTo @route="credentials" @models={{array this.scope list.item.id}} class="is-block">
|
<dd.ToggleIcon @icon="more-horizontal" @text="More options" @hasChevron={{false}} data-test-popup-menu-trigger />
|
||||||
View credentials
|
<dd.Interactive @text="View credentials" @route="credentials" @models={{array this.scope list.item.id}} />
|
||||||
</LinkTo>
|
<dd.Interactive @text="View role" @route="role" @models={{array this.scope list.item.id}} />
|
||||||
</li>
|
{{#if list.item.updatePath.isPending}}
|
||||||
<li class="action">
|
<dd.Generic>
|
||||||
<LinkTo @route="role" @models={{array this.scope list.item.id}} class="is-block">
|
<LoadingDropdownOption />
|
||||||
View role
|
</dd.Generic>
|
||||||
</LinkTo>
|
{{else}}
|
||||||
</li>
|
{{#if list.item.updatePath.canUpdate}}
|
||||||
{{#if list.item.updatePath.isPending}}
|
<dd.Interactive @text="Edit role" @route="role.edit" @models={{array this.scope list.item.id}} />
|
||||||
<li class="action">
|
{{/if}}
|
||||||
<LoadingDropdownOption />
|
{{#if list.item.updatePath.canDelete}}
|
||||||
</li>
|
<dd.Interactive
|
||||||
{{else}}
|
@text="Delete role"
|
||||||
{{#if list.item.updatePath.canUpdate}}
|
@color="critical"
|
||||||
<LinkTo @route="role.edit" @models={{array this.scope list.item.id}} class="is-block">
|
{{on "click" (fn (mut this.showConfirmModal) true)}}
|
||||||
Edit role
|
data-test-confirm-action-trigger
|
||||||
</LinkTo>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if list.item.updatePath.canDelete}}
|
|
||||||
<ConfirmAction
|
|
||||||
@isInDropdown={{true}}
|
|
||||||
@buttonText="Delete role"
|
|
||||||
@confirmMessage="Are you sure you want to delete {{list.item.id}}?"
|
|
||||||
@onConfirmAction={{action
|
|
||||||
(perform
|
|
||||||
Item.callMethod
|
|
||||||
"destroyRecord"
|
|
||||||
list.item
|
|
||||||
(concat "Successfully deleted role " list.item.id)
|
|
||||||
(concat "There was an error deleting the role " list.item.id)
|
|
||||||
(action "refresh")
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
data-test-scope-delete
|
|
||||||
/>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
</Hds::Dropdown>
|
||||||
|
{{#if this.showConfirmModal}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
|
@confirmMessage="Are you sure you want to delete {{list.item.id}}?"
|
||||||
|
@onConfirm={{action
|
||||||
|
(perform
|
||||||
|
Item.callMethod
|
||||||
|
"destroyRecord"
|
||||||
|
list.item
|
||||||
|
(concat "Successfully deleted role " list.item.id)
|
||||||
|
(concat "There was an error deleting the role " list.item.id)
|
||||||
|
(action "refresh")
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
@onClose={{fn (mut this.showConfirmModal) false}}
|
||||||
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</Item.menu>
|
</Item.menu>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|||||||
@@ -58,22 +58,29 @@
|
|||||||
<Icon @name="folder" class="has-text-grey-light" />{{list.item.id}}
|
<Icon @name="folder" class="has-text-grey-light" />{{list.item.id}}
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
<li class="action">
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<LinkTo @route="scope" @model={{list.item.id}} class="is-block">
|
<dd.ToggleIcon @icon="more-horizontal" @text="More options" @hasChevron={{false}} data-test-popup-menu-trigger />
|
||||||
View scope
|
<dd.Interactive @text="View scope" @route="scope" @model={{list.item.id}} />
|
||||||
</LinkTo>
|
{{#if list.item.updatePath.isPending}}
|
||||||
</li>
|
<dd.Generic>
|
||||||
{{#if list.item.updatePath.isPending}}
|
<LoadingDropdownOption />
|
||||||
<li class="action">
|
</dd.Generic>
|
||||||
<LoadingDropdownOption />
|
{{else if list.item.updatePath.canDelete}}
|
||||||
</li>
|
<dd.Interactive
|
||||||
{{else if list.item.updatePath.canDelete}}
|
@text="Delete scope"
|
||||||
<ConfirmAction
|
@color="critical"
|
||||||
@isInDropdown={{true}}
|
{{on "click" (fn (mut this.showConfirmModal) true)}}
|
||||||
@buttonText="Delete scope"
|
data-test-confirm-action-trigger
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</Hds::Dropdown>
|
||||||
|
{{#if this.showConfirmModal}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
@confirmTitle="Delete scope {{list.item.id}}?"
|
@confirmTitle="Delete scope {{list.item.id}}?"
|
||||||
@confirmMessage="This will permanently delete this scope and all roles and credentials contained within"
|
@confirmMessage="This will permanently delete this scope and all roles and credentials contained within"
|
||||||
@onConfirmAction={{action
|
@onClose={{fn (mut this.showConfirmModal) false}}
|
||||||
|
@onConfirm={{action
|
||||||
(perform
|
(perform
|
||||||
Item.callMethod
|
Item.callMethod
|
||||||
"destroyRecord"
|
"destroyRecord"
|
||||||
|
|||||||
@@ -37,43 +37,40 @@
|
|||||||
<span data-test-role={{role.name}}>{{role.name}}</span>
|
<span data-test-role={{role.name}}>{{role.name}}</span>
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
{{#if role.rolesPath.isLoading}}
|
{{#if (or role.canRead role.canEdit role.canDelete)}}
|
||||||
<li class="action">
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} />
|
<dd.ToggleIcon
|
||||||
</li>
|
@icon="more-horizontal"
|
||||||
{{else}}
|
@text="More options"
|
||||||
<li class="action">
|
@hasChevron={{false}}
|
||||||
<LinkTo
|
data-test-popup-menu-trigger
|
||||||
class="has-text-black has-text-weight-semibold"
|
/>
|
||||||
data-test-details
|
{{#if role.canRead}}
|
||||||
@route="roles.role.details"
|
<dd.Interactive @text="Details" @route="roles.role.details" @model={{role}} data-test-details />
|
||||||
@model={{role}}
|
{{/if}}
|
||||||
@disabled={{eq role.canRead false}}
|
{{#if role.canEdit}}
|
||||||
>
|
<dd.Interactive @text="Edit" data-test-edit @route="roles.role.edit" @model={{role}} />
|
||||||
Details
|
{{/if}}
|
||||||
</LinkTo>
|
{{#if role.canDelete}}
|
||||||
</li>
|
<dd.Interactive
|
||||||
<li class="action">
|
@text="Delete"
|
||||||
<LinkTo
|
data-test-delete
|
||||||
class="has-text-black has-text-weight-semibold"
|
@color="critical"
|
||||||
data-test-edit
|
{{on "click" (fn (mut this.roleToDelete) role)}}
|
||||||
@route="roles.role.edit"
|
/>
|
||||||
@model={{role}}
|
{{/if}}
|
||||||
@disabled={{not role.canEdit}}
|
</Hds::Dropdown>
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
<ConfirmAction
|
|
||||||
data-test-delete
|
|
||||||
@isInDropdown={{true}}
|
|
||||||
@buttonText="Delete"
|
|
||||||
@confirmMessage="Deleting this role means that you’ll need to recreate it in order to generate credentials again."
|
|
||||||
@onConfirmAction={{fn this.onDelete role}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</Item.menu>
|
</Item.menu>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
{{#if this.roleToDelete}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
|
@confirmMessage="Deleting this role means that you’ll need to recreate it in order to generate credentials again."
|
||||||
|
@onConfirm={{fn this.onDelete this.roleToDelete}}
|
||||||
|
@onClose={{fn (mut this.roleToDelete) null}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -8,6 +8,7 @@ import { service } from '@ember/service';
|
|||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { getOwner } from '@ember/application';
|
import { getOwner } from '@ember/application';
|
||||||
import errorMessage from 'vault/utils/error-message';
|
import errorMessage from 'vault/utils/error-message';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module Roles
|
* @module Roles
|
||||||
@@ -20,6 +21,7 @@ import errorMessage from 'vault/utils/error-message';
|
|||||||
*/
|
*/
|
||||||
export default class RolesPageComponent extends Component {
|
export default class RolesPageComponent extends Component {
|
||||||
@service flashMessages;
|
@service flashMessages;
|
||||||
|
@tracked roleToDelete = null;
|
||||||
|
|
||||||
get mountPoint() {
|
get mountPoint() {
|
||||||
return getOwner(this).mountPoint;
|
return getOwner(this).mountPoint;
|
||||||
@@ -35,6 +37,8 @@ export default class RolesPageComponent extends Component {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = errorMessage(error, 'Error deleting role. Please try again or contact support');
|
const message = errorMessage(error, 'Error deleting role. Please try again or contact support');
|
||||||
this.flashMessages.danger(message);
|
this.flashMessages.danger(message);
|
||||||
|
} finally {
|
||||||
|
this.roleToDelete = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,46 +49,40 @@
|
|||||||
<span data-test-library={{library.name}}>{{library.name}}</span>
|
<span data-test-library={{library.name}}>{{library.name}}</span>
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
{{#if library.libraryPath.isLoading}}
|
{{#if (or library.canRead library.canEdit library.canDelete)}}
|
||||||
<li class="action">
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} />
|
<dd.ToggleIcon
|
||||||
</li>
|
@icon="more-horizontal"
|
||||||
{{else}}
|
@text="More options"
|
||||||
<li class="action">
|
@hasChevron={{false}}
|
||||||
<LinkTo
|
data-test-popup-menu-trigger
|
||||||
class="has-text-black has-text-weight-semibold"
|
|
||||||
data-test-edit
|
|
||||||
@route="libraries.library.edit"
|
|
||||||
@model={{library}}
|
|
||||||
@disabled={{not library.canEdit}}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
<li class="action">
|
|
||||||
<LinkTo
|
|
||||||
class="has-text-black has-text-weight-semibold"
|
|
||||||
data-test-details
|
|
||||||
@route="libraries.library.details"
|
|
||||||
@model={{library}}
|
|
||||||
@disabled={{not library.canRead}}
|
|
||||||
>
|
|
||||||
Details
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
{{#if library.canDelete}}
|
|
||||||
<ConfirmAction
|
|
||||||
data-test-delete
|
|
||||||
@id={{library.id}}
|
|
||||||
@isInDropdown={{true}}
|
|
||||||
@buttonText="Delete"
|
|
||||||
@confirmMessage="This library and associated accounts will be permanently deleted. You will not be able to recover it."
|
|
||||||
@onConfirmAction={{fn this.onDelete library}}
|
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{#if library.canEdit}}
|
||||||
|
<dd.Interactive @text="Edit" data-test-edit @route="libraries.library.edit" @model={{library}} />
|
||||||
|
{{/if}}
|
||||||
|
{{#if library.canRead}}
|
||||||
|
<dd.Interactive @text="Details" data-test-details @route="libraries.library.details" @model={{library}} />
|
||||||
|
{{/if}}
|
||||||
|
{{#if library.canDelete}}
|
||||||
|
<dd.Interactive
|
||||||
|
@text="Delete"
|
||||||
|
data-test-delete
|
||||||
|
@color="critical"
|
||||||
|
{{on "click" (fn (mut this.libraryToDelete) library)}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</Hds::Dropdown>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</Item.menu>
|
</Item.menu>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
{{#if this.libraryToDelete}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
|
@confirmMessage="This library and associated accounts will be permanently deleted. You will not be able to recover it."
|
||||||
|
@onConfirm={{fn this.onDelete this.libraryToDelete}}
|
||||||
|
@onClose={{fn (mut this.libraryToDelete) false}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -26,6 +26,7 @@ export default class LdapLibrariesPageComponent extends Component<Args> {
|
|||||||
@service declare readonly flashMessages: FlashMessageService;
|
@service declare readonly flashMessages: FlashMessageService;
|
||||||
|
|
||||||
@tracked filterValue = '';
|
@tracked filterValue = '';
|
||||||
|
@tracked libraryToDelete: LdapLibraryModel | null = null;
|
||||||
|
|
||||||
get mountPoint(): string {
|
get mountPoint(): string {
|
||||||
const owner = getOwner(this) as EngineOwner;
|
const owner = getOwner(this) as EngineOwner;
|
||||||
|
|||||||
@@ -50,70 +50,63 @@
|
|||||||
<Hds::Badge @text={{role.type}} data-test-role-type-badge={{role.name}} />
|
<Hds::Badge @text={{role.type}} data-test-role-type-badge={{role.name}} />
|
||||||
</Item.content>
|
</Item.content>
|
||||||
<Item.menu>
|
<Item.menu>
|
||||||
{{#if role.rolePath.isLoading}}
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<li class="action">
|
<dd.ToggleIcon @icon="more-horizontal" @text="More options" @hasChevron={{false}} data-test-popup-menu-trigger />
|
||||||
<Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} />
|
{{#if role.canEdit}}
|
||||||
</li>
|
<dd.Interactive @text="Edit" data-test-edit @route="roles.role.edit" @models={{array role.type role.name}} />
|
||||||
{{else}}
|
{{/if}}
|
||||||
<li class="action">
|
{{#if role.canReadCreds}}
|
||||||
<LinkTo
|
<dd.Interactive
|
||||||
class="has-text-black has-text-weight-semibold"
|
@text="Get credentials"
|
||||||
data-test-edit
|
|
||||||
@route="roles.role.edit"
|
|
||||||
@models={{array role.type role.name}}
|
|
||||||
@disabled={{not role.canEdit}}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
<li class="action">
|
|
||||||
<LinkTo
|
|
||||||
class="has-text-black has-text-weight-semibold"
|
|
||||||
data-test-get-creds
|
data-test-get-creds
|
||||||
@route="roles.role.credentials"
|
@route="roles.role.credentials"
|
||||||
@models={{array role.type role.name}}
|
@models={{array role.type role.name}}
|
||||||
@disabled={{not role.canReadCreds}}
|
/>
|
||||||
>
|
{{/if}}
|
||||||
Get credentials
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
{{#if role.canRotateStaticCreds}}
|
{{#if role.canRotateStaticCreds}}
|
||||||
<ConfirmAction
|
<dd.Interactive
|
||||||
|
@text="Rotate credentials"
|
||||||
data-test-rotate-creds
|
data-test-rotate-creds
|
||||||
@isInDropdown={{true}}
|
@color="critical"
|
||||||
@id={{concat "rotate-" role.id}}
|
{{on "click" (fn (mut this.credsToRotate) role)}}
|
||||||
@buttonText="Rotate credentials"
|
|
||||||
@confirmMessage="When manually rotating credentials, the rotation period will start over."
|
|
||||||
@modalColor="warning"
|
|
||||||
@onConfirmAction={{fn this.onRotate role}}
|
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li class="action">
|
<dd.Interactive
|
||||||
<LinkTo
|
@text="Details"
|
||||||
class="has-text-black has-text-weight-semibold"
|
data-test-details
|
||||||
data-test-details
|
@route="roles.role.details"
|
||||||
@route="roles.role.details"
|
{{! this will force the roles.role model hook to fire since we may only have a partial model loaded in the list view }}
|
||||||
{{! this will force the roles.role model hook to fire since we may only have a partial model loaded in the list view }}
|
@models={{array role.type role.name}}
|
||||||
@models={{array role.type role.name}}
|
/>
|
||||||
@disabled={{not role.canRead}}
|
|
||||||
>
|
|
||||||
Details
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
{{#if role.canDelete}}
|
{{#if role.canDelete}}
|
||||||
<ConfirmAction
|
<dd.Interactive
|
||||||
|
@text="Delete"
|
||||||
data-test-delete
|
data-test-delete
|
||||||
@isInDropdown={{true}}
|
@color="critical"
|
||||||
@buttonText="Delete"
|
{{on "click" (fn (mut this.roleToDelete) role)}}
|
||||||
@confirmMessage="Deleting this role means that you’ll need to recreate it in order to generate credentials again."
|
|
||||||
@onConfirmAction={{fn this.onDelete role}}
|
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
</Hds::Dropdown>
|
||||||
</Item.menu>
|
</Item.menu>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if this.credsToRotate}}
|
||||||
|
<ConfirmModal
|
||||||
|
@confirmMessage="When manually rotating credentials, the rotation period will start over."
|
||||||
|
@onConfirm={{fn this.onRotate this.credsToRotate}}
|
||||||
|
@onClose={{fn (mut this.credsToRotate) null}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{#if this.roleToDelete}}
|
||||||
|
<ConfirmModal
|
||||||
|
@color="critical"
|
||||||
|
@confirmMessage="Deleting this role means that you’ll need to recreate it in order to generate credentials again."
|
||||||
|
@onConfirm={{fn this.onDelete this.roleToDelete}}
|
||||||
|
@onClose={{fn (mut this.roleToDelete) null}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<Hds::Pagination::Numbered
|
<Hds::Pagination::Numbered
|
||||||
@currentPage={{@roles.meta.currentPage}}
|
@currentPage={{@roles.meta.currentPage}}
|
||||||
@currentPageSize={{@roles.meta.pageSize}}
|
@currentPageSize={{@roles.meta.pageSize}}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import type FlashMessageService from 'vault/services/flash-messages';
|
|||||||
import type { Breadcrumb, EngineOwner } from 'vault/vault/app-types';
|
import type { Breadcrumb, EngineOwner } from 'vault/vault/app-types';
|
||||||
import type RouterService from '@ember/routing/router-service';
|
import type RouterService from '@ember/routing/router-service';
|
||||||
import type StoreService from 'vault/services/store';
|
import type StoreService from 'vault/services/store';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
interface Args {
|
interface Args {
|
||||||
roles: Array<LdapRoleModel>;
|
roles: Array<LdapRoleModel>;
|
||||||
@@ -28,6 +29,8 @@ export default class LdapRolesPageComponent extends Component<Args> {
|
|||||||
@service declare readonly flashMessages: FlashMessageService;
|
@service declare readonly flashMessages: FlashMessageService;
|
||||||
@service declare readonly router: RouterService;
|
@service declare readonly router: RouterService;
|
||||||
@service declare readonly store: StoreService;
|
@service declare readonly store: StoreService;
|
||||||
|
@tracked credsToRotate: LdapRoleModel | null = null;
|
||||||
|
@tracked roleToDelete: LdapRoleModel | null = null;
|
||||||
|
|
||||||
get mountPoint(): string {
|
get mountPoint(): string {
|
||||||
const owner = getOwner(this) as EngineOwner;
|
const owner = getOwner(this) as EngineOwner;
|
||||||
@@ -51,6 +54,8 @@ export default class LdapRolesPageComponent extends Component<Args> {
|
|||||||
this.flashMessages.success(message);
|
this.flashMessages.success(message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.flashMessages.danger(`Error rotating credentials \n ${errorMessage(error)}`);
|
this.flashMessages.danger(`Error rotating credentials \n ${errorMessage(error)}`);
|
||||||
|
} finally {
|
||||||
|
this.credsToRotate = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +69,8 @@ export default class LdapRolesPageComponent extends Component<Args> {
|
|||||||
this.flashMessages.success(message);
|
this.flashMessages.success(message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.flashMessages.danger(`Error deleting role \n ${errorMessage(error)}`);
|
this.flashMessages.danger(`Error deleting role \n ${errorMessage(error)}`);
|
||||||
|
} finally {
|
||||||
|
this.roleToDelete = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
</code>
|
</code>
|
||||||
</Item.content>
|
</Item.content>
|
||||||
|
|
||||||
<Item.menu @hasMenu={{false}}>
|
<Item.menu>
|
||||||
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
|
||||||
<dd.ToggleIcon
|
<dd.ToggleIcon
|
||||||
@icon="more-horizontal"
|
@icon="more-horizontal"
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
|||||||
@service declare readonly store: StoreService;
|
@service declare readonly store: StoreService;
|
||||||
@service declare readonly flashMessages: FlashMessageService;
|
@service declare readonly flashMessages: FlashMessageService;
|
||||||
|
|
||||||
@tracked destinationToDelete = null;
|
@tracked destinationToDelete: SyncDestinationModel | null = null;
|
||||||
// for some reason there isn't a full page refresh happening when transitioning on filter change
|
// for some reason there isn't a full page refresh happening when transitioning on filter change
|
||||||
// when the transition happens it causes the FilterInput component to lose focus since it can only focus on didInsert
|
// when the transition happens it causes the FilterInput component to lose focus since it can only focus on didInsert
|
||||||
// to work around this, verify that a transition from this route was completed and then focus the input
|
// to work around this, verify that a transition from this route was completed and then focus the input
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</Item.content>
|
</Item.content>
|
||||||
|
|
||||||
<Item.menu @hasMenu={{false}}>
|
<Item.menu>
|
||||||
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" @width="210px" as |dd|>
|
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" @width="210px" as |dd|>
|
||||||
<dd.ToggleIcon
|
<dd.ToggleIcon
|
||||||
@icon="more-horizontal"
|
@icon="more-horizontal"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
|
|||||||
@service declare readonly store: StoreService;
|
@service declare readonly store: StoreService;
|
||||||
@service declare readonly flashMessages: FlashMessageService;
|
@service declare readonly flashMessages: FlashMessageService;
|
||||||
|
|
||||||
@tracked secretToUnsync = null;
|
@tracked secretToUnsync: SyncAssociationModel | null = null;
|
||||||
|
|
||||||
get mountPoint(): string {
|
get mountPoint(): string {
|
||||||
const owner = getOwner(this) as EngineOwner;
|
const owner = getOwner(this) as EngineOwner;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import rolesPage from 'vault/tests/pages/secrets/backend/kmip/roles';
|
|||||||
import credentialsPage from 'vault/tests/pages/secrets/backend/kmip/credentials';
|
import credentialsPage from 'vault/tests/pages/secrets/backend/kmip/credentials';
|
||||||
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
|
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
|
||||||
import { allEngines } from 'vault/helpers/mountable-secret-engines';
|
import { allEngines } from 'vault/helpers/mountable-secret-engines';
|
||||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
|
||||||
import { runCmd } from 'vault/tests/helpers/commands';
|
import { runCmd } from 'vault/tests/helpers/commands';
|
||||||
|
|
||||||
const getRandomPort = () => {
|
const getRandomPort = () => {
|
||||||
@@ -316,12 +315,6 @@ module('Acceptance | Enterprise | KMIP secrets', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it can revoke a credential from the list', async function (assert) {
|
test('it can revoke a credential from the list', async function (assert) {
|
||||||
// Popup menu causes flakiness
|
|
||||||
setRunOptions({
|
|
||||||
rules: {
|
|
||||||
'color-contrast': { enabled: false },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { path, scope, role } = await generateCreds();
|
const { path, scope, role } = await generateCreds();
|
||||||
await credentialsPage.visit({ backend: path, scope, role });
|
await credentialsPage.visit({ backend: path, scope, role });
|
||||||
// revoke the credentials
|
// revoke the credentials
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import kubernetesScenario from 'vault/mirage/scenarios/kubernetes';
|
|||||||
import kubernetesHandlers from 'vault/mirage/handlers/kubernetes';
|
import kubernetesHandlers from 'vault/mirage/handlers/kubernetes';
|
||||||
import authPage from 'vault/tests/pages/auth';
|
import authPage from 'vault/tests/pages/auth';
|
||||||
import { fillIn, visit, currentURL, click, currentRouteName } from '@ember/test-helpers';
|
import { fillIn, visit, currentURL, click, currentRouteName } from '@ember/test-helpers';
|
||||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
|
||||||
|
|
||||||
module('Acceptance | kubernetes | roles', function (hooks) {
|
module('Acceptance | kubernetes | roles', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
@@ -55,12 +54,6 @@ module('Acceptance | kubernetes | roles', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it should have functional list item menu', async function (assert) {
|
test('it should have functional list item menu', async function (assert) {
|
||||||
// Popup menu causes flakiness
|
|
||||||
setRunOptions({
|
|
||||||
rules: {
|
|
||||||
'color-contrast': { enabled: false },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
assert.expect(3);
|
assert.expect(3);
|
||||||
await this.visitRoles();
|
await this.visitRoles();
|
||||||
for (const action of ['details', 'edit', 'delete']) {
|
for (const action of ['details', 'edit', 'delete']) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import ldapHandlers from 'vault/mirage/handlers/ldap';
|
|||||||
import authPage from 'vault/tests/pages/auth';
|
import authPage from 'vault/tests/pages/auth';
|
||||||
import { click } from '@ember/test-helpers';
|
import { click } from '@ember/test-helpers';
|
||||||
import { isURL, visitURL } from 'vault/tests/helpers/ldap';
|
import { isURL, visitURL } from 'vault/tests/helpers/ldap';
|
||||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
|
||||||
|
|
||||||
module('Acceptance | ldap | libraries', function (hooks) {
|
module('Acceptance | ldap | libraries', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
@@ -38,12 +37,6 @@ module('Acceptance | ldap | libraries', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it should transition to routes from list item action menu', async function (assert) {
|
test('it should transition to routes from list item action menu', async function (assert) {
|
||||||
// Popup menu causes flakiness
|
|
||||||
setRunOptions({
|
|
||||||
rules: {
|
|
||||||
'color-contrast': { enabled: false },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
assert.expect(2);
|
assert.expect(2);
|
||||||
|
|
||||||
for (const action of ['edit', 'details']) {
|
for (const action of ['edit', 'details']) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import ldapHandlers from 'vault/mirage/handlers/ldap';
|
|||||||
import authPage from 'vault/tests/pages/auth';
|
import authPage from 'vault/tests/pages/auth';
|
||||||
import { click, fillIn } from '@ember/test-helpers';
|
import { click, fillIn } from '@ember/test-helpers';
|
||||||
import { isURL, visitURL } from 'vault/tests/helpers/ldap';
|
import { isURL, visitURL } from 'vault/tests/helpers/ldap';
|
||||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
|
||||||
|
|
||||||
module('Acceptance | ldap | roles', function (hooks) {
|
module('Acceptance | ldap | roles', function (hooks) {
|
||||||
setupApplicationTest(hooks);
|
setupApplicationTest(hooks);
|
||||||
@@ -45,12 +44,6 @@ module('Acceptance | ldap | roles', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it should transition to routes from list item action menu', async function (assert) {
|
test('it should transition to routes from list item action menu', async function (assert) {
|
||||||
// Popup menu causes flakiness
|
|
||||||
setRunOptions({
|
|
||||||
rules: {
|
|
||||||
'color-contrast': { enabled: false },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
assert.expect(3);
|
assert.expect(3);
|
||||||
|
|
||||||
for (const action of ['edit', 'get-creds', 'details']) {
|
for (const action of ['edit', 'get-creds', 'details']) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { setupEngine } from 'ember-engines/test-support';
|
|||||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||||
import { render, click } from '@ember/test-helpers';
|
import { render, click } from '@ember/test-helpers';
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
|
import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs';
|
||||||
|
|
||||||
module('Integration | Component | kubernetes | Page::Roles', function (hooks) {
|
module('Integration | Component | kubernetes | Page::Roles', function (hooks) {
|
||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
@@ -17,6 +18,7 @@ module('Integration | Component | kubernetes | Page::Roles', function (hooks) {
|
|||||||
|
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
this.store = this.owner.lookup('service:store');
|
this.store = this.owner.lookup('service:store');
|
||||||
|
this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub(['read', 'update', 'delete']));
|
||||||
this.store.pushPayload('secret-engine', {
|
this.store.pushPayload('secret-engine', {
|
||||||
modelName: 'secret-engine',
|
modelName: 'secret-engine',
|
||||||
data: {
|
data: {
|
||||||
@@ -88,11 +90,6 @@ module('Integration | Component | kubernetes | Page::Roles', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it should render roles list', async function (assert) {
|
test('it should render roles list', async function (assert) {
|
||||||
this.server.post('/sys/capabilities-self', () => ({
|
|
||||||
data: {
|
|
||||||
'kubernetes/role': ['root'],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
await this.renderComponent();
|
await this.renderComponent();
|
||||||
assert.dom('[data-test-list-item-content] svg').hasClass('flight-icon-user', 'List item icon renders');
|
assert.dom('[data-test-list-item-content] svg').hasClass('flight-icon-user', 'List item icon renders');
|
||||||
assert
|
assert
|
||||||
|
|||||||
Reference in New Issue
Block a user