Summary
The frontend recomputes permission decisions (for example project creation) from scopes, org flags, and team roles. That logic drifts from the API. A server-computed _capabilities map on the org payload would let the UI read one field instead of reimplementing access rules.
Current behavior
- Org serializers expose
access as a flat scope list when access=request.access is passed (src/sentry/api/serializers/models/organization.py).
- They also expose policy flags such as
allowMemberProjectCreation (org setting, not "this caller can create").
- Projects/teams already return computed
hasAccess + per-object access.
- UI still stacks scopes + flags + team admin locally, e.g.
static/app/components/projects/canCreateProject.tsx via useCanCreateProject.
- Project create API rules live separately:
- Org create:
POST scopes project:read|write|admin, then deny when member creation is disabled unless org:write.
- Team create:
POST scopes project:write|admin, with a per-team team:admin escape when member creation is disabled.
Gap
- UI and API permission models already diverge (scopes checked,
org:write bypass, team-admin path).
- Every new org-level action risks another client-side recreation of server rules.
access is the wrong place to extend: it is a public scope array, not a capability map.
Related work this would improve
Proposed shape (discussion)
Add a private sibling field on the detailed/org session serializer (not inside access, not on the user endpoint):
{
"access": ["org:read", "project:read", "team:read"],
"allowMemberProjectCreation": true,
"_capabilities": {
"canCreateProject": true
}
}
Notes:
- Leading underscore marks it as UI/internal; also exclude from public OpenAPI if present on a documented endpoint.
- Compute with the same rules as the create endpoints so UI gating cannot drift.
- Start with
canCreateProject; grow the map only when another UI gate is re-encoding server policy.
- Prefer the detailed org bootstrap payload the frontend already stores over the bare public summary.
Requested by David Cramer.
--
View Junior Session [Sentry]
Summary
The frontend recomputes permission decisions (for example project creation) from scopes, org flags, and team roles. That logic drifts from the API. A server-computed
_capabilitiesmap on the org payload would let the UI read one field instead of reimplementing access rules.Current behavior
accessas a flat scope list whenaccess=request.accessis passed (src/sentry/api/serializers/models/organization.py).allowMemberProjectCreation(org setting, not "this caller can create").hasAccess+ per-objectaccess.static/app/components/projects/canCreateProject.tsxviauseCanCreateProject.POSTscopesproject:read|write|admin, then deny when member creation is disabled unlessorg:write.POSTscopesproject:write|admin, with a per-teamteam:adminescape when member creation is disabled.Gap
org:writebypass, team-admin path).accessis the wrong place to extend: it is a public scope array, not a capability map.Related work this would improve
organizations:team-rolesfor project create; default-allow member project creation for new orgsteam-rolesgate fromcanCreateProjectand the membership settings toggleProposed shape (discussion)
Add a private sibling field on the detailed/org session serializer (not inside
access, not on the user endpoint):{ "access": ["org:read", "project:read", "team:read"], "allowMemberProjectCreation": true, "_capabilities": { "canCreateProject": true } }Notes:
canCreateProject; grow the map only when another UI gate is re-encoding server policy.Requested by David Cramer.
--
View Junior Session [Sentry]