Problem
Granting a new member access to a project means ticking every service by hand. A project with twelve services costs fourteen clicks, and every service added afterwards needs another visit to Settings → Users → Add Permissions.
The dialog already cascades up on check (ticking a service auto-ticks its environment and project) and down on uncheck (unticking a project clears its children), but never down on check — so ticking a project leaves all of its services unticked.
Proposal
Let a ticked project or environment grant everything beneath it, including services created later.
No new columns. The three existing member arrays get one sharpened rule:
| Column |
Today |
Proposed |
accesedProjects |
project is visible |
full access: every environment and service in it, now and in future |
accessedEnvironments |
environment is visible |
full access: every service in it, now and in future |
accesedServices |
exactly these services |
unchanged — explicit grants |
Project/environment visibility then becomes derived: visible if held outright, or it contains a held environment, or it contains an explicitly granted service.
The catch, and why a migration is needed
Because the dialog auto-adds the parent project whenever a single service is ticked, most existing accesedProjects entries mean "some services here", not "all of them". Reinterpreting them as-is would silently widen access for every existing member.
So a data migration drops any project or environment a member does not already cover service-by-service. Derived visibility is what stops that from being a revocation — the project stays reachable through the services they still hold.
The invariant: net access change is exactly zero. Nobody reaches a service after the migration that they could not before, and nobody loses one.
Two consequences worth agreeing on before I open a PR
- A project a member fully covers today would silently acquire services added tomorrow. That is the feature working as intended, but it does change the security posture of existing grants, so it probably belongs in release notes rather than just a PR body.
- Project-level authority derives from a single child grant. A member holding one service in project P would pass the project-visibility check, so with
project:delete they could remove a project containing services they cannot see. This is already how it behaves today (the auto-added parent does the same), and preserving it is what keeps the migration zero-net-change — but it is the sharpest edge in the model and I would rather raise it than have a reviewer discover it.
Happy to contribute this
I have a working implementation against canary and can open a PR. It keeps enforcement centralised (the two existing service-permission functions cover all 234 call sites, so no router needs to change its checks), adds a single resolver query, and moves the dialog's cascade rules into a pure, unit-tested module.
Before I do — does this direction look right to you, and is the migration approach acceptable? Happy to adjust the semantics if you would rather see this expressed differently (for example an explicit opt-in flag per project instead of redefining the existing column).
Problem
Granting a new member access to a project means ticking every service by hand. A project with twelve services costs fourteen clicks, and every service added afterwards needs another visit to Settings → Users → Add Permissions.
The dialog already cascades up on check (ticking a service auto-ticks its environment and project) and down on uncheck (unticking a project clears its children), but never down on check — so ticking a project leaves all of its services unticked.
Proposal
Let a ticked project or environment grant everything beneath it, including services created later.
No new columns. The three existing
memberarrays get one sharpened rule:accesedProjectsaccessedEnvironmentsaccesedServicesProject/environment visibility then becomes derived: visible if held outright, or it contains a held environment, or it contains an explicitly granted service.
The catch, and why a migration is needed
Because the dialog auto-adds the parent project whenever a single service is ticked, most existing
accesedProjectsentries mean "some services here", not "all of them". Reinterpreting them as-is would silently widen access for every existing member.So a data migration drops any project or environment a member does not already cover service-by-service. Derived visibility is what stops that from being a revocation — the project stays reachable through the services they still hold.
The invariant: net access change is exactly zero. Nobody reaches a service after the migration that they could not before, and nobody loses one.
Two consequences worth agreeing on before I open a PR
project:deletethey could remove a project containing services they cannot see. This is already how it behaves today (the auto-added parent does the same), and preserving it is what keeps the migration zero-net-change — but it is the sharpest edge in the model and I would rather raise it than have a reviewer discover it.Happy to contribute this
I have a working implementation against
canaryand can open a PR. It keeps enforcement centralised (the two existing service-permission functions cover all 234 call sites, so no router needs to change its checks), adds a single resolver query, and moves the dialog's cascade rules into a pure, unit-tested module.Before I do — does this direction look right to you, and is the migration approach acceptable? Happy to adjust the semantics if you would rather see this expressed differently (for example an explicit opt-in flag per project instead of redefining the existing column).