UI: handle reduced disclosure on replication endpoints (#24379)

* add replicationRedacted attribute to cluster model

* disallow access to replication pages if repl endpoints are redacted

* hide replicatio nav item

* Hide replication card on dashboard
This commit is contained in:
Chelsea Shaw
2023-12-05 14:31:29 -06:00
committed by GitHub
parent a41852379b
commit 588dd73fe0
8 changed files with 52 additions and 12 deletions

View File

@@ -61,6 +61,12 @@ export default ApplicationAdapter.extend({
}
if (replicationStatus && replicationStatus instanceof AdapterError === false) {
ret = Object.assign(ret, replicationStatus.data);
} else if (
replicationStatus instanceof AdapterError &&
replicationStatus?.errors.find((err) => err === 'disabled path')
) {
// set redacted if result is an error which only happens when redacted
ret = Object.assign(ret, { replication_redacted: true });
}
return resolve(ret);
});

View File

@@ -52,7 +52,14 @@
}}
<Nav.Title data-test-sidebar-nav-heading="Monitoring">Monitoring</Nav.Title>
{{/if}}
{{#if (and this.version.isEnterprise this.namespace.inRootNamespace (has-permission "status" routeParams="replication"))}}
{{#if
(and
this.version.isEnterprise
this.namespace.inRootNamespace
(not this.cluster.replicationRedacted)
(has-permission "status" routeParams="replication")
)
}}
<Nav.Link
@route="vault.cluster.replication.index"
@text="Replication"

View File

@@ -16,8 +16,10 @@ export default class ClusterModel extends Model {
@attr('boolean') standby;
@attr('string') type;
@attr('object') license;
// manually set on response when sys/health failure
// manually set on response in cluster adapter
@attr('boolean') hasChrootNamespace;
@attr('boolean') replicationRedacted;
/* Licensing concerns */
get licenseExpiry() {

View File

@@ -30,12 +30,13 @@ export default class VaultClusterDashboardRoute extends Route.extend(ClusterRout
model() {
const clusterModel = this.modelFor('vault.cluster');
const hasChroot = clusterModel?.hasChrootNamespace;
const replication = hasChroot
? null
: {
dr: clusterModel.dr,
performance: clusterModel.performance,
};
const replication =
hasChroot || clusterModel.replicationRedacted
? null
: {
dr: clusterModel.dr,
performance: clusterModel.performance,
};
return hash({
replication,
secretsEngines: this.store.query('secret-engine', {}),

View File

@@ -12,7 +12,9 @@
{{#if @license}}
<Dashboard::ClientCountCard @license={{@license}} />
{{/if}}
{{#if (and @isRootNamespace (has-permission "status" routeParams="replication"))}}
{{#if
(and @isRootNamespace (has-permission "status" routeParams="replication") (not (is-empty-value @replication)))
}}
<Dashboard::ReplicationCard
@replication={{@replication}}
@version={{@version}}

View File

@@ -13,8 +13,13 @@ export default Route.extend(ClusterRoute, {
version: service(),
store: service(),
auth: service(),
router: service(),
beforeModel() {
if (this.auth.activeCluster.replicationRedacted) {
// disallow replication access if endpoints are redacted
return this.router.transitionTo('vault.cluster');
}
return this.version.fetchFeatures().then(() => {
return this._super(...arguments);
});

View File

@@ -4,6 +4,7 @@
*/
import modifyPassthroughResponse from '../helpers/modify-passthrough-response';
import { Response } from 'miragejs';
export default function (server) {
server.get('/sys/health', (schema, req) =>
@@ -12,7 +13,10 @@ export default function (server) {
server.get('/sys/seal-status', (schema, req) =>
modifyPassthroughResponse(req, { version: '', cluster_name: '', build_date: '' })
);
server.get('sys/replication/status', () => new Response(404));
server.get('sys/replication/dr/status', () => new Response(404));
server.get('sys/replication/performance/status', () => new Response(404));
server.get('sys/replication/status', () => new Response(404, {}, { errors: ['disabled path'] }));
server.get('sys/replication/dr/status', () => new Response(404, {}, { errors: ['disabled path'] }));
server.get(
'sys/replication/performance/status',
() => new Response(404, {}, { errors: ['disabled path'] })
);
}

View File

@@ -134,4 +134,17 @@ module('Acceptance | Enterprise | reduced disclosure test', function (hooks) {
.dom('[data-test-footer-version]')
.hasText(`Vault ${versionSvc.version}`, 'Version is shown after login');
});
test('does not allow access to replication pages', async function (assert) {
await authPage.login();
assert.dom('[data-test-sidebar-nav-link="Replication"]').doesNotExist('hides replication nav item');
await visit(`/vault/replication/dr`);
assert.strictEqual(
currentRouteName(),
'vault.cluster.dashboard',
'redirects to dashboard if replication access attempted'
);
assert.dom('[data-test-card="replication"]').doesNotExist('hides replication card on dashboard');
});
});