Skip to content

Commit

Permalink
KV search box when no list access to metadata (#12626)
Browse files Browse the repository at this point in the history
* get credentials card test and setup

* call getcrednetials card and remove path test error

* configuration

* metadata search box

* changelog

* checking if it is noReadAccess

* try removing test

* blah

* a test

* blah

* stuff

* attempting a clean up to solve issue

* Another attempt

* test1

* test2

* test3

* test4

* test5

* test6

* test7

* finally?

* clean up
  • Loading branch information
Monkeychip committed Sep 29, 2021
1 parent f90eeac commit b487486
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 314 deletions.
3 changes: 3 additions & 0 deletions changelog/12626.txt
@@ -0,0 +1,3 @@
```release-note:improvement
ui: Add KV secret search box when no metadata list access.
```
30 changes: 23 additions & 7 deletions ui/app/components/get-credentials-card.js
Expand Up @@ -5,11 +5,16 @@
*
* @example
* ```js
* <GetCredentialsCard @title="Get Credentials" @searchLabel="Role to use" @models={{array 'database/roles'}} />
* <GetCredentialsCard @title="Get Credentials" @searchLabel="Role to use" @models={{array 'database/roles'}} @type="role" @backend={{model.backend}}/>
* ```
* @param title=null {String} - The title displays the card title
* @param searchLabel=null {String} - The text above the searchSelect component
* @param models=null {Array} - An array of model types to fetch from the API. Passed through to SearchSelect component
* @param type=null {String} - Determines where the transitionTo goes. If role to backend.credentials, if secret backend.show
* @param shouldUseFallback=[false] {Boolean} - If true the input is used instead of search select.
* @param subText=[null] {String} - Text below title
* @param placeHolder=[null] {String} - Only works if shouldUseFallback is true. Displays the helper text inside the input.
* @param backend=null {String} - Name of the backend to look up on search.
*/

import Component from '@glimmer/component';
Expand All @@ -20,25 +25,36 @@ export default class GetCredentialsCard extends Component {
@service router;
@service store;
@tracked role = '';
@tracked secret = '';
@action
async transitionToCredential() {
const role = this.role;
const secret = this.secret;
if (role) {
this.router.transitionTo('vault.cluster.secrets.backend.credentials', role);
}
if (secret) {
this.router.transitionTo('vault.cluster.secrets.backend.show', secret);
}
}
get buttonDisabled() {
return !this.role;
return !this.role && !this.secret;
}
@action
handleRoleInput(value) {
// if it comes in from the fallback component then the value is a string otherwise it's an array
let role = value;
if (Array.isArray(value)) {
role = value[0];
if (this.args.type === 'role') {
// if it comes in from the fallback component then the value is a string otherwise it's an array
// which currently only happens if type is role.
if (Array.isArray(value)) {
this.role = value[0];
} else {
this.role = value;
}
}
if (this.args.type === 'secret') {
this.secret = value;
}
this.role = role;
}
}
4 changes: 2 additions & 2 deletions ui/app/components/secret-create-or-update.js
Expand Up @@ -11,7 +11,7 @@
* @modelForData={{@modelForData}}
* @isV2=true
* @secretData={{@secretData}}
* @canCreateSecretMetadata=true
* @canCreateSecretMetadata=false
* />
* ```
* @param {string} mode - create, edit, show determines what view to display
Expand All @@ -20,7 +20,7 @@
* @param {object} modelForData - a class that helps track secret data, defined in secret-edit
* @param {boolean} isV2 - whether or not KV1 or KV2
* @param {object} secretData - class that is created in secret-edit
* @param {boolean} canCreateSecretMetadata - based on permissions to the /metadata/ endpoint. If user has secret create access.
* @param {boolean} canUpdateSecretMetadata - based on permissions to the /metadata/ endpoint. If user has secret update. create is not enough for metadata.
*/

import Component from '@glimmer/component';
Expand Down
3 changes: 2 additions & 1 deletion ui/app/components/secret-edit.js
Expand Up @@ -109,7 +109,8 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, {
'mode'
),
canDeleteSecretMetadata: alias('checkMetadataCapabilities.canDelete'),
canCreateSecretMetadata: alias('checkMetadataCapabilities.canCreate'),
canUpdateSecretMetadata: alias('checkMetadataCapabilities.canUpdate'),
canReadSecretMetadata: alias('checkMetadataCapabilities.canRead'),

requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'),

Expand Down
28 changes: 20 additions & 8 deletions ui/app/routes/vault/cluster/secrets/backend/configuration.js
Expand Up @@ -4,19 +4,31 @@ import Route from '@ember/routing/route';
export default Route.extend({
wizard: service(),
store: service(),
model() {
async model() {
let backend = this.modelFor('vault.cluster.secrets.backend');
if (this.wizard.featureState === 'list') {
this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', backend.get('type'));
}
if (backend.isV2KV) {
// design wants specific default to show that can't be set in the model
backend.set('casRequired', backend.casRequired ? backend.casRequired : 'False');
backend.set(
'deleteVersionAfter',
backend.deleteVersionAfter !== '0s' ? backend.deleteVersionAfter : 'Never delete'
);
backend.set('maxVersions', backend.maxVersions ? backend.maxVersions : 'Not set');
let canRead = await this.store
.findRecord('capabilities', `${backend.id}/config`)
.then(response => response.canRead);
// only set these config params if they can read the config endpoint.
if (canRead) {
// design wants specific default to show that can't be set in the model
backend.set('casRequired', backend.casRequired ? backend.casRequired : 'False');
backend.set(
'deleteVersionAfter',
backend.deleteVersionAfter !== '0s' ? backend.deleteVersionAfter : 'Never delete'
);
backend.set('maxVersions', backend.maxVersions ? backend.maxVersions : 'Not set');
} else {
// remove the default values from the model if they don't have read access otherwise it will display the defaults even if they've been set (because they error on returning config data)
// normally would catch the config error in the secret-v2 adapter, but I need the functions to proceed, not stop. So we remove the values here.
backend.set('casRequired', null);
backend.set('deleteVersionAfter', null);
backend.set('maxVersions', null);
}
}
return backend;
},
Expand Down
6 changes: 6 additions & 0 deletions ui/app/routes/vault/cluster/secrets/backend/list.js
Expand Up @@ -10,6 +10,7 @@ const SUPPORTED_BACKENDS = supportedSecretBackends();
export default Route.extend({
templateName: 'vault/cluster/secrets/backend/list',
pathHelp: service('path-help'),
noMetadataPermissions: false,
queryParams: {
page: {
refreshModel: true,
Expand Down Expand Up @@ -111,6 +112,9 @@ export default Route.extend({
// if we're at the root we don't want to throw
if (backendModel && err.httpStatus === 404 && secret === '') {
return [];
} else if (backendModel.engineType === 'kv' && backendModel.isV2KV) {
this.set('noMetadataPermissions', true);
return [];
} else {
// else we're throwing and dealing with this in the error action
throw err;
Expand Down Expand Up @@ -149,6 +153,7 @@ export default Route.extend({
let backend = this.enginePathParam();
let backendModel = this.store.peekRecord('secret-engine', backend);
let has404 = this.has404;
let noMetadataPermissions = this.noMetadataPermissions;
// only clear store cache if this is a new model
if (secret !== controller.get('baseKey.id')) {
this.store.clearAllDatasets();
Expand All @@ -157,6 +162,7 @@ export default Route.extend({
controller.setProperties({
model,
has404,
noMetadataPermissions,
backend,
backendModel,
baseKey: { id: secret },
Expand Down
18 changes: 14 additions & 4 deletions ui/app/routes/vault/cluster/secrets/backend/metadata.js
Expand Up @@ -3,6 +3,7 @@ import { inject as service } from '@ember/service';

export default class MetadataShow extends Route {
@service store;
noReadAccess = false;

beforeModel() {
const { backend } = this.paramsFor('vault.cluster.secrets.backend');
Expand All @@ -11,14 +12,23 @@ export default class MetadataShow extends Route {

model(params) {
let { secret } = params;
return this.store.queryRecord('secret-v2', {
backend: this.backend,
id: secret,
});
return this.store
.queryRecord('secret-v2', {
backend: this.backend,
id: secret,
})
.catch(error => {
// there was an error likely in read metadata.
// still load the page and handle what you show by filtering for this property
if (error.httpStatus === 403) {
this.noReadAccess = true;
}
});
}

setupController(controller, model) {
controller.set('backend', this.backend); // for backendCrumb
controller.set('model', model);
controller.set('noReadAccess', this.noReadAccess);
}
}
5 changes: 4 additions & 1 deletion ui/app/templates/components/get-credentials-card.hbs
@@ -1,18 +1,21 @@
<form class="selectable-card is-rounded no-flex">
<form class="selectable-card is-rounded no-flex data-test-get-credentials-card">
<div class="is-flex-between is-fullwidth card-details" >
<h3 class="title is-5">{{@title}}</h3>
</div>
<div class="has-top-bottom-margin">
<p class="is-label search-label">{{@searchLabel}}</p>
</div>
<p class="sub-text">{{@subText}}</p>
<SearchSelect
@id={{id}}
@models={{@models}}
@selectLimit='1'
@backend={{@backend}}
@fallbackComponent='input-search'
@shouldUseFallback={{@shouldUseFallback}}
@onChange={{action 'handleRoleInput' }}
@inputValue={{get model valuePath}}
@placeHolder={{@placeHolder}}
data-test-search-roles
/>
<input
Expand Down
1 change: 1 addition & 0 deletions ui/app/templates/components/input-search.hbs
Expand Up @@ -5,6 +5,7 @@
@type="text"
@value={{this.searchInput}}
{{on 'keyup' this.inputChanged}}
@placeholder={{@placeHolder}}
/>
</div>
</div>
6 changes: 4 additions & 2 deletions ui/app/templates/components/secret-create-or-update.hbs
Expand Up @@ -97,7 +97,8 @@
{{/each}}
</div>
{{/if}}
{{#if (and @isV2 @canCreateSecretMetadata) }}
{{!-- must have UPDATE permissions to add secret metadata. Create only will not work --}}
{{#if (and @isV2 @canUpdateSecretMetadata)}}
<ToggleButton
@class="is-block"
@toggleAttr={{"showMetadata"}}
Expand All @@ -113,7 +114,7 @@
@updateValidationErrorCount={{action "updateValidationErrorCount"}}
/>
{{/if}}
{{/if}}
{{/if}}
<div class="field is-grouped box is-fullwidth is-bottomless">
<div class="control">
<button
Expand All @@ -135,6 +136,7 @@
{{/if}}

{{#if (eq @mode "edit")}}
{{!-- no metadata option because metadata is version agnostic --}}
<form onsubmit={{action "createOrUpdateKey" "edit"}}>
<div class="box is-sideless is-fullwidth is-marginless padding-top">
{{#if @model.canReadSecretData}}
Expand Down
5 changes: 3 additions & 2 deletions ui/app/templates/components/secret-edit.hbs
Expand Up @@ -26,7 +26,8 @@
Secret
</LinkTo>
</LinkTo>
{{#if model.canReadMetadata}}
{{!-- must have read access to /metadata see tab or update to update metadata--}}
{{#if (or this.canReadSecretMetadata this.canUpdateSecretMetadata)}}
<LinkTo @route="vault.cluster.secrets.backend.metadata" @model={{key.id}} @tagName="li" @activeClass="is-active" data-test-secret-metadata-tab>
<LinkTo @route="vault.cluster.secrets.backend.metadata">
Metadata
Expand Down Expand Up @@ -65,7 +66,7 @@
@isV2={{isV2}}
@secretData={{secretData}}
@buttonDisabled={{buttonDisabled}}
@canCreateSecretMetadata={{canCreateSecretMetadata}}
@canUpdateSecretMetadata={{canUpdateSecretMetadata}}
/>
{{else if (eq mode "show")}}
<SecretFormShow
Expand Down

0 comments on commit b487486

Please sign in to comment.