From e01a76625e6ca953acb4414864c6fd6888c1df33 Mon Sep 17 00:00:00 2001 From: Yun Jiang Date: Wed, 3 Jul 2024 14:16:06 +0100 Subject: [PATCH 1/5] feat(mobile): Adding setting in mobile app to import TLS client certificate and private key --- mobile/assets/i18n/en-US.json | 18 ++- mobile/lib/entities/store.entity.dart | 36 +++++ mobile/lib/utils/http_ssl_cert_override.dart | 43 +++++- .../widgets/settings/advanced_settings.dart | 2 + .../settings/ssl_client_cert_settings.dart | 129 ++++++++++++++++++ mobile/pubspec.yaml | 1 + 6 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 mobile/lib/widgets/settings/ssl_client_cert_settings.dart diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json index 3b8db159878a4..139c5895b177b 100644 --- a/mobile/assets/i18n/en-US.json +++ b/mobile/assets/i18n/en-US.json @@ -531,5 +531,19 @@ "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89", "viewer_remove_from_stack": "Remove from Stack", "viewer_stack_use_as_main_asset": "Use as Main Asset", - "viewer_unstack": "Un-Stack" -} \ No newline at end of file + "viewer_unstack": "Un-Stack", + "header_settings_header_name_input": "Header name", + "header_settings_header_value_input": "Header value", + "header_settings_page_title": "Proxy Headers", + "header_settings_add_header_tip": "Add Header", + "header_settings_field_validator_msg": "Value cannot be empty", + "client_cert_title": "SSL Client Certificate", + "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login", + "client_cert_import": "Import", + "client_cert_remove": "Remove", + "client_cert_remove_msg": "Client certificate is removed", + "client_cert_import_success_msg": "Client certificate is imported", + "client_cert_invalid_msg": "Invalid certificate file or wrong password", + "client_cert_dialog_msg_confirm": "OK", + "client_cert_enter_password": "Enter Password" +} diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart index 55d252b307a44..c97bb94472e4e 100644 --- a/mobile/lib/entities/store.entity.dart +++ b/mobile/lib/entities/store.entity.dart @@ -1,3 +1,7 @@ + +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:collection/collection.dart'; import 'package:immich_mobile/entities/user.entity.dart'; import 'package:isar/isar.dart'; @@ -140,6 +144,36 @@ class StoreValue { } } +class SSLClientCertStoreVal { + final Uint8List data; + final String? password; + + SSLClientCertStoreVal(this.data, this.password); + + void save() { + final b64Str = base64Encode(data); + Store.put(StoreKey.sslClientCertData, b64Str); + if (password != null) { + Store.put(StoreKey.sslClientPasswd, password!); + } + } + + static SSLClientCertStoreVal? load() { + final b64Str = Store.tryGet(StoreKey.sslClientCertData); + if (b64Str == null) { + return null; + } + final Uint8List certData = base64Decode(b64Str); + final passwd = Store.tryGet(StoreKey.sslClientPasswd); + return SSLClientCertStoreVal(certData, passwd); + } + + static void delete() { + Store.delete(StoreKey.sslClientCertData); + Store.delete(StoreKey.sslClientPasswd); + } +} + class StoreKeyNotFoundException implements Exception { final StoreKey key; StoreKeyNotFoundException(this.key); @@ -164,6 +198,8 @@ enum StoreKey { serverEndpoint(12, type: String), autoBackup(13, type: bool), backgroundBackup(14, type: bool), + sslClientCertData(15, type: String), + sslClientPasswd(16, type: String), // user settings from [AppSettingsEnum] below: loadPreview(100, type: bool), loadOriginal(101, type: bool), diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index 25d22b6812864..0aae3825724a5 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -4,11 +4,50 @@ import 'package:immich_mobile/entities/store.entity.dart'; import 'package:logging/logging.dart'; class HttpSSLCertOverride extends HttpOverrides { + static final Logger _log = Logger("HttpSSLCertOverride"); + final SSLClientCertStoreVal? _clientCert; + late final SecurityContext? _ctxWithCert; + + HttpSSLCertOverride() + :_clientCert = SSLClientCertStoreVal.load() { + if (_clientCert != null) { + _ctxWithCert = SecurityContext(withTrustedRoots: true); + if (_ctxWithCert != null) { + setClientCert(_ctxWithCert, _clientCert); + } else { + _log.severe("Failed to create security context with client cert!"); + } + } else { + _ctxWithCert = null; + } + } + + static bool setClientCert(SecurityContext ctx, SSLClientCertStoreVal cert) { + try { + _log.info("Setting client certificate"); + ctx.usePrivateKeyBytes(cert.data, password: cert.password); + if (!Platform.isIOS) { + ctx.useCertificateChainBytes(cert.data, password: cert.password); + } + } catch(e) { + _log.severe("Failed to set SSL client cert: $e"); + return false; + } + return true; + } + @override HttpClient createHttpClient(SecurityContext? context) { + if (context != null) { + if (_clientCert != null) { + setClientCert(context, _clientCert); + } + } else { + context = _ctxWithCert; + } + return super.createHttpClient(context) ..badCertificateCallback = (X509Certificate cert, String host, int port) { - var log = Logger("HttpSSLCertOverride"); AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert; @@ -28,7 +67,7 @@ class HttpSSLCertOverride extends HttpOverrides { } if (!selfSignedCertsAllowed) { - log.severe("Invalid SSL certificate for $host:$port"); + _log.severe("Invalid SSL certificate for $host:$port"); } return selfSignedCertsAllowed; diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 60ad4ea3d3619..ec1ab79cf722b 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/immich_logger.service.dart'; import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; +import 'package:immich_mobile/widgets/settings/ssl_client_cert_settings.dart'; import 'package:logging/logging.dart'; class AdvancedSettings extends HookConsumerWidget { @@ -64,6 +65,7 @@ class AdvancedSettings extends HookConsumerWidget { onChanged: (_) => HttpOverrides.global = HttpSSLCertOverride(), ), const CustomeProxyHeaderSettings(), + SslClientCertSettings(isLoggedIn: ref.read(currentUserProvider) != null), ]; return SettingsSubPageScaffold(settings: advancedSettings); diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart new file mode 100644 index 0000000000000..b15d9c5c0d8f4 --- /dev/null +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -0,0 +1,129 @@ +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; + +class SslClientCertSettings extends StatefulWidget { + const SslClientCertSettings({super.key, required this.isLoggedIn}); + + final bool isLoggedIn; + + @override + State createState() => _SslClientCertSettingsState(); +} + +class _SslClientCertSettingsState extends State { + _SslClientCertSettingsState() + : isCertExist = SSLClientCertStoreVal.load() != null; + + bool isCertExist; + + @override + Widget build(BuildContext context) { + return ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 20), + horizontalTitleGap: 20, + isThreeLine: true, + title: Text( + "client_cert_title".tr(), + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w500, + ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "client_cert_subtitle".tr(), + style: context.textTheme.bodyMedium,), + const SizedBox(height: 6,), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: widget.isLoggedIn ? null : () => importCert(context), + child: Text("client_cert_import".tr()),), + const SizedBox(width: 15,), + ElevatedButton( + onPressed: widget.isLoggedIn || !isCertExist ? null : () => removeCert(context), + child: Text("client_cert_remove".tr()),), + ],), + ], + ), + ); + } + + void showMessage(BuildContext context, String msg) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + content: Text(msg), + actions: [ + TextButton(onPressed: () => ctx.pop(), child: Text("client_cert_dialog_msg_confirm".tr()),), + ], + ),); + } + + void storeCert(BuildContext context, Uint8List data, String? passwd) { + if (passwd != null && passwd.isEmpty) { + passwd = null; + } + final cert = SSLClientCertStoreVal(data, passwd); + // Test whether the certificate is valid + final isCertValid = HttpSSLCertOverride.setClientCert(SecurityContext(withTrustedRoots: true), cert); + if (!isCertValid) { + showMessage(context, "client_cert_invalid_msg".tr()); + return; + } + cert.save(); + HttpOverrides.global = HttpSSLCertOverride(); + setState(() => isCertExist = true,); + showMessage(context, "client_cert_import_success_msg".tr()); + } + + void setPassword(BuildContext context, Uint8List data) { + final passwd = TextEditingController(); + showDialog( + context: context, + barrierDismissible: false, + builder: (ctx) => AlertDialog( + content: TextField( + controller: passwd, + obscureText: true, + obscuringCharacter: "*", + decoration: InputDecoration( + hintText: "client_cert_enter_password".tr(), + ),), + actions: [ + TextButton( + onPressed: () => { ctx.pop(), storeCert(context, data, passwd.text) }, + child: Text("client_cert_dialog_msg_confirm".tr()),), + ], + ),); + } + + Future importCert(BuildContext ctx) async { + FilePickerResult? res = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['p12', 'pfx',],); + if (res != null) { + File file = File(res.files.single.path!); + final bytes = await file.readAsBytes(); + setPassword(ctx, bytes); + } + } + + void removeCert(BuildContext context) { + SSLClientCertStoreVal.delete(); + HttpOverrides.global = HttpSSLCertOverride(); + setState(() => isCertExist = false,); + showMessage(context, "client_cert_remove_msg".tr()); + } +} \ No newline at end of file diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index f0cda382d2cb9..b7af694aad0ea 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: # easy to remove packages: image_picker: ^1.0.7 # only used to select user profile image from system gallery -> we can simply select an image from within immich? logging: ^1.2.0 + file_picker: ^8.0.0+1 # This is uncommented in F-Droid build script # Taken from https://github.com/Myzel394/locus/blob/445013d22ec1d759027d4303bd65b30c5c8588c8/pubspec.yaml#L105 From 2c88a5382b8bc264683694f159990c4bca4e5db2 Mon Sep 17 00:00:00 2001 From: Yun Jiang Date: Mon, 8 Jul 2024 10:31:54 +0100 Subject: [PATCH 2/5] Formating dart source code to pass dart format test --- mobile/lib/entities/store.entity.dart | 1 - mobile/lib/utils/http_ssl_cert_override.dart | 6 +- .../settings/ssl_client_cert_settings.dart | 66 +++++++++++++------ 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart index c97bb94472e4e..baa7ff51a323e 100644 --- a/mobile/lib/entities/store.entity.dart +++ b/mobile/lib/entities/store.entity.dart @@ -1,4 +1,3 @@ - import 'dart:convert'; import 'dart:typed_data'; diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart index 0aae3825724a5..7794831adb621 100644 --- a/mobile/lib/utils/http_ssl_cert_override.dart +++ b/mobile/lib/utils/http_ssl_cert_override.dart @@ -8,8 +8,7 @@ class HttpSSLCertOverride extends HttpOverrides { final SSLClientCertStoreVal? _clientCert; late final SecurityContext? _ctxWithCert; - HttpSSLCertOverride() - :_clientCert = SSLClientCertStoreVal.load() { + HttpSSLCertOverride() : _clientCert = SSLClientCertStoreVal.load() { if (_clientCert != null) { _ctxWithCert = SecurityContext(withTrustedRoots: true); if (_ctxWithCert != null) { @@ -29,7 +28,7 @@ class HttpSSLCertOverride extends HttpOverrides { if (!Platform.isIOS) { ctx.useCertificateChainBytes(cert.data, password: cert.password); } - } catch(e) { + } catch (e) { _log.severe("Failed to set SSL client cert: $e"); return false; } @@ -48,7 +47,6 @@ class HttpSSLCertOverride extends HttpOverrides { return super.createHttpClient(context) ..badCertificateCallback = (X509Certificate cert, String host, int port) { - AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert; // Check if user has allowed self signed SSL certificates. diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index b15d9c5c0d8f4..e809bde2729d9 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -10,7 +10,7 @@ import 'package:immich_mobile/utils/http_ssl_cert_override.dart'; class SslClientCertSettings extends StatefulWidget { const SslClientCertSettings({super.key, required this.isLoggedIn}); - + final bool isLoggedIn; @override @@ -19,7 +19,7 @@ class SslClientCertSettings extends StatefulWidget { class _SslClientCertSettingsState extends State { _SslClientCertSettingsState() - : isCertExist = SSLClientCertStoreVal.load() != null; + : isCertExist = SSLClientCertStoreVal.load() != null; bool isCertExist; @@ -40,8 +40,11 @@ class _SslClientCertSettingsState extends State { children: [ Text( "client_cert_subtitle".tr(), - style: context.textTheme.bodyMedium,), - const SizedBox(height: 6,), + style: context.textTheme.bodyMedium, + ), + const SizedBox( + height: 6, + ), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -49,12 +52,19 @@ class _SslClientCertSettingsState extends State { children: [ ElevatedButton( onPressed: widget.isLoggedIn ? null : () => importCert(context), - child: Text("client_cert_import".tr()),), - const SizedBox(width: 15,), + child: Text("client_cert_import".tr()), + ), + const SizedBox( + width: 15, + ), ElevatedButton( - onPressed: widget.isLoggedIn || !isCertExist ? null : () => removeCert(context), - child: Text("client_cert_remove".tr()),), - ],), + onPressed: widget.isLoggedIn || !isCertExist + ? null + : () => removeCert(context), + child: Text("client_cert_remove".tr()), + ), + ], + ), ], ), ); @@ -66,9 +76,13 @@ class _SslClientCertSettingsState extends State { builder: (ctx) => AlertDialog( content: Text(msg), actions: [ - TextButton(onPressed: () => ctx.pop(), child: Text("client_cert_dialog_msg_confirm".tr()),), + TextButton( + onPressed: () => ctx.pop(), + child: Text("client_cert_dialog_msg_confirm".tr()), + ), ], - ),); + ), + ); } void storeCert(BuildContext context, Uint8List data, String? passwd) { @@ -77,14 +91,17 @@ class _SslClientCertSettingsState extends State { } final cert = SSLClientCertStoreVal(data, passwd); // Test whether the certificate is valid - final isCertValid = HttpSSLCertOverride.setClientCert(SecurityContext(withTrustedRoots: true), cert); + final isCertValid = HttpSSLCertOverride.setClientCert( + SecurityContext(withTrustedRoots: true), cert); if (!isCertValid) { showMessage(context, "client_cert_invalid_msg".tr()); return; } cert.save(); HttpOverrides.global = HttpSSLCertOverride(); - setState(() => isCertExist = true,); + setState( + () => isCertExist = true, + ); showMessage(context, "client_cert_import_success_msg".tr()); } @@ -100,19 +117,26 @@ class _SslClientCertSettingsState extends State { obscuringCharacter: "*", decoration: InputDecoration( hintText: "client_cert_enter_password".tr(), - ),), + ), + ), actions: [ TextButton( - onPressed: () => { ctx.pop(), storeCert(context, data, passwd.text) }, - child: Text("client_cert_dialog_msg_confirm".tr()),), + onPressed: () => {ctx.pop(), storeCert(context, data, passwd.text)}, + child: Text("client_cert_dialog_msg_confirm".tr()), + ), ], - ),); + ), + ); } Future importCert(BuildContext ctx) async { FilePickerResult? res = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: ['p12', 'pfx',],); + allowedExtensions: [ + 'p12', + 'pfx', + ], + ); if (res != null) { File file = File(res.files.single.path!); final bytes = await file.readAsBytes(); @@ -123,7 +147,9 @@ class _SslClientCertSettingsState extends State { void removeCert(BuildContext context) { SSLClientCertStoreVal.delete(); HttpOverrides.global = HttpSSLCertOverride(); - setState(() => isCertExist = false,); + setState( + () => isCertExist = false, + ); showMessage(context, "client_cert_remove_msg".tr()); } -} \ No newline at end of file +} From 2798702ef22f79fbab05719adb5c4756e30cf378 Mon Sep 17 00:00:00 2001 From: Yun Jiang Date: Wed, 10 Jul 2024 14:54:45 +0100 Subject: [PATCH 3/5] Adding missed required trailing commas to pass dart static analysis --- mobile/lib/widgets/settings/ssl_client_cert_settings.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index e809bde2729d9..19598c0c8adbc 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -92,7 +92,9 @@ class _SslClientCertSettingsState extends State { final cert = SSLClientCertStoreVal(data, passwd); // Test whether the certificate is valid final isCertValid = HttpSSLCertOverride.setClientCert( - SecurityContext(withTrustedRoots: true), cert); + SecurityContext(withTrustedRoots: true), + cert, + ); if (!isCertValid) { showMessage(context, "client_cert_invalid_msg".tr()); return; From b93f1de2c097907c3f2f21a8539bdddaf8935efb Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Fri, 26 Jul 2024 08:49:18 -0500 Subject: [PATCH 4/5] update lock file --- mobile/ios/Podfile.lock | 50 +++++++++++++++++++++++++++++++++++++++++ mobile/pubspec.lock | 30 ++++++++++++++++--------- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 7cda1122b12c2..39938b020a039 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -4,6 +4,40 @@ PODS: - ReachabilitySwift - device_info_plus (0.0.1): - Flutter + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter - Flutter (1.0.0) - flutter_local_notifications (0.0.1): - Flutter @@ -46,6 +80,9 @@ PODS: - FlutterMacOS - ReachabilitySwift (5.0.0) - SAMKeychain (1.5.3) + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -54,6 +91,7 @@ PODS: - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) + - SwiftyGif (5.4.5) - Toast (4.0.0) - url_launcher_ios (0.0.1): - Flutter @@ -66,6 +104,7 @@ PODS: DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) @@ -91,10 +130,14 @@ DEPENDENCIES: SPEC REPOS: trunk: + - DKImagePickerController + - DKPhotoGallery - FMDB - MapLibre - ReachabilitySwift - SAMKeychain + - SDWebImage + - SwiftyGif - Toast EXTERNAL SOURCES: @@ -102,6 +145,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/connectivity_plus/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter flutter_local_notifications: @@ -150,6 +195,9 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef @@ -170,9 +218,11 @@ SPEC CHECKSUMS: photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812 video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index fe803b5762183..de2c4ce687a55 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -277,10 +277,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e url: "https://pub.dev" source: hosted - version: "0.3.3+4" + version: "0.3.3+8" crypto: dependency: transitive description: @@ -417,6 +417,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03 + url: "https://pub.dev" + source: hosted + version: "8.0.0+1" file_selector_linux: dependency: transitive description: @@ -548,10 +556,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" + sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.20" flutter_riverpod: dependency: transitive description: @@ -1186,10 +1194,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.8" pointycastle: dependency: transitive description: @@ -1759,18 +1767,18 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.2.0" win32_registry: dependency: transitive description: name: win32_registry - sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" xdg_directories: dependency: transitive description: @@ -1804,5 +1812,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.22.2" From 1e0bb9fd0058da84124f45bc3beb29fa526501bb Mon Sep 17 00:00:00 2001 From: Alex Tran Date: Fri, 26 Jul 2024 08:52:24 -0500 Subject: [PATCH 5/5] variable names --- .../settings/ssl_client_cert_settings.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart index 19598c0c8adbc..0daddd6d88fad 100644 --- a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart +++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart @@ -70,11 +70,11 @@ class _SslClientCertSettingsState extends State { ); } - void showMessage(BuildContext context, String msg) { + void showMessage(BuildContext context, String message) { showDialog( context: context, builder: (ctx) => AlertDialog( - content: Text(msg), + content: Text(message), actions: [ TextButton( onPressed: () => ctx.pop(), @@ -85,11 +85,11 @@ class _SslClientCertSettingsState extends State { ); } - void storeCert(BuildContext context, Uint8List data, String? passwd) { - if (passwd != null && passwd.isEmpty) { - passwd = null; + void storeCert(BuildContext context, Uint8List data, String? password) { + if (password != null && password.isEmpty) { + password = null; } - final cert = SSLClientCertStoreVal(data, passwd); + final cert = SSLClientCertStoreVal(data, password); // Test whether the certificate is valid final isCertValid = HttpSSLCertOverride.setClientCert( SecurityContext(withTrustedRoots: true), @@ -108,13 +108,13 @@ class _SslClientCertSettingsState extends State { } void setPassword(BuildContext context, Uint8List data) { - final passwd = TextEditingController(); + final password = TextEditingController(); showDialog( context: context, barrierDismissible: false, builder: (ctx) => AlertDialog( content: TextField( - controller: passwd, + controller: password, obscureText: true, obscuringCharacter: "*", decoration: InputDecoration( @@ -123,7 +123,8 @@ class _SslClientCertSettingsState extends State { ), actions: [ TextButton( - onPressed: () => {ctx.pop(), storeCert(context, data, passwd.text)}, + onPressed: () => + {ctx.pop(), storeCert(context, data, password.text)}, child: Text("client_cert_dialog_msg_confirm".tr()), ), ],