What We’re Testing
Organization admins can change the role of other members. The backend (handlers/org-management.ts, action change_role) implements:
- Endpoint:
POST /api/org-managementwithaction: "change_role",org_id,target_user_id,new_role - Valid roles:
owner,admin,member,auditor(enforced by both theorg_memberstable CHECK constraint and handler validation) - Authorization: Only admins and owners can change roles (
isOrgAdmincheck) - Self-change blocked: A user cannot change their own role — returns
FORBIDDENwith “Can’t change your own role” - Owner promotion restricted: Only users with the
ownerrole can promote someone toowner - Realtime broadcast: After a role change, a
members:UPDATEevent is broadcast to all connected WebSocket clients - Audit logging: The change is logged via Loki with event
member.role_changed
The frontend (UsersPage.tsx) exposes this via a Change Role option in the actions dropdown menu on each member row. The dialog offers three selectable roles: Admin, Member, and Auditor.
Your Test Setup
| Machine | Role |
|---|---|
| ⊞ Win-A | Admin/Owner browser session |
Prerequisites: The organization must have at least two members — one admin/owner (you) and one member whose role you will change. Complete Chapters 46-47 first to have a second member in the org.
ST1 — Promote Member to Admin via Dashboard
What it verifies: An admin can change a member’s role to admin using the UI, and the change reflects immediately.
Steps:
- Log in to
https://login.quickztna.comas an org admin or owner. - Navigate to the Users page.
- Find a member with the
memberrole badge in the active members table. - Click the three-dot menu icon (far right of their row).
- Select Change Role.
- In the dialog, change the role dropdown from
MembertoAdmin. - Click Update Role.
Expected behavior:
- Toast: “Role updated”
- The dialog closes
- The member’s role badge in the table changes from
membertoadmin(no page refresh needed)
Pass: Role badge updates to admin immediately. The member now has admin privileges.
Fail / Common issues:
- “Failed to update role” — check browser dev tools Network tab. The PATCH request to
/api/db/org_membersshould include{ "role": "admin" }. - Three-dot menu not visible — the target member may be an
owner, or you may be viewing your own row. The menu is hidden for owners and for the current user’s own row.
ST2 — Demote Admin Back to Member
What it verifies: An admin can be demoted back to member, and the role change reflects immediately.
Steps:
- On the Users page, find the user you just promoted to
adminin ST1. - Click the three-dot menu and select Change Role.
- Change the role dropdown to
Member. - Click Update Role.
Expected behavior:
- Toast: “Role updated”
- Role badge changes from
adminback tomember
Pass: Role reverted to member. The user no longer has admin privileges.
Fail / Common issues:
- Same failure modes as ST1. Verify the PATCH request body contains
{ "role": "member" }.
ST3 — Change Role via API (curl)
What it verifies: The change_role action works correctly through the API with proper validation.
Steps:
- Get your access token and org ID.
- Get the
user_idof the target member (visible in theget_org_membersRPC response or by inspecting the Users page network requests). - Send the role change request:
curl -s -X POST https://login.quickztna.com/api/org-management \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"action":"change_role","org_id":"<org_id>","target_user_id":"<user_id>","new_role":"auditor"}'
Expected behavior:
- HTTP 200
- Response body:
{
"success": true,
"data": {
"user_id": "<user_id>",
"role": "auditor"
}
}
Pass: Response confirms the new role. Refreshing the Users page shows the auditor badge on that member.
Fail / Common issues:
- HTTP 400 with
MISSING_FIELDS— ensure all three fields (org_id,target_user_id,new_role) are present. - HTTP 400 with
INVALID_ROLE— thenew_rolevalue must be exactly one of:owner,admin,member,auditor. - HTTP 403 with
FORBIDDEN— you are not an admin/owner of this org, or you are trying to change your own role.
ST4 — Self Role Change Blocked
What it verifies: A user cannot change their own role (prevents accidental self-demotion or unauthorized self-promotion).
Steps:
- Attempt to change your own role via API:
curl -s -X POST https://login.quickztna.com/api/org-management \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"action":"change_role","org_id":"<org_id>","target_user_id":"<your_own_user_id>","new_role":"member"}'
Expected behavior:
- HTTP 400
- Response body:
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Can't change your own role"
}
}
Dashboard note: The three-dot menu does not appear on your own row in the Users page, so this scenario cannot happen through the UI — only through direct API calls.
Pass: Self role change is rejected with a clear error message.
Fail / Common issues:
- Returns 200 — the self-change guard (
target_user_id === user.user_id) is missing or broken.
ST5 — Owner Promotion Restricted to Owners
What it verifies: Only an owner can promote someone to the owner role. An admin cannot do this.
Steps:
- Log in as a user who has the
adminrole (notowner) in the org. - Attempt to promote a member to owner via API:
curl -s -X POST https://login.quickztna.com/api/org-management \
-H "Authorization: Bearer <admin_access_token>" \
-H "Content-Type: application/json" \
-d '{"action":"change_role","org_id":"<org_id>","target_user_id":"<member_user_id>","new_role":"owner"}'
Expected behavior:
- HTTP 403
- Response body:
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "Only owners can promote to owner role"
}
}
- Now log in as the org
ownerand send the same request.
Expected behavior (as owner):
- HTTP 200
- Response confirms the target user now has the
ownerrole
Pass: Admin is blocked from granting owner. Owner can grant owner.
Fail / Common issues:
- Admin can promote to owner — the caller-role check is missing. The handler must verify
callerRole?.role === 'owner'before allowingnew_role === 'owner'. - The
ownerrole does not appear in the frontend dropdown — by design, the frontend Change Role dialog only showsAdmin,Member, andAuditor. Owner promotion is an API-only operation.
Summary
| Sub-test | Exercises | Key assertion |
|---|---|---|
| ST1 | Dashboard promote member to admin | Role badge changes to admin, toast confirmation |
| ST2 | Dashboard demote admin to member | Role badge reverts to member |
| ST3 | API change_role to auditor | 200 response with new role, page reflects change |
| ST4 | Self role change blocked | 400 FORBIDDEN — “Can’t change your own role” |
| ST5 | Owner promotion restriction | Admin gets 403, only owner can promote to owner |