Permissions and roles
Overview
Section titled “Overview”Three roles control access to Formation. Every authenticated request carries one or more of them; the role determines what the user (or service) is allowed to do.
| Role | Who has it | What it grants |
|---|---|---|
| User | Every human end-user | Full read and write access to all primary data: addresses, schemes, developments, companies, investment events, occupier events, portfolios. |
| Admin | A subset of human users | Everything the User role grants, plus access to system settings, tag management, and market-boundary editing. |
| API.Access | Service principals (load tests, machine-to-machine integrations) | Full read and write access to primary data. Cannot access admin functions. |
Roles are assigned via Azure AD group membership and surfaced into the request as claims.
What each role can do
Section titled “What each role can do”- View every list page and detail page.
- Create, edit, and delete records they have access to (across all primary entities).
- Add, edit, and delete notes, tags applications, and external links on records.
- Apply existing tags to records.
- Run searches across every list.
- Export list pages to Excel.
Everything User can do, plus:
- Open Settings → Admin panels.
- Use the Tag Manager: create new tags, rename or delete existing tags, manage tag colours / categories.
- Create, edit, or delete Market Boundaries through the in-app editor. Non-admins can see boundaries on maps but can’t change them.
- Other admin-only settings panels that affect system behaviour (visibility may change as features are added).
The Tag Manager renders in read-only mode for non-Admin users: they can browse the tag list but the edit / add / delete buttons are hidden.
API.Access
Section titled “API.Access”Service principals (load tests, automation, future machine-to-machine integrations) get API.Access. This grants the same primary-data permissions as User: read and write across addresses, schemes, companies, events, portfolios. It explicitly does not grant Admin — service principals are deliberately excluded from administrative actions.
How roles are enforced
Section titled “How roles are enforced”Default policy
Section titled “Default policy”Every API endpoint requires authentication and one of the three roles. Anonymous requests get rejected by the auth layer before they reach any controller. A request authenticated as a member of an unrecognised group gets the same outcome — no access.
Admin gating
Section titled “Admin gating”Two server-side policies control role-restricted operations:
AdminOnly—RequireRole("Admin"). Used on endpoints that should reject service principals as well as ordinary users. Applied to tag management, market-boundary writes, and admin-only settings.RequireRole—RequireRole("User", "Admin", "API.Access"). Used on endpoints that any authenticated principal can hit.
The split exists because service principals get API.Access but not User — so a naive RequireRole("User", "Admin") would lock them out unintentionally. AdminOnly deliberately doesn’t include API.Access so admin-only endpoints stay human-only.
UI gating
Section titled “UI gating”Wherever the UI hides an action (e.g. the Tag Manager’s edit buttons), it’s a defensive measure on top of the server-side policy: the hidden action would also be rejected by the API if the user found a way to invoke it.
The UI never grants access — only the server does. UI gating is for tidiness, not security.
Where role gating shows up today
Section titled “Where role gating shows up today”| Area | Visible to | Editable by |
|---|---|---|
| Tag Manager (Settings → Tags) | Everyone | Admin only |
Market Boundary editor (/market-boundaries/[id]) | Everyone (view) | Admin only (edit) |
| Settings → Admin panels | Admin only | Admin only |
| All other list / detail / edit pages | User, Admin, API.Access | User, Admin, API.Access |
If a feature added later restricts certain entities to certain roles (e.g. “only Admins can edit verified companies”), it will be documented on the relevant entity page’s Permissions section.
Related rules
Section titled “Related rules”- Market Boundaries — boundary edits are admin-gated.
- Notes and tags — tag management vs tag application.
Where this lives
Section titled “Where this lives”- Policy registration:
src/services/api/app/app.api/Program.cs(around line 552-562) - Role transformation from Azure AD groups:
src/services/api/app/app.api/Services/GroupRoleClaimsTransformation.cs - UI role check:
src/services/web/src/lib/stores/roles.svelte.ts(hasRole('Admin')) - Settings / Tag Manager admin-gating consumers:
src/services/web/src/lib/components/system/Settings.svelte,TagManager.svelte