How I designed permissions people can actually understand (Node + Prisma)
Description and use
Implemented a transparent, department‑based permission system that automatically updates when users move between departments, displays access reasons, and ensures tenant isolation.
Problem
In many companies, it is unclear who can see a folder and why; access is granted person by person, inherited in ways forgotten, and never cleaned up.
Outcome
Implemented a transparent, department‑based permission system that automatically updates when users move between departments, provides clear reasons for access, and ensures tenant isolation.
Workflow
- Define permissions by department and role rather than individual users.
- Implement four permission levels (MANAGE, EDIT, COMMENT, VIEW) with highest level winning.
- Allow nodes to inherit parent permissions up to the space, with the option to stop inheritance for specific nodes.
- Create a single resolveAccess function that returns both permission level and reason for each grant.
- Resolve permission trees in batches, one query per level, to avoid N+1 queries.
- Cache results per request.
- Scope all queries to a tenant‑scoped Prisma client and test isolation with integration tests.
- Deploy SpiderFolio with Docker Compose and provide documentation.
Evidence
Access is granted person by person, inherited in ways nobody remembers, and never cleaned up.
I set one rule: every folder, file and page must show who has access and the reason, for example "Quality department · inherited from /Procedures".
Access is given to departments and roles. When someone moves from Production to Quality, their access changes on its own.
MANAGE > EDIT > COMMENT > VIEW. A person collects every non‑expired grant given to them and to their departments (including parent departments). If several apply, the highest level wins.
Folders, files and pages form one tree. By default a node inherits its parent's access up to the space. Any node can stop inheritance, so a "Payroll" folder inside "HR" can be locked down without touching the rest.
All of this lives in a single resolveAccess function backed by a table of test cases. Instead of returning only a level, it also returns the reason for each grant.
Every query goes through a tenant‑scoped Prisma client, and an integration test checks that a user from company A gets a 404 on every endpoint for company B's data.
- Record date: 2026-11-02T00:13:44.486Z