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

  1. Roles and scopes.
  2. Record-level sharing and private records.
  3. The order of checks, and deny by default.
  4. Testing rules with scenarios.
  5. 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:

  1. Tenant boundary. Different tenant: deny. Nothing below can override this.
  2. Record rules. Private and not the owner or an admin: deny.
  3. Role and scope. Does the user's role allow this action on records in this scope?
  4. Sharing. Was this record shared with this user?
  5. 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:

CheckPasses when
Tenantthe row's tenant_id is the request's tenant
Scopethe user is an admin, or the row's team is one of the user's teams, or the row is shared with the user
Privatethe 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

MistakeWrongCorrectWhy it happens
Tenant-wide scope by accidentteam-lead edit: tenantteam-lead edit: teamWide scopes are the easy default when shipping
Allow by default"No rule says no, so yes"No rule says yes, so noNew actions arrive without rules
Private records checked lastA role rule allows before the private check runsPrivate check before rolesOrder of checks was never written down
UI-only filteringAPI returns all tickets; the UI hides someFilter in the API and the databaseThe API is called directly by scripts and integrations
No scenariosPermissions tested by handScenario table in CIOver-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

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.