mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 13:26:07 +00:00
Merge pull request #15023 from hashicorp/ui/fix/token-secret-id-handling
ui: Fix token in cookie passthrough
This commit is contained in:
commit
c35556caa7
@ -1,40 +1,19 @@
|
|||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { runInDebug } from '@ember/debug';
|
|
||||||
|
|
||||||
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
||||||
|
|
||||||
export default class ApplicationRoute extends Route.extend(WithBlockingActions) {
|
export default class ApplicationRoute extends Route.extend(WithBlockingActions) {
|
||||||
@service('client/http') client;
|
@service('client/http') client;
|
||||||
@service('env') env;
|
@service('env') env;
|
||||||
@service('repository/token') tokenRepo;
|
@service() hcp;
|
||||||
@service('settings') settings;
|
|
||||||
|
|
||||||
data;
|
data;
|
||||||
|
|
||||||
async model() {
|
async model() {
|
||||||
if (this.env.var('CONSUL_ACLS_ENABLED')) {
|
if (this.env.var('CONSUL_ACLS_ENABLED')) {
|
||||||
const secret = this.env.var('CONSUL_HTTP_TOKEN');
|
await this.hcp.updateTokenIfNecessary(this.env.var('CONSUL_HTTP_TOKEN'));
|
||||||
const existing = await this.settings.findBySlug('token');
|
|
||||||
if (!existing.AccessorID && secret) {
|
|
||||||
try {
|
|
||||||
const token = await this.tokenRepo.self({
|
|
||||||
secret: secret,
|
|
||||||
dc: this.env.var('CONSUL_DATACENTER_LOCAL'),
|
|
||||||
});
|
|
||||||
await this.settings.persist({
|
|
||||||
token: {
|
|
||||||
AccessorID: token.AccessorID,
|
|
||||||
SecretID: token.SecretID,
|
|
||||||
Namespace: token.Namespace,
|
|
||||||
Partition: token.Partition,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
runInDebug((_) => console.error(e));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -12,3 +12,63 @@ export default class EnvService extends Service {
|
|||||||
return env(key);
|
return env(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Stub class that can be used in testing when we want to test
|
||||||
|
* interactions with the EnvService. We can use `EnvStub.stubEnv` to setup
|
||||||
|
* an Env-Service that returns certain values we need to execute our tests.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* // some-test.js
|
||||||
|
* test('testing interaction with Env-service', async function(assert) {
|
||||||
|
* this.owner.register('service:env', class Stub extends EnvStub {
|
||||||
|
* . stubEnv = {
|
||||||
|
* CONSUL_ACLS_ENABLED: true
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class EnvStub extends EnvService {
|
||||||
|
var(key) {
|
||||||
|
const { stubEnv } = this;
|
||||||
|
|
||||||
|
const stubbed = stubEnv[key];
|
||||||
|
|
||||||
|
if (stubbed) {
|
||||||
|
return stubbed;
|
||||||
|
} else {
|
||||||
|
return super.var(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to allow stubbing out data that is accessed by the application
|
||||||
|
* based on the Env-service. You will need to call this before the env-service gets
|
||||||
|
* initialized because it overrides the env-service injection on the owner.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* test('test something env related', async function(assert) {
|
||||||
|
* setupTestEnv(this.owner, {
|
||||||
|
* CONSUL_ACLS_ENABLED: true
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {*} owner - the owner of the test instance (usually `this.owner`)
|
||||||
|
* @param {*} stubEnv - an object that holds the stubbed env-data
|
||||||
|
*/
|
||||||
|
export function setupTestEnv(owner, stubEnv) {
|
||||||
|
owner.register(
|
||||||
|
'service:env',
|
||||||
|
class Stub extends EnvStub {
|
||||||
|
stubEnv = stubEnv;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
37
ui/packages/consul-ui/app/services/hcp.js
Normal file
37
ui/packages/consul-ui/app/services/hcp.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import Service, { inject as service } from '@ember/service';
|
||||||
|
import { runInDebug } from '@ember/debug';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service to encapsulate all logic that handles dealing with setting up consul
|
||||||
|
* core correctly when started via HCP.
|
||||||
|
*/
|
||||||
|
export default class HCPService extends Service {
|
||||||
|
@service('env') env;
|
||||||
|
@service('repository/token') tokenRepo;
|
||||||
|
@service('settings') settings;
|
||||||
|
|
||||||
|
async updateTokenIfNecessary(secret) {
|
||||||
|
if (secret) {
|
||||||
|
const existing = await this.settings.findBySlug('token');
|
||||||
|
|
||||||
|
if (secret && secret !== existing.SecretID) {
|
||||||
|
try {
|
||||||
|
const token = await this.tokenRepo.self({
|
||||||
|
secret: secret,
|
||||||
|
dc: this.env.var('CONSUL_DATACENTER_LOCAL'),
|
||||||
|
});
|
||||||
|
await this.settings.persist({
|
||||||
|
token: {
|
||||||
|
AccessorID: token.AccessorID,
|
||||||
|
SecretID: token.SecretID,
|
||||||
|
Namespace: token.Namespace,
|
||||||
|
Partition: token.Partition,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
runInDebug((_) => console.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
ui/packages/consul-ui/tests/acceptance/hcp-login-test.js
Normal file
173
ui/packages/consul-ui/tests/acceptance/hcp-login-test.js
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { visit } from '@ember/test-helpers';
|
||||||
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
|
import { setupTestEnv } from 'consul-ui/services/env';
|
||||||
|
import TokenRepo from 'consul-ui/services/repository/token';
|
||||||
|
import SettingsService from 'consul-ui/services/settings';
|
||||||
|
|
||||||
|
const TOKEN_SET_BY_HCP = 'token-set-by-hcp';
|
||||||
|
|
||||||
|
module('Acceptance | hcp login', function (hooks) {
|
||||||
|
setupApplicationTest(hooks);
|
||||||
|
|
||||||
|
module('with `CONSUL_HTTP_TOKEN` not set', function () {
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
setupTestEnv(this.owner, {
|
||||||
|
CONSUL_ACLS_ENABLED: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('we do not call the token endpoint', async function (assert) {
|
||||||
|
this.owner.register(
|
||||||
|
'service:repository/token',
|
||||||
|
class extends TokenRepo {
|
||||||
|
self() {
|
||||||
|
assert.step('token');
|
||||||
|
|
||||||
|
return super.self(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await visit('/');
|
||||||
|
|
||||||
|
assert.verifySteps([], 'we do not try to fetch new token');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module('with `CONSUL_HTTP_TOKEN` set', function (hooks) {
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
setupTestEnv(this.owner, {
|
||||||
|
CONSUL_ACLS_ENABLED: true,
|
||||||
|
CONSUL_HTTP_TOKEN: TOKEN_SET_BY_HCP,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('when no token was persisted to settings', async function (assert) {
|
||||||
|
assert.expect(3);
|
||||||
|
|
||||||
|
// stub out the settings service to not access local-storage directly
|
||||||
|
this.owner.register(
|
||||||
|
'service:settings',
|
||||||
|
class extends SettingsService {
|
||||||
|
async findBySlug(slug) {
|
||||||
|
// make sure we don't find anything
|
||||||
|
if (slug === 'token') {
|
||||||
|
// we return an empty string if nothing is found
|
||||||
|
return Promise.resolve('');
|
||||||
|
} else {
|
||||||
|
return super.findBySlug(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// There's no way to hook into the api handlers like with mirage
|
||||||
|
// so we need to stub the repo methods
|
||||||
|
this.owner.register(
|
||||||
|
'service:repository/token',
|
||||||
|
class extends TokenRepo {
|
||||||
|
self(params) {
|
||||||
|
const { secret } = params;
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
secret,
|
||||||
|
TOKEN_SET_BY_HCP,
|
||||||
|
'we try to request token based on what HCP set for us'
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.step('token');
|
||||||
|
|
||||||
|
return super.self(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await visit('/');
|
||||||
|
|
||||||
|
assert.verifySteps(['token'], 'we try to call token endpoint to fetch new token');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('when we already persisted a token to settings and it is different to the secret HCP set for us', async function (assert) {
|
||||||
|
assert.expect(3);
|
||||||
|
|
||||||
|
this.owner.register(
|
||||||
|
'service:settings',
|
||||||
|
class extends SettingsService {
|
||||||
|
async findBySlug(slug) {
|
||||||
|
if (slug === 'token') {
|
||||||
|
return Promise.resolve({
|
||||||
|
AccessorID: 'accessor',
|
||||||
|
SecretID: 'secret',
|
||||||
|
Namespace: 'default',
|
||||||
|
Partition: 'default',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return super.findBySlug(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.owner.register(
|
||||||
|
'service:repository/token',
|
||||||
|
class extends TokenRepo {
|
||||||
|
self(params) {
|
||||||
|
const { secret } = params;
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
secret,
|
||||||
|
TOKEN_SET_BY_HCP,
|
||||||
|
'we try to request token based on what HCP set for us'
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.step('token');
|
||||||
|
|
||||||
|
return super.self(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await visit('/');
|
||||||
|
|
||||||
|
assert.verifySteps(['token'], 'we call token endpoint to fetch new token');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('when we already persisted a token to settings, but it is the same secret as HCP set for us', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
this.owner.register(
|
||||||
|
'service:settings',
|
||||||
|
class extends SettingsService {
|
||||||
|
async findBySlug(slug) {
|
||||||
|
if (slug === 'token') {
|
||||||
|
return Promise.resolve({
|
||||||
|
AccessorID: 'accessor',
|
||||||
|
SecretID: TOKEN_SET_BY_HCP,
|
||||||
|
Namespace: 'default',
|
||||||
|
Partition: 'default',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return super.findBySlug(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.owner.register(
|
||||||
|
'service:repository/token',
|
||||||
|
class extends TokenRepo {
|
||||||
|
self() {
|
||||||
|
assert.step('token');
|
||||||
|
|
||||||
|
return super.self(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await visit('/');
|
||||||
|
|
||||||
|
assert.verifySteps([], 'we do not try to fetch new token');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user