From 7e01d74ea3477a9f5f05018e54e5fd16d95874bb Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 14:43:44 +0100 Subject: [PATCH 01/40] Fixed log --- lib/widgets/token_widgets.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/token_widgets.dart b/lib/widgets/token_widgets.dart index fd5a3a76c..7a8c818f1 100644 --- a/lib/widgets/token_widgets.dart +++ b/lib/widgets/token_widgets.dart @@ -772,7 +772,7 @@ class _TotpWidgetState extends _OTPTokenWidgetState SystemChannels.lifecycle.setMessageHandler((msg) { log( "SystemChannels:", - name: "totpwidget.dart", + name: "totp_widgets.dart", error: msg, ); if (msg == AppLifecycleState.resumed.toString()) { From 7af3368b3011075258a10a9fe0f0275c0220c099 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 14:43:56 +0100 Subject: [PATCH 02/40] Removed code obfuscation --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 5df3c0042..6ea401750 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -69,11 +69,11 @@ android { // Enables code shrinking, obfuscation, and optimization for only // your project's release build type. - minifyEnabled true + minifyEnabled false // Enables resource shrinking, which is performed by the // Android Gradle plugin. - shrinkResources true + shrinkResources false // Includes the default ProGuard rules files that are packaged with // the Android Gradle plugin. To learn more, go to the section about From 0f727a25e38a924accb7198c363b63d925e66d0a Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 14:51:16 +0100 Subject: [PATCH 03/40] Use 0-padded otp string from plugin --- lib/utils/utils.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 8bc9c26fd..78a2220ef 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -89,35 +89,35 @@ bool isValidEncoding(String secret, Encodings encoding) { } String calculateHotpValue(HOTPToken token) { - return "${OTPLibrary.OTP.generateHOTPCode( + return OTPLibrary.OTP.generateHOTPCodeString( token.secret, token.counter, length: token.digits, algorithm: _mapAlgorithms(token.algorithm), - )}"; + ); } // TODO test this method, may use mockito for 'faking' the system time String calculateTotpValue(TOTPToken token) { - return "${OTPLibrary.OTP.generateTOTPCode( + return OTPLibrary.OTP.generateTOTPCodeString( token.secret, DateTime.now().millisecondsSinceEpoch, length: token.digits, algorithm: _mapAlgorithms(token.algorithm), interval: token.period, isGoogle: true, - )}"; + ); } String calculateOtpValue(OTPToken token) { if (token is HOTPToken) { - return calculateHotpValue(token).padLeft(token.digits, '0'); + return calculateHotpValue(token); } else if (token is TOTPToken) { - return calculateTotpValue(token).padLeft(token.digits, '0'); + return calculateTotpValue(token); } throw ArgumentError.value(token, "token", - "The token kind of $token is not supported by this method"); + "The token kind of $token is not supported by this method."); } OTPLibrary.Algorithm _mapAlgorithms(Algorithms algorithm) { @@ -132,7 +132,7 @@ OTPLibrary.Algorithm _mapAlgorithms(Algorithms algorithm) { return OTPLibrary.Algorithm.SHA512; default: throw ArgumentError.value(algorithm, "algorithmName", - "This algortihm is unknown and not supported!"); + "This algorithm is unknown and not supported!"); } } From 72d4909a6775a596a2dcb6709934d6fd9008ebad Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 15:34:04 +0100 Subject: [PATCH 04/40] movd utility functions around --- lib/screens/custom_about_screen.dart | 6 +- lib/screens/main_screen.dart | 1 + lib/utils/network_utils.dart | 89 ++++++++++ lib/utils/parsing_utils.dart | 20 +++ lib/utils/utils.dart | 165 ++++++------------ lib/widgets/token_widgets.dart | 1 + lib/widgets/update_firebase_token_dialog.dart | 2 +- 7 files changed, 168 insertions(+), 116 deletions(-) create mode 100644 lib/utils/network_utils.dart diff --git a/lib/screens/custom_about_screen.dart b/lib/screens/custom_about_screen.dart index cd4c74ff6..6fbe9b5cf 100644 --- a/lib/screens/custom_about_screen.dart +++ b/lib/screens/custom_about_screen.dart @@ -29,8 +29,10 @@ import 'package:url_launcher/url_launcher.dart'; class CustomLicenseScreen extends StatefulWidget { final String applicationName = "PrivacyIDEA Authenticator"; - final Widget applicationIcon = - SvgPicture.asset('res/logo/app_logo_light.svg', width: 330,); + final Widget applicationIcon = SvgPicture.asset( + 'res/logo/app_logo_light.svg', + width: 330, + ); final String applicationLegalese = "Apache License 2.0"; final Uri gitHubLink = Uri.parse("https://github.com/privacyidea/pi-authenticator"); diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 4b86fec60..14ebd5031 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -46,6 +46,7 @@ import 'package:privacyidea_authenticator/utils/crypto_utils.dart'; import 'package:privacyidea_authenticator/utils/identifiers.dart'; import 'package:privacyidea_authenticator/utils/license_utils.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; +import 'package:privacyidea_authenticator/utils/network_utils.dart'; import 'package:privacyidea_authenticator/utils/parsing_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; import 'package:privacyidea_authenticator/utils/utils.dart'; diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart new file mode 100644 index 000000000..7ed20f595 --- /dev/null +++ b/lib/utils/network_utils.dart @@ -0,0 +1,89 @@ +/* + privacyIDEA Authenticator + + Authors: Timo Sturm + + Copyright (c) 2017-2021 NetKnights GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:developer'; +import 'dart:io'; + +import 'package:http/http.dart'; +import 'package:http/io_client.dart'; + +import 'identifiers.dart'; + +/// Custom POST request allows to not verify certificates +Future doPost( + {bool sslVerify, Uri url, Map body}) async { + log("Sending post request", + name: "utils.dart", + error: "URI: $url, SSLVerify: $sslVerify, Body: $body"); + + if (body.entries.any((element) => element.value == null)) { + throw ArgumentError( + "Can not send request because the [body] contains a null value," + " this is not permitted."); + } + + HttpClient httpClient = HttpClient(); + httpClient.badCertificateCallback = + ((X509Certificate cert, String host, int port) => !sslVerify); + httpClient.userAgent = USER_AGENT_STRING; + + IOClient ioClient = IOClient(httpClient); + + Response response = await ioClient.post(url, body: body); + + log("Received response", + name: "utils.dart", + error: 'Status code: ${response.statusCode}\n Body: ${response.body}'); + + ioClient.close(); + + return response; +} + +Future doGet( + {Uri url, Map parameters, bool sslVerify = true}) async { + ArgumentError.checkNotNull( + sslVerify, 'Parameter [sslVerify] must not be null!'); + + HttpClient httpClient = HttpClient(); + httpClient.badCertificateCallback = + ((X509Certificate cert, String host, int port) => !sslVerify); + httpClient.userAgent = USER_AGENT_STRING; + + IOClient ioClient = IOClient(httpClient); + // TODO Make this more general! + // TODO Are the parameters the headers? + String urlWithParameters = '$url?serial=${parameters['serial']}' + '×tamp=${parameters['timestamp']}' + '&signature=${parameters['signature']}'; + Response response = await ioClient.get(urlWithParameters); + +// String urlWithParameters = '$url'; +// parameters.forEach((key, value) => urlWithParameters += '&$key=$value'); +// print('$urlWithParameters'); +// Response response = await ioClient.get(urlWithParameters); + + log("Received response", + name: "utils.dart", + error: 'Status code: ${response.statusCode}\n Body: ${response.body}'); + + ioClient.close(); + return response; +} diff --git a/lib/utils/parsing_utils.dart b/lib/utils/parsing_utils.dart index faa423b47..502c75a2c 100644 --- a/lib/utils/parsing_utils.dart +++ b/lib/utils/parsing_utils.dart @@ -1,3 +1,23 @@ +/* + privacyIDEA Authenticator + + Authors: Timo Sturm + + Copyright (c) 2017-2021 NetKnights GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + import 'dart:convert'; import 'dart:developer'; import 'dart:typed_data'; diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 78a2220ef..214bc01c8 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -21,18 +21,67 @@ import 'dart:convert'; import 'dart:core'; import 'dart:developer'; -import 'dart:io'; import 'dart:typed_data'; import 'package:base32/base32.dart' as Base32Converter; import 'package:hex/hex.dart' as HexConverter; -import 'package:http/http.dart'; -import 'package:http/io_client.dart'; import 'package:otp/otp.dart' as OTPLibrary; import 'package:privacyidea_authenticator/model/tokens.dart'; import 'identifiers.dart'; +/// Inserts [char] at the position [pos] in the given String ([str]), +/// and returns the resulting String. +/// +/// Example: insertCharAt("ABCD", " ", 2) --> "AB CD" +String insertCharAt(String str, String char, int pos) { + return str.substring(0, pos) + char + str.substring(pos, str.length); +} + +/// Inserts [' '] after every [period] characters in [str]. +/// Trims leading and trailing whitespaces. Returns the resulting String. +/// +/// Example: "ABCD", 1 --> "A B C D" +/// Example: "ABCD", 2 --> "AB CD" +String splitPeriodically(String str, int period) { + String result = ""; + for (int i = 0; i < str.length; i++) { + i % 4 == 0 ? result += " ${str[i]}" : result += str[i]; + } + + return result.trim(); +} + +Algorithms mapStringToAlgorithm(String algoAsString) { + for (Algorithms alg in Algorithms.values) { + if (equalsIgnoreCase(enumAsString(alg), algoAsString)) { + return alg; + } + } + + throw ArgumentError.value(algoAsString, "algorAsString", + "$algoAsString cannot be mapped to $Algorithms"); +} + +/// This implementation is taken from the library +/// [foundation](https://api.flutter.dev/flutter/foundation/describeEnum.html). +/// That library sadly depends on [dart.ui] and thus cannot be used in tests. +/// Therefor only using this code enables us to use this library ([utils.dart]) +/// in tests. +String enumAsString(Object enumEntry) { + final String description = enumEntry.toString(); + final int indexOfDot = description.indexOf('.'); + assert(indexOfDot != -1 && indexOfDot < description.length - 1); + return description.substring(indexOfDot + 1); +} + +bool equalsIgnoreCase(String s1, String s2) { + return s1.toLowerCase() == s2.toLowerCase(); +} + +// TODO Everything after this line should be in 'crypto_utils.dart, +// but that depends on foundations.dart and that depends on dart.ui, +// which ultimately makes it impossible to run driver tests. Uint8List decodeSecretToUint8(String secret, Encodings encoding) { ArgumentError.checkNotNull(secret, "secret"); ArgumentError.checkNotNull(encoding, "encoding"); @@ -135,113 +184,3 @@ OTPLibrary.Algorithm _mapAlgorithms(Algorithms algorithm) { "This algorithm is unknown and not supported!"); } } - -/// Inserts [char] at the position [pos] in the given String ([str]), -/// and returns the resulting String. -/// -/// Example: insertCharAt("ABCD", " ", 2) --> "AB CD" -String insertCharAt(String str, String char, int pos) { - return str.substring(0, pos) + char + str.substring(pos, str.length); -} - -/// Inserts [' '] after every [period] characters in [str]. -/// Trims leading and trailing whitespaces. Returns the resulting String. -/// -/// Example: "ABCD", 1 --> "A B C D" -/// Example: "ABCD", 2 --> "AB CD" -String splitPeriodically(String str, int period) { - String result = ""; - for (int i = 0; i < str.length; i++) { - i % 4 == 0 ? result += " ${str[i]}" : result += str[i]; - } - - return result.trim(); -} - -Algorithms mapStringToAlgorithm(String algoAsString) { - for (Algorithms alg in Algorithms.values) { - if (equalsIgnoreCase(enumAsString(alg), algoAsString)) { - return alg; - } - } - - throw ArgumentError.value(algoAsString, "algorAsString", - "$algoAsString cannot be mapped to $Algorithms"); -} - -/// This implementation is taken from the library -/// [foundation](https://api.flutter.dev/flutter/foundation/describeEnum.html). -/// That library sadly depends on [dart.ui] and thus cannot be used in tests. -/// Therefor only using this code enables us to use this library ([utils.dart]) -/// in tests. -String enumAsString(Object enumEntry) { - final String description = enumEntry.toString(); - final int indexOfDot = description.indexOf('.'); - assert(indexOfDot != -1 && indexOfDot < description.length - 1); - return description.substring(indexOfDot + 1); -} - -bool equalsIgnoreCase(String s1, String s2) => - s1.toLowerCase() == s2.toLowerCase(); - -/// Custom POST request allows to not verify certificates -Future doPost( - {bool sslVerify, Uri url, Map body}) async { - log("Sending post request", - name: "utils.dart", - error: "URI: $url, SSLVerify: $sslVerify, Body: $body"); - - if (body.entries.any((element) => element.value == null)) { - throw ArgumentError( - "Can not send request because the [body] contains a null value," - " this is not permitted."); - } - - HttpClient httpClient = HttpClient(); - httpClient.badCertificateCallback = - ((X509Certificate cert, String host, int port) => !sslVerify); - httpClient.userAgent = USER_AGENT_STRING; - - IOClient ioClient = IOClient(httpClient); - - Response response = await ioClient.post(url, body: body); - - log("Received response", - name: "utils.dart", - error: 'Status code: ${response.statusCode}\n Body: ${response.body}'); - - ioClient.close(); - - return response; -} - -Future doGet( - {Uri url, Map parameters, bool sslVerify = true}) async { - ArgumentError.checkNotNull( - sslVerify, 'Parameter [sslVerify] must not be null!'); - - HttpClient httpClient = HttpClient(); - httpClient.badCertificateCallback = - ((X509Certificate cert, String host, int port) => !sslVerify); - httpClient.userAgent = USER_AGENT_STRING; - - IOClient ioClient = IOClient(httpClient); - // TODO Make this more general! - // TODO Are the parameters the headers? - String urlWithParameters = '$url?serial=${parameters['serial']}' - '×tamp=${parameters['timestamp']}' - '&signature=${parameters['signature']}'; - Response response = await ioClient.get(urlWithParameters); - -// String urlWithParameters = '$url'; -// parameters.forEach((key, value) => urlWithParameters += '&$key=$value'); -// print('$urlWithParameters'); -// Response response = await ioClient.get(urlWithParameters); - - log("Received response", - name: "utils.dart", - error: 'Status code: ${response.statusCode}\n Body: ${response.body}'); - - ioClient.close(); - return response; -} diff --git a/lib/widgets/token_widgets.dart b/lib/widgets/token_widgets.dart index 7a8c818f1..992fa0d95 100644 --- a/lib/widgets/token_widgets.dart +++ b/lib/widgets/token_widgets.dart @@ -38,6 +38,7 @@ import 'package:privacyidea_authenticator/screens/main_screen.dart'; import 'package:privacyidea_authenticator/utils/application_theme_utils.dart'; import 'package:privacyidea_authenticator/utils/crypto_utils.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; +import 'package:privacyidea_authenticator/utils/network_utils.dart'; import 'package:privacyidea_authenticator/utils/parsing_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; import 'package:privacyidea_authenticator/utils/utils.dart'; diff --git a/lib/widgets/update_firebase_token_dialog.dart b/lib/widgets/update_firebase_token_dialog.dart index 1186ebfc3..2bd30e1ed 100644 --- a/lib/widgets/update_firebase_token_dialog.dart +++ b/lib/widgets/update_firebase_token_dialog.dart @@ -29,8 +29,8 @@ import 'package:pi_authenticator_legacy/pi_authenticator_legacy.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; import 'package:privacyidea_authenticator/utils/crypto_utils.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; +import 'package:privacyidea_authenticator/utils/network_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; -import 'package:privacyidea_authenticator/utils/utils.dart'; class UpdateFirebaseTokenDialog extends StatefulWidget { @override From 663b94b7ce23b4f5e022cfec3825acb4cfde2709 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 15:45:47 +0100 Subject: [PATCH 05/40] Changed spelling for privacyIDEA in guide --- res/md/GUIDE_de.md | 4 ++-- res/md/GUIDE_en.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/res/md/GUIDE_de.md b/res/md/GUIDE_de.md index 92c46999e..2f11d936a 100644 --- a/res/md/GUIDE_de.md +++ b/res/md/GUIDE_de.md @@ -1,4 +1,4 @@ -# Willkommen im PrivacyIDEA Authenticator +# Willkommen im privacyIDEA Authenticator Hier wird Ihnen ein kurzer Einblick in die Funktionen der App gegeben. + Das Menü ist über den Schalter `⋮` oder `…` in der oberen rechten Ecke erreichbar, @@ -14,7 +14,7 @@ eingescannt werden indem der `+` Schalter in der unteren rechten Ecke gedrückt + Das Umbenennen und Löschen von Token ist erreichbar, indem Sie sie nach links wischen: ![Renaming and deleting tokens by swiping left](resource:res/gif/help_delete_rename.gif) -# PrivacyIDEA Push Token: +# privacyIDEA Push Token: + Ausstehende Push-Anfragen können manuell abgefragt werden, indem Sie nach unten wischen: ![Manually polling by swiping down](resource:res/gif/help_manual_poll.gif) diff --git a/res/md/GUIDE_en.md b/res/md/GUIDE_en.md index dc0c48048..399e0a877 100644 --- a/res/md/GUIDE_en.md +++ b/res/md/GUIDE_en.md @@ -1,4 +1,4 @@ -# Welcome to the PrivacyIDEA Authenticator. +# Welcome to the privacyIDEA Authenticator. This screen gives you a quick introduction to the app. + The menu button `⋮` or `…` in the upper right corner of the screen gives access to several things: @@ -13,7 +13,7 @@ in the lower right corner of the screen. + Tokens can be deleted and renamed by swiping left and clicking the corresponding button: ![Renaming and deleting tokens by swiping left](resource:res/gif/help_delete_rename.gif) -# PrivacyIDEA Push Token: +# privacyIDEA Push Token: + If privacyIDEAs push tokens are used, pending challenges can be polled by swiping down: ![Manually polling by swiping down](resource:res/gif/help_manual_poll.gif) From 0a1f44cf5b2396f28f3ef37b6178918c1d4a2e55 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 15:51:41 +0100 Subject: [PATCH 06/40] changed spelling of privacyIDEA on about screen --- lib/screens/custom_about_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/screens/custom_about_screen.dart b/lib/screens/custom_about_screen.dart index 6fbe9b5cf..779f633ba 100644 --- a/lib/screens/custom_about_screen.dart +++ b/lib/screens/custom_about_screen.dart @@ -28,7 +28,7 @@ import 'package:privacyidea_authenticator/utils/localization_utils.dart'; import 'package:url_launcher/url_launcher.dart'; class CustomLicenseScreen extends StatefulWidget { - final String applicationName = "PrivacyIDEA Authenticator"; + final String applicationName = "privacyIDEA Authenticator"; final Widget applicationIcon = SvgPicture.asset( 'res/logo/app_logo_light.svg', width: 330, From 1f7b9793837da0fc2f2379daef48afd952290330 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 21 Jan 2021 17:19:39 +0100 Subject: [PATCH 07/40] Added first draft for synchronizing progress bars --- lib/widgets/token_widgets.dart | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/widgets/token_widgets.dart b/lib/widgets/token_widgets.dart index fd5a3a76c..ecb239f39 100644 --- a/lib/widgets/token_widgets.dart +++ b/lib/widgets/token_widgets.dart @@ -746,6 +746,15 @@ class _TotpWidgetState extends _OTPTokenWidgetState void initState() { super.initState(); + /// Calculate the progress of the LinearProgressIndicator depending on the + /// current time. The Indicator takes values in [0.0, 1.0]. + double getCurrentProgress() { + // FIXME The tokens are still slightly out of sync! + int unixTime = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; + + return (unixTime % (_token.period)) * (1 / _token.period); + } + controller = AnimationController( duration: Duration(seconds: _token.period), // Animate the progress for the duration of the tokens period. @@ -761,22 +770,23 @@ class _TotpWidgetState extends _OTPTokenWidgetState ..addStatusListener((status) { // Add listener to restart the animation after the period, also updates the otp value. if (status == AnimationStatus.completed) { - controller.forward(from: 0.0); + controller.forward(from: getCurrentProgress()); _updateOtpValue(); } }) - ..forward(); // Start the animation. + ..forward(from: getCurrentProgress()); // Start the animation. // Update the otp value when the android app resumes, this prevents outdated otp values // ignore: missing_return SystemChannels.lifecycle.setMessageHandler((msg) { log( "SystemChannels:", - name: "totpwidget.dart", + name: "totp_widgets.dart", error: msg, ); if (msg == AppLifecycleState.resumed.toString()) { _updateOtpValue(); + controller.forward(from: getCurrentProgress()); } }); } From e360e34dc1890fce737e6f75a96aa89b9b9e5af8 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Sat, 30 Jan 2021 11:04:43 +0100 Subject: [PATCH 08/40] Show notification and play sound when app is open --- CHANGELOG.md | 7 +++++++ lib/screens/main_screen.dart | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5255ca696..15ec18f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [3.0.9] - 2021-xx-xx + +### Changed + +- Notification with sound is shown when the app is open now too + + ## [3.0.8] - 2021-01-07 ### Added diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 4b86fec60..13850df00 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -659,8 +659,7 @@ class _MainScreenState extends State { token.knownPushRequests.put(pushRequest.id); StorageUtil.saveOrReplaceToken(token); // Save the pending request. - - if (inBackground) _showNotification(token, pushRequest, false); + _showNotification(token, pushRequest, false); } else { log( "The push request $pushRequest already exists " From ac254221cb3d7ef05d7971b07923b40057eaf53f Mon Sep 17 00:00:00 2001 From: timosturm <54933730+timosturm@users.noreply.github.com> Date: Sat, 30 Jan 2021 11:42:38 +0100 Subject: [PATCH 09/40] Update flutter_driver.yml --- .github/workflows/flutter_driver.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter_driver.yml b/.github/workflows/flutter_driver.yml index d44956104..168189fd7 100644 --- a/.github/workflows/flutter_driver.yml +++ b/.github/workflows/flutter_driver.yml @@ -13,7 +13,7 @@ jobs: strategy: # When set to true, GitHub cancels all in-progress jobs if any # matrix job fails. - fail-fast: true + fail-fast: false matrix: api-level: [21, 28, 29] # [minSdk, most used, newest] 18 would be minSDK but does not support x86_64 target: [default] # [default, google_apis] @@ -34,6 +34,6 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: x86_64 - profile: Nexus 6 + profile: Pixel 3 script: "flutter driver --dart-define=testing_mode=true --target=test_driver/run_all.dart" From 6b52e558676f6e59d6e9b43762a007c41a36e0d8 Mon Sep 17 00:00:00 2001 From: timosturm <54933730+timosturm@users.noreply.github.com> Date: Sat, 30 Jan 2021 12:00:57 +0100 Subject: [PATCH 10/40] Update flutter_driver.yml --- .github/workflows/flutter_driver.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter_driver.yml b/.github/workflows/flutter_driver.yml index 168189fd7..1b2fe21d2 100644 --- a/.github/workflows/flutter_driver.yml +++ b/.github/workflows/flutter_driver.yml @@ -34,6 +34,6 @@ jobs: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} arch: x86_64 - profile: Pixel 3 + profile: Nexus 6 script: "flutter driver --dart-define=testing_mode=true --target=test_driver/run_all.dart" From 0e3e21a9a4f84cb857c5183c48ebfa0c0449a78b Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Sat, 30 Jan 2021 13:30:01 +0100 Subject: [PATCH 11/40] updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15ec18f86..896403a1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changed - Notification with sound is shown when the app is open now too +- Synchronized progress indicator of totp tokens ## [3.0.8] - 2021-01-07 From a24c36aa0170b3722f93028a2eed1bd849ac7021 Mon Sep 17 00:00:00 2001 From: timosturm <54933730+timosturm@users.noreply.github.com> Date: Thu, 4 Feb 2021 10:55:25 +0100 Subject: [PATCH 12/40] Update lib/widgets/token_widgets.dart --- lib/widgets/token_widgets.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/widgets/token_widgets.dart b/lib/widgets/token_widgets.dart index d45d6a62b..cc88da157 100644 --- a/lib/widgets/token_widgets.dart +++ b/lib/widgets/token_widgets.dart @@ -750,7 +750,6 @@ class _TotpWidgetState extends _OTPTokenWidgetState /// Calculate the progress of the LinearProgressIndicator depending on the /// current time. The Indicator takes values in [0.0, 1.0]. double getCurrentProgress() { - // FIXME The tokens are still slightly out of sync! int unixTime = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000; return (unixTime % (_token.period)) * (1 / _token.period); From b06f0dde59e581706200a748b94cf2ba4e5a43fe Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 4 Feb 2021 11:16:59 +0100 Subject: [PATCH 13/40] Changed Version number in change log --- CHANGELOG.md | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 896403a1d..2df150df3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.0.9] - 2021-xx-xx +## [3.0.11] - 2021-xx-xx ### Changed diff --git a/pubspec.yaml b/pubspec.yaml index 9d156c036..725e324f6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ repository: https://github.com/privacyidea/pi-authenticator # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 3.0.8+0300008 # TODO Set the right version number -# version: major.minor.build + 2x major|2x minor|3x build <- 2 per number +# version: major.minor.build + 2x major|2x minor|3x build # version: version number + build number (optional) # android: build-name + versionCode # iOS : CFBundleShortVersionString + CFBundleVersion From 4669674bc46335943bae4e0541d4959cb206d9f2 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 4 Feb 2021 11:50:45 +0100 Subject: [PATCH 14/40] fixed handling of connection errors, dialog is dismissable at all times --- lib/screens/settings_screen.dart | 6 ++- lib/utils/localization_utils.dart | 10 ++++ lib/widgets/update_firebase_token_dialog.dart | 50 ++++++++++++------- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 73888d274..87eaafad0 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -40,12 +40,15 @@ class SettingsScreen extends StatefulWidget { } class SettingsScreenState extends State { + final GlobalKey _scaffoldKey = GlobalKey(); + @override Widget build(BuildContext context) { bool isSystemDarkMode = MediaQuery.of(context).platformBrightness == Brightness.dark; return Scaffold( + key: _scaffoldKey, appBar: AppBar( title: Text( widget._title, @@ -116,7 +119,8 @@ class SettingsScreenState extends State { onPressed: () => showDialog( context: context, barrierDismissible: false, - builder: (context) => UpdateFirebaseTokenDialog(), + builder: (context) => UpdateFirebaseTokenDialog( + scaffoldKey: _scaffoldKey), ), ), ), diff --git a/lib/utils/localization_utils.dart b/lib/utils/localization_utils.dart index 9cf01108c..fa5f8916c 100644 --- a/lib/utils/localization_utils.dart +++ b/lib/utils/localization_utils.dart @@ -496,6 +496,16 @@ class Localization { ); } + String get errorSynchronizationNoNetworkConnection { + return Intl.message( + 'Synchronizing tokens failed, privacyIDEA server could not be reached.', + name: 'errorSynchronizationNoNetworkConnection', + desc: 'Tells the user that synchronizing the push tokens failed because ' + 'the server could not be reached.', + locale: localeName, + ); + } + String get errorRollOutNoNetworkConnection { return Intl.message( "No network connection. Roll-out not possible.", diff --git a/lib/widgets/update_firebase_token_dialog.dart b/lib/widgets/update_firebase_token_dialog.dart index 2bd30e1ed..1670e9cb2 100644 --- a/lib/widgets/update_firebase_token_dialog.dart +++ b/lib/widgets/update_firebase_token_dialog.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'dart:developer'; +import 'dart:io'; import 'dart:ui'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -33,17 +34,23 @@ import 'package:privacyidea_authenticator/utils/network_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; class UpdateFirebaseTokenDialog extends StatefulWidget { + final GlobalKey + _scaffoldKey; // Used to display messages to user. + + const UpdateFirebaseTokenDialog( + {Key key, GlobalKey scaffoldKey}) + : this._scaffoldKey = scaffoldKey, + super(key: key); + @override State createState() => _UpdateFirebaseTokenDialogState(); } class _UpdateFirebaseTokenDialogState extends State { - String _title; Widget _content = Row( mainAxisAlignment: MainAxisAlignment.center, children: [CircularProgressIndicator()], ); - bool _showDismiss = false; @override void initState() { @@ -59,12 +66,9 @@ class _UpdateFirebaseTokenDialogState extends State { title: Text(Localization.of(context).synchronizePushDialogTitle), content: _content, actions: [ - Visibility( - visible: _showDismiss, - child: RaisedButton( - child: Text(Localization.of(context).dismiss), - onPressed: () => Navigator.pop(context), - ), + RaisedButton( + child: Text(Localization.of(context).dismiss), + onPressed: () => Navigator.pop(context), ), ], ), @@ -104,15 +108,27 @@ class _UpdateFirebaseTokenDialogState extends State { ? await Legacy.sign(p.serial, message) : createBase32Signature(p.getPrivateTokenKey(), utf8.encode(message)); - Response response = - await doPost(sslVerify: p.sslVerify, url: p.url, body: { - 'new_fb_token': token, - 'serial': p.serial, - 'timestamp': timestamp, - 'signature': signature - }); + Response response; + try { + response = await doPost(sslVerify: p.sslVerify, url: p.url, body: { + 'new_fb_token': token, + 'serial': p.serial, + 'timestamp': timestamp, + 'signature': signature + }); + } on SocketException catch (e) { + log('Socket exception occurred: $e', + name: 'update_firebase_token_dialog.dart'); + widget._scaffoldKey.currentState.showSnackBar(SnackBar( + content: Text( + Localization.of(context).errorSynchronizationNoNetworkConnection), + duration: Duration(seconds: 3), + )); + Navigator.pop(context); + return; + } - if (response.statusCode == 200) { + if (response != null && response.statusCode == 200) { log('Updating firebase token for push token: ${p.serial} succeeded!', name: 'update_firebase_token_dialog.dart'); } else { @@ -125,7 +141,6 @@ class _UpdateFirebaseTokenDialogState extends State { if (tokenWithFailedUpdate.isEmpty && tokenWithOutUrl.isEmpty) { setState(() { _content = Text(Localization.of(context).allTokensSynchronized); - _showDismiss = true; }); } else { List children = []; @@ -164,7 +179,6 @@ class _UpdateFirebaseTokenDialogState extends State { ), ), ); - _showDismiss = true; }); } } From 608bdeac004c464013777c97cd3ce7707931fa55 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 4 Feb 2021 11:50:55 +0100 Subject: [PATCH 15/40] added to changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df150df3..6f9771bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Notification with sound is shown when the app is open now too - Synchronized progress indicator of totp tokens +- Synchronization of push tokens can be canceled + +### Fixed + +- Handle failing synchronization of push tokens by informing the user and closing the dialog ## [3.0.8] - 2021-01-07 From c254938b5509cad12abf49a54216224c6e7a66a7 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 4 Feb 2021 12:58:01 +0100 Subject: [PATCH 16/40] added translations --- CHANGELOG.md | 4 +++ lib/l10n/intl_de.arb | 54 +++++++++++++++++++++++++++++ lib/l10n/intl_en.arb | 60 +++++++++++++++++++++++++++++++-- lib/l10n/intl_messages.arb | 60 +++++++++++++++++++++++++++++++-- lib/l10n/messages_de.dart | 9 +++++ lib/l10n/messages_en.dart | 9 +++++ lib/l10n/messages_messages.dart | 9 +++++ 7 files changed, 199 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9771bf8..37afe1c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [3.0.11] - 2021-xx-xx +### Added + +- Added missing german translations for synchronizing push tokens + ### Changed - Notification with sound is shown when the app is open now too diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index ac38921ba..55f45cdc0 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -367,5 +367,59 @@ "description": "Description for checkbox, if the checkbox is ticked, the guide screen is shown on every app start.", "type": "text", "placeholders": {} + }, + "errorSynchronizationNoNetworkConnection": "Die Synchronisation ist fehlgeschlagen, da der privacyIDEA Server nicht erreicht werden konnte.", + "@errorSynchronizationNoNetworkConnection": { + "description": "Tells the user that synchronizing the push tokens failed because the server could not be reached.", + "type": "text", + "placeholders": {} + }, + "Push Token": "Push Token", + "@Push Token": { + "description": "Title for the settings block concerning the push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronize push tokens": "Synchronisiere Push Tokens", + "@Synchronize push tokens": { + "description": "Title of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Synchronizes tokens with the privacyIDEA server.": "Synchronisiert Push Tokens mit dem privacyIDEA Server.", + "@Synchronizes tokens with the privacyIDEA server.": { + "description": "Description of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Sync": "Sync", + "@Sync": { + "description": "Text of button that is used to synchronize push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronizing tokens.": "Synchronisiere Tokens.", + "@Synchronizing tokens.": { + "description": "Title of the push synchronization dialog.", + "type": "text", + "placeholders": {} + }, + "All tokens are synchronized.": "Alle Token wurden synchronisiert.", + "@All tokens are synchronized.": { + "description": "Content of the push synchronization dialog. Signaling the user that everything worked.", + "type": "text", + "placeholders": {} + }, + "Synchronization failed for the following tokens, please try again:": "Synchronisation ist für die folgenden Tokens fehlgeschlagen, bitte versuchen Sie es erneut:", + "@Synchronization failed for the following tokens, please try again:": { + "description": "Headline for the list of tokens where the synchronization failed.", + "type": "text", + "placeholders": {} + }, + "The following tokens do not support synchronization and must be rolled out again:": "Die folgenden Tokens unterstützen keine Synchronisation und müssen neu ausgerollt werden:", + "@The following tokens do not support synchronization and must be rolled out again:": { + "description": "Informs the user that the following tokens cannot be synchronized as they do not support that.", + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 697015d57..e3b4a8d30 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2021-01-12T17:39:37.559359", + "@@last_modified": "2021-02-04T11:51:13.167335", "Guide": "Guide", "@Guide": { "description": "Button to open the guide screen.", @@ -62,7 +62,7 @@ }, "Digits": "Digits", "@Digits": { - "description": "Title of the dropdown button where the number of digits for the opt value is selecte.", + "description": "Title of the dropdown button where the number of digits for the opt value is selected.", "type": "text", "placeholders": {} }, @@ -206,6 +206,12 @@ "type": "text", "placeholders": {} }, + "Push Token": "Push Token", + "@Push Token": { + "description": "Title for the settings block concerning the push tokens.", + "type": "text", + "placeholders": {} + }, "Theme": "Theme", "@Theme": { "description": "Title of the setting group where the theme can be selected.", @@ -248,6 +254,48 @@ "type": "text", "placeholders": {} }, + "Synchronize push tokens": "Synchronize push tokens", + "@Synchronize push tokens": { + "description": "Title of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Synchronizes tokens with the privacyIDEA server.": "Synchronizes tokens with the privacyIDEA server.", + "@Synchronizes tokens with the privacyIDEA server.": { + "description": "Description of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Sync": "Sync", + "@Sync": { + "description": "Text of button that is used to synchronize push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronizing tokens.": "Synchronizing tokens.", + "@Synchronizing tokens.": { + "description": "Title of the push synchronization dialog.", + "type": "text", + "placeholders": {} + }, + "All tokens are synchronized.": "All tokens are synchronized.", + "@All tokens are synchronized.": { + "description": "Content of the push synchronization dialog. Signaling the user that everything worked.", + "type": "text", + "placeholders": {} + }, + "Synchronization failed for the following tokens, please try again:": "Synchronization failed for the following tokens, please try again:", + "@Synchronization failed for the following tokens, please try again:": { + "description": "Headline for the list of tokens where the synchronization failed.", + "type": "text", + "placeholders": {} + }, + "The following tokens do not support synchronization and must be rolled out again:": "The following tokens do not support synchronization and must be rolled out again:", + "@The following tokens do not support synchronization and must be rolled out again:": { + "description": "Informs the user that the following tokens cannot be synchronized as they do not support that.", + "type": "text", + "placeholders": {} + }, "errorOnlyOneFirebaseProjectIsSupported": "The firebase configuration of {name} differs from the one currently used by the app. Currently only one is supported.", "@errorOnlyOneFirebaseProjectIsSupported": { "description": "Tells the user that the token can not be used because it has a different firebase configuration than the current used configuration of the application.", @@ -270,7 +318,7 @@ }, "errorRollOutFailed": "Rolling out token {name} failed. Error code: {errorCode}", "@errorRollOutFailed": { - "description": "Tells the user that the token could not be rolled out, because a network error occured.", + "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { @@ -281,6 +329,12 @@ } } }, + "errorSynchronizationNoNetworkConnection": "Synchronizing tokens failed, privacyIDEA server could not be reached.", + "@errorSynchronizationNoNetworkConnection": { + "description": "Tells the user that synchronizing the push tokens failed because the server could not be reached.", + "type": "text", + "placeholders": {} + }, "errorRollOutNoNetworkConnection": "No network connection. Roll-out not possible.", "@errorRollOutNoNetworkConnection": { "description": "Tells the user that the roll-out failed because no network connection is available.", diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 697015d57..e3b4a8d30 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2021-01-12T17:39:37.559359", + "@@last_modified": "2021-02-04T11:51:13.167335", "Guide": "Guide", "@Guide": { "description": "Button to open the guide screen.", @@ -62,7 +62,7 @@ }, "Digits": "Digits", "@Digits": { - "description": "Title of the dropdown button where the number of digits for the opt value is selecte.", + "description": "Title of the dropdown button where the number of digits for the opt value is selected.", "type": "text", "placeholders": {} }, @@ -206,6 +206,12 @@ "type": "text", "placeholders": {} }, + "Push Token": "Push Token", + "@Push Token": { + "description": "Title for the settings block concerning the push tokens.", + "type": "text", + "placeholders": {} + }, "Theme": "Theme", "@Theme": { "description": "Title of the setting group where the theme can be selected.", @@ -248,6 +254,48 @@ "type": "text", "placeholders": {} }, + "Synchronize push tokens": "Synchronize push tokens", + "@Synchronize push tokens": { + "description": "Title of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Synchronizes tokens with the privacyIDEA server.": "Synchronizes tokens with the privacyIDEA server.", + "@Synchronizes tokens with the privacyIDEA server.": { + "description": "Description of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Sync": "Sync", + "@Sync": { + "description": "Text of button that is used to synchronize push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronizing tokens.": "Synchronizing tokens.", + "@Synchronizing tokens.": { + "description": "Title of the push synchronization dialog.", + "type": "text", + "placeholders": {} + }, + "All tokens are synchronized.": "All tokens are synchronized.", + "@All tokens are synchronized.": { + "description": "Content of the push synchronization dialog. Signaling the user that everything worked.", + "type": "text", + "placeholders": {} + }, + "Synchronization failed for the following tokens, please try again:": "Synchronization failed for the following tokens, please try again:", + "@Synchronization failed for the following tokens, please try again:": { + "description": "Headline for the list of tokens where the synchronization failed.", + "type": "text", + "placeholders": {} + }, + "The following tokens do not support synchronization and must be rolled out again:": "The following tokens do not support synchronization and must be rolled out again:", + "@The following tokens do not support synchronization and must be rolled out again:": { + "description": "Informs the user that the following tokens cannot be synchronized as they do not support that.", + "type": "text", + "placeholders": {} + }, "errorOnlyOneFirebaseProjectIsSupported": "The firebase configuration of {name} differs from the one currently used by the app. Currently only one is supported.", "@errorOnlyOneFirebaseProjectIsSupported": { "description": "Tells the user that the token can not be used because it has a different firebase configuration than the current used configuration of the application.", @@ -270,7 +318,7 @@ }, "errorRollOutFailed": "Rolling out token {name} failed. Error code: {errorCode}", "@errorRollOutFailed": { - "description": "Tells the user that the token could not be rolled out, because a network error occured.", + "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { @@ -281,6 +329,12 @@ } } }, + "errorSynchronizationNoNetworkConnection": "Synchronizing tokens failed, privacyIDEA server could not be reached.", + "@errorSynchronizationNoNetworkConnection": { + "description": "Tells the user that synchronizing the push tokens failed because the server could not be reached.", + "type": "text", + "placeholders": {} + }, "errorRollOutNoNetworkConnection": "No network connection. Roll-out not possible.", "@errorRollOutNoNetworkConnection": { "description": "Tells the user that the roll-out failed because no network connection is available.", diff --git a/lib/l10n/messages_de.dart b/lib/l10n/messages_de.dart index 398367bf3..59515259f 100644 --- a/lib/l10n/messages_de.dart +++ b/lib/l10n/messages_de.dart @@ -44,6 +44,7 @@ class MessageLookup extends MessageLookupByLibrary { "About" : MessageLookupByLibrary.simpleMessage("Über"), "Add token" : MessageLookupByLibrary.simpleMessage("Token hinzufügen"), "Algorithm" : MessageLookupByLibrary.simpleMessage("Algorithmus"), + "All tokens are synchronized." : MessageLookupByLibrary.simpleMessage("Alle Token wurden synchronisiert."), "Cancel" : MessageLookupByLibrary.simpleMessage("Abbrechen"), "Confirm deletion" : MessageLookupByLibrary.simpleMessage("Löschen bestätigen"), "Dark theme" : MessageLookupByLibrary.simpleMessage("Dunkles Thema"), @@ -64,6 +65,7 @@ class MessageLookup extends MessageLookupByLibrary { "Please enter a name for this token." : MessageLookupByLibrary.simpleMessage("Bitte geben Sie einen Namen für diesen Token ein."), "Please enter a secret for this token." : MessageLookupByLibrary.simpleMessage("Bitte geben Sie ein Geheimnis für diesen Token ein."), "Polling for new challenges" : MessageLookupByLibrary.simpleMessage("Frage ausstehende Authentifizierungsanfragen ab"), + "Push Token" : MessageLookupByLibrary.simpleMessage("Push Token"), "Rename" : MessageLookupByLibrary.simpleMessage("Umbenennen"), "Rename token" : MessageLookupByLibrary.simpleMessage("Token umbenennen"), "Request push challenges from the server periodically. Enable this if push challenges are not received normally." : MessageLookupByLibrary.simpleMessage("Fordert regelmäßig Push-Anfragen vom Server an. Aktivieren Sie diese Funktion, wenn Nachrichten ansonsten nicht erhalten werden."), @@ -74,7 +76,13 @@ class MessageLookup extends MessageLookupByLibrary { "Show this screen on start:" : MessageLookupByLibrary.simpleMessage("Zeige bei App-Start:"), "Some of the tokens are outdated and do not support polling" : MessageLookupByLibrary.simpleMessage("Einige der Token sind veraltet und unterstützen keine aktiven Anfragen"), "Something went wrong." : MessageLookupByLibrary.simpleMessage("Etwas ist schiefgelaufen."), + "Sync" : MessageLookupByLibrary.simpleMessage("Sync"), + "Synchronization failed for the following tokens, please try again:" : MessageLookupByLibrary.simpleMessage("Synchronisation ist für die folgenden Tokens fehlgeschlagen, bitte versuchen Sie es erneut:"), + "Synchronize push tokens" : MessageLookupByLibrary.simpleMessage("Synchronisiere Push Tokens"), + "Synchronizes tokens with the privacyIDEA server." : MessageLookupByLibrary.simpleMessage("Synchronisiert Push Tokens mit dem privacyIDEA Server."), + "Synchronizing tokens." : MessageLookupByLibrary.simpleMessage("Synchronisiere Tokens."), "The firebase configuration is corrupted and cannot be used." : MessageLookupByLibrary.simpleMessage("Die Firebase-Konfiguration ist beschädigt und kann nicht genutzt werden."), + "The following tokens do not support synchronization and must be rolled out again:" : MessageLookupByLibrary.simpleMessage("Die folgenden Tokens unterstützen keine Synchronisation und müssen neu ausgerollt werden:"), "The secret does not fit the current encoding" : MessageLookupByLibrary.simpleMessage("Das Geheimnis entspricht nicht der gewählten\n Verschlüsselung."), "Theme" : MessageLookupByLibrary.simpleMessage("Thema"), "Type" : MessageLookupByLibrary.simpleMessage("Art"), @@ -90,6 +98,7 @@ class MessageLookup extends MessageLookupByLibrary { "errorRollOutFailed" : m6, "errorRollOutNoNetworkConnection" : MessageLookupByLibrary.simpleMessage("Fehlende Netzwerkverbindung. Ausrollen nicht möglich."), "errorRollOutUnknownError" : m7, + "errorSynchronizationNoNetworkConnection" : MessageLookupByLibrary.simpleMessage("Die Synchronisation ist fehlgeschlagen, da der privacyIDEA Server nicht erreicht werden konnte."), "errorTokenExpired" : m8, "otpValueCopiedMessage" : m9, "retry" : MessageLookupByLibrary.simpleMessage("Erneut versuchen"), diff --git a/lib/l10n/messages_en.dart b/lib/l10n/messages_en.dart index 961aab9a2..8b3552cd6 100644 --- a/lib/l10n/messages_en.dart +++ b/lib/l10n/messages_en.dart @@ -44,6 +44,7 @@ class MessageLookup extends MessageLookupByLibrary { "About" : MessageLookupByLibrary.simpleMessage("About"), "Add token" : MessageLookupByLibrary.simpleMessage("Add token"), "Algorithm" : MessageLookupByLibrary.simpleMessage("Algorithm"), + "All tokens are synchronized." : MessageLookupByLibrary.simpleMessage("All tokens are synchronized."), "Cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), "Confirm deletion" : MessageLookupByLibrary.simpleMessage("Confirm deletion"), "Dark theme" : MessageLookupByLibrary.simpleMessage("Dark theme"), @@ -64,6 +65,7 @@ class MessageLookup extends MessageLookupByLibrary { "Please enter a name for this token." : MessageLookupByLibrary.simpleMessage("Please enter a name for this token."), "Please enter a secret for this token." : MessageLookupByLibrary.simpleMessage("Please enter a secret for this token."), "Polling for new challenges" : MessageLookupByLibrary.simpleMessage("Polling for new challenges"), + "Push Token" : MessageLookupByLibrary.simpleMessage("Push Token"), "Rename" : MessageLookupByLibrary.simpleMessage("Rename"), "Rename token" : MessageLookupByLibrary.simpleMessage("Rename token"), "Request push challenges from the server periodically. Enable this if push challenges are not received normally." : MessageLookupByLibrary.simpleMessage("Request push challenges from the server periodically. Enable this if push challenges are not received normally."), @@ -74,7 +76,13 @@ class MessageLookup extends MessageLookupByLibrary { "Show this screen on start:" : MessageLookupByLibrary.simpleMessage("Show this screen on start:"), "Some of the tokens are outdated and do not support polling" : MessageLookupByLibrary.simpleMessage("Some of the tokens are outdated and do not support polling"), "Something went wrong." : MessageLookupByLibrary.simpleMessage("Something went wrong."), + "Sync" : MessageLookupByLibrary.simpleMessage("Sync"), + "Synchronization failed for the following tokens, please try again:" : MessageLookupByLibrary.simpleMessage("Synchronization failed for the following tokens, please try again:"), + "Synchronize push tokens" : MessageLookupByLibrary.simpleMessage("Synchronize push tokens"), + "Synchronizes tokens with the privacyIDEA server." : MessageLookupByLibrary.simpleMessage("Synchronizes tokens with the privacyIDEA server."), + "Synchronizing tokens." : MessageLookupByLibrary.simpleMessage("Synchronizing tokens."), "The firebase configuration is corrupted and cannot be used." : MessageLookupByLibrary.simpleMessage("The firebase configuration is corrupted and cannot be used."), + "The following tokens do not support synchronization and must be rolled out again:" : MessageLookupByLibrary.simpleMessage("The following tokens do not support synchronization and must be rolled out again:"), "The secret does not fit the current encoding" : MessageLookupByLibrary.simpleMessage("The secret does not fit the current encoding"), "Theme" : MessageLookupByLibrary.simpleMessage("Theme"), "Type" : MessageLookupByLibrary.simpleMessage("Type"), @@ -90,6 +98,7 @@ class MessageLookup extends MessageLookupByLibrary { "errorRollOutFailed" : m6, "errorRollOutNoNetworkConnection" : MessageLookupByLibrary.simpleMessage("No network connection. Roll-out not possible."), "errorRollOutUnknownError" : m7, + "errorSynchronizationNoNetworkConnection" : MessageLookupByLibrary.simpleMessage("Synchronizing tokens failed, privacyIDEA server could not be reached."), "errorTokenExpired" : m8, "otpValueCopiedMessage" : m9, "retry" : MessageLookupByLibrary.simpleMessage("Retry"), diff --git a/lib/l10n/messages_messages.dart b/lib/l10n/messages_messages.dart index a30e4ae32..3ab7499fa 100644 --- a/lib/l10n/messages_messages.dart +++ b/lib/l10n/messages_messages.dart @@ -44,6 +44,7 @@ class MessageLookup extends MessageLookupByLibrary { "About" : MessageLookupByLibrary.simpleMessage("About"), "Add token" : MessageLookupByLibrary.simpleMessage("Add token"), "Algorithm" : MessageLookupByLibrary.simpleMessage("Algorithm"), + "All tokens are synchronized." : MessageLookupByLibrary.simpleMessage("All tokens are synchronized."), "Cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), "Confirm deletion" : MessageLookupByLibrary.simpleMessage("Confirm deletion"), "Dark theme" : MessageLookupByLibrary.simpleMessage("Dark theme"), @@ -64,6 +65,7 @@ class MessageLookup extends MessageLookupByLibrary { "Please enter a name for this token." : MessageLookupByLibrary.simpleMessage("Please enter a name for this token."), "Please enter a secret for this token." : MessageLookupByLibrary.simpleMessage("Please enter a secret for this token."), "Polling for new challenges" : MessageLookupByLibrary.simpleMessage("Polling for new challenges"), + "Push Token" : MessageLookupByLibrary.simpleMessage("Push Token"), "Rename" : MessageLookupByLibrary.simpleMessage("Rename"), "Rename token" : MessageLookupByLibrary.simpleMessage("Rename token"), "Request push challenges from the server periodically. Enable this if push challenges are not received normally." : MessageLookupByLibrary.simpleMessage("Request push challenges from the server periodically. Enable this if push challenges are not received normally."), @@ -74,7 +76,13 @@ class MessageLookup extends MessageLookupByLibrary { "Show this screen on start:" : MessageLookupByLibrary.simpleMessage("Show this screen on start:"), "Some of the tokens are outdated and do not support polling" : MessageLookupByLibrary.simpleMessage("Some of the tokens are outdated and do not support polling"), "Something went wrong." : MessageLookupByLibrary.simpleMessage("Something went wrong."), + "Sync" : MessageLookupByLibrary.simpleMessage("Sync"), + "Synchronization failed for the following tokens, please try again:" : MessageLookupByLibrary.simpleMessage("Synchronization failed for the following tokens, please try again:"), + "Synchronize push tokens" : MessageLookupByLibrary.simpleMessage("Synchronize push tokens"), + "Synchronizes tokens with the privacyIDEA server." : MessageLookupByLibrary.simpleMessage("Synchronizes tokens with the privacyIDEA server."), + "Synchronizing tokens." : MessageLookupByLibrary.simpleMessage("Synchronizing tokens."), "The firebase configuration is corrupted and cannot be used." : MessageLookupByLibrary.simpleMessage("The firebase configuration is corrupted and cannot be used."), + "The following tokens do not support synchronization and must be rolled out again:" : MessageLookupByLibrary.simpleMessage("The following tokens do not support synchronization and must be rolled out again:"), "The secret does not fit the current encoding" : MessageLookupByLibrary.simpleMessage("The secret does not fit the current encoding"), "Theme" : MessageLookupByLibrary.simpleMessage("Theme"), "Type" : MessageLookupByLibrary.simpleMessage("Type"), @@ -90,6 +98,7 @@ class MessageLookup extends MessageLookupByLibrary { "errorRollOutFailed" : m6, "errorRollOutNoNetworkConnection" : MessageLookupByLibrary.simpleMessage("No network connection. Roll-out not possible."), "errorRollOutUnknownError" : m7, + "errorSynchronizationNoNetworkConnection" : MessageLookupByLibrary.simpleMessage("Synchronizing tokens failed, privacyIDEA server could not be reached."), "errorTokenExpired" : m8, "otpValueCopiedMessage" : m9, "retry" : MessageLookupByLibrary.simpleMessage("Retry"), From 8a908b8228380e170e8e20bf8da9c79b3cd3afdf Mon Sep 17 00:00:00 2001 From: timosturm <54933730+timosturm@users.noreply.github.com> Date: Thu, 4 Feb 2021 13:35:53 +0100 Subject: [PATCH 17/40] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37afe1c2f..66d52a403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Added -- Added missing german translations for synchronizing push tokens +- Added missing German translations for synchronizing push tokens ### Changed @@ -74,4 +74,3 @@ [3.0.7]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.6...v3.0.7 [3.0.6]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.4...v3.0.6 [3.0.4]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.0...v3.0.4 - From 3e6ff00117fd2e77191fc64fc07ec8bcee03554c Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 4 Feb 2021 15:19:16 +0100 Subject: [PATCH 18/40] Parse uri encoded ascii characters on parsing --- lib/utils/parsing_utils.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/utils/parsing_utils.dart b/lib/utils/parsing_utils.dart index 502c75a2c..8f84f2816 100644 --- a/lib/utils/parsing_utils.dart +++ b/lib/utils/parsing_utils.dart @@ -237,7 +237,8 @@ Map parsePiAuth(Uri uri) { Map uriMap = Map(); uriMap[URI_TYPE] = uri.host; - uriMap[URI_ISSUER] = uri.queryParameters['issuer']; + + uriMap[URI_ISSUER] = Uri.decodeFull(uri.queryParameters['issuer']); // If we do not support the version of this piauth url, we can stop here. String pushVersionAsString = uri.queryParameters["v"]; @@ -323,7 +324,7 @@ Map parseOtpAuth(Uri uri) { // parse.host -> Type totp or hotp uriMap[URI_TYPE] = uri.host; - uriMap[URI_ISSUER] = uri.queryParameters['issuer']; + uriMap[URI_ISSUER] = Uri.decodeFull(uri.queryParameters['issuer']); // parse.path.substring(1) -> Label log("Key: [..] | Value: [..]"); @@ -465,7 +466,7 @@ Map parseOtpAuth(Uri uri) { } String _parseLabel(Uri uri) { - return uri.path.substring(1); + return Uri.decodeFull(uri.path.substring(1)); } bool is2StepURI(Uri uri) { From b0728df7776960d2f25399da0b84229df7de2d5d Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Thu, 4 Feb 2021 15:23:10 +0100 Subject: [PATCH 19/40] added changelog --- CHANGELOG.md | 4 ++++ lib/model/tokens.dart | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df150df3..34cc0c01e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Notification with sound is shown when the app is open now too - Synchronized progress indicator of totp tokens +### Fixed + +- Uri encoded characters (e.g. @ as %40) are now correctly displayed for token labels + ## [3.0.8] - 2021-01-07 diff --git a/lib/model/tokens.dart b/lib/model/tokens.dart index 647871d84..5b2e9e835 100644 --- a/lib/model/tokens.dart +++ b/lib/model/tokens.dart @@ -38,7 +38,7 @@ abstract class Token { String get tokenVersion => _tokenVersion; - String get label => _label; + String get label => Uri.decodeFull(_label); set label(String label) { this._label = label; @@ -46,7 +46,7 @@ abstract class Token { String get id => _id; - String get issuer => _issuer; + String get issuer => Uri.decodeFull(_issuer); Token(this._label, this._issuer, this._id, this.type); From 65c57e21f7fad63186126cee5f9a258dc5583b03 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 12:49:05 +0100 Subject: [PATCH 20/40] Removed automatic loading of legacy tokens --- lib/screens/main_screen.dart | 7 +------ lib/utils/storage_utils.dart | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 946d50b59..a3bf8026f 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -220,16 +220,11 @@ class _MainScreenState extends State { } _loadTokenList() async { - AppSettings settings = AppSettings.of(context); - - List l1 = - await StorageUtil.loadAllTokens(loadLegacy: settings.getLoadLegacy()); + List l1 = await StorageUtil.loadAllTokens(); // Prevent the list items from skipping around on ui updates l1.sort((a, b) => a.id.hashCode.compareTo(b.id.hashCode)); setState(() => this._tokenList = l1); - // Because we only want to load legacy tokens once: - settings.setLoadLegacy(false); } @override diff --git a/lib/utils/storage_utils.dart b/lib/utils/storage_utils.dart index 02fccc709..c56b90c48 100644 --- a/lib/utils/storage_utils.dart +++ b/lib/utils/storage_utils.dart @@ -47,8 +47,8 @@ class StorageUtil { /// Saves [token] securely on the device, if [token] already exists /// in the storage the existing value is overwritten. - static void saveOrReplaceToken(Token token) async => await _storage.write( - key: _GLOBAL_PREFIX + token.id, value: jsonEncode(token)); + static Future saveOrReplaceToken(Token token) async => await _storage + .write(key: _GLOBAL_PREFIX + token.id, value: jsonEncode(token)); static Future loadToken(String id) async => (await loadAllTokens()).firstWhere((t) => t.id == id, orElse: () => null); @@ -57,15 +57,15 @@ class StorageUtil { /// this device. /// If [loadLegacy] is set to true, will attempt to load old android and ios tokens. /// - static Future> loadAllTokens({bool loadLegacy = false}) async { - if (loadLegacy) { - // Load legacy tokens and add them to the storage. - List legacyTokens = await StorageUtil.loadAllTokensLegacy(); - - for (Token t in legacyTokens) { - await StorageUtil.saveOrReplaceToken(t); - } - } + static Future> loadAllTokens() async { +// if (loadLegacy) { +// // Load legacy tokens and add them to the storage. +// List legacyTokens = await StorageUtil.loadAllTokensLegacy(); +// +// for (Token t in legacyTokens) { +// await StorageUtil.saveOrReplaceToken(t); +// } +// } Map keyValueMap = await _storage.readAll(); From 046bb971909d5d7a488351424794dd8b8d936095 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 13:24:26 +0100 Subject: [PATCH 21/40] Added custom dialog to make migration and button in settings --- lib/screens/settings_screen.dart | 25 ++- lib/widgets/migrate_legacy_tokens_dialog.dart | 161 ++++++++++++++++++ 2 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 lib/widgets/migrate_legacy_tokens_dialog.dart diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 73888d274..0c0f59b42 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -26,6 +26,7 @@ import 'package:flutter/material.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; +import 'package:privacyidea_authenticator/widgets/migrate_legacy_tokens_dialog.dart'; import 'package:privacyidea_authenticator/widgets/settings_groups.dart'; import 'package:privacyidea_authenticator/widgets/update_firebase_token_dialog.dart'; import 'package:streaming_shared_preferences/streaming_shared_preferences.dart'; @@ -179,6 +180,21 @@ class SettingsScreenState extends State { ); }, ), + SettingsGroup( + title: 'Migration', + children: [ + ListTile( + title: Text('Migrate tokens from previous app version.'), + trailing: RaisedButton( + child: Text('Migrate'), + onPressed: () => showDialog( + context: context, + barrierDismissible: false, + builder: (context) => MigrateLegacyTokensDialog()), + ), + ), + ], + ), // Divider(), // SettingsGroup( @@ -240,9 +256,7 @@ class AppSettings extends InheritedWidget { // Preferences static String _prefHideOtps = 'KEY_HIDE_OTPS'; static String _prefEnablePoll = 'KEY_ENABLE_POLLING'; - static String _loadLegacyKey = 'KEY_LOAD_LEGACY'; static String _showGuideOnStartKey = 'KEY_SHOW_GUIDE_ON_START'; - final bool isTestMode; @override bool updateShouldNotify(InheritedWidget oldWidget) => true; @@ -254,7 +268,6 @@ class AppSettings extends InheritedWidget { : _hideOpts = preferences.getBool(_prefHideOtps, defaultValue: false), _enablePolling = preferences.getBool(_prefEnablePoll, defaultValue: false), - _loadLegacy = preferences.getBool(_loadLegacyKey, defaultValue: true), isTestMode = const bool.fromEnvironment('testing_mode', defaultValue: false), _showGuideOnStart = @@ -263,8 +276,8 @@ class AppSettings extends InheritedWidget { final Preference _hideOpts; final Preference _enablePolling; - final Preference _loadLegacy; final Preference _showGuideOnStart; + final bool isTestMode; Stream streamHideOpts() => _hideOpts; @@ -274,10 +287,6 @@ class AppSettings extends InheritedWidget { void setEnablePolling(bool value) => _enablePolling.setValue(value); - void setLoadLegacy(bool value) => _loadLegacy.setValue(value); - - bool getLoadLegacy() => _loadLegacy.getValue(); - bool get showGuideOnStart => _showGuideOnStart.getValue(); set showGuideOnStart(bool value) => _showGuideOnStart.setValue(value); diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart new file mode 100644 index 000000000..5504fa4f9 --- /dev/null +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -0,0 +1,161 @@ +/* + privacyIDEA Authenticator + + Authors: Timo Sturm + + Copyright (c) 2017-2021 NetKnights GmbH + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import 'dart:developer'; +import 'dart:ui'; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:privacyidea_authenticator/model/tokens.dart'; +import 'package:privacyidea_authenticator/utils/localization_utils.dart'; +import 'package:privacyidea_authenticator/utils/storage_utils.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MigrateLegacyTokensDialog extends StatefulWidget { + final Uri _githubLink = + Uri.parse('https://github.com/privacyidea/pi-authenticator/issues'); + + @override + State createState() => _MigrateLegacyTokensDialogState(); +} + +class _MigrateLegacyTokensDialogState extends State { + Widget _content = Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CircularProgressIndicator()], + ); + + TapGestureRecognizer _tabRecognizer; + var problem; // I.e. Error or Exception + + @override + void initState() { + super.initState(); + _tabRecognizer = TapGestureRecognizer() + ..onTap = () => _launchUri(widget._githubLink); + _migrateTokens(); + } + + @override + void dispose() { + _tabRecognizer?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BackdropFilter( + filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), + child: AlertDialog( + title: Text('Migrating tokens from previous version'), + content: _content, + actions: [ + Visibility( + visible: problem != null, + child: RaisedButton( + onPressed: () => + Clipboard.setData(new ClipboardData(text: '$problem')), + child: Text('Copy to clipboard'), + ), + ), + RaisedButton( + child: Text(Localization.of(context).dismiss), + onPressed: () => Navigator.pop(context), + ), + ], + ), + ); + } + + void _launchUri(Uri link) async { + String uri = link.toString(); + if (await canLaunch(uri)) launch(uri); + } + + void _migrateTokens() async { + List children = []; + + // Load legacy tokens and add them to the storage. + log('Attempt to load legacy tokens.', + name: 'migrate_legacy_tokens_dialog.dart'); + try { + List legacyTokens = await StorageUtil.loadAllTokensLegacy(); + + for (Token t in legacyTokens) { + await StorageUtil.saveOrReplaceToken(t); + } + + throw Exception('Example exception'); + } catch (e, stacktrace) { + // Catch Exceptions and Errors together with stacktrace: + problem = [e, stacktrace]; + + children.add( + RichText( + text: TextSpan( + children: [ + TextSpan( + text: '${widget._githubLink}', + recognizer: _tabRecognizer, + style: Theme.of(context) + .textTheme + .subtitle1 + .copyWith(color: Colors.blue), + ), + TextSpan( + text: ' with the error information below:', + style: Theme.of(context).textTheme.subtitle1, + ), + ], + text: 'Something went wrong, please submit an issue under ', + style: Theme.of(context).textTheme.subtitle1), + ), + ); + children.add(Padding( + padding: EdgeInsets.only(top: 10), + child: Container( + child: Text('$problem'), + color: Colors.black26, + ), + )); + } + + if (children.isEmpty) { + children.add(Text('All tokens migrated successfully')); + } + + final ScrollController controller = ScrollController(); + + setState(() { + _content = Scrollbar( + isAlwaysShown: true, + controller: controller, + child: SingleChildScrollView( + controller: controller, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), + ), + ); + }); + } +} From f7befd4531d67f2831830a6a2c843c95eeac4b32 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:10:54 +0100 Subject: [PATCH 22/40] Changed app version and changelog --- CHANGELOG.md | 7 ++++++- pubspec.lock | 7 +++++++ pubspec.yaml | 3 ++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df150df3..c282a75aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,16 @@ # Changelog -## [3.0.11] - 2021-xx-xx +## [3.0.12] - 2021-xx-xx ### Changed - Notification with sound is shown when the app is open now too - Synchronized progress indicator of totp tokens +- Migrating tokens from app versions prior to 3.0.0 is now a manual process accessible in the settings + +### Fixed + +- Fixed errors occurring by automatic migration of tokens from prior versions by removing automatic migration ## [3.0.8] - 2021-01-07 diff --git a/pubspec.lock b/pubspec.lock index de7122fdb..b3ef61bf9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -298,6 +298,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_file_dialog: + dependency: "direct main" + description: + name: flutter_file_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" flutter_local_notifications: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 725e324f6..dfe4bea8a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ repository: https://github.com/privacyidea/pi-authenticator # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 3.0.8+0300008 # TODO Set the right version number +version: 3.0.12+0300012 # TODO Set the right version number # version: major.minor.build + 2x major|2x minor|3x build # version: version number + build number (optional) # android: build-name + versionCode @@ -74,6 +74,7 @@ dependencies: flutter_markdown: ^0.5.1 url_launcher: ^5.7.10 flutter_svg: ^0.19.1 + flutter_file_dialog: ^1.0.0 flutter_cupertino_localizations: ^1.0.1 intl: ^0.16.0 From d719712bcb6283784597dc4a8db424d7ec2c4478 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:12:06 +0100 Subject: [PATCH 23/40] Added option to save the error to a file --- lib/widgets/migrate_legacy_tokens_dialog.dart | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index 5504fa4f9..8a124576a 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -18,12 +18,15 @@ limitations under the License. */ +import 'dart:convert'; import 'dart:developer'; import 'dart:ui'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_file_dialog/flutter_file_dialog.dart'; +import 'package:package_info/package_info.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; @@ -68,12 +71,27 @@ class _MigrateLegacyTokensDialogState extends State { title: Text('Migrating tokens from previous version'), content: _content, actions: [ + Visibility( + visible: problem != null, + child: RaisedButton( + child: Text('Save'), + onPressed: () async { + final params = SaveFileDialogParams( + sourceFilePath: null, + fileName: 'privacyIDEA_authenticator_${DateTime.now()}.log', + data: utf8.encode('$problem'), + ); + + await FlutterFileDialog.saveFile(params: params); + }, + ), + ), Visibility( visible: problem != null, child: RaisedButton( onPressed: () => Clipboard.setData(new ClipboardData(text: '$problem')), - child: Text('Copy to clipboard'), + child: Text('Copy'), ), ), RaisedButton( @@ -102,11 +120,10 @@ class _MigrateLegacyTokensDialogState extends State { for (Token t in legacyTokens) { await StorageUtil.saveOrReplaceToken(t); } - - throw Exception('Example exception'); } catch (e, stacktrace) { // Catch Exceptions and Errors together with stacktrace: - problem = [e, stacktrace]; + String version = (await PackageInfo.fromPlatform()).version; + problem = 'Version: $version\n$e\n$stacktrace'; children.add( RichText( From a9a87e1bfbf2e99fb421654d787b86f998f9a806 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:25:54 +0100 Subject: [PATCH 24/40] Prevent migrating same token if it already exists --- lib/widgets/migrate_legacy_tokens_dialog.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index 8a124576a..92d6125e4 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -115,9 +115,15 @@ class _MigrateLegacyTokensDialogState extends State { log('Attempt to load legacy tokens.', name: 'migrate_legacy_tokens_dialog.dart'); try { + List existingTokens = await StorageUtil.loadAllTokens(); List legacyTokens = await StorageUtil.loadAllTokensLegacy(); for (Token t in legacyTokens) { + // Prevent migrating token if it already was migrated! + // This would likely overwrite the existing token. + if (existingTokens.any((e) => e.id == t.id)) { + continue; + } await StorageUtil.saveOrReplaceToken(t); } } catch (e, stacktrace) { From 17e0cf01535c9f36352dda495ec2ce398d3aa83f Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:35:37 +0100 Subject: [PATCH 25/40] update token list after migration --- lib/screens/main_screen.dart | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index a3bf8026f..d4e465ee7 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -756,19 +756,7 @@ class _MainScreenState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => -// LicensePage( -// applicationName: "privacyIDEA Authenticator", -// applicationVersion: info.version, -// applicationIcon: Padding( -// padding: EdgeInsets.all(40.0), -// child: Image.asset('res/logo/app_logo_light.png'), -// ), -// applicationLegalese: "Apache License 2.0", -// ), -// ), -// ) - CustomLicenseScreen(), + builder: (context) => CustomLicenseScreen(), ), ); } else if (value == "add_manually") { @@ -782,7 +770,7 @@ class _MainScreenState extends State { context, MaterialPageRoute( builder: (context) => SettingsScreen('Settings'), - )); + )).then((value) => _loadTokenList()); } else if (value == 'guide') { Navigator.push( context, From 81469cf6fe76ae11642ac08af5a59ff6ce56026b Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:51:42 +0100 Subject: [PATCH 26/40] changed migrated tokens id to be uuid --- lib/utils/storage_utils.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/utils/storage_utils.dart b/lib/utils/storage_utils.dart index c56b90c48..dcf9ae7d7 100644 --- a/lib/utils/storage_utils.dart +++ b/lib/utils/storage_utils.dart @@ -28,6 +28,7 @@ import 'package:privacyidea_authenticator/model/firebase_config.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; import 'package:privacyidea_authenticator/utils/identifiers.dart'; import 'package:privacyidea_authenticator/utils/utils.dart'; +import 'package:uuid/uuid.dart'; // TODO test the behavior of this class. class StorageUtil { @@ -186,10 +187,12 @@ class StorageUtil { for (var tokenMap in jsonDecode(await Legacy.loadAllTokens())) { Token token; + String id = Uuid().v4(); + if (tokenMap['type'] == 'hotp') { token = HOTPToken( issuer: tokenMap['label'], - id: tokenMap['serial'], + id: id, label: tokenMap['label'], counter: tokenMap['counter'], digits: tokenMap['digits'], @@ -199,7 +202,7 @@ class StorageUtil { } else if (tokenMap['type'] == 'totp') { token = TOTPToken( issuer: tokenMap['label'], - id: tokenMap['serial'], + id: id, label: tokenMap['label'], period: tokenMap['period'], digits: tokenMap['digits'], @@ -210,7 +213,7 @@ class StorageUtil { token = PushToken( issuer: tokenMap['label'], label: tokenMap['label'], - id: tokenMap['serial'], + id: id, serial: tokenMap['serial'], expirationDate: DateTime.now().subtract(Duration(minutes: 60)), enrollmentCredentials: null, From b3b13f735a1a87671132895b807505275ad1f753 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:52:00 +0100 Subject: [PATCH 27/40] skip existing tokens when migrating --- lib/widgets/migrate_legacy_tokens_dialog.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index 92d6125e4..f2f02a94e 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -121,7 +121,14 @@ class _MigrateLegacyTokensDialogState extends State { for (Token t in legacyTokens) { // Prevent migrating token if it already was migrated! // This would likely overwrite the existing token. - if (existingTokens.any((e) => e.id == t.id)) { + if ((t is OTPToken && + existingTokens + .whereType() + .any((e) => e.secret == t.secret)) || + (t is PushToken && + existingTokens + .whereType() + .any((e) => e.serial == t.serial))) { continue; } await StorageUtil.saveOrReplaceToken(t); From 3b0abcf75483bf0466846a855e800231c4625e48 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Fri, 5 Feb 2021 15:56:59 +0100 Subject: [PATCH 28/40] removed check for doubled migration --- lib/widgets/migrate_legacy_tokens_dialog.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index f2f02a94e..d8d324106 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -119,18 +119,18 @@ class _MigrateLegacyTokensDialogState extends State { List legacyTokens = await StorageUtil.loadAllTokensLegacy(); for (Token t in legacyTokens) { - // Prevent migrating token if it already was migrated! + // TODO? Prevent migrating token if it already was migrated! // This would likely overwrite the existing token. - if ((t is OTPToken && - existingTokens - .whereType() - .any((e) => e.secret == t.secret)) || - (t is PushToken && - existingTokens - .whereType() - .any((e) => e.serial == t.serial))) { - continue; - } +// if ((t is OTPToken && +// existingTokens +// .whereType() +// .any((e) => e.secret == t.secret && e.type == t.type)) || +// (t is PushToken && +// existingTokens +// .whereType() +// .any((e) => e.serial == t.serial))) { +// continue; +// } await StorageUtil.saveOrReplaceToken(t); } } catch (e, stacktrace) { From 993521460609b58d4b9d2b80c524a5a9863a47e3 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Mon, 8 Feb 2021 11:39:54 +0100 Subject: [PATCH 29/40] Fixed exception when saving tokens --- lib/model/tokens.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/model/tokens.dart b/lib/model/tokens.dart index 5b2e9e835..000ab3021 100644 --- a/lib/model/tokens.dart +++ b/lib/model/tokens.dart @@ -38,7 +38,7 @@ abstract class Token { String get tokenVersion => _tokenVersion; - String get label => Uri.decodeFull(_label); + String get label => _label == null ? "" : Uri.decodeFull(_label); set label(String label) { this._label = label; @@ -46,7 +46,7 @@ abstract class Token { String get id => _id; - String get issuer => Uri.decodeFull(_issuer); + String get issuer => _issuer == null ? "" : Uri.decodeFull(_issuer); Token(this._label, this._issuer, this._id, this.type); From 3395c755c4bd7d848d245678bd0e7128cee95ec5 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Mon, 8 Feb 2021 14:28:41 +0100 Subject: [PATCH 30/40] Removed save error and removed stacktrace --- lib/widgets/migrate_legacy_tokens_dialog.dart | 84 ++++++++----------- pubspec.lock | 7 -- pubspec.yaml | 1 - 3 files changed, 34 insertions(+), 58 deletions(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index d8d324106..371ecce97 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -18,14 +18,11 @@ limitations under the License. */ -import 'dart:convert'; import 'dart:developer'; import 'dart:ui'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_file_dialog/flutter_file_dialog.dart'; import 'package:package_info/package_info.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; @@ -71,29 +68,14 @@ class _MigrateLegacyTokensDialogState extends State { title: Text('Migrating tokens from previous version'), content: _content, actions: [ - Visibility( - visible: problem != null, - child: RaisedButton( - child: Text('Save'), - onPressed: () async { - final params = SaveFileDialogParams( - sourceFilePath: null, - fileName: 'privacyIDEA_authenticator_${DateTime.now()}.log', - data: utf8.encode('$problem'), - ); - - await FlutterFileDialog.saveFile(params: params); - }, - ), - ), - Visibility( - visible: problem != null, - child: RaisedButton( - onPressed: () => - Clipboard.setData(new ClipboardData(text: '$problem')), - child: Text('Copy'), - ), - ), +// Visibility( +// visible: problem != null, +// child: RaisedButton( +// onPressed: () => +// Clipboard.setData(new ClipboardData(text: '$problem')), +// child: Text('Copy'), +// ), +// ), RaisedButton( child: Text(Localization.of(context).dismiss), onPressed: () => Navigator.pop(context), @@ -133,32 +115,34 @@ class _MigrateLegacyTokensDialogState extends State { // } await StorageUtil.saveOrReplaceToken(t); } - } catch (e, stacktrace) { + throw Exception('Something went wrong'); + } catch (e) { // Catch Exceptions and Errors together with stacktrace: String version = (await PackageInfo.fromPlatform()).version; - problem = 'Version: $version\n$e\n$stacktrace'; - - children.add( - RichText( - text: TextSpan( - children: [ - TextSpan( - text: '${widget._githubLink}', - recognizer: _tabRecognizer, - style: Theme.of(context) - .textTheme - .subtitle1 - .copyWith(color: Colors.blue), - ), - TextSpan( - text: ' with the error information below:', - style: Theme.of(context).textTheme.subtitle1, - ), - ], - text: 'Something went wrong, please submit an issue under ', - style: Theme.of(context).textTheme.subtitle1), - ), - ); + problem = 'Version: $version\n$e'; + +// children.add( +// RichText( +// text: TextSpan( +// children: [ +// TextSpan( +// text: '${widget._githubLink}', +// recognizer: _tabRecognizer, +// style: Theme.of(context) +// .textTheme +// .subtitle1 +// .copyWith(color: Colors.blue), +// ), +// TextSpan( +// text: ' with the error information below:', +// style: Theme.of(context).textTheme.subtitle1, +// ), +// ], +// text: 'Something went wrong, please submit an issue under ', +// style: Theme.of(context).textTheme.subtitle1), +// ), +// ); + children.add(Text('Something went wrong:')); children.add(Padding( padding: EdgeInsets.only(top: 10), child: Container( diff --git a/pubspec.lock b/pubspec.lock index b3ef61bf9..de7122fdb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -298,13 +298,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_file_dialog: - dependency: "direct main" - description: - name: flutter_file_dialog - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" flutter_local_notifications: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index dfe4bea8a..a364c96de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,7 +74,6 @@ dependencies: flutter_markdown: ^0.5.1 url_launcher: ^5.7.10 flutter_svg: ^0.19.1 - flutter_file_dialog: ^1.0.0 flutter_cupertino_localizations: ^1.0.1 intl: ^0.16.0 From 669bbf98a6e8179035a6ad47212372cfaaf4e4c0 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Mon, 8 Feb 2021 15:26:24 +0100 Subject: [PATCH 31/40] return empty list --- lib/utils/storage_utils.dart | 8 +++++++- lib/widgets/migrate_legacy_tokens_dialog.dart | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/utils/storage_utils.dart b/lib/utils/storage_utils.dart index dcf9ae7d7..6a41b01d1 100644 --- a/lib/utils/storage_utils.dart +++ b/lib/utils/storage_utils.dart @@ -185,7 +185,13 @@ class StorageUtil { static Future> loadAllTokensLegacy() async { List tokenList = []; - for (var tokenMap in jsonDecode(await Legacy.loadAllTokens())) { + String json = await Legacy.loadAllTokens(); + + if (json == null || json == "") { + return tokenList; + } + + for (var tokenMap in jsonDecode(json)) { Token token; String id = Uuid().v4(); diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index 371ecce97..677595437 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -115,7 +115,10 @@ class _MigrateLegacyTokensDialogState extends State { // } await StorageUtil.saveOrReplaceToken(t); } - throw Exception('Something went wrong'); + + if (legacyTokens.isEmpty) { + children.add(Text('No tokens exist that could be migrated.')); + } } catch (e) { // Catch Exceptions and Errors together with stacktrace: String version = (await PackageInfo.fromPlatform()).version; From dad23cc2d1ac916ad996a3f78516de12f3ca6295 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Mon, 8 Feb 2021 15:34:26 +0100 Subject: [PATCH 32/40] Removed unused code --- lib/widgets/migrate_legacy_tokens_dialog.dart | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index 677595437..c5f4a4e6d 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -68,14 +68,6 @@ class _MigrateLegacyTokensDialogState extends State { title: Text('Migrating tokens from previous version'), content: _content, actions: [ -// Visibility( -// visible: problem != null, -// child: RaisedButton( -// onPressed: () => -// Clipboard.setData(new ClipboardData(text: '$problem')), -// child: Text('Copy'), -// ), -// ), RaisedButton( child: Text(Localization.of(context).dismiss), onPressed: () => Navigator.pop(context), @@ -97,22 +89,9 @@ class _MigrateLegacyTokensDialogState extends State { log('Attempt to load legacy tokens.', name: 'migrate_legacy_tokens_dialog.dart'); try { - List existingTokens = await StorageUtil.loadAllTokens(); List legacyTokens = await StorageUtil.loadAllTokensLegacy(); for (Token t in legacyTokens) { - // TODO? Prevent migrating token if it already was migrated! - // This would likely overwrite the existing token. -// if ((t is OTPToken && -// existingTokens -// .whereType() -// .any((e) => e.secret == t.secret && e.type == t.type)) || -// (t is PushToken && -// existingTokens -// .whereType() -// .any((e) => e.serial == t.serial))) { -// continue; -// } await StorageUtil.saveOrReplaceToken(t); } @@ -124,27 +103,6 @@ class _MigrateLegacyTokensDialogState extends State { String version = (await PackageInfo.fromPlatform()).version; problem = 'Version: $version\n$e'; -// children.add( -// RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: '${widget._githubLink}', -// recognizer: _tabRecognizer, -// style: Theme.of(context) -// .textTheme -// .subtitle1 -// .copyWith(color: Colors.blue), -// ), -// TextSpan( -// text: ' with the error information below:', -// style: Theme.of(context).textTheme.subtitle1, -// ), -// ], -// text: 'Something went wrong, please submit an issue under ', -// style: Theme.of(context).textTheme.subtitle1), -// ), -// ); children.add(Text('Something went wrong:')); children.add(Padding( padding: EdgeInsets.only(top: 10), From 1c0aa864957673b8808abcc8091e378da474f8eb Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Mon, 8 Feb 2021 15:54:50 +0100 Subject: [PATCH 33/40] Added localization --- lib/l10n/intl_de.arb | 94 ++++++++++++++++++- lib/l10n/intl_en.arb | 90 +++++++++++++++++- lib/l10n/intl_messages.arb | 90 +++++++++++++++++- lib/l10n/messages_de.dart | 14 +++ lib/l10n/messages_en.dart | 14 +++ lib/l10n/messages_messages.dart | 14 +++ lib/screens/settings_screen.dart | 6 +- lib/utils/localization_utils.dart | 52 ++++++++++ lib/widgets/migrate_legacy_tokens_dialog.dart | 8 +- 9 files changed, 367 insertions(+), 15 deletions(-) diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index ac38921ba..899884c29 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -56,7 +56,7 @@ }, "Digits": "Ziffern", "@Digits": { - "description": "Title of the dropdown button where the number of digits for the opt value is selecte.", + "description": "Title of the dropdown button where the number of digits for the opt value is selected.", "type": "text", "placeholders": {} }, @@ -228,7 +228,7 @@ }, "errorRollOutFailed": "Ausrollen von {name} ist fehlgeschlagen. Fehlercode: {errorCode}", "@errorRollOutFailed": { - "description": "Tells the user that the token could not be rolled out, because a network error occured.", + "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { @@ -367,5 +367,95 @@ "description": "Description for checkbox, if the checkbox is ticked, the guide screen is shown on every app start.", "type": "text", "placeholders": {} + }, + "Push Token": "Push Token", + "@Push Token": { + "description": "Title for the settings block concerning the push tokens.", + "type": "text", + "placeholders": {} + }, + "errorSynchronizationNoNetworkConnection": "Die Synchronisation ist fehlgeschlagen, da der privacyIDEA Server nicht erreicht werden konnte.", + "@errorSynchronizationNoNetworkConnection": { + "description": "Tells the user that synchronizing the push tokens failed because the server could not be reached.", + "type": "text", + "placeholders": {} + }, + "Synchronize push tokens": "Synchronisiere Push Tokens", + "@Synchronize push tokens": { + "description": "Title of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Synchronizes tokens with the privacyIDEA server.": "Synchronisiert Push Tokens mit dem privacyIDEA Server.", + "@Synchronizes tokens with the privacyIDEA server.": { + "description": "Description of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Sync": "Sync", + "@Sync": { + "description": "Text of button that is used to synchronize push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronizing tokens.": "Synchronisiere Tokens.", + "@Synchronizing tokens.": { + "description": "Title of the push synchronization dialog.", + "type": "text", + "placeholders": {} + }, + "All tokens are synchronized.": "Alle Token wurden synchronisiert.", + "@All tokens are synchronized.": { + "description": "Content of the push synchronization dialog. Signaling the user that everything worked.", + "type": "text", + "placeholders": {} + }, + "Synchronization failed for the following tokens, please try again:": "Synchronisation ist für die folgenden Tokens fehlgeschlagen, bitte versuchen Sie es erneut:", + "@Synchronization failed for the following tokens, please try again:": { + "description": "Headline for the list of tokens where the synchronization failed.", + "type": "text", + "placeholders": {} + }, + "The following tokens do not support synchronization and must be rolled out again:": "Die folgenden Tokens unterstützen keine Synchronisation und müssen neu ausgerollt werden:", + "@The following tokens do not support synchronization and must be rolled out again:": { + "description": "Informs the user that the following tokens cannot be synchronized as they do not support that.", + "type": "text", + "placeholders": {} + }, + "Migrate tokens from previous app version.": "Migriere Token aus der vorherigen App Version.", + "@Migrate tokens from previous app version.": { + "description": "Description of migration.", + "type": "text", + "placeholders": {} + }, + "Migrate": "Migriere", + "@Migrate": { + "description": "Text of button to start migration.", + "type": "text", + "placeholders": {} + }, + "Migration": "Migration", + "@Migration": { + "description": "Title of settings group for migrating tokens.", + "type": "text", + "placeholders": {} + }, + "All tokens migrated successfully.": "Alle Token wurden erfolgreich migriert.", + "@All tokens migrated successfully.": { + "description": "Tells the user that all tokens are migrated successfully.", + "type": "text", + "placeholders": {} + }, + "No tokens exist that could be migrated.": "Es existieren keine Token, die migriert werden könnten.", + "@No tokens exist that could be migrated.": { + "description": "Tells the user that no tokens exist that could be migrated.", + "type": "text", + "placeholders": {} + }, + "Migrating Token": "Migriere Token", + "@Migrating Token": { + "description": "Title of migration dialog.", + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 697015d57..5930fed00 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2021-01-12T17:39:37.559359", + "@@last_modified": "2021-02-08T15:53:27.303244", "Guide": "Guide", "@Guide": { "description": "Button to open the guide screen.", @@ -62,7 +62,7 @@ }, "Digits": "Digits", "@Digits": { - "description": "Title of the dropdown button where the number of digits for the opt value is selecte.", + "description": "Title of the dropdown button where the number of digits for the opt value is selected.", "type": "text", "placeholders": {} }, @@ -206,6 +206,12 @@ "type": "text", "placeholders": {} }, + "Push Token": "Push Token", + "@Push Token": { + "description": "Title for the settings block concerning the push tokens.", + "type": "text", + "placeholders": {} + }, "Theme": "Theme", "@Theme": { "description": "Title of the setting group where the theme can be selected.", @@ -248,6 +254,48 @@ "type": "text", "placeholders": {} }, + "Synchronize push tokens": "Synchronize push tokens", + "@Synchronize push tokens": { + "description": "Title of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Synchronizes tokens with the privacyIDEA server.": "Synchronizes tokens with the privacyIDEA server.", + "@Synchronizes tokens with the privacyIDEA server.": { + "description": "Description of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Sync": "Sync", + "@Sync": { + "description": "Text of button that is used to synchronize push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronizing tokens.": "Synchronizing tokens.", + "@Synchronizing tokens.": { + "description": "Title of the push synchronization dialog.", + "type": "text", + "placeholders": {} + }, + "All tokens are synchronized.": "All tokens are synchronized.", + "@All tokens are synchronized.": { + "description": "Content of the push synchronization dialog. Signaling the user that everything worked.", + "type": "text", + "placeholders": {} + }, + "Synchronization failed for the following tokens, please try again:": "Synchronization failed for the following tokens, please try again:", + "@Synchronization failed for the following tokens, please try again:": { + "description": "Headline for the list of tokens where the synchronization failed.", + "type": "text", + "placeholders": {} + }, + "The following tokens do not support synchronization and must be rolled out again:": "The following tokens do not support synchronization and must be rolled out again:", + "@The following tokens do not support synchronization and must be rolled out again:": { + "description": "Informs the user that the following tokens cannot be synchronized as they do not support that.", + "type": "text", + "placeholders": {} + }, "errorOnlyOneFirebaseProjectIsSupported": "The firebase configuration of {name} differs from the one currently used by the app. Currently only one is supported.", "@errorOnlyOneFirebaseProjectIsSupported": { "description": "Tells the user that the token can not be used because it has a different firebase configuration than the current used configuration of the application.", @@ -270,7 +318,7 @@ }, "errorRollOutFailed": "Rolling out token {name} failed. Error code: {errorCode}", "@errorRollOutFailed": { - "description": "Tells the user that the token could not be rolled out, because a network error occured.", + "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { @@ -367,5 +415,41 @@ "@Polling for new challenges": { "type": "text", "placeholders": {} + }, + "Migrate tokens from previous app version.": "Migrate tokens from previous app version.", + "@Migrate tokens from previous app version.": { + "description": "Description of migration.", + "type": "text", + "placeholders": {} + }, + "Migrate": "Migrate", + "@Migrate": { + "description": "Text of button to start migration.", + "type": "text", + "placeholders": {} + }, + "Migration": "Migration", + "@Migration": { + "description": "Title of settings group for migrating tokens.", + "type": "text", + "placeholders": {} + }, + "All tokens migrated successfully.": "All tokens migrated successfully.", + "@All tokens migrated successfully.": { + "description": "Tells the user that all tokens are migrated successfully.", + "type": "text", + "placeholders": {} + }, + "No tokens exist that could be migrated.": "No tokens exist that could be migrated.", + "@No tokens exist that could be migrated.": { + "description": "Tells the user that no tokens exist that could be migrated.", + "type": "text", + "placeholders": {} + }, + "Migrating Token": "Migrating Token", + "@Migrating Token": { + "description": "Title of migration dialog.", + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 697015d57..5930fed00 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2021-01-12T17:39:37.559359", + "@@last_modified": "2021-02-08T15:53:27.303244", "Guide": "Guide", "@Guide": { "description": "Button to open the guide screen.", @@ -62,7 +62,7 @@ }, "Digits": "Digits", "@Digits": { - "description": "Title of the dropdown button where the number of digits for the opt value is selecte.", + "description": "Title of the dropdown button where the number of digits for the opt value is selected.", "type": "text", "placeholders": {} }, @@ -206,6 +206,12 @@ "type": "text", "placeholders": {} }, + "Push Token": "Push Token", + "@Push Token": { + "description": "Title for the settings block concerning the push tokens.", + "type": "text", + "placeholders": {} + }, "Theme": "Theme", "@Theme": { "description": "Title of the setting group where the theme can be selected.", @@ -248,6 +254,48 @@ "type": "text", "placeholders": {} }, + "Synchronize push tokens": "Synchronize push tokens", + "@Synchronize push tokens": { + "description": "Title of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Synchronizes tokens with the privacyIDEA server.": "Synchronizes tokens with the privacyIDEA server.", + "@Synchronizes tokens with the privacyIDEA server.": { + "description": "Description of synchronizing push tokens in settings.", + "type": "text", + "placeholders": {} + }, + "Sync": "Sync", + "@Sync": { + "description": "Text of button that is used to synchronize push tokens.", + "type": "text", + "placeholders": {} + }, + "Synchronizing tokens.": "Synchronizing tokens.", + "@Synchronizing tokens.": { + "description": "Title of the push synchronization dialog.", + "type": "text", + "placeholders": {} + }, + "All tokens are synchronized.": "All tokens are synchronized.", + "@All tokens are synchronized.": { + "description": "Content of the push synchronization dialog. Signaling the user that everything worked.", + "type": "text", + "placeholders": {} + }, + "Synchronization failed for the following tokens, please try again:": "Synchronization failed for the following tokens, please try again:", + "@Synchronization failed for the following tokens, please try again:": { + "description": "Headline for the list of tokens where the synchronization failed.", + "type": "text", + "placeholders": {} + }, + "The following tokens do not support synchronization and must be rolled out again:": "The following tokens do not support synchronization and must be rolled out again:", + "@The following tokens do not support synchronization and must be rolled out again:": { + "description": "Informs the user that the following tokens cannot be synchronized as they do not support that.", + "type": "text", + "placeholders": {} + }, "errorOnlyOneFirebaseProjectIsSupported": "The firebase configuration of {name} differs from the one currently used by the app. Currently only one is supported.", "@errorOnlyOneFirebaseProjectIsSupported": { "description": "Tells the user that the token can not be used because it has a different firebase configuration than the current used configuration of the application.", @@ -270,7 +318,7 @@ }, "errorRollOutFailed": "Rolling out token {name} failed. Error code: {errorCode}", "@errorRollOutFailed": { - "description": "Tells the user that the token could not be rolled out, because a network error occured.", + "description": "Tells the user that the token could not be rolled out, because a network error occurred.", "type": "text", "placeholders": { "name": { @@ -367,5 +415,41 @@ "@Polling for new challenges": { "type": "text", "placeholders": {} + }, + "Migrate tokens from previous app version.": "Migrate tokens from previous app version.", + "@Migrate tokens from previous app version.": { + "description": "Description of migration.", + "type": "text", + "placeholders": {} + }, + "Migrate": "Migrate", + "@Migrate": { + "description": "Text of button to start migration.", + "type": "text", + "placeholders": {} + }, + "Migration": "Migration", + "@Migration": { + "description": "Title of settings group for migrating tokens.", + "type": "text", + "placeholders": {} + }, + "All tokens migrated successfully.": "All tokens migrated successfully.", + "@All tokens migrated successfully.": { + "description": "Tells the user that all tokens are migrated successfully.", + "type": "text", + "placeholders": {} + }, + "No tokens exist that could be migrated.": "No tokens exist that could be migrated.", + "@No tokens exist that could be migrated.": { + "description": "Tells the user that no tokens exist that could be migrated.", + "type": "text", + "placeholders": {} + }, + "Migrating Token": "Migrating Token", + "@Migrating Token": { + "description": "Title of migration dialog.", + "type": "text", + "placeholders": {} } } \ No newline at end of file diff --git a/lib/l10n/messages_de.dart b/lib/l10n/messages_de.dart index 398367bf3..fac2f22dd 100644 --- a/lib/l10n/messages_de.dart +++ b/lib/l10n/messages_de.dart @@ -44,6 +44,8 @@ class MessageLookup extends MessageLookupByLibrary { "About" : MessageLookupByLibrary.simpleMessage("Über"), "Add token" : MessageLookupByLibrary.simpleMessage("Token hinzufügen"), "Algorithm" : MessageLookupByLibrary.simpleMessage("Algorithmus"), + "All tokens are synchronized." : MessageLookupByLibrary.simpleMessage("Alle Token wurden synchronisiert."), + "All tokens migrated successfully." : MessageLookupByLibrary.simpleMessage("Alle Token wurden erfolgreich migriert."), "Cancel" : MessageLookupByLibrary.simpleMessage("Abbrechen"), "Confirm deletion" : MessageLookupByLibrary.simpleMessage("Löschen bestätigen"), "Dark theme" : MessageLookupByLibrary.simpleMessage("Dunkles Thema"), @@ -56,14 +58,20 @@ class MessageLookup extends MessageLookupByLibrary { "Generating phone part" : MessageLookupByLibrary.simpleMessage("Generiere Telefonanteil"), "Guide" : MessageLookupByLibrary.simpleMessage("Anleitung"), "Light theme" : MessageLookupByLibrary.simpleMessage("Helles Thema"), + "Migrate" : MessageLookupByLibrary.simpleMessage("Migriere"), + "Migrate tokens from previous app version." : MessageLookupByLibrary.simpleMessage("Migriere Token aus der vorherigen App Version."), + "Migrating Token" : MessageLookupByLibrary.simpleMessage("Migriere Token"), + "Migration" : MessageLookupByLibrary.simpleMessage("Migration"), "Miscellaneous" : MessageLookupByLibrary.simpleMessage("Verschiedenes"), "Name" : MessageLookupByLibrary.simpleMessage("Name"), "Next" : MessageLookupByLibrary.simpleMessage("Weiter"), + "No tokens exist that could be migrated." : MessageLookupByLibrary.simpleMessage("Es existieren keine Token, die migriert werden könnten."), "Period" : MessageLookupByLibrary.simpleMessage("Periode"), "Phone part:" : MessageLookupByLibrary.simpleMessage("Telefonanteil:"), "Please enter a name for this token." : MessageLookupByLibrary.simpleMessage("Bitte geben Sie einen Namen für diesen Token ein."), "Please enter a secret for this token." : MessageLookupByLibrary.simpleMessage("Bitte geben Sie ein Geheimnis für diesen Token ein."), "Polling for new challenges" : MessageLookupByLibrary.simpleMessage("Frage ausstehende Authentifizierungsanfragen ab"), + "Push Token" : MessageLookupByLibrary.simpleMessage("Push Token"), "Rename" : MessageLookupByLibrary.simpleMessage("Umbenennen"), "Rename token" : MessageLookupByLibrary.simpleMessage("Token umbenennen"), "Request push challenges from the server periodically. Enable this if push challenges are not received normally." : MessageLookupByLibrary.simpleMessage("Fordert regelmäßig Push-Anfragen vom Server an. Aktivieren Sie diese Funktion, wenn Nachrichten ansonsten nicht erhalten werden."), @@ -74,7 +82,13 @@ class MessageLookup extends MessageLookupByLibrary { "Show this screen on start:" : MessageLookupByLibrary.simpleMessage("Zeige bei App-Start:"), "Some of the tokens are outdated and do not support polling" : MessageLookupByLibrary.simpleMessage("Einige der Token sind veraltet und unterstützen keine aktiven Anfragen"), "Something went wrong." : MessageLookupByLibrary.simpleMessage("Etwas ist schiefgelaufen."), + "Sync" : MessageLookupByLibrary.simpleMessage("Sync"), + "Synchronization failed for the following tokens, please try again:" : MessageLookupByLibrary.simpleMessage("Synchronisation ist für die folgenden Tokens fehlgeschlagen, bitte versuchen Sie es erneut:"), + "Synchronize push tokens" : MessageLookupByLibrary.simpleMessage("Synchronisiere Push Tokens"), + "Synchronizes tokens with the privacyIDEA server." : MessageLookupByLibrary.simpleMessage("Synchronisiert Push Tokens mit dem privacyIDEA Server."), + "Synchronizing tokens." : MessageLookupByLibrary.simpleMessage("Synchronisiere Tokens."), "The firebase configuration is corrupted and cannot be used." : MessageLookupByLibrary.simpleMessage("Die Firebase-Konfiguration ist beschädigt und kann nicht genutzt werden."), + "The following tokens do not support synchronization and must be rolled out again:" : MessageLookupByLibrary.simpleMessage("Die folgenden Tokens unterstützen keine Synchronisation und müssen neu ausgerollt werden:"), "The secret does not fit the current encoding" : MessageLookupByLibrary.simpleMessage("Das Geheimnis entspricht nicht der gewählten\n Verschlüsselung."), "Theme" : MessageLookupByLibrary.simpleMessage("Thema"), "Type" : MessageLookupByLibrary.simpleMessage("Art"), diff --git a/lib/l10n/messages_en.dart b/lib/l10n/messages_en.dart index 961aab9a2..0358c384c 100644 --- a/lib/l10n/messages_en.dart +++ b/lib/l10n/messages_en.dart @@ -44,6 +44,8 @@ class MessageLookup extends MessageLookupByLibrary { "About" : MessageLookupByLibrary.simpleMessage("About"), "Add token" : MessageLookupByLibrary.simpleMessage("Add token"), "Algorithm" : MessageLookupByLibrary.simpleMessage("Algorithm"), + "All tokens are synchronized." : MessageLookupByLibrary.simpleMessage("All tokens are synchronized."), + "All tokens migrated successfully." : MessageLookupByLibrary.simpleMessage("All tokens migrated successfully."), "Cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), "Confirm deletion" : MessageLookupByLibrary.simpleMessage("Confirm deletion"), "Dark theme" : MessageLookupByLibrary.simpleMessage("Dark theme"), @@ -56,14 +58,20 @@ class MessageLookup extends MessageLookupByLibrary { "Generating phone part" : MessageLookupByLibrary.simpleMessage("Generating phone part"), "Guide" : MessageLookupByLibrary.simpleMessage("Guide"), "Light theme" : MessageLookupByLibrary.simpleMessage("Light theme"), + "Migrate" : MessageLookupByLibrary.simpleMessage("Migrate"), + "Migrate tokens from previous app version." : MessageLookupByLibrary.simpleMessage("Migrate tokens from previous app version."), + "Migrating Token" : MessageLookupByLibrary.simpleMessage("Migrating Token"), + "Migration" : MessageLookupByLibrary.simpleMessage("Migration"), "Miscellaneous" : MessageLookupByLibrary.simpleMessage("Miscellaneous"), "Name" : MessageLookupByLibrary.simpleMessage("Name"), "Next" : MessageLookupByLibrary.simpleMessage("Next"), + "No tokens exist that could be migrated." : MessageLookupByLibrary.simpleMessage("No tokens exist that could be migrated."), "Period" : MessageLookupByLibrary.simpleMessage("Period"), "Phone part:" : MessageLookupByLibrary.simpleMessage("Phone part:"), "Please enter a name for this token." : MessageLookupByLibrary.simpleMessage("Please enter a name for this token."), "Please enter a secret for this token." : MessageLookupByLibrary.simpleMessage("Please enter a secret for this token."), "Polling for new challenges" : MessageLookupByLibrary.simpleMessage("Polling for new challenges"), + "Push Token" : MessageLookupByLibrary.simpleMessage("Push Token"), "Rename" : MessageLookupByLibrary.simpleMessage("Rename"), "Rename token" : MessageLookupByLibrary.simpleMessage("Rename token"), "Request push challenges from the server periodically. Enable this if push challenges are not received normally." : MessageLookupByLibrary.simpleMessage("Request push challenges from the server periodically. Enable this if push challenges are not received normally."), @@ -74,7 +82,13 @@ class MessageLookup extends MessageLookupByLibrary { "Show this screen on start:" : MessageLookupByLibrary.simpleMessage("Show this screen on start:"), "Some of the tokens are outdated and do not support polling" : MessageLookupByLibrary.simpleMessage("Some of the tokens are outdated and do not support polling"), "Something went wrong." : MessageLookupByLibrary.simpleMessage("Something went wrong."), + "Sync" : MessageLookupByLibrary.simpleMessage("Sync"), + "Synchronization failed for the following tokens, please try again:" : MessageLookupByLibrary.simpleMessage("Synchronization failed for the following tokens, please try again:"), + "Synchronize push tokens" : MessageLookupByLibrary.simpleMessage("Synchronize push tokens"), + "Synchronizes tokens with the privacyIDEA server." : MessageLookupByLibrary.simpleMessage("Synchronizes tokens with the privacyIDEA server."), + "Synchronizing tokens." : MessageLookupByLibrary.simpleMessage("Synchronizing tokens."), "The firebase configuration is corrupted and cannot be used." : MessageLookupByLibrary.simpleMessage("The firebase configuration is corrupted and cannot be used."), + "The following tokens do not support synchronization and must be rolled out again:" : MessageLookupByLibrary.simpleMessage("The following tokens do not support synchronization and must be rolled out again:"), "The secret does not fit the current encoding" : MessageLookupByLibrary.simpleMessage("The secret does not fit the current encoding"), "Theme" : MessageLookupByLibrary.simpleMessage("Theme"), "Type" : MessageLookupByLibrary.simpleMessage("Type"), diff --git a/lib/l10n/messages_messages.dart b/lib/l10n/messages_messages.dart index a30e4ae32..f0dc1cb34 100644 --- a/lib/l10n/messages_messages.dart +++ b/lib/l10n/messages_messages.dart @@ -44,6 +44,8 @@ class MessageLookup extends MessageLookupByLibrary { "About" : MessageLookupByLibrary.simpleMessage("About"), "Add token" : MessageLookupByLibrary.simpleMessage("Add token"), "Algorithm" : MessageLookupByLibrary.simpleMessage("Algorithm"), + "All tokens are synchronized." : MessageLookupByLibrary.simpleMessage("All tokens are synchronized."), + "All tokens migrated successfully." : MessageLookupByLibrary.simpleMessage("All tokens migrated successfully."), "Cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), "Confirm deletion" : MessageLookupByLibrary.simpleMessage("Confirm deletion"), "Dark theme" : MessageLookupByLibrary.simpleMessage("Dark theme"), @@ -56,14 +58,20 @@ class MessageLookup extends MessageLookupByLibrary { "Generating phone part" : MessageLookupByLibrary.simpleMessage("Generating phone part"), "Guide" : MessageLookupByLibrary.simpleMessage("Guide"), "Light theme" : MessageLookupByLibrary.simpleMessage("Light theme"), + "Migrate" : MessageLookupByLibrary.simpleMessage("Migrate"), + "Migrate tokens from previous app version." : MessageLookupByLibrary.simpleMessage("Migrate tokens from previous app version."), + "Migrating Token" : MessageLookupByLibrary.simpleMessage("Migrating Token"), + "Migration" : MessageLookupByLibrary.simpleMessage("Migration"), "Miscellaneous" : MessageLookupByLibrary.simpleMessage("Miscellaneous"), "Name" : MessageLookupByLibrary.simpleMessage("Name"), "Next" : MessageLookupByLibrary.simpleMessage("Next"), + "No tokens exist that could be migrated." : MessageLookupByLibrary.simpleMessage("No tokens exist that could be migrated."), "Period" : MessageLookupByLibrary.simpleMessage("Period"), "Phone part:" : MessageLookupByLibrary.simpleMessage("Phone part:"), "Please enter a name for this token." : MessageLookupByLibrary.simpleMessage("Please enter a name for this token."), "Please enter a secret for this token." : MessageLookupByLibrary.simpleMessage("Please enter a secret for this token."), "Polling for new challenges" : MessageLookupByLibrary.simpleMessage("Polling for new challenges"), + "Push Token" : MessageLookupByLibrary.simpleMessage("Push Token"), "Rename" : MessageLookupByLibrary.simpleMessage("Rename"), "Rename token" : MessageLookupByLibrary.simpleMessage("Rename token"), "Request push challenges from the server periodically. Enable this if push challenges are not received normally." : MessageLookupByLibrary.simpleMessage("Request push challenges from the server periodically. Enable this if push challenges are not received normally."), @@ -74,7 +82,13 @@ class MessageLookup extends MessageLookupByLibrary { "Show this screen on start:" : MessageLookupByLibrary.simpleMessage("Show this screen on start:"), "Some of the tokens are outdated and do not support polling" : MessageLookupByLibrary.simpleMessage("Some of the tokens are outdated and do not support polling"), "Something went wrong." : MessageLookupByLibrary.simpleMessage("Something went wrong."), + "Sync" : MessageLookupByLibrary.simpleMessage("Sync"), + "Synchronization failed for the following tokens, please try again:" : MessageLookupByLibrary.simpleMessage("Synchronization failed for the following tokens, please try again:"), + "Synchronize push tokens" : MessageLookupByLibrary.simpleMessage("Synchronize push tokens"), + "Synchronizes tokens with the privacyIDEA server." : MessageLookupByLibrary.simpleMessage("Synchronizes tokens with the privacyIDEA server."), + "Synchronizing tokens." : MessageLookupByLibrary.simpleMessage("Synchronizing tokens."), "The firebase configuration is corrupted and cannot be used." : MessageLookupByLibrary.simpleMessage("The firebase configuration is corrupted and cannot be used."), + "The following tokens do not support synchronization and must be rolled out again:" : MessageLookupByLibrary.simpleMessage("The following tokens do not support synchronization and must be rolled out again:"), "The secret does not fit the current encoding" : MessageLookupByLibrary.simpleMessage("The secret does not fit the current encoding"), "Theme" : MessageLookupByLibrary.simpleMessage("Theme"), "Type" : MessageLookupByLibrary.simpleMessage("Type"), diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 0c0f59b42..399556cac 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -181,12 +181,12 @@ class SettingsScreenState extends State { }, ), SettingsGroup( - title: 'Migration', + title: Localization.of(context).migration, children: [ ListTile( - title: Text('Migrate tokens from previous app version.'), + title: Text(Localization.of(context).migrationDesc), trailing: RaisedButton( - child: Text('Migrate'), + child: Text(Localization.of(context).migrate), onPressed: () => showDialog( context: context, barrierDismissible: false, diff --git a/lib/utils/localization_utils.dart b/lib/utils/localization_utils.dart index 9cf01108c..5d46aeb3b 100644 --- a/lib/utils/localization_utils.dart +++ b/lib/utils/localization_utils.dart @@ -606,6 +606,58 @@ class Localization { String get pollNow { return Intl.message('Polling for new challenges', locale: localeName); } + + // ########################################################################### + // Migration + // ########################################################################### + + String get migrationDesc { + return Intl.message( + 'Migrate tokens from previous app version.', + desc: 'Description of migration.', + locale: localeName, + ); + } + + String get migrate { + return Intl.message( + 'Migrate', + desc: 'Text of button to start migration.', + locale: localeName, + ); + } + + String get migration { + return Intl.message( + 'Migration', + desc: 'Title of settings group for migrating tokens.', + locale: localeName, + ); + } + + String get migrationSuccess { + return Intl.message( + 'All tokens migrated successfully.', + desc: 'Tells the user that all tokens are migrated successfully.', + locale: localeName, + ); + } + + String get migrationNoTokens { + return Intl.message( + 'No tokens exist that could be migrated.', + desc: 'Tells the user that no tokens exist that could be migrated.', + locale: localeName, + ); + } + + String get migrationDialogTitle { + return Intl.message( + 'Migrating Token', + desc: 'Title of migration dialog.', + locale: localeName, + ); + } } class MyLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index c5f4a4e6d..e04e81a77 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -65,7 +65,7 @@ class _MigrateLegacyTokensDialogState extends State { return BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: AlertDialog( - title: Text('Migrating tokens from previous version'), + title: Text(Localization.of(context).migrationDialogTitle), content: _content, actions: [ RaisedButton( @@ -96,14 +96,14 @@ class _MigrateLegacyTokensDialogState extends State { } if (legacyTokens.isEmpty) { - children.add(Text('No tokens exist that could be migrated.')); + children.add(Text(Localization.of(context).migrationNoTokens)); } } catch (e) { // Catch Exceptions and Errors together with stacktrace: String version = (await PackageInfo.fromPlatform()).version; problem = 'Version: $version\n$e'; - children.add(Text('Something went wrong:')); + children.add(Text(Localization.of(context).somethingWentWrong)); children.add(Padding( padding: EdgeInsets.only(top: 10), child: Container( @@ -114,7 +114,7 @@ class _MigrateLegacyTokensDialogState extends State { } if (children.isEmpty) { - children.add(Text('All tokens migrated successfully')); + children.add(Text(Localization.of(context).migrationSuccess)); } final ScrollController controller = ScrollController(); From 271cbc0f6d20f73845d138794261b78c9e54c484 Mon Sep 17 00:00:00 2001 From: Nils Behlen Date: Tue, 9 Feb 2021 10:58:34 +0100 Subject: [PATCH 34/40] add migration info to guide, filter existing push token when migrating * Removed the first section of the guide (which was pretty self-explanatory), replaced with info about migration * push token can be checked by serial when migrating to not be added another time --- lib/widgets/migrate_legacy_tokens_dialog.dart | 18 ++++++++++++++++-- res/md/GUIDE_de.md | 13 ++++--------- res/md/GUIDE_en.md | 12 ++++-------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index e04e81a77..891b6acc5 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -25,8 +25,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:package_info/package_info.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; +import 'package:privacyidea_authenticator/utils/identifiers.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; +import 'package:privacyidea_authenticator/utils/utils.dart'; import 'package:url_launcher/url_launcher.dart'; class MigrateLegacyTokensDialog extends StatefulWidget { @@ -91,8 +93,20 @@ class _MigrateLegacyTokensDialogState extends State { try { List legacyTokens = await StorageUtil.loadAllTokensLegacy(); - for (Token t in legacyTokens) { - await StorageUtil.saveOrReplaceToken(t); + List currentPushToken = await StorageUtil.loadAllTokens(); + currentPushToken = currentPushToken + .where((element) => element.type == enumAsString(TokenTypes.PIPUSH)) + .toList(); + + for (Token old in legacyTokens) { + // Skip push token which already exist (by serial) + if (old.type == enumAsString(TokenTypes.PIPUSH)) { + if (currentPushToken.indexWhere((element) => + (old as PushToken).serial == (element as PushToken).serial) > -1) { + continue; + } + } + await StorageUtil.saveOrReplaceToken(old); } if (legacyTokens.isEmpty) { diff --git a/res/md/GUIDE_de.md b/res/md/GUIDE_de.md index 2f11d936a..9fe371542 100644 --- a/res/md/GUIDE_de.md +++ b/res/md/GUIDE_de.md @@ -1,17 +1,12 @@ # Willkommen im privacyIDEA Authenticator Hier wird Ihnen ein kurzer Einblick in die Funktionen der App gegeben. -+ Das Menü ist über den Schalter `⋮` oder `…` in der oberen rechten Ecke erreichbar, - dort stehen folgende Punkte zur Auswahl: - + Zugriff auf diese Einführung über `Anleitung` - + Manuelles hinzufügen von Token über `Token hinzufügen` - + Bearbeitung der Einstellungen über `Einstellungen` - + Informationen über die Applikation über `Über` ++ Token aus der vorherigen App Version können in den `Einstellungen` im Bereich `Migration` geladen werden. -+ Token können wie zuvor beschrieben manuelle hinzugefügt werden, alternativ können QR-Codes -eingescannt werden indem der `+` Schalter in der unteren rechten Ecke gedrückt wird. ++ Token werden durch das scannen von QR-Codes hinzugefügt. Drücken Sie dazu `+` in der unteren rechten Ecke. +Alternativ können Token auch manuell über das `Menü` hinzugefügt werden. -+ Das Umbenennen und Löschen von Token ist erreichbar, indem Sie sie nach links wischen: ++ Token können gelöscht oder umbenannt werden, indem Sie sie nach links wischen: ![Renaming and deleting tokens by swiping left](resource:res/gif/help_delete_rename.gif) # privacyIDEA Push Token: diff --git a/res/md/GUIDE_en.md b/res/md/GUIDE_en.md index 399e0a877..6b11e4f74 100644 --- a/res/md/GUIDE_en.md +++ b/res/md/GUIDE_en.md @@ -1,16 +1,12 @@ # Welcome to the privacyIDEA Authenticator. This screen gives you a quick introduction to the app. -+ The menu button `⋮` or `…` in the upper right corner of the screen gives access to several things: - + Accessing this screen by clicking `Guide` - + Manually adding tokens by clicking `Add tokens` - + Editing the apps settings by clicking `Settings` - + Viewing information about the app by clicking `About` ++ Token form the previous App version can be loaded via the `Settings` in the `Migration` section. -+ Tokens can be added manually as mentioned before or by scanning a QR-Code by clicking the `+` button -in the lower right corner of the screen. ++ Tokens can be added by scanning a QR-Code by clicking the `+` button in the lower right corner + or they can be added manually via the `Menu` in the upper right corner. -+ Tokens can be deleted and renamed by swiping left and clicking the corresponding button: ++ Tokens can be deleted or renamed by swiping left and clicking the corresponding button: ![Renaming and deleting tokens by swiping left](resource:res/gif/help_delete_rename.gif) # privacyIDEA Push Token: From b64bc7c40838ae34c5b995dea5075837054fc904 Mon Sep 17 00:00:00 2001 From: Nils Behlen Date: Tue, 9 Feb 2021 11:04:06 +0100 Subject: [PATCH 35/40] remove first line from guide --- res/md/GUIDE_de.md | 1 - res/md/GUIDE_en.md | 1 - 2 files changed, 2 deletions(-) diff --git a/res/md/GUIDE_de.md b/res/md/GUIDE_de.md index 9fe371542..9ae540a91 100644 --- a/res/md/GUIDE_de.md +++ b/res/md/GUIDE_de.md @@ -1,5 +1,4 @@ # Willkommen im privacyIDEA Authenticator -Hier wird Ihnen ein kurzer Einblick in die Funktionen der App gegeben. + Token aus der vorherigen App Version können in den `Einstellungen` im Bereich `Migration` geladen werden. diff --git a/res/md/GUIDE_en.md b/res/md/GUIDE_en.md index 6b11e4f74..080d4f0df 100644 --- a/res/md/GUIDE_en.md +++ b/res/md/GUIDE_en.md @@ -1,5 +1,4 @@ # Welcome to the privacyIDEA Authenticator. -This screen gives you a quick introduction to the app. + Token form the previous App version can be loaded via the `Settings` in the `Migration` section. From dc09efac9fcc3ee986bda8c91e239e16f52b0078 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Tue, 9 Feb 2021 12:25:45 +0100 Subject: [PATCH 36/40] Simplified code that checks for existing push tokens on migration --- lib/widgets/migrate_legacy_tokens_dialog.dart | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/widgets/migrate_legacy_tokens_dialog.dart b/lib/widgets/migrate_legacy_tokens_dialog.dart index 891b6acc5..67072d99a 100644 --- a/lib/widgets/migrate_legacy_tokens_dialog.dart +++ b/lib/widgets/migrate_legacy_tokens_dialog.dart @@ -25,10 +25,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:package_info/package_info.dart'; import 'package:privacyidea_authenticator/model/tokens.dart'; -import 'package:privacyidea_authenticator/utils/identifiers.dart'; import 'package:privacyidea_authenticator/utils/localization_utils.dart'; import 'package:privacyidea_authenticator/utils/storage_utils.dart'; -import 'package:privacyidea_authenticator/utils/utils.dart'; import 'package:url_launcher/url_launcher.dart'; class MigrateLegacyTokensDialog extends StatefulWidget { @@ -92,17 +90,13 @@ class _MigrateLegacyTokensDialogState extends State { name: 'migrate_legacy_tokens_dialog.dart'); try { List legacyTokens = await StorageUtil.loadAllTokensLegacy(); - - List currentPushToken = await StorageUtil.loadAllTokens(); - currentPushToken = currentPushToken - .where((element) => element.type == enumAsString(TokenTypes.PIPUSH)) - .toList(); + List currentPushToken = + (await StorageUtil.loadAllTokens()).whereType().toList(); for (Token old in legacyTokens) { // Skip push token which already exist (by serial) - if (old.type == enumAsString(TokenTypes.PIPUSH)) { - if (currentPushToken.indexWhere((element) => - (old as PushToken).serial == (element as PushToken).serial) > -1) { + if (old is PushToken) { + if (currentPushToken.any((e) => old.serial == e.serial)) { continue; } } From eaed48653a486e78696cbe15c72aefc25675f21a Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Tue, 9 Feb 2021 13:39:23 +0100 Subject: [PATCH 37/40] Only show push settings if at least one push token is enrolled --- lib/screens/settings_screen.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index b7299046d..eb33afb46 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -95,11 +95,14 @@ class SettingsScreenState extends State { builder: (context, snapshot) { bool showPushSettingsGroup = true; - List pushTokenList = snapshot.hasData - ? snapshot.data.whereType().toList() + List enrolledPushTokenList = snapshot.hasData + ? snapshot.data + .whereType() + .where((e) => e.isRolledOut) + .toList() : []; - if (pushTokenList.isEmpty) { + if (enrolledPushTokenList.isEmpty) { log('No push tokens exist, push settings are hidden.', name: 'settings_screen.dart'); showPushSettingsGroup = false; @@ -130,11 +133,11 @@ class SettingsScreenState extends State { AppSettings.of(context).streamEnablePolling(), builder: (context, value) { Function onChange; - List unsupported = pushTokenList + List unsupported = enrolledPushTokenList .where((e) => e.url == null) .toList(); - if (pushTokenList.any((element) => + if (enrolledPushTokenList.any((element) => element.isRolledOut && element.url != null)) { // Set onChange to activate switch in ui. onChange = (value) => @@ -153,7 +156,7 @@ class SettingsScreenState extends State { child: Padding( padding: EdgeInsets.only(left: 10), child: unsupported.isNotEmpty && - pushTokenList.isNotEmpty + enrolledPushTokenList.isNotEmpty ? GestureDetector( onTap: () => _showPollingInfo(unsupported), From fe70b38a14483389bee6dbfdef3556398baeb869 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Tue, 9 Feb 2021 13:41:40 +0100 Subject: [PATCH 38/40] Edited change log --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a98fdf7a..c16d9007d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.0.12] - 2021-xx-xx +## [3.0.12] - 2021-02-09 ### Added @@ -17,6 +17,7 @@ - Fixed errors occurring by automatic migration of tokens from prior versions by removing automatic migration - Handle failing synchronization of push tokens by informing the user and closing the dialog +- To prevent bugs, push settings are only accessible if at least one push token is fully enrolled ### Fixed From 3eb470ce35abea147b75709119a8b9a9e83e5770 Mon Sep 17 00:00:00 2001 From: Timo Sturm Date: Tue, 9 Feb 2021 13:42:14 +0100 Subject: [PATCH 39/40] added change link to changelod --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c16d9007d..a1c963e7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ - parallel development of android and ios version +[3.0.12]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.8...v3.0.12 [3.0.8]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.7...v3.0.8 [3.0.7]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.6...v3.0.7 [3.0.6]: https://github.com/privacyidea/pi-authenticator/compare/v3.0.4...v3.0.6 From 962affc22ac31e7d2f2c3de98e9d23d0c51f0c26 Mon Sep 17 00:00:00 2001 From: Timo Date: Tue, 9 Feb 2021 15:09:34 +0100 Subject: [PATCH 40/40] update xcode project files --- ios/Runner.xcodeproj/project.pbxproj | 50 +++++++++++++++---- .../xcshareddata/xcschemes/Runner.xcscheme | 8 +-- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6eca80157..16c35e6ec 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -41,7 +41,7 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146EE1CF9000F007C117D /* privacyIDEA Authenticator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "privacyIDEA Authenticator.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -105,7 +105,7 @@ 97C146EF1CF9000F007C117D /* Products */ = { isa = PBXGroup; children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, + 97C146EE1CF9000F007C117D /* privacyIDEA Authenticator.app */, ); name = Products; sourceTree = ""; @@ -156,7 +156,7 @@ ); name = Runner; productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productReference = 97C146EE1CF9000F007C117D /* privacyIDEA Authenticator.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -264,9 +264,38 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyRSA/SwiftyRSA.framework", + "${BUILT_PRODUCTS_DIR}/barcode_scan/barcode_scan.framework", + "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", + "${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", + "${BUILT_PRODUCTS_DIR}/pi_authenticator_legacy/pi_authenticator_legacy.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", + "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyRSA.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/barcode_scan.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/pi_authenticator_legacy.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -348,7 +377,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -371,13 +400,14 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; @@ -431,7 +461,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -480,7 +510,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -504,13 +534,14 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; @@ -533,13 +564,14 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; - PRODUCT_NAME = "$(TARGET_NAME)"; + PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1d36553cb..00b9d5b98 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -31,7 +31,7 @@ @@ -54,7 +54,7 @@ @@ -71,7 +71,7 @@