Merge remote-tracking branch 'remotes/from/ce/main'
Some checks are pending
build / setup (push) Waiting to run
build / Check ce/* Pull Requests (push) Blocked by required conditions
build / ui (push) Blocked by required conditions
build / artifacts-ce (push) Blocked by required conditions
build / artifacts-ent (push) Blocked by required conditions
build / hcp-image (push) Blocked by required conditions
build / test (push) Blocked by required conditions
build / test-hcp-image (push) Blocked by required conditions
build / completed-successfully (push) Blocked by required conditions
CI / setup (push) Waiting to run
CI / Run Autopilot upgrade tool (push) Blocked by required conditions
CI / Run Go tests (push) Blocked by required conditions
CI / Run Go tests tagged with testonly (push) Blocked by required conditions
CI / Run Go tests with data race detection (push) Blocked by required conditions
CI / Run Go tests with FIPS configuration (push) Blocked by required conditions
CI / Test UI (push) Blocked by required conditions
CI / tests-completed (push) Blocked by required conditions
Run linters / Setup (push) Waiting to run
Run linters / Deprecated functions (push) Blocked by required conditions
Run linters / Code checks (push) Blocked by required conditions
Run linters / Protobuf generate delta (push) Blocked by required conditions
Run linters / Format (push) Blocked by required conditions
Run linters / Semgrep (push) Waiting to run
Check Copywrite Headers / copywrite (push) Waiting to run
Security Scan / scan (push) Waiting to run

This commit is contained in:
hc-github-team-secure-vault-core 2026-02-11 18:16:30 +00:00
commit fbbf175789
20 changed files with 84 additions and 82 deletions

View file

@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Page::Header @title="Raft Storage">
<Page::Header @title="Raft storage">
<:actions>
<Hds::Dropdown as |dd|>
<dd.ToggleButton @text="Snapshots" @color="secondary" />

View file

@ -6,7 +6,7 @@
<Page::Header @title="Restore Snapshot">
<:breadcrumbs>
<Page::Breadcrumbs
@breadcrumbs={{array (hash label="Raft Storage" route="vault.cluster.storage") (hash label="Restore Snapshot")}}
@breadcrumbs={{array (hash label="Raft storage" route="vault.cluster.storage") (hash label="Restore Snapshot")}}
/>
</:breadcrumbs>
</Page::Header>

View file

@ -68,22 +68,29 @@ export const PERMISSIONS_BANNER_STATES = {
export const RESULTANT_ACL_PATH = 'sys/internal/ui/resultant-acl'; // export for tests
// API_PATHS is used to find the first accessible path for a given nav item, so it must be kept in order of the sidebar nav structure.
const API_PATHS = {
access: {
methods: 'sys/auth',
mfa: 'identity/mfa/method',
oidc: 'identity/oidc/client',
entities: 'identity/entity/id',
groups: 'identity/group/id',
leases: 'sys/leases/lookup',
namespaces: 'sys/namespaces',
'control-groups': 'sys/control-group/',
sync: {
destinations: 'sys/sync/destinations',
associations: 'sys/sync/associations',
config: 'sys/sync/config',
github: 'sys/sync/github-apps',
},
policies: {
acl: 'sys/policies/acl',
rgp: 'sys/policies/rgp',
egp: 'sys/policies/egp',
},
access: {
'control-groups': 'sys/control-group/',
leases: 'sys/leases/lookup',
methods: 'sys/auth',
mfa: 'identity/mfa/method',
oidc: 'identity/oidc/client',
namespaces: 'sys/namespaces',
groups: 'identity/group/id',
entities: 'identity/entity/id',
},
tools: {
wrap: 'sys/wrapping/wrap',
lookup: 'sys/wrapping/lookup',
@ -92,39 +99,34 @@ const API_PATHS = {
random: 'sys/tools/random',
hash: 'sys/tools/hash',
},
settings: {
customMessages: 'sys/config/ui/custom-messages',
},
status: {
seal: 'sys/seal',
replication: 'sys/replication',
license: 'sys/license',
seal: 'sys/seal',
raft: 'sys/storage/raft/configuration',
},
clients: {
activity: 'sys/internal/counters/activity',
config: 'sys/internal/counters/config',
},
settings: {
customMessages: 'sys/config/ui/custom-messages',
},
sync: {
destinations: 'sys/sync/destinations',
associations: 'sys/sync/associations',
config: 'sys/sync/config',
github: 'sys/sync/github-apps',
},
monitoring: {
'utilization-report': 'sys/utilization-report',
},
};
// API_PATHS_TO_ROUTE_PARAMS is used to resolve route params for that path when checking permissions for the nav item.
const API_PATHS_TO_ROUTE_PARAMS = {
'sys/auth': { route: 'vault.cluster.access.methods', models: [] },
'identity/entity/id': { route: 'vault.cluster.access.identity', models: ['entities'] },
'identity/group/id': { route: 'vault.cluster.access.identity', models: ['groups'] },
'sys/leases/lookup': { route: 'vault.cluster.access.leases', models: [] },
'sys/namespaces': { route: 'vault.cluster.access.namespaces', models: [] },
'sys/control-group/': { route: 'vault.cluster.access.control-groups', models: [] },
'identity/mfa/method': { route: 'vault.cluster.access.mfa', models: [] },
'identity/oidc/client': { route: 'vault.cluster.access.oidc', models: [] },
'sys/auth': { route: 'vault.cluster.access.methods', models: [] },
'sys/control-group/': { route: 'vault.cluster.access.control-groups', models: [] },
'sys/leases/lookup': { route: 'vault.cluster.access.leases', models: [] },
'sys/namespaces': { route: 'vault.cluster.access.namespaces', models: [] },
};
// Canary endpoints: quick check for “meaningful UI access” in the *current* namespace.

View file

@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Page::Header @title="Authentication Methods">
<Page::Header @title="Authentication methods">
<:breadcrumbs>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</:breadcrumbs>

View file

@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Page::Header @title=" Multi-Factor Authentication">
<Page::Header @title=" Multi-factor authentication">
<:description>
<Hds::Text::Body>
Configure and enforce multi-factor authentication (MFA) for users logging into Vault, for any

View file

@ -4,7 +4,7 @@
}}
{{#if this.header}}
<Page::Header @title="OIDC Provider">
<Page::Header @title="OIDC provider">
<:breadcrumbs>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</:breadcrumbs>

View file

@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1
}}
<Page::Header @title="Create {{uppercase this.policyType}} Policy">
<Page::Header @title="Create {{uppercase this.policyType}} policy">
<:breadcrumbs>
<Page::Breadcrumbs
@breadcrumbs={{array

View file

@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1
}}
{{#if (or (eq this.policyType "acl") (has-feature "Sentinel"))}}
<Page::Header @title="{{uppercase this.policyType}} Policies">
<Page::Header @title="{{uppercase this.policyType}} policies">
<:badges>
{{#if (not-eq this.policyType "acl")}}
<Hds::Badge @text="Sentinel" />
@ -13,7 +13,7 @@
<Page::Breadcrumbs
@breadcrumbs={{array
(hash label="Vault" route="vault.cluster.dashboard" icon="vault")
(hash label=(concat (uppercase this.policyType) " Policies"))
(hash label=(concat (uppercase this.policyType) " policies"))
}}
/>
</:breadcrumbs>

View file

@ -7,7 +7,7 @@
@ariaLabel="Access Navigation Links"
tabindex="0"
role="region"
data-test-sidebar-nav-panel="Access"
data-test-sidebar-nav-panel="Access control"
as |Nav|
>
<Nav.BackLink

View file

@ -17,8 +17,8 @@
<Nav.Link
@route="vault.cluster.clients.counts.overview"
@current-when="vault.cluster.clients.counts"
@text="Client Usage"
data-test-sidebar-nav-link="Client Usage"
@text="Client usage"
data-test-sidebar-nav-link="Client usage"
/>
{{#if @canReadConfig}}
<Nav.Link

View file

@ -17,14 +17,13 @@
data-test-sidebar-nav-link="Resilience and recovery"
/>
{{/if}}
{{#if (or (has-permission "access") (has-permission "policies"))}}
<Nav.Link
@route={{this.accessRoute}}
@models={{this.accessRouteModels}}
@text="Access"
@text="Access control"
@hasSubItems={{true}}
data-test-sidebar-nav-link="Access"
data-test-sidebar-nav-link="Access control"
/>
{{/if}}
{{#if (has-permission "tools")}}
@ -44,14 +43,6 @@
}}
<Nav.Title data-test-sidebar-nav-heading="Monitoring">Monitoring</Nav.Title>
{{/if}}
{{#if (and this.cluster.usingRaft this.isRootNamespace (has-permission "status" routeParams="raft"))}}
<Nav.Link
@route="vault.cluster.storage"
@model={{this.cluster.name}}
@text="Raft Storage"
data-test-sidebar-nav-link="Raft Storage"
/>
{{/if}}
{{#if (display-nav-item this.navSection.reporting)}}
<Nav.Link
@route={{if (display-nav-item this.routeName.vaultUsage) "vault.cluster.usage-reporting" "vault.cluster.license"}}
@ -68,4 +59,12 @@
data-test-sidebar-nav-link="Client count"
/>
{{/if}}
{{#if (and this.cluster.usingRaft this.isRootNamespace (has-permission "status" routeParams="raft"))}}
<Nav.Link
@route="vault.cluster.storage"
@model={{this.cluster.name}}
@text="Raft storage"
data-test-sidebar-nav-link="Raft storage"
/>
{{/if}}
</Hds::AppSideNav::Portal>

View file

@ -53,23 +53,24 @@ export default class SidebarNavClusterComponent extends Component {
}
get accessRoute() {
if (this.permissions.hasPermission('policies')) {
if (this.permissions.hasNavPermission('policies')) {
return 'vault.cluster.policies';
}
if (this.permissions.hasPermission('access')) {
return 'vault.cluster.access';
if (this.permissions.hasNavPermission('access')) {
return this.permissions.navPathParams('access').route;
}
return null;
}
get accessRouteModels() {
if (this.permissions.hasPermission('policies')) {
return this.routeParamsFor('policies').models;
if (this.permissions.hasNavPermission('policies')) {
return this.routeParamsFor('policies')?.models;
}
if (this.permissions.hasPermission('access')) {
return this.routeParamsFor('access').models;
if (this.permissions.hasNavPermission('access')) {
return this.routeParamsFor('access')?.models;
}
return null;

View file

@ -88,7 +88,7 @@ module('Acceptance | auth-methods list view', function (hooks) {
for (const [key] of Object.entries(authPayload)) {
assert
.dom(GENERAL.linkedBlock(sanitizePath(key)))
.exists({ count: 1 }, `auth method ${key} appears in list view after navigating from OIDC Provider`);
.exists({ count: 1 }, `auth method ${key} appears in list view after navigating from OIDC provider`);
}
});

View file

@ -32,7 +32,7 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) {
test('root-only nav items are unavailable', async function (assert) {
await login();
['Dashboard', 'Secrets', 'Access', 'Operational tools'].forEach((nav) => {
['Dashboard', 'Secrets', 'Access control', 'Operational tools'].forEach((nav) => {
assert.dom(navLink(nav)).exists(`Shows ${nav} nav item in chroot listener`);
});
// Client count is not root-only, but it is hidden for chroot
@ -54,7 +54,7 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) {
const userDefault = await runCmd(createTokenCmd());
await loginNs(namespace, userDefault);
[('Dashboard', 'Secrets', 'Access', 'Operational tools')].forEach((nav) => {
[('Dashboard', 'Secrets', 'Access control', 'Operational tools')].forEach((nav) => {
assert.dom(navLink(nav)).exists(`Shows ${nav} nav item for user with default policy`);
});
['Client count', 'Replication', 'Raft Storage', 'License', 'Seal Vault'].forEach((nav) => {
@ -84,7 +84,7 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) {
);
await loginNs(namespace, reader);
['Dashboard', 'Secrets', 'Access', 'Operational tools'].forEach((nav) => {
['Dashboard', 'Secrets', 'Access control', 'Operational tools'].forEach((nav) => {
assert.dom(navLink(nav)).exists(`Shows ${nav} nav item for user with read access policy`);
});
['Replication', 'Raft Storage', 'License', 'Seal Vault', 'Client count'].forEach((nav) => {
@ -116,7 +116,7 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) {
await runCmd(`write sys/namespaces/child -f`, false);
await loginNs(namespace, childReader);
['Dashboard', 'Secrets', 'Access', 'Operational tools'].forEach((nav) => {
['Dashboard', 'Secrets', 'Access control', 'Operational tools'].forEach((nav) => {
assert.dom(navLink(nav)).exists(`Shows ${nav} nav item`);
});
['Client count', 'Replication', 'Raft Storage', 'License', 'Seal Vault'].forEach((nav) => {
@ -125,7 +125,7 @@ module('Acceptance | chroot-namespace enterprise ui', function (hooks) {
await loginNs(`${namespace}/child`, childReader);
['Dashboard', 'Secrets', 'Access', 'Operational tools'].forEach((nav) => {
['Dashboard', 'Secrets', 'Access control', 'Operational tools'].forEach((nav) => {
assert.dom(navLink(nav)).exists(`Shows ${nav} nav item within child namespace`);
});
['Replication', 'Raft Storage', 'License', 'Seal Vault', 'Client count'].forEach((nav) => {

View file

@ -31,11 +31,11 @@ module('Acceptance | Enterprise | sidebar navigation', function (hooks) {
await click(GENERAL.navLink('Client count'));
assert.dom(panel('Client count')).exists('Client count nav panel renders');
assert.dom(GENERAL.navLink('Client Usage')).hasClass('active', 'Client Usage link is active');
assert.dom(GENERAL.navLink('Client usage')).hasClass('active', 'Client usage link is active');
assert.strictEqual(currentURL(), '/vault/clients/counts/overview', 'Client counts route renders');
await click(GENERAL.navLink('Back to main navigation'));
await click(GENERAL.navLink('Access'));
await click(GENERAL.navLink('Access control'));
await click(GENERAL.navLink('Approval workflow'));
assert.strictEqual(currentURL(), '/vault/access/control-groups', 'Approval workflow route renders');
@ -43,7 +43,7 @@ module('Acceptance | Enterprise | sidebar navigation', function (hooks) {
assert.strictEqual(currentURL(), '/vault/access/namespaces?page=1', 'Replication route renders');
await click(GENERAL.navLink('Back to main navigation'));
await click(GENERAL.navLink('Access'));
await click(GENERAL.navLink('Access control'));
await click(GENERAL.navLink('Role governing policies'));
assert.strictEqual(currentURL(), '/vault/policies/rgp', 'Role governing policies route renders');
@ -54,8 +54,8 @@ module('Acceptance | Enterprise | sidebar navigation', function (hooks) {
test('it should link to correct routes at the access level', async function (assert) {
assert.expect(12);
await click(GENERAL.navLink('Access'));
assert.dom(panel('Access')).exists('Access nav panel renders');
await click(GENERAL.navLink('Access control'));
assert.dom(panel('Access control')).exists('Access nav panel renders');
const links = [
{ label: 'ACL policies', route: '/vault/policies/acl' },

View file

@ -355,7 +355,7 @@ module('Acceptance | oidc-config clients', function (hooks) {
await visit(OIDC_BASE_URL);
assert.strictEqual(currentURL(), '/vault/access/oidc');
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('OIDC Provider');
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('OIDC provider');
assert.dom(SELECTORS.oidcHeader).hasText(
`Configure Vault to act as an OIDC identity provider, and offer Vaults various authentication
methods and source of identity to any client applications. Learn more Create your first app`,

View file

@ -40,7 +40,7 @@ module('Acceptance | policies', function (hooks) {
test('it navigates to and from policy show page from sidebar', async function (assert) {
await visit('/vault/dashboard');
await click(GENERAL.navLink('Access'));
await click(GENERAL.navLink('Access control'));
assert.strictEqual(currentURL(), '/vault/policies/acl', 'currentURL is /vault/policies/acl');
await fillIn('[data-test-component="navigate-input"]', 'default'); // filter for the policy in case there are many on this view and the default policy is on the second page
await click('[data-test-policy-link="default"]');

View file

@ -45,7 +45,7 @@ module('Acceptance | sidebar navigation', function (hooks) {
assert.dom(panel('Cluster')).exists('Cluster nav panel renders');
const subNavs = [
{ label: 'Access', route: 'policies/acl' },
{ label: 'Access control', route: 'policies/acl' },
{ label: 'Operational tools', route: 'tools/wrap' },
];
@ -58,7 +58,7 @@ module('Acceptance | sidebar navigation', function (hooks) {
}
const links = [
{ label: 'Raft Storage', route: '/vault/storage/raft' },
{ label: 'Raft storage', route: '/vault/storage/raft' },
{ label: 'Secrets', route: '/vault/secrets-engines' },
{ label: 'Dashboard', route: '/vault/dashboard' },
];
@ -72,8 +72,8 @@ module('Acceptance | sidebar navigation', function (hooks) {
test('it should link to correct routes at the access level', async function (assert) {
assert.expect(8);
await click(link('Access'));
assert.dom(panel('Access')).exists('Access nav panel renders');
await click(link('Access control'));
assert.dom(panel('Access control')).exists('Access nav panel renders');
const links = [
{ label: 'ACL policies', route: '/vault/policies/acl' },
@ -117,23 +117,23 @@ module('Acceptance | sidebar navigation', function (hooks) {
await click(link('Client count'));
assert.dom(panel('Client count')).exists('Client counts nav panel renders');
assert.strictEqual(currentURL(), '/vault/clients/counts/overview', 'Top level nav link renders overview');
assert.dom(link('Client Usage')).hasClass('active');
assert.dom(link('Client usage')).hasClass('active');
await click(link('Configuration'));
assert.strictEqual(currentURL(), '/vault/clients/config', 'Clients configuration renders');
assert.dom(link('Configuration')).hasClass('active');
await click(link('Client Usage'));
await click(link('Client usage'));
assert.strictEqual(currentURL(), '/vault/clients/counts/overview', 'Sub nav link navigates to overview');
assert.dom(link('Client Usage')).hasClass('active');
assert.dom(link('Client usage')).hasClass('active');
});
test('it should display access nav when mounting and configuring auth methods', async function (assert) {
await click(link('Access'));
await click(link('Access control'));
await click('[data-test-sidebar-nav-link="Authentication methods"]');
await click('[data-test-auth-enable]');
assert.dom('[data-test-sidebar-nav-panel="Access"]').exists('Access nav panel renders');
assert.dom('[data-test-sidebar-nav-panel="Access control"]').exists('Access nav panel renders');
await click(link('Authentication methods'));
await click(GENERAL.linkedBlock('token'));
await click('[data-test-configure-link]');
assert.dom('[data-test-sidebar-nav-panel="Access"]').exists('Access nav panel renders');
assert.dom('[data-test-sidebar-nav-panel="Access control"]').exists('Access nav panel renders');
});
});

View file

@ -18,7 +18,7 @@ const SELECTORS = {
module('Integration | Component | policy-example', function (hooks) {
setupRenderingTest(hooks);
test('it renders the correct paragraph for ACL policy', async function (assert) {
test('it renders the correct paragraph for ACL Policy', async function (assert) {
await render(hbs`
<PolicyExample
@policyType="acl"

View file

@ -61,9 +61,9 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
'Dashboard',
'Secrets',
'Resilience and recovery',
'Access',
'Access control',
'Operational tools',
'Raft Storage',
'Raft storage',
'Client count',
];
@ -81,11 +81,11 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
const links = [
'Dashboard',
'Secrets',
'Access',
'Access control',
'Operational tools',
'Resilience and recovery',
'Reporting',
'Raft Storage',
'Raft storage',
'Client count',
];
// do not add PKI-only Secrets feature as it hides Client count nav link
@ -100,7 +100,7 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
});
test('it should hide enterprise related links in child namespace', async function (assert) {
const links = ['Raft Storage', 'License'];
const links = ['Raft storage', 'License'];
this.owner.lookup('service:namespace').set('path', 'foo');
const stubs = stubFeaturesAndPermissions(this.owner, true, true);
stubs.hasNavPermission.callsFake((route) => route !== 'clients');
@ -131,7 +131,7 @@ module('Integration | Component | sidebar-nav-cluster', function (hooks) {
usingRaft: true,
hasChrootNamespace: true,
});
const links = ['Client Counts', 'Replication', 'Raft Storage', 'License', 'Seal Vault'];
const links = ['Client Counts', 'Replication', 'Raft storage', 'License', 'Seal Vault'];
await renderComponent();
assert