Merge branch 'master' into respond-upstream-throttle

This commit is contained in:
Clint 2018-09-05 11:45:18 -05:00 committed by GitHub
commit 8f869999f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 155 additions and 32 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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') {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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(

View file

@ -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>

View 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>

View 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>

View file

@ -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}}>

View file

@ -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}}>

View file

@ -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 '') }}

View file

@ -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">

View file

@ -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');
});

View file

@ -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);