Implement AccessDenialService with RBAC integration
epic-no-access-screen-access-control-task-002 — Implement the concrete AccessDenialService class that reads the current user role from the role-based access control layer, exposes a Stream<bool> indicating whether the role is blocked from mobile access, and delegates admin portal URL retrieval to the no-access config repository. Global admin role must be treated as blocked.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Implement using a StreamController
Ensure the internal StreamSubscription to the RBAC layer is cancelled in a dispose() method. Avoid using async* generators for the public stream — synchronous transformation via map() on the RBAC stream is cleaner and easier to test. The getAdminPortalUrl() method should be a simple delegation: return _configRepository.getAdminPortalUrl(); — caching is handled in task-003.
Testing Requirements
Unit tests using flutter_test and mocktail. Mock INoAccessConfigRepository and the RBAC role stream. Test scenarios: (1) global_admin role → stream emits true, (2) coordinator role → emits false, (3) peer_mentor role → emits false, (4) null role → emits true, (5) role changes from coordinator to global_admin mid-session → stream emits true after change, (6) dispose called → no further stream emissions or errors. Aim for 100% branch coverage on the blocked-role evaluation logic.
No integration or e2e tests required for this unit.
If the GoRouter redirect callback evaluates the no-access route itself as a blocked destination, it will trigger an infinite redirect loop, crashing the navigator.
Mitigation & Contingency
Mitigation: Add an explicit guard condition in the redirect callback: return null (no redirect) when the current location is already the no-access route or the logout route. Write a dedicated unit test covering this exact scenario.
Contingency: If the redirect loop is detected in production, deploy a hotfix that adds the null-return guard; the feature can be toggled off via the existing feature-flag infrastructure while the fix is prepared.
The access-denial-service may read role state before authentication completes (e.g. during app resume), causing a temporary false-positive block that redirects valid peer-mentor users to the no-access screen.
Mitigation & Contingency
Mitigation: Subscribe to the role-state-manager's loading/ready lifecycle and only evaluate role-based access once the RBAC state is confirmed as loaded. Return a 'pending' state that causes the guard to defer rather than redirect.
Contingency: Add a retry mechanism: if a user lands on the no-access screen but their role subsequently resolves as non-blocked, automatically navigate them to the role-based home screen.