Skip to content

Commit

Permalink
Add Response Wrapping For Secrets (#5664)
Browse files Browse the repository at this point in the history
  • Loading branch information
madalynrose authored Nov 12, 2018
1 parent 98ea0a3 commit 5fcd87a
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 60 deletions.
16 changes: 16 additions & 0 deletions ui/app/adapters/secret-v2-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export default ApplicationAdapter.extend({
return this._url(backend, path) + `?version=${version}`;
},

urlForQueryRecord(id) {
let [backend, path, version] = JSON.parse(id);
return this._url(backend, path) + `?version=${version}`;
},

findRecord() {
return this._super(...arguments).catch(errorOrModel => {
// if it's a real 404, this will be an error, if not
Expand All @@ -30,6 +35,17 @@ export default ApplicationAdapter.extend({
});
},

queryRecord(id, options) {
return this.ajax(this.urlForQueryRecord(id), 'GET', options).then(resp => {
if (options.wrapTTL) {
return resp;
}
resp.id = id;
resp.backend = backend;
return resp;
});
},

urlForCreateRecord(modelName, snapshot) {
let backend = snapshot.belongsTo('secret').belongsTo('engine').id;
let path = snapshot.attr('path');
Expand Down
25 changes: 16 additions & 9 deletions ui/app/adapters/secret.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,29 @@ export default ApplicationAdapter.extend({
return url;
},

optionsForQuery(id, action) {
optionsForQuery(id, action, wrapTTL) {
let data = {};
if (action === 'query') {
data['list'] = true;
data.list = true;
}
if (wrapTTL) {
return { data, wrapTTL };
}

return { data };
},

fetchByQuery(query, action) {
const { id, backend } = query;
return this.ajax(this.urlForSecret(backend, id), 'GET', this.optionsForQuery(id, action)).then(resp => {
resp.id = id;
resp.backend = backend;
return resp;
});
const { id, backend, wrapTTL } = query;
return this.ajax(this.urlForSecret(backend, id), 'GET', this.optionsForQuery(id, action, wrapTTL)).then(
resp => {
if (wrapTTL) {
return resp;
}
resp.id = id;
resp.backend = backend;
return resp;
}
);
},

query(store, type, query) {
Expand Down
52 changes: 52 additions & 0 deletions ui/app/components/secret-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default Component.extend(FocusOnInsertMixin, {
wizard: service(),
router: service(),
store: service(),
flashMessages: service(),

// a key model
key: null,
Expand All @@ -31,6 +32,10 @@ export default Component.extend(FocusOnInsertMixin, {

secretData: null,

wrappedData: null,
isWrapping: false,
showWrapButton: computed.not('wrappedData'),

// called with a bool indicating if there's been a change in the secretData
onDataChange() {},
onRefresh() {},
Expand Down Expand Up @@ -235,6 +240,53 @@ export default Component.extend(FocusOnInsertMixin, {
set(this.modelForData, 'secretData', this.secretData.toJSON());
},

handleWrapClick() {
this.set('isWrapping', true);
if (this.isV2) {
this.store
.adapterFor('secret-v2-version')
.queryRecord(this.modelForData.id, { wrapTTL: 1800 })
.then(resp => {
this.set('wrappedData', resp.wrap_info.token);
this.flashMessages.success('Secret Successfully Wrapped!');
})
.catch(() => {
this.flashMessages.error('Could Not Wrap Secret');
})
.finally(() => {
this.set('isWrapping', false);
});
} else {
this.store
.adapterFor('secret')
.queryRecord(null, null, { backend: this.model.backend, id: this.modelForData.id, wrapTTL: 1800 })
.then(resp => {
this.set('wrappedData', resp.wrap_info.token);
this.flashMessages.success('Secret Successfully Wrapped!');
})
.catch(() => {
this.flashMessages.error('Could Not Wrap Secret');
})
.finally(() => {
this.set('isWrapping', false);
});
}
},

clearWrappedData() {
this.set('wrappedData', null);
},

handleCopySuccess() {
this.flashMessages.success('Copied Wrapped Data!');
this.send('clearWrappedData');
},

handleCopyError() {
this.flashMessages.error('Could Not Copy Wrapped Data');
this.send('clearWrappedData');
},

createOrUpdateKey(type, event) {
event.preventDefault();
let model = this.modelForData;
Expand Down
10 changes: 9 additions & 1 deletion ui/app/components/tool-actions-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const DEFAULTS = {
creation_ttl: null,
data: '{\n}',
unwrap_data: null,
details: null,
wrapTTL: null,
sum: null,
random_bytes: null,
Expand All @@ -33,6 +34,7 @@ export default Component.extend(DEFAULTS, {
algorithm: 'sha2-256',

tagName: '',
unwrapActiveTab: 'data',

didReceiveAttrs() {
this._super(...arguments);
Expand Down Expand Up @@ -76,7 +78,13 @@ export default Component.extend(DEFAULTS, {
let props = {};
let secret = (resp && resp.data) || resp.auth;
if (secret && action === 'unwrap') {
props = assign({}, props, { unwrap_data: secret });
let details = {
'Request ID': resp.request_id,
'Lease ID': resp.lease_id || 'None',
Renewable: resp.renewable ? 'Yes' : 'No',
'Lease Duration': resp.lease_duration || 'None',
};
props = assign({}, props, { unwrap_data: secret }, { details: details });
}
props = assign({}, props, secret);
if (resp && resp.wrap_info) {
Expand Down
4 changes: 4 additions & 0 deletions ui/app/styles/components/masked-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
padding-top: $spacing-s;
}

.has-padding {
padding: $size-10 $size-8;
}

// we want to style the boxes the same everywhere so they
// need to be the same font and small
.masked-input.masked .masked-value {
Expand Down
7 changes: 4 additions & 3 deletions ui/app/styles/components/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@

ul {
border-color: transparent;
min-height: 3rem;
}

li {
&:focus {
box-shadow: none;
}
&.is-active a {
&.is-active a, &.is-active .tab {
border-color: $blue;
color: $blue;
}
&:first-child a {
&:first-child a, &:first-child .tab {
margin-left: $size-5;
}
}

a {
a, .tab {
color: $grey-dark;
font-weight: $font-weight-semibold;
text-decoration: none;
Expand Down
1 change: 1 addition & 0 deletions ui/app/templates/components/masked-input.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<CopyButton
@clipboardText={{value}}
@class="copy-button button is-compact"
@success={{success}}
data-test-copy-button
>
<ICon @glyph="copy" aria-hidden="true" @size=16 />
Expand Down
119 changes: 88 additions & 31 deletions ui/app/templates/components/secret-edit.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<ConfirmAction
@buttonClasses="button is-compact is-ghost has-icon-right"
@onConfirmAction={{action "deleteKey"}}
@confirmMessage={{if isV2
@confirmMessage={{if isV2
(concat "This will permanently delete " model.id " and all its versions. Are you sure you want to do this?")
(concat "Are you sure you want to delete " model.id "?")
}}
Expand All @@ -53,45 +53,102 @@
<label for="json" class="has-text-grey">JSON</label>
</div>
{{#if (and (eq mode 'show') (or canEditV2Secret canEdit))}}
<div class="control">
{{#let (concat 'vault.cluster.secrets.backend.' (if (eq mode 'show') 'edit' 'show')) as |targetRoute|}}
{{#if isV2}}
<LinkTo
@params={{array targetRoute model.id (query-params version=this.modelForData.version)}}
@replace={{true}}
class="link link-plain has-text-weight-semibold"
data-test-secret-edit="true"
>
Create new version
</LinkTo>
{{else}}
<LinkTo
@params={{array targetRoute model.id}}
@replace={{true}}
class="link link-plain has-text-weight-semibold"
data-test-secret-edit="true"
>
Edit Secret
</LinkTo>
{{#unless (and isV2 (or modelForData.destroyed modelForData.deleted))}}
<div class="control">
<BasicDropdown
@class="popup-menu"
@horizontalPosition="auto-right"
@verticalPosition="below"
@onClose={{action "clearWrappedData"}}
as |D|
>
<D.trigger
data-test-popup-menu-trigger="true"
@class={{concat "link link-plain has-text-weight-semibold" (if D.isOpen " is-active")}}
@tagName="button"
>
Copy Secret
</D.trigger>
<D.content @class="popup-menu-content is-wide">
<nav class="box menu">
<ul class="menu-list">
<li class="action">
<CopyButton
@class="link link-plain has-text-weight-semibold is-ghost"
@clipboardText={{codemirrorString}}
@success={{action (set-flash-message "JSON Copied!")}}
data-test-copy-button
>
Copy JSON
</CopyButton>
</li>
<li class="action">
{{#if showWrapButton}}
<button
class="link link-plain has-text-weight-semibold is-ghost {{if isWrapping "is-loading"}}"
type="button"
{{action "handleWrapClick"}}
data-test-wrap-button
disabled={{isWrapping}}
>
Wrap Secret
</button>
{{else}}
<MaskedInput
@class="has-padding"
@displayOnly={{true}}
@allowCopy={{true}}
@value={{wrappedData}}
@success={{action "handleCopySuccess"}}
@error={{action "handleCopyError"}}
/>
{{/if}}
</li>
</ul>
</nav>
</D.content>
</BasicDropdown>
</div>
{{/unless}}
<div class="control">
{{#if isV2}}
<LinkTo
@params={{array targetRoute model.id (query-params version=this.modelForData.version)}}
@replace={{true}}
class="link link-plain has-text-weight-semibold"
data-test-secret-edit="true"
>
Create new version
</LinkTo>
{{else}}
<LinkTo
@params={{array targetRoute model.id}}
@replace={{true}}
class="link link-plain has-text-weight-semibold"
data-test-secret-edit="true"
>
Edit Secret
</LinkTo>
{{/if}}
{{/let}}
</div>
</div>
{{/let}}
{{/if}}
{{#if (and (eq @mode "show") this.isV2)}}
<div class="control">
<SecretVersionMenu @version={{this.modelForData}} />
</div>
<div class="control">
<BasicDropdown
@class="popup-menu"
@horizontalPosition="auto-right"
@verticalPosition="below"
<BasicDropdown
@class="popup-menu"
@horizontalPosition="auto-right"
@verticalPosition="below"
as |D|
>
<D.trigger
<D.trigger
data-test-popup-menu-trigger="true"
@class={{concat "popup-menu-trigger button is-ghost has-text-grey" (if D.isOpen " is-active")}}
@tagName="button"
@class={{concat "popup-menu-trigger button is-ghost has-text-grey" (if D.isOpen " is-active")}}
@tagName="button"
>
History <ICon @glyph="chevron-right" @size="11" />
</D.trigger>
Expand All @@ -103,8 +160,8 @@
@mode="versions"
@secret={{this.model.id}}
@class="has-text-black has-text-weight-semibold has-bottom-shadow"
>
View version history
>
View version history
</SecretLink>
</li>
</ul>
Expand Down
Loading

0 comments on commit 5fcd87a

Please sign in to comment.