high priority medium complexity backend pending backend specialist Tier 4

Acceptance Criteria

An admin attempting to modify a user outside their org subtree receives an `OutOfScopeException` with error code ADMIN_SCOPE_VIOLATION before any Supabase mutation is attempted
OrgHierarchyService.getSubtreeIds() is called once per request and the result is cached for the duration of the request to avoid redundant DB queries
AdminRlsGuard and the service-layer validation are both enforced — defense in depth: RLS blocks unauthorized DB writes even if service validation is bypassed
Super-admins (org_level = 'national') bypass subtree checks and may manage users across all org nodes
Validation logic is extracted into a reusable `assertAdminScope(actorId, targetUserId)` method callable from any service method without duplicating subtree fetching
All WRITE operations on UserManagementService (pause, deactivate, update role, update certification) pass through `assertAdminScope` — no write method bypasses it
When a user has membership in multiple org nodes (up to 5 per NHF requirements), validation passes if the target user's PRIMARY org node is within the admin's subtree
Scope violation attempts are recorded in the security_audit_log table with actor_id, target_user_id, attempted_operation, and timestamp
Integration test: Admin A (node 5) cannot modify User B (node 12) when node 12 is not in A's subtree; returns 403-equivalent error
Integration test: Admin A (node 5) CAN modify User C (node 7) when node 7 IS in A's subtree

Technical Requirements

frameworks
Flutter
Dart
Riverpod
apis
Supabase Postgres (RLS policies)
OrgHierarchyService.getSubtreeIds()
AdminRlsGuard
data models
UserProfile
OrgNode
OrgMembership
SecurityAuditLog
performance requirements
Subtree ID lookup completes in under 200ms for org trees up to 1400 nodes (NHF scale)
Subtree IDs are request-scoped cached — maximum 1 DB call per admin action regardless of how many users are modified
assertAdminScope adds no more than 50ms overhead to any service method
security requirements
Service-layer validation must occur BEFORE any read of the target user's sensitive data, not just before writes
Supabase RLS policies act as the final enforcement layer; service validation is the first layer
Scope violation log entries must not be deletable by any client-accessible role
Multi-org membership (up to 5 nodes) must not allow privilege escalation: only the primary node determines admin write scope

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Implement `assertAdminScope` as a method on UserManagementService that accepts `actorAdminId` and `targetUserId`, fetches (or reads from request cache) the actor's subtree, then queries the target user's primary org node. Throw a typed `AdminScopeViolationException` — do not use generic exceptions, as callers need to distinguish scope violations from other errors for correct HTTP-status-equivalent mapping. For the subtree cache, use a simple `Map>` keyed by admin ID stored in the service's request context object — do not use a long-lived cache as org hierarchies can change between requests. The Supabase RLS policy should mirror the service logic using a Postgres function `is_in_subtree(actor_id, target_org_id)` so both enforcement layers share the same tree traversal logic.

Document the dual-layer pattern in a code comment so future developers don't remove the service-layer check thinking RLS is sufficient alone.

Testing Requirements

Unit tests: mock OrgHierarchyService to return a fixed subtree; assert that each UserManagementService write method calls assertAdminScope exactly once; assert OutOfScopeException is thrown for out-of-subtree target IDs; assert super-admin role bypasses the check. Parameterized tests: run scope check against 10 different (actor, target) org node pairs covering in-scope, out-of-scope, same-node, parent, and child scenarios. Integration tests with Supabase local: verify RLS independently blocks a direct table INSERT from an out-of-scope admin JWT even when service validation is disabled. Security regression test: confirm that a user with membership in 5 org nodes cannot be modified by an admin whose subtree contains only a secondary (non-primary) org node of that user.

Component
User Management Service
service high
Epic Risks (4)
medium impact high prob technical

OrgHierarchyNavigator rendering NHF's full 1,400-chapter tree in a single widget may cause Flutter frame-rate drops below 60 fps on mid-range devices, making the navigator unusable for NHF national admins.

Mitigation & Contingency

Mitigation: Implement lazy expansion: only load immediate children on node expand rather than the full tree upfront. Use virtual scrolling for long sibling lists. Test with a synthetic 1,400-node dataset on a low-end Android device during development.

Contingency: If lazy expansion is insufficient, replace the tree widget with a paginated drill-down navigator (select level → select child) that avoids rendering more than 50 nodes at a time.

medium impact medium prob dependency

Bufdir may update their required export column structure or file format during or after development. If the AdminExportService hardcodes the current Bufdir schema, any format change requires a code release rather than a config update.

Mitigation & Contingency

Mitigation: Drive the Bufdir column mapping from a configuration repository rather than hardcoded constants. Abstract column definitions into a named schema config so that format changes require only a config update and re-deployment without service logic changes.

Contingency: If Bufdir format changes post-launch, release a config update within one sprint. If the change is structural (new required sections), scope a targeted service update and communicate timeline to partner organisations.

high impact medium prob integration

Role transition side-effects in UserManagementService (e.g., certification expiry removing mentor from chapter listing, pause triggering coordinator notification) may interact with external services like HLF's website sync. Incomplete side-effect handling could leave the system in an inconsistent state.

Mitigation & Contingency

Mitigation: Model side-effects as explicit domain events published after the primary state change is persisted. Implement event handlers as idempotent operations so re-processing is safe. Write integration tests that assert all side-effects fire correctly for each role transition type.

Contingency: If a side-effect fails after the primary change is persisted, log the failure with full context and trigger a manual reconciliation alert to the on-call team. Provide an admin-accessible re-trigger action for failed side-effects.

medium impact medium prob scope

If AdminStatisticsService cache TTL is set too long, org_admin may see significantly stale KPI values (e.g., a mentor newly paused an hour ago still appears as active), undermining trust in the dashboard.

Mitigation & Contingency

Mitigation: Default cache TTL to 5 minutes with a manual refresh action on the dashboard. Implement cache invalidation triggered by UserManagementService write operations that affect counted entities.

Contingency: If staleness causes org admin complaints post-launch, reduce TTL to 60 seconds and introduce a real-time Supabase subscription for high-impact counters (paused mentors, expiring certifications).