mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 01:32:33 +00:00
UI: chroot namespace listener (#23942)
This commit is contained in:
3
changelog/23942.txt
Normal file
3
changelog/23942.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:bug
|
||||
ui: fix broken GUI when accessing from listener with chroot_namespace defined
|
||||
```
|
||||
@@ -80,6 +80,11 @@ export default ApplicationAdapter.extend({
|
||||
performancestandbycode: 200,
|
||||
},
|
||||
unauthenticated: true,
|
||||
}).catch(() => {
|
||||
// sys/health will only fail when chroot set
|
||||
// because it's allowed in root namespace only and
|
||||
// configured to return a 200 response in other fail scenarios
|
||||
return { has_chroot_namespace: true };
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ export default class ClusterModel extends Model {
|
||||
@attr('boolean') standby;
|
||||
@attr('string') type;
|
||||
@attr('object') license;
|
||||
// manually set on response when sys/health failure
|
||||
@attr('boolean') hasChrootNamespace;
|
||||
|
||||
/* Licensing concerns */
|
||||
get licenseExpiry() {
|
||||
|
||||
@@ -9,17 +9,16 @@ import { alias, and, equal } from '@ember/object/computed';
|
||||
export default Model.extend({
|
||||
name: attr('string'),
|
||||
// https://developer.hashicorp.com/vault/api-docs/system/health
|
||||
initialized: attr('boolean'),
|
||||
sealed: attr('boolean'),
|
||||
isSealed: alias('sealed'),
|
||||
standby: attr('boolean'),
|
||||
isActive: equal('standby', false),
|
||||
clusterName: attr('string'),
|
||||
clusterId: attr('string'),
|
||||
|
||||
isLeader: and('initialized', 'isActive'),
|
||||
|
||||
// https://developer.hashicorp.com/vault/api-docs/system/seal-status
|
||||
initialized: attr('boolean'),
|
||||
sealed: attr('boolean'),
|
||||
isSealed: alias('sealed'),
|
||||
// The "t" parameter is the threshold, and "n" is the number of shares.
|
||||
t: attr('number'),
|
||||
n: attr('number'),
|
||||
|
||||
@@ -29,18 +29,20 @@ export default class VaultClusterDashboardRoute extends Route.extend(ClusterRout
|
||||
|
||||
model() {
|
||||
const clusterModel = this.modelFor('vault.cluster');
|
||||
const replication = {
|
||||
dr: clusterModel.dr,
|
||||
performance: clusterModel.performance,
|
||||
};
|
||||
|
||||
const hasChroot = clusterModel?.hasChrootNamespace;
|
||||
const replication = hasChroot
|
||||
? null
|
||||
: {
|
||||
dr: clusterModel.dr,
|
||||
performance: clusterModel.performance,
|
||||
};
|
||||
return hash({
|
||||
replication,
|
||||
secretsEngines: this.store.query('secret-engine', {}),
|
||||
license: this.store.queryRecord('license', {}).catch(() => null),
|
||||
isRootNamespace: this.namespace.inRootNamespace,
|
||||
isRootNamespace: this.namespace.inRootNamespace && !hasChroot,
|
||||
version: this.version,
|
||||
vaultConfiguration: this.getVaultConfiguration(),
|
||||
vaultConfiguration: hasChroot ? null : this.getVaultConfiguration(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class VersionService extends Service {
|
||||
@task
|
||||
*getVersion() {
|
||||
if (this.version) return;
|
||||
const response = yield this.store.adapterFor('cluster').health();
|
||||
const response = yield this.store.adapterFor('cluster').sealStatus();
|
||||
this.version = response.version;
|
||||
return;
|
||||
}
|
||||
|
||||
46
ui/app/templates/error.hbs
Normal file
46
ui/app/templates/error.hbs
Normal file
@@ -0,0 +1,46 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<div class="is-flex-grow-1 is-flex-v-centered">
|
||||
<div class="empty-state-content">
|
||||
<div class="is-flex-v-centered has-bottom-margin-xxl">
|
||||
<div class="brand-icon-large">
|
||||
<Icon @name="vault" @size="24" @stretched={{true}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-flex-center">
|
||||
<div class="error-icon">
|
||||
<Icon @name="help" @size="24" class="has-text-grey" @stretched={{true}} />
|
||||
</div>
|
||||
<div class="has-left-margin-s">
|
||||
<h1 class="is-size-4 has-text-semibold has-text-grey has-line-height-1">
|
||||
{{#if (eq this.model.httpStatus 403)}}
|
||||
Not authorized
|
||||
{{else if (eq this.model.httpStatus 404)}}
|
||||
Page not found
|
||||
{{else}}
|
||||
Error
|
||||
{{/if}}
|
||||
</h1>
|
||||
<p class="has-text-grey is-size-8">Error {{this.model.httpStatus}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="has-text-grey has-top-margin-m has-bottom-padding-l has-border-bottom-light" data-test-error-description>
|
||||
{{this.model.message}}
|
||||
{{join ". " this.model.errors}}
|
||||
</p>
|
||||
|
||||
<div class="is-flex-between has-top-margin-s">
|
||||
<ExternalLink @href="/" @sameTab={{true}} class="is-no-underline is-flex-center has-text-semibold">
|
||||
<Chevron @direction="left" />
|
||||
Go home
|
||||
</ExternalLink>
|
||||
<DocLink @path="/vault/api-docs#http-status-codes">
|
||||
Learn more
|
||||
</DocLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,12 +4,12 @@
|
||||
~}}
|
||||
|
||||
<Dashboard::Overview
|
||||
@replication={{@model.replication}}
|
||||
@secretsEngines={{@model.secretsEngines}}
|
||||
@license={{@model.license}}
|
||||
@isRootNamespace={{@model.isRootNamespace}}
|
||||
@version={{@model.version}}
|
||||
@vaultConfiguration={{@model.vaultConfiguration}}
|
||||
@replication={{this.model.replication}}
|
||||
@secretsEngines={{this.model.secretsEngines}}
|
||||
@license={{this.model.license}}
|
||||
@isRootNamespace={{this.model.isRootNamespace}}
|
||||
@version={{this.model.version}}
|
||||
@vaultConfiguration={{this.model.vaultConfiguration}}
|
||||
@refreshModel={{this.refreshModel}}
|
||||
@replicationUpdatedAt={{this.replicationUpdatedAt}}
|
||||
/>
|
||||
15
ui/mirage/handlers/chroot-namespace.js
Normal file
15
ui/mirage/handlers/chroot-namespace.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { Response } from 'miragejs';
|
||||
|
||||
/*
|
||||
These are mocked responses to mimic what we get from the server
|
||||
when within a chrooted listener (assuming the namespace exists)
|
||||
*/
|
||||
export default function (server) {
|
||||
server.get('sys/health', () => new Response(400, {}, { errors: ['unsupported path'] }));
|
||||
server.get('sys/replication/status', () => new Response(400, {}, { errors: ['unsupported path'] }));
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
// add all handlers here
|
||||
// individual lookup done in mirage config
|
||||
import base from './base';
|
||||
import chrootNamespace from './chroot-namespace';
|
||||
import clients from './clients';
|
||||
import db from './db';
|
||||
import hcpLink from './hcp-link';
|
||||
@@ -19,6 +20,7 @@ import reducedDisclosure from './reduced-disclosure';
|
||||
|
||||
export {
|
||||
base,
|
||||
chrootNamespace,
|
||||
clients,
|
||||
db,
|
||||
hcpLink,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"fmt:styles": "prettier --write app/styles/**/*.*",
|
||||
"start": "VAULT_ADDR=http://localhost:8200; ember server --proxy=$VAULT_ADDR",
|
||||
"start2": "ember server --proxy=http://localhost:8202 --port=4202",
|
||||
"start:chroot": "ember server --proxy=http://localhost:8300 --port=4300",
|
||||
"start:mirage": "start () { MIRAGE_DEV_HANDLER=$1 yarn run start; }; start",
|
||||
"test": "npm-run-all --print-name lint:js:quiet lint:hbs:quiet && node scripts/start-vault.js",
|
||||
"test:enos": "npm-run-all lint:js:quiet lint:hbs:quiet && node scripts/enos-test-ember.js",
|
||||
|
||||
29
ui/tests/acceptance/chroot-namespace-test.js
Normal file
29
ui/tests/acceptance/chroot-namespace-test.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import { currentRouteName } from '@ember/test-helpers';
|
||||
import authPage from 'vault/tests/pages/auth';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import ENV from 'vault/config/environment';
|
||||
|
||||
module('Acceptance | chroot-namespace enterprise ui', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.before(function () {
|
||||
ENV['ember-cli-mirage'].handler = 'chrootNamespace';
|
||||
});
|
||||
hooks.after(function () {
|
||||
ENV['ember-cli-mirage'].handler = null;
|
||||
});
|
||||
|
||||
test('it should render normally when chroot namespace exists', async function (assert) {
|
||||
await authPage.login();
|
||||
assert.strictEqual(currentRouteName(), 'vault.cluster.dashboard', 'goes to dashboard page');
|
||||
assert.dom('[data-test-badge-namespace]').includesText('root', 'Shows root namespace badge');
|
||||
});
|
||||
});
|
||||
@@ -49,10 +49,11 @@ module('Acceptance | landing page dashboard', function (hooks) {
|
||||
await visit('/vault/dashboard');
|
||||
const version = this.owner.lookup('service:version');
|
||||
const versionName = version.version;
|
||||
const versionNameEnd = version.isEnterprise ? versionName.indexOf('+') : versionName.length;
|
||||
assert
|
||||
.dom(SELECTORS.cardHeader('Vault version'))
|
||||
.hasText(`Vault v${versionName.slice(0, versionNameEnd)} root`);
|
||||
const versionText = version.isEnterprise
|
||||
? `Vault v${versionName.slice(0, versionName.indexOf('+'))} root`
|
||||
: `Vault v${versionName}`;
|
||||
|
||||
assert.dom(SELECTORS.cardHeader('Vault version')).hasText(versionText);
|
||||
});
|
||||
|
||||
module('secrets engines card', function (hooks) {
|
||||
|
||||
Reference in New Issue
Block a user