Merge branch 'master' into respond-upstream-throttle
This commit is contained in:
commit
8f869999f2
|
@ -15,15 +15,21 @@ IMPROVEMENTS:
|
|||
|
||||
* AliCloud Agent Support: Vault Agent can now authenticate against the
|
||||
AliCloud auth method.
|
||||
* UI: Enable AliCloud auth method and Azure secrets engine via the UI.
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* core: Properly check error return from random byte reading [GH-5277]
|
||||
* core: Re-add `sys/` top-route injection for now [GH-5241]
|
||||
* core: Properly store the replication checkpoint file if it's larger than the
|
||||
storage engine's per-item limit
|
||||
* secrets/database: Fix nil pointer when revoking some leases [GH-5262]
|
||||
* secrets/pki: Fix sign-verbatim losing extra Subject attributes [GH-5245]
|
||||
* secrets/pki: Remove certificates from store when tidying revoked
|
||||
certificates [GH-5231]
|
||||
certificates and simplify API [GH-5231]
|
||||
* ui: JSON editor will not coerce input to an object, and will now show an
|
||||
error about Vault expecting an object [GH-5271]
|
||||
|
||||
|
||||
## 0.11.0 (August 28th, 2018)
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ func TestBackend_CA_Steps(t *testing.T) {
|
|||
NotBefore: time.Now().Add(-30 * time.Second),
|
||||
NotAfter: time.Now().Add(262980 * time.Hour),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
IsCA: true,
|
||||
}
|
||||
caBytes, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, cak.Public(), cak)
|
||||
if err != nil {
|
||||
|
@ -107,7 +107,7 @@ func TestBackend_CA_Steps(t *testing.T) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
subjKeyID, err = certutil.GetSubjKeyID(rak)
|
||||
_, err = certutil.GetSubjKeyID(rak)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -437,6 +437,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName,
|
|||
}
|
||||
|
||||
verifyRevocation := func(t *testing.T, serial string, shouldFind bool) {
|
||||
t.Helper()
|
||||
// Verify it is now revoked
|
||||
{
|
||||
resp, err := client.Logical().Read(rootName + "cert/" + intSerialNumber)
|
||||
|
|
|
@ -23,6 +23,11 @@ func pathTidy(b *backend) *framework.Path {
|
|||
the certificate store`,
|
||||
},
|
||||
|
||||
"tidy_revocation_list": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: `Deprecated; synonym for 'tidy_revoked_certs`,
|
||||
},
|
||||
|
||||
"tidy_revoked_certs": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: `Set to true to expire all revoked
|
||||
|
@ -54,6 +59,7 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
|
|||
safetyBuffer := d.Get("safety_buffer").(int)
|
||||
tidyCertStore := d.Get("tidy_cert_store").(bool)
|
||||
tidyRevokedCerts := d.Get("tidy_revoked_certs").(bool)
|
||||
tidyRevocationList := d.Get("tidy_revocation_list").(bool)
|
||||
|
||||
if safetyBuffer < 1 {
|
||||
return logical.ErrorResponse("safety_buffer must be greater than zero"), nil
|
||||
|
@ -121,7 +127,7 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
|
|||
}
|
||||
}
|
||||
|
||||
if tidyRevokedCerts {
|
||||
if tidyRevokedCerts || tidyRevocationList {
|
||||
b.revokeStorageLock.Lock()
|
||||
defer b.revokeStorageLock.Unlock()
|
||||
|
||||
|
|
|
@ -62,8 +62,11 @@ export default Component.extend({
|
|||
|
||||
getConfigModelType(methodType) {
|
||||
let mountType = this.get('mountType');
|
||||
let noConfig = ['approle'];
|
||||
if (mountType === 'secret' || noConfig.includes(methodType)) {
|
||||
// will be something like secret-aws
|
||||
// or auth-azure
|
||||
let key = `${mountType}-${methodType}`;
|
||||
let noConfig = ['auth-approle', 'auth-alicloud'];
|
||||
if (mountType === 'secret' || noConfig.includes(key)) {
|
||||
return;
|
||||
}
|
||||
if (methodType === 'aws') {
|
||||
|
|
|
@ -30,6 +30,7 @@ export default Ember.Component.extend(FocusOnInsertMixin, {
|
|||
// use a named action here so we don't have to pass one in
|
||||
// this will bubble to the route
|
||||
toggleAdvancedEdit: 'toggleAdvancedEdit',
|
||||
error: null,
|
||||
|
||||
codemirrorString: null,
|
||||
|
||||
|
@ -79,7 +80,8 @@ export default Ember.Component.extend(FocusOnInsertMixin, {
|
|||
'key.isFolder',
|
||||
'key.isError',
|
||||
'key.flagsIsInvalid',
|
||||
'hasLintError'
|
||||
'hasLintError',
|
||||
'error'
|
||||
),
|
||||
|
||||
basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function() {
|
||||
|
@ -242,10 +244,15 @@ export default Ember.Component.extend(FocusOnInsertMixin, {
|
|||
},
|
||||
|
||||
codemirrorUpdated(val, codemirror) {
|
||||
this.set('error', null);
|
||||
codemirror.performLint();
|
||||
const noErrors = codemirror.state.lint.marked.length === 0;
|
||||
if (noErrors) {
|
||||
this.get('secretData').fromJSONString(val);
|
||||
try {
|
||||
this.get('secretData').fromJSONString(val);
|
||||
} catch (e) {
|
||||
this.set('error', e.message);
|
||||
}
|
||||
}
|
||||
this.set('hasLintError', !noErrors);
|
||||
this.set('codemirrorString', val);
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const MOUNTABLE_AUTH_METHODS = [
|
||||
{
|
||||
displayName: 'AliCloud',
|
||||
value: 'alicloud',
|
||||
type: 'alicloud',
|
||||
category: 'cloud',
|
||||
},
|
||||
{
|
||||
displayName: 'AppRole',
|
||||
value: 'approle',
|
||||
|
@ -79,7 +85,7 @@ const MOUNTABLE_AUTH_METHODS = [
|
|||
];
|
||||
|
||||
export function methods() {
|
||||
return MOUNTABLE_AUTH_METHODS;
|
||||
return MOUNTABLE_AUTH_METHODS.slice();
|
||||
}
|
||||
|
||||
export default Ember.Helper.helper(methods);
|
||||
|
|
|
@ -13,6 +13,12 @@ const MOUNTABLE_SECRET_ENGINES = [
|
|||
type: 'aws',
|
||||
category: 'cloud',
|
||||
},
|
||||
{
|
||||
displayName: 'Azure',
|
||||
value: 'azure',
|
||||
type: 'azure',
|
||||
category: 'cloud',
|
||||
},
|
||||
{
|
||||
displayName: 'Consul',
|
||||
value: 'consul',
|
||||
|
@ -76,7 +82,7 @@ const MOUNTABLE_SECRET_ENGINES = [
|
|||
];
|
||||
|
||||
export function engines() {
|
||||
return MOUNTABLE_SECRET_ENGINES;
|
||||
return MOUNTABLE_SECRET_ENGINES.slice();
|
||||
}
|
||||
|
||||
export default Ember.Helper.helper(engines);
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { typeOf, guidFor } = Ember;
|
||||
|
||||
export default Ember.ArrayProxy.extend({
|
||||
fromJSON(json) {
|
||||
const contents = Object.keys(json || []).map(key => {
|
||||
if (json && typeOf(json) !== 'object') {
|
||||
throw new Error('Vault expects data to be formatted as an JSON object.');
|
||||
}
|
||||
let contents = Object.keys(json || []).map(key => {
|
||||
let obj = {
|
||||
name: key,
|
||||
value: json[key],
|
||||
};
|
||||
Ember.guidFor(obj);
|
||||
guidFor(obj);
|
||||
return obj;
|
||||
});
|
||||
this.setObjects(
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
<div class="wizard-header">
|
||||
<PopupMenu @class="wizard-dismiss-menu">
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<button type="button" class="button link " onclick={{action "dismissWizard"}}>
|
||||
Dismiss
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</PopupMenu>
|
||||
{{#unless hidePopup}}
|
||||
<PopupMenu @class="wizard-dismiss-menu">
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<button type="button" class="button link " onclick={{action "dismissWizard"}}>
|
||||
Dismiss
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</PopupMenu>
|
||||
{{/unless}}
|
||||
<h1 class="title is-5">
|
||||
<ICon @glyph={{glyph}} @size="21" /> {{headerText}}
|
||||
</h1>
|
||||
|
|
10
ui/app/templates/components/wizard/alicloud-method.hbs
Normal file
10
ui/app/templates/components/wizard/alicloud-method.hbs
Normal file
|
@ -0,0 +1,10 @@
|
|||
<WizardSection
|
||||
@headerText="AliCloud"
|
||||
@headerIcon="enable/alicloud"
|
||||
@docText="Docs: AliCloud Authentication"
|
||||
@docPath="/docs/auth/alicloud.html"
|
||||
>
|
||||
<p>
|
||||
The AliCloud auth method provides an automated mechanism to retrieve a Vault token for AliCloud entities.
|
||||
</p>
|
||||
</WizardSection>
|
10
ui/app/templates/components/wizard/azure-engine.hbs
Normal file
10
ui/app/templates/components/wizard/azure-engine.hbs
Normal file
|
@ -0,0 +1,10 @@
|
|||
<WizardSection
|
||||
@headerText="Azure"
|
||||
@headerIcon="enable/azure"
|
||||
@docText="Docs: Azure Secrets"
|
||||
@docPath="/docs/secrets/azure/index.html"
|
||||
>
|
||||
<p>
|
||||
The Azure secrets engine dynamically generates Azure service principals and role assignments. Vault roles can be mapped to one or more Azure roles, providing a simple, flexible way to manage the permissions granted to generated service principals.
|
||||
</p>
|
||||
</WizardSection>
|
|
@ -1,4 +1,12 @@
|
|||
<WizardContent @headerText="Welcome to Vault" @glyph="tour" @class="collapsed">
|
||||
<WizardContent
|
||||
@headerText="Welcome to Vault"
|
||||
@glyph="tour"
|
||||
@class="collapsed"
|
||||
@hidePopup={{true}}
|
||||
>
|
||||
<button type="button" class="button is-transparent icon dismiss-collapsed" {{action onDismiss}}>
|
||||
<ICon @glyph="close" />
|
||||
</button>
|
||||
<p>Want a tour? Our helpful guide will introduce you to the Vault Web UI.</p>
|
||||
<div class="box wizard-divider-box">
|
||||
<button type="button" class="button is-transparent has-icon-left has-text-white" {{action onAdvance}}>
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
<WizardContent @headerText="Vault Web UI Guide" @glyph="tour" @class="collapsed">
|
||||
|
||||
<button type="button" class="button is-transparent icon dismiss-collapsed" {{action onDismiss}}>
|
||||
<ICon @glyph="close" />
|
||||
</button>
|
||||
<WizardContent
|
||||
@headerText="Vault Web UI Guide"
|
||||
@glyph="tour"
|
||||
@class="collapsed"
|
||||
@hidePopup={{true}}
|
||||
>
|
||||
<button type="button" class="button is-transparent icon dismiss-collapsed" {{action onDismiss}}>
|
||||
<ICon @glyph="close" />
|
||||
</button>
|
||||
<p>Feel free to explore Vault. Click below to get back to the guide or close this window.</p>
|
||||
<div class="box wizard-divider-box">
|
||||
<button type="button" class="button is-transparent has-icon-left has-text-white" {{action onAdvance}}>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<form class="{{if showAdvancedMode 'advanced-edit' 'simple-edit'}}" onsubmit={{action "createOrUpdateKey" "create"}} onchange={{action "handleChange"}}>
|
||||
<div class="field box is-fullwidth is-sideless is-marginless">
|
||||
<NamespaceReminder @mode="create" @noun="secret" />
|
||||
{{message-error model=key}}
|
||||
{{message-error model=key errorMessage=error}}
|
||||
<label class="label is-font-weight-normal" for="kv-key">Path for this secret</label>
|
||||
<div class="field has-addons">
|
||||
{{#if (not-eq key.initialParentKey '') }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<form onsubmit={{action "createOrUpdateKey" "update"}} onchange={{action "handleChange"}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{message-error model=key}}
|
||||
{{message-error model=key errorMessage=error}}
|
||||
<NamespaceReminder @mode="edit" @noun="secret" />
|
||||
{{#unless showAdvancedMode}}
|
||||
<div class="table info-table-row-header">
|
||||
|
|
|
@ -3,6 +3,9 @@ import hbs from 'htmlbars-inline-precompile';
|
|||
|
||||
moduleForComponent('secret-edit', 'Integration | Component | secret edit', {
|
||||
integration: true,
|
||||
beforeEach() {
|
||||
this.inject.service('code-mirror', { as: 'codeMirror' });
|
||||
},
|
||||
});
|
||||
|
||||
test('it disables JSON toggle in show mode when is an advanced format', function(assert) {
|
||||
|
@ -19,7 +22,7 @@ test('it disables JSON toggle in show mode when is an advanced format', function
|
|||
assert.dom('[data-test-secret-json-toggle]').isDisabled();
|
||||
});
|
||||
|
||||
test('it does JSON toggle in show mode when is', function(assert) {
|
||||
test('it does JSON toggle in show mode when showing string data', function(assert) {
|
||||
this.set('mode', 'show');
|
||||
this.set('key', {
|
||||
secretData: {
|
||||
|
@ -32,3 +35,38 @@ test('it does JSON toggle in show mode when is', function(assert) {
|
|||
this.render(hbs`{{secret-edit mode=mode key=key }}`);
|
||||
assert.dom('[data-test-secret-json-toggle]').isNotDisabled();
|
||||
});
|
||||
|
||||
test('it shows an error when creating and data is not an object', function(assert) {
|
||||
this.set('mode', 'create');
|
||||
this.set('key', {
|
||||
secretData: {
|
||||
int: '2',
|
||||
null: 'null',
|
||||
float: '1.234',
|
||||
},
|
||||
});
|
||||
|
||||
this.render(hbs`{{secret-edit mode=mode key=key preferAdvancedEdit=true }}`);
|
||||
let instance = this.codeMirror.instanceFor(this.$('[data-test-component=json-editor]').attr('id'));
|
||||
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
|
||||
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
|
||||
});
|
||||
|
||||
test('it shows an error when editing and the data is not an object', function(assert) {
|
||||
this.set('mode', 'edit');
|
||||
this.set('capabilities', {
|
||||
canUpdate: true,
|
||||
});
|
||||
this.set('key', {
|
||||
secretData: {
|
||||
int: '2',
|
||||
null: 'null',
|
||||
float: '1.234',
|
||||
},
|
||||
});
|
||||
|
||||
this.render(hbs`{{secret-edit capabilities=capabilities mode=mode key=key preferAdvancedEdit=true }}`);
|
||||
let instance = this.codeMirror.instanceFor(this.$('[data-test-component=json-editor]').attr('id'));
|
||||
instance.setValue(JSON.stringify([{ foo: 'bar' }]));
|
||||
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
|
||||
});
|
||||
|
|
|
@ -37,6 +37,17 @@ fromJSONTests.forEach(function([name, input, content]) {
|
|||
});
|
||||
});
|
||||
|
||||
test(`fromJSON: non-object input`, function(assert) {
|
||||
let input = [{ foo: 'bar' }];
|
||||
assert.throws(
|
||||
() => {
|
||||
KVObject.create({ content: [] }).fromJSON(input);
|
||||
},
|
||||
/Vault expects data to be formatted as an JSON object/,
|
||||
'throws when non-object input is used to construct the KVObject'
|
||||
);
|
||||
});
|
||||
|
||||
fromJSONTests.forEach(function([name, input, content]) {
|
||||
test(`fromJSONString: ${name}`, function(assert) {
|
||||
let inputString = JSON.stringify(input, null, 2);
|
||||
|
|
Loading…
Reference in a new issue