feat(rbac): enforce fine-grained console permissions - #310
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds fine-grained, server-side role-based access control (RBAC) to RustDesk Console.
It introduces:
The goal is to let organizations delegate specific administrative work without turning every operator into a full Console administrator.
Examples include:
All authorization decisions are enforced by the backend. Frontend menu visibility and disabled controls are treated only as user-experience helpers.
Paired frontend PR: databk/rustdesk-console-web#281
Motivation
Before this change, Console authorization was primarily based on whether a user was an administrator, with additional checks distributed across individual features.
That model could not safely express requirements such as:
Authentication and authorization are separate concerns. A valid login proves who the user is, but it does not prove that the user may perform every Console operation.
This PR therefore adds an explicit, deny-by-default authorization model and applies it consistently to relevant requests.
Identity model
The backend recognizes three identity states.
System owner / super administrator
The original system owner remains the unique super administrator.
The system owner:
If an existing database contains more than one owner account, startup fails explicitly instead of silently selecting or modifying an owner.
Ordinary user
A user without any assigned RBAC role is treated as an ordinary user.
An ordinary user retains only the existing personal address-book basic functionality. This basic capability is not stored as a role permission and cannot be removed through role editing.
RBAC user
A non-owner user may receive one or more persisted custom roles.
Effective authorization is calculated from the union of the assigned role grants, subject to scope and protection rules. Assigning a real RBAC role replaces the UI representation of the virtual ordinary-user identity; the virtual identity itself is never stored as a database role.
Permission catalog
The backend permission catalog is the source of truth for:
The catalog covers the following resource areas.
The following capabilities are intentionally not assignable through ordinary roles:
They remain visible to clients as owner-only capabilities so the UI can explain the boundary instead of presenting them as missing or unknown permissions.
Permission dependencies
Permissions may declare prerequisites.
For example, a write operation generally requires the corresponding view permission. The backend validates these dependencies when a role is created or updated.
This prevents unusable or misleading roles such as being allowed to edit a resource that the same user is not permitted to retrieve.
Persisted roles are also filtered defensively when effective permissions are calculated. If stored data is incomplete or damaged, missing prerequisite permissions fail closed instead of granting the dependent operation.
Strategy assignment is an intentional exception: a user may receive
strategies.assignwithout fullstrategies.view. In that case, the user can access only the minimal strategy candidate projection needed for assignment.Scope model
Permissions support two scope modes.
Global scope
A global grant applies to all resources covered by that permission.
Example:
The user may edit every device they are otherwise allowed to access.
Selected device-group scope
A scoped grant applies only to explicitly selected device groups.
Example:
The user may operate on Finance devices but cannot use the same actions on Sales or Engineering devices.
Scope checks are applied to:
When the same permission is granted globally and through selected device groups, the global grant wins because it already covers the narrower grants.
Client-supplied user IDs, role IDs, device IDs, or device-group IDs are never trusted as proof of authorization.
Request authorization flow
Relevant backend requests follow this authorization sequence:
This design keeps authentication, function authorization, object authorization, and target protection as separate checks.
A missing permission check must not turn into an implicit allow. Protected routes use centralized metadata and guards, while service-level checks enforce object, scope, and write-boundary constraints.
Role-definition management
Only the system owner may:
Role payloads are validated against the backend permission catalog.
The backend rejects:
Role presets are intentionally not persisted as backend identities.
The frontend presets:
are convenience templates that compile into explicit permission selections and role metadata. After submission, the backend treats the result as an ordinary validated role. If the user modifies a preset, it simply becomes a custom permission set.
This avoids coupling backend authorization to UI template names.
Constrained role delegation
roles.viewandroles.assignare separate permissions.A delegated administrator with
roles.assignmay assign roles only when all of the following conditions are satisfied:The backend returns target-specific role eligibility, including whether each role can be assigned or removed and a stable reason code when it cannot.
The frontend may use this information to disable ineligible roles, but the backend repeats the complete validation when the assignment is saved.
This prevents request forgery, stale-page submission, combined-role escalation, and omission of locked assignments.
Protected accounts
A role may mark its members as protected accounts.
Protection is role metadata, not a normal permission code.
A non-owner administrator cannot perform administrative operations against a protected account, including:
The restriction is enforced across single-item routes, batch routes, alternate mutation paths, and candidate lists.
The system owner remains able to manage protected accounts.
The system-administrator frontend preset enables account protection by default, but protection is still stored and validated as explicit role metadata.
Before the system owner disables protection on a role that already has members, the backend reports the affected member count and requires an explicit confirmed mutation. The current role state and member count are revalidated at the transactional write boundary.
Personal self-service security operations remain separate from administrative operations.
Session and permission freshness
Role and account-security changes must not leave old authority active in an existing session.
The backend invalidates or rechecks relevant sessions after operations such as:
Permission changes therefore take effect on the next protected backend request. A user cannot keep exercising revoked permissions simply because an older page remains open.
Pending login, TFA, and passkey flows are also invalidated where the corresponding security operation requires it.
Strategy assignment without full strategy access
A user may have permission to assign a strategy without permission to inspect its complete configuration.
For this case, the backend provides a minimal candidate representation containing only assignment-required fields such as:
The response does not include the full strategy configuration or secret-bearing fields.
Normal strategy detail endpoints still require the full strategy-view permission.
This preserves practical delegation without turning strategy assignment into an indirect data-exposure path.
Transaction and batch semantics
Security-sensitive writes are authorized again at the transaction boundary.
For supported administrative batch operations:
This applies to relevant role, user, user-group, strategy, security, and status operations.
The goal is to prevent time-of-check/time-of-use authorization races and misleading partial-success responses.
Audit records
The Console audit model records administrative authorization activity with sufficient context to reconstruct what happened.
Records include relevant fields such as:
Actor identity remains attributable even when related user data later changes or is removed.
Audit access itself is protected by
audit.view.Address-book behavior
Personal address-book access remains a basic authenticated-user capability and is not converted into a role permission.
Shared address-book administration is covered separately by explicit RBAC permissions.
Existing RustDesk client-facing address-book compatibility routes remain available because they are consumed by RustDesk clients. They were not removed merely because the Console Web application uses newer management endpoints.
Shared-address-book ACL operations continue to use their existing resource-level access rules and are not silently converted into unrestricted Console-administrator operations.
Database and startup behavior
The RBAC schema is added through the project's existing TypeORM synchronization path.
The changes include storage for:
The existing system owner is preserved.
A partial unique database constraint enforces that at most one user may be marked as the system owner. Startup accepts databases containing zero or one owner, but rejects databases containing multiple owners instead of silently repairing them.
A database backup is recommended before deploying this change to an existing installation.
Compatibility and boundaries
This PR does not modify:
hbbshbbrThe RBAC implementation protects Console management operations. It does not change how RustDesk clients connect to the RustDesk server.
Existing non-owner users without roles are intentionally reduced to ordinary-user access. Administrators should assign explicit roles before expecting those accounts to retain management access.
The implementation intentionally does not introduce:
These features would substantially increase the authorization and testing surface and are outside the scope of this PR.
Security invariants
The implementation follows these invariants:
Verification
Automated verification completed for the final backend branch:
git diff --check: passedCoverage includes:
A read-only concurrency smoke test also completed without 5xx responses, timeouts, database-lock failures, or process termination.
Suggested review scenarios
Summary by CodeRabbit
New Features
Bug Fixes
Security