UI: handle reduced disclosure endpoints (#24262)

* Create app-footer component with tests

* glimmerize vault route + controller

* Add dev mode badge to new footer

* Fix version on dashboard

* update app-footer tests

* update version title component

* Handle case for chroot namespace fail on health check

* cleanup

* fix ent tests

* add missing headers

* extra version fetch on login success, clear version on logout and seal

* Add coverage for clearing version on seal

* rename isOSS to isCommunity

* remove is-version helper

* test version in footer on unseal flow

* fix enterprise test

* VAULT-21399 test coverage

* VAULT-21400 test coverage
This commit is contained in:
Chelsea Shaw
2023-12-04 14:28:16 -06:00
committed by GitHub
parent e3aa18c7f7
commit cb217388d4
38 changed files with 359 additions and 156 deletions

View File

@@ -5,7 +5,6 @@
import AdapterError from '@ember-data/adapter/error';
import { inject as service } from '@ember/service';
import { assign } from '@ember/polyfills';
import { hash, resolve } from 'rsvp';
import { assert } from '@ember/debug';
import { pluralize } from 'ember-inflector';
@@ -22,6 +21,7 @@ const ENDPOINTS = [
'init',
'capabilities-self',
'license',
'internal/ui/version',
];
const REPLICATION_ENDPOINTS = {
@@ -55,12 +55,12 @@ export default ApplicationAdapter.extend({
id,
name: snapshot.attr('name'),
};
ret = assign(ret, health);
ret = Object.assign(ret, health);
if (sealStatus instanceof AdapterError === false) {
ret = assign(ret, { nodes: [sealStatus] });
ret = Object.assign(ret, { nodes: [sealStatus] });
}
if (replicationStatus && replicationStatus instanceof AdapterError === false) {
ret = assign(ret, replicationStatus.data);
ret = Object.assign(ret, replicationStatus.data);
}
return resolve(ret);
});
@@ -94,6 +94,10 @@ export default ApplicationAdapter.extend({
});
},
fetchVersion() {
return this.ajax(`${this.urlFor('internal/ui/version')}`, 'GET').catch(() => ({}));
},
sealStatus() {
return this.ajax(this.urlFor('seal-status'), 'GET', { unauthenticated: true });
},

View File

@@ -0,0 +1,31 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}
<Hds::AppFooter as |AF|>
<AF.ExtraBefore>
{{#if this.isDevelopment}}
<div class="env-banner">
<div class="level-item notification">
<Icon @name="git-branch" /><Icon @name="pencil-tool" />
Local development
<Icon @name="pencil-tool" /><Icon @name="git-branch" />
</div>
</div>
{{/if}}
</AF.ExtraBefore>
<AF.Link @href={{changelog-url-for this.version.version}} data-test-footer-version>
Vault
{{this.version.version}}
</AF.Link>
{{#if this.version.isCommunity}}
<AF.Link @href="https://hashicorp.com/products/vault/trial?source=vaultui" data-test-footer-upgrade-link>
Upgrade to Vault Enterprise
</AF.Link>
{{/if}}
<AF.Link @href={{doc-link "/vault"}} data-test-footer-documentation-link>
Documentation
</AF.Link>
<AF.LegalLinks />
</Hds::AppFooter>

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { service } from '@ember/service';
import Component from '@glimmer/component';
import ENV from 'vault/config/environment';
export default class AppFooterComponent extends Component {
@service version;
get isDevelopment() {
return ENV.environment === 'development';
}
}

View File

@@ -12,17 +12,10 @@ import { inject as service } from '@ember/service';
*
* @example
* ```js
* <Dashboard::VaultVersionTitle />
* <Dashboard::VaultVersionTitle @version={{this.versionSvc}} />
* ```
*/
export default class DashboardVaultVersionTitle extends Component {
@service version;
@service namespace;
get versionHeader() {
return this.version.isEnterprise
? `Vault v${this.version.version.slice(0, this.version.version.indexOf('+'))}`
: `Vault v${this.version.version}`;
}
}

View File

@@ -15,7 +15,7 @@
d="M69.7218638,30.2482468 L63.2587814,8.45301543 L58,8.45301543 L65.9885305,34.6072931 L73.4551971,34.6072931 L81.4437276,8.45301543 L76.1849462,8.45301543 L69.7218638,30.2482468 Z M97.6329749,22.0014025 C97.6329749,17.2103787 95.8265233,15.0897616 89.6845878,15.0897616 C87.5168459,15.0897616 84.8272401,15.4431978 82.9806452,15.9929874 L83.5827957,19.6451613 C85.3089606,19.2917251 87.2358423,19.056101 89.0021505,19.056101 C92.1333333,19.056101 92.7354839,19.802244 92.7354839,21.9228612 L92.7354839,23.9256662 L88.0387097,23.9256662 C84.0645161,23.9256662 82.3383513,25.4179523 82.3383513,29.3057504 C82.3383513,32.6044881 83.8637993,35 87.4365591,35 C89.4035842,35 91.4910394,34.4502104 93.2573477,33.3113604 L93.618638,34.6072931 L97.6329749,34.6072931 L97.6329749,22.0014025 Z M92.7354839,30.2089762 C91.8121864,30.7194951 90.4874552,31.1907433 89.0422939,31.1907433 C87.5168459,31.1907433 87.0752688,30.601683 87.0752688,29.2664797 C87.0752688,27.8134642 87.5168459,27.3814867 89.1225806,27.3814867 L92.7354839,27.3814867 L92.7354839,30.2089762 Z M102.421505,15.4824684 L102.421505,29.345021 C102.421505,32.7615708 103.585663,35 106.837276,35 C109.125448,35 112.216487,34.1753156 114.665233,32.997195 L115.146953,34.6072931 L118.880287,34.6072931 L118.880287,15.4824684 L113.982796,15.4824684 L113.982796,28.7559607 C112.216487,29.6591865 110.088889,30.3660589 108.884588,30.3660589 C107.760573,30.3660589 107.318996,29.85554 107.318996,28.8345021 L107.318996,15.4824684 L102.421505,15.4824684 Z M129.168459,34.6072931 L129.168459,7 L124.270968,7.66760168 L124.270968,34.6072931 L129.168459,34.6072931 Z M144.394265,30.601683 C143.551254,30.8373072 142.6681,30.9943899 141.94552,30.9943899 C140.660932,30.9943899 140.179211,30.3267882 140.179211,29.3057504 L140.179211,19.2917251 L144.875986,19.2917251 L145.197133,15.4824684 L140.179211,15.4824684 L140.179211,10.0631136 L135.28172,10.7307153 L135.28172,15.4824684 L132.351254,15.4824684 L132.351254,19.2917251 L135.28172,19.2917251 L135.28172,29.9340813 C135.28172,33.3506311 137.088172,35 140.660932,35 C141.905376,35 143.912545,34.6858345 144.956272,34.2538569 L144.394265,30.601683 Z"
></path>
{{#if (is-version "Enterprise")}}
{{#if this.version.isEnterprise}}
<g id="vault-logo-edition-enterprise" transform="translate(65.000000, 40.000000)" fill-rule="nonzero">
<polygon
points="0.435816733 0.579322709 4.39438247 0.579322709 4.39438247 1.35454183 1.30175299 1.35454183 1.30175299 3.46577689 4.17171315 3.46577689 4.17171315 4.24099602 1.30175299 4.24099602 1.30175299 6.52541833 4.40262948 6.52541833 4.40262948 7.30063745 0.435816733 7.30063745"

View File

@@ -3,20 +3,15 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { inject as service } from '@ember/service';
import Controller from '@ember/controller';
import config from '../config/environment';
export default Controller.extend({
queryParams: [
export default class VaultController extends Controller {
queryParams = [
{
wrappedToken: 'wrapped_token',
redirectTo: 'redirect_to',
},
],
wrappedToken: '',
redirectTo: '',
env: config.environment,
auth: service(),
store: service(),
});
];
wrappedToken = '';
redirectTo = '';
}

View File

@@ -9,11 +9,12 @@ import { task, timeout } from 'ember-concurrency';
import { sanitizePath } from 'core/utils/sanitize-path';
export default Controller.extend({
flashMessages: service(),
vaultController: controller('vault'),
clusterController: controller('vault.cluster'),
flashMessages: service(),
namespaceService: service('namespace'),
featureFlagService: service('featureFlag'),
version: service(),
auth: service(),
router: service(),
queryParams: [{ authMethod: 'with', oidcProvider: 'o' }],
@@ -56,6 +57,7 @@ export default Controller.extend({
authSuccess({ isRoot, namespace }) {
let transition;
this.version.fetchVersion();
if (this.redirectTo) {
// here we don't need the namespace because it will be encoded in redirectTo
transition = this.router.transitionTo(this.redirectTo);

View File

@@ -8,6 +8,8 @@ import Controller from '@ember/controller';
export default Controller.extend({
auth: service(),
router: service(),
version: service(),
actions: {
seal() {
@@ -17,7 +19,9 @@ export default Controller.extend({
.then(() => {
this.model.cluster.get('leaderNode').set('sealed', true);
this.auth.deleteCurrentToken();
return this.transitionToRoute('vault.cluster.unseal');
// Reset version so it doesn't show on footer
this.version.version = null;
return this.router.transitionTo('vault.cluster.unseal');
});
},
},

View File

@@ -4,14 +4,16 @@
*/
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
router: service(),
showLicenseError: false,
actions: {
transitionToCluster() {
return this.model.reload().then(() => {
return this.transitionToRoute('vault.cluster', this.model.name);
return this.router.transitionTo('vault.cluster', this.model.name);
});
},

View File

@@ -8,18 +8,21 @@ import { Promise } from 'rsvp';
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import Ember from 'ember';
/* eslint-disable ember/no-ember-testing-in-module-scope */
const SPLASH_DELAY = Ember.testing ? 0 : 300;
export default Route.extend({
store: service(),
version: service(),
const SPLASH_DELAY = 300;
export default class VaultRoute extends Route {
@service router;
@service store;
@service version;
beforeModel() {
return this.version.fetchVersion();
},
// So we can know what type (Enterprise/Community) we're running
return this.version.fetchType();
}
model() {
const delay = Ember.testing ? 0 : SPLASH_DELAY;
// hardcode single cluster
const fixture = {
data: {
@@ -34,13 +37,13 @@ export default Route.extend({
return new Promise((resolve) => {
later(() => {
resolve(this.store.peekAll('cluster'));
}, SPLASH_DELAY);
}, delay);
});
},
}
redirect(model, transition) {
if (model.get('length') === 1 && transition.targetName === 'vault.index') {
return this.transitionTo('vault.cluster', model.get('firstObject.name'));
return this.router.transitionTo('vault.cluster', model.get('firstObject.name'));
}
},
});
}
}

View File

@@ -58,7 +58,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
const managedRoot = this.featureFlagService.managedNamespaceRoot;
assert(
'Cannot use VAULT_CLOUD_ADMIN_NAMESPACE flag with non-enterprise Vault version',
!(managedRoot && this.version.isOSS)
!(managedRoot && this.version.isCommunity)
);
if (!namespace && currentTokenName && !Ember.testing) {
// if no namespace queryParam and user authenticated,
@@ -80,6 +80,7 @@ export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
if (id) {
this.auth.setCluster(id);
if (this.auth.currentToken) {
this.version.fetchVersion();
await this.permissions.getPaths.perform();
}
return this.version.fetchFeatures();

View File

@@ -10,10 +10,11 @@ import { inject as service } from '@ember/service';
export default Route.extend(ClusterRoute, {
store: service(),
version: service(),
router: service(),
beforeModel() {
if (this.version.isOSS) {
this.transitionTo('vault.cluster');
if (this.version.isCommunity) {
this.router.transitionTo('vault.cluster');
}
},

View File

@@ -17,6 +17,7 @@ export default Route.extend(ModelBoundaryRoute, {
permissions: service(),
namespaceService: service('namespace'),
router: service(),
version: service(),
modelTypes: computed(function () {
return ['secret', 'secret-engine'];
@@ -32,6 +33,7 @@ export default Route.extend(ModelBoundaryRoute, {
this.console.clearLog(true);
this.flashMessages.clearMessages();
this.permissions.reset();
this.version.version = null;
queryParams.with = authType;
if (ns) {

View File

@@ -13,7 +13,7 @@ export default Route.extend(ClusterRoute, ListRoute, {
version: service(),
shouldReturnEmptyModel(policyType, version) {
return policyType !== 'acl' && (version.get('isOSS') || !version.get('hasSentinel'));
return policyType !== 'acl' && (version.get('isCommunity') || !version.get('hasSentinel'));
},
model(params) {

View File

@@ -73,7 +73,7 @@ export default Service.extend({
},
tokenForUrl(url) {
if (this.version.isOSS) {
if (this.version.isCommunity) {
return null;
}
let pathForUrl = parseURL(url).pathname;
@@ -89,7 +89,7 @@ export default Service.extend({
checkForControlGroup(callbackArgs, response, wasWrapTTLRequested) {
const creationPath = response && response?.wrap_info?.creation_path;
if (
this.version.isOSS ||
this.version.isCommunity ||
wasWrapTTLRequested ||
!response ||
(creationPath && WRAPPED_RESPONSE_PATHS.includes(creationPath)) ||

View File

@@ -11,7 +11,17 @@ export default class VersionService extends Service {
@service store;
@tracked features = [];
@tracked version = null;
@tracked type = null;
get isEnterprise() {
return this.type === 'enterprise';
}
get isCommunity() {
return !this.isEnterprise;
}
/* Features */
get hasPerfReplication() {
return this.features.includes('Performance Replication');
}
@@ -32,26 +42,35 @@ export default class VersionService extends Service {
return this.features.includes('Control Groups');
}
get isEnterprise() {
if (!this.version) return false;
return this.version.includes('+');
get versionDisplay() {
if (!this.version) {
return '';
}
return this.isEnterprise ? `v${this.version.slice(0, this.version.indexOf('+'))}` : `v${this.version}`;
}
get isOSS() {
return !this.isEnterprise;
@task({ drop: true })
*getVersion() {
if (this.version) return;
const response = yield this.store.adapterFor('cluster').fetchVersion();
this.version = response.data?.version;
}
@task
*getVersion() {
if (this.version) return;
const response = yield this.store.adapterFor('cluster').sealStatus();
this.version = response.version;
return;
*getType() {
if (this.type !== null) return;
const response = yield this.store.adapterFor('cluster').health();
if (response.has_chroot_namespace) {
// chroot_namespace feature is only available in enterprise
this.type = 'enterprise';
return;
}
this.type = response.enterprise ? 'enterprise' : 'community';
}
@keepLatestTask
*getFeatures() {
if (this.features?.length || this.isOSS) {
if (this.features?.length || this.isCommunity) {
return;
}
try {
@@ -67,6 +86,10 @@ export default class VersionService extends Service {
return this.getVersion.perform();
}
fetchType() {
return this.getType.perform();
}
fetchFeatures() {
return this.getFeatures.perform();
}

View File

@@ -4,7 +4,7 @@
*/
.env-banner {
align-self: center;
font-size: 0.8rem;
border-radius: 3rem;
background: linear-gradient(
135deg,
@@ -13,8 +13,6 @@
); // only use case for purple in the app. define here instead of utils/color_var
animation: env-banner-color-rotate 8s infinite linear alternate;
color: $white;
margin-top: -20px;
margin-bottom: 6px;
.hs-icon {
margin: 0;
@@ -22,7 +20,7 @@
.notification {
background-color: transparent;
line-height: 1.66;
line-height: 2;
padding: 0 $spacing-12;
}
}

View File

@@ -6,7 +6,8 @@
<PageHeader as |p|>
<p.levelLeft>
<h1 class="title is-3" data-test-dashboard-card-header="Vault version">
{{this.versionHeader}}
Vault
{{@version.versionDisplay}}
{{#if @version.isEnterprise}}
<Hds::Badge @text={{this.namespace.currentNamespace}} @icon="org" data-test-badge-namespace />
{{/if}}

View File

@@ -5,28 +5,4 @@
{{outlet}}
<Hds::AppFooter as |AF|>
<AF.Link @href={{changelog-url-for this.auth.activeCluster.leaderNode.version}}>
Vault
{{this.auth.activeCluster.leaderNode.version}}
</AF.Link>
{{#if (is-version "OSS")}}
<AF.Link @href="https://hashicorp.com/products/vault/trial?source=vaultui">
Upgrade to Vault Enterprise
</AF.Link>
{{/if}}
<AF.Link @href={{doc-link "/vault"}}>
Documentation
</AF.Link>
<AF.LegalLinks />
</Hds::AppFooter>
{{#if (eq this.env "development")}}
<div class="env-banner level development">
<div class="level-item notification">
<Icon @name="git-branch" /><Icon @name="pencil-tool" />
Local development
<Icon @name="pencil-tool" /><Icon @name="git-branch" />
</div>
</div>
{{/if}}
<AppFooter />

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
/* eslint-disable ember/no-observers */
import { inject as service } from '@ember/service';
import { assert } from '@ember/debug';
import Helper from '@ember/component/helper';
import { observer } from '@ember/object';
export default Helper.extend({
version: service(),
onFeaturesChange: observer('version.version', function () {
this.recompute();
}),
compute([sku]) {
if (sku !== 'OSS' && sku !== 'Enterprise') {
assert(`${sku} is not one of the available values for Vault versions.`, false);
return false;
}
return this.get(`version.is${sku}`);
},
});

View File

@@ -1,6 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export { default } from 'core/helpers/is-version';

View File

@@ -18,6 +18,7 @@ export default function (server) {
server.get('/sys/health', function () {
return {
enterprise: true,
initialized: true,
sealed: false,
standby: false,

View File

@@ -26,6 +26,7 @@ const generateHealthResponse = (now, state) => {
break;
}
return {
enterprise: true,
initialized: true,
sealed: false,
standby: false,

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { settled, visit } from '@ember/test-helpers';
import authPage from 'vault/tests/pages/auth';
import { createTokenCmd, runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands';
import { pollCluster } from 'vault/tests/helpers/poll-cluster';
import VAULT_KEYS from 'vault/tests/helpers/vault-keys';
import ENV from 'vault/config/environment';
const { unsealKeys } = VAULT_KEYS;
const SELECTORS = {
footerVersion: `[data-test-footer-version]`,
dashboardTitle: `[data-test-dashboard-card-header="Vault version"]`,
};
module('Acceptance | Enterprise | reduced disclosure test', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.before(function () {
ENV['ember-cli-mirage'].handler = 'mfaConfig';
});
hooks.beforeEach(function () {
this.versionSvc = this.owner.lookup('service:version');
return authPage.logout();
});
hooks.after(function () {
ENV['ember-cli-mirage'].handler = null;
});
test('it works when reduced disclosure enabled', async function (assert) {
const namespace = 'reduced-disclosure';
assert.dom(SELECTORS.footerVersion).hasText(`Vault`, 'shows Vault without version when logged out');
await authPage.login();
// Ensure it shows version on dashboard
assert.dom(SELECTORS.dashboardTitle).includesText(`Vault v1.`);
assert
.dom(SELECTORS.footerVersion)
.hasText(`Vault ${this.versionSvc.version}`, 'shows Vault version after login');
await runCmd(`write sys/namespaces/${namespace} -f`, false);
await authPage.loginNs(namespace);
assert
.dom(SELECTORS.footerVersion)
.hasText(`Vault ${this.versionSvc.version}`, 'shows Vault version within namespace');
const token = await runCmd(createTokenCmd('default'));
await authPage.logout();
assert.dom(SELECTORS.footerVersion).hasText(`Vault`, 'no vault version after logout');
await authPage.loginNs(namespace, token);
assert
.dom(SELECTORS.footerVersion)
.hasText(`Vault ${this.versionSvc.version}`, 'shows Vault version for default policy in namespace');
});
test('it works for user accessing child namespace', async function (assert) {
const namespace = 'reduced-disclosure';
await authPage.login();
await runCmd(`write sys/namespaces/${namespace} -f`, false);
const token = await runCmd(
tokenWithPolicyCmd(
'child-ns-access',
`
path "${namespace}/sys/*" {
capabilities = ["read"]
}
`
)
);
await authPage.logout();
await authPage.login(token);
assert
.dom(SELECTORS.footerVersion)
.hasText(`Vault ${this.versionSvc.version}`, 'shows Vault version for default policy in namespace');
// navigate to child namespace
await visit(`/vault/dashboard?namespace=${namespace}`);
assert
.dom(SELECTORS.footerVersion)
.hasText(
`Vault ${this.versionSvc.version}`,
'shows Vault version for default policy in child namespace'
);
assert.dom(SELECTORS.dashboardTitle).includesText('Vault v1.');
});
test('shows correct version on unseal flow', async function (assert) {
await authPage.login();
const versionSvc = this.owner.lookup('service:version');
await visit('/vault/settings/seal');
assert
.dom('[data-test-footer-version]')
.hasText(`Vault ${versionSvc.version}`, 'shows version on seal page');
assert.strictEqual(currentURL(), '/vault/settings/seal');
// seal
await click('[data-test-seal]');
await click('[data-test-confirm-button]');
await pollCluster(this.owner);
await settled();
assert.strictEqual(currentURL(), '/vault/unseal', 'vault is on the unseal page');
assert.dom('[data-test-footer-version]').hasText(`Vault`, 'Clears version on unseal');
// unseal
for (const key of unsealKeys) {
await fillIn('[data-test-shamir-key-input]', key);
await click('button[type="submit"]');
await pollCluster(this.owner);
await settled();
}
assert.dom('[data-test-cluster-status]').doesNotExist('ui does not show sealed warning');
assert.strictEqual(currentRouteName(), 'vault.cluster.auth', 'vault is ready to authenticate');
assert.dom('[data-test-footer-version]').hasText(`Vault`, 'Version is still not shown before auth');
await authPage.login();
assert
.dom('[data-test-footer-version]')
.hasText(`Vault ${versionSvc.version}`, 'Version is shown after login');
});
});

View File

@@ -22,7 +22,6 @@ module('Acceptance | Enterprise | sidebar navigation', function (hooks) {
// common links are tested in the sidebar-nav test and will not be covered here
test('it should render enterprise only navigation links', async function (assert) {
assert.expect(12);
assert.dom(panel('Cluster')).exists('Cluster nav panel renders');
await click(link('Replication'));

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
const selectors = {
versionDisplay: '[data-test-footer-version]',
upgradeLink: '[data-test-footer-upgrade-link]',
docsLink: '[data-test-footer-documentation-link]',
};
module('Integration | Component | app-footer', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.versionSvc = this.owner.lookup('service:version');
});
test('it renders a sane default', async function (assert) {
await render(hbs`<AppFooter />`);
assert.dom(selectors.versionDisplay).hasText('Vault', 'Vault without version by default');
assert.dom(selectors.upgradeLink).hasText('Upgrade to Vault Enterprise', 'upgrade link shows');
assert.dom(selectors.docsLink).hasText('Documentation', 'displays docs link');
});
test('it renders for community version', async function (assert) {
this.versionSvc.version = '1.15.1';
this.versionSvc.type = 'community';
await render(hbs`<AppFooter />`);
assert.dom(selectors.versionDisplay).hasText('Vault 1.15.1', 'Vault shows version when available');
assert.dom(selectors.upgradeLink).hasText('Upgrade to Vault Enterprise', 'upgrade link shows');
assert.dom(selectors.docsLink).hasText('Documentation', 'displays docs link');
});
test('it renders for ent version', async function (assert) {
this.versionSvc.version = '1.15.1+hsm';
this.versionSvc.type = 'enterprise';
await render(hbs`<AppFooter />`);
assert.dom(selectors.versionDisplay).hasText('Vault 1.15.1+hsm', 'shows version when available');
assert.dom(selectors.upgradeLink).doesNotExist('upgrade link not shown');
assert.dom(selectors.docsLink).hasText('Documentation', 'displays docs link');
});
});

View File

@@ -114,6 +114,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
test('it should show client count on enterprise w/ license', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
this.license = {
autoloaded: {
license_id: '7adbf1f4-56ef-35cd-3a6c-50ef2627865d',
@@ -142,6 +143,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
test('it should hide client count on enterprise w/o license ', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
this.isRootNamespace = true;
await render(
@@ -168,6 +170,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
test('it should hide replication on enterprise not on root namespace', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
this.isRootNamespace = false;
this.license = {
autoloaded: {
@@ -217,6 +220,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
test('shows the learn more card on enterprise', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
this.version.features = [
'Performance Replication',
'DR Replication',

View File

@@ -28,6 +28,7 @@ module('Integration | Component | license-banners', function (hooks) {
this.tomorrow = addDays(mockNow, 1);
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
});
hooks.after(function () {
timestamp.now.restore();

View File

@@ -24,7 +24,7 @@ module('Integration | Component | link-status', function (hooks) {
// this can be removed once feature is released for OSS
hooks.beforeEach(function () {
this.owner.lookup('service:version').set('version', '1.13.0+ent');
this.owner.lookup('service:version').set('type', 'enterprise');
this.statuses = statuses;
});
@@ -37,7 +37,7 @@ module('Integration | Component | link-status', function (hooks) {
});
test('it does not render banner in oss version', async function (assert) {
this.owner.lookup('service:version').set('version', '1.13.0');
this.owner.lookup('service:version').set('type', 'community');
await render(hbs`
<LinkStatus @status={{get this.statuses 0}} />
@@ -50,7 +50,6 @@ module('Integration | Component | link-status', function (hooks) {
await render(hbs`
<LinkStatus @status={{get this.statuses 0}} />
`);
assert.dom(SELECTORS.bannerConnected).exists('Success banner renders for connected state');
assert
.dom('[data-test-link-status]')

View File

@@ -50,7 +50,7 @@ module('Integration | Component | mount-backend/type-form', function (hooks) {
module('Enterprise', function (hooks) {
hooks.beforeEach(function () {
this.version = this.owner.lookup('service:version');
this.version.version = '1.12.1+ent';
this.version.type = 'enterprise';
});
test('it renders correct items for enterprise secrets', async function (assert) {

View File

@@ -151,7 +151,7 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
test('it renders enterprise params in crl section', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
await render(
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @hasConfig={{true}} />,`,
{ owner: this.engine }
@@ -166,7 +166,7 @@ module('Integration | Component | Page::PkiConfigurationDetails', function (hook
test('it does not render enterprise params in crl section', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1';
this.version.type = 'community';
await render(
hbs`<Page::PkiConfigurationDetails @urls={{this.urls}} @crl={{this.crl}} @hasConfig={{true}} />,`,
{ owner: this.engine }

View File

@@ -276,7 +276,7 @@ module('Integration | Component | page/pki-configuration-edit', function (hooks)
test('it renders enterprise only params', async function (assert) {
assert.expect(6);
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1+ent';
this.version.type = 'enterprise';
this.server.post(`/${this.backend}/config/acme`, () => {});
this.server.post(`/${this.backend}/config/cluster`, () => {});
this.server.post(`/${this.backend}/config/crl`, (schema, req) => {
@@ -327,7 +327,7 @@ module('Integration | Component | page/pki-configuration-edit', function (hooks)
test('it does not render enterprise only params for OSS', async function (assert) {
assert.expect(9);
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1';
this.version.type = 'community';
this.server.post(`/${this.backend}/config/acme`, () => {});
this.server.post(`/${this.backend}/config/cluster`, () => {});
this.server.post(`/${this.backend}/config/crl`, (schema, req) => {

View File

@@ -19,7 +19,7 @@ module('Integration | Component | pki tidy form', function (hooks) {
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.version = this.owner.lookup('service:version');
this.version.version = '1.14.1+ent';
this.version.type = 'enterprise';
this.server.post('/sys/capabilities-self', () => {});
this.onSave = () => {};
this.onCancel = () => {};
@@ -33,7 +33,6 @@ module('Integration | Component | pki tidy form', function (hooks) {
test('it hides or shows fields depending on auto-tidy toggle', async function (assert) {
assert.expect(37);
this.version.version = '1.14.1+ent';
const sectionHeaders = [
'Universal operations',
'ACME operations',
@@ -82,7 +81,6 @@ module('Integration | Component | pki tidy form', function (hooks) {
test('it renders all attribute fields, including enterprise', async function (assert) {
assert.expect(25);
this.version.version = '1.14.1+ent';
this.autoTidy.enabled = true;
const skipFields = ['enabled', 'tidyAcme', 'intervalDuration']; // combined with duration ttl or asserted separately
await render(
@@ -123,7 +121,7 @@ module('Integration | Component | pki tidy form', function (hooks) {
test('it hides enterprise fields for OSS', async function (assert) {
assert.expect(7);
this.version.version = '1.14.1';
this.version.type = 'community';
this.autoTidy.enabled = true;
const enterpriseFields = [

View File

@@ -27,7 +27,7 @@ module('Integration | Component | sidebar-frame', function (hooks) {
const currentCluster = this.owner.lookup('service:currentCluster');
currentCluster.setCluster({ hcpLinkStatus: 'connected' });
const version = this.owner.lookup('service:version');
version.version = '1.13.0-dev1+ent';
version.type = 'enterprise';
await render(hbs`
<Sidebar::Frame @showSidebar={{true}}>

View File

@@ -45,7 +45,7 @@ export default create({
await this.usernameInput(username);
return this.passwordInput(password).submit();
},
loginNs: async function (ns) {
loginNs: async function (ns, token = rootToken) {
// make sure we're always logged out and logged back in
await this.logout();
await settled();
@@ -55,7 +55,7 @@ export default create({
await settled();
await this.namespaceInput(ns);
await settled();
await this.tokenInput(rootToken).submit();
await this.tokenInput(token).submit();
return;
},
clickLogout: async function (clearNamespace = false) {

View File

@@ -79,7 +79,7 @@ module('Unit | Adapter | kv/data', function (hooks) {
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
this.version = this.owner.lookup('service:version');
this.version.version = 'example+ent'; // Required for testing control-group flow
this.version.type = 'enterprise'; // Required for testing control-group flow
this.secretMountPath = this.owner.lookup('service:secret-mount-path');
this.backend = 'my/kv-back&end';
this.secretMountPath.currentPath = this.backend;

View File

@@ -51,8 +51,8 @@ module('Unit | Service | control group', function (hooks) {
hooks.afterEach(function () {});
const isOSS = (context) => set(context, 'version.isOSS', true);
const isEnt = (context) => set(context, 'version.isOSS', false);
const isCommunity = (context) => set(context, 'version.type', 'community');
const isEnt = (context) => set(context, 'version.type', 'enterprise');
const resolvesArgs = (assert, result, expectedArgs) => {
return result.then((...args) => {
return assert.deepEqual(args, expectedArgs, 'resolves with the passed args');
@@ -61,31 +61,31 @@ module('Unit | Service | control group', function (hooks) {
[
[
'it resolves isOSS:true, wrapTTL: true, response: has wrap_info',
isOSS,
'it resolves isCommunity:true, wrapTTL: true, response: has wrap_info',
isCommunity,
[[{ one: 'two', three: 'four' }], { wrap_info: { token: 'foo', accessor: 'bar' } }, true],
(assert, result) => resolvesArgs(assert, result, [{ one: 'two', three: 'four' }]),
],
[
'it resolves isOSS:true, wrapTTL: false, response: has no wrap_info',
isOSS,
'it resolves isCommunity:true, wrapTTL: false, response: has no wrap_info',
isCommunity,
[[{ one: 'two', three: 'four' }], { wrap_info: null }, false],
(assert, result) => resolvesArgs(assert, result, [{ one: 'two', three: 'four' }]),
],
[
'it resolves isOSS: false and wrapTTL:true response: has wrap_info',
'it resolves isCommunity: false and wrapTTL:true response: has wrap_info',
isEnt,
[[{ one: 'two', three: 'four' }], { wrap_info: { token: 'foo', accessor: 'bar' } }, true],
(assert, result) => resolvesArgs(assert, result, [{ one: 'two', three: 'four' }]),
],
[
'it resolves isOSS: false and wrapTTL:false response: has no wrap_info',
'it resolves isCommunity: false and wrapTTL:false response: has no wrap_info',
isEnt,
[[{ one: 'two', three: 'four' }], { wrap_info: null }, false],
(assert, result) => resolvesArgs(assert, result, [{ one: 'two', three: 'four' }]),
],
[
'it rejects isOSS: false, wrapTTL:false, response: has wrap_info',
'it rejects isCommunity: false, wrapTTL:false, response: has wrap_info',
isEnt,
[
[{ one: 'two', three: 'four' }],
@@ -107,7 +107,8 @@ module('Unit | Service | control group', function (hooks) {
],
].forEach(function ([name, setup, args, expectation]) {
test(`checkForControlGroup: ${name}`, function (assert) {
const assertCount = name === 'it rejects isOSS: false, wrapTTL:false, response: has wrap_info' ? 2 : 1;
const assertCount =
name === 'it rejects isCommunity: false, wrapTTL:false, response: has wrap_info' ? 2 : 1;
assert.expect(assertCount);
if (setup) {
setup(this);

View File

@@ -9,24 +9,17 @@ import { setupTest } from 'ember-qunit';
module('Unit | Service | version', function (hooks) {
setupTest(hooks);
test('setting version computes isOSS properly', function (assert) {
test('setting type computes isCommunity properly', function (assert) {
const service = this.owner.lookup('service:version');
service.version = '0.9.5';
assert.true(service.isOSS);
service.type = 'community';
assert.true(service.isCommunity);
assert.false(service.isEnterprise);
});
test('setting version computes isEnterprise properly', function (assert) {
test('setting type computes isEnterprise properly', function (assert) {
const service = this.owner.lookup('service:version');
service.version = '0.9.5+ent';
assert.false(service.isOSS);
assert.true(service.isEnterprise);
});
test('setting version with hsm ending computes isEnterprise properly', function (assert) {
const service = this.owner.lookup('service:version');
service.version = '0.9.5+ent.hsm';
assert.false(service.isOSS);
service.type = 'enterprise';
assert.false(service.isCommunity);
assert.true(service.isEnterprise);
});