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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user