Skip to content

feat(admin): COOL-52: Admin impersonate role#9202

Merged
dannash100 merged 35 commits intomainfrom
feat/impersonate-a-role
Apr 21, 2026
Merged

feat(admin): COOL-52: Admin impersonate role#9202
dannash100 merged 35 commits intomainfrom
feat/impersonate-a-role

Conversation

@dannash100
Copy link
Copy Markdown
Contributor

@dannash100 dannash100 commented Feb 20, 2026

Changes

⌘ + click avatar

impersonate.mov

Server (facility-server)

  • POST /admin/impersonate — admin-only endpoint that accepts a roleId, mints a new JWT with the impersonated role embedded, and returns the token + recalculated permissions for that role

Redux store (web/store/auth.js)

  • startImpersonation(role) — calls the server endpoint, swaps the JWT via api.setToken(), persists the impersonated role + permissions to localStorage, rebuilds the CASL ability for the impersonated role, dispatches IMPERSONATE_ROLE
  • stopImpersonation() — same endpoint with roleId: null, restores original token/permissions/ability, dispatches STOP_IMPERSONATION

Auth context (web/contexts/Auth.jsx)

  • Selects impersonatingRole and ability from Redux and exposes them app-wide via AuthContext
  • All permission checks use the swapped ability, so the entire UI reflects the impersonated role's access

UI (web/components/Sidebar/)

  • HiddenSyncAvatar — meta-click (Cmd/Ctrl+click) on the avatar opens the popover (admin only), visual indicator when impersonating

Deploys

  • Deploy to Tamanu Internal

Tests

  • Run E2E Tests

Review Hero

  • Run Review Hero
  • Auto-fix review suggestions Wait for Review Hero to finish, resolve any comments you disagree with or want to fix manually, then check this to auto-fix the rest.
  • Auto-fix CI failures Check this to auto-fix lint errors, test failures, and other CI issues.

Remember to...

  • ...write or update tests
  • ...add UI screenshots and testing notes to the Linear issue
  • ...add any manual upgrade steps to the Linear issue
  • ...update the config reference, settings reference, or any relevant runbook(s)
  • ...call out additions or changes to config files for the deployment team to take note of

gemini-code-assist[bot]

This comment was marked as outdated.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@dannash100
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread packages/web/app/store/auth.js Outdated
Comment thread packages/web/app/contexts/Auth.jsx Outdated
@dannash100
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread packages/web/app/api/TamanuApi.jsx Outdated
Comment thread packages/web/app/store/auth.js
@dannash100
Copy link
Copy Markdown
Contributor Author

bugbot run

@dannash100
Copy link
Copy Markdown
Contributor Author

Code scanning issue is hitting existing code

@beyondessential beyondessential deleted a comment from gemini-code-assist Bot Feb 20, 2026
@dannash100 dannash100 changed the title feat: Super admin impersonate role feat(admin): COOL-52: Admin impersonate role Feb 20, 2026
Comment thread packages/web/app/store/auth.js
Comment thread packages/facility-server/app/routes/apiv1/index.js
@dannash100 dannash100 marked this pull request as ready for review February 20, 2026 03:02
Comment thread packages/web/app/store/auth.js Outdated
Comment thread packages/web/app/components/HiddenSyncAvatar.jsx
Comment thread packages/web/app/components/Sidebar/ImpersonationSelector.jsx
Comment thread packages/shared/src/permissions/middleware.js Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 26, 2026

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 26, 2026

🍹 up on tamanu-on-k8s/bes/tamanu-on-k8s/feat-impersonate-a-role

Pulumi report
   Updating (feat-impersonate-a-role)

View Live: https://app.pulumi.com/bes/tamanu-on-k8s/feat-impersonate-a-role/updates/16

Downloading plugin random-4.19.0: starting
Downloading plugin random-4.19.0: done
Installing plugin random-4.19.0: starting
Installing plugin random-4.19.0: done

@ Updating....
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running 
@ Updating....
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read kubernetes:core/v1:Namespace tamanu-feat-impersonate-a-role
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read pulumi:pulumi:StackReference bes/k8s-core/tamanu-internal-main
@ Updating....
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read pulumi:pulumi:StackReference bes/k8s-core/tamanu-internal-main
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read pulumi:pulumi:StackReference bes/core/tamanu-internal
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read pulumi:pulumi:StackReference bes/core/tamanu-internal
@ Updating..........
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read kubernetes:core/v1:Namespace tamanu-feat-impersonate-a-role
@ Updating....
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Waiting for central-db...
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Waiting for facility-1-db...
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Waiting for facility-2-db...
~  kubernetes:apps/v1:Deployment facility-2-web updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment central-web updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment facility-1-web updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment patient-portal-web updating (0s) [diff: ~spec]
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read kubernetes:core/v1:ConfigMap actual-provisioning
++ kubernetes:batch/v1:Job central-migrator creating replacement (0s) [diff: ~spec]
@ Updating....
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Secret facility-2-db-superuser not found or not ready: Error: HTTP-Code: 404
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Message: Unknown API Status Code!
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Body: "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"secrets \\\"facility-2-db-superuser\\\" not found\",\"reason\":\"NotFound\",\"details\":{\"name\":\"facility-2-db-superuser\",\"kind\":\"secrets\"},\"code\":404}
"
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Headers: {"audit-id":"69815bb0-5f1c-47c2-b7c5-b02867e78a6d","cache-control":"no-cache, private","connection":"close","content-length":"220","content-type":"application/json","date":"Tue, 21 Apr 2026 03:06:31 GMT","x-kubernetes-pf-flowschema-uid":"3fb296fc-e46b-45d1-9306-057e37ddd229","x-kubernetes-pf-prioritylevel-uid":"feccf24d-a074-4fa8-aa6f-db82477fc2f5"}
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Secret facility-1-db-superuser not found or not ready: Error: HTTP-Code: 404
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Message: Unknown API Status Code!
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Body: "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"secrets \\\"facility-1-db-superuser\\\" not found\",\"reason\":\"NotFound\",\"details\":{\"name\":\"facility-1-db-superuser\",\"kind\":\"secrets\"},\"code\":404}
"
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Headers: {"audit-id":"6a7ad35b-d0f9-42b1-91a7-9015f8245a33","cache-control":"no-cache, private","connection":"close","content-length":"220","content-type":"application/json","date":"Tue, 21 Apr 2026 03:06:31 GMT","x-kubernetes-pf-flowschema-uid":"3fb296fc-e46b-45d1-9306-057e37ddd229","x-kubernetes-pf-prioritylevel-uid":"feccf24d-a074-4fa8-aa6f-db82477fc2f5"}
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Secret central-db-superuser not found or not ready: Error: HTTP-Code: 404
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Message: Unknown API Status Code!
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Body: "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"secrets \\\"central-db-superuser\\\" not found\",\"reason\":\"NotFound\",\"details\":{\"name\":\"central-db-superuser\",\"kind\":\"secrets\"},\"code\":404}
"
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running Headers: {"audit-id":"92e8d954-9f23-4001-96e3-484033f20d98","cache-control":"no-cache, private","connection":"close","content-length":"214","content-type":"application/json","date":"Tue, 21 Apr 2026 03:06:31 GMT","x-kubernetes-pf-flowschema-uid":"3fb296fc-e46b-45d1-9306-057e37ddd229","x-kubernetes-pf-prioritylevel-uid":"feccf24d-a074-4fa8-aa6f-db82477fc2f5"}
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role running read kubernetes:core/v1:ConfigMap actual-provisioning
++ kubernetes:batch/v1:Job central-migrator creating replacement (0s) [diff: ~spec]; 
@ Updating....
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (0s) [diff: ~spec]
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (0s) [diff: ~spec]
@ Updating....
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (0s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-migrator creating replacement (2s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-migrator-6e27d6c2" to start
++ kubernetes:batch/v1:Job central-migrator creating replacement (2s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-migrator-6e27d6c2" to succeed (Active: 1 | Succeeded: 0 | Failed: 0)
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (0s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (0s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-2-migrator-9f94c085" to start
~  kubernetes:apps/v1:Deployment facility-2-web updating (3s) [diff: ~spec]; Waiting for app ReplicaSet to be available (0/1 Pods available)
~  kubernetes:apps/v1:Deployment facility-1-web updating (3s) [diff: ~spec]; Waiting for app ReplicaSet to be available (0/1 Pods available)
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (0s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-2-migrator-9f94c085" to succeed (Active: 1 | Succeeded: 0 | Failed: 0)
~  kubernetes:apps/v1:Deployment patient-portal-web updating (3s) [diff: ~spec]; Waiting for app ReplicaSet to be available (0/1 Pods available)
~  kubernetes:apps/v1:Deployment central-web updating (3s) [diff: ~spec]; Waiting for app ReplicaSet to be available (0/1 Pods available)
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (1s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-1-migrator-4b58987d" to start
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (1s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-1-migrator-4b58987d" to succeed (Active: 1 | Succeeded: 0 | Failed: 0)
@ Updating.............
~  kubernetes:apps/v1:Deployment facility-2-web updating (13s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-2-web-808c50a5-85bb5689bb-xfpqt]: containers with unready status: [http]
~  kubernetes:apps/v1:Deployment facility-1-web updating (13s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-1-web-37cad006-6fbb9f8b79-bwvk9]: containers with unready status: [http]
~  kubernetes:apps/v1:Deployment patient-portal-web updating (13s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/patient-portal-web-c1a62b5c-77d9cd55db-64bw2]: containers with unready status: [http]
~  kubernetes:apps/v1:Deployment central-web updating (13s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/central-web-d93e4b76-865979b845-r5jgn]: containers with unready status: [http]
@ Updating..........
~  kubernetes:apps/v1:Deployment facility-2-web updating (20s) [diff: ~spec]; Waiting for app ReplicaSet to be available (1/2 Pods available)
@ Updating....
~  kubernetes:apps/v1:Deployment facility-1-web updating (20s) [diff: ~spec]; Waiting for app ReplicaSet to be available (1/2 Pods available)
~  kubernetes:apps/v1:Deployment central-web updating (20s) [diff: ~spec]; Waiting for app ReplicaSet to be available (1/2 Pods available)
@ Updating....
~  kubernetes:apps/v1:Deployment patient-portal-web updating (21s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment patient-portal-web updating (21s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment patient-portal-web updated (21s) [diff: ~spec]; 
@ Updating....
~  kubernetes:apps/v1:Deployment facility-1-web updating (23s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-1-web-37cad006-6fbb9f8b79-xv7gr]: containers with unready status: [http]
~  kubernetes:apps/v1:Deployment facility-2-web updating (23s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-2-web-808c50a5-85bb5689bb-k6hb5]: containers with unready status: [http]
~  kubernetes:apps/v1:Deployment central-web updating (23s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/central-web-d93e4b76-865979b845-j67pz]: containers with unready status: [http]
@ Updating.............
~  kubernetes:apps/v1:Deployment central-web updating (33s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment central-web updating (33s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-web updated (33s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-web updating (33s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-1-web updating (33s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-web updated (33s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-web updating (33s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-2-web updating (33s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-web updated (33s) [diff: ~spec]; 
@ Updating........................
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (51s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-1-migrator-4b58987d-lhkth]: Container "migrator" completed with exit code 0
++ kubernetes:batch/v1:Job central-migrator creating replacement (53s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/central-migrator-6e27d6c2-w7w8n]: Container "migrator" completed with exit code 0
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (51s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-2-migrator-9f94c085-gzpbh]: Container "migrator" completed with exit code 0
@ Updating.....
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (53s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-1-migrator-4b58987d" to succeed (Active: 0 | Succeeded: 0 | Failed: 0)
++ kubernetes:batch/v1:Job central-migrator creating replacement (55s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-migrator-6e27d6c2" to succeed (Active: 0 | Succeeded: 0 | Failed: 0)
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (54s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-1-migrator-4b58987d" to succeed (Active: 0 | Succeeded: 1 | Failed: 0)
++ kubernetes:batch/v1:Job facility-1-migrator creating replacement (54s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job facility-1-migrator created replacement (54s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job facility-1-migrator replacing (0s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job facility-1-migrator replaced (0.00s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-api updating (0s) [diff: ~spec]
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (54s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-2-migrator-9f94c085" to succeed (Active: 0 | Succeeded: 0 | Failed: 0)
~  kubernetes:apps/v1:Deployment facility-1-sync updating (0s) [diff: ~spec]
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (54s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/facility-2-migrator-9f94c085" to succeed (Active: 0 | Succeeded: 1 | Failed: 0)
++ kubernetes:batch/v1:Job facility-2-migrator creating replacement (54s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-migrator creating replacement (55s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-migrator-6e27d6c2" to succeed (Active: 0 | Succeeded: 1 | Failed: 0)
++ kubernetes:batch/v1:Job central-migrator creating replacement (55s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-tasks updating (0s) [diff: ~spec]
++ kubernetes:batch/v1:Job facility-2-migrator created replacement (54s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-migrator created replacement (55s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job facility-2-migrator replacing (0s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job central-migrator replacing (0s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job facility-2-migrator replaced (0.00s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job central-migrator replaced (0.00s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-tasks updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment facility-2-api updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment facility-2-sync updating (0s) [diff: ~spec]
++ kubernetes:batch/v1:Job central-provisioner creating replacement (0s) [diff: ~spec]
@ Updating....
~  kubernetes:apps/v1:Deployment facility-1-tasks updating (0s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-1-tasks updating (0s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-tasks updated (0.77s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-provisioner creating replacement (0s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-provisioner creating replacement (0s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-provisioner-22a16fb9" to start
~  kubernetes:apps/v1:Deployment facility-1-api updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-1-api updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-api updated (1s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-provisioner creating replacement (1s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-provisioner-22a16fb9" to succeed (Active: 1 | Succeeded: 0 | Failed: 0)
@ Updating....
~  kubernetes:apps/v1:Deployment facility-1-sync updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-1-sync updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-1-sync updated (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-tasks updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-2-tasks updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-tasks updated (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-api updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-2-api updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-api updated (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-sync updating (1s) [diff: ~spec]; warning: [MinimumReplicasUnavailable] Deployment does not have minimum availability.
~  kubernetes:apps/v1:Deployment facility-2-sync updating (1s) [diff: ~spec]; warning: Replicas scaled to 0 for Deployment "facility-2-sync"
@ Updating....
~  kubernetes:apps/v1:Deployment facility-2-sync updating (2s) [diff: ~spec]; Waiting for app ReplicaSet to be available (0/1 Pods available)
@ Updating............
~  kubernetes:apps/v1:Deployment facility-2-sync updating (11s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/facility-2-sync-5f786c8f79-jrg2l]: containers with unready status: [server]
@ Updating..........
~  kubernetes:apps/v1:Deployment facility-2-sync updating (18s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment facility-2-sync updating (18s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment facility-2-sync updated (18s) [diff: ~spec]; 
@ Updating.........
++ kubernetes:batch/v1:Job central-provisioner creating replacement (24s) [diff: ~spec]; warning: [Pod tamanu-feat-impersonate-a-role/central-provisioner-22a16fb9-vzf4m]: Container "provisioner" completed with exit code 0
@ Updating.....
++ kubernetes:batch/v1:Job central-provisioner creating replacement (26s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-provisioner-22a16fb9" to succeed (Active: 0 | Succeeded: 0 | Failed: 0)
++ kubernetes:batch/v1:Job central-provisioner creating replacement (26s) [diff: ~spec]; Waiting for Job "tamanu-feat-impersonate-a-role/central-provisioner-22a16fb9" to succeed (Active: 0 | Succeeded: 1 | Failed: 0)
++ kubernetes:batch/v1:Job central-provisioner creating replacement (26s) [diff: ~spec]; 
++ kubernetes:batch/v1:Job central-provisioner created replacement (26s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job central-provisioner replacing (0s) [diff: ~spec]; 
+- kubernetes:batch/v1:Job central-provisioner replaced (0.00s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-fhir-refresh updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment central-tasks updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment central-api updating (0s) [diff: ~spec]
~  kubernetes:apps/v1:Deployment central-fhir-resolver updating (0s) [diff: ~spec]
@ Updating....
~  kubernetes:apps/v1:Deployment central-tasks updating (0s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment central-tasks updating (0s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-tasks updated (0.78s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-api updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment central-api updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-api updated (1s) [diff: ~spec]; 
@ Updating....
~  kubernetes:apps/v1:Deployment central-fhir-refresh updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment central-fhir-refresh updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-fhir-resolver updating (1s) [diff: ~spec]; Deployment initialization complete
~  kubernetes:apps/v1:Deployment central-fhir-resolver updating (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-fhir-refresh updated (1s) [diff: ~spec]; 
~  kubernetes:apps/v1:Deployment central-fhir-resolver updated (1s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job central-provisioner deleting original (0s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job central-provisioner deleting original (0s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job central-provisioner deleted original (0.45s) [diff: ~spec]; 
@ Updating....
-- kubernetes:batch/v1:Job facility-1-migrator deleting original (0s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job facility-2-migrator deleting original (0s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job central-migrator deleting original (0s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job facility-1-migrator deleting original (0s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job facility-1-migrator deleted original (0.81s) [diff: ~spec]; 
@ Updating....
-- kubernetes:batch/v1:Job facility-2-migrator deleting original (1s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job facility-2-migrator deleted original (1s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job central-migrator deleting original (1s) [diff: ~spec]; 
-- kubernetes:batch/v1:Job central-migrator deleted original (1s) [diff: ~spec]; 
   pulumi:pulumi:Stack tamanu-on-k8s-feat-impersonate-a-role  15 messages
Diagnostics:
 pulumi:pulumi:Stack (tamanu-on-k8s-feat-impersonate-a-role):
   Waiting for central-db...
   Waiting for facility-1-db...
   Waiting for facility-2-db...

   Secret facility-2-db-superuser not found or not ready: Error: HTTP-Code: 404
   Message: Unknown API Status Code!
   Body: "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"secrets \\\"facility-2-db-superuser\\\" not found\",\"reason\":\"NotFound\",\"details\":{\"name\":\"facility-2-db-superuser\",\"kind\":\"secrets\"},\"code\":404}
"
   Headers: {"audit-id":"69815bb0-5f1c-47c2-b7c5-b02867e78a6d","cache-control":"no-cache, private","connection":"close","content-length":"220","content-type":"application/json","date":"Tue, 21 Apr 2026 03:06:31 GMT","x-kubernetes-pf-flowschema-uid":"3fb296fc-e46b-45d1-9306-057e37ddd229","x-kubernetes-pf-prioritylevel-uid":"feccf24d-a074-4fa8-aa6f-db82477fc2f5"}
   Secret facility-1-db-superuser not found or not ready: Error: HTTP-Code: 404
   Message: Unknown API Status Code!
   Body: "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"secrets \\\"facility-1-db-superuser\\\" not found\",\"reason\":\"NotFound\",\"details\":{\"name\":\"facility-1-db-superuser\",\"kind\":\"secrets\"},\"code\":404}
"
   Headers: {"audit-id":"6a7ad35b-d0f9-42b1-91a7-9015f8245a33","cache-control":"no-cache, private","connection":"close","content-length":"220","content-type":"application/json","date":"Tue, 21 Apr 2026 03:06:31 GMT","x-kubernetes-pf-flowschema-uid":"3fb296fc-e46b-45d1-9306-057e37ddd229","x-kubernetes-pf-prioritylevel-uid":"feccf24d-a074-4fa8-aa6f-db82477fc2f5"}
   Secret central-db-superuser not found or not ready: Error: HTTP-Code: 404
   Message: Unknown API Status Code!
   Body: "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"secrets \\\"central-db-superuser\\\" not found\",\"reason\":\"NotFound\",\"details\":{\"name\":\"central-db-superuser\",\"kind\":\"secrets\"},\"code\":404}
"
   Headers: {"audit-id":"92e8d954-9f23-4001-96e3-484033f20d98","cache-control":"no-cache, private","connection":"close","content-length":"214","content-type":"application/json","date":"Tue, 21 Apr 2026 03:06:31 GMT","x-kubernetes-pf-flowschema-uid":"3fb296fc-e46b-45d1-9306-057e37ddd229","x-kubernetes-pf-prioritylevel-uid":"feccf24d-a074-4fa8-aa6f-db82477fc2f5"}

   [Pulumi Neo] Would you like help with these diagnostics?
   https://app.pulumi.com/bes/tamanu-on-k8s/feat-impersonate-a-role/updates/16?explainFailure

Outputs:
   urls: {
       Central      : "https://central.feat-impersonate-a-role.cd.tamanu.app"
       Facility- 1  : "https://facility-1.feat-impersonate-a-role.cd.tamanu.app"
       Facility- 2  : "https://facility-2.feat-impersonate-a-role.cd.tamanu.app"
       PatientPortal: "https://portal.feat-impersonate-a-role.cd.tamanu.app"
   }

Resources:
   ~ 14 updated
   +-4 replaced
   18 changes. 65 unchanged

Duration: 1m38s

   

Comment thread packages/web/app/store/auth.js
Copy link
Copy Markdown
Collaborator

@rohan-bes rohan-bes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks well implemented, nice. Couple comments but nothing blocking.

Comment thread packages/shared/src/permissions/rolesToPermissions.js Outdated
Comment thread packages/facility-server/app/routes/apiv1/index.js
Comment thread packages/web/app/store/auth.js
Comment thread packages/web/app/store/auth.js
Comment thread packages/web/app/api/TamanuApi.jsx
Comment thread packages/web/app/api/TamanuApi.jsx
Comment thread packages/facility-server/app/routes/apiv1/index.js
Comment thread packages/web/app/api/TamanuApi.jsx
Comment thread packages/web/app/store/auth.js
Comment thread packages/web/app/store/auth.js
} catch {
return null;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual JWT decode duplicates existing jose utility

Low Severity

getImpersonateRoleIdFromToken manually splits and decodes the JWT payload using atob with base64url-to-base64 replacements, duplicating the logic already available in the jose library (decodeJwt) which is a dependency of @tamanu/api-client and supports browser environments. The base class already uses decodeJwt from jose in its login method to decode JWT claims. Using the existing utility avoids subtle encoding edge cases (e.g., missing base64 padding) and reduces duplication.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c7c9b9a. Configure here.

Comment thread packages/web/app/components/Sidebar/ImpersonationSelector.jsx
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit aada6d3. Configure here.

[STOP_IMPERSONATION]: action => ({
impersonatingRole: null,
ability: action.ability,
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redux token state becomes stale after impersonation

Medium Severity

The IMPERSONATE_ROLE and STOP_IMPERSONATION Redux actions update ability and impersonatingRole but never update token. Meanwhile, startImpersonation and stopImpersonation thunks call api.setToken(token) which writes the new JWT to localStorage and the API client. This creates a split-brain state: state.auth.token holds the pre-impersonation JWT while localStorage and the API client hold the post-impersonation JWT. Any code reading from state.auth.token — such as the bootstrap code in index.js — would see a stale value.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit aada6d3. Configure here.

@dannash100 dannash100 merged commit 4ba5114 into main Apr 21, 2026
98 checks passed
@dannash100 dannash100 deleted the feat/impersonate-a-role branch April 21, 2026 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants