Ui/transit modal (#8575)
* wip -- add modal component using ember-wormhole, add static content but still need to enable onClose * add onClose to modal * WIP * add copy and close button * add copy and close button * and copy and close button to modal * use modal on each key action page * make text copied text more generic * update datakey textareas to codemirror * only show user input on encrypt and decrypt * only show user input on all key actions * separate copy ciphertext, plaintext, and close button on datakey modal * style ciphertext and plaintext as code * only show separate copy buttons on datakey modal if both outputs are shown * update modal styling * style modal * add descriptions to each key action * remove conditional from hmac modal since we only ever show hmac output * add modal for export key action * make output scroll horizontally with copy button next to it * make output scroll horizontally with copy button next to it * escape & in copy and close button, format text output so it scrolls horizontally * fix formatting of key action descriptions * Ui/add transit modal tests (#8523) * Fix tests for updated transit with modals workflow * WIP // remove box shadow from key actions descriptions * WIP // flash messages on successful action match mocks * WIP // remove ciphertext view after datakey created * WIP // make flash messages when copy & closing less generic, and match copy flash message * WIP // Optionally show close button on modal, with tests * remove unused deps from modal test * WIP // Fix verify modal styling and content * Add modal for sign action * Fix output of non-wrapped export key * Fix output of non-wrapped export key * Add description to JSDOCS about modal component * Add help text about plaintext encoded in base64 * add flash msgs for datakey and export * flash success msg when closing modal on export page * clarify sign success msg * address PR feedback * add indentation for export key json * Fix modal tests pt 2 * Remove decode after decrypt in transit tests Co-authored-by: Chelsea Shaw <chelshaw.dev@gmail.com>
This commit is contained in:
parent
a69167ac11
commit
e11567be82
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @module Modal
|
||||
* Modal components are used to overlay content on top of the page. Has a darkened background,
|
||||
* a title, and in order to close it you must pass an onClose function.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <Modal @title={'myTitle'} @showCloseButton={true} @onClose={() => {}}/>
|
||||
* ```
|
||||
* @param {function} onClose - onClose is the action taken when someone clicks the modal background or close button (if shown).
|
||||
* @param {string} [title] - This text shows up in the header section of the modal.
|
||||
* @param {boolean} [showCloseButton=false] - controls whether the close button in the top right corner shows.
|
||||
*/
|
||||
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
title: null,
|
||||
showCloseButton: false,
|
||||
onClose: () => {},
|
||||
});
|
|
@ -43,12 +43,24 @@ const PARAMS_FOR_ACTION = {
|
|||
decrypt: ['ciphertext', 'context', 'nonce'],
|
||||
rewrap: ['ciphertext', 'context', 'nonce', 'key_version'],
|
||||
};
|
||||
const SUCCESS_MESSAGE_FOR_ACTION = {
|
||||
sign: 'Signed your data',
|
||||
// the verify action doesn't trigger a success message
|
||||
hmac: 'Created your hash output',
|
||||
encrypt: 'Created a wrapped token for your data',
|
||||
decrypt: 'Decrypted the data from your token',
|
||||
rewrap: 'Created a new token for your data',
|
||||
datakey: 'Generated your key',
|
||||
export: 'Exported your key',
|
||||
};
|
||||
export default Component.extend(TRANSIT_PARAMS, {
|
||||
store: service(),
|
||||
flashMessages: service(),
|
||||
|
||||
// public attrs
|
||||
selectedAction: null,
|
||||
key: null,
|
||||
isModalActive: false,
|
||||
|
||||
onRefresh() {},
|
||||
init() {
|
||||
|
@ -137,6 +149,12 @@ export default Component.extend(TRANSIT_PARAMS, {
|
|||
this.set('errors', null);
|
||||
},
|
||||
|
||||
triggerSuccessMessage(action) {
|
||||
const message = SUCCESS_MESSAGE_FOR_ACTION[action];
|
||||
if (!message) return;
|
||||
this.get('flashMessages').success(message);
|
||||
},
|
||||
|
||||
handleSuccess(resp, options, action) {
|
||||
let props = {};
|
||||
if (resp && resp.data) {
|
||||
|
@ -149,10 +167,12 @@ export default Component.extend(TRANSIT_PARAMS, {
|
|||
if (options.wrapTTL) {
|
||||
props = assign({}, props, { wrappedToken: resp.wrap_info.token });
|
||||
}
|
||||
this.toggleProperty('isModalActive');
|
||||
this.setProperties(props);
|
||||
if (action === 'rotate') {
|
||||
this.get('onRefresh')();
|
||||
}
|
||||
this.triggerSuccessMessage(action);
|
||||
},
|
||||
|
||||
compactData(data) {
|
||||
|
@ -184,6 +204,13 @@ export default Component.extend(TRANSIT_PARAMS, {
|
|||
arr.forEach(param => this.set(param, null));
|
||||
},
|
||||
|
||||
toggleModal(successMessage) {
|
||||
if (!!successMessage && typeof successMessage === 'string') {
|
||||
this.get('flashMessages').success(successMessage);
|
||||
}
|
||||
this.toggleProperty('isModalActive');
|
||||
},
|
||||
|
||||
doSubmit(data, options = {}) {
|
||||
const { backend, id } = this.getModelInfo();
|
||||
const action = this.get('selectedAction');
|
||||
|
@ -192,7 +219,7 @@ export default Component.extend(TRANSIT_PARAMS, {
|
|||
if (action === 'encrypt' && !!formData.plaintext) {
|
||||
formData.plaintext = encodeString(formData.plaintext);
|
||||
}
|
||||
if ((action === 'hmac' || action === 'verify') && !!formData.input) {
|
||||
if ((action === 'hmac' || action === 'verify' || action === 'sign') && !!formData.input) {
|
||||
formData.input = encodeString(formData.input);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
.modal-background {
|
||||
background: rgb(235, 238, 242, 0.9);
|
||||
}
|
||||
|
||||
.modal-card {
|
||||
box-shadow: $box-shadow-highest;
|
||||
border: 1px solid $grey-light;
|
||||
|
||||
&-head {
|
||||
border-radius: 0;
|
||||
background-color: $grey-lightest;
|
||||
border-bottom: 1px solid $grey-light;
|
||||
}
|
||||
|
||||
&-foot {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
&-title.title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.copy-text {
|
||||
background-color: $grey-lightest;
|
||||
padding: $spacing-s;
|
||||
margin-bottom: $spacing-s;
|
||||
|
||||
code {
|
||||
overflow: scroll;
|
||||
max-width: calc(100% - 36px);
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.copy-close {
|
||||
margin-top: $spacing-s;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: inherit;
|
||||
}
|
|
@ -9,10 +9,11 @@
|
|||
|
||||
.transit-card {
|
||||
border-radius: $radius;
|
||||
box-shadow: 0 0 0 1px rgba($grey-dark, 0.3), $box-shadow-middle;
|
||||
box-shadow: 0 0 0 1px rgba($grey-light, 0.4);
|
||||
display: grid;
|
||||
grid-template-columns: 0.45fr 2fr;
|
||||
padding: $spacing-m;
|
||||
border: none;
|
||||
|
||||
.transit-icon {
|
||||
justify-self: center;
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
@import './components/loader';
|
||||
@import './components/login-form';
|
||||
@import './components/masked-input';
|
||||
@import './components/modal';
|
||||
@import './components/namespace-picker';
|
||||
@import './components/namespace-reminder';
|
||||
@import './components/navigate-input';
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{{#ember-wormhole to="modal-wormhole"}}
|
||||
<div class="modal {{if isActive 'is-active'}}" aria-modal="true">
|
||||
<div class="modal-background" onclick={{onClose}} data-test-modal-background></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<h2 class="modal-card-title title is-5" data-test-modal-title>{{title}}</h2>
|
||||
{{#if showCloseButton}}
|
||||
<button class="delete" aria-label="close" onclick={{onClose}} data-test-modal-close-button></button>
|
||||
{{/if}}
|
||||
</header>
|
||||
{{yield}}
|
||||
</div>
|
||||
</div>
|
||||
{{/ember-wormhole}}
|
|
@ -1,124 +1,126 @@
|
|||
<form {{action 'doSubmit' (hash param=param context=context nonce=nonce bits=bits) on="submit"}}>
|
||||
{{#if ciphertext}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{#if (eq param 'plaintext')}}
|
||||
<NamespaceReminder @mode="perform" @noun="datakey creation" />
|
||||
<div class="content">
|
||||
<p>Generate a new high-entropy key and value using <code>{{key.name}}</code> as the encryption key.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="plaintext" class="is-label">Plaintext</label>
|
||||
<div class="control">
|
||||
<textarea readonly class="textarea" id="plaintext">{{plaintext}}</textarea>
|
||||
<label for="param" class="is-label">Output format</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="param"
|
||||
id="param"
|
||||
onchange={{action (mut param) value="target.value"}}
|
||||
>
|
||||
{{#each (array "plaintext" "wrapped") as |paramOption|}}
|
||||
<option selected={{eq param paramOption}} value={{paramOption}}>
|
||||
{{paramOption}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if key.derived}}
|
||||
<div class="field">
|
||||
<label for="context" class="is-label">
|
||||
Context
|
||||
</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="context" value=context class="input" data-test-transit-input="context"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=context data-test-transit-b64-toggle="context"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq key.convergentEncryptionVersion 1)}}
|
||||
<div class="field">
|
||||
<label for="nonce" class="is-label">Nonce</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="nonce" value=nonce class="input" data-test-transit-input="nonce"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=nonce data-test-transit-b64-toggle="nonce"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field">
|
||||
<label for="ciphertext" class="is-label">Ciphertext</label>
|
||||
<div class="control">
|
||||
<textarea readonly class="textarea" id="ciphertext">{{ciphertext}}</textarea>
|
||||
<label for="bits" class="is-label">Bits</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="bits"
|
||||
id="bits"
|
||||
onchange={{action (mut bits) value="target.value"}}
|
||||
>
|
||||
{{#each (array 128 256 512) as |bitOption|}}
|
||||
<option selected={{eq bits bitOption}} value={{bitOption}}>
|
||||
<code>{{bitOption}}</code>
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
{{#if (eq param 'plaintext')}}
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardTarget="#plaintext"
|
||||
class="button is-primary"
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message 'Plaintext copied!'))
|
||||
}}
|
||||
Copy plaintext
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardTarget="#ciphertext"
|
||||
class=(concat "button is-primary " (if (eq param "plaintext") "is-outlined" ""))
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message 'Ciphertext copied!'))
|
||||
}}
|
||||
Copy ciphertext
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button {{action 'onClear'}} type="button" class="button">
|
||||
Back
|
||||
<button type="submit" class="button is-primary">
|
||||
Create datakey
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="datakey creation" />
|
||||
<div class="field">
|
||||
<label for="param" class="is-label">Output format</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="param"
|
||||
id="param"
|
||||
onchange={{action (mut param) value="target.value"}}
|
||||
>
|
||||
{{#each (array "plaintext" "wrapped") as |paramOption|}}
|
||||
<option selected={{eq param paramOption}} value={{paramOption}}>
|
||||
{{paramOption}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if key.derived}}
|
||||
<div class="field">
|
||||
<label for="context" class="is-label">
|
||||
Context
|
||||
</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="context" value=context class="input" data-test-transit-input="context"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=context data-test-transit-b64-toggle="context"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq key.convergentEncryptionVersion 1)}}
|
||||
<div class="field">
|
||||
<label for="nonce" class="is-label">Nonce</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="nonce" value=nonce class="input" data-test-transit-input="nonce"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=nonce data-test-transit-b64-toggle="nonce"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field">
|
||||
<label for="bits" class="is-label">Bits</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="bits"
|
||||
id="bits"
|
||||
onchange={{action (mut bits) value="target.value"}}
|
||||
>
|
||||
{{#each (array 128 256 512) as |bitOption|}}
|
||||
<option selected={{eq bits bitOption}} value={{bitOption}}>
|
||||
<code>{{bitOption}}</code>
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
Create datakey
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
<Modal @title="Copy your generated key" @onClose={{action (mut isModalActive) false}} @isActive={{isModalActive}}>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
{{#if (eq param 'plaintext')}}
|
||||
<h2 class="title is-6">Plaintext</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left">{{plaintext}}</code>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy"
|
||||
@clipboardText={{plaintext}} @buttonType="button" @success={{action (set-flash-message 'Plaintext copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
<p class="help has-bottom-margin">Plaintext is base64 encoded</p>
|
||||
<h2 class="title is-6">Ciphertext</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left">{{ciphertext}}</code>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy"
|
||||
@clipboardText={{ciphertext}} @buttonType="button" @success={{action (set-flash-message 'Ciphertext copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
<CopyButton class="button is-primary" data-test-button="modal-copy-close" @clipboardText={{plaintext}}
|
||||
@buttonType="button" @success={{action (set-flash-message 'Plaintext copied!')}}>
|
||||
Copy Plaintext
|
||||
</CopyButton>
|
||||
<CopyButton class="button is-primary" data-test-button="modal-copy-close" @clipboardText={{ciphertext}}
|
||||
@buttonType="button" @success={{action (set-flash-message 'Ciphertext copied!')}}>
|
||||
Copy Ciphertext
|
||||
</CopyButton>
|
||||
<button type="submit" class="button is-secondary" onclick={{action (mut isModalActive) false}}>Close</button>
|
||||
{{else}}
|
||||
<h2 class="title is-6">Ciphertext</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left">{{ciphertext}}</code>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy"
|
||||
@clipboardText={{ciphertext}} @buttonType="button" @success={{action (set-flash-message 'Ciphertext copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
<CopyButton class="button is-primary copy-close" data-test-button="modal-copy-close" @clipboardText={{ciphertext}}
|
||||
@buttonType="button" @success={{action "toggleModal" "Ciphertext copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
{{/if}}
|
||||
</div>
|
||||
</section>
|
||||
</Modal>
|
||||
|
|
|
@ -1,96 +1,80 @@
|
|||
<form {{action 'doSubmit' (hash ciphertext=ciphertext context=context nonce=nonce) on="submit"}}>
|
||||
{{#if (and plaintext ciphertext)}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="plaintext" class="is-label">
|
||||
Plaintext
|
||||
</label>
|
||||
<div id="plaintext-control" class="control is-relative">
|
||||
{{ivy-codemirror
|
||||
value=plaintext
|
||||
options=(hash
|
||||
readOnly=true
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="plaintext"
|
||||
}}
|
||||
{{b64-toggle value=plaintext isInput=false initialEncoding="base64" data-test-transit-b64-toggle="plaintext"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="content">
|
||||
<p>You can decrypt ciphertext using <code>{{key.name}}</code> as the encryption key.</p>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardTarget="#plaintext"
|
||||
class="button is-primary"
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message 'Plaintext copied!'))
|
||||
<div class="field">
|
||||
<label for="ciphertext" class="is-label">Ciphertext</label>
|
||||
<div id="ciphertext-control" class="control">
|
||||
{{ivy-codemirror
|
||||
valueUpdated=(action (mut ciphertext))
|
||||
options=(hash
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="ciphertext"
|
||||
}}
|
||||
Copy
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button {{action 'onClear'}} type="button" class="button">
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{#if key.derived}}
|
||||
<div class="field">
|
||||
<label for="ciphertext" class="is-label">Ciphertext</label>
|
||||
<div id="ciphertext-control" class="control">
|
||||
{{ivy-codemirror
|
||||
value=ciphertext
|
||||
valueUpdated=(action (mut ciphertext))
|
||||
options=(hash
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="ciphertext"
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
{{#if key.derived}}
|
||||
<div class="field">
|
||||
<label for="context" class="is-label">
|
||||
Context
|
||||
</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="context" value=context class="input" data-test-transit-input="context"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=context data-test-transit-b64-toggle="context"}}
|
||||
</div>
|
||||
<label for="context" class="is-label">
|
||||
Context
|
||||
</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="context" value=context class="input" data-test-transit-input="context"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=context data-test-transit-b64-toggle="context"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq key.convergentEncryptionVersion 1)}}
|
||||
<div class="field">
|
||||
<label for="nonce" class="is-label">Nonce</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="nonce" value=nonce class="input" data-test-transit-input="nonce"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=nonce data-test-transit-b64-toggle="nonce"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq key.convergentEncryptionVersion 1)}}
|
||||
<div class="field">
|
||||
<label for="nonce" class="is-label">Nonce</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="nonce" value=nonce class="input" data-test-transit-input="nonce"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=nonce data-test-transit-b64-toggle="nonce"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary" id="decrypt" data-test-button-decrypt>
|
||||
Decrypt
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary" id="decrypt" data-test-button-decrypt>
|
||||
Decrypt
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
{{#if isModalActive}}
|
||||
<Modal @title="Copy your unwrapped data" @onClose={{action (mut isModalActive) false}} @isActive={{isModalActive}}>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<h2 class="title is-6">Plaintext</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left" data-test-encrypted-value="plaintext">{{plaintext}}</code>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy" @clipboardText={{plaintext}}
|
||||
@buttonType="button" @success={{action (set-flash-message 'Plaintext copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
<p class="help">Plaintext is base64 encoded</p>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<CopyButton class="button is-primary copy-close" data-test-button="modal-copy-close" @clipboardText={{plaintext}}
|
||||
@buttonType="button" @success={{action "toggleModal" "Plaintext copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
</footer>
|
||||
</Modal>
|
||||
{{/if}}
|
||||
|
|
|
@ -1,43 +1,9 @@
|
|||
<form {{action 'doSubmit' (hash plaintext=plaintext context=context nonce=nonce key_version=key_version encodedBase64=encodedBase64) on="submit"}}>
|
||||
{{#if (and plaintext ciphertext)}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="ciphertext" class="is-label">Ciphertext</label>
|
||||
<div id="ciphertext-control" class="control is-expanded">
|
||||
{{ivy-codemirror
|
||||
value=ciphertext
|
||||
options=(hash
|
||||
readOnly=true
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="ciphertext"
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardText=ciphertext
|
||||
class="button is-primary"
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message 'Ciphertext copied!'))
|
||||
}}
|
||||
Copy
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button {{action "onClear"}} type="button" class="button" data-test-encrypt-back-button>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="encryption" />
|
||||
<div class="content">
|
||||
<p>You can encrypt plaintext data using <code>{{key.name}}</code> as the encryption key.</p>
|
||||
</div>
|
||||
{{key-version-select
|
||||
key=key
|
||||
onVersionChange=(action (mut key_version))
|
||||
|
@ -103,5 +69,36 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
<Modal
|
||||
@title="Copy your token"
|
||||
@onClose={{action (mut isModalActive) false}}
|
||||
@isActive={{isModalActive}}
|
||||
data-test-encrypt-modal>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<h2 class="title is-6">Ciphertext</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left" data-test-encrypted-value="ciphertext">{{ciphertext}}</code>
|
||||
<CopyButton
|
||||
class="button is-compact is-transparent level-right"
|
||||
data-test-button="modal-copy"
|
||||
@clipboardText={{ciphertext}}
|
||||
@buttonType="button"
|
||||
@success={{action (set-flash-message 'Ciphertext copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<CopyButton
|
||||
class="button is-primary copy-close"
|
||||
data-test-button="modal-copy-close"
|
||||
@clipboardText={{ciphertext}}
|
||||
@buttonType="button"
|
||||
@success={{action "toggleModal" "Ciphertext copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
</footer>
|
||||
</Modal>
|
||||
|
|
|
@ -3,101 +3,86 @@
|
|||
(hash wrapTTL=wrapTTL)
|
||||
on="submit" }}
|
||||
>
|
||||
{{#if (or keys wrappedToken) }}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
{{#if wrapTTL}}
|
||||
<label for="export" class="is-label">Wrapped Key</label>
|
||||
<div class="control">
|
||||
<textarea readonly nowrap wrap="off" class="textarea" id="export">{{wrappedToken}}</textarea>
|
||||
</div>
|
||||
{{else}}
|
||||
<label class="is-label">Exported Key</label>
|
||||
{{json-editor
|
||||
value=(stringify keys)
|
||||
options=(hash
|
||||
readOnly=true
|
||||
)
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="content">
|
||||
<p>Export a key using <code>{{key.name}}</code> as the encryption key.</p>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardText=(if wrapTTL wrappedToken (stringify keys))
|
||||
class="button is-primary"
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message (if wrapTTL 'Wrapped key copied!' 'Exported key copied!')))
|
||||
}}
|
||||
Copy
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button {{action 'onClear'}} type="button" class="button">
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="type" class="is-label">Key type</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="type"
|
||||
id="type"
|
||||
onchange={{action (mut exportKeyType) value="target.value"}}
|
||||
>
|
||||
{{#each key.exportKeyTypes as |currOption|}}
|
||||
<option selected={{eq exportKeyType currOption}} value={{currOption}}>
|
||||
<code>{{currOption}}</code>
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="type" class="is-label">Key type</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="type"
|
||||
id="type"
|
||||
onchange={{action (mut exportKeyType) value="target.value"}}
|
||||
>
|
||||
{{#each key.exportKeyTypes as |currOption|}}
|
||||
<option selected={{eq exportKeyType currOption}} value={{currOption}}>
|
||||
<code>{{currOption}}</code>
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="b-checkbox">
|
||||
{{input type="checkbox" name="exportVersion" id="exportVersion" class="styled" checked=exportVersion}}
|
||||
<label for="exportVersion" class="is-label">
|
||||
Export a single version
|
||||
</label>
|
||||
</div>
|
||||
{{#if exportVersion}}
|
||||
<div class="field">
|
||||
<label for="version" class="is-label">Version</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="version"
|
||||
id="version"
|
||||
onchange={{action (mut exportKeyVersion) value="target.value"}}
|
||||
>
|
||||
{{#each key.validKeyVersions as |versionOption|}}
|
||||
<option selected={{eq exportKeyVersion versionOption}} value={{versionOption}}>
|
||||
<code>{{versionOption}}</code>
|
||||
{{#if (eq key.validKeyVersions.lastObject versionOption)}}
|
||||
<span> (latest) </span>
|
||||
{{/if}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="b-checkbox">
|
||||
{{input type="checkbox" name="exportVersion" id="exportVersion" class="styled" checked=exportVersion}}
|
||||
<label for="exportVersion" class="is-label">
|
||||
Export a single version
|
||||
</label>
|
||||
</div>
|
||||
{{#if exportVersion}}
|
||||
<div class="field">
|
||||
<label for="version" class="is-label">Version</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="version"
|
||||
id="version"
|
||||
onchange={{action (mut exportKeyVersion) value="target.value"}}
|
||||
>
|
||||
{{#each key.validKeyVersions as |versionOption|}}
|
||||
<option selected={{eq exportKeyVersion versionOption}} value={{versionOption}}>
|
||||
<code>{{versionOption}}</code>
|
||||
{{#if (eq key.validKeyVersions.lastObject versionOption)}}
|
||||
<span> (latest) </span>
|
||||
{{/if}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{wrap-ttl onChange=(action (mut wrapTTL))}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
Export key
|
||||
</button>
|
||||
</div>
|
||||
{{wrap-ttl onChange=(action (mut wrapTTL))}}
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
Export key
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
<Modal @title="Copy your wrapped key" @onClose={{action (mut isModalActive) false}} @isActive={{isModalActive}}>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<h2 class="title is-6">Wrapped Key</h2>
|
||||
<div class="copy-text level">
|
||||
<pre data-test-encrypted-value="export" class="level-left">{{if wrapTTL wrappedToken (stringify keys)}}</pre>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy"
|
||||
@clipboardText={{if wrapTTL wrappedToken (stringify keys)}} @buttonType="button" @success={{action (set-flash-message 'Token copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<CopyButton class="button is-primary copy-close" data-test-button="modal-copy-close" @clipboardText={{if wrapTTL wrappedToken (stringify keys)}}
|
||||
@buttonType="button" @success={{action "toggleModal" "Token copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
</footer>
|
||||
</Modal>
|
||||
|
|
|
@ -1,98 +1,83 @@
|
|||
<form {{action 'doSubmit' (hash input=input algorithm=algorithm key_version=key_version) on="submit"}}>
|
||||
{{#if hmac}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="hmac" class="is-label">HMAC</label>
|
||||
<div id="hmac-control" class="control">
|
||||
{{ivy-codemirror
|
||||
value=hmac
|
||||
options=(hash
|
||||
readOnly=true
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="HMAC creation" />
|
||||
<div class="content">
|
||||
<p>Generate the digest of given data using the specified hash algorithm and <code>{{key.name}}</code> as the named key.</p>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardText=hmac
|
||||
class="button is-primary"
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message 'HMAC copied!'))
|
||||
{{key-version-select
|
||||
key=key
|
||||
onVersionChange=(action (mut key_version))
|
||||
key_version=key_version
|
||||
}}
|
||||
<div class="field">
|
||||
<label for="input" class="is-label">
|
||||
Input
|
||||
</label>
|
||||
<div id="input-control" class="control is-relative">
|
||||
{{ivy-codemirror
|
||||
valueUpdated=(action (mut input))
|
||||
options=(hash
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="input"
|
||||
}}
|
||||
Copy
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button {{action 'onClear'}} type="button" class="button">
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="HMAC creation" />
|
||||
{{key-version-select
|
||||
key=key
|
||||
onVersionChange=(action (mut key_version))
|
||||
key_version=key_version
|
||||
}}
|
||||
<div class="field">
|
||||
<label for="input" class="is-label">
|
||||
Input
|
||||
</label>
|
||||
<div id="input-control" class="control is-relative">
|
||||
{{ivy-codemirror
|
||||
value=input
|
||||
valueUpdated=(action (mut input))
|
||||
options=(hash
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="input"
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
{{input
|
||||
type="checkbox"
|
||||
id="encodedBase64"
|
||||
checked=encodedBase64
|
||||
data-test-transit-input="encodedBase64" }}
|
||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="algorithm" class="is-label">Hash Algorithm</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="algorithm"
|
||||
id="algorithm"
|
||||
onchange={{action (mut algorithm) value="target.value"}}
|
||||
>
|
||||
{{#each (sha2-digest-sizes) as |algo|}}
|
||||
<option selected={{if algorithm (eq algorithm algo)}} value={{algo}}>
|
||||
<code>{{algo}}</code>
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
{{input
|
||||
type="checkbox"
|
||||
id="encodedBase64"
|
||||
checked=encodedBase64
|
||||
data-test-transit-input="encodedBase64" }}
|
||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="algorithm" class="is-label">Hash Algorithm</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="algorithm"
|
||||
id="algorithm"
|
||||
onchange={{action (mut algorithm) value="target.value"}}
|
||||
>
|
||||
{{#each (sha2-digest-sizes) as |algo|}}
|
||||
<option selected={{if algorithm (eq algorithm algo)}} value={{algo}}>
|
||||
<code>{{algo}}</code>
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
HMAC
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
HMAC
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
<Modal @title="Copy your unwrapped data" @onClose={{action (mut isModalActive) false}} @isActive={{isModalActive}}>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<h2 class="title is-6">HMAC</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left" data-test-encrypted-value="hmac">{{hmac}}</code>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy"
|
||||
@clipboardText={{hmac}} @buttonType="button" @success={{action (set-flash-message 'HMAC copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<CopyButton class="button is-primary copy-close" data-test-button="modal-copy-close" @clipboardText={{hmac}}
|
||||
@buttonType="button" @success={{action "toggleModal" "HMAC copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
</footer>
|
||||
</Modal>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<form {{action 'doSubmit' (hash ciphertext=ciphertext context=context nonce=nonce key_version=key_version) on="submit"}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="rewrap" />
|
||||
<div class="content">
|
||||
<p>You can rewrap the provided ciphertext using the latest version of <code>{{key.name}}</code> as the encryption key.</p>
|
||||
</div>
|
||||
{{key-version-select
|
||||
key=key
|
||||
onVersionChange=(action (mut key_version))
|
||||
|
@ -10,7 +13,6 @@
|
|||
<label for="ciphertext" class="is-label">Ciphertext</label>
|
||||
<div class="control is-expanded">
|
||||
{{ivy-codemirror
|
||||
value=ciphertext
|
||||
valueUpdated=(action (mut ciphertext))
|
||||
options=(hash
|
||||
lineNumbers=true
|
||||
|
@ -63,3 +65,23 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<Modal @title="Copy your token" @onClose={{action (mut isModalActive)}} @isActive={{isModalActive}}>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<h2 class="title is-6">Ciphertext</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left" data-test-encrypted-value="ciphertext">{{ciphertext}}</code>
|
||||
<CopyButton class="button is-compact is-transparent level-right" data-test-button="modal-copy"
|
||||
@clipboardText={{ciphertext}} @buttonType="button" @success={{action (set-flash-message 'Ciphertext copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<CopyButton class="button is-primary copy-close" data-test-button="modal-copy-close" @clipboardText={{ciphertext}}
|
||||
@buttonType="button" @success={{action "toggleModal" "Ciphertext copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
</footer>
|
||||
</Modal>
|
||||
|
|
|
@ -1,118 +1,138 @@
|
|||
<form {{action 'doSubmit' (hash input=input hash_algorithm=hash_algorithm signature_algorithm=signature_algorithm key_version=key_version context=context prehashed=prehashed) on="submit"}}>
|
||||
{{#if signature}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="signature" class="is-label">Signature</label>
|
||||
<div class="control">
|
||||
<textarea readonly class="textarea" id="signature">{{signature}}</textarea>
|
||||
</div>
|
||||
<form {{action 'doSubmit' (hash input=input hash_algorithm=hash_algorithm signature_algorithm=signature_algorithm key_version=key_version context=context prehashed=prehashed encodedBase64=encodedBase64) on="submit"}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="signing" />
|
||||
<div class="content">
|
||||
<p>Return the cryptographic signature of the given data using <code>{{key.name}}</code> as the encryption key and the specified hash algorithm.</p>
|
||||
</div>
|
||||
{{key-version-select
|
||||
key=key
|
||||
onVersionChange=(action (mut key_version))
|
||||
key_version=key_version
|
||||
}}
|
||||
<div class="field">
|
||||
<label for="input" class="is-label">
|
||||
Input
|
||||
</label>
|
||||
<div class="control is-relative">
|
||||
{{ivy-codemirror
|
||||
value=input
|
||||
valueUpdated=(action (mut input))
|
||||
options=(hash
|
||||
lineNumbers=true
|
||||
tabSize=2
|
||||
mode='ruby'
|
||||
theme='hashi'
|
||||
)
|
||||
data-test-transit-input="input"
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
{{#copy-button
|
||||
clipboardTarget="#signature"
|
||||
class="button is-primary"
|
||||
buttonType="button"
|
||||
success=(action (set-flash-message 'Signature copied!'))
|
||||
}}
|
||||
Copy
|
||||
{{/copy-button}}
|
||||
</div>
|
||||
<div class="control">
|
||||
<button {{action 'onClear'}} type="button" class="button">
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
<div class="field">
|
||||
{{input type="checkbox" id="encodedBase64" checked=encodedBase64 data-test-transit-input="encodedBase64"}}
|
||||
<label for="encodedBase64">This data is already encoded in base64</label>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="signing" />
|
||||
{{key-version-select
|
||||
key=key
|
||||
onVersionChange=(action (mut key_version))
|
||||
key_version=key_version
|
||||
}}
|
||||
{{#if key.derived}}
|
||||
<div class="field">
|
||||
<label for="input" class="is-label">
|
||||
Input
|
||||
<label for="context" class="is-label">
|
||||
Context
|
||||
</label>
|
||||
<div class="control is-relative">
|
||||
{{textarea id="input" name="input" value=input class="textarea" data-test-transit-input="input"}}
|
||||
{{b64-toggle value=input isInput=false data-test-transit-b64-toggle="input"}}
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="context" value=context class="input" data-test-transit-input="context"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=context data-test-transit-b64-toggle="context"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if key.derived}}
|
||||
<div class="field">
|
||||
<label for="context" class="is-label">
|
||||
Context
|
||||
</label>
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
{{input type="text" id="context" value=context class="input" data-test-transit-input="context"}}
|
||||
</div>
|
||||
<div class="control">
|
||||
{{b64-toggle value=context data-test-transit-b64-toggle="context"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field">
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<label for="hash_algorithm" class="is-label">Hash Algorithm</label>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="control is-flex">
|
||||
{{input id="prehashed" type="checkbox" name="prehashed" class="switch is-rounded is-success is-small" checked=prehashed }}
|
||||
<label for="prehashed">Prehashed</label>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="hash_algorithm"
|
||||
id="hash_algorithm"
|
||||
onchange={{action (mut hash_algorithm) value="target.value"}}
|
||||
>
|
||||
{{#each (sha2-digest-sizes) as |algo|}}
|
||||
<option selected={{if hash_algorithm (eq hash_algorithm algo) (eq algo 'sha2-256')}} value={{algo}}>
|
||||
{{algo}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if (or (eq key.type 'rsa-2048') (eq key.type 'rsa-3072') (eq key.type 'rsa-4096'))}}
|
||||
<div class="field">
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<label for="hash_algorithm" class="is-label">Hash Algorithm</label>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="control is-flex">
|
||||
{{input id="prehashed" type="checkbox" name="prehashed" class="switch is-rounded is-success is-small" checked=prehashed }}
|
||||
<label for="prehashed">Prehashed</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label for="signature_algorithm" class="is-label">Signature Algorithm</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="hash_algorithm"
|
||||
id="hash_algorithm"
|
||||
onchange={{action (mut hash_algorithm) value="target.value"}}
|
||||
>
|
||||
{{#each (sha2-digest-sizes) as |algo|}}
|
||||
<option selected={{if hash_algorithm (eq hash_algorithm algo) (eq algo 'sha2-256')}} value={{algo}}>
|
||||
{{algo}}
|
||||
</option>
|
||||
{{/each}}
|
||||
name="signature_algorithm"
|
||||
id="signature_algorithm"
|
||||
data-test-signature-algorithm="true"
|
||||
onchange={{action (mut signature_algorithm) value="target.value"}}
|
||||
>
|
||||
{{#each (array 'pss' 'pkcs1v15') as |sigAlgo|}}
|
||||
<option selected={{if signature_algorithm (eq signature_algorithm sigAlgo) (eq sigAlgo 'pss')}} value={{sigAlgo}}>
|
||||
{{sigAlgo}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if (or (eq key.type 'rsa-2048') (eq key.type 'rsa-3072') (eq key.type 'rsa-4096'))}}
|
||||
<div class="field">
|
||||
<label for="signature_algorithm" class="is-label">Signature Algorithm</label>
|
||||
<div class="control is-expanded">
|
||||
<div class="select is-fullwidth">
|
||||
<select
|
||||
name="signature_algorithm"
|
||||
id="signature_algorithm"
|
||||
data-test-signature-algorithm="true"
|
||||
onchange={{action (mut signature_algorithm) value="target.value"}}
|
||||
>
|
||||
{{#each (array 'pss' 'pkcs1v15') as |sigAlgo|}}
|
||||
<option selected={{if signature_algorithm (eq signature_algorithm sigAlgo) (eq sigAlgo 'pss')}} value={{sigAlgo}}>
|
||||
{{sigAlgo}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" disabled={{loading}} class="button is-primary {{if loading 'is-loading'}}">
|
||||
Sign
|
||||
</button>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<div class="control">
|
||||
<button type="submit" disabled={{loading}} class="button is-primary {{if loading 'is-loading'}}">
|
||||
Sign
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<Modal
|
||||
@title="Copy your signature"
|
||||
@onClose={{action (mut isModalActive) false}}
|
||||
@isActive={{isModalActive}}
|
||||
data-test-sign-modal>
|
||||
<section class="modal-card-body">
|
||||
<div class="box is-shadowless is-fullwidth is-sideless">
|
||||
<h2 class="title is-6">Signature</h2>
|
||||
<div class="copy-text level">
|
||||
<code class="level-left" data-test-encrypted-value="signature">{{signature}}</code>
|
||||
<CopyButton
|
||||
class="button is-compact is-transparent level-right"
|
||||
data-test-button="modal-copy"
|
||||
@clipboardText={{signature}}
|
||||
@buttonType="button"
|
||||
@success={{action (set-flash-message 'Signature copied!')}}>
|
||||
<Icon @glyph="copy-action" aria-label="Copy" />
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<CopyButton
|
||||
class="button is-primary copy-close"
|
||||
data-test-button="modal-copy-close"
|
||||
@clipboardText={{signature}}
|
||||
@buttonType="button"
|
||||
@success={{action "toggleModal" "Signature copied!"}}>
|
||||
Copy & Close
|
||||
</CopyButton>
|
||||
</footer>
|
||||
</Modal>
|
|
@ -1,25 +1,8 @@
|
|||
<form {{action "doSubmit" (hash input=input signature=signature signature_algorithm=signature_algorithm hmac=hmac hash_algorithm=hash_algorithm context=context prehashed=prehashed encodedBase64=encodedBase64) on="submit"}}>
|
||||
{{#if (not-eq valid null)}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<h4 class="is-label">Verified</h4>
|
||||
<div class="box">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-half has-text-centered">
|
||||
<p class="box is-shadowless {{if valid 'has-text-success' 'has-text-danger'}}" data-test-transit-verify="true">
|
||||
<Icon @glyph={{if valid "check-plain" "cancel-plain"}} />
|
||||
The input is <b>{{if valid 'valid' 'not valid'}}</b> for the given {{if signature 'signature' 'hmac'}}.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Check whether the provided signature is valid for the given data.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<button {{action 'onClear'}} type="button" class="button">
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="input" class="is-label">
|
||||
Input
|
||||
|
@ -222,5 +205,13 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
<Modal @title="Results" @onClose={{action (mut isModalActive) false}} @isActive={{isModalActive}}>
|
||||
<section class="modal-card-body">
|
||||
<AlertBanner
|
||||
@type={{if valid 'success' 'danger'}}
|
||||
@title={{if valid 'Valid' 'Not Valid'}}
|
||||
@message="The input is {{if valid 'valid' 'not valid'}} for the given {{if signature 'signature' 'HMAC'}}"
|
||||
data-test-transit-verify="true"/>
|
||||
</section>
|
||||
</Modal>
|
||||
|
|
|
@ -19,3 +19,4 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
"ember-svg-jar": "^2.1.0",
|
||||
"ember-test-selectors": "^2.1.0",
|
||||
"ember-truth-helpers": "^2.1.0",
|
||||
"ember-wormhole": "^0.5.5",
|
||||
"escape-string-regexp": "^2.0.0",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
|
|
|
@ -94,14 +94,15 @@ const testConvergentEncryption = async function(assert, keyName) {
|
|||
context: 'nqR8LiVgNh/lwO2rArJJE9F9DMhh0lKo4JX9DAAkCDw=',
|
||||
encodePlaintext: false,
|
||||
encodeContext: false,
|
||||
decodeAfterDecrypt: false,
|
||||
assertAfterEncrypt: key => {
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after encrypt`);
|
||||
assert.ok(
|
||||
/vault:/.test(find('#ciphertext-control .CodeMirror').CodeMirror.getValue()),
|
||||
/vault:/.test(find('[data-test-encrypted-value="ciphertext"]').innerText),
|
||||
`${key}: ciphertext shows a vault-prefixed ciphertext`
|
||||
);
|
||||
},
|
||||
assertBeforeDecrypt: key => {
|
||||
assert.dom('.modal.is-active').doesNotExist(`${key}: Modal not open before decrypt`);
|
||||
assert
|
||||
.dom('[data-test-transit-input="context"]')
|
||||
.hasValue(
|
||||
|
@ -111,8 +112,9 @@ const testConvergentEncryption = async function(assert, keyName) {
|
|||
},
|
||||
|
||||
assertAfterDecrypt: key => {
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after decrypt`);
|
||||
assert.equal(
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.getValue(),
|
||||
find('[data-test-encrypted-value="plaintext"]').innerText,
|
||||
'NaXud2QW7KjyK6Me9ggh+zmnCeBGdG93LQED49PtoOI=',
|
||||
`${key}: the ui shows the base64-encoded plaintext`
|
||||
);
|
||||
|
@ -124,21 +126,23 @@ const testConvergentEncryption = async function(assert, keyName) {
|
|||
context: encodeString('context'),
|
||||
encodePlaintext: false,
|
||||
encodeContext: false,
|
||||
decodeAfterDecrypt: false,
|
||||
assertAfterEncrypt: key => {
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after encrypt`);
|
||||
assert.ok(
|
||||
/vault:/.test(find('#ciphertext-control .CodeMirror').CodeMirror.getValue()),
|
||||
/vault:/.test(find('[data-test-encrypted-value="ciphertext"]').innerText),
|
||||
`${key}: ciphertext shows a vault-prefixed ciphertext`
|
||||
);
|
||||
},
|
||||
assertBeforeDecrypt: key => {
|
||||
assert.dom('.modal.is-active').doesNotExist(`${key}: Modal not open before decrypt`);
|
||||
assert
|
||||
.dom('[data-test-transit-input="context"]')
|
||||
.hasValue(encodeString('context'), `${key}: the ui shows the input context`);
|
||||
},
|
||||
assertAfterDecrypt: key => {
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after decrypt`);
|
||||
assert.equal(
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.getValue(),
|
||||
find('[data-test-encrypted-value="plaintext"]').innerText,
|
||||
'NaXud2QW7KjyK6Me9ggh+zmnCeBGdG93LQED49PtoOI=',
|
||||
`${key}: the ui shows the base64-encoded plaintext`
|
||||
);
|
||||
|
@ -150,22 +154,24 @@ const testConvergentEncryption = async function(assert, keyName) {
|
|||
context: encodeString('context'),
|
||||
encodePlaintext: false,
|
||||
encodeContext: false,
|
||||
decodeAfterDecrypt: true,
|
||||
assertAfterEncrypt: key => {
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after encrypt`);
|
||||
assert.ok(
|
||||
/vault:/.test(find('#ciphertext-control .CodeMirror').CodeMirror.getValue()),
|
||||
/vault:/.test(find('[data-test-encrypted-value="ciphertext"]').innerText),
|
||||
`${key}: ciphertext shows a vault-prefixed ciphertext`
|
||||
);
|
||||
},
|
||||
assertBeforeDecrypt: key => {
|
||||
assert.dom('.modal.is-active').doesNotExist(`${key}: Modal not open before decrypt`);
|
||||
assert
|
||||
.dom('[data-test-transit-input="context"]')
|
||||
.hasValue(encodeString('context'), `${key}: the ui shows the input context`);
|
||||
},
|
||||
assertAfterDecrypt: key => {
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after decrypt`);
|
||||
assert.equal(
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.getValue(),
|
||||
'This is the secret',
|
||||
find('[data-test-encrypted-value="plaintext"]').innerText,
|
||||
encodeString('This is the secret'),
|
||||
`${key}: the ui decodes plaintext`
|
||||
);
|
||||
},
|
||||
|
@ -177,24 +183,24 @@ const testConvergentEncryption = async function(assert, keyName) {
|
|||
context: 'secret 2',
|
||||
encodePlaintext: true,
|
||||
encodeContext: true,
|
||||
decodeAfterDecrypt: true,
|
||||
assertAfterEncrypt: key => {
|
||||
assert.ok(find('[data-test-transit-input="ciphertext"]'), `${key}: ciphertext box shows`);
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after encrypt`);
|
||||
assert.ok(
|
||||
/vault:/.test(find('#ciphertext-control .CodeMirror').CodeMirror.getValue()),
|
||||
/vault:/.test(find('[data-test-encrypted-value="ciphertext"]').innerText),
|
||||
`${key}: ciphertext shows a vault-prefixed ciphertext`
|
||||
);
|
||||
},
|
||||
assertBeforeDecrypt: key => {
|
||||
assert.dom('.modal.is-active').doesNotExist(`${key}: Modal not open before decrypt`);
|
||||
assert
|
||||
.dom('[data-test-transit-input="context"]')
|
||||
.hasValue(encodeString('secret 2'), `${key}: the ui shows the encoded context`);
|
||||
},
|
||||
assertAfterDecrypt: key => {
|
||||
assert.ok(find('[data-test-transit-input="plaintext"]'), `${key}: plaintext box shows`);
|
||||
assert.dom('.modal.is-active').exists(`${key}: Modal opens after decrypt`);
|
||||
assert.equal(
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.getValue(),
|
||||
'There are many secrets 🤐',
|
||||
find('[data-test-encrypted-value="plaintext"]').innerText,
|
||||
encodeString('There are many secrets 🤐'),
|
||||
`${key}: the ui decodes plaintext`
|
||||
);
|
||||
},
|
||||
|
@ -212,27 +218,33 @@ const testConvergentEncryption = async function(assert, keyName) {
|
|||
if (testCase.encodeContext) {
|
||||
await click('[data-test-transit-b64-toggle="context"]');
|
||||
}
|
||||
assert.dom('.modal.is-active').doesNotExist(`${name}: is not open before encrypt`);
|
||||
await click('[data-test-button-encrypt]');
|
||||
await settled();
|
||||
if (testCase.assertAfterEncrypt) {
|
||||
testCase.assertAfterEncrypt(keyName);
|
||||
}
|
||||
// store ciphertext for decryption step
|
||||
const copiedCiphertext = find('[data-test-encrypted-value="ciphertext"]').innerText;
|
||||
await click('[data-test-modal-background]');
|
||||
await settled();
|
||||
assert.dom('.modal.is-active').doesNotExist(`${name}: Modal closes after background clicked`);
|
||||
await click('[data-test-transit-action-link="decrypt"]');
|
||||
await settled();
|
||||
if (testCase.assertBeforeDecrypt) {
|
||||
testCase.assertBeforeDecrypt(keyName);
|
||||
}
|
||||
find('#ciphertext-control .CodeMirror').CodeMirror.setValue(copiedCiphertext);
|
||||
await click('[data-test-button-decrypt]');
|
||||
await settled();
|
||||
|
||||
if (testCase.assertAfterDecrypt) {
|
||||
if (testCase.decodeAfterDecrypt) {
|
||||
await click('[data-test-transit-b64-toggle="plaintext"]');
|
||||
testCase.assertAfterDecrypt(keyName);
|
||||
} else {
|
||||
testCase.assertAfterDecrypt(keyName);
|
||||
}
|
||||
testCase.assertAfterDecrypt(keyName);
|
||||
}
|
||||
|
||||
await click('[data-test-modal-background]');
|
||||
await settled();
|
||||
assert.dom('.modal.is-active').doesNotExist(`${name}: Modal closes after background clicked`);
|
||||
}
|
||||
};
|
||||
module('Acceptance | transit', function(hooks) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, findAll } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | modal', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<Modal></Modal><div id="modal-wormhole"></div>`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '', 'renders without interior content');
|
||||
assert.equal(findAll('[data-test-modal-close-button]').length, 0, 'does not render close modal button');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<Modal @showCloseButton={{true}}>
|
||||
template block text
|
||||
</Modal>
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'template block text', 'renders with interior content');
|
||||
assert.equal(findAll('[data-test-modal-close-button]').length, 1, 'renders close modal button');
|
||||
});
|
||||
});
|
|
@ -45,25 +45,36 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
|
||||
test('it requires `key`', async function(assert) {
|
||||
let promise = waitForError();
|
||||
render(hbs`{{transit-key-actions}}`);
|
||||
render(hbs`
|
||||
{{transit-key-actions}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
let err = await promise;
|
||||
assert.ok(err.message.includes('`key` is required for'), 'asserts without key');
|
||||
});
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
this.set('key', { backend: 'transit', supportedActions: ['encrypt'] });
|
||||
await render(hbs`{{transit-key-actions selectedAction="encrypt" key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction="encrypt" key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
assert.equal(findAll('[data-test-transit-action="encrypt"]').length, 1, 'renders encrypt');
|
||||
|
||||
this.set('key', { backend: 'transit', supportedActions: ['sign'] });
|
||||
await render(hbs`{{transit-key-actions selectedAction="sign" key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction="sign" key=key}}
|
||||
<div id="modal-wormhole"></div>`);
|
||||
assert.equal(findAll('[data-test-transit-action="sign"]').length, 1, 'renders sign');
|
||||
});
|
||||
|
||||
test('it renders: signature_algorithm field', async function(assert) {
|
||||
this.set('key', { backend: 'transit', supportsSigning: true, supportedActions: ['sign', 'verify'] });
|
||||
this.set('selectedAction', 'sign');
|
||||
await render(hbs`{{transit-key-actions selectedAction=selectedAction key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction=selectedAction key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
assert.equal(
|
||||
findAll('[data-test-signature-algorithm]').length,
|
||||
0,
|
||||
|
@ -98,7 +109,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
|
||||
test('it renders: rotate', async function(assert) {
|
||||
this.set('key', { backend: 'transit', id: 'akey', supportedActions: ['rotate'] });
|
||||
await render(hbs`{{transit-key-actions selectedAction="rotate" key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction="rotate" key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
|
||||
assert.equal(find('*').textContent.trim(), '', 'renders an empty div');
|
||||
|
||||
|
@ -117,7 +131,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
this.set('key', key);
|
||||
this.set('selectedAction', 'encrypt');
|
||||
this.set('storeService.keyActionReturnVal', { ciphertext: 'secret' });
|
||||
await render(hbs`{{transit-key-actions selectedAction=selectedAction key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction=selectedAction key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.setValue('plaintext');
|
||||
await click('button[type="submit"]');
|
||||
|
@ -134,12 +151,12 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
'passes expected args to the adapter'
|
||||
);
|
||||
|
||||
assert.equal(find('#ciphertext-control .CodeMirror').CodeMirror.getValue(), 'secret');
|
||||
assert.equal(find('[data-test-encrypted-value="ciphertext"]').innerText, 'secret');
|
||||
|
||||
const preEncodedValue = encodeString('plaintext');
|
||||
// Click back button
|
||||
await click('[data-test-encrypt-back-button]');
|
||||
// exit modal
|
||||
await click('[data-test-modal-background]');
|
||||
// Encrypt again, with pre-encoded value and checkbox selected
|
||||
const preEncodedValue = encodeString('plaintext');
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.setValue(preEncodedValue);
|
||||
await click('input[data-test-transit-input="encodedBase64"]');
|
||||
await click('button[type="submit"]');
|
||||
|
@ -166,7 +183,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
const key = assign({}, keyDefaults, keyattrs);
|
||||
this.set('key', key);
|
||||
this.set('storeService.keyActionReturnVal', { ciphertext: 'secret' });
|
||||
await render(hbs`{{transit-key-actions selectedAction="encrypt" key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction="encrypt" key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
|
||||
findAll('.CodeMirror')[0].CodeMirror.setValue('plaintext');
|
||||
assert.equal(findAll('#key_version').length, 1, 'it renders the key version selector');
|
||||
|
@ -194,7 +214,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
const key = assign({}, keyDefaults, keyattrs);
|
||||
this.set('key', key);
|
||||
this.set('storeService.keyActionReturnVal', { ciphertext: 'secret' });
|
||||
await render(hbs`{{transit-key-actions selectedAction="encrypt" key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions selectedAction="encrypt" key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
|
||||
// await fillIn('#plaintext', 'plaintext');
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.setValue('plaintext');
|
||||
|
@ -205,7 +228,7 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
);
|
||||
});
|
||||
|
||||
test('it carries ciphertext value over to decrypt', async function(assert) {
|
||||
test('it does not carry ciphertext value over to decrypt', async function(assert) {
|
||||
const plaintext = 'not so secret';
|
||||
await doEncrypt.call(this, assert, ['decrypt']);
|
||||
|
||||
|
@ -213,15 +236,8 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
this.set('selectedAction', 'decrypt');
|
||||
assert.equal(
|
||||
find('#ciphertext-control .CodeMirror').CodeMirror.getValue(),
|
||||
'secret',
|
||||
'keeps ciphertext value'
|
||||
);
|
||||
|
||||
await click('button[type="submit"]');
|
||||
assert.equal(
|
||||
find('#plaintext-control .CodeMirror').CodeMirror.getValue(),
|
||||
plaintext,
|
||||
'renders decrypted value'
|
||||
'',
|
||||
'does not prefill ciphertext value'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -233,7 +249,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
exportKeyTypes: ['encryption'],
|
||||
validKeyVersions: [1],
|
||||
});
|
||||
await render(hbs`{{transit-key-actions key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
};
|
||||
|
||||
test('it can export a key:default behavior', async function(assert) {
|
||||
|
@ -254,7 +273,7 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
'passes expected args to the adapter'
|
||||
);
|
||||
assert.equal(this.get('storeService.callArgsOptions.wrapTTL'), '30m', 'passes value for wrapTTL');
|
||||
assert.equal(find('#export').value, 'wrapped-token', 'wraps by default');
|
||||
assert.equal(find('[data-test-encrypted-value="export"]').innerText, 'wrapped-token', 'wraps by default');
|
||||
});
|
||||
|
||||
test('it can export a key:unwrapped behavior', async function(assert) {
|
||||
|
@ -264,9 +283,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
await click('#wrap-response');
|
||||
await triggerEvent('#wrap-response', 'change');
|
||||
await click('button[type="submit"]');
|
||||
assert.dom('.modal.is-active').exists('Modal opens after export');
|
||||
assert.deepEqual(
|
||||
JSON.parse(findAll('.CodeMirror')[0].CodeMirror.getValue()),
|
||||
response,
|
||||
find('.modal [data-test-encrypted-value="export"]').innerText,
|
||||
JSON.stringify(response, null, 2),
|
||||
'prints json response'
|
||||
);
|
||||
});
|
||||
|
@ -280,9 +300,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
await click('#exportVersion');
|
||||
await triggerEvent('#exportVersion', 'change');
|
||||
await click('button[type="submit"]');
|
||||
assert.dom('.modal.is-active').exists('Modal opens after export');
|
||||
assert.deepEqual(
|
||||
JSON.parse(findAll('.CodeMirror')[0].CodeMirror.getValue()),
|
||||
response,
|
||||
find('.modal [data-test-encrypted-value="export"]').innerText,
|
||||
JSON.stringify(response, null, 2),
|
||||
'prints json response'
|
||||
);
|
||||
assert.deepEqual(
|
||||
|
@ -306,7 +327,10 @@ module('Integration | Component | transit key actions', function(hooks) {
|
|||
supportedActions: ['hmac'],
|
||||
validKeyVersions: [1],
|
||||
});
|
||||
await render(hbs`{{transit-key-actions key=key}}`);
|
||||
await render(hbs`
|
||||
{{transit-key-actions key=key}}
|
||||
<div id="modal-wormhole"></div>
|
||||
`);
|
||||
await fillIn('#algorithm', 'sha2-384');
|
||||
await blur('#algorithm');
|
||||
await click('button[type="submit"]');
|
||||
|
|
|
@ -7576,6 +7576,14 @@ ember-truth-helpers@^2.1.0:
|
|||
dependencies:
|
||||
ember-cli-babel "^6.6.0"
|
||||
|
||||
ember-wormhole@^0.5.5:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/ember-wormhole/-/ember-wormhole-0.5.5.tgz#db417ff748cb21e574cd5f233889897bc27096cb"
|
||||
integrity sha512-z8l3gpoKmRA2BnTwvnYRk4jKVcETKHpddsD6kpS+EJ4EfyugadFS3zUqBmRDuJhFbNP8BVBLXlbbATj+Rk1Kgg==
|
||||
dependencies:
|
||||
ember-cli-babel "^6.10.0"
|
||||
ember-cli-htmlbars "^2.0.1"
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
|
|
Loading…
Reference in New Issue