From 56726ac69f235f3af349ac28e65b180ae188478a Mon Sep 17 00:00:00 2001 From: Xavier Paquet-Rapold <80051842+XavierPaquet-Rapold@users.noreply.github.com> Date: Fri, 27 Oct 2023 17:57:14 -0400 Subject: [PATCH 1/2] Hotfix/fix ios build (#878) * fix ios build * update IPHONEOS_DEPLOYMENT_TARGET * Update version * Update workflows to update pods on dev and install pods on master and release --------- Co-authored-by: MysticFragilist --- .github/workflows/dev-workflow.yaml | 23 +++++++++++-- .github/workflows/master-workflow.yaml | 6 ++-- .github/workflows/release-workflow.yaml | 3 +- ios/Podfile | 7 ++++ ios/Podfile.lock | 44 ++++++++++++------------- ios/Runner.xcodeproj/project.pbxproj | 32 +++++++++++++++--- pubspec.yaml | 2 +- 7 files changed, 84 insertions(+), 33 deletions(-) diff --git a/.github/workflows/dev-workflow.yaml b/.github/workflows/dev-workflow.yaml index d1cf1f30e..73e189cdf 100644 --- a/.github/workflows/dev-workflow.yaml +++ b/.github/workflows/dev-workflow.yaml @@ -9,6 +9,8 @@ on: - 'README.fr.md' - 'android/fastlane/**' - 'ios/fastlane/**' + - '.github/master-workflow.yaml' + - '.github/release-workflow.yaml' concurrency: group: ${{ github.ref }} cancel-in-progress: true @@ -241,9 +243,26 @@ jobs: run: | flutter pub get cd ios - rm Podfile.lock - pod install --repo-update + pod update flutter clean + + - name: Commit pod updates + if: matrix.target == 'iOS' + id: commit_pod_versions + uses: stefanzweifel/git-auto-commit-action@v4 + with: + file_pattern: "*.lock" + commit_user_name: github-actions[bot] + commit_user_email: 41898282+github-actions[bot]@users.noreply.github.com + commit_message: "[BOT] Applying pod update." + add_options: '-u' + + # Fail workflow, because new commit will execute workflow + - if: ${{ matrix.target == 'iOS' && steps.commit_pod_versions.outputs.changes_detected == 'true' }} + name: Fail workflow if pod version change + run: | + echo 'Pod update applied, running bot commit workflow' + exit 1 # Get dependencies and decrypt needed files. - run: flutter pub get diff --git a/.github/workflows/master-workflow.yaml b/.github/workflows/master-workflow.yaml index 015eb9dfc..dda3d5ae9 100644 --- a/.github/workflows/master-workflow.yaml +++ b/.github/workflows/master-workflow.yaml @@ -11,6 +11,9 @@ on: - 'README.fr.md' - 'android/fastlane/**' - 'ios/fastlane/**' + - '.github/dev-workflow.yaml' + - '.github/release-workflow.yaml' + concurrency: group: ${{ github.ref }} cancel-in-progress: true @@ -175,8 +178,7 @@ jobs: run: | flutter pub get cd ios - rm Podfile.lock - pod install --repo-update + pod install flutter clean # Get dependencies and decrypt needed files. diff --git a/.github/workflows/release-workflow.yaml b/.github/workflows/release-workflow.yaml index caaa805b3..72aafe6e1 100644 --- a/.github/workflows/release-workflow.yaml +++ b/.github/workflows/release-workflow.yaml @@ -50,8 +50,7 @@ jobs: run: | flutter pub get cd ios - rm Podfile.lock - pod install --repo-update + pod install flutter clean - run: flutter doctor -v diff --git a/ios/Podfile b/ios/Podfile index 17b3307cc..f3ff7cdc6 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,6 @@ # Uncomment this line to define a global platform for your project # platform :ios, '11.0' +app_ios_deployment_target = Gem::Version.new('12.0') # Change to your current deployment target # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -44,5 +45,11 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) + target.build_configurations.each do |configuration| + pod_ios_deployment_target = Gem::Version.new(configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET']) + if pod_ios_deployment_target <= app_ios_deployment_target + configuration.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' + end + end end end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 80c95058f..516bcab83 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -32,7 +32,7 @@ PODS: - Firebase/RemoteConfig (= 10.3.0) - firebase_core - Flutter - - FirebaseABTesting (10.5.0): + - FirebaseABTesting (10.16.0): - FirebaseCore (~> 10.0) - FirebaseAnalytics (10.3.0): - FirebaseAnalytics/AdIdSupport (= 10.3.0) @@ -56,7 +56,7 @@ PODS: - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Logger (~> 7.8) - - FirebaseCoreInternal (10.5.0): + - FirebaseCoreInternal (10.16.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" - FirebaseCrashlytics (10.3.0): - FirebaseCore (~> 10.0) @@ -65,7 +65,7 @@ PODS: - GoogleUtilities/Environment (~> 7.8) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (~> 2.1) - - FirebaseInstallations (10.5.0): + - FirebaseInstallations (10.16.0): - FirebaseCore (~> 10.0) - GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) @@ -112,7 +112,7 @@ PODS: - GoogleUtilities/Network (~> 7.8) - "GoogleUtilities/NSData+zlib (~> 7.8)" - nanopb (< 2.30910.0, >= 2.30908.0) - - GoogleDataTransport (9.2.1): + - GoogleDataTransport (9.2.5): - GoogleUtilities/Environment (~> 7.7) - nanopb (< 2.30910.0, >= 2.30908.0) - PromisesObjC (< 3.0, >= 1.2) @@ -121,24 +121,24 @@ PODS: - GoogleMaps/Base (5.2.0) - GoogleMaps/Maps (5.2.0): - GoogleMaps/Base - - GoogleUtilities/AppDelegateSwizzler (7.11.0): + - GoogleUtilities/AppDelegateSwizzler (7.11.5): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (7.11.0): + - GoogleUtilities/Environment (7.11.5): - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.11.0): + - GoogleUtilities/Logger (7.11.5): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.11.0): + - GoogleUtilities/MethodSwizzler (7.11.5): - GoogleUtilities/Logger - - GoogleUtilities/Network (7.11.0): + - GoogleUtilities/Network (7.11.5): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.11.0)" - - GoogleUtilities/Reachability (7.11.0): + - "GoogleUtilities/NSData+zlib (7.11.5)" + - GoogleUtilities/Reachability (7.11.5): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.11.0): + - GoogleUtilities/UserDefaults (7.11.5): - GoogleUtilities/Logger - home_widget (0.0.1): - Flutter @@ -153,7 +153,7 @@ PODS: - Flutter - path_provider_ios (0.0.1): - Flutter - - PromisesObjC (2.1.1) + - PromisesObjC (2.3.1) - ReachabilitySwift (5.0.0) - shared_preferences_ios (0.0.1): - Flutter @@ -267,30 +267,30 @@ SPEC CHECKSUMS: firebase_core: bf59c32d2e53814f558efa20840c1902fa2fe461 firebase_crashlytics: a45cced3521640e1e389d8b3662936ea9afd6055 firebase_remote_config: 5007603d4cec2dc1e5016077a7ec36ed93c5041b - FirebaseABTesting: 8cb5cc4e395c8dce8a2820a6a329020ead56fe2f + FirebaseABTesting: 03f0a8b88cf618350527f2c6a2234e29b9c65064 FirebaseAnalytics: 036232b6a1e2918e5f67572417be1173576245f3 FirebaseCore: 988754646ab3bd4bdcb740f1bfe26b9f6c0d5f2a - FirebaseCoreInternal: e463f41bb935cd049505bf7e9a5bdd7dcea90df6 + FirebaseCoreInternal: 26233f705cc4531236818a07ac84d20c333e505a FirebaseCrashlytics: f20d956f8229010b645e534693c39e0b7843c268 - FirebaseInstallations: 935bc4abb6f7a035cab7a0c31cb777b2be3dd254 + FirebaseInstallations: b822f91a61f7d1ba763e5ccc9d4f2e6f2ed3b3ee FirebaseRemoteConfig: c24f767c17b0440ee63c7e93380d599173556113 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - flutter_config: 2226c1df19c78fe34a05eb7f1363445f18e76fc1 + flutter_config: f48f0d47a284f1791aacce2687eabb3309ba7a41 flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a google_maps_flutter_ios: 66201f392bf62d500f07670a30488a247b9bb5b9 GoogleAppMeasurement: c7d6fff39bf2d829587d74088d582e32d75133c3 - GoogleDataTransport: ea169759df570f4e37bdee1623ec32a7e64e67c4 + GoogleDataTransport: 54dee9d48d14580407f8f5fbf2f496e92437a2f2 GoogleMaps: 025272d5876d3b32604e5c080dc25eaf68764693 - GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f + GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 home_widget: 2829415127ee92e876f816cbbe44c0b6601b8a37 in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 - PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 @@ -299,6 +299,6 @@ SPEC CHECKSUMS: url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162 -PODFILE CHECKSUM: a6ab79567d5a527b85ace1f55bd19f140d2d619c +PODFILE CHECKSUM: fa4650732678ed866d3e81e65744dc86c9d74a54 -COCOAPODS: 1.11.3 +COCOAPODS: 1.13.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 42497f9ae..e694350ab 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -358,6 +358,7 @@ 2514E4C0200A7C8F9602FA07 /* [CP] Embed Pods Frameworks */, EC003C2FBA7B622A19ED75DB /* [CP] Copy Pods Resources */, 187D822F290314B500821D5F /* Embed Foundation Extensions */, + 8328A493741025D7CEB9E6D2 /* [firebase_crashlytics] Crashlytics Upload Symbols */, ); buildRules = ( ); @@ -483,6 +484,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 8328A493741025D7CEB9E6D2 /* [firebase_crashlytics] Crashlytics Upload Symbols */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "\"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME}\"", + "\"$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)\"", + ); + name = "[firebase_crashlytics] Crashlytics Upload Symbols"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --flutter-project \"$PROJECT_DIR/firebase_app_id_file.json\" "; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -777,7 +798,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -803,6 +824,7 @@ INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "ÉTSMobile"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -863,7 +885,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -912,7 +934,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -940,6 +962,7 @@ INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "ÉTSMobile"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -972,6 +995,7 @@ INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "ÉTSMobile"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/pubspec.yaml b/pubspec.yaml index 21663b9a0..88555c599 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: The 4th generation of ÉTSMobile, the main gateway between the Éco # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.28.2+1 +version: 4.28.3+1 environment: sdk: ">=2.10.0 <3.0.0" From c6fb55e1ce969b5d3aee54136a9f0e254f7c4a42 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 27 Oct 2023 18:03:06 -0400 Subject: [PATCH 2/2] revert 1290210108bf9d63000a8303d067f2eab262eb53 --- ios/ETSMobile Widget/Constants.swift | 7 +- .../Provider/GradesProvider.swift | 2 +- ios/Services/KeychainService.swift | 14 +--- lib/core/managers/user_repository.dart | 39 +++------- lib/core/viewmodels/login_viewmodel.dart | 4 + pubspec.lock | 7 ++ pubspec.yaml | 3 +- test/managers/user_repository_test.dart | 78 +++++-------------- .../services/flutter_secure_storage_mock.dart | 12 +-- 9 files changed, 55 insertions(+), 111 deletions(-) diff --git a/ios/ETSMobile Widget/Constants.swift b/ios/ETSMobile Widget/Constants.swift index 852a7fc34..795c54a6c 100644 --- a/ios/ETSMobile Widget/Constants.swift +++ b/ios/ETSMobile Widget/Constants.swift @@ -5,11 +5,10 @@ // Created by Club Applets on 2022-02-03. // -// Keychain related constants public let widgetGroupId = "group.ca.etsmtl.applets.ETSMobile" -public let keychainServiceAttr = "flutter_secure_storage_service" -public let usernameKey = "usernameKey" -public let passwordKey = "passwordKey" +public let keychainServiceAttr = "flutter_keychain" +public let usernameKey = "WidgetSecureUser" +public let passwordKey = "WidgetSecurePass" // Urls related to MonETS public let monEtsAPI = "https://portail.etsmtl.ca/api/" diff --git a/ios/ETSMobile Widget/Provider/GradesProvider.swift b/ios/ETSMobile Widget/Provider/GradesProvider.swift index 91b6632f4..2572141c3 100644 --- a/ios/ETSMobile Widget/Provider/GradesProvider.swift +++ b/ios/ETSMobile Widget/Provider/GradesProvider.swift @@ -20,7 +20,7 @@ struct GradesProvider: TimelineProvider { title: "Grades - A2022") init() { - keychainService = KeychainService(accessGroup: "group.ca.etsmtl.applets.ETSMobile") + keychainService = KeychainService() signetsService = SignetsService.shared } diff --git a/ios/Services/KeychainService.swift b/ios/Services/KeychainService.swift index b4acfe251..258c448ae 100644 --- a/ios/Services/KeychainService.swift +++ b/ios/Services/KeychainService.swift @@ -8,27 +8,19 @@ import Foundation struct KeychainService { - let accessGroup: String - - init(accessGroup: String) { - self.accessGroup = accessGroup - } - func get(key: String) -> String? { let searchQuery: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: key, - kSecAttrAccessGroup as String: accessGroup, kSecAttrService as String: keychainServiceAttr, + kSecAttrAccount as String: key, kSecReturnData as String: kCFBooleanTrue ?? true ] - + var item: CFTypeRef? - let status = SecItemCopyMatching(searchQuery as CFDictionary, &item) + var status = SecItemCopyMatching(searchQuery as CFDictionary, &item) guard status == errSecSuccess else { return nil } let data = Data(referencing: item as! NSData) let stringData = String(data: data, encoding: String.Encoding.utf8) return stringData } } - diff --git a/lib/core/managers/user_repository.dart b/lib/core/managers/user_repository.dart index 1e74544d5..0befb05ad 100644 --- a/lib/core/managers/user_repository.dart +++ b/lib/core/managers/user_repository.dart @@ -24,7 +24,6 @@ class UserRepository { static const String usernameSecureKey = "usernameKey"; static const String passwordSecureKey = "passwordKey"; - static const String groupOption = "group.ca.etsmtl.applets.ETSMobile"; @visibleForTesting static const String infoCacheKey = "infoCache"; @visibleForTesting @@ -111,14 +110,8 @@ class UserRepository { // Save the credentials in the secure storage if (!isSilent) { try { - await _secureStorage.write( - key: usernameSecureKey, - value: username, - iOptions: _getIOSOptions()); - await _secureStorage.write( - key: passwordSecureKey, - value: password, - iOptions: _getIOSOptions()); + await _secureStorage.write(key: usernameSecureKey, value: username); + await _secureStorage.write(key: passwordSecureKey, value: password); } on PlatformException catch (e, stacktrace) { await _secureStorage.deleteAll(); _analyticsService.logError( @@ -133,19 +126,13 @@ class UserRepository { return true; } - IOSOptions _getIOSOptions() { - return const IOSOptions(groupId: groupOption); - } - /// Check if there are credentials saved and so authenticate the user, otherwise /// return false Future silentAuthenticate() async { try { - final username = await _secureStorage.read( - key: usernameSecureKey, iOptions: _getIOSOptions()); + final username = await _secureStorage.read(key: usernameSecureKey); if (username != null) { - final password = await _secureStorage.read( - key: passwordSecureKey, iOptions: _getIOSOptions()); + final password = await _secureStorage.read(key: passwordSecureKey); return await authenticate( username: username, password: password, isSilent: true); } @@ -166,12 +153,10 @@ class UserRepository { // Delete the credentials from the secure storage try { - await _secureStorage.delete( - key: usernameSecureKey, iOptions: _getIOSOptions()); - await _secureStorage.delete( - key: passwordSecureKey, iOptions: _getIOSOptions()); + await _secureStorage.delete(key: usernameSecureKey); + await _secureStorage.delete(key: passwordSecureKey); } on PlatformException catch (e, stacktrace) { - await _secureStorage.deleteAll(iOptions: _getIOSOptions()); + await _secureStorage.deleteAll(); _analyticsService.logError(tag, "Authenticate - PlatformException - ${e.toString()}", e, stacktrace); return false; @@ -192,8 +177,7 @@ class UserRepository { } } try { - final password = await _secureStorage.read( - key: passwordSecureKey, iOptions: _getIOSOptions()); + final password = await _secureStorage.read(key: passwordSecureKey); return password; } on PlatformException catch (e, stacktrace) { await _secureStorage.deleteAll(); @@ -322,11 +306,10 @@ class UserRepository { /// Check whether the user was previously authenticated. Future wasPreviouslyLoggedIn() async { try { - final String username = await _secureStorage.read( - key: passwordSecureKey, iOptions: _getIOSOptions()); + final String username = await _secureStorage.read(key: passwordSecureKey); if (username != null) { - final String password = await _secureStorage.read( - key: passwordSecureKey, iOptions: _getIOSOptions()); + final String password = + await _secureStorage.read(key: passwordSecureKey); return password.isNotEmpty; } } on PlatformException catch (e, stacktrace) { diff --git a/lib/core/viewmodels/login_viewmodel.dart b/lib/core/viewmodels/login_viewmodel.dart index 5e25b150e..db77b170e 100644 --- a/lib/core/viewmodels/login_viewmodel.dart +++ b/lib/core/viewmodels/login_viewmodel.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; // Package imports: +import 'package:flutter_keychain/flutter_keychain.dart'; +import 'package:stacked/stacked.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:stacked/stacked.dart'; @@ -74,6 +76,8 @@ class LoginViewModel extends BaseViewModel { username: _universalCode.toUpperCase(), password: _password); if (response) { + await FlutterKeychain.put(key: "WidgetSecureUser", value: _universalCode); + await FlutterKeychain.put(key: "WidgetSecurePass", value: _password); _navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard); _preferencesService.setDateTime(PreferencesFlag.ratingTimer, DateTime.now().add(const Duration(days: 7))); diff --git a/pubspec.lock b/pubspec.lock index b2e55531b..2499d4d1c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -421,6 +421,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + flutter_keychain: + dependency: "direct main" + description: + name: flutter_keychain + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.0" flutter_launcher_icons: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 88555c599..7888b689f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ description: The 4th generation of ÉTSMobile, the main gateway between the Éco # pub.dev using `pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.28.3+1 +version: 4.28.4+1 environment: sdk: ">=2.10.0 <3.0.0" @@ -74,6 +74,7 @@ dependencies: carousel_slider: ^4.2.1 reorderable_grid_view: ^2.2.6 import_sorter: ^4.6.0 + flutter_keychain: ^2.1.1 dev_dependencies: flutter_test: diff --git a/test/managers/user_repository_test.dart b/test/managers/user_repository_test.dart index 95b23e34d..b475990d4 100644 --- a/test/managers/user_repository_test.dart +++ b/test/managers/user_repository_test.dart @@ -76,13 +76,9 @@ void main() { // Verify the secureStorage is used verify(secureStorage.write( - key: UserRepository.usernameSecureKey, - value: user.username, - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + key: UserRepository.usernameSecureKey, value: user.username)); verify(secureStorage.write( - key: UserRepository.passwordSecureKey, - value: "", - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + key: UserRepository.passwordSecureKey, value: "")); // Verify the user id is set in the analytics verify(analyticsService.setUserProperties( @@ -138,9 +134,7 @@ void main() { // Verify the secureStorage is used verify(secureStorage.write( - key: UserRepository.usernameSecureKey, - value: user.username, - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + key: UserRepository.usernameSecureKey, value: user.username)); // Verify the user id is set in the analytics verify(analyticsService.setUserProperties( @@ -176,13 +170,9 @@ void main() { // Verify the secureStorage is used verify(secureStorage.write( - key: UserRepository.usernameSecureKey, - value: user.username, - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + key: UserRepository.usernameSecureKey, value: user.username)); verify(secureStorage.write( - key: UserRepository.passwordSecureKey, - value: "", - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + key: UserRepository.passwordSecureKey, value: "")); // Verify the user id is set in the analytics verify(analyticsService.setUserProperties( @@ -246,12 +236,8 @@ void main() { reason: "Result should be true"); verifyInOrder([ - secureStorage.read( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), - secureStorage.read( - key: UserRepository.passwordSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), + secureStorage.read(key: UserRepository.usernameSecureKey), + secureStorage.read(key: UserRepository.passwordSecureKey), monETSApi.authenticate(username: username, password: password), analyticsService.setUserProperties( userId: username, domain: user.domain) @@ -277,12 +263,8 @@ void main() { reason: "Result should be false"); verifyInOrder([ - secureStorage.read( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), - secureStorage.read( - key: UserRepository.passwordSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), + secureStorage.read(key: UserRepository.usernameSecureKey), + secureStorage.read(key: UserRepository.passwordSecureKey), monETSApi.authenticate(username: username, password: password), analyticsService.logError(UserRepository.tag, any, any, any) ]); @@ -301,11 +283,8 @@ void main() { expect(await manager.silentAuthenticate(), isFalse, reason: "Result should be false"); - verifyInOrder([ - secureStorage.read( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)) - ]); + verifyInOrder( + [secureStorage.read(key: UserRepository.usernameSecureKey)]); verifyNoMoreInteractions(secureStorage); verifyZeroInteractions(monETSApi); @@ -332,9 +311,7 @@ void main() { reason: "Result should be false"); verifyInOrder([ - secureStorage.read( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), + secureStorage.read(key: UserRepository.usernameSecureKey), secureStorage.deleteAll(), analyticsService.logError(UserRepository.tag, any, any, any) ]); @@ -348,12 +325,8 @@ void main() { expect(manager.monETSUser, null, reason: "The user shouldn't be available after a logout"); - verify(secureStorage.delete( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); - verify(secureStorage.delete( - key: UserRepository.passwordSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + verify(secureStorage.delete(key: UserRepository.usernameSecureKey)); + verify(secureStorage.delete(key: UserRepository.passwordSecureKey)); verifyNever( analyticsService.logError(UserRepository.tag, any, any, any)); @@ -370,11 +343,8 @@ void main() { expect(manager.monETSUser, null, reason: "The user shouldn't be available after a logout"); - verify(secureStorage.delete( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); - verify(secureStorage.deleteAll( - iOptions: const IOSOptions(groupId: UserRepository.groupOption))); + verify(secureStorage.delete(key: UserRepository.usernameSecureKey)); + verify(secureStorage.deleteAll()); verify(analyticsService.logError(UserRepository.tag, any, any, any)); }); }); @@ -405,12 +375,8 @@ void main() { reason: "Result should be 'password'"); verifyInOrder([ - secureStorage.read( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), - secureStorage.read( - key: UserRepository.passwordSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), + secureStorage.read(key: UserRepository.usernameSecureKey), + secureStorage.read(key: UserRepository.passwordSecureKey), monETSApi.authenticate(username: username, password: password), analyticsService.setUserProperties( userId: username, domain: user.domain) @@ -438,12 +404,8 @@ void main() { verifyInOrder([ analyticsService.logEvent(UserRepository.tag, any), - secureStorage.read( - key: UserRepository.usernameSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), - secureStorage.read( - key: UserRepository.passwordSecureKey, - iOptions: const IOSOptions(groupId: UserRepository.groupOption)), + secureStorage.read(key: UserRepository.usernameSecureKey), + secureStorage.read(key: UserRepository.passwordSecureKey), monETSApi.authenticate(username: username, password: password), analyticsService.setUserProperties( userId: username, domain: user.domain) diff --git a/test/mock/services/flutter_secure_storage_mock.dart b/test/mock/services/flutter_secure_storage_mock.dart index 4d6b425d4..6775d7f80 100644 --- a/test/mock/services/flutter_secure_storage_mock.dart +++ b/test/mock/services/flutter_secure_storage_mock.dart @@ -10,29 +10,25 @@ class FlutterSecureStorageMock extends Mock implements FlutterSecureStorage { /// Stub the read function of [FlutterSecureStorage] static void stubRead(FlutterSecureStorageMock mock, {@required String key, @required String valueToReturn}) { - when(mock.read(key: key, iOptions: anyNamed("iOptions"))) - .thenAnswer((_) async => valueToReturn); + when(mock.read(key: key)).thenAnswer((_) async => valueToReturn); } /// Stub the read function of [FlutterSecureStorage] with an [Exception] static void stubReadException(FlutterSecureStorageMock mock, {@required String key, @required Exception exceptionToThrow}) { - when(mock.read(key: key, iOptions: anyNamed("iOptions"))) - .thenThrow(exceptionToThrow); + when(mock.read(key: key)).thenThrow(exceptionToThrow); } /// Stub the write function of [FlutterSecureStorage] with an [Exception] static void stubWriteException(FlutterSecureStorageMock mock, {@required String key, @required Exception exceptionToThrow}) { - when(mock.write( - key: key, value: anyNamed("value"), iOptions: anyNamed("iOptions"))) + when(mock.write(key: key, value: anyNamed("value"))) .thenThrow(exceptionToThrow); } /// Stub the delete function of [FlutterSecureStorage] with an [Exception] static void stubDeleteException(FlutterSecureStorageMock mock, {@required String key, @required Exception exceptionToThrow}) { - when(mock.delete(key: key, iOptions: anyNamed("iOptions"))) - .thenThrow(exceptionToThrow); + when(mock.delete(key: key)).thenThrow(exceptionToThrow); } }