fix: impersonation UX — banner contrast, route blocking, nav filtering

- Banner: white elevated button for contrast, fixed 48px height,
  layout top padding offset so content isn't obscured
- Middleware: allow GET me/profile (viewing), block mutations only;
  add auth/refresh to blocked routes
- Navigation: hide Platform section during impersonation; hide
  org-dependent items when impersonated user has no organisation
- Test: add read-only routes allowed test, auth/refresh blocked test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 02:51:50 +02:00
parent 4df668b5b8
commit 67ce1e9d9d
4 changed files with 64 additions and 37 deletions

View File

@@ -14,15 +14,14 @@ use Symfony\Component\HttpFoundation\Response;
class HandleImpersonation
{
/**
* Routes that are blocked during impersonation.
* These are prefix-matched against the request path (without api/v1 prefix).
* Routes that are blocked during impersonation (all methods).
* Prefix-matched against the request path (without api/v1 prefix).
*/
private const SENSITIVE_ROUTE_PREFIXES = [
'auth/password',
private const BLOCKED_ROUTE_PREFIXES = [
'auth/logout',
'auth/refresh',
'auth/mfa',
'auth/trusted-devices',
'me/profile',
'me/change-password',
'me/change-email',
'admin/impersonate',
@@ -101,7 +100,12 @@ class HandleImpersonation
$path = $request->path();
$path = preg_replace('#^api/v1/#', '', $path);
foreach (self::SENSITIVE_ROUTE_PREFIXES as $prefix) {
// Block profile mutations but allow GET (viewing)
if (str_starts_with($path, 'me/profile') && $request->method() !== 'GET') {
return true;
}
foreach (self::BLOCKED_ROUTE_PREFIXES as $prefix) {
if (str_starts_with($path, $prefix)) {
return true;
}