Merge pull request #4251 from hashicorp/bugfix/ui-null-tokens

Ensure a blank/empty token is sent if the localStorage kv doesn't exist
This commit is contained in:
John Cowen 2018-06-22 16:36:20 +01:00 committed by GitHub
commit 4db6f80de6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 131 additions and 3 deletions

View file

@ -7,19 +7,27 @@ export default Service.extend({
storage: window.localStorage, storage: window.localStorage,
findHeaders: function() { findHeaders: function() {
// TODO: if possible this should be a promise // TODO: if possible this should be a promise
const token = get(this, 'storage').getItem('token');
// TODO: The old UI always sent ?token=
// replicate the old functionality here
// but remove this to be cleaner if its not necessary
return { return {
'X-Consul-Token': get(this, 'storage').getItem('token'), 'X-Consul-Token': token === null ? '' : token,
}; };
}, },
findAll: function(key) { findAll: function(key) {
return Promise.resolve({ token: get(this, 'storage').getItem('token') }); const token = get(this, 'storage').getItem('token');
return Promise.resolve({ token: token === null ? '' : token });
}, },
findBySlug: function(slug) { findBySlug: function(slug) {
// TODO: Force localStorage to always be strings...
// const value = get(this, 'storage').getItem(slug);
return Promise.resolve(get(this, 'storage').getItem(slug)); return Promise.resolve(get(this, 'storage').getItem(slug));
}, },
persist: function(obj) { persist: function(obj) {
const storage = get(this, 'storage'); const storage = get(this, 'storage');
Object.keys(obj).forEach((item, i) => { Object.keys(obj).forEach((item, i) => {
// TODO: ...everywhere
storage.setItem(item, obj[item]); storage.setItem(item, obj[item]);
}); });
return Promise.resolve(obj); return Promise.resolve(obj);

View file

@ -13,7 +13,7 @@
<fieldset> <fieldset>
<label class="type-text"> <label class="type-text">
<span>ACL Token</span> <span>ACL Token</span>
{{ input type='password' value=item.token }} {{ input type='password' value=item.token name="token" }}
<em>The token is sent with requests as the <code>X-Consul-Token</code> HTTP header parameter. This is used to control the ACL for the web UI.</em> <em>The token is sent with requests as the <code>X-Consul-Token</code> HTTP header parameter. This is used to control the ACL for the web UI.</em>
</label> </label>
</fieldset> </fieldset>

View file

@ -0,0 +1,15 @@
@setupApplicationTest
Feature: settings / update: Update Settings
In order to authenticate with an ACL token
As a user
I need to be able to add my token via the UI
Scenario: I click Save without actually typing anything
Given 1 datacenter model with the value "datacenter"
When I visit the settings page
Then the url should be /settings
And I submit
Then I have settings like yaml
---
token: ''
---

View file

@ -0,0 +1,10 @@
import steps from '../steps';
// step definitions that are shared between features should be moved to the
// tests/acceptance/steps/steps.js file
export default function(assert) {
return steps(assert).then('I should find a file', function() {
assert.ok(true, this.step);
});
}

View file

@ -0,0 +1,10 @@
import steps from './steps';
// step definitions that are shared between features should be moved to the
// tests/acceptance/steps/steps.js file
export default function(assert) {
return steps(assert).then('I should find a file', function() {
assert.ok(true, this.step);
});
}

View file

@ -0,0 +1,36 @@
@setupApplicationTest
Feature: token headers
In order to authenticate with tokens
As a user
I need to be able to specify a ACL token AND/OR leave it blank to authenticate with the API
Scenario: Arriving at the index page having not set a token previously
Given 1 datacenter model with the value "datacenter"
When I visit the index page
Then the url should be /datacenter/services
And a GET request is made to "/v1/catalog/datacenters" from yaml
---
headers:
X-Consul-Token: ''
---
Scenario: Set the token to [Token] and then navigate to the index page
Given 1 datacenter model with the value "datacenter"
When I visit the settings page
Then the url should be /settings
Then I type with yaml
---
token: [Token]
---
And I submit
When I visit the index page
Then the url should be /datacenter/services
And a GET request is made to "/v1/catalog/datacenters" from yaml
---
headers:
X-Consul-Token: [Token]
---
Where:
---------
| Token |
| token |
| '' |
---------

View file

@ -64,6 +64,7 @@ function setupScenario(featureAnnotations, scenarioAnnotations) {
} }
return function(model) { return function(model) {
model.afterEach(function() { model.afterEach(function() {
window.localStorage.clear();
api.server.reset(); api.server.reset();
}); });
}; };

View file

@ -1,5 +1,6 @@
import index from 'consul-ui/tests/pages/index'; import index from 'consul-ui/tests/pages/index';
import dcs from 'consul-ui/tests/pages/dc'; import dcs from 'consul-ui/tests/pages/dc';
import settings from 'consul-ui/tests/pages/settings';
import services from 'consul-ui/tests/pages/dc/services/index'; import services from 'consul-ui/tests/pages/dc/services/index';
import service from 'consul-ui/tests/pages/dc/services/show'; import service from 'consul-ui/tests/pages/dc/services/show';
import nodes from 'consul-ui/tests/pages/dc/nodes/index'; import nodes from 'consul-ui/tests/pages/dc/nodes/index';
@ -12,6 +13,7 @@ import acl from 'consul-ui/tests/pages/dc/acls/edit';
export default { export default {
index, index,
dcs, dcs,
settings,
services, services,
service, service,
nodes, nodes,

View file

@ -0,0 +1,6 @@
import { create, visitable, clickable } from 'ember-cli-page-object';
export default create({
visit: visitable('/settings'),
submit: clickable('[type=submit]'),
});

View file

@ -153,6 +153,37 @@ export default function(assert) {
); );
}); });
}) })
// TODO: This one can replace the above one, it covers more use cases
// also DRY it out a bit
.then('a $method request is made to "$url" from yaml\n$yaml', function(method, url, yaml) {
const request = api.server.history[api.server.history.length - 2];
assert.equal(
request.method,
method,
`Expected the request method to be ${method}, was ${request.method}`
);
assert.equal(request.url, url, `Expected the request url to be ${url}, was ${request.url}`);
let data = yaml.body || {};
const body = JSON.parse(request.requestBody);
Object.keys(data).forEach(function(key, i, arr) {
assert.equal(
body[key],
data[key],
`Expected the payload to contain ${key} to equal ${body[key]}, ${key} was ${data[key]}`
);
});
data = yaml.headers || {};
const headers = request.requestHeaders;
Object.keys(data).forEach(function(key, i, arr) {
assert.equal(
headers[key],
data[key],
`Expected the payload to contain ${key} to equal ${headers[key]}, ${key} was ${
data[key]
}`
);
});
})
.then('a $method request is made to "$url" with the body "$body"', function( .then('a $method request is made to "$url" with the body "$body"', function(
method, method,
url, url,
@ -214,6 +245,15 @@ export default function(assert) {
`Expected ${num} ${model}s with ${property} set to "${value}", saw ${len}` `Expected ${num} ${model}s with ${property} set to "${value}", saw ${len}`
); );
}) })
.then('I have settings like yaml\n$yaml', function(data) {
// TODO: Inject this
const settings = window.localStorage;
Object.keys(data).forEach(function(prop) {
const actual = settings.getItem(prop);
const expected = data[prop];
assert.strictEqual(actual, expected, `Expected settings to be ${expected} was ${actual}`);
});
})
.then('I see $property on the $component like yaml\n$yaml', function( .then('I see $property on the $component like yaml\n$yaml', function(
property, property,
component, component,