Assign a role to a user (single-role model — overwrites any prior
assignment). Idempotent. Emits user.roleChanged to
admin.dashboard_user_activity when the role actually changes.
Create a new role with the given permission set. permissionKeys is the
full set; unknown keys fail the whole op with ROLE_UNKNOWN_PERMISSION.
Delete a role. Refuses to delete system roles (is_system = true) and
any role still referenced by admin.dashboard_user.role_id.
Permission keys (resource[/sub]:r|w) granted by the user's role.
Returns [] when the user has no role assigned. Result is deduped and
sorted for stable consumption by the auth layer.
NOTE: This is a derived computation (flat string[] of effective
permission keys), not a getByX shortcut against the role catalog.
The service-pattern.md "no special gets — add a filter to search"
rule targets entity lookups; there's no search filter shape on
Role or Permission that produces this projection without the same
three-table join. Kept here for the hot auth path
(admin-dashboard/src/lib/auth.ts → getAuthPermissions).
Optionalinput: {Optionalids?: string[]External ids (role_…) of roles to filter by.
Optionalnames?: string[]Role names to filter by (exact match).
Optionalpage?: numberOptionalpageSize?: numberOptionaluserIds?: string[]External ids of users — returns the (deduped) set of roles held by any user in this list. Single-role model, so each input user contributes at most one role.
Clear the role on a user (sets role_id = NULL). Emits
user.roleChanged when the role actually changes.
Update name / description and/or replace the permission set.
When permissionKeys is provided, it is treated as a full replacement —
the diff against the current set is computed in-service and applied as
an INSERT/DELETE pair inside a single transaction.
Role catalog + role↔permission mapping + per-user role assignment.
Single role per user. Role mutations on users are written to
admin.dashboard_user_activityasuser.roleChanged; this service is the authoritative writer for both surfaces.