feat(config): support retrying entering encryption password on failure

This commit is contained in:
Jagandeep Brar
2022-03-17 00:11:28 -04:00
parent 4c60df5523
commit cd9a3d295e
10 changed files with 96 additions and 67 deletions

View File

@@ -34,6 +34,8 @@
"settings.BackupToCloudDescription": "Backup Configuration Data",
"settings.BackupToCloudFailure": "Failed to Backup",
"settings.BackupToCloudSuccess": "Successfully Backed Up",
"settings.BackupToDevice": "Backup to Device",
"settings.BackupToDeviceDescription": "Backup Configuration Data",
"settings.BannersNotificationModuleSupportHeader": "Supported Modules",
"settings.BannersNotificationModuleSupportBody": "Webhook-based notifications are currently only supported in the modules listed below.\n\nAdditional module support will come in the future!",
"settings.BasicAuthentication": "Basic Authentication",
@@ -197,6 +199,8 @@
"settings.RestoreFromCloudFailure": "Failed to Restore",
"settings.RestoreFromCloudSuccess": "Successfully Restored",
"settings.RestoreFromCloudSuccessMessage": "Your configuration has been restored",
"settings.RestoreFromDevice": "Restore from Device",
"settings.RestoreFromDeviceDescription": "Restore Configuration Data",
"settings.SignedInFailure": "Failed to Sign In",
"settings.SignedInSuccess": "Successfully Signed In",
"settings.SignedOutFailure": "Failed to Sign Out",
@@ -282,6 +286,7 @@
"lunasea.Remove": "Remove",
"lunasea.Rename": "Rename",
"lunasea.Restore": "Restore",
"lunasea.Retry": "Retry",
"lunasea.ReturnToDashboard": "Return to Dashboard",
"lunasea.SearchTextBar": "Search…",
"lunasea.Set": "Set",

View File

@@ -40,7 +40,7 @@ class Desktop implements LunaFileSystem {
data: result.files[0].bytes!,
);
} else {
showLunaInfoSnackBar(
showLunaErrorSnackBar(
title: 'lunasea.InvalidFileTypeSelected'.tr(),
message: 'lunasea.PleaseTryAgain'.tr(),
);

View File

@@ -53,7 +53,7 @@ class Mobile implements LunaFileSystem {
data: result.files[0].bytes!,
);
} else {
showLunaInfoSnackBar(
showLunaErrorSnackBar(
title: 'lunasea.InvalidFileTypeSelected'.tr(),
message: 'lunasea.PleaseTryAgain'.tr(),
);

View File

@@ -4,12 +4,25 @@ Future<void> showLunaErrorSnackBar({
required String title,
dynamic error,
String? message,
bool showButton = false,
String buttonText = 'view',
Function? buttonOnPressed,
}) async =>
showLunaSnackBar(
title: title,
message: message ?? LunaLogger.checkLogsMessage,
type: LunaSnackbarType.ERROR,
showButton: error != null,
buttonOnPressed: () async => LunaDialogs().textPreview(
LunaState.navigatorKey.currentContext!, 'Error', error.toString()),
showButton: error != null || showButton,
buttonText: buttonText,
buttonOnPressed: () async {
if (error != null) {
LunaDialogs().textPreview(
LunaState.navigatorKey.currentContext!,
'Error',
error.toString(),
);
} else if (buttonOnPressed != null) {
buttonOnPressed();
}
},
);

View File

@@ -22,7 +22,7 @@ class LunaEncryption {
return _encrypted;
} catch (error, stack) {
LunaLogger().error(
'Failed to decrypted "$data" using the key "$encryptionKey"',
'Failed to encrypt "${data.substring(0, 10)}..."',
error,
stack,
);
@@ -44,7 +44,7 @@ class LunaEncryption {
return _encrypter.decrypt64(data, iv: iv);
} catch (error, stack) {
LunaLogger().error(
'Failed to decrypted "$data" using the key "$decryptionKey"',
'Failed to decrypt "${data.substring(0, 10)}..."',
error,
stack,
);

View File

@@ -35,35 +35,16 @@ class _State extends State<SettingsAccountRestoreConfigurationTile> {
if (_loadingState == LunaLoadingState.ACTIVE) return;
updateState(LunaLoadingState.ACTIVE);
try {
List<LunaFirebaseBackupDocument> documents =
await LunaFirebaseFirestore().getBackupEntries();
Tuple2<bool, LunaFirebaseBackupDocument?> result =
await SettingsDialogs().getBackupFromCloud(context, documents);
final docs = await LunaFirebaseFirestore().getBackupEntries();
final result = await SettingsDialogs().getBackupFromCloud(context, docs);
if (result.item1) {
String? encrypted =
await LunaFirebaseStorage().downloadBackup(result.item2!.id);
Tuple2<bool, String> key =
await SettingsDialogs().decryptBackup(context);
if (key.item1) {
String decrypted = LunaEncryption().decrypt(key.item2, encrypted!);
if (decrypted != LunaEncryption.ENCRYPTION_FAILURE) {
await LunaConfiguration().import(context, decrypted).then((_) {
updateState(LunaLoadingState.INACTIVE);
showLunaSuccessSnackBar(
title: 'settings.RestoreFromCloudSuccess'.tr(),
message: 'settings.RestoreFromCloudSuccessMessage'.tr(),
);
});
} else {
showLunaErrorSnackBar(
title: 'settings.RestoreFromCloudFailure'.tr(),
message: 'lunasea.IncorrectEncryptionKey'.tr(),
);
}
}
String? id = result.item2!.id;
String? encrypted = await LunaFirebaseStorage().downloadBackup(id);
if (encrypted != null) _decryptBackup(context, encrypted);
}
} catch (error, stack) {
LunaLogger().error('Restore Failed', error, stack);
LunaLogger().error('Failed to restore cloud backup', error, stack);
showLunaErrorSnackBar(
title: 'settings.RestoreFromCloudFailure'.tr(),
error: error,
@@ -71,4 +52,26 @@ class _State extends State<SettingsAccountRestoreConfigurationTile> {
}
updateState(LunaLoadingState.INACTIVE);
}
Future<void> _decryptBackup(BuildContext context, String encrypted) async {
Tuple2<bool, String> _key = await SettingsDialogs().decryptBackup(context);
if (_key.item1) {
String decrypted = LunaEncryption().decrypt(_key.item2, encrypted);
if (decrypted != LunaEncryption.ENCRYPTION_FAILURE) {
await LunaConfiguration().import(context, decrypted);
showLunaSuccessSnackBar(
title: 'settings.RestoreFromCloudSuccess'.tr(),
message: 'settings.RestoreFromCloudSuccessMessage'.tr(),
);
} else {
showLunaErrorSnackBar(
title: 'settings.RestoreFromCloudFailure'.tr(),
message: 'settings.IncorrectEncryptionKey'.tr(),
showButton: true,
buttonText: 'lunasea.Retry'.tr(),
buttonOnPressed: () async => _decryptBackup(context, encrypted),
);
}
}
}
}

View File

@@ -10,8 +10,8 @@ class SettingsSystemBackupRestoreBackupTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LunaBlock(
title: 'Backup to Device',
body: const [TextSpan(text: 'Backup Configuration Data')],
title: 'settings.BackupToDevice'.tr(),
body: [TextSpan(text: 'settings.BackupToDeviceDescription'.tr())],
trailing: const LunaIconButton(icon: Icons.upload_rounded),
onTap: () async => _backup(context),
);
@@ -31,16 +31,18 @@ class SettingsSystemBackupRestoreBackupTile extends StatelessWidget {
'$name.lunasea',
utf8.encode(encrypted),
);
if (result)
if (result) {
showLunaSuccessSnackBar(
title: 'Saved Backup',
message: 'Backup has been successfully saved');
title: 'settings.BackupToCloudSuccess'.tr(),
message: '$name.lunasea',
);
}
}
}
} catch (error, stack) {
LunaLogger().error('Backup Failed', error, stack);
LunaLogger().error('Failed to create device backup', error, stack);
showLunaErrorSnackBar(
title: 'Failed to Backup',
title: 'settings.BackupToCloudFailure'.tr(),
error: error,
);
}

View File

@@ -10,8 +10,8 @@ class SettingsSystemBackupRestoreRestoreTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return LunaBlock(
title: 'Restore from Device',
body: const [TextSpan(text: 'Restore Configuration Data')],
title: 'settings.RestoreFromDevice'.tr(),
body: [TextSpan(text: 'settings.RestoreFromDeviceDescription'.tr())],
trailing: const LunaIconButton(icon: Icons.download_rounded),
onTap: () async => _restore(context),
);
@@ -20,35 +20,36 @@ class SettingsSystemBackupRestoreRestoreTile extends StatelessWidget {
Future<void> _restore(BuildContext context) async {
try {
LunaFile? file = await LunaFileSystem().read(context, ['lunasea']);
if (file != null) {
Tuple2<bool, String> _key =
await SettingsDialogs().decryptBackup(context);
if (_key.item1) {
String _decrypted = LunaEncryption().decrypt(
_key.item2,
String.fromCharCodes(file.data),
);
if (_decrypted != LunaEncryption.ENCRYPTION_FAILURE) {
LunaConfiguration().import(context, _decrypted).then(
(_) => showLunaSuccessSnackBar(
title: 'Restored',
message: 'Your configuration has been restored',
),
);
} else {
showLunaErrorSnackBar(
title: 'Failed to Restore',
message: 'An incorrect encryption key was supplied',
);
}
}
}
if (file != null) await _decryptBackup(context, file);
} catch (error, stack) {
LunaLogger().error('Restore Failed', error, stack);
LunaLogger().error('Failed to restore device backup', error, stack);
showLunaErrorSnackBar(
title: 'Failed to Restore',
title: 'settings.RestoreFromCloudFailure'.tr(),
error: error,
);
}
}
Future<void> _decryptBackup(BuildContext context, LunaFile file) async {
Tuple2<bool, String> _key = await SettingsDialogs().decryptBackup(context);
if (_key.item1) {
String encrypted = String.fromCharCodes(file.data);
String decrypted = LunaEncryption().decrypt(_key.item2, encrypted);
if (decrypted != LunaEncryption.ENCRYPTION_FAILURE) {
await LunaConfiguration().import(context, decrypted);
showLunaSuccessSnackBar(
title: 'settings.RestoreFromCloudSuccess'.tr(),
message: 'settings.RestoreFromCloudSuccessMessage'.tr(),
);
} else {
showLunaErrorSnackBar(
title: 'settings.RestoreFromCloudFailure'.tr(),
message: 'settings.IncorrectEncryptionKey'.tr(),
showButton: true,
buttonText: 'lunasea.Retry'.tr(),
buttonOnPressed: () async => _decryptBackup(context, file),
);
}
}
}
}

View File

@@ -52,6 +52,7 @@
"lunasea.Remove": "Remove",
"lunasea.Rename": "Rename",
"lunasea.Restore": "Restore",
"lunasea.Retry": "Retry",
"lunasea.ReturnToDashboard": "Return to Dashboard",
"lunasea.SearchTextBar": "Search…",
"lunasea.Set": "Set",

View File

@@ -34,6 +34,8 @@
"settings.BackupToCloudDescription": "Backup Configuration Data",
"settings.BackupToCloudFailure": "Failed to Backup",
"settings.BackupToCloudSuccess": "Successfully Backed Up",
"settings.BackupToDevice": "Backup to Device",
"settings.BackupToDeviceDescription": "Backup Configuration Data",
"settings.BannersNotificationModuleSupportHeader": "Supported Modules",
"settings.BannersNotificationModuleSupportBody": "Webhook-based notifications are currently only supported in the modules listed below.\n\nAdditional module support will come in the future!",
"settings.BasicAuthentication": "Basic Authentication",
@@ -197,6 +199,8 @@
"settings.RestoreFromCloudFailure": "Failed to Restore",
"settings.RestoreFromCloudSuccess": "Successfully Restored",
"settings.RestoreFromCloudSuccessMessage": "Your configuration has been restored",
"settings.RestoreFromDevice": "Restore from Device",
"settings.RestoreFromDeviceDescription": "Restore Configuration Data",
"settings.SignedInFailure": "Failed to Sign In",
"settings.SignedInSuccess": "Successfully Signed In",
"settings.SignedOutFailure": "Failed to Sign Out",