ソースを参照

ChangePassword: Confirm before signing out from other devices

Neeraj Gupta 1 年間 前
コミット
4fba22f204

+ 7 - 0
lib/generated/intl/messages_en.dart

@@ -564,6 +564,7 @@ class MessageLookup extends MessageLookupByLibrary {
         "discord": MessageLookupByLibrary.simpleMessage("Discord"),
         "dismiss": MessageLookupByLibrary.simpleMessage("Dismiss"),
         "distanceInKMUnit": MessageLookupByLibrary.simpleMessage("km"),
+        "doNotSignOut": MessageLookupByLibrary.simpleMessage("Do not sign out"),
         "doThisLater": MessageLookupByLibrary.simpleMessage("Do this later"),
         "doYouWantToDiscardTheEditsYouHaveMade":
             MessageLookupByLibrary.simpleMessage(
@@ -1208,6 +1209,12 @@ class MessageLookup extends MessageLookupByLibrary {
             MessageLookupByLibrary.simpleMessage("Shared with you"),
         "sharing": MessageLookupByLibrary.simpleMessage("Sharing..."),
         "showMemories": MessageLookupByLibrary.simpleMessage("Show memories"),
+        "signOutFromOtherDevices":
+            MessageLookupByLibrary.simpleMessage("Sign out from other devices"),
+        "signOutOtherBody": MessageLookupByLibrary.simpleMessage(
+            "If you think someone might know your password, you can force all other devices using your account to sign out."),
+        "signOutOtherDevices":
+            MessageLookupByLibrary.simpleMessage("Sign out other devices"),
         "signUpTerms": MessageLookupByLibrary.simpleMessage(
             "I agree to the <u-terms>terms of service</u-terms> and <u-policy>privacy policy</u-policy>"),
         "singleFileDeleteFromDevice": m49,

+ 40 - 0
lib/generated/l10n.dart

@@ -8097,6 +8097,46 @@ class S {
       args: [],
     );
   }
+
+  /// `Sign out from other devices`
+  String get signOutFromOtherDevices {
+    return Intl.message(
+      'Sign out from other devices',
+      name: 'signOutFromOtherDevices',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `If you think someone might know your password, you can force all other devices using your account to sign out.`
+  String get signOutOtherBody {
+    return Intl.message(
+      'If you think someone might know your password, you can force all other devices using your account to sign out.',
+      name: 'signOutOtherBody',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Sign out other devices`
+  String get signOutOtherDevices {
+    return Intl.message(
+      'Sign out other devices',
+      name: 'signOutOtherDevices',
+      desc: '',
+      args: [],
+    );
+  }
+
+  /// `Do not sign out`
+  String get doNotSignOut {
+    return Intl.message(
+      'Do not sign out',
+      name: 'doNotSignOut',
+      desc: '',
+      args: [],
+    );
+  }
 }
 
 class AppLocalizationDelegate extends LocalizationsDelegate<S> {

+ 6 - 1
lib/l10n/intl_en.arb

@@ -1152,5 +1152,10 @@
   },
   "contacts": "Contacts",
   "noInternetConnection": "No internet connection",
-  "pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again."
+  "pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again.",
+
+  "signOutFromOtherDevices": "Sign out from other devices",
+  "signOutOtherBody": "If you think someone might know your password, you can force all other devices using your account to sign out.",
+  "signOutOtherDevices": "Sign out other devices",
+  "doNotSignOut": "Do not sign out"
 }

+ 10 - 3
lib/services/user_service.dart

@@ -502,6 +502,7 @@ class UserService {
   Future<void> registerOrUpdateSrp(
     Uint8List loginKey, {
     SetKeysRequest? setKeysRequest,
+    bool logOutOtherDevices = false,
   }) async {
     try {
       final String username = const Uuid().v4().toString();
@@ -558,6 +559,7 @@ class UserService {
               'setupID': setupSRPResponse.setupID,
               'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)),
               'updatedKeyAttr': setKeysRequest.toMap(),
+              'logOutOtherDevices': logOutOtherDevices,
             },
           );
         }
@@ -676,8 +678,9 @@ class UserService {
 
   Future<void> updateKeyAttributes(
     KeyAttributes keyAttributes,
-    Uint8List loginKey,
-  ) async {
+    Uint8List loginKey, {
+    required bool logoutOtherDevices,
+  }) async {
     try {
       final setKeyRequest = SetKeysRequest(
         kekSalt: keyAttributes.kekSalt,
@@ -686,7 +689,11 @@ class UserService {
         memLimit: keyAttributes.memLimit!,
         opsLimit: keyAttributes.opsLimit!,
       );
-      await registerOrUpdateSrp(loginKey, setKeysRequest: setKeyRequest);
+      await registerOrUpdateSrp(
+        loginKey,
+        setKeysRequest: setKeyRequest,
+        logOutOtherDevices: logoutOtherDevices,
+      );
       await _config.setKeyAttributes(keyAttributes);
     } catch (e) {
       _logger.severe(e);

+ 34 - 5
lib/ui/account/password_entry_page.dart

@@ -7,11 +7,13 @@ import 'package:photos/core/event_bus.dart';
 import 'package:photos/events/account_configured_event.dart';
 import 'package:photos/events/subscription_purchased_event.dart';
 import "package:photos/generated/l10n.dart";
+import "package:photos/l10n/l10n.dart";
 import "package:photos/models/key_gen_result.dart";
 import 'package:photos/services/user_service.dart';
 import 'package:photos/ui/account/recovery_key_page.dart';
 import 'package:photos/ui/common/dynamic_fab.dart';
 import 'package:photos/ui/common/web_page.dart';
+import "package:photos/ui/components/models/button_type.dart";
 import 'package:photos/ui/payment/subscription.dart';
 import 'package:photos/utils/dialog_util.dart';
 import 'package:photos/utils/navigation_util.dart';
@@ -27,9 +29,10 @@ enum PasswordEntryMode {
 class PasswordEntryPage extends StatefulWidget {
   final PasswordEntryMode mode;
 
-  const PasswordEntryPage({required this.mode, Key?
-  key,})
-      : super(key: key);
+  const PasswordEntryPage({
+    required this.mode,
+    Key? key,
+  }) : super(key: key);
 
   @override
   State<PasswordEntryPage> createState() => _PasswordEntryPageState();
@@ -379,13 +382,18 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
   }
 
   void _updatePassword() async {
+    final logOutFromOthers = await logOutFromOtherDevices(context);
     final dialog =
         createProgressDialog(context, S.of(context).generatingEncryptionKeys);
     await dialog.show();
     try {
       final result = await Configuration.instance
           .getAttributesForNewPassword(_passwordController1.text);
-      await UserService.instance.updateKeyAttributes(result.item1, result.item2);
+      await UserService.instance.updateKeyAttributes(
+        result.item1,
+        result.item2,
+        logoutOtherDevices: logOutFromOthers,
+      );
       await dialog.hide();
       showShortToast(context, S.of(context).passwordChangedSuccessfully);
       Navigator.of(context).pop();
@@ -400,12 +408,33 @@ class _PasswordEntryPageState extends State<PasswordEntryPage> {
     }
   }
 
+  Future<bool> logOutFromOtherDevices(BuildContext context) async {
+    bool logOutFromOther = true;
+    await showChoiceDialog(
+      context,
+      title: context.l10n.signOutFromOtherDevices,
+      body: context.l10n.signOutOtherBody,
+      isDismissible: false,
+      firstButtonLabel: context.l10n.signOutOtherDevices,
+      firstButtonType: ButtonType.critical,
+      firstButtonOnTap: () async {
+        logOutFromOther = true;
+      },
+      secondButtonLabel: context.l10n.doNotSignOut,
+      secondButtonOnTap: () async {
+        logOutFromOther = false;
+      },
+    );
+    return logOutFromOther;
+  }
+
   Future<void> _showRecoveryCodeDialog(String password) async {
     final dialog =
         createProgressDialog(context, S.of(context).generatingEncryptionKeys);
     await dialog.show();
     try {
-      final KeyGenResult result = await Configuration.instance.generateKey(password);
+      final KeyGenResult result =
+          await Configuration.instance.generateKey(password);
       Configuration.instance.setVolatilePassword(null);
       await dialog.hide();
       onDone() async {