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:
commit
4db6f80de6
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
15
ui-v2/tests/acceptance/settings/update.feature
Normal file
15
ui-v2/tests/acceptance/settings/update.feature
Normal 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: ''
|
||||||
|
---
|
||||||
|
|
10
ui-v2/tests/acceptance/steps/settings/update-steps.js
Normal file
10
ui-v2/tests/acceptance/steps/settings/update-steps.js
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
10
ui-v2/tests/acceptance/steps/token-header-steps.js
Normal file
10
ui-v2/tests/acceptance/steps/token-header-steps.js
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
36
ui-v2/tests/acceptance/token-header.feature
Normal file
36
ui-v2/tests/acceptance/token-header.feature
Normal 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 |
|
||||||
|
| '' |
|
||||||
|
---------
|
|
@ -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();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
6
ui-v2/tests/pages/settings.js
Normal file
6
ui-v2/tests/pages/settings.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { create, visitable, clickable } from 'ember-cli-page-object';
|
||||||
|
|
||||||
|
export default create({
|
||||||
|
visit: visitable('/settings'),
|
||||||
|
submit: clickable('[type=submit]'),
|
||||||
|
});
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue