Describe the bug
The migration create and edit forms build themselves from the CRD object, exactly as the backup forms in #4053 do, and no role in the tree grants read access to the two CRDs they ask for. The forms fail with a 403 for every user who is not a cluster admin.
MigrationSourceCreatePage.tsx, MigrationSourceEditPage.tsx and MigrationTaskCreatePage.tsx all call useCRDSchema, which issues a by-name get against apiextensions.k8s.io at cluster scope with the logged-in user's own token. The names they ask for are vmimportsources.forklift.cozystack.io and vmimporttasks.forklift.cozystack.io.
Nothing grants those. cozy:migration:view grants the forklift.cozystack.io resources themselves, which is a different API group from apiextensions.k8s.io, and a repo-wide search for either CRD name under packages/ returns no rule naming them.
Why this is filed separately from #4053
#4236 fixes the backup half by adding the five backup CRD names to cozystack-dashboard-readonly in packages/system/dashboard/templates/rbac.yaml, which is bound to system:authenticated by a ClusterRoleBinding in the same file. That is the carrier that works, because a cluster-scoped request is authorized against ClusterRoleBindings and every tenant role in this repository is bound by a namespaced RoleBinding instead.
The migration forms were outside that PR's scope and still have no grant.
Expected behaviour
A user who can already create a migration source or task can open the form for it.
Suggested fix
Add the two names to the same rule #4236 introduces. It already carries resourceNames and stops at get, which is the right verb: the hook does a by-name get, and a rule carrying resourceNames cannot authorize a list at all.
The same tradeoff applies as for the backup names: that role reaches every authenticated user rather than the members of one tenant. That is the standing shape for this role, which already holds storageclasses, applicationdefinitions and backupclasses on the same terms.
Worth knowing before implementing
Do not reach for a ClusterRole labelled rbac.cozystack.io/aggregate-to-tenant-view. That is what #4053 originally proposed and what the first revision of #4236 implemented, and it cannot work: the rule renders, aggregates correctly, and never reaches the authorizer, because cluster-scoped requests are not authorized through a RoleBinding.
A render test over the template would pass either way, since the defect is in the binding rather than the render. packages/system/dashboard/tests/cluster_usage_rbac_test.yaml is the same-package precedent that pins a cluster-scoped grant together with its binding's subject, and an assertion in that shape would catch it.
Describe the bug
The migration create and edit forms build themselves from the CRD object, exactly as the backup forms in #4053 do, and no role in the tree grants read access to the two CRDs they ask for. The forms fail with a 403 for every user who is not a cluster admin.
MigrationSourceCreatePage.tsx,MigrationSourceEditPage.tsxandMigrationTaskCreatePage.tsxall calluseCRDSchema, which issues a by-namegetagainstapiextensions.k8s.ioat cluster scope with the logged-in user's own token. The names they ask for arevmimportsources.forklift.cozystack.ioandvmimporttasks.forklift.cozystack.io.Nothing grants those.
cozy:migration:viewgrants theforklift.cozystack.ioresources themselves, which is a different API group fromapiextensions.k8s.io, and a repo-wide search for either CRD name underpackages/returns no rule naming them.Why this is filed separately from #4053
#4236 fixes the backup half by adding the five backup CRD names to
cozystack-dashboard-readonlyinpackages/system/dashboard/templates/rbac.yaml, which is bound tosystem:authenticatedby a ClusterRoleBinding in the same file. That is the carrier that works, because a cluster-scoped request is authorized against ClusterRoleBindings and every tenant role in this repository is bound by a namespaced RoleBinding instead.The migration forms were outside that PR's scope and still have no grant.
Expected behaviour
A user who can already create a migration source or task can open the form for it.
Suggested fix
Add the two names to the same rule #4236 introduces. It already carries
resourceNamesand stops atget, which is the right verb: the hook does a by-name get, and a rule carryingresourceNamescannot authorize a list at all.The same tradeoff applies as for the backup names: that role reaches every authenticated user rather than the members of one tenant. That is the standing shape for this role, which already holds
storageclasses,applicationdefinitionsandbackupclasseson the same terms.Worth knowing before implementing
Do not reach for a ClusterRole labelled
rbac.cozystack.io/aggregate-to-tenant-view. That is what #4053 originally proposed and what the first revision of #4236 implemented, and it cannot work: the rule renders, aggregates correctly, and never reaches the authorizer, because cluster-scoped requests are not authorized through a RoleBinding.A render test over the template would pass either way, since the defect is in the binding rather than the render.
packages/system/dashboard/tests/cluster_usage_rbac_test.yamlis the same-package precedent that pins a cluster-scoped grant together with its binding's subject, and an assertion in that shape would catch it.