mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	UI: Hds::Dropdown replace PopupMenu (#25321)
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/25321.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/25321.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:improvement | ||||||
|  | ui: Use Hds::Dropdown component to replace list view popup menus | ||||||
|  | ``` | ||||||
| @@ -1,46 +0,0 @@ | |||||||
| /** |  | ||||||
|  * Copyright (c) HashiCorp, Inc. |  | ||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| import { inject as service } from '@ember/service'; |  | ||||||
| import { assert } from '@ember/debug'; |  | ||||||
| import Component from '@ember/component'; |  | ||||||
|  |  | ||||||
| export default Component.extend({ |  | ||||||
|   tagName: '', |  | ||||||
|   flashMessages: service(), |  | ||||||
|   params: null, |  | ||||||
|   successMessage() { |  | ||||||
|     return 'Save was successful'; |  | ||||||
|   }, |  | ||||||
|   errorMessage() { |  | ||||||
|     return 'There was an error saving'; |  | ||||||
|   }, |  | ||||||
|   onError(model) { |  | ||||||
|     if (model && model.rollbackAttributes) { |  | ||||||
|       model.rollbackAttributes(); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|   onSuccess() {}, |  | ||||||
|   // override and return a promise |  | ||||||
|   transaction() { |  | ||||||
|     assert('override transaction call in an extension of popup-base', false); |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   actions: { |  | ||||||
|     performTransaction() { |  | ||||||
|       const args = [...arguments]; |  | ||||||
|       const messageArgs = this.messageArgs(...args); |  | ||||||
|       return this.transaction(...args) |  | ||||||
|         .then(() => { |  | ||||||
|           this.onSuccess(); |  | ||||||
|           this.flashMessages.success(this.successMessage(...messageArgs)); |  | ||||||
|         }) |  | ||||||
|         .catch((e) => { |  | ||||||
|           this.onError(...messageArgs); |  | ||||||
|           this.flashMessages.success(this.errorMessage(e, ...messageArgs)); |  | ||||||
|         }); |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
| @@ -3,25 +3,38 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import Base from './_popup-base'; | import Component from '@glimmer/component'; | ||||||
|  | import { service } from '@ember/service'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  | import { action } from '@ember/object'; | ||||||
|  | import errorMessage from 'vault/utils/error-message'; | ||||||
|  |  | ||||||
| export default Base.extend({ | export default class IdentityPopupAlias extends Component { | ||||||
|   messageArgs(model) { |   @service flashMessages; | ||||||
|     const type = model.get('identityType'); |   @tracked showConfirmModal = false; | ||||||
|     const id = model.id; |  | ||||||
|     return [type, id]; |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   successMessage(type, id) { |   onSuccess(type, id) { | ||||||
|     return `Successfully deleted ${type}: ${id}`; |     if (this.args.onSuccess) { | ||||||
|   }, |       this.args.onSuccess(); | ||||||
|  |     } | ||||||
|  |     this.flashMessages.success(`Successfully deleted ${type}: ${id}`); | ||||||
|  |   } | ||||||
|  |   onError(err, type, id) { | ||||||
|  |     if (this.args.onError) { | ||||||
|  |       this.args.onError(); | ||||||
|  |     } | ||||||
|  |     const error = errorMessage(err); | ||||||
|  |     this.flashMessages.danger(`There was a problem deleting ${type}: ${id} - ${error}`); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   errorMessage(e, type, id) { |   @action | ||||||
|     const error = e.errors ? e.errors.join(' ') : e.message; |   async deleteAlias() { | ||||||
|     return `There was a problem deleting ${type}: ${id} - ${error}`; |     const { identityType, id } = this.args.item; | ||||||
|   }, |     try { | ||||||
|  |       await this.args.item.destroyRecord(); | ||||||
|   transaction(model) { |       this.onSuccess(identityType, id); | ||||||
|     return model.destroyRecord(); |     } catch (e) { | ||||||
|   }, |       this.onError(e, identityType, id); | ||||||
| }); |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,37 +3,44 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import { alias } from '@ember/object/computed'; | import Component from '@glimmer/component'; | ||||||
| import { computed } from '@ember/object'; | import { action } from '@ember/object'; | ||||||
| import Base from './_popup-base'; | import { service } from '@ember/service'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  | import errorMessage from 'vault/utils/error-message'; | ||||||
|  |  | ||||||
| export default Base.extend({ | export default class IdentityPopupMembers extends Component { | ||||||
|   model: alias('params.firstObject'), |   @service flashMessages; | ||||||
|  |   @tracked showConfirmModal = false; | ||||||
|  |  | ||||||
|   groupArray: computed('params', function () { |   onSuccess(memberId) { | ||||||
|     return this.params.objectAt(1); |     if (this.args.onSuccess) { | ||||||
|   }), |       this.args.onSuccess(); | ||||||
|  |     } | ||||||
|  |     this.flashMessages.success(`Successfully removed '${memberId}' from the group`); | ||||||
|  |   } | ||||||
|  |   onError(err, memberId) { | ||||||
|  |     if (this.args.onError) { | ||||||
|  |       this.args.onError(); | ||||||
|  |     } | ||||||
|  |     const error = errorMessage(err); | ||||||
|  |     this.flashMessages.danger(`There was a problem removing '${memberId}' from the group - ${error}`); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   memberId: computed('params', function () { |   transaction() { | ||||||
|     return this.params.objectAt(2); |     const members = this.args.model[this.args.groupArray]; | ||||||
|   }), |     this.args.model[this.args.groupArray] = members.without(this.args.memberId); | ||||||
|  |     return this.args.model.save(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   messageArgs(/*model, groupArray, memberId*/) { |   @action | ||||||
|     return [...arguments]; |   async removeGroup() { | ||||||
|   }, |     const memberId = this.args.memberId; | ||||||
|  |     try { | ||||||
|   successMessage(model, groupArray, memberId) { |       await this.transaction(); | ||||||
|     return `Successfully removed '${memberId}' from the group`; |       this.onSuccess(memberId); | ||||||
|   }, |     } catch (e) { | ||||||
|  |       this.onError(e, memberId); | ||||||
|   errorMessage(e, model, groupArray, memberId) { |     } | ||||||
|     const error = e.errors ? e.errors.join(' ') : e.message; |   } | ||||||
|     return `There was a problem removing '${memberId}' from the group - ${error}`; | } | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   transaction(model, groupArray, memberId) { |  | ||||||
|     const members = model.get(groupArray); |  | ||||||
|     model.set(groupArray, members.without(memberId)); |  | ||||||
|     return model.save(); |  | ||||||
|   }, |  | ||||||
| }); |  | ||||||
|   | |||||||
| @@ -3,32 +3,45 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import Base from './_popup-base'; | import { action } from '@ember/object'; | ||||||
| import { computed } from '@ember/object'; | import { service } from '@ember/service'; | ||||||
| import { alias } from '@ember/object/computed'; | import Component from '@glimmer/component'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  | import errorMessage from 'vault/utils/error-message'; | ||||||
|  |  | ||||||
| export default Base.extend({ | export default class IdentityPopupMetadata extends Component { | ||||||
|   model: alias('params.firstObject'), |   @service flashMessages; | ||||||
|   key: computed('params', function () { |   @tracked showConfirmModal = false; | ||||||
|     return this.params.objectAt(1); |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   messageArgs(model, key) { |   onSuccess(key) { | ||||||
|     return [model, key]; |     if (this.args.onSuccess) { | ||||||
|   }, |       this.args.onSuccess(); | ||||||
|  |     } | ||||||
|  |     this.flashMessages.success(`Successfully removed '${key}' from metadata`); | ||||||
|  |   } | ||||||
|  |   onError(err, key) { | ||||||
|  |     if (this.args.onError) { | ||||||
|  |       this.args.onError(); | ||||||
|  |     } | ||||||
|  |     const error = errorMessage(err); | ||||||
|  |     this.flashMessages.danger(`There was a problem removing '${key}' from the metadata - ${error}`); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   successMessage(model, key) { |   transaction() { | ||||||
|     return `Successfully removed '${key}' from metadata`; |     const metadata = this.args.model.metadata; | ||||||
|   }, |     delete metadata[this.args.key]; | ||||||
|   errorMessage(e, model, key) { |     this.args.model.metadata = { ...metadata }; | ||||||
|     const error = e.errors ? e.errors.join(' ') : e.message; |     return this.args.model.save(); | ||||||
|     return `There was a problem removing '${key}' from the metadata - ${error}`; |   } | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   transaction(model, key) { |   @action | ||||||
|     const metadata = model.metadata; |   async removeMetadata() { | ||||||
|     delete metadata[key]; |     const key = this.args.key; | ||||||
|     model.set('metadata', { ...metadata }); |     try { | ||||||
|     return model.save(); |       await this.transaction(); | ||||||
|   }, |       this.onSuccess(key); | ||||||
| }); |     } catch (e) { | ||||||
|  |       this.onError(e, key); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,32 +3,47 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import { alias } from '@ember/object/computed'; | import Component from '@glimmer/component'; | ||||||
| import { computed } from '@ember/object'; | import { action } from '@ember/object'; | ||||||
| import Base from './_popup-base'; | import { service } from '@ember/service'; | ||||||
|  | import errorMessage from 'vault/utils/error-message'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  |  | ||||||
| export default Base.extend({ | export default class IdentityPopupPolicy extends Component { | ||||||
|   model: alias('params.firstObject'), |   @service flashMessages; | ||||||
|   policyName: computed('params', function () { |   @tracked showConfirmModal = false; | ||||||
|     return this.params.objectAt(1); |  | ||||||
|   }), |  | ||||||
|  |  | ||||||
|   messageArgs(model, policyName) { |   onSuccess(policyName, modelId) { | ||||||
|     return [model, policyName]; |     if (this.args.onSuccess) { | ||||||
|   }, |       this.args.onSuccess(); | ||||||
|  |     } | ||||||
|  |     this.flashMessages.success(`Successfully removed '${policyName}' policy from ${modelId}`); | ||||||
|  |   } | ||||||
|  |   onError(err, policyName) { | ||||||
|  |     if (this.args.onError) { | ||||||
|  |       this.args.onError(); | ||||||
|  |     } | ||||||
|  |     const error = errorMessage(err); | ||||||
|  |     this.flashMessages.danger(`There was a problem removing '${policyName}' policy - ${error}`); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   successMessage(model, policyName) { |   transaction() { | ||||||
|     return `Successfully removed '${policyName}' policy from ${model.id} `; |     const policies = this.args.model.policies; | ||||||
|   }, |     this.args.model.policies = policies.without(this.args.policyName); | ||||||
|  |     return this.args.model.save(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   errorMessage(e, model, policyName) { |   @action | ||||||
|     const error = e.errors ? e.errors.join(' ') : e.message; |   async removePolicy() { | ||||||
|     return `There was a problem removing '${policyName}' policy - ${error}`; |     const { | ||||||
|   }, |       policyName, | ||||||
|  |       model: { id }, | ||||||
|   transaction(model, policyName) { |     } = this.args; | ||||||
|     const policies = model.get('policies'); |     try { | ||||||
|     model.set('policies', policies.without(policyName)); |       await this.transaction(); | ||||||
|     return model.save(); |       this.onSuccess(policyName, id); | ||||||
|   }, |     } catch (e) { | ||||||
| }); |       this.onError(e, policyName); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								ui/app/components/secret-list/aws-role-item.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ui/app/components/secret-list/aws-role-item.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  |  | ||||||
|  | export default class SecretListAwsRoleItemComponent extends Component { | ||||||
|  |   @tracked showConfirmModal = false; | ||||||
|  | } | ||||||
| @@ -22,6 +22,7 @@ import { action } from '@ember/object'; | |||||||
|  |  | ||||||
| export default class DatabaseListItem extends Component { | export default class DatabaseListItem extends Component { | ||||||
|   @tracked roleType = ''; |   @tracked roleType = ''; | ||||||
|  |   @tracked actionRunning = null; | ||||||
|   @service store; |   @service store; | ||||||
|   @service flashMessages; |   @service flashMessages; | ||||||
|  |  | ||||||
| @@ -41,6 +42,7 @@ export default class DatabaseListItem extends Component { | |||||||
|   resetConnection(id) { |   resetConnection(id) { | ||||||
|     const { backend } = this.args.item; |     const { backend } = this.args.item; | ||||||
|     const adapter = this.store.adapterFor('database/connection'); |     const adapter = this.store.adapterFor('database/connection'); | ||||||
|  |     this.actionRunning = 'reset'; | ||||||
|     adapter |     adapter | ||||||
|       .resetConnection(backend, id) |       .resetConnection(backend, id) | ||||||
|       .then(() => { |       .then(() => { | ||||||
| @@ -48,12 +50,14 @@ export default class DatabaseListItem extends Component { | |||||||
|       }) |       }) | ||||||
|       .catch((e) => { |       .catch((e) => { | ||||||
|         this.flashMessages.danger(e.errors); |         this.flashMessages.danger(e.errors); | ||||||
|       }); |       }) | ||||||
|  |       .finally(() => (this.actionRunning = null)); | ||||||
|   } |   } | ||||||
|   @action |   @action | ||||||
|   rotateRootCred(id) { |   rotateRootCred(id) { | ||||||
|     const { backend } = this.args.item; |     const { backend } = this.args.item; | ||||||
|     const adapter = this.store.adapterFor('database/connection'); |     const adapter = this.store.adapterFor('database/connection'); | ||||||
|  |     this.actionRunning = 'rotateRoot'; | ||||||
|     adapter |     adapter | ||||||
|       .rotateRootCredentials(backend, id) |       .rotateRootCredentials(backend, id) | ||||||
|       .then(() => { |       .then(() => { | ||||||
| @@ -61,12 +65,14 @@ export default class DatabaseListItem extends Component { | |||||||
|       }) |       }) | ||||||
|       .catch((e) => { |       .catch((e) => { | ||||||
|         this.flashMessages.danger(e.errors); |         this.flashMessages.danger(e.errors); | ||||||
|       }); |       }) | ||||||
|  |       .finally(() => (this.actionRunning = null)); | ||||||
|   } |   } | ||||||
|   @action |   @action | ||||||
|   rotateRoleCred(id) { |   rotateRoleCred(id) { | ||||||
|     const { backend } = this.args.item; |     const { backend } = this.args.item; | ||||||
|     const adapter = this.store.adapterFor('database/credential'); |     const adapter = this.store.adapterFor('database/credential'); | ||||||
|  |     this.actionRunning = 'rotateRole'; | ||||||
|     adapter |     adapter | ||||||
|       .rotateRoleCredentials(backend, id) |       .rotateRoleCredentials(backend, id) | ||||||
|       .then(() => { |       .then(() => { | ||||||
| @@ -74,6 +80,7 @@ export default class DatabaseListItem extends Component { | |||||||
|       }) |       }) | ||||||
|       .catch((e) => { |       .catch((e) => { | ||||||
|         this.flashMessages.danger(e.errors); |         this.flashMessages.danger(e.errors); | ||||||
|       }); |       }) | ||||||
|  |       .finally(() => (this.actionRunning = null)); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								ui/app/components/secret-list/item.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ui/app/components/secret-list/item.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  |  | ||||||
|  | export default class SecretListItemComponent extends Component { | ||||||
|  |   @tracked showConfirmModal = false; | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								ui/app/components/secret-list/ssh-role-item.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ui/app/components/secret-list/ssh-role-item.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { tracked } from '@glimmer/tracking'; | ||||||
|  |  | ||||||
|  | export default class SecretListSshRoleItemComponent extends Component { | ||||||
|  |   @tracked showConfirmModal = false; | ||||||
|  | } | ||||||
| @@ -10,6 +10,9 @@ import ListController from 'core/mixins/list-controller'; | |||||||
| export default Controller.extend(ListController, { | export default Controller.extend(ListController, { | ||||||
|   flashMessages: service(), |   flashMessages: service(), | ||||||
|  |  | ||||||
|  |   entityToDisable: null, | ||||||
|  |   itemToDelete: null, | ||||||
|  |  | ||||||
|   // callback from HDS pagination to set the queryParams page |   // callback from HDS pagination to set the queryParams page | ||||||
|   get paginationQueryParams() { |   get paginationQueryParams() { | ||||||
|     return (page) => { |     return (page) => { | ||||||
| @@ -33,7 +36,8 @@ export default Controller.extend(ListController, { | |||||||
|           this.flashMessages.success( |           this.flashMessages.success( | ||||||
|             `There was a problem deleting ${type}: ${id} - ${e.errors.join(' ') || e.message}` |             `There was a problem deleting ${type}: ${id} - ${e.errors.join(' ') || e.message}` | ||||||
|           ); |           ); | ||||||
|         }); |         }) | ||||||
|  |         .finally(() => this.set('itemToDelete', null)); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|     toggleDisabled(model) { |     toggleDisabled(model) { | ||||||
| @@ -51,7 +55,8 @@ export default Controller.extend(ListController, { | |||||||
|           this.flashMessages.success( |           this.flashMessages.success( | ||||||
|             `There was a problem ${action[1]} ${type}: ${id} - ${e.errors.join(' ') || e.message}` |             `There was a problem ${action[1]} ${type}: ${id} - ${e.errors.join(' ') || e.message}` | ||||||
|           ); |           ); | ||||||
|         }); |         }) | ||||||
|  |         .finally(() => this.set('entityToDisable', null)); | ||||||
|     }, |     }, | ||||||
|     reloadRecord(model) { |     reloadRecord(model) { | ||||||
|       model.reload(); |       model.reload(); | ||||||
|   | |||||||
| @@ -21,8 +21,8 @@ export default Controller.extend({ | |||||||
|  |  | ||||||
|   filterFocused: false, |   filterFocused: false, | ||||||
|  |  | ||||||
|   // set via the route `loading` action |   isLoading: false, // set via the route `loading` action | ||||||
|   isLoading: false, |   policyToDelete: null, // set when clicking 'Delete' from popup menu | ||||||
|  |  | ||||||
|   // callback from HDS pagination to set the queryParams page |   // callback from HDS pagination to set the queryParams page | ||||||
|   get paginationQueryParams() { |   get paginationQueryParams() { | ||||||
| @@ -77,7 +77,8 @@ export default Controller.extend({ | |||||||
|           flash.danger( |           flash.danger( | ||||||
|             `There was an error deleting the ${policyType.toUpperCase()} policy "${name}": ${errors}.` |             `There was an error deleting the ${policyType.toUpperCase()} policy "${name}": ${errors}.` | ||||||
|           ); |           ); | ||||||
|         }); |         }) | ||||||
|  |         .finally(() => this.set('policyToDelete', null)); | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ export default class VaultClusterSecretsBackendController extends Controller { | |||||||
|   @tracked secretEngineOptions = []; |   @tracked secretEngineOptions = []; | ||||||
|   @tracked selectedEngineType = null; |   @tracked selectedEngineType = null; | ||||||
|   @tracked selectedEngineName = null; |   @tracked selectedEngineName = null; | ||||||
|  |   @tracked engineToDisable = null; | ||||||
|  |  | ||||||
|   get sortedDisplayableBackends() { |   get sortedDisplayableBackends() { | ||||||
|     // show supported secret engines first and then organize those by id. |     // show supported secret engines first and then organize those by id. | ||||||
| @@ -80,6 +81,8 @@ export default class VaultClusterSecretsBackendController extends Controller { | |||||||
|       this.flashMessages.danger( |       this.flashMessages.danger( | ||||||
|         `There was an error disabling the ${engineType} Secrets Engine at ${path}: ${err.errors.join(' ')}.` |         `There was an error disabling the ${engineType} Secrets Engine at ${path}: ${err.errors.join(' ')}.` | ||||||
|       ); |       ); | ||||||
|  |     } finally { | ||||||
|  |       this.engineToDisable = null; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -84,13 +84,5 @@ export default IdentityModel.extend({ | |||||||
|   canEdit: alias('updatePath.canUpdate'), |   canEdit: alias('updatePath.canUpdate'), | ||||||
|  |  | ||||||
|   aliasPath: lazyCapabilities(apiPath`identity/group-alias`), |   aliasPath: lazyCapabilities(apiPath`identity/group-alias`), | ||||||
|   canAddAlias: computed('aliasPath.canCreate', 'type', 'alias', function () { |   canAddAlias: alias('aliasPath.canCreate'), | ||||||
|     const type = this.type; |  | ||||||
|     const alias = this.alias; |  | ||||||
|     // internal groups can't have aliases, and external groups can only have one |  | ||||||
|     if (type === 'internal' || alias) { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|     return this.aliasPath.canCreate; |  | ||||||
|   }), |  | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -101,12 +101,12 @@ export default class OidcClientModel extends Model { | |||||||
|   // CAPABILITIES // |   // CAPABILITIES // | ||||||
|   @lazyCapabilities(apiPath`identity/oidc/client/${'name'}`, 'name') clientPath; |   @lazyCapabilities(apiPath`identity/oidc/client/${'name'}`, 'name') clientPath; | ||||||
|   get canRead() { |   get canRead() { | ||||||
|     return this.clientPath.get('canRead'); |     return this.clientPath.get('canRead') !== false; | ||||||
|   } |   } | ||||||
|   get canEdit() { |   get canEdit() { | ||||||
|     return this.clientPath.get('canUpdate'); |     return this.clientPath.get('canUpdate') !== false; | ||||||
|   } |   } | ||||||
|   get canDelete() { |   get canDelete() { | ||||||
|     return this.clientPath.get('canDelete'); |     return this.clientPath.get('canDelete') !== false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -53,12 +53,12 @@ export default class OidcProviderModel extends Model { | |||||||
|  |  | ||||||
|   @lazyCapabilities(apiPath`identity/oidc/provider/${'name'}`, 'name') providerPath; |   @lazyCapabilities(apiPath`identity/oidc/provider/${'name'}`, 'name') providerPath; | ||||||
|   get canRead() { |   get canRead() { | ||||||
|     return this.providerPath.get('canRead'); |     return this.providerPath.get('canRead') !== false; | ||||||
|   } |   } | ||||||
|   get canEdit() { |   get canEdit() { | ||||||
|     return this.providerPath.get('canUpdate'); |     return this.providerPath.get('canUpdate') !== false; | ||||||
|   } |   } | ||||||
|   get canDelete() { |   get canDelete() { | ||||||
|     return this.providerPath.get('canDelete'); |     return this.providerPath.get('canDelete') !== false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ | |||||||
|         </span> |         </span> | ||||||
|       </div> |       </div> | ||||||
|       <div class="column has-text-right"> |       <div class="column has-text-right"> | ||||||
|         <Identity::PopupAlias @params={{array item}} /> |         <Identity::PopupAlias @item={{item}} /> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </LinkedBlock> |   </LinkedBlock> | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ | |||||||
|         </div> |         </div> | ||||||
|         <div class="column has-text-right"> |         <div class="column has-text-right"> | ||||||
|           {{#if @model.canEdit}} |           {{#if @model.canEdit}} | ||||||
|             <Identity::PopupMembers @params={{array @model "memberGroupIds" gid}} /> |             <Identity::PopupMembers @model={{@model}} @groupArray="memberGroupIds" @memberId={{gid}} /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
| @@ -38,7 +38,7 @@ | |||||||
|         </div> |         </div> | ||||||
|         <div class="column has-text-right"> |         <div class="column has-text-right"> | ||||||
|           {{#if @model.canEdit}} |           {{#if @model.canEdit}} | ||||||
|             <Identity::PopupMembers @params={{array @model "memberEntityIds" gid}} /> |             <Identity::PopupMembers @model={{@model}} @groupArray="memberEntityIds" @memberId={{gid}} /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="column has-text-right"> |       <div class="column has-text-right"> | ||||||
|         {{#if @model.canEdit}} |         {{#if @model.canEdit}} | ||||||
|           <Identity::PopupMetadata @params={{array @model key}} /> |           <Identity::PopupMetadata @key={{key}} @model={{@model}} /> | ||||||
|         {{/if}} |         {{/if}} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="column has-text-right"> |       <div class="column has-text-right"> | ||||||
|         {{#if @model.canEdit}} |         {{#if @model.canEdit}} | ||||||
|           <Identity::PopupPolicy @params={{array @model policyName}} /> |           <Identity::PopupPolicy @model={{@model}} @policyName={{policyName}} /> | ||||||
|         {{/if}} |         {{/if}} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -3,43 +3,38 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
| <PopupMenu @name="alias-menu"> | <div class="has-text-right"> | ||||||
|   {{#let (get this.params "0") as |item|}} |   <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|     <nav class="menu" aria-label="navigation for managing aliases"> |     <dd.ToggleIcon | ||||||
|       <ul class="menu-list"> |       @icon="more-horizontal" | ||||||
|         <li class="action"> |       @text="Alias management options" | ||||||
|           <LinkTo |       @hasChevron={{false}} | ||||||
|  |       data-test-popup-menu-trigger | ||||||
|  |     /> | ||||||
|  |     <dd.Interactive | ||||||
|  |       @text="Details" | ||||||
|       @route="vault.cluster.access.identity.aliases.show" |       @route="vault.cluster.access.identity.aliases.show" | ||||||
|             @models={{array (pluralize item.parentType) item.id "details"}} |       @models={{array (pluralize @item.parentType) @item.id "details"}} | ||||||
|           > |     /> | ||||||
|             Details |     {{#if @item.updatePath.isPending}} | ||||||
|           </LinkTo> |       <dd.Generic class="has-text-center"> | ||||||
|         </li> |  | ||||||
|         {{#if item.updatePath.isPending}} |  | ||||||
|           <li class="action"> |  | ||||||
|         <LoadingDropdownOption /> |         <LoadingDropdownOption /> | ||||||
|           </li> |       </dd.Generic> | ||||||
|     {{else}} |     {{else}} | ||||||
|           {{#if item.canEdit}} |       {{#if @item.canEdit}} | ||||||
|             <li class="action"> |         <dd.Interactive | ||||||
|               <LinkTo |           @text="Edit" | ||||||
|           @route="vault.cluster.access.identity.aliases.edit" |           @route="vault.cluster.access.identity.aliases.edit" | ||||||
|                 @models={{array (pluralize item.parentType) item.id}} |           @models={{array (pluralize @item.parentType) @item.id}} | ||||||
|               > |  | ||||||
|                 Edit |  | ||||||
|               </LinkTo> |  | ||||||
|             </li> |  | ||||||
|           {{/if}} |  | ||||||
|           {{#if item.canDelete}} |  | ||||||
|             <ConfirmAction |  | ||||||
|               @buttonText="Delete" |  | ||||||
|               @isInDropdown={{true}} |  | ||||||
|               @onConfirmAction={{action "performTransaction" item}} |  | ||||||
|               data-test-item-delete |  | ||||||
|         /> |         /> | ||||||
|       {{/if}} |       {{/if}} | ||||||
|  |       {{#if @item.canDelete}} | ||||||
|  |         <dd.Interactive @text="Remove" @color="critical" {{on "click" (fn (mut this.showConfirmModal) true)}} /> | ||||||
|  |       {{/if}} | ||||||
|  |     {{/if}} | ||||||
|  |   </Hds::Dropdown> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal @color="critical" @onClose={{fn (mut this.showConfirmModal) false}} @onConfirm={{this.deleteAlias}} /> | ||||||
| {{/if}} | {{/if}} | ||||||
|       </ul> |  | ||||||
|     </nav> |  | ||||||
|   {{/let}} |  | ||||||
| </PopupMenu> |  | ||||||
| @@ -3,18 +3,24 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
| <PopupMenu @name="member-edit-menu"> | <div class="has-text-right"> | ||||||
|   <nav class="menu" aria-label="navigation for managing identity members"> |   <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|     <ul class="menu-list"> |     <dd.ToggleIcon | ||||||
|       <li class="action"> |       @icon="more-horizontal" | ||||||
|         <ConfirmAction |       @text="Identity member options" | ||||||
|           @buttonText="Remove" |       @hasChevron={{false}} | ||||||
|           @confirmTitle="Remove this group?" |       data-test-popup-menu-trigger | ||||||
|           @isInDropdown={{true}} |  | ||||||
|           @confirmMessage="This may affect permissions for this group." |  | ||||||
|           @onConfirmAction={{action "performTransaction" this.model this.groupArray this.memberId}} |  | ||||||
|     /> |     /> | ||||||
|       </li> |     <dd.Interactive @text="Remove" @color="critical" {{on "click" (fn (mut this.showConfirmModal) true)}} /> | ||||||
|     </ul> |   </Hds::Dropdown> | ||||||
|   </nav> | </div> | ||||||
| </PopupMenu> |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @onClose={{fn (mut this.showConfirmModal) false}} | ||||||
|  |     @onConfirm={{this.removeGroup}} | ||||||
|  |     @confirmTitle="Remove this group?" | ||||||
|  |     @confirmMessage="This may affect permissions for this group." | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -3,18 +3,19 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
| <PopupMenu @name="metadata-edit-menu"> | <div class="has-text-right"> | ||||||
|   <nav class="menu" aria-label="navigation for managing identity metadata"> |   <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|     <ul class="menu-list"> |     <dd.ToggleIcon @icon="more-horizontal" @text="Metadata options" @hasChevron={{false}} data-test-popup-menu-trigger /> | ||||||
|       <li class="action"> |     <dd.Interactive @text="Remove" @color="critical" {{on "click" (fn (mut this.showConfirmModal) true)}} /> | ||||||
|         <ConfirmAction |   </Hds::Dropdown> | ||||||
|           @buttonText="Remove" | </div> | ||||||
|  |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @onClose={{fn (mut this.showConfirmModal) false}} | ||||||
|  |     @onConfirm={{this.removeMetadata}} | ||||||
|     @confirmTitle="Remove metadata?" |     @confirmTitle="Remove metadata?" | ||||||
|           @isInDropdown={{true}} |  | ||||||
|     @confirmMessage="This data may be used outside of Vault." |     @confirmMessage="This data may be used outside of Vault." | ||||||
|           @onConfirmAction={{action "performTransaction" this.model this.key}} |  | ||||||
|   /> |   /> | ||||||
|       </li> | {{/if}} | ||||||
|     </ul> |  | ||||||
|   </nav> |  | ||||||
| </PopupMenu> |  | ||||||
| @@ -3,28 +3,30 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
| <PopupMenu @name="policy-menu"> | <div class="has-text-right"> | ||||||
|   <nav class="menu" aria-label="navigation for managing identity policies"> |   <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|     <ul class="menu-list"> |     <dd.ToggleIcon | ||||||
|       <li class="action"> |       @icon="more-horizontal" | ||||||
|         <LinkTo @route="vault.cluster.policy.show" @models={{array "acl" this.policyName}}> |       @text="Identity policy management options" | ||||||
|           View Policy |       @hasChevron={{false}} | ||||||
|         </LinkTo> |       data-test-popup-menu-trigger | ||||||
|       </li> |  | ||||||
|       <li class="action"> |  | ||||||
|         <LinkTo @route="vault.cluster.policy.edit" @models={{array "acl" this.policyName}}> |  | ||||||
|           Edit Policy |  | ||||||
|         </LinkTo> |  | ||||||
|       </li> |  | ||||||
|       <li class="action"> |  | ||||||
|         <ConfirmAction |  | ||||||
|           @buttonText="Remove from {{this.model.identityType}}" |  | ||||||
|           @confirmTitle="Remove this policy?" |  | ||||||
|           @isInDropdown={{true}} |  | ||||||
|           @confirmMessage="This policy may affect permissions to access Vault data." |  | ||||||
|           @onConfirmAction={{action "performTransaction" this.model this.policyName}} |  | ||||||
|     /> |     /> | ||||||
|       </li> |     <dd.Interactive @text="View policy" @route="vault.cluster.policy.show" @models={{array "acl" @policyName}} /> | ||||||
|     </ul> |     <dd.Interactive @text="Edit policy" @route="vault.cluster.policy.edit" @models={{array "acl" @policyName}} /> | ||||||
|   </nav> |     <dd.Interactive | ||||||
| </PopupMenu> |       @text="Remove from {{@model.identityType}}" | ||||||
|  |       @color="critical" | ||||||
|  |       {{on "click" (fn (mut this.showConfirmModal) true)}} | ||||||
|  |     /> | ||||||
|  |   </Hds::Dropdown> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @onClose={{fn (mut this.showConfirmModal) false}} | ||||||
|  |     @onConfirm={{this.removePolicy}} | ||||||
|  |     @confirmTitle="Remove this policy?" | ||||||
|  |     @confirmMessage="This policy may affect permissions to access Vault data." | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -19,30 +19,26 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="level-right is-flex is-paddingless is-marginless"> |     <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|       <div class="level-item"> |       <div class="level-item"> | ||||||
|         <PopupMenu> |         <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|           <nav class="menu" aria-label="navigation for managing login enforcements"> |           <dd.ToggleIcon | ||||||
|             <ul class="menu-list"> |             @icon="more-horizontal" | ||||||
|               <li> |             @text="Manage M-F-A enforcement" | ||||||
|                 <LinkTo |             @hasChevron={{false}} | ||||||
|  |             data-test-popup-menu-trigger | ||||||
|  |           /> | ||||||
|  |           <dd.Interactive | ||||||
|  |             @text="Details" | ||||||
|             @route="vault.cluster.access.mfa.enforcements.enforcement" |             @route="vault.cluster.access.mfa.enforcements.enforcement" | ||||||
|             @model={{@model.name}} |             @model={{@model.name}} | ||||||
|             data-test-list-item-link="details" |             data-test-list-item-link="details" | ||||||
|                 > |           /> | ||||||
|                   Details |           <dd.Interactive | ||||||
|                 </LinkTo> |             @text="Edit" | ||||||
|               </li> |  | ||||||
|               <li> |  | ||||||
|                 <LinkTo |  | ||||||
|             @route="vault.cluster.access.mfa.enforcements.enforcement.edit" |             @route="vault.cluster.access.mfa.enforcements.enforcement.edit" | ||||||
|             @model={{@model.name}} |             @model={{@model.name}} | ||||||
|             data-test-list-item-link="edit" |             data-test-list-item-link="edit" | ||||||
|                 > |           /> | ||||||
|                   Edit |         </Hds::Dropdown> | ||||||
|                 </LinkTo> |  | ||||||
|               </li> |  | ||||||
|             </ul> |  | ||||||
|           </nav> |  | ||||||
|         </PopupMenu> |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|   | |||||||
| @@ -30,30 +30,26 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="level-right is-flex is-paddingless is-marginless"> |     <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|       <div class="level-item"> |       <div class="level-item"> | ||||||
|         <PopupMenu> |         <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|           <nav class="menu" aria-label="navigation for managing MFA methods"> |           <dd.ToggleIcon | ||||||
|             <ul class="menu-list"> |             @icon="more-horizontal" | ||||||
|               <li> |             @text="Manage M-F-A method" | ||||||
|                 <LinkTo |             @hasChevron={{false}} | ||||||
|  |             data-test-popup-menu-trigger | ||||||
|  |           /> | ||||||
|  |           <dd.Interactive | ||||||
|  |             @text="Details" | ||||||
|             @route="vault.cluster.access.mfa.methods.method" |             @route="vault.cluster.access.mfa.methods.method" | ||||||
|             @model={{@model.id}} |             @model={{@model.id}} | ||||||
|             data-test-mfa-method-menu-link="details" |             data-test-mfa-method-menu-link="details" | ||||||
|                 > |           /> | ||||||
|                   Details |           <dd.Interactive | ||||||
|                 </LinkTo> |             @text="Edit" | ||||||
|               </li> |  | ||||||
|               <li> |  | ||||||
|                 <LinkTo |  | ||||||
|             @route="vault.cluster.access.mfa.methods.method.edit" |             @route="vault.cluster.access.mfa.methods.method.edit" | ||||||
|             @model={{@model.id}} |             @model={{@model.id}} | ||||||
|             data-test-mfa-method-menu-link="edit" |             data-test-mfa-method-menu-link="edit" | ||||||
|                 > |           /> | ||||||
|                   Edit |         </Hds::Dropdown> | ||||||
|                 </LinkTo> |  | ||||||
|               </li> |  | ||||||
|             </ul> |  | ||||||
|           </nav> |  | ||||||
|         </PopupMenu> |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
|   | |||||||
| @@ -24,32 +24,32 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="level-right is-flex is-paddingless is-marginless"> |       <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|         <div class="level-item"> |         <div class="level-item"> | ||||||
|           <PopupMenu> |           {{#if (or client.canRead client.canEdit)}} | ||||||
|             <nav class="menu" aria-label="navigation for managing OIDC client {{client.name}}"> |             <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|               <ul class="menu-list"> |               <dd.ToggleIcon | ||||||
|                 <li> |                 @icon="more-horizontal" | ||||||
|                   <LinkTo |                 @text="Application nav options" | ||||||
|  |                 @hasChevron={{false}} | ||||||
|  |                 data-test-popup-menu-trigger | ||||||
|  |               /> | ||||||
|  |               {{#if client.canRead}} | ||||||
|  |                 <dd.Interactive | ||||||
|  |                   @text="Details" | ||||||
|                   @route="vault.cluster.access.oidc.clients.client.details" |                   @route="vault.cluster.access.oidc.clients.client.details" | ||||||
|                   @model={{client.name}} |                   @model={{client.name}} | ||||||
|                     @disabled={{eq client.canRead false}} |  | ||||||
|                   data-test-oidc-client-menu-link="details" |                   data-test-oidc-client-menu-link="details" | ||||||
|                   > |                 /> | ||||||
|                     Details |               {{/if}} | ||||||
|                   </LinkTo> |               {{#if client.canEdit}} | ||||||
|                 </li> |                 <dd.Interactive | ||||||
|                 <li> |                   @text="Edit" | ||||||
|                   <LinkTo |  | ||||||
|                   @route="vault.cluster.access.oidc.clients.client.edit" |                   @route="vault.cluster.access.oidc.clients.client.edit" | ||||||
|                   @model={{client.name}} |                   @model={{client.name}} | ||||||
|                     @disabled={{eq client.canEdit false}} |  | ||||||
|                   data-test-oidc-client-menu-link="edit" |                   data-test-oidc-client-menu-link="edit" | ||||||
|                   > |                 /> | ||||||
|                     Edit |               {{/if}} | ||||||
|                   </LinkTo> |             </Hds::Dropdown> | ||||||
|                 </li> |           {{/if}} | ||||||
|               </ul> |  | ||||||
|             </nav> |  | ||||||
|           </PopupMenu> |  | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -24,32 +24,32 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="level-right is-flex is-paddingless is-marginless"> |       <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|         <div class="level-item"> |         <div class="level-item"> | ||||||
|           <PopupMenu> |           {{#if (or provider.canRead provider.canEdit)}} | ||||||
|             <nav class="menu" aria-label="navigation for managing provider {{provider.name}}"> |             <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|               <ul class="menu-list"> |               <dd.ToggleIcon | ||||||
|                 <li> |                 @icon="more-horizontal" | ||||||
|                   <LinkTo |                 @text="Provider nav options" | ||||||
|  |                 @hasChevron={{false}} | ||||||
|  |                 data-test-popup-menu-trigger | ||||||
|  |               /> | ||||||
|  |               {{#if provider.canRead}} | ||||||
|  |                 <dd.Interactive | ||||||
|  |                   @text="Details" | ||||||
|                   @route="vault.cluster.access.oidc.providers.provider.details" |                   @route="vault.cluster.access.oidc.providers.provider.details" | ||||||
|                   @model={{provider.name}} |                   @model={{provider.name}} | ||||||
|                     @disabled={{eq provider.canRead false}} |  | ||||||
|                   data-test-oidc-provider-menu-link="details" |                   data-test-oidc-provider-menu-link="details" | ||||||
|                   > |                 /> | ||||||
|                     Details |               {{/if}} | ||||||
|                   </LinkTo> |               {{#if provider.canEdit}} | ||||||
|                 </li> |                 <dd.Interactive | ||||||
|                 <li> |                   @text="Edit" | ||||||
|                   <LinkTo |  | ||||||
|                   @route="vault.cluster.access.oidc.providers.provider.edit" |                   @route="vault.cluster.access.oidc.providers.provider.edit" | ||||||
|                   @model={{provider.name}} |                   @model={{provider.name}} | ||||||
|                     @disabled={{not provider.canEdit}} |  | ||||||
|                   data-test-oidc-provider-menu-link="edit" |                   data-test-oidc-provider-menu-link="edit" | ||||||
|                   > |                 /> | ||||||
|                     Edit |               {{/if}} | ||||||
|                   </LinkTo> |             </Hds::Dropdown> | ||||||
|                 </li> |           {{/if}} | ||||||
|               </ul> |  | ||||||
|             </nav> |  | ||||||
|           </PopupMenu> |  | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -23,58 +23,60 @@ | |||||||
|       </LinkTo> |       </LinkTo> | ||||||
|     </div> |     </div> | ||||||
|     <div class="column has-text-right"> |     <div class="column has-text-right"> | ||||||
|       <PopupMenu @name="role-aws-nav" @contentClass="is-wide"> |       <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|         <nav class="menu" aria-label="navigation for managing A-W-S role {{@item.id}}"> |         <dd.ToggleIcon | ||||||
|           <ul class="menu-list"> |           @icon="more-horizontal" | ||||||
|  |           @text="manage A-W-S role {{@item.id}}" | ||||||
|  |           @hasChevron={{false}} | ||||||
|  |           data-test-popup-menu-trigger | ||||||
|  |         /> | ||||||
|         {{#if @item.generatePath.isPending}} |         {{#if @item.generatePath.isPending}} | ||||||
|               <li class="action"> |           <dd.Generic class="has-text-center"> | ||||||
|             <LoadingDropdownOption /> |             <LoadingDropdownOption /> | ||||||
|               </li> |           </dd.Generic> | ||||||
|         {{else if @item.canGenerate}} |         {{else if @item.canGenerate}} | ||||||
|               <li class="action"> |           <dd.Interactive | ||||||
|                 <LinkTo |             @text="Generate credentials" | ||||||
|             @route="vault.cluster.secrets.backend.credentials" |             @route="vault.cluster.secrets.backend.credentials" | ||||||
|             @model={{@item.id}} |             @model={{@item.id}} | ||||||
|             data-test-role-aws-link="generate" |             data-test-role-aws-link="generate" | ||||||
|                 > |           /> | ||||||
|                   Generate credentials |  | ||||||
|                 </LinkTo> |  | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#if @item.updatePath.isPending}} |         {{#if @item.updatePath.isPending}} | ||||||
|               <li class="action"> |           <dd.Generic class="has-text-center"> | ||||||
|             <LoadingDropdownOption /> |             <LoadingDropdownOption /> | ||||||
|               </li> |           </dd.Generic> | ||||||
|               <li class="action"> |  | ||||||
|                 <LoadingDropdownOption /> |  | ||||||
|               </li> |  | ||||||
|         {{else}} |         {{else}} | ||||||
|           {{#if @item.canRead}} |           {{#if @item.canRead}} | ||||||
|                 <li class="action"> |             <dd.Interactive | ||||||
|                   <LinkTo @route="vault.cluster.secrets.backend.show" @model={{@item.id}} data-test-role-ssh-link="show"> |               @text="Details" | ||||||
|                     Details |               @route="vault.cluster.secrets.backend.show" | ||||||
|                   </LinkTo> |               @model={{@item.id}} | ||||||
|                 </li> |               data-test-role-ssh-link="show" | ||||||
|  |             /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|           {{#if @item.canEdit}} |           {{#if @item.canEdit}} | ||||||
|                 <li class="action"> |             <dd.Interactive | ||||||
|                   <LinkTo @route="vault.cluster.secrets.backend.edit" @model={{@item.id}} data-test-role-ssh-link="edit"> |               @text="Edit" | ||||||
|                     Edit |               @route="vault.cluster.secrets.backend.edit" | ||||||
|                   </LinkTo> |               @model={{@item.id}} | ||||||
|                 </li> |               data-test-role-ssh-link="edit" | ||||||
|  |             /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|           {{#if @item.canDelete}} |           {{#if @item.canDelete}} | ||||||
|                 <ConfirmAction |             <dd.Interactive | ||||||
|                   @buttonText="Delete" |               @text="Delete" | ||||||
|                   @isInDropdown={{true}} |               @color="critical" | ||||||
|                   @onConfirmAction={{@delete}} |               {{on "click" (fn (mut this.showConfirmModal) true)}} | ||||||
|               data-test-aws-role-delete={{@item.id}} |               data-test-aws-role-delete={{@item.id}} | ||||||
|             /> |             /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|         {{/if}} |         {{/if}} | ||||||
|           </ul> |       </Hds::Dropdown> | ||||||
|         </nav> |  | ||||||
|       </PopupMenu> |  | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </LinkedBlock> | </LinkedBlock> | ||||||
|  |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal @color="critical" @onClose={{fn (mut this.showConfirmModal) false}} @onConfirm={{@delete}} /> | ||||||
|  | {{/if}} | ||||||
| @@ -26,77 +26,56 @@ | |||||||
|       </LinkTo> |       </LinkTo> | ||||||
|     </div> |     </div> | ||||||
|     <div class="column has-text-right"> |     <div class="column has-text-right"> | ||||||
|       <PopupMenu name="secret-menu"> |       <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|         <nav class="menu" aria-label="navigation for managing database {{@item.id}}"> |         <dd.ToggleIcon | ||||||
|           <ul class="menu-list"> |           @icon="more-horizontal" | ||||||
|  |           @text="Manage database {{@item.id}}" | ||||||
|  |           @hasChevron={{false}} | ||||||
|  |           data-test-popup-menu-trigger | ||||||
|  |         /> | ||||||
|         {{#if @item.canEdit}} |         {{#if @item.canEdit}} | ||||||
|               <li class="action"> |           <dd.Interactive @text="Edit connection" @route="vault.cluster.secrets.backend.edit" @model={{@item.id}} /> | ||||||
|                 <SecretLink @mode="edit" @secret={{@item.id}} class="has-text-black has-text-weight-semibold"> |  | ||||||
|                   Edit connection |  | ||||||
|                 </SecretLink> |  | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#if @item.canEditRole}} |         {{#if @item.canEditRole}} | ||||||
|               <li class="action"> |           <dd.Interactive @text="Edit Role" @route="vault.cluster.secrets.backend.edit" @model={{concat "role/" @item.id}} /> | ||||||
|                 <SecretLink @mode="edit" @secret={{concat "role/" @item.id}} class="has-text-black has-text-weight-semibold"> |  | ||||||
|                   Edit Role |  | ||||||
|                 </SecretLink> |  | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#if @item.canReset}} |         {{#if @item.canReset}} | ||||||
|               <li class="action"> |           <dd.Interactive | ||||||
|                 <Hds::Button |  | ||||||
|             @text="Reset connection" |             @text="Reset connection" | ||||||
|                   @color="secondary" |             @icon={{if (eq this.actionRunning "reset") "loading"}} | ||||||
|                   class="link" |  | ||||||
|             {{on "click" (fn this.resetConnection @item.id)}} |             {{on "click" (fn this.resetConnection @item.id)}} | ||||||
|           /> |           /> | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#if (and (eq @item.type "dynamic") @item.canGenerateCredentials)}} |         {{#if (and (eq @item.type "dynamic") @item.canGenerateCredentials)}} | ||||||
|               <li class="action"> |           <dd.Interactive | ||||||
|                 <LinkTo |             @text="Generate credentials" | ||||||
|             @route="vault.cluster.secrets.backend.credentials" |             @route="vault.cluster.secrets.backend.credentials" | ||||||
|             @model={{@item.id}} |             @model={{@item.id}} | ||||||
|             @query={{hash roleType=this.keyTypeValue}} |             @query={{hash roleType=this.keyTypeValue}} | ||||||
|                 > |           /> | ||||||
|                   Generate credentials |  | ||||||
|                 </LinkTo> |  | ||||||
|               </li> |  | ||||||
|         {{else if (and (eq @item.type "static") @item.canGetCredentials)}} |         {{else if (and (eq @item.type "static") @item.canGetCredentials)}} | ||||||
|               <li class="action"> |           <dd.Interactive | ||||||
|                 <LinkTo |             @text="Get credentials" | ||||||
|             @route="vault.cluster.secrets.backend.credentials" |             @route="vault.cluster.secrets.backend.credentials" | ||||||
|             @model={{@item.id}} |             @model={{@item.id}} | ||||||
|             @query={{hash roleType=this.keyTypeValue}} |             @query={{hash roleType=this.keyTypeValue}} | ||||||
|                 > |           /> | ||||||
|                   Get credentials |  | ||||||
|                 </LinkTo> |  | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#if (and @item.canRotateRoleCredentials (eq this.keyTypeValue "static"))}} |         {{#if (and @item.canRotateRoleCredentials (eq this.keyTypeValue "static"))}} | ||||||
|               <li class="action"> |           <dd.Interactive | ||||||
|                 <Hds::Button |  | ||||||
|             @text="Rotate credentials" |             @text="Rotate credentials" | ||||||
|                   @color="secondary" |             @icon={{if (eq this.actionRunning "rotateRole") "loading"}} | ||||||
|                   class="link" |  | ||||||
|             {{on "click" (fn this.rotateRoleCred @item.id)}} |             {{on "click" (fn this.rotateRoleCred @item.id)}} | ||||||
|           /> |           /> | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|         {{#if @item.canRotateRoot}} |         {{#if @item.canRotateRoot}} | ||||||
|               <li class="action"> |           <dd.Interactive | ||||||
|                 <Hds::Button |  | ||||||
|             @text="Rotate root credentials" |             @text="Rotate root credentials" | ||||||
|                   @color="secondary" |             @icon={{if (eq this.actionRunning "rotateRoot") "loading"}} | ||||||
|                   class="link" |  | ||||||
|             {{on "click" (fn this.rotateRootCred @item.id)}} |             {{on "click" (fn this.rotateRootCred @item.id)}} | ||||||
|           /> |           /> | ||||||
|               </li> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|           </ul> |       </Hds::Dropdown> | ||||||
|         </nav> |  | ||||||
|       </PopupMenu> |  | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </LinkedBlock> | </LinkedBlock> | ||||||
| @@ -30,56 +30,57 @@ | |||||||
|       </SecretLink> |       </SecretLink> | ||||||
|     </div> |     </div> | ||||||
|     <div class="column has-text-right"> |     <div class="column has-text-right"> | ||||||
|       <PopupMenu name="secret-menu"> |       <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|         <nav class="menu" aria-label="navigation for managing {{@item.id}}"> |         <dd.ToggleIcon | ||||||
|           <ul class="menu-list"> |           @icon="more-horizontal" | ||||||
|  |           @text="Manage database {{@item.id}}" | ||||||
|  |           @hasChevron={{false}} | ||||||
|  |           data-test-popup-menu-trigger | ||||||
|  |         /> | ||||||
|         {{#if @item.isFolder}} |         {{#if @item.isFolder}} | ||||||
|               <SecretLink @mode="list" @secret={{@item.id}} class="has-text-black has-text-weight-semibold"> |           <dd.Interactive @text="Contents" @route="vault.cluster.secrets.backend.list" @model={{@item.id}} /> | ||||||
|                 Contents |  | ||||||
|               </SecretLink> |  | ||||||
|         {{else}} |         {{else}} | ||||||
|           {{#if (or @item.versionPath.isLoading @item.secretPath.isLoading)}} |           {{#if (or @item.versionPath.isLoading @item.secretPath.isLoading)}} | ||||||
|                 <li class="action"> |             <dd.Generic class="has-text-center"> | ||||||
|               <LoadingDropdownOption /> |               <LoadingDropdownOption /> | ||||||
|                 </li> |             </dd.Generic> | ||||||
|           {{else}} |           {{else}} | ||||||
|             {{#if @item.canRead}} |             {{#if @item.canRead}} | ||||||
|                   <li class="action"> |               <dd.Interactive | ||||||
|                     <SecretLink |                 @text="Details" | ||||||
|                       @mode="show" |                 @route="vault.cluster.secrets.backend.show" | ||||||
|                       @secret={{@item.id}} |                 @model={{@item.id}} | ||||||
|                       @queryParams={{secret-query-params @backendModel.type @item.type asQueryParams=true}} |                 @query={{secret-query-params @backendModel.type @item.type asQueryParams=true}} | ||||||
|                       class="has-text-black has-text-weight-semibold" |               /> | ||||||
|                     > |  | ||||||
|                       Details |  | ||||||
|                     </SecretLink> |  | ||||||
|                   </li> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|             {{#if @item.canEdit}} |             {{#if @item.canEdit}} | ||||||
|                   <li class="action"> |               <dd.Interactive | ||||||
|                     <SecretLink |                 @text="Edit" | ||||||
|                       @mode="edit" |                 @route="vault.cluster.secrets.backend.edit" | ||||||
|                       @secret={{@item.id}} |                 @model={{@item.id}} | ||||||
|                       @queryParams={{secret-query-params @backendModel.type @item.type asQueryParams=true}} |                 @query={{secret-query-params @backendModel.type @item.type asQueryParams=true}} | ||||||
|                       class="has-text-black has-text-weight-semibold" |               /> | ||||||
|                     > |  | ||||||
|                       Edit |  | ||||||
|                     </SecretLink> |  | ||||||
|                   </li> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|             {{#if @item.canDelete}} |             {{#if @item.canDelete}} | ||||||
|                   <ConfirmAction |               <dd.Interactive | ||||||
|                     @isInDropdown={{true}} |                 @text="Delete" | ||||||
|                     @buttonText="Delete" |                 @color="critical" | ||||||
|                     @confirmMessage="This will permanently delete this secret." |                 data-test-confirm-action-trigger | ||||||
|                     @onConfirmAction={{@delete}} |                 {{on "click" (fn (mut this.showConfirmModal) true)}} | ||||||
|               /> |               /> | ||||||
|             {{/if}} |             {{/if}} | ||||||
|           {{/if}} |           {{/if}} | ||||||
|         {{/if}} |         {{/if}} | ||||||
|           </ul> |       </Hds::Dropdown> | ||||||
|         </nav> |  | ||||||
|       </PopupMenu> |  | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </LinkedBlock> | </LinkedBlock> | ||||||
|  |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @onClose={{fn (mut this.showConfirmModal) false}} | ||||||
|  |     @confirmMessage="This will permanently delete this secret." | ||||||
|  |     @onConfirm={{@delete}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -36,89 +36,86 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="column has-text-right"> |     <div class="column has-text-right"> | ||||||
|       {{#if (eq @backendType "ssh")}} |       {{#if (eq @backendType "ssh")}} | ||||||
|         <PopupMenu @name="role-ssh-nav"> |         <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|           <nav class="menu" aria-label="navigation for managing SSH role {{@item.id}}"> |           <dd.ToggleIcon | ||||||
|             <ul class="menu-list"> |             @icon="more-horizontal" | ||||||
|  |             @text="Manage SSH role {{@item.id}}" | ||||||
|  |             @hasChevron={{false}} | ||||||
|  |             data-test-popup-menu-trigger | ||||||
|  |           /> | ||||||
|           {{#if (eq @item.keyType "otp")}} |           {{#if (eq @item.keyType "otp")}} | ||||||
|             {{#if @item.generatePath.isPending}} |             {{#if @item.generatePath.isPending}} | ||||||
|                   <li class="action"> |               <dd.Generic class="has-text-center"> | ||||||
|                     <Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} /> |                 <LoadingDropdownOption /> | ||||||
|                   </li> |               </dd.Generic> | ||||||
|             {{else if @item.canGenerate}} |             {{else if @item.canGenerate}} | ||||||
|                   <li class="action"> |               <dd.Interactive | ||||||
|                     <LinkTo |                 @text="Generate credentials" | ||||||
|                 @route="vault.cluster.secrets.backend.credentials" |                 @route="vault.cluster.secrets.backend.credentials" | ||||||
|                 @model={{@item.id}} |                 @model={{@item.id}} | ||||||
|                 data-test-role-ssh-link="generate" |                 data-test-role-ssh-link="generate" | ||||||
|                     > |               /> | ||||||
|                       Generate Credentials |  | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|           {{else if (eq @item.keyType "ca")}} |           {{else if (eq @item.keyType "ca")}} | ||||||
|             {{#if @item.signPath.isPending}} |             {{#if @item.signPath.isPending}} | ||||||
|                   <li class="action"> |               <dd.Generic class="has-text-center"> | ||||||
|                     <li class="action"> |                 <LoadingDropdownOption /> | ||||||
|                       <Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} /> |               </dd.Generic> | ||||||
|                     </li> |  | ||||||
|                   </li> |  | ||||||
|             {{else if @item.canGenerate}} |             {{else if @item.canGenerate}} | ||||||
|                   <li class="action"> |               <dd.Interactive | ||||||
|                     <LinkTo |                 @text="Sign Keys" | ||||||
|                 @route="vault.cluster.secrets.backend.sign" |                 @route="vault.cluster.secrets.backend.sign" | ||||||
|                 @model={{@item.id}} |                 @model={{@item.id}} | ||||||
|                 data-test-role-ssh-link="generate" |                 data-test-role-ssh-link="generate" | ||||||
|                     > |  | ||||||
|                       Sign Keys |  | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|                 {{/if}} |  | ||||||
|               {{/if}} |  | ||||||
|               {{#if @item.canEditZeroAddress}} |  | ||||||
|                 <li class="action"> |  | ||||||
|                   <Hds::Button |  | ||||||
|                     disabled={{@loadingToggleZeroAddress}} |  | ||||||
|                     class="link" |  | ||||||
|                     @icon={{if @loadingToggleZeroAddress "loading"}} |  | ||||||
|                     @isIconOnly={{@loadingToggleZeroAddress}} |  | ||||||
|                     {{on "click" @toggleZeroAddress}} |  | ||||||
|                     @text={{if @item.zeroAddress "Disable Zero Address" "Enable Zero Address"}} |  | ||||||
|               /> |               /> | ||||||
|                 </li> |             {{/if}} | ||||||
|  |           {{/if}} | ||||||
|  |           {{#if @loadingToggleZeroAddress}} | ||||||
|  |             <dd.Generic class="has-text-center"> | ||||||
|  |               <LoadingDropdownOption /> | ||||||
|  |             </dd.Generic> | ||||||
|  |           {{else if @item.canEditZeroAddress}} | ||||||
|  |             <dd.Interactive | ||||||
|  |               @text={{if @item.zeroAddress "Disable Zero Address" "Enable Zero Address"}} | ||||||
|  |               {{on "click" @toggleZeroAddress}} | ||||||
|  |             /> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|           {{#if @item.updatePath.isPending}} |           {{#if @item.updatePath.isPending}} | ||||||
|                 <li class="action"> |             <dd.Generic class="has-text-center"> | ||||||
|                   <Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} /> |               <LoadingDropdownOption /> | ||||||
|                   <Hds::Button disabled @color="tertiary" @icon="loading" @text="loading" @isIconOnly={{true}} /> |             </dd.Generic> | ||||||
|                 </li> |  | ||||||
|           {{else}} |           {{else}} | ||||||
|             {{#if @item.canRead}} |             {{#if @item.canRead}} | ||||||
|                   <li class="action"> |               <dd.Interactive | ||||||
|                     <LinkTo @route="vault.cluster.secrets.backend.show" @model={{@item.id}} data-test-role-ssh-link="show"> |                 @text="Details" | ||||||
|                       Details |                 @route="vault.cluster.secrets.backend.show" | ||||||
|                     </LinkTo> |                 @model={{@item.id}} | ||||||
|                   </li> |                 data-test-role-ssh-link="show" | ||||||
|  |               /> | ||||||
|             {{/if}} |             {{/if}} | ||||||
|             {{#if @item.canEdit}} |             {{#if @item.canEdit}} | ||||||
|                   <li class="action"> |               <dd.Interactive | ||||||
|                     <LinkTo @route="vault.cluster.secrets.backend.edit" @model={{@item.id}} data-test-role-ssh-link="edit"> |                 @text="Edit" | ||||||
|                       Edit |                 @route="vault.cluster.secrets.backend.edit" | ||||||
|                     </LinkTo> |                 @model={{@item.id}} | ||||||
|                   </li> |                 data-test-role-ssh-link="edit" | ||||||
|  |               /> | ||||||
|             {{/if}} |             {{/if}} | ||||||
|             {{#if @item.canDelete}} |             {{#if @item.canDelete}} | ||||||
|                   <ConfirmAction |               <dd.Interactive | ||||||
|                     @buttonText="Delete" |                 @text="Delete" | ||||||
|                     @isInDropdown={{true}} |                 @color="critical" | ||||||
|                     @onConfirmAction={{@delete}} |                 {{on "click" (fn (mut this.showConfirmModal) true)}} | ||||||
|                 data-test-ssh-role-delete |                 data-test-ssh-role-delete | ||||||
|               /> |               /> | ||||||
|             {{/if}} |             {{/if}} | ||||||
|           {{/if}} |           {{/if}} | ||||||
|             </ul> |         </Hds::Dropdown> | ||||||
|           </nav> |  | ||||||
|         </PopupMenu> |  | ||||||
|       {{/if}} |       {{/if}} | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </LinkedBlock> | </LinkedBlock> | ||||||
|  |  | ||||||
|  | {{#if this.showConfirmModal}} | ||||||
|  |   <ConfirmModal @color="critical" @onClose={{fn (mut this.showConfirmModal) false}} @onConfirm={{@delete}} /> | ||||||
|  | {{/if}} | ||||||
| @@ -25,26 +25,20 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="column has-text-right"> |       <div class="column has-text-right"> | ||||||
|         {{#if (or @item.updatePath.canRead @item.updatePath.canUpdate)}} |         {{#if (or @item.updatePath.canRead @item.updatePath.canUpdate)}} | ||||||
|           <PopupMenu name="secret-menu"> |           <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|             <nav class="menu" aria-label="navigation for managing transformation item {{@itemPath}}"> |             <dd.ToggleIcon | ||||||
|               <ul class="menu-list"> |               @icon="more-horizontal" | ||||||
|  |               @text="Manage transform {{@itemType}}" | ||||||
|  |               @hasChevron={{false}} | ||||||
|  |               data-test-popup-menu-trigger | ||||||
|  |             /> | ||||||
|             {{#if @item.updatePath.canRead}} |             {{#if @item.updatePath.canRead}} | ||||||
|                   <li class="action"> |               <dd.Interactive @text="Details" @route="vault.cluster.secrets.backend.show" @model={{@itemPath}} /> | ||||||
|                     <SecretLink @mode="show" @secret={{@itemPath}} class="has-text-black has-text-weight-semibold"> |  | ||||||
|                       Details |  | ||||||
|                     </SecretLink> |  | ||||||
|                   </li> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|             {{#if @item.updatePath.canUpdate}} |             {{#if @item.updatePath.canUpdate}} | ||||||
|                   <li class="action"> |               <dd.Interactive @text="Edit" @route="vault.cluster.secrets.backend.edit" @model={{@itemPath}} /> | ||||||
|                     <SecretLink @mode="edit" @secret={{@itemPath}} class="has-text-black has-text-weight-semibold"> |  | ||||||
|                       Edit |  | ||||||
|                     </SecretLink> |  | ||||||
|                   </li> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|               </ul> |           </Hds::Dropdown> | ||||||
|             </nav> |  | ||||||
|           </PopupMenu> |  | ||||||
|         {{/if}} |         {{/if}} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| @@ -55,17 +49,13 @@ | |||||||
|       <div class="column is-12 has-text-grey has-text-weight-semibold"> |       <div class="column is-12 has-text-grey has-text-weight-semibold"> | ||||||
|         <Icon @name="file" class="has-text-grey-light" /> |         <Icon @name="file" class="has-text-grey-light" /> | ||||||
|         {{#if this.isBuiltin}} |         {{#if this.isBuiltin}} | ||||||
|           <ToolTip @verticalPosition="above" @horizontalPosition="left" as |T|> |           <Hds::TooltipButton | ||||||
|             <T.Trigger @tabindex={{false}}> |             @text="This is a built-in HashiCorp {{@itemType}}. It can't be viewed or edited." | ||||||
|  |             @placement="top-start" | ||||||
|  |             aria-label="Why this item cannot be viewed or edited" | ||||||
|  |           > | ||||||
|             {{@item.id}} |             {{@item.id}} | ||||||
|             </T.Trigger> |           </Hds::TooltipButton> | ||||||
|             <T.Content @defaultClass="tool-tip"> |  | ||||||
|               <div class="box"> |  | ||||||
|                 This is a built-in HashiCorp |  | ||||||
|                 {{@itemType}}. It can't be viewed or edited. |  | ||||||
|               </div> |  | ||||||
|             </T.Content> |  | ||||||
|           </ToolTip> |  | ||||||
|         {{else}} |         {{else}} | ||||||
|           {{@item.id}} |           {{@item.id}} | ||||||
|         {{/if}} |         {{/if}} | ||||||
|   | |||||||
| @@ -3,8 +3,6 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
| {{! CBS TODO do not let click if !canRead }} |  | ||||||
| {{#if (eq @options.item "transformation")}} |  | ||||||
| <LinkedBlock | <LinkedBlock | ||||||
|   @params={{array "vault.cluster.secrets.backend.show" @item.id}} |   @params={{array "vault.cluster.secrets.backend.show" @item.id}} | ||||||
|   class="list-item-row" |   class="list-item-row" | ||||||
| @@ -26,43 +24,21 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="column has-text-right"> |     <div class="column has-text-right"> | ||||||
|       {{#if (or @item.updatePath.canRead @item.updatePath.canUpdate)}} |       {{#if (or @item.updatePath.canRead @item.updatePath.canUpdate)}} | ||||||
|           <PopupMenu name="secret-menu" aria-label={{concat "navigation for managing transformation " @item.id}}> |         <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|             <nav class="menu"> |           <dd.ToggleIcon | ||||||
|               <ul class="menu-list"> |             @icon="more-horizontal" | ||||||
|                 {{#if (or @item.versionPath.isLoading @item.secretPath.isLoading)}} |             @text="Manage transformation" | ||||||
|                   <li class="action"> |             @hasChevron={{false}} | ||||||
|                     <LoadingDropdownOption /> |             data-test-popup-menu-trigger | ||||||
|                   </li> |           /> | ||||||
|                 {{else}} |  | ||||||
|           {{#if @item.updatePath.canRead}} |           {{#if @item.updatePath.canRead}} | ||||||
|                     <li class="action"> |             <dd.Interactive @text="Details" @route="vault.cluster.secrets.backend.show" @model={{@item.id}} /> | ||||||
|                       <SecretLink @mode="show" @secret={{@item.id}} class="has-text-black has-text-weight-semibold"> |  | ||||||
|                         Details |  | ||||||
|                       </SecretLink> |  | ||||||
|                     </li> |  | ||||||
|           {{/if}} |           {{/if}} | ||||||
|           {{#if @item.updatePath.canUpdate}} |           {{#if @item.updatePath.canUpdate}} | ||||||
|                     <li class="action"> |             <dd.Interactive @text="Edit" @route="vault.cluster.secrets.backend.edit" @model={{@item.id}} /> | ||||||
|                       <SecretLink @mode="edit" @secret={{@item.id}} class="has-text-black has-text-weight-semibold"> |  | ||||||
|                         Edit |  | ||||||
|                       </SecretLink> |  | ||||||
|                     </li> |  | ||||||
|           {{/if}} |           {{/if}} | ||||||
|                 {{/if}} |         </Hds::Dropdown> | ||||||
|               </ul> |  | ||||||
|             </nav> |  | ||||||
|           </PopupMenu> |  | ||||||
|       {{/if}} |       {{/if}} | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </LinkedBlock> | </LinkedBlock> | ||||||
| {{else}} |  | ||||||
|   <div class="list-item-row"> |  | ||||||
|     <div class="columns is-mobile"> |  | ||||||
|       <div class="column is-12 has-text-grey has-text-weight-semibold"> |  | ||||||
|         <Icon @name="file" class="has-text-grey-light" /> |  | ||||||
|         {{if (eq @item.id " ") "(self)" (or @item.keyWithoutParent @item.id)}} |  | ||||||
|       </div> |  | ||||||
|     </div> |  | ||||||
|   </div> |  | ||||||
| {{/if}} |  | ||||||
| @@ -151,21 +151,15 @@ | |||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|           <div class="column is-1 is-flex-end"> |           <div class="column is-1 is-flex-end"> | ||||||
|             <PopupMenu name="secret-menu"> |             <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|               <nav class="menu" aria-label="copy public key"> |               <dd.ToggleIcon | ||||||
|                 <ul class="menu-list"> |                 @icon="more-horizontal" | ||||||
|                   <li class="action"> |                 @text="Public key options" | ||||||
|                     <Hds::Copy::Button |                 @hasChevron={{false}} | ||||||
|                       @text="Copy Public Key" |                 data-test-popup-menu-trigger | ||||||
|                       @textToCopy={{meta.public_key}} |  | ||||||
|                       @isFullWidth={{true}} |  | ||||||
|                       class="in-dropdown link is-flex-start" |  | ||||||
|                       {{on "click" (action (set-flash-message "Public key copied!"))}} |  | ||||||
|               /> |               /> | ||||||
|                   </li> |               <dd.CopyItem @text={{meta.public_key}} @copyItemTitle="Copy Public Key" /> | ||||||
|                 </ul> |             </Hds::Dropdown> | ||||||
|               </nav> |  | ||||||
|             </PopupMenu> |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -5,15 +5,10 @@ | |||||||
|  |  | ||||||
| <div class="wizard-header"> | <div class="wizard-header"> | ||||||
|   {{#unless this.hidePopup}} |   {{#unless this.hidePopup}} | ||||||
|     <PopupMenu @class="wizard-dismiss-menu"> |     <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|       <nav class="menu" aria-label="navigation for wizard content"> |       <dd.ToggleIcon @icon="more-horizontal" @text="Wizard dismiss menu" @hasChevron={{false}} class="wizard-dismiss-menu" /> | ||||||
|         <ul class="menu-list"> |       <dd.Interactive @text="Dismiss" {{on "click" (action "dismissWizard")}} /> | ||||||
|           <li class="action"> |     </Hds::Dropdown> | ||||||
|             <Hds::Button @text="Dismiss" @color="secondary" class="link" {{on "click" (action "dismissWizard")}} /> |  | ||||||
|           </li> |  | ||||||
|         </ul> |  | ||||||
|       </nav> |  | ||||||
|     </PopupMenu> |  | ||||||
|   {{/unless}} |   {{/unless}} | ||||||
|   <h1 class="title is-5"> |   <h1 class="title is-5"> | ||||||
|     <Icon @name={{this.glyph}} /> |     <Icon @name={{this.glyph}} /> | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ | |||||||
|           </span> |           </span> | ||||||
|         </div> |         </div> | ||||||
|         <div class="column has-text-right"> |         <div class="column has-text-right"> | ||||||
|           <Identity::PopupAlias @params={{array item}} @onSuccess={{action "onDelete"}} /> |           <Identity::PopupAlias @item={{item}} @onSuccess={{action "onDelete"}} /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </LinkedBlock> |     </LinkedBlock> | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
|     <LinkedBlock |     <LinkedBlock | ||||||
|       @params={{array "vault.cluster.access.identity.show" item.id "details"}} |       @params={{array "vault.cluster.access.identity.show" item.id "details"}} | ||||||
|       class="list-item-row" |       class="list-item-row" | ||||||
|       data-test-identity-row |       data-test-identity-row={{item.name}} | ||||||
|     > |     > | ||||||
|       <div class="columns is-mobile"> |       <div class="columns is-mobile"> | ||||||
|         <div class="column is-7-tablet is-10-mobile"> |         <div class="column is-7-tablet is-10-mobile"> | ||||||
| @@ -32,63 +32,53 @@ | |||||||
|           {{/if}} |           {{/if}} | ||||||
|         </div> |         </div> | ||||||
|         <div class="column has-text-right"> |         <div class="column has-text-right"> | ||||||
|           <PopupMenu @name="identity-item" @onOpen={{action "reloadRecord" item}}> |           <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|             <nav class="menu" aria-label="navigation for managing identity"> |             <dd.ToggleIcon | ||||||
|               <ul class="menu-list"> |               @icon="more-horizontal" | ||||||
|                 <li class="action"> |               @text="Identity management options" | ||||||
|                   <LinkTo @route="vault.cluster.access.identity.show" @models={{array item.id "details"}}> |               @hasChevron={{false}} | ||||||
|                     Details |               {{on "click" (action "reloadRecord" item)}} | ||||||
|                   </LinkTo> |               data-test-popup-menu-trigger | ||||||
|                 </li> |             /> | ||||||
|  |             <dd.Interactive | ||||||
|  |               @text="Details" | ||||||
|  |               @route="vault.cluster.access.identity.show" | ||||||
|  |               @models={{array item.id "details"}} | ||||||
|  |             /> | ||||||
|             {{#if (or item.isReloading item.updatePath.isPending item.aliasPath.isPending)}} |             {{#if (or item.isReloading item.updatePath.isPending item.aliasPath.isPending)}} | ||||||
|                   <li class="action"> |               <dd.Generic class="has-text-center"> | ||||||
|                 <LoadingDropdownOption /> |                 <LoadingDropdownOption /> | ||||||
|                   </li> |               </dd.Generic> | ||||||
|             {{else}} |             {{else}} | ||||||
|               {{#if item.canAddAlias}} |               {{#if item.canAddAlias}} | ||||||
|                     <li class="action"> |                 {{! entities can always add aliases, internal groups cannot have any and external groups can only have one }} | ||||||
|                       <LinkTo |                 {{#if (or (eq this.identityType "entity") (and (eq item.type "external") (not item.alias)))}} | ||||||
|  |                   <dd.Interactive | ||||||
|  |                     data-test-popup-menu="create alias" | ||||||
|  |                     @text="Create alias" | ||||||
|                     @route="vault.cluster.access.identity.aliases.add" |                     @route="vault.cluster.access.identity.aliases.add" | ||||||
|                     @models={{array (pluralize this.identityType) item.id}} |                     @models={{array (pluralize this.identityType) item.id}} | ||||||
|                       > |                   /> | ||||||
|                         Create alias |                 {{/if}} | ||||||
|                       </LinkTo> |  | ||||||
|                     </li> |  | ||||||
|               {{/if}} |               {{/if}} | ||||||
|               {{#if item.canEdit}} |               {{#if item.canEdit}} | ||||||
|                     <li class="action"> |                 <dd.Interactive @text="Edit" @route="vault.cluster.access.identity.edit" @model={{item.id}} /> | ||||||
|                       <LinkTo @route="vault.cluster.access.identity.edit" @model={{item.id}}> |  | ||||||
|                         Edit |  | ||||||
|                       </LinkTo> |  | ||||||
|                     </li> |  | ||||||
|                     <li class="action"> |  | ||||||
|                 {{#if item.disabled}} |                 {{#if item.disabled}} | ||||||
|                         <Hds::Button @text="Enable" {{on "click" (action "toggleDisabled" item)}} class="link" /> |                   <dd.Interactive @text="Enable" {{on "click" (action "toggleDisabled" item)}} /> | ||||||
|                 {{else if (eq this.identityType "entity")}} |                 {{else if (eq this.identityType "entity")}} | ||||||
|                         <ConfirmAction |                   <dd.Interactive @text="Disable" @color="critical" {{on "click" (fn (mut this.entityToDisable) item)}} /> | ||||||
|                           @isInDropdown={{true}} |  | ||||||
|                           @buttonText="Disable" |  | ||||||
|                           @confirmMessage="Associated tokens will not be revoked, but cannot be used" |  | ||||||
|                           @confirmTitle="Disable this entity?" |  | ||||||
|                           @onConfirmAction={{action "toggleDisabled" item}} |  | ||||||
|                           @modalColor="warning" |  | ||||||
|                         /> |  | ||||||
|                 {{/if}} |                 {{/if}} | ||||||
|                     </li> |  | ||||||
|               {{/if}} |               {{/if}} | ||||||
|               {{#if item.canDelete}} |               {{#if item.canDelete}} | ||||||
|                     <ConfirmAction |                 <dd.Interactive | ||||||
|                       @isInDropdown={{true}} |                   @text="Delete" | ||||||
|                       @buttonText="Delete" |                   @color="critical" | ||||||
|                       @onConfirmAction={{action "delete" item}} |                   {{on "click" (fn (mut this.itemToDelete) item)}} | ||||||
|                       @confirmTitle="Delete this {{this.identityType}}?" |                   data-test-popup-menu="delete" | ||||||
|                       data-test-item-delete |  | ||||||
|                 /> |                 /> | ||||||
|               {{/if}} |               {{/if}} | ||||||
|             {{/if}} |             {{/if}} | ||||||
|               </ul> |           </Hds::Dropdown> | ||||||
|             </nav> |  | ||||||
|           </PopupMenu> |  | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </LinkedBlock> |     </LinkedBlock> | ||||||
| @@ -118,3 +108,21 @@ | |||||||
|     /> |     /> | ||||||
|   </EmptyState> |   </EmptyState> | ||||||
| {{/if}} | {{/if}} | ||||||
|  |  | ||||||
|  | {{#if this.entityToDisable}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @confirmMessage="Associated tokens will not be revoked, but cannot be used." | ||||||
|  |     @confirmTitle="Disable this entity?" | ||||||
|  |     @onConfirm={{action "toggleDisabled" this.entityToDisable}} | ||||||
|  |     @onClose={{fn (mut this.entityToDisable) null}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
|  |  | ||||||
|  | {{#if this.itemToDelete}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @confirmTitle="Delete this {{this.identityType}}?" | ||||||
|  |     @onConfirm={{action "delete" this.itemToDelete}} | ||||||
|  |     @onClose={{fn (mut this.itemToDelete) null}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -83,17 +83,20 @@ | |||||||
|           {{#if target.link}} |           {{#if target.link}} | ||||||
|             <div class="level-right is-flex is-paddingless is-marginless"> |             <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|               <div class="level-item"> |               <div class="level-item"> | ||||||
|                 <PopupMenu> |                 <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                   <nav class="menu" aria-label="Enforcement target more menu"> |                   <dd.ToggleIcon | ||||||
|                     <ul class="menu-list"> |                     @icon="more-horizontal" | ||||||
|                       <li> |                     @text="Manage enforcement target" | ||||||
|                         <LinkTo @route={{target.link}} @models={{target.linkModels}} data-test-target-link={{target.title}}> |                     @hasChevron={{false}} | ||||||
|                           Details |                     data-test-popup-menu-trigger | ||||||
|                         </LinkTo> |                   /> | ||||||
|                       </li> |                   <dd.Interactive | ||||||
|                     </ul> |                     @text="Details" | ||||||
|                   </nav> |                     @route={{target.link}} | ||||||
|                 </PopupMenu> |                     @models={{target.linkModels}} | ||||||
|  |                     data-test-target-link={{target.title}} | ||||||
|  |                   /> | ||||||
|  |                 </Hds::Dropdown> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|           {{/if}} |           {{/if}} | ||||||
|   | |||||||
| @@ -38,32 +38,28 @@ | |||||||
|       {{#if (not-eq model.name "allow_all")}} |       {{#if (not-eq model.name "allow_all")}} | ||||||
|         <div class="level-right is-flex is-paddingless is-marginless"> |         <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|           <div class="level-item"> |           <div class="level-item"> | ||||||
|             <PopupMenu> |             <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|               <nav class="menu"> |               <dd.ToggleIcon | ||||||
|                 <ul class="menu-list"> |                 @icon="more-horizontal" | ||||||
|                   <li> |                 @text="Assignment nav options" | ||||||
|                     <LinkTo |                 @hasChevron={{false}} | ||||||
|  |                 data-test-popup-menu-trigger | ||||||
|  |               /> | ||||||
|  |               <dd.Interactive | ||||||
|  |                 @text="Details" | ||||||
|                 @route="vault.cluster.access.oidc.assignments.assignment.details" |                 @route="vault.cluster.access.oidc.assignments.assignment.details" | ||||||
|                 @model={{model.name}} |                 @model={{model.name}} | ||||||
|                 @disabled={{eq model.canRead false}} |                 @disabled={{eq model.canRead false}} | ||||||
|                 data-test-oidc-assignment-menu-link="details" |                 data-test-oidc-assignment-menu-link="details" | ||||||
|                     > |               /> | ||||||
|                       Details |               <dd.Interactive | ||||||
|                     </LinkTo> |                 @text="Edit" | ||||||
|                   </li> |  | ||||||
|                   <li> |  | ||||||
|                     <LinkTo |  | ||||||
|                 @route="vault.cluster.access.oidc.assignments.assignment.edit" |                 @route="vault.cluster.access.oidc.assignments.assignment.edit" | ||||||
|                 @model={{model.name}} |                 @model={{model.name}} | ||||||
|                 @disabled={{eq model.canEdit false}} |                 @disabled={{eq model.canEdit false}} | ||||||
|                 data-test-oidc-assignment-menu-link="edit" |                 data-test-oidc-assignment-menu-link="edit" | ||||||
|                     > |               /> | ||||||
|                       Edit |             </Hds::Dropdown> | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|                 </ul> |  | ||||||
|               </nav> |  | ||||||
|             </PopupMenu> |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       {{/if}} |       {{/if}} | ||||||
|   | |||||||
| @@ -28,32 +28,28 @@ | |||||||
|       </div> |       </div> | ||||||
|       <div class="level-right is-flex is-paddingless is-marginless"> |       <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|         <div class="level-item"> |         <div class="level-item"> | ||||||
|           <PopupMenu> |           <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|             <nav class="menu"> |             <dd.ToggleIcon | ||||||
|               <ul class="menu-list"> |               @icon="more-horizontal" | ||||||
|                 <li> |               @text="Key nav options" | ||||||
|                   <LinkTo |               @hasChevron={{false}} | ||||||
|  |               data-test-popup-menu-trigger | ||||||
|  |             /> | ||||||
|  |             <dd.Interactive | ||||||
|  |               @text="Details" | ||||||
|               @route="vault.cluster.access.oidc.keys.key.details" |               @route="vault.cluster.access.oidc.keys.key.details" | ||||||
|               @model={{model.name}} |               @model={{model.name}} | ||||||
|               @disabled={{eq model.canRead false}} |               @disabled={{eq model.canRead false}} | ||||||
|               data-test-oidc-key-menu-link="details" |               data-test-oidc-key-menu-link="details" | ||||||
|                   > |             /> | ||||||
|                     Details |             <dd.Interactive | ||||||
|                   </LinkTo> |               @text="Edit" | ||||||
|                 </li> |  | ||||||
|                 <li> |  | ||||||
|                   <LinkTo |  | ||||||
|               @route="vault.cluster.access.oidc.keys.key.edit" |               @route="vault.cluster.access.oidc.keys.key.edit" | ||||||
|               @model={{model.name}} |               @model={{model.name}} | ||||||
|               @disabled={{eq model.canEdit false}} |               @disabled={{eq model.canEdit false}} | ||||||
|               data-test-oidc-key-menu-link="edit" |               data-test-oidc-key-menu-link="edit" | ||||||
|                   > |             /> | ||||||
|                     Edit |           </Hds::Dropdown> | ||||||
|                   </LinkTo> |  | ||||||
|                 </li> |  | ||||||
|               </ul> |  | ||||||
|             </nav> |  | ||||||
|           </PopupMenu> |  | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -29,32 +29,28 @@ | |||||||
|         </div> |         </div> | ||||||
|         <div class="level-right is-flex is-paddingless is-marginless"> |         <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|           <div class="level-item"> |           <div class="level-item"> | ||||||
|             <PopupMenu> |             <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|               <nav class="menu"> |               <dd.ToggleIcon | ||||||
|                 <ul class="menu-list"> |                 @icon="more-horizontal" | ||||||
|                   <li> |                 @text="Assignment nav options" | ||||||
|                     <LinkTo |                 @hasChevron={{false}} | ||||||
|  |                 data-test-popup-menu-trigger | ||||||
|  |               /> | ||||||
|  |               <dd.Interactive | ||||||
|  |                 @text="Details" | ||||||
|                 @route="vault.cluster.access.oidc.scopes.scope.details" |                 @route="vault.cluster.access.oidc.scopes.scope.details" | ||||||
|                 @model={{model.name}} |                 @model={{model.name}} | ||||||
|                 @disabled={{eq model.canRead false}} |                 @disabled={{eq model.canRead false}} | ||||||
|                 data-test-oidc-scope-menu-link="details" |                 data-test-oidc-scope-menu-link="details" | ||||||
|                     > |               /> | ||||||
|                       Details |               <dd.Interactive | ||||||
|                     </LinkTo> |                 @text="Edit" | ||||||
|                   </li> |  | ||||||
|                   <li> |  | ||||||
|                     <LinkTo |  | ||||||
|                 @route="vault.cluster.access.oidc.scopes.scope.edit" |                 @route="vault.cluster.access.oidc.scopes.scope.edit" | ||||||
|                 @model={{model.name}} |                 @model={{model.name}} | ||||||
|                 @disabled={{eq model.canEdit false}} |                 @disabled={{eq model.canEdit false}} | ||||||
|                 data-test-oidc-scope-menu-link="edit" |                 data-test-oidc-scope-menu-link="edit" | ||||||
|                     > |               /> | ||||||
|                       Edit |             </Hds::Dropdown> | ||||||
|                     </LinkTo> |  | ||||||
|                   </li> |  | ||||||
|                 </ul> |  | ||||||
|               </nav> |  | ||||||
|             </PopupMenu> |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -91,52 +91,44 @@ | |||||||
|               </LinkTo> |               </LinkTo> | ||||||
|             </div> |             </div> | ||||||
|             <div class="column has-text-right"> |             <div class="column has-text-right"> | ||||||
|               <PopupMenu name="policy-nav"> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <nav class="menu"> |                 <dd.ToggleIcon | ||||||
|                   <ul class="menu-list"> |                   @icon="more-horizontal" | ||||||
|  |                   @text="Policy nav menu" | ||||||
|  |                   @hasChevron={{false}} | ||||||
|  |                   data-test-popup-menu-trigger | ||||||
|  |                 /> | ||||||
|                 {{#if item.updatePath.isPending}} |                 {{#if item.updatePath.isPending}} | ||||||
|                       <li class="action"> |                   <dd.Generic class="has-text-center"> | ||||||
|                     <LoadingDropdownOption /> |                     <LoadingDropdownOption /> | ||||||
|                       </li> |                   </dd.Generic> | ||||||
|                       <li class="action"> |  | ||||||
|                         <LoadingDropdownOption /> |  | ||||||
|                       </li> |  | ||||||
|                 {{else}} |                 {{else}} | ||||||
|                   {{#if item.canRead}} |                   {{#if item.canRead}} | ||||||
|                         <li class="action"> |                     <dd.Interactive | ||||||
|                           <LinkTo |                       @text="Details" | ||||||
|                       @route="vault.cluster.policy.show" |                       @route="vault.cluster.policy.show" | ||||||
|                       @models={{array this.policyType item.id}} |                       @models={{array this.policyType item.id}} | ||||||
|                       data-test-policy-link="show" |                       data-test-policy-link="show" | ||||||
|                           > |                     /> | ||||||
|                             Details |  | ||||||
|                           </LinkTo> |  | ||||||
|                         </li> |  | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                   {{#if item.canEdit}} |                   {{#if item.canEdit}} | ||||||
|                         <li class="action"> |                     <dd.Interactive | ||||||
|                           <LinkTo |                       @text="Edit" | ||||||
|                       @route="vault.cluster.policy.edit" |                       @route="vault.cluster.policy.edit" | ||||||
|                       @models={{array this.policyType item.id}} |                       @models={{array this.policyType item.id}} | ||||||
|                       data-test-policy-link="edit" |                       data-test-policy-link="edit" | ||||||
|                           > |                     /> | ||||||
|                             Edit |  | ||||||
|                           </LinkTo> |  | ||||||
|                         </li> |  | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                       {{#if item.canDelete}} |                   {{#if (and item.canDelete (not-eq item.name "default"))}} | ||||||
|                         <ConfirmAction |                     <dd.Interactive | ||||||
|                           @isInDropdown={{true}} |                       @text="Delete" | ||||||
|                           @buttonText="Delete" |                       @color="critical" | ||||||
|                           @confirmTitle="Delete this policy?" |                       data-test-confirm-action-trigger | ||||||
|                           @confirmMessage="This will permanently delete this policy and may affect access to some data" |                       {{on "click" (fn (mut this.policyToDelete) item)}} | ||||||
|                           @onConfirmAction={{action "deletePolicy" item}} |  | ||||||
|                     /> |                     /> | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                 {{/if}} |                 {{/if}} | ||||||
|                   </ul> |               </Hds::Dropdown> | ||||||
|                 </nav> |  | ||||||
|               </PopupMenu> |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </LinkedBlock> |         </LinkedBlock> | ||||||
| @@ -181,3 +173,13 @@ | |||||||
| {{else}} | {{else}} | ||||||
|   <UpgradePage @title="Sentinel" @minimumEdition="Vault Enterprise Premium" /> |   <UpgradePage @title="Sentinel" @minimumEdition="Vault Enterprise Premium" /> | ||||||
| {{/if}} | {{/if}} | ||||||
|  |  | ||||||
|  | {{#if this.policyToDelete}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @confirmTitle="Delete this policy?" | ||||||
|  |     @confirmMessage="This will permanently delete this policy and may affect access to some data." | ||||||
|  |     @onClose={{fn (mut this.policyToDelete) null}} | ||||||
|  |     @onConfirm={{action "deletePolicy" this.policyToDelete}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -86,28 +86,39 @@ | |||||||
|         </ReadMore> |         </ReadMore> | ||||||
|       {{/if}} |       {{/if}} | ||||||
|     </div> |     </div> | ||||||
|     {{! meatball sandwich menu }} |  | ||||||
|     <div class="linked-block-popup-menu"> |     <div class="linked-block-popup-menu"> | ||||||
|       <PopupMenu @name="engine-menu"> |       <Hds::Dropdown @isInline={{true}} as |dd|> | ||||||
|         <nav class="menu" aria-label="{{if backend.isSupportedBackend 'supported' 'unsupported'}} secrets engine menu"> |         <dd.ToggleIcon | ||||||
|           <ul class="menu-list"> |           @icon="more-horizontal" | ||||||
|             <li class="action"> |           @text="{{if backend.isSupportedBackend 'supported' 'unsupported'}} secrets engine menu" | ||||||
|               <LinkTo @route="vault.cluster.secrets.backend.configuration" @model={{backend.id}} data-test-engine-config> |           @hasChevron={{false}} | ||||||
|                 View configuration |           data-test-popup-menu-trigger | ||||||
|               </LinkTo> |         /> | ||||||
|             </li> |         <dd.Interactive | ||||||
|  |           @text="View configuration" | ||||||
|  |           @route="vault.cluster.secrets.backend.configuration" | ||||||
|  |           @model={{backend.id}} | ||||||
|  |           data-test-engine-config | ||||||
|  |         /> | ||||||
|         {{#if (not-eq backend.type "cubbyhole")}} |         {{#if (not-eq backend.type "cubbyhole")}} | ||||||
|               <ConfirmAction |           <dd.Interactive | ||||||
|                 @isInDropdown={{true}} |             @text="Disable" | ||||||
|                 @confirmMessage="Any data in this engine will be permanently deleted." |             @color="critical" | ||||||
|                 @confirmTitle="Disable engine?" |             {{on "click" (fn (mut this.engineToDisable) backend)}} | ||||||
|                 @buttonText="Disable" |             data-test-confirm-action-trigger | ||||||
|                 @onConfirmAction={{perform this.disableEngine backend}} |  | ||||||
|           /> |           /> | ||||||
|         {{/if}} |         {{/if}} | ||||||
|           </ul> |       </Hds::Dropdown> | ||||||
|         </nav> |  | ||||||
|       </PopupMenu> |  | ||||||
|     </div> |     </div> | ||||||
|   </LinkedBlock> |   </LinkedBlock> | ||||||
| {{/each}} | {{/each}} | ||||||
|  |  | ||||||
|  | {{#if this.engineToDisable}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @confirmMessage="Any data in this engine will be permanently deleted." | ||||||
|  |     @confirmTitle="Disable engine?" | ||||||
|  |     @onClose={{fn (mut this.engineToDisable) null}} | ||||||
|  |     @onConfirm={{perform this.disableEngine this.engineToDisable}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -56,28 +56,22 @@ | |||||||
|         </div> |         </div> | ||||||
|         <div class="level-right is-flex is-paddingless is-marginless"> |         <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|           <div class="level-item"> |           <div class="level-item"> | ||||||
|             <PopupMenu @name="engine-menu"> |             {{#if (or message.canEditCustomMessages message.canDeleteCustomMessages)}} | ||||||
|               <nav class="menu"> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <ul class="menu-list"> |                 <dd.ToggleIcon | ||||||
|  |                   @icon="more-horizontal" | ||||||
|  |                   @text="Message popup menu" | ||||||
|  |                   @hasChevron={{false}} | ||||||
|  |                   data-test-popup-menu-trigger | ||||||
|  |                 /> | ||||||
|                 {{#if message.canEditCustomMessages}} |                 {{#if message.canEditCustomMessages}} | ||||||
|                     <li class="action"> |                   <dd.Interactive @text="Edit" @route="messages.message.edit" @model={{message.id}} /> | ||||||
|                       <LinkTo @route="messages.message.edit" @model={{message.id}}> |  | ||||||
|                         Edit |  | ||||||
|                       </LinkTo> |  | ||||||
|                     </li> |  | ||||||
|                 {{/if}} |                 {{/if}} | ||||||
|                 {{#if message.canDeleteCustomMessages}} |                 {{#if message.canDeleteCustomMessages}} | ||||||
|                     <ConfirmAction |                   <dd.Interactive @text="Disable" @color="critical" {{on "click" (fn (mut this.messageToDelete) message)}} /> | ||||||
|                       @isInDropdown={{true}} |                 {{/if}} | ||||||
|                       @buttonText="Delete" |               </Hds::Dropdown> | ||||||
|                       @confirmTitle="Are you sure?" |  | ||||||
|                       @confirmMessage="This will delete this message permanently. You cannot undo this action." |  | ||||||
|                       @onConfirmAction={{perform this.deleteMessage message}} |  | ||||||
|                     /> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|                 </ul> |  | ||||||
|               </nav> |  | ||||||
|             </PopupMenu> |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
| @@ -118,3 +112,12 @@ | |||||||
|     </M.Footer> |     </M.Footer> | ||||||
|   </Hds::Modal> |   </Hds::Modal> | ||||||
| {{/if}} | {{/if}} | ||||||
|  |  | ||||||
|  | {{#if this.messageToDelete}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @confirmMessage="This will delete this message permanently. You cannot undo this action." | ||||||
|  |     @onClose={{fn (mut this.messageToDelete) null}} | ||||||
|  |     @onConfirm={{perform this.deleteMessage this.messageToDelete}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -30,6 +30,7 @@ export default class MessagesList extends Component { | |||||||
|   @service customMessages; |   @service customMessages; | ||||||
|  |  | ||||||
|   @tracked showMaxMessageModal = false; |   @tracked showMaxMessageModal = false; | ||||||
|  |   @tracked messageToDelete = null; | ||||||
|  |  | ||||||
|   // This follows the pattern in sync/addon/components/secrets/page/destinations for FilterInput. |   // This follows the pattern in sync/addon/components/secrets/page/destinations for FilterInput. | ||||||
|   // Currently, FilterInput doesn't do a full page refresh causing it to lose focus. |   // Currently, FilterInput doesn't do a full page refresh causing it to lose focus. | ||||||
| @@ -110,6 +111,8 @@ export default class MessagesList extends Component { | |||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       const message = errorMessage(e); |       const message = errorMessage(e); | ||||||
|       this.flashMessages.danger(message); |       this.flashMessages.danger(message); | ||||||
|  |     } finally { | ||||||
|  |       this.messageToDelete = null; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,44 +25,13 @@ | |||||||
| {{/if}} | {{/if}} | ||||||
|  |  | ||||||
| {{#if this.showConfirmModal}} | {{#if this.showConfirmModal}} | ||||||
|   <Hds::Modal |   <ConfirmModal | ||||||
|     id="confirm-action-modal" |  | ||||||
|     class="has-text-left" |  | ||||||
|     @color={{this.modalColor}} |     @color={{this.modalColor}} | ||||||
|     @size="small" |  | ||||||
|     @onClose={{fn (mut this.showConfirmModal) false}} |     @onClose={{fn (mut this.showConfirmModal) false}} | ||||||
|     as |M| |     @onConfirm={{this.onConfirm}} | ||||||
|   > |     @confirmTitle={{@confirmTitle}} | ||||||
|     {{#if @disabledMessage}} |     @confirmMessage={{this.confirmMessage}} | ||||||
|       <M.Header data-test-confirm-action-title @icon="x-circle"> |     @disabledMessage={{@disabledMessage}} | ||||||
|         Not allowed |     @isRunning={{@isRunning}} | ||||||
|       </M.Header> |  | ||||||
|       <M.Body data-test-confirm-action-message> |  | ||||||
|         {{@disabledMessage}} |  | ||||||
|       </M.Body> |  | ||||||
|       <M.Footer as |F|> |  | ||||||
|         <Hds::Button data-test-confirm-cancel-button @text="Close" {{on "click" F.close}} /> |  | ||||||
|       </M.Footer> |  | ||||||
|     {{else}} |  | ||||||
|       <M.Header data-test-confirm-action-title @icon="alert-circle"> |  | ||||||
|         {{or @confirmTitle "Are you sure?"}} |  | ||||||
|       </M.Header> |  | ||||||
|       <M.Body data-test-confirm-action-message> |  | ||||||
|         {{this.confirmMessage}} |  | ||||||
|       </M.Body> |  | ||||||
|       <M.Footer as |F|> |  | ||||||
|         <Hds::ButtonSet> |  | ||||||
|           <Hds::Button |  | ||||||
|             data-test-confirm-button |  | ||||||
|             disabled={{@isRunning}} |  | ||||||
|             @icon={{if @isRunning "loading"}} |  | ||||||
|             @color={{if (eq this.modalColor "critical") "critical" "primary"}} |  | ||||||
|             @text="Confirm" |  | ||||||
|             {{on "click" this.onConfirm}} |  | ||||||
|   /> |   /> | ||||||
|           <Hds::Button data-test-confirm-cancel-button @color="secondary" @text="Cancel" {{on "click" F.close}} /> |  | ||||||
|         </Hds::ButtonSet> |  | ||||||
|       </M.Footer> |  | ||||||
|     {{/if}} |  | ||||||
|   </Hds::Modal> |  | ||||||
| {{/if}} | {{/if}} | ||||||
							
								
								
									
										51
									
								
								ui/lib/core/addon/components/confirm-modal.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								ui/lib/core/addon/components/confirm-modal.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | {{! | ||||||
|  |   Copyright (c) HashiCorp, Inc. | ||||||
|  |   SPDX-License-Identifier: BUSL-1.1 | ||||||
|  | ~}} | ||||||
|  |  | ||||||
|  | {{! Replaces ConfirmAction in dropdowns, instead use dd.Interactive + this modal }} | ||||||
|  | {{! Destructive action confirmation modal that asks "Are you sure?" or similar @confirmTitle }} | ||||||
|  | {{! If a tracked property is used to pass the list item to the destructive action,  }} | ||||||
|  | {{! remember to reset item to null via the @onClose action }} | ||||||
|  |  | ||||||
|  | <Hds::Modal | ||||||
|  |   id="confirm-action-modal" | ||||||
|  |   class="has-text-left" | ||||||
|  |   @color={{or @color "warning"}} | ||||||
|  |   @size="small" | ||||||
|  |   @onClose={{@onClose}} | ||||||
|  |   data-test-confirm-modal | ||||||
|  |   as |M| | ||||||
|  | > | ||||||
|  |   {{#if @disabledMessage}} | ||||||
|  |     <M.Header data-test-confirm-action-title @icon="x-circle"> | ||||||
|  |       Not allowed | ||||||
|  |     </M.Header> | ||||||
|  |     <M.Body data-test-confirm-action-message> | ||||||
|  |       {{@disabledMessage}} | ||||||
|  |     </M.Body> | ||||||
|  |     <M.Footer as |F|> | ||||||
|  |       <Hds::Button data-test-confirm-cancel-button @text="Close" {{on "click" F.close}} /> | ||||||
|  |     </M.Footer> | ||||||
|  |   {{else}} | ||||||
|  |     <M.Header data-test-confirm-action-title @icon="alert-circle"> | ||||||
|  |       {{or @confirmTitle "Are you sure?"}} | ||||||
|  |     </M.Header> | ||||||
|  |     <M.Body data-test-confirm-action-message> | ||||||
|  |       {{or @confirmMessage "You will not be able to recover it later."}} | ||||||
|  |     </M.Body> | ||||||
|  |     <M.Footer as |F|> | ||||||
|  |       <Hds::ButtonSet> | ||||||
|  |         <Hds::Button | ||||||
|  |           data-test-confirm-button | ||||||
|  |           disabled={{@isRunning}} | ||||||
|  |           @icon={{if @isRunning "loading"}} | ||||||
|  |           @color={{if (eq @color "critical") "critical" "primary"}} | ||||||
|  |           @text="Confirm" | ||||||
|  |           {{on "click" @onConfirm}} | ||||||
|  |         /> | ||||||
|  |         <Hds::Button data-test-confirm-cancel-button @color="secondary" @text="Cancel" {{on "click" F.close}} /> | ||||||
|  |       </Hds::ButtonSet> | ||||||
|  |     </M.Footer> | ||||||
|  |   {{/if}} | ||||||
|  | </Hds::Modal> | ||||||
| @@ -5,7 +5,8 @@ | |||||||
| import Component from '@glimmer/component'; | import Component from '@glimmer/component'; | ||||||
| /** | /** | ||||||
|  * @module ConfirmationModal |  * @module ConfirmationModal | ||||||
|  * ConfirmationModal components wrap the <Hds::Modal> component to present a critical (red) type-to-confirm modal. |  * ConfirmationModal components wrap the <Hds::Modal> component to present a critical (red) type-to-confirm modal | ||||||
|  |  * which require the user to type something to confirm the action. | ||||||
|  * They are used for extremely destructive actions that require extra consideration before confirming. |  * They are used for extremely destructive actions that require extra consideration before confirming. | ||||||
|  * |  * | ||||||
|  * @example |  * @example | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								ui/lib/core/app/components/confirm-modal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ui/lib/core/app/components/confirm-modal.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | export { default } from 'core/components/confirm-modal'; | ||||||
| @@ -71,57 +71,55 @@ | |||||||
|           </div> |           </div> | ||||||
|           <div class="level-right is-flex is-paddingless is-marginless"> |           <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|             <div class="level-item"> |             <div class="level-item"> | ||||||
|               <PopupMenu> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <nav class="menu" aria-label="menu items for managing {{metadata.path}}"> |                 <dd.ToggleIcon | ||||||
|                   <ul class="menu-list"> |                   @icon="more-horizontal" | ||||||
|  |                   @text="Manage secret" | ||||||
|  |                   @hasChevron={{false}} | ||||||
|  |                   data-test-popup-menu-trigger | ||||||
|  |                 /> | ||||||
|                 {{#if metadata.pathIsDirectory}} |                 {{#if metadata.pathIsDirectory}} | ||||||
|                       <li> |                   <dd.Interactive @text="Content" @route="list-directory" @model={{metadata.fullSecretPath}} /> | ||||||
|                         <LinkTo @route="list-directory" @model={{metadata.fullSecretPath}}> |  | ||||||
|                           Content |  | ||||||
|                         </LinkTo> |  | ||||||
|                       </li> |  | ||||||
|                 {{else}} |                 {{else}} | ||||||
|                       <li> |                   <dd.Interactive @text="Details" @route="secret.details" @model={{metadata.fullSecretPath}} /> | ||||||
|                         <LinkTo @route="secret.details" @model={{metadata.fullSecretPath}}> |  | ||||||
|                           Details |  | ||||||
|                         </LinkTo> |  | ||||||
|                       </li> |  | ||||||
|                   {{#if metadata.canReadMetadata}} |                   {{#if metadata.canReadMetadata}} | ||||||
|                         <li> |                     <dd.Interactive | ||||||
|                           <LinkTo @route="secret.metadata.versions" @model={{metadata.fullSecretPath}}> |                       @text="View version history" | ||||||
|                             View version history |                       @route="secret.metadata.versions" | ||||||
|                           </LinkTo> |                       @model={{metadata.fullSecretPath}} | ||||||
|                         </li> |                     /> | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                   {{#if metadata.canCreateVersionData}} |                   {{#if metadata.canCreateVersionData}} | ||||||
|                         <li> |                     <dd.Interactive | ||||||
|                           <LinkTo |                       @text="Create new version" | ||||||
|                       @route="secret.details.edit" |                       @route="secret.details.edit" | ||||||
|                       @model={{metadata.fullSecretPath}} |                       @model={{metadata.fullSecretPath}} | ||||||
|                       data-test-popup-create-new-version |                       data-test-popup-create-new-version | ||||||
|                           > |                     /> | ||||||
|                             Create new version |  | ||||||
|                           </LinkTo> |  | ||||||
|                         </li> |  | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                   {{#if metadata.canDeleteMetadata}} |                   {{#if metadata.canDeleteMetadata}} | ||||||
|                         <ConfirmAction |                     <dd.Interactive | ||||||
|                           @buttonText="Permanently delete" |                       @text="Permanently delete" | ||||||
|                           @isInDropdown={{true}} |                       @color="critical" | ||||||
|                           @onConfirmAction={{fn this.onDelete metadata}} |                       {{on "click" (fn (mut this.metadataToDelete) metadata)}} | ||||||
|                           @confirmMessage="This will permanently delete this secret and all its versions." |  | ||||||
|                       data-test-popup-metadata-delete |                       data-test-popup-metadata-delete | ||||||
|                     /> |                     /> | ||||||
|                   {{/if}} |                   {{/if}} | ||||||
|                 {{/if}} |                 {{/if}} | ||||||
|                   </ul> |               </Hds::Dropdown> | ||||||
|                 </nav> |  | ||||||
|               </PopupMenu> |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </LinkedBlock> |       </LinkedBlock> | ||||||
|     {{/each}} |     {{/each}} | ||||||
|  |     {{#if this.metadataToDelete}} | ||||||
|  |       <ConfirmModal | ||||||
|  |         @color="critical" | ||||||
|  |         @onClose={{fn (mut this.metadataToDelete) null}} | ||||||
|  |         @onConfirm={{fn this.onDelete this.metadataToDelete}} | ||||||
|  |         @confirmMessage="This will permanently delete this secret and all its versions." | ||||||
|  |       /> | ||||||
|  |     {{/if}} | ||||||
|     {{! Pagination }} |     {{! Pagination }} | ||||||
|     <Hds::Pagination::Numbered |     <Hds::Pagination::Numbered | ||||||
|       @currentPage={{@secrets.meta.currentPage}} |       @currentPage={{@secrets.meta.currentPage}} | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ export default class KvListPageComponent extends Component { | |||||||
|   @service store; |   @service store; | ||||||
|  |  | ||||||
|   @tracked secretPath; |   @tracked secretPath; | ||||||
|  |   @tracked metadataToDelete = null; // set to the metadata intended to delete | ||||||
|  |  | ||||||
|   get mountPoint() { |   get mountPoint() { | ||||||
|     // mountPoint tells transition where to start. In this case, mountPoint will always be vault.cluster.secrets.backend.kv. |     // mountPoint tells transition where to start. In this case, mountPoint will always be vault.cluster.secrets.backend.kv. | ||||||
| @@ -71,6 +72,8 @@ export default class KvListPageComponent extends Component { | |||||||
|     } catch (error) { |     } catch (error) { | ||||||
|       const message = errorMessage(error, 'Error deleting secret. Please try again or contact support.'); |       const message = errorMessage(error, 'Error deleting secret. Please try again or contact support.'); | ||||||
|       this.flashMessages.danger(message); |       this.flashMessages.danger(message); | ||||||
|  |     } finally { | ||||||
|  |       this.metadataToDelete = null; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,31 +70,27 @@ | |||||||
|  |  | ||||||
|         <div class="level-right"> |         <div class="level-right"> | ||||||
|           <div class="level-item"> |           <div class="level-item"> | ||||||
|             <PopupMenu @name="version-{{versionData.version}}"> |             <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|               <nav class="menu"> |               <dd.ToggleIcon | ||||||
|                 <ul class="menu-list"> |                 @icon="more-horizontal" | ||||||
|                   <li> |                 @text="Manage version" | ||||||
|                     <LinkTo @route="secret.details" @query={{hash version=versionData.version}}> |                 @hasChevron={{false}} | ||||||
|                       View version |                 data-test-popup-menu-trigger | ||||||
|                       {{versionData.version}} |               /> | ||||||
|                     </LinkTo> |               <dd.Interactive | ||||||
|                   </li> |                 @text="View version {{versionData.version}}" | ||||||
|                   {{#if @metadata.canCreateVersionData}} |                 @route="secret.details" | ||||||
|                     <li> |                 @query={{hash version=versionData.version}} | ||||||
|                       <LinkTo |               /> | ||||||
|  |               {{#if (and @metadata.canCreateVersionData (not versionData.destroyed) (not versionData.isSecretDeleted))}} | ||||||
|  |                 <dd.Interactive | ||||||
|  |                   @text="Create new version from {{versionData.version}}" | ||||||
|                   @route="secret.details.edit" |                   @route="secret.details.edit" | ||||||
|                   @query={{hash version=versionData.version}} |                   @query={{hash version=versionData.version}} | ||||||
|                   data-test-create-new-version-from={{versionData.version}} |                   data-test-create-new-version-from={{versionData.version}} | ||||||
|                         @disabled={{or versionData.destroyed versionData.isSecretDeleted}} |                 /> | ||||||
|                       > |  | ||||||
|                         Create new version from |  | ||||||
|                         {{versionData.version}} |  | ||||||
|                       </LinkTo> |  | ||||||
|                     </li> |  | ||||||
|               {{/if}} |               {{/if}} | ||||||
|                 </ul> |             </Hds::Dropdown> | ||||||
|               </nav> |  | ||||||
|             </PopupMenu> |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -84,22 +84,21 @@ | |||||||
|           </div> |           </div> | ||||||
|           <div class="level-right is-flex is-paddingless is-marginless"> |           <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|             <div class="level-item"> |             <div class="level-item"> | ||||||
|               <PopupMenu> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <nav class="menu" aria-label="issuer config options"> |                 <dd.ToggleIcon | ||||||
|                   <ul class="menu-list"> |                   @icon="more-horizontal" | ||||||
|                     <li data-test-popup-menu-details> |                   @text="Manage issuer" | ||||||
|                       <LinkTo @route="issuers.issuer.details" @model={{pkiIssuer.id}}> |                   @hasChevron={{false}} | ||||||
|                         Details |                   data-test-popup-menu-trigger | ||||||
|                       </LinkTo> |                 /> | ||||||
|                     </li> |                 <dd.Interactive | ||||||
|                     <li> |                   @text="Details" | ||||||
|                       <LinkTo @route="issuers.issuer.edit" @model={{pkiIssuer.id}}> |                   @route="issuers.issuer.details" | ||||||
|                         Edit |                   @model={{pkiIssuer.id}} | ||||||
|                       </LinkTo> |                   data-test-popup-menu-details | ||||||
|                     </li> |                 /> | ||||||
|                   </ul> |                 <dd.Interactive @text="Edit" @route="issuers.issuer.edit" @model={{pkiIssuer.id}} /> | ||||||
|                 </nav> |               </Hds::Dropdown> | ||||||
|               </PopupMenu> |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -40,32 +40,32 @@ | |||||||
|           </div> |           </div> | ||||||
|           <div class="level-right is-flex is-paddingless is-marginless"> |           <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|             <div class="level-item"> |             <div class="level-item"> | ||||||
|               <PopupMenu> |               {{#if (or @canRead @canEdit)}} | ||||||
|                 <nav class="menu"> |                 <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                   <ul class="menu-list"> |                   <dd.ToggleIcon | ||||||
|                     <li> |                     @icon="more-horizontal" | ||||||
|                       <LinkTo |                     @text="Manage key" | ||||||
|  |                     @hasChevron={{false}} | ||||||
|  |                     data-test-popup-menu-trigger | ||||||
|  |                   /> | ||||||
|  |                   {{#if @canRead}} | ||||||
|  |                     <dd.Interactive | ||||||
|  |                       @text="Details" | ||||||
|                       @route="keys.key.details" |                       @route="keys.key.details" | ||||||
|                       @model={{pkiKey.keyId}} |                       @model={{pkiKey.keyId}} | ||||||
|                         @disabled={{not @canRead}} |  | ||||||
|                       data-test-key-menu-link="details" |                       data-test-key-menu-link="details" | ||||||
|                       > |                     /> | ||||||
|                         Details |                   {{/if}} | ||||||
|                       </LinkTo> |                   {{#if @canEdit}} | ||||||
|                     </li> |                     <dd.Interactive | ||||||
|                     <li> |                       @text="Edit" | ||||||
|                       <LinkTo |  | ||||||
|                       @route="keys.key.edit" |                       @route="keys.key.edit" | ||||||
|                       @model={{pkiKey.keyId}} |                       @model={{pkiKey.keyId}} | ||||||
|                         @disabled={{not @canEdit}} |  | ||||||
|                       data-test-key-menu-link="edit" |                       data-test-key-menu-link="edit" | ||||||
|                       > |                     /> | ||||||
|                         Edit |                   {{/if}} | ||||||
|                       </LinkTo> |                 </Hds::Dropdown> | ||||||
|                     </li> |               {{/if}} | ||||||
|                   </ul> |  | ||||||
|                 </nav> |  | ||||||
|               </PopupMenu> |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -26,17 +26,15 @@ | |||||||
|           </div> |           </div> | ||||||
|           <div class="level-right is-flex is-paddingless is-marginless"> |           <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|             <div class="level-item"> |             <div class="level-item"> | ||||||
|               <PopupMenu> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <nav class="menu"> |                 <dd.ToggleIcon | ||||||
|                   <ul class="menu-list"> |                   @icon="more-horizontal" | ||||||
|                     <li> |                   @text="Manage certificate" | ||||||
|                       <LinkTo @route="certificates.certificate.details" @model={{pkiCertificate.id}}> |                   @hasChevron={{false}} | ||||||
|                         Details |                   data-test-popup-menu-trigger | ||||||
|                       </LinkTo> |                 /> | ||||||
|                     </li> |                 <dd.Interactive @text="Details" @route="certificates.certificate.details" @model={{pkiCertificate.id}} /> | ||||||
|                   </ul> |               </Hds::Dropdown> | ||||||
|                 </nav> |  | ||||||
|               </PopupMenu> |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -27,22 +27,16 @@ | |||||||
|           </div> |           </div> | ||||||
|           <div class="level-right is-flex is-paddingless is-marginless"> |           <div class="level-right is-flex is-paddingless is-marginless"> | ||||||
|             <div class="level-item"> |             <div class="level-item"> | ||||||
|               <PopupMenu> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <nav class="menu"> |                 <dd.ToggleIcon | ||||||
|                   <ul class="menu-list"> |                   @icon="more-horizontal" | ||||||
|                     <li> |                   @text="Manage role" | ||||||
|                       <LinkTo @route="roles.role.details" @model={{pkiRole.id}}> |                   @hasChevron={{false}} | ||||||
|                         Details |                   data-test-popup-menu-trigger | ||||||
|                       </LinkTo> |                 /> | ||||||
|                     </li> |                 <dd.Interactive @text="Details" @route="roles.role.details" @model={{pkiRole.id}} /> | ||||||
|                     <li> |                 <dd.Interactive @text="Edit" @route="roles.role.edit" @model={{pkiRole.id}} /> | ||||||
|                       <LinkTo @route="roles.role.edit" @model={{pkiRole.id}}> |               </Hds::Dropdown> | ||||||
|                         Edit |  | ||||||
|                       </LinkTo> |  | ||||||
|                     </li> |  | ||||||
|                   </ul> |  | ||||||
|                 </nav> |  | ||||||
|               </PopupMenu> |  | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ export default Controller.extend(copy(DEFAULTS, true), { | |||||||
|   store: service(), |   store: service(), | ||||||
|   rm: service('replication-mode'), |   rm: service('replication-mode'), | ||||||
|   replicationMode: alias('rm.mode'), |   replicationMode: alias('rm.mode'), | ||||||
|  |   secondaryToRevoke: null, | ||||||
|  |  | ||||||
|   submitError(e) { |   submitError(e) { | ||||||
|     if (e.errors) { |     if (e.errors) { | ||||||
| @@ -114,7 +115,8 @@ export default Controller.extend(copy(DEFAULTS, true), { | |||||||
|           }); |           }); | ||||||
|         }, |         }, | ||||||
|         (...args) => this.submitError(...args) |         (...args) => this.submitError(...args) | ||||||
|       ); |       ) | ||||||
|  |       .finally(() => this.set('secondaryToRevoke', null)); | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   actions: { |   actions: { | ||||||
|   | |||||||
| @@ -29,34 +29,29 @@ | |||||||
|           </div> |           </div> | ||||||
|           <div class="column has-text-right"> |           <div class="column has-text-right"> | ||||||
|             {{#if (or (eq this.replicationMode "performance") this.model.canRevokeSecondary)}} |             {{#if (or (eq this.replicationMode "performance") this.model.canRevokeSecondary)}} | ||||||
|               <PopupMenu @name="secondary-details"> |               <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                 <nav class="menu"> |                 <dd.ToggleIcon | ||||||
|                   <ul class="menu-list"> |                   @icon="more-horizontal" | ||||||
|  |                   @text="Secondary popup nav menu" | ||||||
|  |                   @hasChevron={{false}} | ||||||
|  |                   data-test-popup-menu-trigger | ||||||
|  |                 /> | ||||||
|                 {{#if (eq this.replicationMode "performance")}} |                 {{#if (eq this.replicationMode "performance")}} | ||||||
|                       <li class="action"> |                   <dd.Interactive | ||||||
|                         <LinkTo |                     @text="Path filter config" | ||||||
|                     @route="mode.secondaries.config-show" |                     @route="mode.secondaries.config-show" | ||||||
|                     @models={{array this.replicationMode secondary}} |                     @models={{array this.replicationMode secondary}} | ||||||
|                     data-test-replication-path-filter-link={{true}} |                     data-test-replication-path-filter-link={{true}} | ||||||
|                         > |                   /> | ||||||
|                           Path filter config |  | ||||||
|                         </LinkTo> |  | ||||||
|                       </li> |  | ||||||
|                 {{/if}} |                 {{/if}} | ||||||
|                 {{#if this.model.canRevokeSecondary}} |                 {{#if this.model.canRevokeSecondary}} | ||||||
|                       <li class="action"> |                   <dd.Interactive | ||||||
|                         <ConfirmAction |                     @text="Revoke" | ||||||
|                           @buttonText="Revoke" |                     @color="critical" | ||||||
|                           @isInDropdown={{true}} |                     {{on "click" (fn (mut this.secondaryToRevoke) secondary)}} | ||||||
|                           @confirmTitle="Revoke token?" |  | ||||||
|                           @confirmMessage="This will revoke this secondary token." |  | ||||||
|                           @onConfirmAction={{action "onSubmit" "revoke-secondary" "primary" (hash id=secondary)}} |  | ||||||
|                   /> |                   /> | ||||||
|                       </li> |  | ||||||
|                 {{/if}} |                 {{/if}} | ||||||
|                   </ul> |               </Hds::Dropdown> | ||||||
|                 </nav> |  | ||||||
|               </PopupMenu> |  | ||||||
|             {{/if}} |             {{/if}} | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
| @@ -77,3 +72,13 @@ | |||||||
|     </EmptyState> |     </EmptyState> | ||||||
|   {{/if}} |   {{/if}} | ||||||
| {{/if}} | {{/if}} | ||||||
|  |  | ||||||
|  | {{#if this.secondaryToRevoke}} | ||||||
|  |   <ConfirmModal | ||||||
|  |     @color="critical" | ||||||
|  |     @confirmTitle="Revoke token?" | ||||||
|  |     @confirmMessage="This will revoke this secondary token." | ||||||
|  |     @onClose={{fn (mut this.secondaryToRevoke) null}} | ||||||
|  |     @onConfirm={{action "onSubmit" "revoke-secondary" "primary" (hash id=this.secondaryToRevoke)}} | ||||||
|  |   /> | ||||||
|  | {{/if}} | ||||||
| @@ -90,7 +90,7 @@ | |||||||
|                 {{/if}} |                 {{/if}} | ||||||
|               </B.Td> |               </B.Td> | ||||||
|               <B.Td @align="right"> |               <B.Td @align="right"> | ||||||
|                 <Hds::Dropdown @isInline={{true}} as |dd|> |                 <Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|> | ||||||
|                   <dd.ToggleIcon |                   <dd.ToggleIcon | ||||||
|                     @icon="more-horizontal" |                     @icon="more-horizontal" | ||||||
|                     @text="Actions" |                     @text="Actions" | ||||||
|   | |||||||
| @@ -8,7 +8,11 @@ import { selectChoose, clickTrigger } from 'ember-power-select/test-support/help | |||||||
| import page from 'vault/tests/pages/access/identity/create'; | import page from 'vault/tests/pages/access/identity/create'; | ||||||
| import showPage from 'vault/tests/pages/access/identity/show'; | import showPage from 'vault/tests/pages/access/identity/show'; | ||||||
| import indexPage from 'vault/tests/pages/access/identity/index'; | import indexPage from 'vault/tests/pages/access/identity/index'; | ||||||
|  | const SELECTORS = { | ||||||
|  |   identityRow: (name) => `[data-test-identity-row="${name}"]`, | ||||||
|  |   popupMenu: '[data-test-popup-menu-trigger]', | ||||||
|  |   menuDelete: '[data-test-popup-menu="delete"]', | ||||||
|  | }; | ||||||
| export const testCRUD = async (name, itemType, assert) => { | export const testCRUD = async (name, itemType, assert) => { | ||||||
|   await page.visit({ item_type: itemType }); |   await page.visit({ item_type: itemType }); | ||||||
|   await settled(); |   await settled(); | ||||||
| @@ -24,7 +28,6 @@ export const testCRUD = async (name, itemType, assert) => { | |||||||
|     `${itemType}: navigates to show on create` |     `${itemType}: navigates to show on create` | ||||||
|   ); |   ); | ||||||
|   assert.ok(showPage.nameContains(name), `${itemType}: renders the name on the show page`); |   assert.ok(showPage.nameContains(name), `${itemType}: renders the name on the show page`); | ||||||
|  |  | ||||||
|   await indexPage.visit({ item_type: itemType }); |   await indexPage.visit({ item_type: itemType }); | ||||||
|   await settled(); |   await settled(); | ||||||
|   assert.strictEqual( |   assert.strictEqual( | ||||||
| @@ -32,10 +35,10 @@ export const testCRUD = async (name, itemType, assert) => { | |||||||
|     1, |     1, | ||||||
|     `${itemType}: lists the entity in the entity list` |     `${itemType}: lists the entity in the entity list` | ||||||
|   ); |   ); | ||||||
|   await indexPage.items.filterBy('name', name)[0].menu(); |  | ||||||
|   await waitUntil(() => find('[data-test-item-delete]')); |   await click(`${SELECTORS.identityRow(name)} ${SELECTORS.popupMenu}`); | ||||||
|   await indexPage.delete(); |   await waitUntil(() => find(SELECTORS.menuDelete)); | ||||||
|   await settled(); |   await click(SELECTORS.menuDelete); | ||||||
|   await indexPage.confirmDelete(); |   await indexPage.confirmDelete(); | ||||||
|   await settled(); |   await settled(); | ||||||
|   assert.ok( |   assert.ok( | ||||||
|   | |||||||
| @@ -3,12 +3,22 @@ | |||||||
|  * SPDX-License-Identifier: BUSL-1.1 |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| import { currentRouteName } from '@ember/test-helpers'; | import { fillIn, click, currentRouteName, currentURL, visit } from '@ember/test-helpers'; | ||||||
| import { module, test } from 'qunit'; | import { module, test } from 'qunit'; | ||||||
| import { setupApplicationTest } from 'ember-qunit'; | import { setupApplicationTest } from 'ember-qunit'; | ||||||
| import page from 'vault/tests/pages/access/identity/index'; | import page from 'vault/tests/pages/access/identity/index'; | ||||||
| import authPage from 'vault/tests/pages/auth'; | import authPage from 'vault/tests/pages/auth'; | ||||||
|  | import { runCmd } from 'vault/tests/helpers/commands'; | ||||||
|  | import { SELECTORS as GENERAL } from 'vault/tests/helpers/general-selectors'; | ||||||
|  | import { v4 as uuidv4 } from 'uuid'; | ||||||
|  |  | ||||||
|  | const SELECTORS = { | ||||||
|  |   listItem: (name) => `[data-test-identity-row="${name}"]`, | ||||||
|  |   menu: `[data-test-popup-menu-trigger]`, | ||||||
|  |   menuItem: (element) => `[data-test-popup-menu="${element}"]`, | ||||||
|  |   submit: '[data-test-identity-submit]', | ||||||
|  |   confirm: '[data-test-confirm-button]', | ||||||
|  | }; | ||||||
| module('Acceptance | /access/identity/entities', function (hooks) { | module('Acceptance | /access/identity/entities', function (hooks) { | ||||||
|   setupApplicationTest(hooks); |   setupApplicationTest(hooks); | ||||||
|  |  | ||||||
| @@ -33,4 +43,62 @@ module('Acceptance | /access/identity/entities', function (hooks) { | |||||||
|       'navigates to the correct route' |       'navigates to the correct route' | ||||||
|     ); |     ); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   test('it renders popup menu for entities', async function (assert) { | ||||||
|  |     const name = `entity-${uuidv4()}`; | ||||||
|  |     await runCmd(`vault write identity/entity name="${name}" policies="default"`); | ||||||
|  |     await visit('/vault/access/identity/entities'); | ||||||
|  |     assert.strictEqual(currentURL(), '/vault/access/identity/entities', 'navigates to entities tab'); | ||||||
|  |  | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menu}`); | ||||||
|  |     assert | ||||||
|  |       .dom('.hds-dropdown ul') | ||||||
|  |       .hasText('Details Create alias Edit Disable Delete', 'all actions render for entities'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menuItem('delete')}`); | ||||||
|  |     await click(SELECTORS.confirm); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders popup menu for external groups', async function (assert) { | ||||||
|  |     const name = `external-${uuidv4()}`; | ||||||
|  |     await runCmd(`vault write identity/group name="${name}" policies="default" type="external"`); | ||||||
|  |     await visit('/vault/access/identity/groups'); | ||||||
|  |     assert.strictEqual(currentURL(), '/vault/access/identity/groups', 'navigates to the groups tab'); | ||||||
|  |  | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menu}`); | ||||||
|  |     assert | ||||||
|  |       .dom('.hds-dropdown ul') | ||||||
|  |       .hasText('Details Create alias Edit Delete', 'all actions render for external groups'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menuItem('delete')}`); | ||||||
|  |     await click(SELECTORS.confirm); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders popup menu for external groups with alias', async function (assert) { | ||||||
|  |     const name = `external-hasalias-${uuidv4()}`; | ||||||
|  |     await runCmd(`vault write identity/group name="${name}" policies="default" type="external"`); | ||||||
|  |     await visit('/vault/access/identity/groups'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menu}`); | ||||||
|  |     await click(SELECTORS.menuItem('create alias')); | ||||||
|  |     await fillIn(GENERAL.inputByAttr('name'), 'alias-test'); | ||||||
|  |     await click(SELECTORS.submit); | ||||||
|  |  | ||||||
|  |     await visit('/vault/access/identity/groups'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menu}`); | ||||||
|  |     assert | ||||||
|  |       .dom('.hds-dropdown ul') | ||||||
|  |       .hasText('Details Edit Delete', 'no "Create alias" option for external groups with an alias'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menuItem('delete')}`); | ||||||
|  |     await click(SELECTORS.confirm); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders popup menu for internal groups', async function (assert) { | ||||||
|  |     const name = `internal-${uuidv4()}`; | ||||||
|  |     await runCmd(`vault write identity/group name="${name}" policies="default" type="internal"`); | ||||||
|  |     await visit('/vault/access/identity/groups'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menu}`); | ||||||
|  |     assert | ||||||
|  |       .dom('.hds-dropdown ul') | ||||||
|  |       .hasText('Details Edit Delete', 'no "Create alias" option for internal groups'); | ||||||
|  |     await click(`${SELECTORS.listItem(name)} ${SELECTORS.menuItem('delete')}`); | ||||||
|  |     await click(SELECTORS.confirm); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -255,7 +255,7 @@ module('Acceptance | mfa-method', function (hooks) { | |||||||
|     await visit('/vault/access/mfa/methods'); |     await visit('/vault/access/mfa/methods'); | ||||||
|     const id = this.element.querySelector('[data-test-mfa-method-list-item] .tag').textContent.trim(); |     const id = this.element.querySelector('[data-test-mfa-method-list-item] .tag').textContent.trim(); | ||||||
|     const model = this.store.peekRecord('mfa-method', id); |     const model = this.store.peekRecord('mfa-method', id); | ||||||
|     await click('[data-test-mfa-method-list-item] .ember-basic-dropdown-trigger'); |     await click('[data-test-mfa-method-list-item] [data-test-popup-menu-trigger]'); | ||||||
|     await click('[data-test-mfa-method-menu-link="edit"]'); |     await click('[data-test-mfa-method-menu-link="edit"]'); | ||||||
|  |  | ||||||
|     const keys = ['issuer', 'period', 'key_size', 'qr_size', 'algorithm', 'digits', 'skew']; |     const keys = ['issuer', 'period', 'key_size', 'qr_size', 'algorithm', 'digits', 'skew']; | ||||||
|   | |||||||
| @@ -319,7 +319,7 @@ module('Acceptance | pki workflow', function (hooks) { | |||||||
|       ); |       ); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     test('it hide corrects actions for user with read policy', async function (assert) { |     test('it hides correct actions for user with read policy', async function (assert) { | ||||||
|       await authPage.login(this.pkiKeyReader); |       await authPage.login(this.pkiKeyReader); | ||||||
|       await visit(`/vault/secrets/${this.mountPath}/pki/overview`); |       await visit(`/vault/secrets/${this.mountPath}/pki/overview`); | ||||||
|       await click(SELECTORS.keysTab); |       await click(SELECTORS.keysTab); | ||||||
| @@ -330,7 +330,7 @@ module('Acceptance | pki workflow', function (hooks) { | |||||||
|       assert.dom('.linked-block').exists({ count: 1 }, 'One key is in list'); |       assert.dom('.linked-block').exists({ count: 1 }, 'One key is in list'); | ||||||
|       const keyId = find(SELECTORS.keyPages.keyId).innerText; |       const keyId = find(SELECTORS.keyPages.keyId).innerText; | ||||||
|       await click(SELECTORS.keyPages.popupMenuTrigger); |       await click(SELECTORS.keyPages.popupMenuTrigger); | ||||||
|       assert.dom(SELECTORS.keyPages.popupMenuEdit).hasClass('disabled', 'popup menu edit link is disabled'); |       assert.dom(SELECTORS.keyPages.popupMenuEdit).doesNotExist('popup menu edit link is not shown'); | ||||||
|       await click(SELECTORS.keyPages.popupMenuDetails); |       await click(SELECTORS.keyPages.popupMenuDetails); | ||||||
|       assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`); |       assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`); | ||||||
|       assert.dom(SELECTORS.keyPages.keyDeleteButton).doesNotExist('Delete key button is not shown'); |       assert.dom(SELECTORS.keyPages.keyDeleteButton).doesNotExist('Delete key button is not shown'); | ||||||
|   | |||||||
| @@ -117,7 +117,7 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) { | |||||||
|       assert.dom(PAGE.secretTab('Metadata')).doesNotHaveClass('active'); |       assert.dom(PAGE.secretTab('Metadata')).doesNotHaveClass('active'); | ||||||
|       assert.dom(PAGE.secretTab('Version History')).hasText('Version History'); |       assert.dom(PAGE.secretTab('Version History')).hasText('Version History'); | ||||||
|       assert.dom(PAGE.secretTab('Version History')).doesNotHaveClass('active'); |       assert.dom(PAGE.secretTab('Version History')).doesNotHaveClass('active'); | ||||||
|       assert.dom(PAGE.toolbarAction).exists({ count: 5 }, 'toolbar renders all actions'); |       assert.dom(PAGE.toolbarAction).exists({ count: 4 }, 'toolbar renders all actions'); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     test('it navigates back to engine index route via breadcrumbs from secret details', async function (assert) { |     test('it navigates back to engine index route via breadcrumbs from secret details', async function (assert) { | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ module('Acceptance | secrets/ssh', function (hooks) { | |||||||
|     assert.strictEqual(listPage.secrets.length, 1, 'shows role in the list'); |     assert.strictEqual(listPage.secrets.length, 1, 'shows role in the list'); | ||||||
|     const secret = listPage.secrets.objectAt(0); |     const secret = listPage.secrets.objectAt(0); | ||||||
|     await secret.menuToggle(); |     await secret.menuToggle(); | ||||||
|     assert.ok(listPage.menuItems.length > 0, 'shows links in the menu'); |     assert.dom('.hds-dropdown li').exists({ count: 5 }, 'Renders 5 popup menu items'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it deletes a role', async function (assert) { |   test('it deletes a role', async function (assert) { | ||||||
|   | |||||||
| @@ -244,7 +244,7 @@ module('Acceptance | transit (flaky)', function (hooks) { | |||||||
|     assert.dom(SELECTORS.infoRow('Convergent encryption')).hasText('Yes'); |     assert.dom(SELECTORS.infoRow('Convergent encryption')).hasText('Yes'); | ||||||
|     await click(SELECTORS.rootCrumb(this.path)); |     await click(SELECTORS.rootCrumb(this.path)); | ||||||
|     await click(SELECTORS.popupMenu); |     await click(SELECTORS.popupMenu); | ||||||
|     const actions = findAll('.ember-basic-dropdown-content li'); |     const actions = findAll('.hds-dropdown__list li'); | ||||||
|     assert.strictEqual(actions.length, 2, 'shows 2 items in popup menu'); |     assert.strictEqual(actions.length, 2, 'shows 2 items in popup menu'); | ||||||
|  |  | ||||||
|     await click(SELECTORS.secretLink); |     await click(SELECTORS.secretLink); | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ export const SELECTORS = { | |||||||
|   generateIssuerRoot: '[data-test-generate-issuer="root"]', |   generateIssuerRoot: '[data-test-generate-issuer="root"]', | ||||||
|   generateIssuerIntermediate: '[data-test-generate-issuer="intermediate"]', |   generateIssuerIntermediate: '[data-test-generate-issuer="intermediate"]', | ||||||
|   issuerPopupMenu: '[data-test-popup-menu-trigger]', |   issuerPopupMenu: '[data-test-popup-menu-trigger]', | ||||||
|   issuerPopupDetails: '[data-test-popup-menu-details] a', |   issuerPopupDetails: '[data-test-popup-menu-details]', | ||||||
|   issuerDetails: { |   issuerDetails: { | ||||||
|     title: '[data-test-pki-issuer-page-title]', |     title: '[data-test-pki-issuer-page-title]', | ||||||
|     ...ISSUERDETAILS, |     ...ISSUERDETAILS, | ||||||
|   | |||||||
| @@ -49,10 +49,10 @@ module('Integration | Component | auth-config-form options', function (hooks) { | |||||||
|     }); |     }); | ||||||
|     sinon.spy(model.config, 'serialize'); |     sinon.spy(model.config, 'serialize'); | ||||||
|     this.set('model', model); |     this.set('model', model); | ||||||
|     await render(hbs`{{auth-config-form/options model=this.model}}`); |     await render(hbs`<AuthConfigForm::Options @model={{this.model}} />`); | ||||||
|     component.save(); |     component.save(); | ||||||
|     return settled().then(() => { |     return settled().then(() => { | ||||||
|       assert.ok(model.config.serialize.calledOnce); |       assert.strictEqual(model.config.serialize.callCount, 1, 'config serialize was called once'); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								ui/tests/integration/components/confirm-modal-test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								ui/tests/integration/components/confirm-modal-test.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import { module, test } from 'qunit'; | ||||||
|  | import { setupRenderingTest } from 'vault/tests/helpers'; | ||||||
|  | import { click, render } from '@ember/test-helpers'; | ||||||
|  | import { hbs } from 'ember-cli-htmlbars'; | ||||||
|  | import sinon from 'sinon'; | ||||||
|  |  | ||||||
|  | module('Integration | Component | confirm-modal', function (hooks) { | ||||||
|  |   setupRenderingTest(hooks); | ||||||
|  |  | ||||||
|  |   hooks.beforeEach(function () { | ||||||
|  |     this.onConfirm = sinon.spy(); | ||||||
|  |     this.onClose = sinon.spy(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders a reasonable default', async function (assert) { | ||||||
|  |     await render(hbs`<ConfirmModal @onConfirm={{this.onConfirm}} @onClose={{this.onClose}} />`); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-confirm-modal]') | ||||||
|  |       .hasClass('hds-modal--color-warning', 'renders warning modal color'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-confirm-button]') | ||||||
|  |       .hasClass('hds-button--color-primary', 'renders primary confirm button'); | ||||||
|  |     assert.dom('[data-test-confirm-action-title]').hasText('Are you sure?', 'renders default title'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-confirm-action-message]') | ||||||
|  |       .hasText('You will not be able to recover it later.', 'renders default body text'); | ||||||
|  |     await click('[data-test-confirm-cancel-button]'); | ||||||
|  |     assert.ok(this.onClose.called, 'calls the onClose action when Cancel is clicked'); | ||||||
|  |     await click('[data-test-confirm-button]'); | ||||||
|  |     assert.ok(this.onConfirm.called, 'calls the onConfirm action when Confirm is clicked'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -90,7 +90,7 @@ module('Integration | Component | kv | Page::List', function (hooks) { | |||||||
|  |  | ||||||
|     const popupSelector = `${PAGE.list.item('my-secret-0')} ${PAGE.popup}`; |     const popupSelector = `${PAGE.list.item('my-secret-0')} ${PAGE.popup}`; | ||||||
|     await click(popupSelector); |     await click(popupSelector); | ||||||
|     await click('[data-test-confirm-action-trigger]'); |     await click('[data-test-popup-metadata-delete]'); | ||||||
|     await click('[data-test-confirm-button]'); |     await click('[data-test-confirm-button]'); | ||||||
|     assert.dom(PAGE.list.item('my-secret-0')).doesNotExist('deleted the first record from the list'); |     assert.dom(PAGE.list.item('my-secret-0')).doesNotExist('deleted the first record from the list'); | ||||||
|   }); |   }); | ||||||
|   | |||||||
| @@ -87,10 +87,10 @@ module('Integration | Component | kv | Page::Secret::Metadata::Version-History', | |||||||
|       { owner: this.engine } |       { owner: this.engine } | ||||||
|     ); |     ); | ||||||
|     // because the popup menu is nested in a linked block we must combine the two selectors |     // because the popup menu is nested in a linked block we must combine the two selectors | ||||||
|     const popupSelector = `${PAGE.versions.linkedBlock(2)} ${PAGE.popup}`; |     const popupSelector = `${PAGE.versions.linkedBlock(1)} ${PAGE.popup}`; | ||||||
|     await click(popupSelector); |     await click(popupSelector); | ||||||
|     assert |     assert | ||||||
|       .dom('[data-test-create-new-version-from="2"]') |       .dom('[data-test-create-new-version-from="1"]') | ||||||
|       .exists('Shows the option to create a new version from that secret.'); |       .exists('Shows the option to create a new version from that secret.'); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								ui/tests/integration/components/oidc/client-list-test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ui/tests/integration/components/oidc/client-list-test.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import { module, test } from 'qunit'; | ||||||
|  | import { setupRenderingTest } from 'vault/tests/helpers'; | ||||||
|  | import { setupMirage } from 'ember-cli-mirage/test-support'; | ||||||
|  | import { click, render } from '@ember/test-helpers'; | ||||||
|  | import { hbs } from 'ember-cli-htmlbars'; | ||||||
|  | import { overrideCapabilities } from 'vault/tests/helpers/oidc-config'; | ||||||
|  | import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; | ||||||
|  |  | ||||||
|  | module('Integration | Component | oidc/client-list', function (hooks) { | ||||||
|  |   setupRenderingTest(hooks); | ||||||
|  |   setupMirage(hooks); | ||||||
|  |  | ||||||
|  |   hooks.beforeEach(function () { | ||||||
|  |     this.store = this.owner.lookup('service:store'); | ||||||
|  |     this.store.createRecord('oidc/client', { name: 'first-client' }); | ||||||
|  |     this.store.createRecord('oidc/client', { name: 'second-client' }); | ||||||
|  |     this.model = this.store.peekAll('oidc/client'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders list of clients', async function (assert) { | ||||||
|  |     this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub(['read', 'update'])); | ||||||
|  |     await render(hbs`<Oidc::ClientList @model={{this.model}} />`); | ||||||
|  |  | ||||||
|  |     assert.dom('[data-test-oidc-client-linked-block]').exists({ count: 2 }, 'Two clients are rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-client-linked-block="first-client"]').exists('First client is rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-client-linked-block="second-client"]').exists('Second client is rendered'); | ||||||
|  |  | ||||||
|  |     await click('[data-test-oidc-client-linked-block="first-client"] [data-test-popup-menu-trigger]'); | ||||||
|  |     assert.dom('[data-test-oidc-client-menu-link="details"]').exists('Details link is rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-client-menu-link="edit"]').exists('Edit link is rendered'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders popup menu based on permissions', async function (assert) { | ||||||
|  |     this.server.post('/sys/capabilities-self', (schema, req) => { | ||||||
|  |       const { paths } = JSON.parse(req.requestBody); | ||||||
|  |       if (paths[0] === 'identity/oidc/client/first-client') { | ||||||
|  |         return overrideCapabilities('identity/oidc/client/first-client', ['read']); | ||||||
|  |       } else { | ||||||
|  |         return overrideCapabilities('identity/oidc/client/second-client', ['deny']); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     await render(hbs`<Oidc::ClientList @model={{this.model}} />`); | ||||||
|  |  | ||||||
|  |     assert.dom('[data-test-popup-menu-trigger]').exists({ count: 1 }, 'Only one popup menu is rendered'); | ||||||
|  |     await click('[data-test-popup-menu-trigger]'); | ||||||
|  |     assert.dom('[data-test-oidc-client-menu-link="details"]').exists('Details link is rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-client-menu-link="edit"]').doesNotExist('Edit link is not rendered'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										55
									
								
								ui/tests/integration/components/oidc/provider-list-test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								ui/tests/integration/components/oidc/provider-list-test.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: BUSL-1.1 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import { module, test } from 'qunit'; | ||||||
|  | import { setupRenderingTest } from 'vault/tests/helpers'; | ||||||
|  | import { setupMirage } from 'ember-cli-mirage/test-support'; | ||||||
|  | import { click, render } from '@ember/test-helpers'; | ||||||
|  | import { hbs } from 'ember-cli-htmlbars'; | ||||||
|  | import { overrideCapabilities } from 'vault/tests/helpers/oidc-config'; | ||||||
|  | import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; | ||||||
|  |  | ||||||
|  | module('Integration | Component | oidc/provider-list', function (hooks) { | ||||||
|  |   setupRenderingTest(hooks); | ||||||
|  |   setupMirage(hooks); | ||||||
|  |  | ||||||
|  |   hooks.beforeEach(function () { | ||||||
|  |     this.store = this.owner.lookup('service:store'); | ||||||
|  |     this.store.createRecord('oidc/provider', { name: 'first-provider', issuer: 'foobar' }); | ||||||
|  |     this.store.createRecord('oidc/provider', { name: 'second-provider', issuer: 'foobar' }); | ||||||
|  |     this.model = this.store.peekAll('oidc/provider'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders list of providers', async function (assert) { | ||||||
|  |     this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub(['read', 'update'])); | ||||||
|  |     await render(hbs`<Oidc::ProviderList @model={{this.model}} />`); | ||||||
|  |  | ||||||
|  |     assert.dom('[data-test-oidc-provider-linked-block]').exists({ count: 2 }, 'Two clients are rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-provider-linked-block="first-provider"]').exists('First client is rendered'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-oidc-provider-linked-block="second-provider"]') | ||||||
|  |       .exists('Second client is rendered'); | ||||||
|  |  | ||||||
|  |     await click('[data-test-oidc-provider-linked-block="first-provider"] [data-test-popup-menu-trigger]'); | ||||||
|  |     assert.dom('[data-test-oidc-provider-menu-link="details"]').exists('Details link is rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-provider-menu-link="edit"]').exists('Edit link is rendered'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders popup menu based on permissions', async function (assert) { | ||||||
|  |     this.server.post('/sys/capabilities-self', (schema, req) => { | ||||||
|  |       const { paths } = JSON.parse(req.requestBody); | ||||||
|  |       if (paths[0] === 'identity/oidc/provider/first-provider') { | ||||||
|  |         return overrideCapabilities('identity/oidc/provider/first-provider', ['read']); | ||||||
|  |       } else { | ||||||
|  |         return overrideCapabilities('identity/oidc/provider/second-provider', ['deny']); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |     await render(hbs`<Oidc::ProviderList @model={{this.model}} />`); | ||||||
|  |     assert.dom('[data-test-popup-menu-trigger]').exists({ count: 1 }, 'Only one popup menu is rendered'); | ||||||
|  |     await click('[data-test-popup-menu-trigger]'); | ||||||
|  |     assert.dom('[data-test-oidc-provider-menu-link="details"]').exists('Details link is rendered'); | ||||||
|  |     assert.dom('[data-test-oidc-provider-menu-link="edit"]').doesNotExist('Edit link is not rendered'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @@ -91,8 +91,8 @@ module('Integration | Component | pki key list page', function (hooks) { | |||||||
|     assert.dom(SELECTORS.popupMenuEdit).exists('edit link exists'); |     assert.dom(SELECTORS.popupMenuEdit).exists('edit link exists'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it hides or disables actions when permission denied', async function (assert) { |   test('it hides actions when permission denied', async function (assert) { | ||||||
|     assert.expect(4); |     assert.expect(3); | ||||||
|     await render( |     await render( | ||||||
|       hbs` |       hbs` | ||||||
|         <Page::PkiKeyList |         <Page::PkiKeyList | ||||||
| @@ -108,8 +108,6 @@ module('Integration | Component | pki key list page', function (hooks) { | |||||||
|     ); |     ); | ||||||
|     assert.dom(SELECTORS.importKey).doesNotExist('renders import action'); |     assert.dom(SELECTORS.importKey).doesNotExist('renders import action'); | ||||||
|     assert.dom(SELECTORS.generateKey).doesNotExist('renders generate action'); |     assert.dom(SELECTORS.generateKey).doesNotExist('renders generate action'); | ||||||
|     await click(SELECTORS.popupMenuTrigger); |     assert.dom(SELECTORS.popupMenuTrigger).doesNotExist('does not render popup menu when no permission'); | ||||||
|     assert.dom(SELECTORS.popupMenuDetails).hasClass('disabled', 'details link enabled'); |  | ||||||
|     assert.dom(SELECTORS.popupMenuEdit).hasClass('disabled', 'edit link enabled'); |  | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -53,8 +53,8 @@ module('Integration | Component | transform-list-item', function (hooks) { | |||||||
|     />`); |     />`); | ||||||
|  |  | ||||||
|     assert.dom('[data-test-secret-link="template/foo"]').exists('shows clickable list item'); |     assert.dom('[data-test-secret-link="template/foo"]').exists('shows clickable list item'); | ||||||
|     await click('button.popup-menu-trigger'); |     await click('[data-test-popup-menu-trigger]'); | ||||||
|     assert.dom('.popup-menu-content li').exists({ count: 1 }, 'has one option'); |     assert.dom('.hds-dropdown li').exists({ count: 1 }, 'has one option'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it has details and edit menu item if read & edit capabilities', async function (assert) { |   test('it has details and edit menu item if read & edit capabilities', async function (assert) { | ||||||
| @@ -76,8 +76,8 @@ module('Integration | Component | transform-list-item', function (hooks) { | |||||||
|     />`); |     />`); | ||||||
|  |  | ||||||
|     assert.dom('[data-test-secret-link="alphabet/foo"]').exists('shows clickable list item'); |     assert.dom('[data-test-secret-link="alphabet/foo"]').exists('shows clickable list item'); | ||||||
|     await click('button.popup-menu-trigger'); |     await click('[data-test-popup-menu-trigger]'); | ||||||
|     assert.dom('.popup-menu-content li').exists({ count: 2 }, 'has both options'); |     assert.dom('.hds-dropdown li').exists({ count: 2 }, 'has both options'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it is not clickable if built-in template with all capabilities', async function (assert) { |   test('it is not clickable if built-in template with all capabilities', async function (assert) { | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ export default create({ | |||||||
|     menu: clickable('[data-test-popup-menu-trigger]'), |     menu: clickable('[data-test-popup-menu-trigger]'), | ||||||
|     name: text('[data-test-identity-link]'), |     name: text('[data-test-identity-link]'), | ||||||
|   }), |   }), | ||||||
|   delete: clickable('[data-test-item-delete]', { |   delete: clickable('[data-test-popup-menu="delete"]', { | ||||||
|     testContainer: '#ember-testing', |     testContainer: '#ember-testing', | ||||||
|   }), |   }), | ||||||
|   confirmDelete: clickable('[data-test-confirm-button]'), |   confirmDelete: clickable('[data-test-confirm-button]'), | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ export default create({ | |||||||
|     name: text('[data-test-identity-link]'), |     name: text('[data-test-identity-link]'), | ||||||
|   }), |   }), | ||||||
|  |  | ||||||
|   delete: clickable('[data-test-item-delete]', { |   delete: clickable('[data-test-popup-menu="delete"]', { | ||||||
|     testContainer: '#ember-testing', |     testContainer: '#ember-testing', | ||||||
|   }), |   }), | ||||||
|   confirmDelete: clickable('[data-test-confirm-button]'), |   confirmDelete: clickable('[data-test-confirm-button]'), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 claire bontempo
					claire bontempo