Add Toolbar component

This commit is contained in:
Joshua Ogle 2019-02-14 09:04:46 -07:00
parent 345a1ef43f
commit a655e76944
69 changed files with 957 additions and 729 deletions

View File

@ -37,6 +37,7 @@ export const GLYPHS_WITH_SVG_TAG = [
'neutral-circled-outline',
'perf-replication',
'person',
'plus-plain',
'role',
'status-indicator',
'stopwatch',

View File

@ -0,0 +1,5 @@
import Component from '@ember/component';
export default Component.extend({
classNames: ['toolbar-actions'],
});

View File

@ -0,0 +1,5 @@
import LinkComponent from '@ember/routing/link-component';
export default LinkComponent.extend({
classNames: ['toolbar-button'],
});

View File

@ -0,0 +1,6 @@
import LinkComponent from '@ember/routing/link-component';
export default LinkComponent.extend({
classNames: ['toolbar-button'],
glyph: 'chevron-right',
});

View File

@ -0,0 +1,5 @@
import DownloadButton from './download-button';
export default DownloadButton.extend({
classNames: ['toolbar-button'],
});

View File

@ -0,0 +1,5 @@
import Component from '@ember/component';
export default Component.extend({
classNames: ['toolbar-filters'],
});

View File

@ -0,0 +1,6 @@
import SecretLink from './secret-link';
export default SecretLink.extend({
class: 'toolbar-button',
glyph: 'chevron-right',
});

View File

@ -0,0 +1,6 @@
import Component from '@ember/component';
export default Component.extend({
classNames: ['toolbar'],
tagName: 'nav',
});

View File

@ -18,3 +18,21 @@
justify-content: center;
align-items: center;
}
.auth-form .toolbar {
border-top: 0;
border-bottom: $light-border;
padding: 0 $spacing-s;
.field {
width: 100%;
}
.field-label {
margin-right: $spacing-s;
}
.is-label {
color: $grey;
}
}

View File

@ -9,7 +9,7 @@
&.thead {
box-shadow: 0 1px 0 $grey-light, 0 -1px 0 $grey-light;
margin: 0;
padding: 0 $size-6;
padding: 0 $size-6 0 0;
.column {
padding: 0.5rem 0.75rem;

View File

@ -1,24 +0,0 @@
.secret-control-bar {
margin: 0;
padding: $size-10 $size-9;
background: $grey-lighter;
box-shadow: 0 1px 0 $grey-light, 0 -1px 0 $grey-light;
display: flex;
justify-content: flex-end;
.control {
flex: 0 1 auto;
padding: 0 $size-10;
font-size: $size-8;
height: 1.8rem;
line-height: 1.8rem;
display: flex;
flex-direction: column;
justify-content: center;
.switch[type='checkbox'].is-small + label::before {
top: 0.5rem;
}
.switch[type='checkbox'].is-small + label::after {
top: 0.6rem;
}
}
}

View File

@ -1,3 +1,7 @@
.page-header + .tabs-container {
box-shadow: none;
}
.tabs {
box-shadow: inset 0 -1px 0 $grey-light;
@ -15,25 +19,25 @@
border-color: $blue;
color: $blue;
}
&:first-child a,
&:first-child .tab {
margin-left: $size-5;
}
}
a,
.tab {
color: $grey-dark;
color: $grey;
font-weight: $font-weight-semibold;
text-decoration: none;
padding: $size-6 $size-8 $size-8;
border-bottom: 2px solid transparent;
transition: border-color $speed;
transition: background-color $speed, border-color $speed;
&:hover,
&:active {
border-color: $grey-light;
}
&:hover {
background-color: $ui-gray-050;
}
}
.ember-basic-dropdown-trigger {

View File

@ -0,0 +1,97 @@
.tabs-container + .toolbar {
border-top: 0;
}
.toolbar {
background-color: $ui-gray-010;
border-top: $base-border;
box-shadow: 0 2px 0 -1px $ui-gray-300;
position: relative;
&::after {
background: linear-gradient(to right, rgba($ui-gray-010, 0), $ui-gray-010);
bottom: 0;
content: '';
position: absolute;
right: 0;
top: 0;
width: $spacing-s;
z-index: 2;
}
.input,
.select {
min-width: 160px;
}
}
.toolbar-scroller {
align-items: center;
display: flex;
height: 44px;
justify-content: space-between;
overflow-x: auto;
width: 100%;
@include from($mobile) {
padding: 0 $spacing-xxs;
}
&::-webkit-scrollbar {
border-bottom: $base-border;
height: $spacing-xxs;
}
&::-webkit-scrollbar-thumb {
background: $ui-gray-300;
border-radius: $spacing-xxs;
}
}
.toolbar-filters,
.toolbar-actions {
align-items: center;
display: flex;
flex: 1;
white-space: nowrap;
}
.toolbar-filters + .toolbar-actions {
@include until($mobile) {
border-left: $base-border;
margin-left: $spacing-xs;
padding-left: $spacing-xs;
}
}
.toolbar-actions {
@include from($mobile) {
justify-content: flex-end;
}
}
.toolbar-button {
@extend .button;
@extend .is-ghost;
@extend .has-icon-right;
border: 0;
color: $black;
transition: background-color $speed;
&:hover {
background-color: $ui-gray-100;
border: 0;
color: $blue;
}
&:active {
box-shadow: none;
}
}
.toolbar-separator {
border-right: $light-border;
height: 32px;
margin: 0 $spacing-xs;
width: 0;
}

View File

@ -71,13 +71,13 @@
@import './components/radial-progress';
@import './components/role-item';
@import './components/search-select';
@import './components/secret-control-bar';
@import './components/shamir-progress';
@import './components/sidebar';
@import './components/splash-page';
@import './components/status-menu';
@import './components/tabs';
@import './components/token-expire-warning';
@import './components/toolbar';
@import './components/tool-tip';
@import './components/unseal-warning';
@import './components/upgrade-overlay';

View File

@ -192,8 +192,8 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12);
.icon {
&,
&:first-child:last-child {
margin-left: $size-11;
margin-right: -$size-10;
margin-left: $spacing-xxs;
margin-right: -$spacing-xxs;
}
}
}

View File

@ -1,16 +1,14 @@
.table {
thead,
.thead {
background: $grey-lighter;
box-shadow: 0 1px 0 0 $grey-light, 0 -1px 0 0 $grey-light;
th,
.th {
text-transform: uppercase;
font-size: $size-8;
color: $grey-dark;
font-weight: normal;
padding: 0.5rem 1.5rem;
color: $grey;
font-weight: $font-weight-semibold;
padding: 1rem 1.5rem 0;
border-width: 0 0 1px 0;
border-color: $grey-light;
}

View File

@ -4,6 +4,31 @@
{{partial 'svg/vault-loading'}}
</div>
{{/if}}
{{#if (has-feature "Namespaces")}}
<Toolbar>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="is-label" for="namespace">Namespace</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input
value={{namespaceQueryParam}}
placeholder="/ (Root)"
oninput={{perform updateNamespace value="target.value"}}
autocomplete="off"
name="namespace"
id="namespace"
class="input"
type="text"
/>
</div>
</div>
</div>
</div>
</Toolbar>
{{/if}}
{{#if hasMethodsWithPath}}
<nav class="tabs is-marginless">
<ul>

View File

@ -4,20 +4,8 @@
{{capitalize (pluralize identityType)}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#if (eq identityType "entity")}}
{{#link-to "vault.cluster.access.identity.merge" (pluralize identityType) class="button has-icon-right is-ghost is-compact" data-test-entity-merge-link=true}}
Merge {{pluralize identityType}}
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
{{/if}}
{{#link-to "vault.cluster.access.identity.create" (pluralize identityType) class="button has-icon-right is-ghost is-compact" data-test-entity-create-link=true}}
Create {{identityType}}
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</p.levelRight>
</PageHeader>
<div class="box is-sideless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs">
<ul>
{{#link-to "vault.cluster.access.identity.index" (pluralize identityType) tagName="li"}}
@ -33,12 +21,26 @@
</ul>
</nav>
</div>
{{#if model.meta.total}}
<div class="box is-sideless has-background-grey-lighter has-short-padding is-marginless">
<div class="level">
<div class="level-left">
{{identity/lookup-input type=identityType}}
</div>
</div>
</div>
{{/if}}
<Toolbar>
{{#if model.meta.total}}
<ToolbarFilters>
{{identity/lookup-input type=identityType}}
</ToolbarFilters>
{{/if}}
<ToolbarActions>
{{#if (eq identityType "entity")}}
<ToolbarButton
@params={{array "vault.cluster.access.identity.merge" (pluralize identityType)}}
data-test-entity-merge-link=true
>
Merge {{pluralize identityType}}
</ToolbarButton>
{{/if}}
<ToolbarButton
@params={{array "vault.cluster.access.identity.create" (pluralize identityType)}}
data-test-entity-create-link=true
>
Create {{identityType}}
</ToolbarButton>
</ToolbarActions>
</Toolbar>

View File

@ -1,5 +1,5 @@
<form {{action (perform lookup) on="submit"}}>
<div class="field has-addons">
<div class="field is-flex">
<div class="control">
<div class="select is-fullwidth">
<select

View File

@ -9,11 +9,11 @@
oninput={{action "handleInput" value="target.value"}}
onkeyup={{action "handleKeyUp" }}
onkeydown={{action "handleKeyPress"}}
onkeydown={{action "handleKeyPress"}}
onfocus={{action "setFilterFocused" true}}
onblur={{action "setFilterFocused" false}}
/>
/>
{{i-con glyph="ios-search-strong" class="is-left has-text-grey" size=18}}
{{i-con glyph="ios-search-strong" class="is-left has-text-grey-light" size=20}}
</p>
</div>

View File

@ -10,7 +10,7 @@
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-secret-header="true">
{{#if (eq mode "create") }}
{{#if (eq mode "create")}}
Create an AWS Role
{{else if (eq mode 'edit')}}
Edit AWS Role <code>{{model.id}}</code>
@ -19,40 +19,33 @@
{{/if}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
{{#if (eq mode "show") }}
{{#if (or model.canUpdate model.canDelete)}}
<div class="control">
{{#secret-link
secret=model.id
mode="edit"
replace=true
class="button has-icon-right is-ghost is-compact"
}}
Edit role
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
{{#if model.canGenerate}}
<div class="control">
{{#secret-link
mode="credentials"
secret=model.id
class="button has-icon-right is-ghost is-compact"
data-test-backend-credentials="iam"
}}
Generate credentials
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
{{/if}}
</div>
</p.levelRight>
</PageHeader>
<Toolbar>
<ToolbarActions>
{{#if (eq mode "show")}}
{{#if (or model.canUpdate model.canDelete)}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="edit"
@replace=true
>
Edit role
</ToolbarSecretLink>
{{/if}}
{{#if model.canGenerate}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="credentials"
@data-test-backend-credentials="iam"
>
Generate credentials
</ToolbarSecretLink>
{{/if}}
{{/if}}
</ToolbarActions>
</Toolbar>
{{#if (or (eq mode 'edit') (eq mode 'create'))}}
{{partial 'partials/role-aws/form'}}
{{else}}

View File

@ -19,56 +19,45 @@
{{/if}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
{{#if (eq mode "show") }}
{{#if (or model.canUpdate model.canDelete)}}
<div class="control">
{{#secret-link
secret=model.id
mode="edit"
replace=true
class="button has-icon-right is-ghost is-compact"
data-test-edit-link=true
}}
Edit role
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
{{#if model.canGenerate}}
<div class="control">
{{#secret-link
mode="credentials"
secret=model.id
queryParams=(query-params action="issue")
class="button has-icon-right is-ghost is-compact"
data-test-credentials-link=true
}}
Generate Certificate
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
{{#if model.canSign}}
<div class="control">
{{#secret-link
mode="credentials"
secret=model.id
queryParams=(query-params action="sign")
class="button has-icon-right is-ghost is-compact"
data-test-sign-link=true
}}
Sign Certificate
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
{{/if}}
</div>
</p.levelRight>
</PageHeader>
<Toolbar>
<ToolbarActions>
{{#if (eq mode "show") }}
{{#if (or model.canUpdate model.canDelete)}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="edit"
@data-test-edit-link=true
@replace=true
>
Edit role
</ToolbarSecretLink>
{{/if}}
{{#if model.canGenerate}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="credentials"
@queryParams={{query-params action="issue"}}
@data-test-credentials-link=true
>
Generate Certificate
</ToolbarSecretLink>
{{/if}}
{{#if model.canSign}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="credentials"
@queryParams={{query-params action="sign"}}
@data-test-sign-link=true
>
Sign Certificate
</ToolbarSecretLink>
{{/if}}
{{/if}}
</ToolbarActions>
</Toolbar>
{{#if (or (eq mode 'edit') (eq mode 'create'))}}
{{partial 'partials/role-pki/form'}}
{{else}}

View File

@ -19,51 +19,44 @@
{{/if}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
{{#if (eq mode "show") }}
{{#if (or model.canUpdate model.canDelete)}}
<div class="control">
{{#secret-link
secret=model.id
mode="edit"
replace=true
class="button has-icon-right is-ghost is-compact"
data-test-edit-link=true
}}
Edit role
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
<div class="control">
{{#if (eq model.keyType "otp")}}
{{#secret-link
mode="credentials"
secret=model.id
class="button has-icon-right is-ghost is-compact"
data-test-backend-credentials=true
}}
Generate credentials
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
{{else}}
{{#secret-link
mode="sign"
secret=model.id
class="button has-icon-right is-ghost is-compact"
data-test-backend-credentials=true
}}
Sign keys
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
{{/if}}
</div>
{{/if}}
</div>
</p.levelRight>
</PageHeader>
{{#if (eq mode "show") }}
<Toolbar>
<ToolbarActions>
{{#if (or model.canUpdate model.canDelete)}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="edit"
@data-test-edit-link=true
@replace=true
>
Edit role
</ToolbarSecretLink>
{{/if}}
{{#if (eq model.keyType "otp")}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="credentials"
@data-test-backend-credentials=true
@replace=true
>
Generate Credential
</ToolbarSecretLink>
{{else}}
<ToolbarSecretLink
@secret={{model.id}}
@mode="sign"
@data-test-backend-credentials=true
@replace=true
>
Sign Keys
</ToolbarSecretLink>
{{/if}}
</ToolbarActions>
</Toolbar>
{{/if}}
{{#if (or (eq mode 'edit') (eq mode 'create'))}}
{{partial 'partials/role-ssh/form'}}
{{else}}

View File

@ -21,129 +21,30 @@
{{/if}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#if canDelete}}
<ConfirmAction
@buttonClasses="button is-compact is-ghost has-icon-right"
@onConfirmAction={{action "deleteKey"}}
@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 "?")
}}
@cancelButtonText="Cancel"
data-test-secret-delete="true"
>
Delete secret <ICon @glyph="chevron-right" @size="11" />
</ConfirmAction>
{{/if}}
</p.levelRight>
</PageHeader>
<div class="secret-control-bar">
<Toolbar>
{{#unless (and (eq mode 'show') isWriteWithoutRead)}}
<div class="control">
<input
data-test-secret-json-toggle=true
id="json"
type="checkbox"
name="json"
class="switch is-rounded is-success is-small"
checked={{showAdvancedMode}}
onchange={{action "toggleAdvanced" value="target.checked"}}
disabled={{and (eq mode 'show') secretDataIsAdvanced}}
/>
<label for="json" class="has-text-grey">JSON</label>
</div>
<ToolbarFilters>
<input
data-test-secret-json-toggle=true
id="json"
type="checkbox"
name="json"
class="switch is-rounded is-success is-small"
checked={{showAdvancedMode}}
onchange={{action "toggleAdvanced" value="target.checked"}}
disabled={{and (eq mode 'show') secretDataIsAdvanced}}
/>
<label for="json" class="has-text-grey">JSON</label>
</ToolbarFilters>
{{/unless}}
{{#if (and (eq mode 'show') (or canEditV2Secret canEdit))}}
{{#let (concat 'vault.cluster.secrets.backend.' (if (eq mode 'show') 'edit' 'show')) as |targetRoute|}}
{{#unless (and isV2 (or isWriteWithoutRead 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}}
</div>
{{/let}}
{{/if}}
{{#if (and (eq @mode "show") this.isV2 (not @model.failedServerRead))}}
<div class="control">
<ToolbarActions>
{{#if (and (eq @mode "show") this.isV2 (not @model.failedServerRead))}}
<SecretVersionMenu
@version={{this.modelForData}}
@onRefresh={{action 'refresh'}}
/>
</div>
<div class="control">
<BasicDropdown
@class="popup-menu"
@horizontalPosition="auto-right"
@ -152,10 +53,11 @@
>
<D.trigger
data-test-popup-menu-trigger="true"
@class={{concat "popup-menu-trigger button is-ghost has-text-grey" (if D.isOpen " is-active")}}
@class={{concat "popup-menu-trigger toolbar-button" (if D.isOpen " is-active")}}
@tagName="button"
>
History <ICon @glyph="chevron-right" @size="11" />
History
<ICon @glyph="chevron-down" @size="12" />
</D.trigger>
<D.content @class="popup-menu-content ">
<nav class="box menu">
@ -188,7 +90,104 @@
</nav>
</D.content>
</BasicDropdown>
</div>
{{/if}}
</div>
<div class="toolbar-separator"/>
{{/if}}
{{#if (and (eq mode 'show') canDelete)}}
<ConfirmAction
@buttonClasses="toolbar-button"
@onConfirmAction={{action "deleteKey"}}
@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 "?")
}}
@cancelButtonText="Cancel"
data-test-secret-delete="true"
>
Delete secret
<ICon @glyph="chevron-right" @size="12" />
</ConfirmAction>
{{/if}}
{{#if (and (eq mode 'show') (or canEditV2Secret canEdit))}}
{{#let (concat 'vault.cluster.secrets.backend.' (if (eq mode 'show') 'edit' 'show')) as |targetRoute|}}
{{#unless (and isV2 (or isWriteWithoutRead modelForData.destroyed modelForData.deleted))}}
<BasicDropdown
@class="popup-menu"
@horizontalPosition="auto-right"
@verticalPosition="below"
@onClose={{action "clearWrappedData"}}
as |D|
>
<D.trigger
data-test-popup-menu-trigger="true"
@class={{concat "toolbar-button" (if D.isOpen " is-active")}}
@tagName="button"
>
Copy secret
<ICon @glyph="chevron-down" @size="12" />
</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>
{{/unless}}
{{#if isV2}}
<ToolbarAddButton
@params={{array targetRoute model.id (query-params version=this.modelForData.version)}}
@data-test-secret-edit="true"
@replace={{true}}
>
Create new version
</ToolbarAddButton>
{{else}}
<ToolbarAddButton
@params={{array targetRoute model.id}}
@data-test-secret-edit="true"
@replace={{true}}
>
Edit secret
</ToolbarAddButton>
{{/if}}
{{/let}}
{{/if}}
</ToolbarActions>
</Toolbar>
{{partial partialName}}

View File

@ -25,35 +25,9 @@
{{/if}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#unless (or isCertTab isConfigure)}}
<div class="control">
{{#secret-link
mode="create"
secret=''
queryParams=(query-params initialKey=(or filter baseKey.id))
class="button has-icon-right is-ghost is-compact"
data-test-secret-create=true
}}
{{options.create}}
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/unless}}
{{#if (or (eq model.type "aws") (eq model.type "ssh") (eq model.type "pki"))}}
<div class="control">
{{#link-to "vault.cluster.settings.configure-secret-backend" model.id
class="button has-icon-right is-ghost is-compact"
data-test-secret-backend-configure=true
}}
Configure {{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</div>
{{/if}}
</p.levelRight>
</PageHeader>
{{#if options.tabs}}
<div class="box is-bottomless is-marginless is-fullwidth is-paddingless">
<div class="tabs-container box is-bottomless is-marginless is-fullwidth is-paddingless">
<nav class="tabs">
<ul>
{{#each options.tabs as |oTab|}}
@ -72,7 +46,7 @@
{{/if}}
{{/each}}
{{#link-to 'vault.cluster.secrets.backend.configuration' tagName="li" activeClass="is-active"}}
{{#link-to 'vault.cluster.secrets.backend.configuration' }}
{{#link-to 'vault.cluster.secrets.backend.configuration' data-test-configuration-tab=true}}
Configuration
{{/link-to}}
{{/link-to}}
@ -81,7 +55,7 @@
</div>
{{else}}
{{!-- if there are no tabs in the options, we'll hardcode them here --}}
<div class="box is-bottomless is-marginless is-fullwidth is-paddingless">
<div class="tabs-container box is-bottomless is-marginless is-fullwidth is-paddingless">
<nav class="tabs">
<ul>
{{#if (contains model.engineType (supported-secret-backends))}}
@ -92,7 +66,7 @@
{{/link-to}}
{{/if}}
{{#link-to 'vault.cluster.secrets.backend.configuration' tagName="li" activeClass="is-active"}}
{{#link-to 'vault.cluster.secrets.backend.configuration' }}
{{#link-to 'vault.cluster.secrets.backend.configuration' data-test-configuration-tab=true}}
Configuration
{{/link-to}}
{{/link-to}}

View File

@ -1,19 +1,19 @@
<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 "toolbar-button" (if D.isOpen " is-active")}}
@tagName="button"
>
{{#if useDefaultTrigger}}
<ICon aria-label="More options" @glyph="more" @size="16" @class="has-text-black auto-width" />
{{else}}
Version {{this.version.version}}
<ICon @glyph="chevron-right" @size="11" />
Version {{this.version.version}}
<ICon @glyph="chevron-down" @size="12" />
{{/if}}
</D.trigger>
<D.content @class="popup-menu-content ">
@ -93,4 +93,4 @@
</ul>
</nav>
</D.content>
</BasicDropdown>
</BasicDropdown>

View File

@ -1,6 +1,6 @@
{{#with (tabs-for-auth-section model.type tabType) as |tabs|}}
{{#if tabs.length}}
<div class="box is-sideless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs">
<ul>
{{#each tabs as |tab|}}

View File

@ -0,0 +1,2 @@
{{yield}}
<ICon @glyph="plus-plain" @size=12 />

View File

@ -0,0 +1,2 @@
{{yield}}
<ICon @glyph={{glyph}} @size=12 />

View File

@ -0,0 +1,2 @@
{{@actionText}}
<ICon @glyph="chevron-right" @size=11 />

View File

@ -0,0 +1,17 @@
<SecretLink
@class="toolbar-button"
@mode={{mode}}
@secret={{secret}}
@queryParams={{queryParams}}
@data-test-secret-create={{data-test-secret-create}}
@data-test-credentials-link={{data-test-credentials-link}}
@data-test-backend-credentials={{data-test-backend-credentials}}
@data-test-edit-link={{data-test-edit-link}}
@data-test-sign-link={{data-test-sign-link}}
@data-test-transit-link={{data-test-transit-link}}
@data-test-transit-key-actions-link={{data-test-transit-key-actions-link}}
@data-test-transit-action-link={{data-test-transit-action-link}}
@replace={{replace}}>
{{yield}}
<ICon @glyph={{glyph}} @size=12 />
</SecretLink>

View File

@ -0,0 +1,3 @@
<div class="toolbar-scroller">
{{yield}}
</div>

View File

@ -19,36 +19,6 @@
{{/if}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
{{#if (eq mode "show") }}
{{#if (or capabilities.canUpdate capabilities.canDelete)}}
<div class="control">
{{#secret-link
secret=key.id
mode="edit"
replace=true
class="button has-icon-right is-ghost is-compact"
}}
Edit encryption key
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
<div class="control">
{{#secret-link
mode="actions"
secret=key.id
class="button has-icon-right is-ghost is-compact"
data-test-transit-key-actions-link=true
}}
Key actions
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</div>
{{/if}}
</div>
</p.levelRight>
</PageHeader>
{{partial (concat 'partials/transit-form-' mode)}}

View File

@ -1,20 +1,17 @@
{{#if (eq selectedAction 'rotate')}}
{{#if key.canRotate}}
<div class="field is-grouped is-grouped-split box is-fullwidth is-bottomless">
<div class="level-right is-fullwidth">
{{#confirm-action
buttonClasses="button"
onConfirmAction=(action "doSubmit")
confirmMessage=(concat 'Are you sure you want to rotate "' key.id '"?')
confirmButtonText="Confirm rotation"
cancelButtonText="Cancel"
messageClasses="is-block-mobile has-text-grey"
data-test-transit-key-rotate=true
}}
Rotate encryption key
{{/confirm-action}}
</div>
</div>
{{#confirm-action
buttonClasses="toolbar-button"
onConfirmAction=(action "doSubmit")
confirmMessage=(concat 'Are you sure you want to rotate "' key.id '"?')
confirmButtonText="Confirm rotation"
cancelButtonText="Cancel"
messageClasses="is-block-mobile has-text-grey"
data-test-transit-key-rotate=true
}}
Rotate encryption key
<ICon @glyph="chevron-right" @size=12 />
{{/confirm-action}}
{{/if}}
{{else}}
{{message-error errors=errors}}

View File

@ -1,4 +1,4 @@
<div class="box is-sideless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs">
<ul>
{{#link-to "vault.cluster.settings.configure-secret-backend" model.id (query-params tab='') tagName="li"}}
@ -15,6 +15,7 @@
</ul>
</nav>
</div>
{{#if (eq tab "leases")}}
<form
{{action "save" "saveAWSLease" (hash lease=model.lease lease_max=model.leaseMax) on="submit"}}

View File

@ -1,4 +1,4 @@
<div class="box is-bottomless is-fullwidth is-paddingless">
<div class="tabs-container box is-bottomless is-fullwidth is-paddingless">
<nav class="tabs">
<ul>
{{#each (array 'cert' 'urls' 'crl' 'tidy') as |section|}}

View File

@ -1,4 +1,4 @@
<div class="box is-sideless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs">
<ul>
<li class="{{if (eq tab '') 'is-active'}}">
@ -28,6 +28,37 @@
</nav>
</div>
<Toolbar>
<ToolbarActions>
{{#if (eq tab 'versions')}}
<TransitKeyActions
@key={{key}}
@selectedAction="rotate"
@capabilities={{capabilities}}
@onRefresh={{action "refresh"}}
/>
{{/if}}
{{#if (eq mode "show") }}
{{#if (or capabilities.canUpdate capabilities.canDelete)}}
<ToolbarSecretLink
@secret={{key.id}}
@mode="edit"
replace=true
>
Edit encryption key
</ToolbarSecretLink>
{{/if}}
<ToolbarSecretLink
@secret={{key.id}}
@mode="actions"
@data-test-transit-key-actions-link=true
>
Key actions
</ToolbarSecretLink>
{{/if}}
</ToolbarActions>
</Toolbar>
{{#if (eq tab 'versions')}}
{{#if (or
(eq key.type "aes256-gcm96")
@ -161,14 +192,6 @@
{{/if}}
{{/each-in}}
{{/if}}
<p>
{{transit-key-actions
key=key
selectedAction="rotate"
capabilities=capabilities
onRefresh=(action "refresh")
}}
</p>
{{else}}
{{info-table-row label="Type" value=key.type}}
{{info-table-row label="Deletion allowed" value=(stringify key.deletionAllowed)}}

View File

@ -1,3 +1,3 @@
<svg width="{{size}}" height="{{size}}" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M8.338 2.255a.79.79 0 0 0-.645 0L.657 5.378c-.363.162-.534.538-.534.875 0 .337.171.713.534.875l1.436.637c-.332.495-.638 1.18-.744 2.106a.887.887 0 0 0-.26 1.559c.02.081.03.215.013.392-.02.205-.074.43-.162.636-.186.431-.45.64-.741.64v.98c.651 0 1.108-.365 1.403-.797l.06.073c.32.372.826.763 1.455.763v-.98c-.215 0-.474-.145-.71-.42-.111-.13-.2-.27-.259-.393a1.014 1.014 0 0 1-.06-.155c-.01-.036-.013-.055-.013-.058h-.022a2.544 2.544 0 0 0 .031-.641.886.886 0 0 0-.006-1.51c.1-.868.398-1.477.699-1.891l.332.147-.023.746v2.228c0 .115.04.22.105.304.124.276.343.5.587.677.297.217.675.396 1.097.54.846.288 1.943.456 3.127.456 1.185 0 2.281-.168 3.128-.456.422-.144.8-.323 1.097-.54.244-.177.462-.401.586-.677a.488.488 0 0 0 .106-.304V8.218l2.455-1.09c.363-.162.534-.538.534-.875 0-.337-.17-.713-.534-.875L8.338 2.255zm-.34 2.955L3.64 7.38l4.375 1.942 6.912-3.069-6.912-3.07-6.912 3.07 1.665.74 4.901-2.44.328.657zM14.307 1H12.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L14.307 1zm-2.368 7.653v2.383a.436.436 0 0 0-.007.021c-.017.063-.084.178-.282.322-.193.14-.473.28-.836.404-.724.247-1.71.404-2.812.404-1.1 0-2.087-.157-2.811-.404a3.188 3.188 0 0 1-.836-.404c-.198-.144-.265-.26-.282-.322a.437.437 0 0 0-.007-.02V8.983l.01-.338 3.617 1.605a.791.791 0 0 0 .645 0l3.6-1.598z" fill-rule="evenodd"/>
<path d="M13.307 1H11.5a.5.5 0 1 1 0-1h3a.499.499 0 0 1 .5.65V3.5a.5.5 0 1 1-1 0V1.72l-1.793 1.774a.5.5 0 0 1-.713-.701L13.307 1zM12 14V8a.5.5 0 1 1 1 0v6.5a.5.5 0 0 1-.5.5H.563a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 .5-.5H8a.5.5 0 0 1 0 1H1v12h11zM4 6a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1H4zm0 2.5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1H4zM4 11a.5.5 0 1 1 0-1h5a.5.5 0 1 1 0 1H4z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 470 B

View File

@ -0,0 +1,3 @@
<svg width={{size}} height={{size}} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 210 B

View File

@ -5,15 +5,16 @@
Control Groups
</h1>
</p.levelLeft>
<p.levelRight>
{{#if model.canConfigure}}
{{#link-to 'vault.cluster.access.control-groups-configure' class="button has-icon-right is-ghost is-compact"}}
Configure
<ICon @glyph="chevron-right" @size=11 />
{{/link-to}}
{{/if}}
</p.levelRight>
</PageHeader>
{{#if model.canConfigure}}
<Toolbar>
<ToolbarActions>
<ToolbarButton @params={{array 'vault.cluster.access.control-groups-configure'}}>
Configure
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{/if}}
<ControlGroup @model={{model}} />
{{else}}
<UpgradePage @title="Control Groups" @minimumEdition="Vault Enterprise Premium" />

View File

@ -5,15 +5,16 @@
Control Groups
</h1>
</p.levelLeft>
<p.levelRight>
{{#if model.canConfigure}}
{{#link-to 'vault.cluster.access.control-groups-configure' class="button has-icon-right is-ghost is-compact"}}
Configure
<ICon @glyph="chevron-right" @size=11 />
{{/link-to}}
{{/if}}
</p.levelRight>
</PageHeader>
{{#if model.canConfigure}}
<Toolbar>
<ToolbarActions>
<ToolbarButton @params={{array 'vault.cluster.access.control-groups-configure'}}>
Configure
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{/if}}
<form {{action (nav-to-route 'vault.cluster.access.control-group-accessor' model.id) on="submit"}}>
<div class="box is-sideless is-fullwidth is-marginless">
<p class="has-text-grey is-size-8">

View File

@ -16,14 +16,8 @@
{{model.name}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#link-to "vault.cluster.access.identity.aliases.edit" model.id class="button has-icon-right is-ghost is-compact" data-test-alias-edit-link=true}}
Edit {{lowercase (humanize model.identityType)}}
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</p.levelRight>
</PageHeader>
<div class="box is-sideless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs sub-nav">
<ul>
{{#each (tabs-for-identity-show model.identityType) as |tab|}}
@ -36,4 +30,14 @@
</ul>
</nav>
</div>
<Toolbar>
<ToolbarActions>
<ToolbarButton
@params={{array "vault.cluster.access.identity.aliases.edit" model.id}}
data-test-alias-edit-link=true
>
Edit {{lowercase (humanize model.identityType)}}
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{component (concat 'identity/item-alias/alias-' section) model=model}}

View File

@ -16,20 +16,8 @@
{{model.name}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#unless (or (and (eq model.identityType "group") (eq model.type "internal")) model.alias)}}
{{#link-to "vault.cluster.access.identity.aliases.add" (pluralize model.identityType) model.id class="button has-icon-right is-ghost is-compact" data-test-entity-create-link=true}}
Create alias
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
{{/unless}}
{{#link-to "vault.cluster.access.identity.edit" (pluralize model.identityType) model.id class="button has-icon-right is-ghost is-compact" data-test-entity-edit-link=true}}
Edit {{model.identityType}}
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</p.levelRight>
</PageHeader>
<div class="box is-sideless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
<nav class="tabs sub-nav">
<ul>
{{#each (tabs-for-identity-show model.identityType model.type) as |tab|}}
@ -42,4 +30,18 @@
</ul>
</nav>
</div>
<Toolbar>
<ToolbarActions>
{{#unless (or (and (eq model.identityType "group") (eq model.type "internal")) model.alias)}}
{{#link-to "vault.cluster.access.identity.aliases.add" (pluralize model.identityType) model.id class="button has-icon-right is-ghost is-compact" data-test-entity-create-link=true}}
Add alias
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
{{/unless}}
{{#link-to "vault.cluster.access.identity.edit" (pluralize model.identityType) model.id class="button has-icon-right is-ghost is-compact" data-test-entity-edit-link=true}}
Edit {{model.identityType}}
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</ToolbarActions>
</Toolbar>
{{component (concat 'identity/item-' section) model=model}}

View File

@ -15,53 +15,10 @@
{{/link-to}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
{{#if (not-eq baseKey.id '')}}
<div class="control">
{{#if (and capabilities.forceRevokePrefix.canUpdate (not confirmingRevoke))}}
{{#confirm-action
buttonClasses="button is-ghost is-compact"
onConfirmAction=(action "revokePrefix" baseKey.id true)
confirmMessage= (concat
"Confirming removes all leases under "
baseKey.id
" and disregards any errors encountered."
)
confirmButtonText=(concat "Revoke " baseKey.id)
confirmButtonClasses="button is-danger is-outlined is-compact"
cancelButtonText="Cancel"
cancelButtonClasses="button is-outlined is-compact"
showConfirm=confirmingForceRevoke
}}
Force revoke prefix
{{/confirm-action}}
{{/if}}
</div>
<div class="control">
{{#if (and capabilities.revokePrefix.canUpdate (not confirmingForceRevoke))}}
{{#confirm-action
buttonClasses="button is-ghost is-compact"
onConfirmAction=(action "revokePrefix" baseKey.id)
confirmMessage= (concat "Confirming will remove all leases under " baseKey.id)
confirmButtonText=(concat "Revoke " baseKey.id)
confirmButtonClasses="button is-danger is-outlined is-compact"
cancelButtonText="Cancel"
cancelButtonClasses="button is-outlined is-compact"
showConfirm=confirmingRevoke
data-test-lease-revoke-prefix=true
}}
Revoke prefix
{{/confirm-action}}
{{/if}}
</div>
{{/if}}
</div>
</p.levelRight>
</PageHeader>
<div class="box is-sideless has-background-grey-lighter has-short-padding is-marginless">
<div class="level">
<div class="level-left">
<Toolbar>
<ToolbarFilters>
{{navigate-input
filterFocusDidChange=(action "setFilterFocus")
filterDidChange=(action "setFilter")
@ -89,9 +46,49 @@
</p>
{{/if}}
{{/if}}
</div>
</div>
</div>
</ToolbarFilters>
<ToolbarActions>
{{#if (not-eq baseKey.id '')}}
<div class="control">
{{#if (and capabilities.forceRevokePrefix.canUpdate (not confirmingRevoke))}}
{{#confirm-action
buttonClasses="button is-ghost is-compact"
onConfirmAction=(action "revokePrefix" baseKey.id true)
confirmMessage= (concat
"Confirming removes all leases under "
baseKey.id
" and disregards any errors encountered."
)
confirmButtonText=(concat "Revoke " baseKey.id)
confirmButtonClasses="button is-danger is-outlined is-compact"
cancelButtonText="Cancel"
cancelButtonClasses="button is-outlined is-compact"
showConfirm=confirmingForceRevoke
}}
Force revoke prefix
{{/confirm-action}}
{{/if}}
</div>
<div class="control">
{{#if (and capabilities.revokePrefix.canUpdate (not confirmingForceRevoke))}}
{{#confirm-action
buttonClasses="button is-ghost is-compact"
onConfirmAction=(action "revokePrefix" baseKey.id)
confirmMessage= (concat "Confirming will remove all leases under " baseKey.id)
confirmButtonText=(concat "Revoke " baseKey.id)
confirmButtonClasses="button is-danger is-outlined is-compact"
cancelButtonText="Cancel"
cancelButtonClasses="button is-outlined is-compact"
showConfirm=confirmingRevoke
data-test-lease-revoke-prefix=true
}}
Revoke prefix
{{/confirm-action}}
{{/if}}
</div>
{{/if}}
</ToolbarActions>
</Toolbar>
{{#if model.meta.total}}
{{#each model as |item|}}

View File

@ -12,25 +12,21 @@
{{capitalize model.methodType}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#if (eq section "configuration")}}
<div class="field is-grouped">
<div class="control">
{{#link-to
"vault.cluster.settings.auth.configure"
model.id
class="button is-ghost has-icon-right is-compact"
data-test-configure-link=true
}}
Configure
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</div>
</div>
{{/if}}
</p.levelRight>
</PageHeader>
{{section-tabs model 'authShow'}}
{{component (concat "auth-method/" section) model=model}}
{{#if (eq section "configuration")}}
<Toolbar>
<ToolbarActions>
<ToolbarButton
@params={{array "vault.cluster.settings.auth.configure" model.id}}
data-test-configure-link=true
>
Configure
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{/if}}
{{component (concat "auth-method/" section) model=model}}

View File

@ -4,14 +4,16 @@
Authentication Methods
</h1>
</p.levelLeft>
<p.levelRight>
{{#link-to 'vault.cluster.settings.auth.enable' class="button has-icon-right is-ghost is-compact"}}
Enable new method
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</p.levelRight>
</PageHeader>
<Toolbar>
<ToolbarActions>
<ToolbarButton @params={{array 'vault.cluster.settings.auth.enable'}}>
Enable new method
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{#each (sort-by "path" model) as |method|}}
<div
class="list-item-row"

View File

@ -5,13 +5,16 @@
Namespaces
</h1>
</p.levelLeft>
<p.levelRight>
{{#link-to 'vault.cluster.access.namespaces.create' class="button has-icon-right is-ghost is-compact"}}
Create a Namespace
<ICon @glyph="chevron-right" @size=11 />
{{/link-to}}
</p.levelRight>
</PageHeader>
<Toolbar>
<ToolbarActions>
<ToolbarButton @params={{array 'vault.cluster.access.namespaces.create'}}>
Create namespace
</ToolbarButton>
</ToolbarActions>
</Toolbar>
<ListView @items={{model}} @itemNoun="namespace" @emptyActions="empty-action-namespaces" as |list|>
<ListItem as |Item|>
<Item.content>

View File

@ -4,31 +4,6 @@
Sign in to Vault
</h1>
</Page.header>
{{#if (has-feature "Namespaces")}}
<Page.sub-header>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="is-label" for="namespace">Namespace</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input
value={{namespaceQueryParam}}
placeholder="/ (Root)"
oninput={{perform updateNamespace value="target.value"}}
autocomplete="off"
name="namespace"
id="namespace"
class="input"
type="text"
/>
</div>
</div>
</div>
</div>
</Page.sub-header>
{{/if}}
<Page.content>
<AuthForm
@wrappedToken={{wrappedToken}}

View File

@ -10,42 +10,42 @@
{{/unless}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#link-to "vault.cluster.policies.create" class="button has-icon-right is-ghost is-compact" data-test-policy-create-link=true}}
Create {{uppercase policyType}} policy
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</p.levelRight>
</PageHeader>
{{#if model.meta.total}}
<div class="box is-sideless has-background-grey-lighter has-short-padding is-marginless">
<div class="level">
<div class="level-left">
{{navigate-input
filterFocusDidChange=(action "setFilterFocus")
filterDidChange=(action "setFilter")
filter=filter
filterMatchesKey=filterMatchesKey
firstPartialMatch=firstPartialMatch
extraNavParams=policyType
placeholder="Filter policies"
mode="policy"
}}
{{#if filterFocused}}
{{#if filterMatchesKey}}
<p class="input-hint">
<kbd>ENTER</kbd> to go to <code>{{or pageFilter filter}}</code>
</p>
{{/if}}
{{#if firstPartialMatch}}
<p class="input-hint">
<kbd>TAB</kbd> to complete <code>{{firstPartialMatch.id}}</code>
</p>
{{/if}}
<Toolbar>
<ToolbarFilters>
{{navigate-input
filterFocusDidChange=(action "setFilterFocus")
filterDidChange=(action "setFilter")
filter=filter
filterMatchesKey=filterMatchesKey
firstPartialMatch=firstPartialMatch
extraNavParams=policyType
placeholder="Filter policies"
mode="policy"
}}
{{#if filterFocused}}
{{#if filterMatchesKey}}
<p class="input-hint">
<kbd>ENTER</kbd> to go to <code>{{or pageFilter filter}}</code>
</p>
{{/if}}
</div>
</div>
</div>
{{#if firstPartialMatch}}
<p class="input-hint">
<kbd>TAB</kbd> to complete <code>{{firstPartialMatch.id}}</code>
</p>
{{/if}}
{{/if}}
</ToolbarFilters>
<ToolbarActions>
<ToolbarButton
@params={{array 'vault.cluster.policies.create'}}
data-test-policy-create-link=true
>
Create {{uppercase policyType}} policy
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{#each model as |item|}}
{{#if (eq item.id "root")}}
<div class="list-item-row is-flex" data-test-policy-item>

View File

@ -20,33 +20,35 @@
</h1>
</p.levelLeft>
</PageHeader>
{{#if (and (not-eq model.id "root") (or capabilities.canUpdate capabilities.canDelete))}}
<Toolbar>
<ToolbarActions>
<ToolbarButton
@params={{array 'vault.cluster.policy.show' model.id}}
data-test-policy-edit-toggle=true
>
Back to policy
</ToolbarButton>
<div class="toolbar-separator" />
{{#if (and (not-eq model.id "default") capabilities.canDelete)}}
{{#confirm-action
buttonClasses="toolbar-button"
onConfirmAction=(action "deletePolicy" model)
confirmMessage=(concat "Are you sure you want to delete " model.id "?")
data-test-policy-delete=true
}}
Delete
<ICon @glyph="chevron-right" @size=12 />
{{/confirm-action}}
{{/if}}
</ToolbarActions>
</Toolbar>
{{/if}}
<form {{action "savePolicy" model on="submit"}}>
<div class="box is-bottomless is-fullwidth is-marginless">
{{message-error model=model}}
<NamespaceReminder @mode="edit" @noun="policy" />
<div class="level is-mobile">
<div class="level-left">
<label for="policy" class="is-label">Policy</label>
</div>
<div class="level-right">
<div class="field is-horizontal is-flex-end is-single-line">
{{#if (and (not-eq model.id "root") (or capabilities.canUpdate capabilities.canDelete))}}
<div class="control is-flex">
{{input
id="edit"
type="checkbox"
name="navToEdit"
class="switch is-rounded is-success is-small"
checked=true
change=(action (nav-to-route 'vault.cluster.policy.show' model.id replace=true) )
data-test-policy-edit-toggle=true
}}
<label for="edit">Edit</label>
</div>
{{/if}}
</div>
</div>
</div>
<label for="policy" class="is-label">Policy</label>
<div class="field">
{{ivy-codemirror
value=model.policy
@ -90,16 +92,6 @@
{{/link-to}}
</div>
</div>
{{#if (and (not-eq model.id "default") capabilities.canDelete)}}
{{#confirm-action
buttonClasses="button is-ghost"
onConfirmAction=(action "deletePolicy" model)
confirmMessage=(concat "Are you sure you want to delete " model.id "?")
data-test-policy-delete=true
}}
Delete
{{/confirm-action}}
{{/if}}
</div>
</div>
</form>

View File

@ -20,34 +20,35 @@
</h1>
</p.levelLeft>
</PageHeader>
<Toolbar>
<ToolbarActions>
<ToolbarDownloadButton
@classNames="toolbar-button"
@actionText="Download policy"
@extension={{if (eq policyType "acl") model.format "sentinel"}}
@filename=model.name
@data=model.policy
/>
{{#if (and (not-eq model.id "root") (or capabilities.canUpdate capabilities.canDelete))}}
<ToolbarButton
@params={{array 'vault.cluster.policy.edit' model.id}}
data-test-policy-edit-toggle=true
>
Edit policy
</ToolbarButton>
{{/if}}
</ToolbarActions>
</Toolbar>
<div class="box is-bottomless is-fullwidth is-marginless">
<div class="level is-mobile">
<div class="level-left">
<label for="policy" class="is-label">Policy</label>
{{#if (eq policyType "acl")}}
<span class="tag is-white is-size-9" data-test-acl-format>({{uppercase model.format}} format)</span>
{{/if}}
</div>
<div class="level-right">
<div class="field is-horizontal is-flex-end is-single-line">
{{#if (and (not-eq model.id "root") (or capabilities.canUpdate capabilities.canDelete))}}
<div class="control is-flex">
{{input
id="edit"
type="checkbox"
name="navToEdit"
class="switch is-rounded is-success is-small"
checked=false
change=(action (nav-to-route 'vault.cluster.policy.edit' model.id replace=true) )
data-test-policy-edit-toggle=true
}}
<label for="edit">Edit</label>
</div>
{{/if}}
</div>
</div>
</div>
<div class="field">
<label for="policy" class="is-label">
Policy
{{#if (eq policyType "acl")}}
<span class="tag is-white is-size-9 has-text-grey" data-test-acl-format>
({{uppercase model.format}} format)
</span>
{{/if}}
</label>
{{ivy-codemirror
value=model.policy
options=(hash
@ -71,13 +72,4 @@
</ul>
</div>
{{/if}}
<div class="field box is-shadowless no-bottom-padding is-marginless">
{{download-button
classNames="link is-pulled-right"
actionText="Download policy"
extension=(if (eq policyType "acl") model.format "sentinel")
filename=model.name
data=model.policy
}}
</div>
</div>

View File

@ -25,7 +25,7 @@
</h1>
</p.levelLeft>
</PageHeader>
<div class="box is-bottomless is-fullwidth is-paddingless is-marginless">
<div class="tabs-container box is-bottomless is-fullwidth is-paddingless is-marginless">
<nav class="tabs sub-nav">
<ul>
<li class="{{if (is-active-route 'vault.cluster.replication.mode.index') 'is-active' ''}}">

View File

@ -33,6 +33,7 @@
data-test-delete-mount-config=true
}}
Delete
<ICon @glyph="chevron-right" @size=12 />
{{/confirm-action}}
</div>
</form>

View File

@ -1,4 +1,26 @@
{{#if model.replicationAttrs.isPrimary}}
<Toolbar>
<ToolbarActions>
{{#if model.replicationAttrs.knownSecondaries.length}}
{{#if model.canRevokeSecondary}}
<ToolbarButton
@params={{array 'vault.cluster.replication.mode.secondaries.revoke' model.name replicationMode}}
data-test-secondary-add=true
>
Revoke secondary
</ToolbarButton>
{{/if}}
{{/if}}
{{#if model.canAddSecondary}}
<ToolbarAddButton
@params={{array 'vault.cluster.replication.mode.secondaries.add' model.name replicationMode}}
@data-test-secondary-add=true
>
Add secondary
</ToolbarAddButton>
{{/if}}
</ToolbarActions>
</Toolbar>
{{#if model.replicationAttrs.knownSecondaries.length}}
{{#each model.replicationAttrs.knownSecondaries as |secondary|}}
<div class="box is-shadowless is-marginless" data-test-secondary-name={{secondary}}>
@ -60,20 +82,4 @@
</DocLink>
</EmptyState>
{{/if}}
<div class="field is-grouped box is-shadowless is-fullwidth">
{{#if model.canAddSecondary}}
<div class="control">
{{#link-to 'vault.cluster.replication.mode.secondaries.add' model.name replicationMode class="button" data-test-secondary-add=true }}
Add
{{/link-to}}
</div>
{{/if}}
{{#if model.canRevokeSecondary}}
<div class="control">
{{#link-to 'vault.cluster.replication.mode.secondaries.revoke' model.name replicationMode class="button"}}
Revoke
{{/link-to}}
</div>
{{/if}}
</div>
{{/if}}

View File

@ -1,11 +1,24 @@
{{secret-list-header model=model isConfigure=true backendCrumb=(hash label=model.id text=model.id path="vault.cluster.secrets.backend.list-root" model=model.id)}}
{{#if (or (eq model.type "aws") (eq model.type "ssh") (eq model.type "pki"))}}
<Toolbar>
<ToolbarActions>
<ToolbarButton
@params={{array 'vault.cluster.settings.configure-secret-backend' model.id}}
data-test-secret-backend-configure=true
>
Configure
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{/if}}
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#each model.attrs as |attr|}}
{{#if (eq attr.type "object")}}
{{info-table-row
alwaysRender=true
label=(or attr.options.label (to-label attr.name))
label=(or attr.options.label (to-label attr.name))
value=(stringify (get model attr.name))
}}
{{else}}

View File

@ -1,10 +1,10 @@
{{secret-list-header isCertTab=(eq tab "certs") model=backendModel baseKey=baseKey backendCrumb=backendCrumb filter=filter}}
{{#with (options-for-backend backendType tab) as |options|}}
{{#if model.meta.total}}
<div class="box is-sideless has-background-grey-lighter has-short-padding is-marginless">
<div class="level">
<div class="level-left">
{{#if (or model.meta.total (or (or (eq model.type "aws") (eq model.type "ssh") (eq model.type "pki")) (not (or isCertTab isConfigure))))}}
<Toolbar>
{{#if model.meta.total}}
<ToolbarFilters>
{{navigate-input
enterpriseProduct="vault"
filterFocusDidChange=(action "setFilterFocus")
@ -32,9 +32,24 @@
</p>
{{/if}}
{{/if}}
</div>
</div>
</div>
</ToolbarFilters>
{{/if}}
<ToolbarActions>
{{#unless (or isCertTab isConfigure)}}
<ToolbarSecretLink
@secret=''
@mode="create"
@glyph="plus-plain"
@queryParams={{query-params initialKey=(or filter baseKey.id)}}
@data-test-secret-create=true
>
{{options.create}}
</ToolbarSecretLink>
{{/unless}}
</ToolbarActions>
</Toolbar>
{{/if}}
{{#if model.meta.total}}
{{#each model as |item|}}
{{partial options.listItemPartial}}
{{else}}

View File

@ -30,23 +30,25 @@
{{model.id}}
</h1>
</p.levelLeft>
<p.levelRight>
{{#secret-link
mode="show"
secret=model.id
class="button has-icon-right is-ghost is-compact"
}}
Details
{{i-con glyph="chevron-right" size=11}}
{{/secret-link}}
</p.levelRight>
</PageHeader>
{{transit-key-actions
selectedAction=selectedAction
backend=backend
key=model
capabilities=capabilities
onRefresh=(action "refresh")
}}
<Toolbar>
<ToolbarActions>
<ToolbarSecretLink
@secret={{model.id}}
@mode="show"
>
Details
</ToolbarSecretLink>
</ToolbarActions>
</Toolbar>
<TransitKeyActions
@selectedAction={{selectedAction}}
@backend={{backend}}
@key={{model}}
@capabilities={{capabilities}}
@onRefresh={{action "refresh"}}
/>
</div>
</div>

View File

@ -4,14 +4,16 @@
Secrets Engines
</h1>
</p.levelLeft>
<p.levelRight>
{{#link-to 'vault.cluster.settings.mount-secret-backend' class="button has-icon-right is-ghost is-compact"}}
Enable new engine
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</p.levelRight>
</PageHeader>
<Toolbar>
<ToolbarActions>
<ToolbarAddButton @params={{array 'vault.cluster.settings.mount-secret-backend'}}>
Enable new engine
</ToolbarAddButton>
</ToolbarActions>
</Toolbar>
{{#each supportedBackends as |backend|}}
{{#linked-block
"vault.cluster.secrets.backend.list-root"

View File

@ -12,22 +12,19 @@
Configure {{get (find-by "type" model.type (mountable-auth-methods)) "displayName"}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
<div class="control">
{{#link-to
"vault.cluster.access.method"
model.id
class="button is-ghost has-icon-right is-compact"
data-test-backend-view-link=true
}}
View method
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</div>
</div>
</p.levelRight>
</PageHeader>
{{section-tabs model}}
<Toolbar>
<ToolbarActions>
<ToolbarButton
@params={{array "vault.cluster.access.method" model.id}}
data-test-backend-view-link=true
>
View method
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{outlet}}

View File

@ -12,22 +12,18 @@
Configure {{get (options-for-backend model.type) "displayName"}}
</h1>
</p.levelLeft>
<p.levelRight>
<div class="field is-grouped">
<div class="control">
{{#link-to
"vault.cluster.secrets.backend"
model.id
class="button has-icon-right is-ghost is-compact"
data-test-backend-view-link=true
}}
View backend
{{i-con glyph="chevron-right" size=11}}
{{/link-to}}
</div>
</div>
</p.levelRight>
</PageHeader>
<Toolbar>
<ToolbarActions>
<ToolbarButton
@params={{array "vault.cluster.secrets.backend" model.id}}
data-test-backend-view-link=true
>
View backend
</ToolbarButton>
</ToolbarActions>
</Toolbar>
{{partial (concat "partials/secret-backend-settings/" model.type)}}
{{outlet}}

View File

@ -33,6 +33,7 @@ module('Acceptance | aws secret backend', function(hooks) {
await enablePage.enable('aws', path);
await click('[data-test-configuration-tab]');
await click('[data-test-secret-backend-configure]');
assert.equal(currentURL(), `/vault/settings/secrets/configure/${path}`);
assert.ok(findAll('[data-test-aws-root-creds-form]').length, 'renders the empty root creds form');
@ -58,7 +59,7 @@ module('Acceptance | aws secret backend', function(hooks) {
await click('[data-test-backend-view-link]');
assert.equal(currentURL(), `/vault/secrets/${path}/list`, `navigates to the roles list`);
await click('[ data-test-secret-create]');
await click('[data-test-secret-create]');
assert.ok(
find('[data-test-secret-header]').textContent.includes('AWS Role'),
`aws: renders the create page`

View File

@ -1,4 +1,4 @@
import { currentRouteName, settled } from '@ember/test-helpers';
import { click, currentRouteName, settled } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import page from 'vault/tests/pages/secrets/backend/list';
@ -22,6 +22,7 @@ module('Acceptance | secrets/pki/list', function(hooks) {
await mountAndNav(assert);
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.list-root', 'redirects from the index');
assert.ok(page.createIsPresent, 'create button is present');
await click('[data-test-configuration-tab]');
assert.ok(page.configureIsPresent, 'configure button is present');
assert.equal(page.tabs.length, 2, 'shows 2 tabs');
assert.ok(page.backendIsEmpty);
@ -35,6 +36,7 @@ module('Acceptance | secrets/pki/list', function(hooks) {
test('it navigates to the configure page', async function(assert) {
await mountAndNav(assert);
await click('[data-test-configuration-tab]');
await page.configure();
await settled();
assert.equal(

View File

@ -63,6 +63,7 @@ module('Acceptance | ssh secret backend', function(hooks) {
const sshPath = `ssh-${now}`;
await enablePage.enable('ssh', sshPath);
await click('[data-test-configuration-tab]');
await click('[data-test-secret-backend-configure]');
assert.equal(currentURL(), `/vault/settings/secrets/configure/${sshPath}`);
assert.ok(findAll('[data-test-ssh-configure-form]').length, 'renders the empty configuration form');

View File

@ -0,0 +1,16 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { isPresent } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | toolbar-actions', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<ToolbarActions>These are the toolbar actions</ToolbarActions>`);
assert.equal(this.element.textContent.trim(), 'These are the toolbar actions');
assert.ok(isPresent('.toolbar-actions'));
});
});

View File

@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { isPresent } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | toolbar-add-button', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<ToolbarAddButton @params={{array "/"}}>Link</ToolbarAddButton>`);
assert.equal(this.element.textContent.trim(), 'Link');
assert.ok(isPresent('.toolbar-button'));
assert.ok(isPresent('.icon'));
});
});

View File

@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { isPresent } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | toolbar-button', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<ToolbarButton @params="/secrets">Link</ToolbarButton>`);
assert.equal(this.element.textContent.trim(), 'Link');
assert.ok(isPresent('.toolbar-button'));
assert.ok(isPresent('.icon'));
});
});

View File

@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { isPresent } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | toolbar-download-button', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<ToolbarDownloadButton @actionText="Link" />`);
assert.equal(this.element.textContent.trim(), 'Link');
assert.ok(isPresent('.toolbar-button'));
assert.ok(isPresent('.icon'));
});
});

View File

@ -0,0 +1,16 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { isPresent } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | toolbar-filters', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<ToolbarFilters>These are the toolbar filters</ToolbarFilters>`);
assert.equal(this.element.textContent.trim(), 'These are the toolbar filters');
assert.ok(isPresent('.toolbar-filters'));
});
});

View File

@ -0,0 +1,17 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { isPresent } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | toolbar', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
await render(hbs`<Toolbar>This is the toolbar content</Toolbar>`);
assert.equal(this.element.textContent.trim(), 'This is the toolbar content');
assert.ok(isPresent('.toolbar'));
assert.ok(isPresent('.toolbar-scroller'));
});
});