mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-03 20:17:59 +00:00 
			
		
		
		
	KV automatic delete state issue in UI (#13166)
* converts secret-v2-version model to native class -- fixes issues with cached values for deleted prop * adds changelog entry * adds disabled state to ToolbarLink component and disables create new version action when users cannot read metadata * updates secret-edit acceptance test
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/13166.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/13166.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					```release-note:bug
 | 
				
			||||||
 | 
					ui: Fixes issue with placeholder not displaying for automatically deleted secrets when deletion time has passed
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
@@ -1,20 +1,21 @@
 | 
				
			|||||||
import { belongsTo, attr } from '@ember-data/model';
 | 
					import { belongsTo, attr } from '@ember-data/model';
 | 
				
			||||||
import Secret from './secret';
 | 
					import SecretModel from './secret';
 | 
				
			||||||
import { computed } from '@ember/object';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Secret.extend({
 | 
					export default class SecretV2VersionModel extends SecretModel {
 | 
				
			||||||
  failedServerRead: attr('boolean'),
 | 
					  @attr('boolean') failedServerRead;
 | 
				
			||||||
  pathAttr: 'path',
 | 
					  @attr('number') version;
 | 
				
			||||||
  version: attr('number'),
 | 
					  @attr('string') path;
 | 
				
			||||||
  secret: belongsTo('secret-v2'),
 | 
					  @attr('string') deletionTime;
 | 
				
			||||||
  path: attr('string'),
 | 
					  @attr('string') createdTime;
 | 
				
			||||||
  deletionTime: attr('string'),
 | 
					  @attr('boolean') detroyed;
 | 
				
			||||||
  createdTime: attr('string'),
 | 
					  @attr('number') currentVersion;
 | 
				
			||||||
  deleted: computed('deletionTime', function() {
 | 
					  @belongsTo('secret-v2') secret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pathAttr = 'path';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get deleted() {
 | 
				
			||||||
    const deletionTime = new Date(this.deletionTime);
 | 
					    const deletionTime = new Date(this.deletionTime);
 | 
				
			||||||
    const now = new Date();
 | 
					    const now = new Date();
 | 
				
			||||||
    return deletionTime <= now;
 | 
					    return deletionTime <= now;
 | 
				
			||||||
  }),
 | 
					  }
 | 
				
			||||||
  destroyed: attr('boolean'),
 | 
					}
 | 
				
			||||||
  currentVersion: attr('number'),
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,12 +155,9 @@ export default Route.extend(UnloadModelRoute, {
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      if (secretModel.failedServerRead) {
 | 
					      if (secretModel.failedServerRead) {
 | 
				
			||||||
        // we couldn't read metadata, so we want to directly fetch the version
 | 
					        // we couldn't read metadata, so we want to directly fetch the version
 | 
				
			||||||
 | 
					        versionModel = await this.store.findRecord('secret-v2-version', JSON.stringify(versionId), {
 | 
				
			||||||
        versionModel =
 | 
					 | 
				
			||||||
          this.store.peekRecord('secret-v2-version', JSON.stringify(versionId)) ||
 | 
					 | 
				
			||||||
          (await this.store.findRecord('secret-v2-version', JSON.stringify(versionId), {
 | 
					 | 
				
			||||||
          reload: true,
 | 
					          reload: true,
 | 
				
			||||||
          }));
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // we may have previously errored, so roll it back here
 | 
					        // we may have previously errored, so roll it back here
 | 
				
			||||||
        version.rollbackAttributes();
 | 
					        version.rollbackAttributes();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@
 | 
				
			|||||||
  color: $black;
 | 
					  color: $black;
 | 
				
			||||||
  transition: background-color $speed;
 | 
					  transition: background-color $speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover {
 | 
					  &:hover:not(.disabled) {
 | 
				
			||||||
    background-color: $ui-gray-100;
 | 
					    background-color: $ui-gray-100;
 | 
				
			||||||
    border: 0;
 | 
					    border: 0;
 | 
				
			||||||
    color: $blue;
 | 
					    color: $blue;
 | 
				
			||||||
@@ -98,6 +98,18 @@
 | 
				
			|||||||
    height: 2.5rem;
 | 
					    height: 2.5rem;
 | 
				
			||||||
    padding: $size-10 $size-8;
 | 
					    padding: $size-10 $size-8;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.disabled {
 | 
				
			||||||
 | 
					    opacity: 0.5;
 | 
				
			||||||
 | 
					    cursor: default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:focus {
 | 
				
			||||||
 | 
					      box-shadow: none;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    &:hover {
 | 
				
			||||||
 | 
					      background: transparent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.toolbar-separator {
 | 
					.toolbar-separator {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,8 @@
 | 
				
			|||||||
            @data-test-secret-edit="true"
 | 
					            @data-test-secret-edit="true"
 | 
				
			||||||
            @replace={{true}}
 | 
					            @replace={{true}}
 | 
				
			||||||
            @type="add"
 | 
					            @type="add"
 | 
				
			||||||
 | 
					            @disabled={{@model.failedServerRead}}
 | 
				
			||||||
 | 
					            @disabledTooltip="Metadata read access is required to create new version"
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            Create new version
 | 
					            Create new version
 | 
				
			||||||
          </ToolbarLink>
 | 
					          </ToolbarLink>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								ui/jsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ui/jsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "compilerOptions": {
 | 
				
			||||||
 | 
					    "experimentalDecorators": true,
 | 
				
			||||||
 | 
					    "allowJs": true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "exclude": [
 | 
				
			||||||
 | 
					    "node_modules"
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,15 +7,17 @@
 | 
				
			|||||||
 * ```js
 | 
					 * ```js
 | 
				
			||||||
 * <Toolbar>
 | 
					 * <Toolbar>
 | 
				
			||||||
 *   <ToolbarActions>
 | 
					 *   <ToolbarActions>
 | 
				
			||||||
 *     <ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add">
 | 
					 *     <ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add" @disabled={{true}} @disabledTooltip="This link is disabled">
 | 
				
			||||||
 *       Create policy
 | 
					 *       Create policy
 | 
				
			||||||
 *     </ToolbarLink>
 | 
					 *     </ToolbarLink>
 | 
				
			||||||
 *   </ToolbarActions>
 | 
					 *   </ToolbarActions>
 | 
				
			||||||
 * </Toolbar>
 | 
					 * </Toolbar>
 | 
				
			||||||
 * ```
 | 
					 * ```
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param params=''{Array} Array to pass to LinkTo
 | 
					 * @param {array} params - Array to pass to LinkTo
 | 
				
			||||||
 * @param type=''{String} Use "add" to change icon
 | 
					 * @param {string} type - Use "add" to change icon
 | 
				
			||||||
 | 
					 * @param {boolean} disabled - pass true to disable link
 | 
				
			||||||
 | 
					 * @param {string} disabledTooltip - tooltip to display on hover when disabled
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Component from '@ember/component';
 | 
					import Component from '@ember/component';
 | 
				
			||||||
@@ -27,6 +29,8 @@ export default Component.extend({
 | 
				
			|||||||
  tagName: '',
 | 
					  tagName: '',
 | 
				
			||||||
  supportsDataTestProperties: true,
 | 
					  supportsDataTestProperties: true,
 | 
				
			||||||
  type: null,
 | 
					  type: null,
 | 
				
			||||||
 | 
					  disabled: false,
 | 
				
			||||||
 | 
					  disabledTooltip: null,
 | 
				
			||||||
  glyph: computed('type', function() {
 | 
					  glyph: computed('type', function() {
 | 
				
			||||||
    if (this.type == 'add') {
 | 
					    if (this.type == 'add') {
 | 
				
			||||||
      return 'plus-plain';
 | 
					      return 'plus-plain';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,20 +1,36 @@
 | 
				
			|||||||
<LinkTo
 | 
					{{#let (component "link-to"
 | 
				
			||||||
  class="toolbar-link"
 | 
					  class="toolbar-link"
 | 
				
			||||||
  @params={{params}}
 | 
					  disabled=disabled
 | 
				
			||||||
  data-test-secret-edit={{data-test-secret-edit}}
 | 
					  params=params
 | 
				
			||||||
  data-test-secondary-add={{data-test-secondary-add}}
 | 
					  data-test-secret-edit=data-test-secret-edit
 | 
				
			||||||
  data-test-configure-link={{data-test-configure-link}}
 | 
					  data-test-secondary-add=data-test-secondary-add
 | 
				
			||||||
  data-test-alias-edit-link={{data-test-alias-edit-link}}
 | 
					  data-test-configure-link=data-test-configure-link
 | 
				
			||||||
  data-test-entity-edit-link={{data-test-entity-edit-link}}
 | 
					  data-test-alias-edit-link=data-test-alias-edit-link
 | 
				
			||||||
  data-test-replication-link={{data-test-replication-link}}
 | 
					  data-test-entity-edit-link=data-test-entity-edit-link
 | 
				
			||||||
  data-test-entity-merge-link={{data-test-entity-merge-link}}
 | 
					  data-test-replication-link=data-test-replication-link
 | 
				
			||||||
  data-test-backend-view-link={{data-test-backend-view-link}}
 | 
					  data-test-entity-merge-link=data-test-entity-merge-link
 | 
				
			||||||
  data-test-entity-create-link={{data-test-entity-create-link}}
 | 
					  data-test-backend-view-link=data-test-backend-view-link
 | 
				
			||||||
  data-test-policy-create-link={{data-test-policy-create-link}}
 | 
					  data-test-entity-create-link=data-test-entity-create-link
 | 
				
			||||||
  data-test-policy-edit-toggle={{data-test-policy-edit-toggle}}
 | 
					  data-test-policy-create-link=data-test-policy-create-link
 | 
				
			||||||
  data-test-secret-backend-configure={{data-test-secret-backend-configure}}
 | 
					  data-test-policy-edit-toggle=data-test-policy-edit-toggle
 | 
				
			||||||
  ...attributes
 | 
					  data-test-secret-backend-configure=data-test-secret-backend-configure
 | 
				
			||||||
>
 | 
					) as |LinkToComponent|}}
 | 
				
			||||||
  {{yield}}
 | 
					  {{#if (and disabled disabledTooltip)}}
 | 
				
			||||||
  <Icon @glyph={{glyph}} />
 | 
					    <ToolTip @verticalPosition="above" as |T|>
 | 
				
			||||||
</LinkTo>
 | 
					      <T.trigger tabindex="-1">
 | 
				
			||||||
 | 
					        <LinkToComponent ...attributes>
 | 
				
			||||||
 | 
					          {{yield}} <Icon @glyph={{glyph}} data-test-icon={{glyph}} />
 | 
				
			||||||
 | 
					        </LinkToComponent>
 | 
				
			||||||
 | 
					      </T.trigger>
 | 
				
			||||||
 | 
					      <T.content @class="tool-tip smaller-font">
 | 
				
			||||||
 | 
					        <div class="box" data-test-disabled-tooltip>
 | 
				
			||||||
 | 
					          {{disabledTooltip}}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </T.content>
 | 
				
			||||||
 | 
					    </ToolTip>
 | 
				
			||||||
 | 
					  {{else}}
 | 
				
			||||||
 | 
					    <LinkToComponent ...attributes>
 | 
				
			||||||
 | 
					      {{yield}} <Icon @glyph={{glyph}} data-test-icon={{glyph}} />
 | 
				
			||||||
 | 
					    </LinkToComponent>
 | 
				
			||||||
 | 
					  {{/if}}
 | 
				
			||||||
 | 
					{{/let}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,18 +4,21 @@
 | 
				
			|||||||
`ToolbarLink` components style links and buttons for the Toolbar
 | 
					`ToolbarLink` components style links and buttons for the Toolbar
 | 
				
			||||||
It should only be used inside of `Toolbar`.
 | 
					It should only be used inside of `Toolbar`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Params**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Param | Type | Default | Description |
 | 
					| Param | Type | Description |
 | 
				
			||||||
| --- | --- | --- | --- |
 | 
					| --- | --- | --- |
 | 
				
			||||||
| params | <code>Array</code> | <code>''</code> | Array to pass to LinkTo |
 | 
					| params | <code>array</code> | Array to pass to LinkTo |
 | 
				
			||||||
| type | <code>String</code> | <code>''</code> | Use "add" to change icon |
 | 
					| type | <code>string</code> | Use "add" to change icon |
 | 
				
			||||||
 | 
					| disabled | <code>boolean</code> | pass true to disable link |
 | 
				
			||||||
 | 
					| disabledTooltip | <code>string</code> | tooltip to display on hover when disabled |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Example**
 | 
					**Example**
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
```js
 | 
					```js
 | 
				
			||||||
<Toolbar>
 | 
					<Toolbar>
 | 
				
			||||||
  <ToolbarActions>
 | 
					  <ToolbarActions>
 | 
				
			||||||
    <ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add">
 | 
					    <ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add" @disabled={{true}} @disabledTooltip="This link is disabled">
 | 
				
			||||||
      Create policy
 | 
					      Create policy
 | 
				
			||||||
    </ToolbarLink>
 | 
					    </ToolbarLink>
 | 
				
			||||||
  </ToolbarActions>
 | 
					  </ToolbarActions>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import hbs from 'htmlbars-inline-precompile';
 | 
					import hbs from 'htmlbars-inline-precompile';
 | 
				
			||||||
import { storiesOf } from '@storybook/ember';
 | 
					import { storiesOf } from '@storybook/ember';
 | 
				
			||||||
import { withKnobs, select, text } from '@storybook/addon-knobs';
 | 
					import { withKnobs, select, text, boolean } from '@storybook/addon-knobs';
 | 
				
			||||||
import notes from './toolbar-link.md';
 | 
					import notes from './toolbar-link.md';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
storiesOf('Toolbar', module)
 | 
					storiesOf('Toolbar', module)
 | 
				
			||||||
@@ -17,6 +17,8 @@ storiesOf('Toolbar', module)
 | 
				
			|||||||
            <ToolbarLink
 | 
					            <ToolbarLink
 | 
				
			||||||
              @params={{array '#'}}
 | 
					              @params={{array '#'}}
 | 
				
			||||||
              @type={{type}}
 | 
					              @type={{type}}
 | 
				
			||||||
 | 
					              @disabled={{disabled}}
 | 
				
			||||||
 | 
					              @disabledTooltip={{disabledTooltip}}
 | 
				
			||||||
            >
 | 
					            >
 | 
				
			||||||
              {{label}}
 | 
					              {{label}}
 | 
				
			||||||
            </ToolbarLink>
 | 
					            </ToolbarLink>
 | 
				
			||||||
@@ -27,6 +29,8 @@ storiesOf('Toolbar', module)
 | 
				
			|||||||
      context: {
 | 
					      context: {
 | 
				
			||||||
        type: select('Type', ['', 'add']),
 | 
					        type: select('Type', ['', 'add']),
 | 
				
			||||||
        label: text('Button text', 'Edit secret'),
 | 
					        label: text('Button text', 'Edit secret'),
 | 
				
			||||||
 | 
					        disabled: boolean('disabled', false),
 | 
				
			||||||
 | 
					        disabledTooltip: text('Tooltip to display when disabled', ''),
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
    { notes }
 | 
					    { notes }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -617,13 +617,8 @@ module('Acceptance | secrets/secret/create', function(hooks) {
 | 
				
			|||||||
    await assert
 | 
					    await assert
 | 
				
			||||||
      .dom('[data-test-value-div="secret-key"]')
 | 
					      .dom('[data-test-value-div="secret-key"]')
 | 
				
			||||||
      .exists('secret view page and info table row with secret-key value');
 | 
					      .exists('secret view page and info table row with secret-key value');
 | 
				
			||||||
    // check you can create new version
 | 
					    // create new version should be disabled with no metadata read access
 | 
				
			||||||
    await click('[data-test-secret-edit="true"]');
 | 
					    assert.dom('[data-test-secret-edit]').hasClass('disabled', 'Create new version action is disabled');
 | 
				
			||||||
    await settled();
 | 
					 | 
				
			||||||
    await fillIn('[data-test-secret-key]', 'version2');
 | 
					 | 
				
			||||||
    await editPage.save();
 | 
					 | 
				
			||||||
    await settled();
 | 
					 | 
				
			||||||
    assert.dom('[data-test-row-label="version2"]').exists('the current version displayed is the new version');
 | 
					 | 
				
			||||||
    assert
 | 
					    assert
 | 
				
			||||||
      .dom('[data-test-popup-menu-trigger="version"]')
 | 
					      .dom('[data-test-popup-menu-trigger="version"]')
 | 
				
			||||||
      .doesNotExist('the version drop down menu does not show');
 | 
					      .doesNotExist('the version drop down menu does not show');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { module, test } from 'qunit';
 | 
					import { module, test } from 'qunit';
 | 
				
			||||||
import { setupRenderingTest } from 'ember-qunit';
 | 
					import { setupRenderingTest } from 'ember-qunit';
 | 
				
			||||||
import { render } from '@ember/test-helpers';
 | 
					import { render, triggerEvent } from '@ember/test-helpers';
 | 
				
			||||||
import { isPresent } from 'ember-cli-page-object';
 | 
					import { isPresent } from 'ember-cli-page-object';
 | 
				
			||||||
import hbs from 'htmlbars-inline-precompile';
 | 
					import hbs from 'htmlbars-inline-precompile';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,4 +14,41 @@ module('Integration | Component | toolbar-link', function(hooks) {
 | 
				
			|||||||
    assert.ok(isPresent('.toolbar-link'));
 | 
					    assert.ok(isPresent('.toolbar-link'));
 | 
				
			||||||
    assert.ok(isPresent('.icon'));
 | 
					    assert.ok(isPresent('.icon'));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('it should render icons', async function(assert) {
 | 
				
			||||||
 | 
					    assert.expect(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await render(hbs`
 | 
				
			||||||
 | 
					      <ToolbarLink
 | 
				
			||||||
 | 
					        @params={{array '/secrets'}}
 | 
				
			||||||
 | 
					        @type={{this.type}}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        Test Link
 | 
				
			||||||
 | 
					      </ToolbarLink>
 | 
				
			||||||
 | 
					    `);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.dom('[data-test-icon="chevron-right"]').exists('Default chevron right icon renders');
 | 
				
			||||||
 | 
					    this.set('type', 'add');
 | 
				
			||||||
 | 
					    assert.dom('[data-test-icon="plus-plain"]').exists('Icon can be overriden to show plus sign');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('it should disable and show tooltip if provided', async function(assert) {
 | 
				
			||||||
 | 
					    assert.expect(3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await render(hbs`
 | 
				
			||||||
 | 
					      <ToolbarLink
 | 
				
			||||||
 | 
					        @params={{array '/secrets'}}
 | 
				
			||||||
 | 
					        @disabled={{true}}
 | 
				
			||||||
 | 
					        @disabledTooltip={{this.tooltip}}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        Test Link
 | 
				
			||||||
 | 
					      </ToolbarLink>
 | 
				
			||||||
 | 
					    `);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert.dom('a').hasClass('disabled', 'Link can be disabled');
 | 
				
			||||||
 | 
					    assert.dom('[data-test-popup-menu-trigger]').doesNotExist('Tooltip is hidden when not provided');
 | 
				
			||||||
 | 
					    this.set('tooltip', 'Test tooltip');
 | 
				
			||||||
 | 
					    await triggerEvent('.ember-basic-dropdown-trigger', 'mouseenter');
 | 
				
			||||||
 | 
					    assert.dom('[data-test-disabled-tooltip]').hasText(this.tooltip, 'Tooltip renders');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user