PR 19793 review

rancher/dashboard PR 19793 (codyrancher), "List the namespaces a user can create the resource in on create forms". Head ee30ed3c4, reviewed 2026-11-05. You are the only requested reviewer, and nobody has reviewed it yet.

Preview: rancherpr19793r.mf-created-dashboard.ui.rancher.space, built from ee30ed3c4, backend mf Rancher (v2.16.0-7758e3e-head). PR + fix: rancherpr19793-fixr.mf-created-dashboard.ui.rancher.space, built from c644df7f0, which is the PR plus card 1's fix, local only and not pushed. Baseline: the shared dashboard-master preview, rebuilt today from 261b8932f, the current tip.

Verdict: comment now, approve once card 1 is in

Test it yourself

The login page defaults to OpenLDAP, so click Log in as a local user first. The password for every issue-8544* user is in ~/Projects/pr-reviews/pr19793-fixtures/password.txt on your Mac. It's not on this page because the page is public.

  1. The PR's own walk: open PR: Create Secret as issue-8544 and pick Opaque. Namespace lists secrets-ns1 and secrets-ns2, as the description says. On master it lists only secrets-ns1.
  2. Card 1: do the same as issue-8544-first. It opens on aaa-view-ns, which is also in the list. Type a name and click Create to get the forbidden error.
  3. The fix: open PR + fix: Create Secret as the same user. It opens on secrets-ns1 and aaa-view-ns is gone. Don't click Create here: a save remembers secrets-ns1 as that user's last namespace, and then the PR preview opens there too and hides the bug. If that happens, issue-8544-cm shows the same thing on default.
  4. Chart install: on any preview, open Apps, then Charts, then rancher-alerting-drivers, then Install. It behaves the same way as Secrets.
Findings
1

The form still opens on a namespace the user can't create in

shell/components/form/NamespaceSelect.vue:108-122 and :187-189, shell/store/index.js:477
Should fix

defaultNamespace picks the last-used namespace, then default, then the first one. It never looks at permissions, and it runs before the check comes back. namespaceSelectOptions then adds the current value back to the list so the select can display it, so the namespace the PR filtered out reappears at the top.

Master: the dropdown lists only aaa-view-ns
master: opens on aaa-view-ns, and it is the only option. This is the issue.
PR: the dropdown opens on aaa-view-ns and lists it above secrets-ns1 and secrets-ns2
PR: the right namespaces are now there, but it still opens on aaa-view-ns and lists it.
PR: clicking Create shows secrets is forbidden in the namespace aaa-view-ns
PR, first Create: forbidden. The user has to notice and switch.
PR plus fix: opens on secrets-ns1 and lists secrets-ns1 and secrets-ns2 only
PR + fix: opens on secrets-ns1, and Create worked when I submitted it.

Who hits it

Anyone whose default isn't creatable. I reproduced two of these, and the third is the same code path:

The setup script in the description can't show it, because view-only-ns sorts after secrets-ns1. Rename it aaa-view-ns and it appears.

The fix

When the check comes back, if the current value is still the default and isn't allowed, select the first allowed namespace. Any other value stays, so a clone or a namespace being typed isn't touched. If nothing is allowed, nothing moves, which matches master. A disabled select now skips the check, so a locked value (the Fleet AppCo create form) can't move. That also saves a request and a spinner on a select nobody can change.

Tested: 4 new unit test cases, and the "moves the default" test fails without the fix. The 3 touched suites pass (65 tests), lint is clean and there are no new type errors. In the browser, it clears the case for issue-8544-first and issue-8544-cm on Secret and chart install, and issue-8544-cm on Deployment. issue-8544, member, read-only and admin behave as before.

Fix diff (for you, not for the GitHub comment)
--- a/shell/components/form/NamespaceSelect.vue
+++ b/shell/components/form/NamespaceSelect.vue
@@ -95,7 +95,7 @@ const inStore = computed(() => store.getters['currentStore']());
 const nsSchema = computed(() => store.getters[`${ inStore.value }/schemaFor`](NAMESPACE));
 
 const typeToCheck = computed(() => {
-  if (!isCreate.value || props.override || props.options || props.forceNamespace || !props.resourceType) {
+  if (!isCreate.value || props.override || props.options || props.forceNamespace || props.disabled || !props.resourceType) {
     return null;
   }
 
@@ -118,6 +118,15 @@ watch(typeToCheck, async(type) => {
   if (type === typeToCheck.value) {
     namespacesAllowingCreate.value = allowed;
     loadingNamespacesAllowingCreate.value = false;
+
+    // defaultNamespace does not know about permissions, so it can be a namespace the user cannot create in
+    if (allowed && namespace.value === store.getters['defaultNamespace'] && !allowed.has(namespace.value as string)) {
+      const firstAllowed = realNamespaceOptions.value[0]?.value;
+
+      if (firstAllowed) {
+        emitChange(firstAllowed as string);
+      }
+    }
   }
 }, { immediate: true });
 
2

allowedNamespaces has no callers left

shell/store/index.js:140, :205, :473, pkg/harvester-manager/components/HarvesterServiceAddOnConfig.vue:73, shell/components/form/__tests__/NamespaceSelect.test.ts:230
Suggestion

This PR removes the last real use. What's left is the getter, getReadOnlyActiveNamespaces, the readonly param on getActiveNamespaces, a mapGetters entry nobody reads, and a fixture line in the PR's own new test. Removing them stops the old links.update rule from being reused by accident. GitHub search finds no other use in the rancher org or in harvester-ui-extension, but I can't see third-party extensions. A follow-up would be fine too.

3

New file under shell/utils

shell/utils/namespace-permissions.ts
Your call

Our checklist says no new files there, and this function has one caller, so it could live in NamespaceSelect.vue. That also drops the store: any. A fair answer from Cody would be that it mirrors shell/utils/project-permissions.ts, which came from your PR 18840, so skip this one if you don't feel strongly.

4

Real Harvester does answer checkPermissions

PR description, "Areas which could experience regressions"
FYI

The description says a Steve without the feature, "e.g. Harvester's", falls back to every namespace. Both our Harvesters, v1.8.0-rc5 and v1.9.0-rc4, return resourcePermissions, so the check will really run there.

5

The two red checks aren't from this PR

FYI
What each user sees

Opens on, then lists, on Create Secret (chart install gave the same answer wherever I ran it). "Create a new Namespace" is left out.

UsermasterPRPR + fix
issue-8544
the description's setup: custom role on one project, read-only on another
opens secrets-ns1
lists only that
opens secrets-ns1
lists ns1, ns2
same as PR
issue-8544-first
same, but the read-only namespace sorts first
opens aaa-view-ns
lists only that
opens aaa-view-ns
lists it, ns1, ns2
Create: forbidden
opens secrets-ns1
lists ns1, ns2
Create: saved
issue-8544-cm
Cluster Member, View All Projects, member of one project
opens default
lists it, ns1, ns2
same as masteropens secrets-ns1
lists ns1, ns2
issue-8544-base
User-Base, same bindings as issue-8544
opens secrets-ns1
lists only that
opens secrets-ns1
lists ns1, ns2
not rerun
issue-8544-member
Project Member on one, read-only on another. Also ConfigMap and Deployment.
opens secrets-ns1
lists ns1, ns2
same as mastersame as master (ConfigMap not rerun)
issue-8544-ro
read-only everywhere, can't create anywhere
opens view-only-ns
lists only that
same as mastersame as master
adminopens default
every namespace
same as mastersame as master (identical list, 109 on all 3 builds)
The description's test steps, on the preview
StepResult
Secrets, Create, Opaque lists secrets-ns1 and secrets-ns2, not view-only-nsTrue with this fixture. Card 1 is the exception.
On master it lists only the pre-selected defaultTrue
Chart install shows the same two namespacesTrue, with rancher-alerting-drivers
As admin, create forms still list every namespaceTrue, same list as master
Edit is unchangedTrue. Editing a ConfigMap as a member: select locked, no check sent.
If the check fails, every namespace is listedTrue. With the check forced to 500, view-only-ns is back.
A Steve without checkPermissions (e.g. Harvester's) falls backThe fallback is unit tested, but v1.8 and v1.9 Harvester aren't that case. See card 4.
What else I checked
CheckResult
Is the backend answer true?6 users and 3 types on local, each namespace compared with a real dry-run create: 0 mismatches. A restricted user on created-dashboard-2: 47 namespaces and 2 types, check says none, all really 403. As admin, all 6 active clusters I tried return resourcePermissions.
Does include=metadata.name drop the permissions?No. Steve writes resourcePermissions after applying include, confirmed in its source and live.
Could a namespace with no rights trigger the "unsupported" fallback?No. Steve sends an empty {}, not a missing key. No namespace came back without it.
Top-nav filter, issue 6094Project, single namespace and Only User Namespaces, with the filter confirmed applied each time: master, PR and PR + fix give the same answers.
Does value.type suit every form?Scripted sweep of the namespace props on every NameNsDescription call in shell and pkg, and in the 33 Harvester extension files. I found no form that passes something other than a resource, sets namespaced=false or gives its own options, apart from chart install, which the PR handles. In the browser I confirmed Secret, ConfigMap, Deployment and chart install.
Loading stateAbout 130 to 350 ms on mf, longest with 165 namespaces. Select disabled, spinner, Create stays disabled. The spinner overlaps the placeholder text, which is cosmetic and not worth raising.
A check that never answersThe select stays locked with a spinner (seen at 8 s, forced). Not worth raising, since it takes a request that never returns.
ConventionsTests use shallowMount and toStrictEqual. No new strings, v-html or ticket references in code. Commit message is fine, and the issue is linked with a milestone.
Suggested review (Comment)

Your usual shape: this intro as the body, the rest as inline comments, no code and no preview link. If you want the screenshots from card 1 on comment 1, they still need uploading. Comments 2 and 3 are optional.

Body

This works well. I tried it with several roles on local and on a downstream cluster, and the list matched what the server allows every time. One thing left on the preselected namespace, plus two small cleanups. Also, Harvester v1.8 and v1.9 do answer checkPermissions, so the note about Harvester falling back could be updated.

1. shell/components/form/NamespaceSelect.vue, line 119

The list is right now, but the namespace the form opens on still isn't checked. defaultNamespace doesn't know about permissions, and namespaceSelectOptions adds the current value back to the list. So a user whose first namespace is read-only opens the form on it, sees it listed, and gets "secrets is forbidden" on Create. A Cluster Member with View All Projects gets the same with default. The setup script doesn't show it because view-only-ns sorts after secrets-ns1; naming it aaa-view-ns does.

Could we move the selection to the first allowed namespace here when the current value is still the default? Other values can stay as they are, so a clone or a typed new namespace isn't touched. A disabled select (Fleet AppCo create) could skip the check too, so its value never moves.

2. shell/components/form/__tests__/NamespaceSelect.test.ts, line 230 (optional)

This was the last real use of allowedNamespaces. The getter, getReadOnlyActiveNamespaces and the readonly param in shell/store/index.js are unused now, as are the mapGetters entry in HarvesterServiceAddOnConfig.vue and this fixture line. Can we remove them here so the old links.update rule doesn't stay around?

3. shell/utils/namespace-permissions.ts, line 16 (optional)

We try not to add new files under shell/utils. This has one caller, so could it live in NamespaceSelect.vue?