Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap secrets #5664

Merged
merged 35 commits into from
Nov 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
62e4133
add menu for copying secrets, implement copying json of secret
Nov 1, 2018
f867433
add wrapped secret copy funcitonality
Nov 1, 2018
6a57da6
add wrap details tab to tools unwrap page
Nov 1, 2018
8345e5c
add menu for copying secrets, implement copying json of secret
Nov 1, 2018
46de9c8
add wrapped secret copy funcitonality
Nov 1, 2018
a9452b2
add wrap details tab to tools unwrap page
Nov 1, 2018
1b5f1d8
wrap secrets properly for kv v2
Nov 5, 2018
277f84b
use clipboard polyfill to copy wrapped
Nov 5, 2018
a319610
fix conflict
Nov 5, 2018
618f320
Merge branch 'master' of github.com:hashicorp/vault into wrap-secrets
Nov 5, 2018
d88b340
resolve merge nonsense
Nov 5, 2018
0d7922a
tidy up styling for unwrap page
Nov 5, 2018
953424a
use modelForData
Nov 5, 2018
df3f893
simplify copy dropdown, break wrapping into two steps
Nov 6, 2018
6111d62
upgrade yarn and regenerate yarn.lock
Nov 6, 2018
0a4947e
only allow copy for secret versions that are not deleted or destroyed
Nov 6, 2018
055933f
show None and No next to wrap details that don't exist
Nov 6, 2018
adc5a01
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
ad94214
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
a93c347
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
a040d28
reset packages, toggle isWrapping even if it errors
Nov 7, 2018
4b123f0
Merge branch 'wrap-secrets' of github.com:hashicorp/vault into wrap-s…
Nov 7, 2018
0feb677
simplify showWrapButton logic, change unwrap tabs to buttons
Nov 7, 2018
1607e5f
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
4d64235
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
30a384d
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
1925af7
Merge branch 'master' into wrap-secrets
madalynrose Nov 7, 2018
286ac71
Merge branch 'master' into wrap-secrets
madalynrose Nov 8, 2018
a3f254d
Merge branch 'master' into wrap-secrets
madalynrose Nov 8, 2018
ff9ef07
Merge branch 'master' of github.com:hashicorp/vault into wrap-secrets
Nov 8, 2018
096d536
evaluate showing wrap secret button on wrapped data
Nov 8, 2018
309bc1a
Merge branch 'master' into wrap-secrets
madalynrose Nov 9, 2018
76ea70d
oops lost data-test attributes on merge
Nov 9, 2018
a82f0c2
Merge branch 'master' into wrap-secrets
madalynrose Nov 12, 2018
5af1563
Merge branch 'master' into wrap-secrets
madalynrose Nov 12, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this get reset or nulled out somewhere? Just thinking through how/why it doesn't stay through multiple popup openings.

this.flashMessages.success('Secret Successfully Wrapped!');
})
.catch(() => {
this.flashMessages.error('Could Not Wrap Secret');
})
.finally(() => {
this.set('isWrapping', false);
});
madalynrose marked this conversation as resolved.
Show resolved Hide resolved
}
},

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 });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't need these changes if we move to reading with wrapTTL.

}
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes not sure why I had all of this trailing whitespace the first time

@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