| Status | Proposed |
| Date | 2026-10-13 |
| Issue | https://github.com/baserow/baserow/issues/1532 |
| Author | Peter Evans (@picklepete) |
| Contributors | Davide Silvestri (@silvestrid) |
A role given to an admin on a database, table or view lowers their access on it, down to “No access”, and admins have locked themselves and each other out by accident. From now on the Admin role a member holds themselves can’t be lowered at the level they hold it or anywhere below it: a workspace Admin keeps full access to the whole workspace, and an Admin of a database to everything in it. A role held through a team never gives that, and nobody can change their own role through the API.
Enterprise RBAC resolves a member’s role per scope: the workspace, an application, a table or a view. A member’s workspace role is stored on their membership and an agent’s on the agent; every other role is a role assignment of a user, an agent or a team on one scope. On one scope the member’s own role wins over the roles of their teams, and the stored workspace role wins over a team’s role on the workspace unless it is “No role”. Across scopes the most specific one wins, and the Admin role gets no special treatment: “No access” on a table replaces Admin on its database, for a workspace admin as for anyone else.
That symmetry was a deliberate choice when RBAC shipped in 2022 (#1474). In practice it hurts. The role picker preselects “No access”, so adding a colleague or a team to a table without touching the dropdown locks them out, admins included (#4345, folded into #6012). An admin locked out of a database can’t see it, can’t open its share dialog and can’t undo the assignment.
In scope: how roles resolve for admins, what the share dialog shows for them, and who can change whose role. Out of scope: the picker’s default role and the confirmation before “No access” (#6012), the access summary of a scope (#6012), which roles a team may hold, and privacy inside a table, which field permissions and personal views keep providing.
| Term | Meaning |
|---|---|
| Scope | The workspace, an application, a table or a view; a role is held on exactly one of them. |
| Level | The position of a scope in the hierarchy: workspace, then application, then table, then view. |
| Direct role | A role the member holds themselves: their stored workspace role, or a role assignment with the member as subject. |
| Team role | A role assignment with a team as subject. It reaches the member through their membership of the team. |
| Admin precedence | The rule that a direct Admin role can’t be lowered at the level it is held or inside it. |
| Inert assignment | A stored role assignment the precedence rule ignores. It stays in the database and has no effect. |
| Self assignment | A request in which the user changing a role is also the subject of that role. |
Two companion files list every combination of roles on the workspace, an application and a child of it, resolved by the role handler itself: the role matrix before ADR 010 shows the result in production today, and the role matrix after ADR 010 adds the result under this record. In short:
An admin of the workspace, by the role stored on their membership or on the agent, is an admin on everything in the workspace. An admin of an application, a table or a view, by a role assignment of their own, is an admin on that scope and on everything inside it. Any role on a descendant scope, directly assigned or through a team, is ignored for them. To restrict such an admin, lower the Admin role that makes them one.
A team’s Admin role on an application or a table applies on that scope like any other team role, and a role on a scope inside still wins there. On one scope the member’s own role keeps winning over the roles of their teams, whatever they are, so a direct Viewer next to a team’s Admin stays a Viewer.
A team’s Admin role on the workspace keeps doing what it does today: a member or an agent whose stored role is “No role” takes it and resolves to Admin on the workspace, but without the precedence, so a role on a scope inside still wins for them. Whether a team should be able to hold Admin on the workspace at all is left for a later decision.
RoleAssignmentHandler.apply_admin_precedence rewrites the ordered per scope role list
when it is built, given the scopes on which the actor holds Admin directly, so permission
checks, the permissions object sent to the frontend, queryset filtering and the field
permissions manager all see the same result. The role permission manager additionally
builds the permissions object and the queryset filters of a workspace admin from the
Admin role’s own operation set, so an operation the role doesn’t hold, for instance one
not yet synced into a long lived instance, stays denied. Permission checks keep walking
the scope list: that walk is what denies a context from another workspace, so no admin
shortcut may skip it.
No data migration. An assignment the rules ignore stays stored, so demoting the admin later makes it effective again, and nothing has to tell an accidental assignment from a deliberate one. The share dialog of an object shows such users and agents with a locked Admin role and an explanation naming the level that makes them an admin, offers removal only, and lists them in its picker with that reason instead of offering them.
Both the workspace user endpoint and the role assignment endpoints reject a self assignment at every scope, workspace included. The UI already rendered the user’s own row read-only; the API now matches it. Another admin has to change an admin’s role, and the last admin check still applies to leaving a workspace.
A direct “No access” on a workspace admin is stored like any other assignment and has no effect. Rejecting it would give one rule two behaviours, since a team assignment can’t be rejected for the admins it happens to contain, and the UI never sends it.
| Option | Why not |
|---|---|
| Keep the most specific role and send admins placeholders | Admins stay locked out of their own objects; placeholders only solve the ordering endpoints, which was the original scope of #1532. |
| Prevent it in the UI only | Teams, other admins and the API still lock admins out, and the existing assignments stay effective. |
| Delete the stale assignments in a migration | Loses the intent behind them, can’t tell accidental from deliberate, and demotion wouldn’t restore them. |
| Reject direct lower roles on admins at the API | Can’t apply to teams, so the same rule would behave two ways. The unused ERROR_CANT_ASSIGN_ROLE_EXCEPTION_TO_ADMIN dates from this idea. |
| Let a team’s Admin role make admins too | Team membership isn’t known where roles are set, so such admins could neither be shown nor locked there, and a member’s effective role would differ from the stored one. |
| Make instance staff a workspace bypass | A different concern. is_staff only unlocks instance settings and must keep doing only that. |