From b5762ea08dda069f171d50b129e8d21193d3940e Mon Sep 17 00:00:00 2001 From: Bernd Steckmeister Date: Fri, 3 Jul 2026 09:59:10 +0200 Subject: [PATCH] feat: Logout-Warnung bei fehlendem Key-Backup (M1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beide Logout-Bestätigungsdialoge prüfen jetzt vorab per neuem isKeyBackupMissing(client) (bootstrap_dialog.dart), ob Cross-Signing/ Key-Backup eingerichtet ist. Fehlt es, zeigt der Dialog eine Klartext-Warnung: alte verschlüsselte Nachrichten wären nach dem Logout auf keinem Gerät mehr lesbar. Ohne fehlendes Backup bleibt der bisherige Text unverändert. --- CHANGES.md | 10 +++++++++ lib/features/auth/bootstrap_dialog.dart | 9 +++++++++ lib/widgets/settings_modal.dart | 27 +++++++++++++++++++------ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7ac3c4a..8a5cec4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1134,3 +1134,13 @@ 2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart 2026-07-03 09:50 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart 2026-07-03 09:51 [Edit] /home/steggi/pyramid/lib/features/auth/login_notifier.dart +2026-07-03 09:51 [Edit] /home/steggi/pyramid/ROADMAP.md +2026-07-03 09:52 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 09:55 [Edit] /home/steggi/pyramid/ROADMAP.md +2026-07-03 09:55 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 09:57 [Edit] /home/steggi/pyramid/lib/features/auth/bootstrap_dialog.dart +2026-07-03 09:58 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart +2026-07-03 09:58 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart +2026-07-03 09:58 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart +2026-07-03 09:58 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart +2026-07-03 09:58 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart diff --git a/lib/features/auth/bootstrap_dialog.dart b/lib/features/auth/bootstrap_dialog.dart index ce08ae7..b22b1c8 100644 --- a/lib/features/auth/bootstrap_dialog.dart +++ b/lib/features/auth/bootstrap_dialog.dart @@ -3,6 +3,15 @@ import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/core/theme.dart'; +/// Ob Cross-Signing/Key-Backup fehlt oder nicht entsperrt ist — wenn ja, würde +/// ein Logout den Zugriff auf ältere verschlüsselte Nachrichten kosten. +Future isKeyBackupMissing(Client client) async { + final enc = client.encryption; + if (enc == null) return true; + if (!enc.crossSigning.enabled) return true; + return !(await enc.keyManager.isCached().catchError((_) => false)); +} + Future showBootstrapDialog( BuildContext context, Client client, { diff --git a/lib/widgets/settings_modal.dart b/lib/widgets/settings_modal.dart index bf0e8e4..61ccd86 100644 --- a/lib/widgets/settings_modal.dart +++ b/lib/widgets/settings_modal.dart @@ -23,6 +23,7 @@ import 'package:pointycastle/export.dart' as pc; import 'package:pyramid/core/app_state.dart'; import 'package:pyramid/core/auth_log.dart'; import 'package:pyramid/core/e2ee_diagnostics.dart'; +import 'package:pyramid/features/auth/bootstrap_dialog.dart' show isKeyBackupMissing; import 'package:pyramid/core/livekit_call_manager.dart'; import 'package:pyramid/core/matrix_client.dart'; import 'package:pyramid/core/voip_manager.dart'; @@ -567,6 +568,9 @@ class _LogoutNavButtonState extends ConsumerState<_LogoutNavButton> { Future _logout() async { final pt = widget.pt; + final client = await ref.read(matrixClientProvider.future); + final backupMissing = await isKeyBackupMissing(client); + if (!mounted) return; final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( @@ -575,8 +579,14 @@ class _LogoutNavButtonState extends ConsumerState<_LogoutNavButton> { borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)), title: Text('Abmelden?', style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)), - content: Text('Du wirst von diesem Gerät abgemeldet.', - style: TextStyle(color: pt.fgMuted, fontSize: 13)), + content: Text( + backupMissing + ? 'Du wirst von diesem Gerät abgemeldet. Es ist kein Schlüssel-Backup eingerichtet — ' + 'ältere verschlüsselte Nachrichten sind danach auf keinem Gerät mehr lesbar.' + : 'Du wirst von diesem Gerät abgemeldet.', + style: TextStyle( + color: backupMissing ? pt.danger : pt.fgMuted, fontSize: 13, height: 1.4), + ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), @@ -596,7 +606,6 @@ class _LogoutNavButtonState extends ConsumerState<_LogoutNavButton> { setState(() => _loading = true); try { widget.onClose(); - final client = await ref.read(matrixClientProvider.future); unawaited(AuthLog.write('User-initiated logout (Settings-Sidebar)')); await client.logout(); } catch (_) {} @@ -5108,6 +5117,9 @@ class _AboutSection extends ConsumerWidget { } Future _confirmLogout(BuildContext context, WidgetRef ref) async { + final client = await ref.read(matrixClientProvider.future); + final backupMissing = await isKeyBackupMissing(client); + if (!context.mounted) return; final confirm = await showDialog( context: context, builder: (ctx) => AlertDialog( @@ -5118,8 +5130,12 @@ class _AboutSection extends ConsumerWidget { title: Text('Abmelden?', style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)), content: Text( - 'Du wirst auf diesem Gerät abgemeldet. Deine Nachrichten bleiben erhalten.', - style: TextStyle(color: pt.fgMuted, fontSize: 13), + backupMissing + ? 'Du wirst auf diesem Gerät abgemeldet. Es ist kein Schlüssel-Backup eingerichtet — ' + 'ältere verschlüsselte Nachrichten sind danach auf keinem Gerät mehr lesbar.' + : 'Du wirst auf diesem Gerät abgemeldet. Deine Nachrichten bleiben erhalten.', + style: TextStyle( + color: backupMissing ? pt.danger : pt.fgMuted, fontSize: 13, height: 1.4), ), actions: [ TextButton( @@ -5139,7 +5155,6 @@ class _AboutSection extends ConsumerWidget { ), ); if (confirm == true) { - final client = await ref.read(matrixClientProvider.future); unawaited(AuthLog.write('User-initiated logout (Settings)')); await client.logout().catchError((_) {}); }