Skip to content

[feat] Role and Permission system#2737

Open
loop-index wants to merge 8 commits intoscpwiki:developfrom
loop-index:role-system
Open

[feat] Role and Permission system#2737
loop-index wants to merge 8 commits intoscpwiki:developfrom
loop-index:role-system

Conversation

@loop-index
Copy link

Implemented a simple RBAC-style role/permission system. Took some heavy inspiration from Discord's role system.

  • Permissions are resource:action pairs (ie, page:edit) shared across platform (assuming all sites have the same resource types and actions)
  • Roles are scoped to site. Site comes with several default roles on creation.
  • User can have multiple roles across multiple sites.
  • Permission check: Does [user] have any [role] with [permission] to [act] on [resource]
  • Implicit roles: Roles that are granted conditionally by the system and not persisted. Example: Guest role granted when user visit a site without membership, or Author role granted when user act on a page that they authored. Idea is to allow customization of permissions for these roles per-site.

Planned improvements:

  • Role management UI
  • Temporary role grants with expiration. Might be useful for temp bans, temp edit grant for 001 proposals, etc.
  • Role hierarchy, where lower-ranking roles cannot affect/grant higher-ranking roles
  • Caching to improve role/permission lookup
  • Per page category permissions like Wikidot. Have not thought too much about this - one idea is to expand the permission schema to allow for subtypes (ie page-component:edit), but then permissions would need to be scoped per-site.

As a proof-of-concept, I've added a permission check for page edits. Currently only the admin account is allowed to edit pages.

@codecov
Copy link

codecov bot commented Mar 7, 2026

Codecov Report

❌ Patch coverage is 0% with 418 lines in your changes missing coverage. Please review.
✅ Project coverage is 2.18%. Comparing base (dfc6c81) to head (3ec2868).
⚠️ Report is 21 commits behind head on develop.

Files with missing lines Patch % Lines
deepwell/src/services/role/service.rs 0.00% 158 Missing ⚠️
deepwell/src/services/permission/service.rs 0.00% 119 Missing ⚠️
deepwell/src/database/seeder/mod.rs 0.00% 46 Missing ⚠️
deepwell/src/services/audit/structs.rs 0.00% 39 Missing ⚠️
deepwell/src/error/error_type.rs 0.00% 14 Missing ⚠️
deepwell/src/models/role.rs 0.00% 10 Missing ⚠️
deepwell/src/endpoints/page.rs 0.00% 7 Missing ⚠️
deepwell/src/models/permission.rs 0.00% 6 Missing ⚠️
deepwell/src/models/role_permission.rs 0.00% 4 Missing ⚠️
deepwell/src/models/site.rs 0.00% 4 Missing ⚠️
... and 3 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           develop   #2737      +/-   ##
==========================================
- Coverage     2.29%   2.18%   -0.11%     
==========================================
  Files          171     176       +5     
  Lines        10287   10799     +512     
==========================================
  Hits           236     236              
- Misses       10051   10563     +512     
Files with missing lines Coverage Δ
deepwell/src/api.rs 0.00% <ø> (ø)
deepwell/src/services/page/structs.rs 0.00% <ø> (ø)
deepwell/src/database/seeder/data.rs 0.00% <0.00%> (ø)
deepwell/src/models/role_permission.rs 0.00% <0.00%> (ø)
deepwell/src/models/site.rs 0.00% <0.00%> (ø)
deepwell/src/models/user_role.rs 0.00% <0.00%> (ø)
deepwell/src/services/page/service.rs 0.00% <0.00%> (ø)
deepwell/src/models/permission.rs 0.00% <0.00%> (ø)
deepwell/src/endpoints/page.rs 0.00% <0.00%> (ø)
deepwell/src/models/role.rs 0.00% <0.00%> (ø)
... and 5 more

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@emmiegit
Copy link
Member

emmiegit commented Mar 7, 2026

Just a small note, temporary bans are implemented - this is done by adding a metadata property to the ban noting its expiration date.

action TEXT NOT NULL,

UNIQUE (resource_type, action)
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the idea here is that permissions like page:read and page:edit will be in this table? I suppose that actions would be implemented in code (e.g. edit) vs the whole permission?

I suppose the reason for that would be that some permissions may be more finely-tuned, like for page categories?

Also if it is platform-wide, keep in mind that permission descriptions need to be localizable, which wouldn't really work with a single description column. (This would be fine if they were per-site, since then it would site admins setting the description, which would then be localized/customized to their site's audience.)

Additionally, if we want to have the concepts of platform permissions and custom site permissions, we can add a nullable site_id column (NULL = platform, otherwise = for that site). Though I'm undecided about this given the localization issue.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair concern - putting page category aside, I think one way to solve the localization problem is using the [resource:action] pair as the key to look up in the locale file. Then we don't have to bake the description inside the entry. That would still not work with the custom site permissions though.

I suppose that actions would be implemented in code (e.g. edit) vs the whole permission?

I think for most actions, just calling a lookup to see if user has permission for a resource would suffice. Checks needing additional logic can have a wrapper over the lookup function to allow for more granular checking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To explore this a bit more, what exactly do you have in mind for custom site permissions? Any illustrative examples?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only use case that I can think of right now is per PageCategory permissions - since each site may have a different list of page categories, the permissions will also have to be scoped per site like how Wikidot currently does it.

implicit BOOLEAN NOT NULL DEFAULT false,
-- Virtual roles are visible to admins where they can select the role's permissions.
-- Virtual roles cannot be manually assigned.
is_virtual BOOLEAN NOT NULL DEFAULT false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design question:

Would it be better for virtual roles to be in the database (and automatically added by jobs or code logic)? This would mean we can just query roles and get all of them.

Or should virtual roles be added automatically in the future RoleService when querying for a user? This way they're always up-to-date but it requires that callers go through this code path to ensure they're getting both all the real roles and the virtual ones.

I'm leaning more towards the latter but I'm curious as to your thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants