Authorization and Sharing
By the Syncfusion DevTutorials team · Last updated
Decide who inside a tenant may read, edit or delete each record: role-based access (RBAC), record-level sharing, private records and row-level security, checked in a fixed order with deny by default.
Who May Do What, Inside One Tenant?
Lesson 2 kept tenants apart. But inside Acme Retail there are agents, team leads, an admin and a contractor. The contractor may only read the tickets someone shared with her. A billing agent must not edit a support ticket. An HR complaint must be private to its owner.
A support team lead at Acme reports a problem: "I can edit Billing's tickets. I don't think I should be able to." She is right. One rule shipped with the wrong scope.
The main idea: check access in a fixed order: tenant boundary, record rules, then role-and-scope rules and sharing. Deny by default. Test the rules with scenarios, because an over-permissive rule never causes an error; it only shows data it should not.
TL;DR
- RBAC (role-based access control) gives each role permissions, such as "agents may edit".
- A scope says which records: own, team, shared with you, or the whole tenant.
- Record-level rules can only narrow (private records) or widen for one record (sharing).
- Deny by default: if no rule allows an action, the answer is no.
- Row-level security can enforce the same scopes inside the database, so list queries cannot leak.
- Write access scenarios ("lee edits T1: deny") and run them in CI. Over-permissive rules are silent bugs.
The order of checks. A no at the tenant boundary or a record rule is final. Roles and sharing can only add access.
A Real-World Example
A project tool lets admins create custom roles. A customer creates "Project Viewer" by copying "Project Manager" and unticking a few boxes. A permission added in a later release, "export project", is ticked by default on every copied role. For a year, viewers can export every project.
No test failed, because no test ever asked "can a viewer export?". The fix was a list of access scenarios, run on every release.
What You Will Learn
- Roles and scopes.
- Record-level sharing and private records.
- The order of checks, and deny by default.
- Testing rules with scenarios.
- Enforcing scopes in the database.
Roles and Scopes
A role alone is too coarse. "Agents may edit tickets" does not say which tickets. Pair every permission with a scope.
Sharing and Private Records
Sharing widens access for one record: "share T4 with raj and vic". Private narrows it: "only the owner and admins see T3", whatever the roles say.
The Order of Checks
Every decision runs the same steps, in the same order:
- Tenant boundary. Different tenant: deny. Nothing below can override this.
- Record rules. Private and not the owner or an admin: deny.
- Role and scope. Does the user's role allow this action on records in this scope?
- Sharing. Was this record shared with this user?
- Nothing allowed it: deny.
Never write "allow" rules that can override step 1 or 2. A support tool that lets "super agents" read across tenants has quietly removed the tenant boundary for every super agent, including compromised ones.
Test With Scenarios
An over-permissive rule never throws an error. The only way to catch it is to state what should happen and check.
Try It: Find the Over-Permissive Rule
The lab has the rules Northwind shipped for Acme Retail and 30 access checks. Two checks give the wrong answer. Find the rule, fix its scope, and get all 30 right without blocking anyone from their own work.
Pass rule: 30 of 30 checks give the expected answer.
Keep scenarios as data (user, action, record, expected) in your test suite, and add one every time a customer reports a permission bug. The suite becomes a written-down policy that cannot drift.
Enforce Scopes in the Database Too
The evaluator answers "may lee edit T1?". List pages ask a different question: "which tickets may lee see?". If the app fetches every ticket and filters in code, one forgotten filter leaks. Row-level security can apply the scope inside the query:
A row passes the policy only if all of these are true:
| Check | Passes when |
|---|---|
| Tenant | the row's tenant_id is the request's tenant |
| Scope | the user is an admin, or the row's team is one of the user's teams, or the row is shared with the user |
| Private | the row is not private, or the user owns it, or the user is an admin |
The app sets app.user_id, app.role and app.teams for each request, exactly like app.tenant_id in Lesson 2.
The same rule as a list filter, applied to every row before any row leaves the database:
For very large tenants with deep team hierarchies and many sharing rules, teams often precompute "who can see what" into an access table or use a dedicated authorization service built on relationship tuples (the "Zanzibar" model behind tools like OpenFGA and SpiceDB). The order of checks stays the same.
Check Yourself
Common mistakes
| Mistake | Wrong | Correct | Why it happens |
|---|---|---|---|
| Tenant-wide scope by accident | team-lead edit: tenant | team-lead edit: team | Wide scopes are the easy default when shipping |
| Allow by default | "No rule says no, so yes" | No rule says yes, so no | New actions arrive without rules |
| Private records checked last | A role rule allows before the private check runs | Private check before roles | Order of checks was never written down |
| UI-only filtering | API returns all tickets; the UI hides some | Filter in the API and the database | The API is called directly by scripts and integrations |
| No scenarios | Permissions tested by hand | Scenario table in CI | Over-permissive rules never fail a test |
Practice
What to Remember
- Roles say what; scopes say which records.
- Check in order: tenant, record rules, roles and sharing, then deny.
- Private records narrow access; sharing widens it for one record.
- Over-permissive rules fail silently. Test with written scenarios.
- Apply scopes to list queries too, ideally in the database.
Further Reading
- OWASP: Authorization Cheat Sheet: deny by default, least privilege, checks on every request.
- PostgreSQL: Row Security Policies: policies that use session settings.
- Zanzibar: Google's consistent, global authorization system (paper): relationship-based access control at scale.
Next: Enterprise Identity: SSO and SCIM. Customers want their staff to sign in with the company account, and to lose access the moment they leave.