Task #89: Permissions picker for roles in admin dashboard

Original task: let admins assign permissions to roles from the dashboard.

Changes:
- lib/api-spec/openapi.yaml: added Permission and ReplaceRolePermissionsBody
  schemas; added GET /permissions, expanded GET /roles/{id}/permissions to
  return full Permission objects, and added admin-guarded
  PUT /roles/{id}/permissions. Ran codegen to refresh generated hooks/zod.
- artifacts/api-server/src/routes/roles.ts:
  * Added a single isSystemRole helper and PROTECTED_ROLE_NAMES set.
  * Added serializePermission + GET /permissions.
  * Expanded GET /roles/:id/permissions (404 on missing role, full
    Permission objects).
  * New PUT /roles/:id/permissions: blocks system/protected roles
    (400 with code "system_role_permissions"), rejects non-positive /
    non-integer permissionIds with 400, validates all ids exist (404),
    deletes+inserts atomically in a transaction, and notifies role
    holders via emitAppsChangedToRoleHolders.
  * Hardened legacy POST /roles/:id/permissions and
    DELETE /roles/:id/permissions/:permissionId so they also reject
    system-role mutations with the same code, closing the bypass that
    the new endpoint was meant to prevent.
  * Re-used isSystemRole in PATCH/DELETE role flows.
- artifacts/tx-os/src/pages/admin.tsx: added a permissions checkbox list
  inside the role editor dialog, disabled (read-only) for system /
  protected roles with a hint. Save flow runs updateRole then
  replaceRolePermissions only when the set actually changed and the role
  is non-system. Front-end ROLES_PROTECTED_NAMES set mirrors the
  back-end fallback so admin/user/order_receiver are recognised even
  when the legacy DB column is 0 (also gates the role-card system badge
  and delete button).
- artifacts/tx-os/src/locales/en.json + ar.json: new translation keys
  for the permissions picker, system-role read-only hint, empty list,
  and error toasts.

Verification: tx-os and api-server typecheck clean. End-to-end test
passes: admin login → edit a created role → toggle/save permissions →
re-open and confirm round-trip → admin role dialog shows disabled
checkboxes and read-only hint → PUT on admin role returns 400
"system_role_permissions". curl regression checks: legacy POST/DELETE
on admin role return the same 400/code; PUT rejects float / negative /
string ids with 400.

Replit-Task-Id: 74180397-afdf-4489-976a-a6fe099684bd
This commit is contained in:
riyadhafraa
2026-04-27 12:58:16 +00:00
parent 1e9024e5a2
commit aa6866936d
9 changed files with 737 additions and 23 deletions
+112
View File
@@ -1284,6 +1284,28 @@ paths:
schema:
$ref: "#/components/schemas/ErrorResponse"
# Permissions (admin)
/permissions:
get:
operationId: listPermissions
tags: [roles]
summary: List all available permissions (admin)
responses:
"200":
description: Permissions
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Permission"
"403":
description: Forbidden
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
# Roles (admin)
/roles:
get:
@@ -1403,6 +1425,70 @@ paths:
schema:
$ref: "#/components/schemas/RoleDeletionConflict"
/roles/{id}/permissions:
get:
operationId: getRolePermissions
tags: [roles]
summary: List permissions assigned to a role (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Permissions assigned to the role
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Permission"
"404":
description: Role not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
put:
operationId: replaceRolePermissions
tags: [roles]
summary: Atomically replace the permissions assigned to a role (admin)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ReplaceRolePermissionsBody"
responses:
"200":
description: Updated permissions list for the role
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Permission"
"400":
description: Cannot modify a system role's permissions, or invalid request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Role or one of the permissions was not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
# Groups (admin)
/groups:
get:
@@ -2120,6 +2206,32 @@ components:
- userCount
- groupCount
Permission:
type: object
properties:
id:
type: integer
name:
type: string
descriptionAr:
type: ["string", "null"]
descriptionEn:
type: ["string", "null"]
required:
- id
- name
ReplaceRolePermissionsBody:
type: object
properties:
permissionIds:
type: array
items:
type: integer
description: Full set of permission IDs the role should grant. Existing assignments not present here are removed.
required:
- permissionIds
GroupSummary:
type: object
properties: