diff --git a/lib/api/impl/privacy_idea_container_api.dart b/lib/api/impl/privacy_idea_container_api.dart index 41b1a4f28..68d358650 100644 --- a/lib/api/impl/privacy_idea_container_api.dart +++ b/lib/api/impl/privacy_idea_container_api.dart @@ -54,7 +54,7 @@ class PiContainerApi implements TokenContainerApi { ////////////////////////////// */ @override - Future sync(TokenContainerFinalized container, TokenState tokenState) async { + Future sync(TokenContainerFinalized container, TokenState tokenState) async { final containerTokenTemplates = tokenState.containerTokens(container.serial).toTemplates(); final notLinkedTokenTemplates = (container.policies.initialTokenTransfer) ? tokenState.notLinkedTokens.toTemplates() : []; @@ -75,7 +75,6 @@ class PiContainerApi implements TokenContainerApi { syncResult: syncResult, encKeyPair: encKeyPair, ); - if (decryptedContainerDict == null) return null; final tokens = decryptedContainerDict[TokenContainer.DICT_TOKENS] as Map; final newOtpAuthTokens = (tokens[TokenContainer.DICT_TOKENS_ADD] as List).whereType().map(Uri.parse).toList(); @@ -125,7 +124,7 @@ class PiContainerApi implements TokenContainerApi { } @override - Future finalizeContainer(TokenContainerUnfinalized container, EccUtils eccUtils) async { + Future finalizeContainer(TokenContainerUnfinalized container, [EccUtils eccUtils = const EccUtils()]) async { final ecPrivateClientKey = container.ecPrivateClientKey; if (ecPrivateClientKey == null) { throw LocalizedException(localizedMessage: (l) => l.errorMissingPrivateKey, unlocalizedMessage: AppLocalizationsEn().errorMissingPrivateKey); @@ -143,17 +142,48 @@ class PiContainerApi implements TokenContainerApi { final signature = eccUtils.signWithPrivateKey(ecPrivateClientKey, message); final body = { - if (container.addDeviceInfos == true) TokenContainer.FINALIZE_DEVICE_BRAND: InfoUtils.deviceBrand, - if (container.addDeviceInfos == true) TokenContainer.FINALIZE_DEVICE_MODEL: InfoUtils.deviceModel, - TokenContainer.FINALIZE_CONTAINER_SERIAL: container.serial, - TokenContainer.FINALIZE_PUBLIC_CLIENT_KEY: container.publicClientKey, - ContainerChallenge.SIGNATURE: signature, + 'container_serial': container.serial, + 'public_client_key': container.publicClientKey, + 'device_brand': InfoUtils.deviceBrand, + 'device_model': InfoUtils.deviceModel, + 'signature': signature, }; - return await _ioClient.doPost(url: container.registrationUrl, body: body, sslVerify: container.sslVerify); + final Response response = await _ioClient.doPost(url: container.registrationUrl, body: body, sslVerify: container.sslVerify); + + PiServerResponse? piResponse; + try { + piResponse = response.asPiServerResponse(); + } catch (e) { + Logger.error('Failed to parse response', error: e); + rethrow; + } + + if (piResponse == null || piResponse.isError) { + Logger.debug('Status code: ${response.statusCode}'); + Logger.debug('Response body: ${response.body}'); + final error = piResponse?.asError; + if (error != null) throw error; + throw ResponseError(response); + } + + ContainerFinalizationResponse finalizationResponse; + try { + finalizationResponse = piResponse.asSuccess!.resultValue; + } catch (e) { + Logger.error('Failed to parse response', error: e); + rethrow; + } + + if (piResponse.isError) { + Logger.error('Error while getting container finalization response: ${piResponse.asError!.piServerResultError}'); + throw piResponse.asError!.piServerResultError; + } + + return finalizationResponse; } @override - Future getTransferQrData(TokenContainerFinalized container) async { + Future getRolloverQrData(TokenContainerFinalized container) async { if (container.policies.rolloverAllowed == false) { throw LocalizedException( localizedMessage: (l) => 'l.errorRolloverNotAllowed', // TODO: Add translation @@ -189,18 +219,24 @@ class PiContainerApi implements TokenContainerApi { throw piResponse.asError!.piServerResultError; } - return piResponse.asSuccess!.resultValue.value; + return piResponse.asSuccess!.resultValue; } @override - Future unregister(TokenContainerFinalized container) async { + Future unregister(TokenContainerFinalized container) async { + if (container.policies.unregisterAllowed == false) { + throw LocalizedException( + localizedMessage: (l) => 'l.errorUnregisterNotAllowed', // TODO: Add translation + unlocalizedMessage: 'AppLocalizationsEn().errorUnregisterNotAllowed', + ); + } final unregisterUrl = container.unregisterUrl; final ContainerChallenge challenge; try { challenge = await _getChallenge(container, unregisterUrl); } on PiServerResultError catch (e) { if (e.code == 3001) { - return true; + return UnregisterContainerResult(success: false); } rethrow; } @@ -212,12 +248,12 @@ class PiContainerApi implements TokenContainerApi { final response = await _ioClient.doPost(url: unregisterUrl, body: body, sslVerify: container.sslVerify); - final piResponse = response.asPiServerResponse(); + final piResponse = response.asPiServerResponse(); final errorResponse = piResponse?.asError; if (errorResponse != null) throw errorResponse.piServerResultError; if (response.statusCode != 200 || piResponse == null) throw ResponseError(response); - return piResponse.asSuccess!.resultValue.success; + return piResponse.asSuccess!.resultValue; } /* ////////////////////////////// @@ -284,7 +320,7 @@ class PiContainerApi implements TokenContainerApi { return syncResult; } - Future?> _getContainerDict({ + Future> _getContainerDict({ required ContainerSyncResult syncResult, required KeyPair encKeyPair, }) async { diff --git a/lib/api/interfaces/container_api.dart b/lib/api/interfaces/container_api.dart index 7a10f80a4..d5bf9901b 100644 --- a/lib/api/interfaces/container_api.dart +++ b/lib/api/interfaces/container_api.dart @@ -17,8 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import 'package:http/http.dart'; - +import '../../model/api_results/pi_server_results/pi_server_result_value.dart'; import '../../model/container_policies.dart'; import '../../model/riverpod_states/token_state.dart'; import '../../model/token_container.dart'; @@ -26,10 +25,10 @@ import '../../model/tokens/token.dart'; import '../../utils/ecc_utils.dart'; abstract class TokenContainerApi { - Future finalizeContainer(TokenContainerUnfinalized container, EccUtils eccUtils); - Future getTransferQrData(TokenContainerFinalized container); + Future finalizeContainer(TokenContainerUnfinalized container, EccUtils eccUtils); + Future getRolloverQrData(TokenContainerFinalized container); Future sync(TokenContainerFinalized container, TokenState tokenState); - Future unregister(TokenContainerFinalized container); + Future unregister(TokenContainerFinalized container); } class ContainerSyncUpdates { diff --git a/lib/mains/main_netknights.dart b/lib/mains/main_netknights.dart index abae2b430..62ada7c1d 100644 --- a/lib/mains/main_netknights.dart +++ b/lib/mains/main_netknights.dart @@ -22,6 +22,7 @@ import 'package:easy_dynamic_theme/easy_dynamic_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.dart'; import '../../../../../../../model/riverpod_states/settings_state.dart'; import '../firebase_options/default_firebase_options.dart'; @@ -80,6 +81,8 @@ class PrivacyIDEAAuthenticator extends ConsumerWidget { globalRef = ref; return LayoutBuilder(builder: (context, constraints) { WidgetsBinding.instance.addPostFrameCallback((_) { + final localizations = AppLocalizations.of(context); + if (localizations != null) ref.read(localizationNotifierProvider.notifier).update(localizations); ref.read(appConstraintsNotifierProvider.notifier).update(constraints); }); return MaterialApp( @@ -88,15 +91,14 @@ class PrivacyIDEAAuthenticator extends ConsumerWidget { overscroll: false, ), debugShowCheckedModeBanner: true, - navigatorKey: globalNavigatorKey, - localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, locale: ref.watch(settingsProvider).whenOrNull(data: (data) => data.currentLocale) ?? SettingsState.localeDefault, title: _customization.appName, theme: _customization.generateLightTheme(), darkTheme: _customization.generateDarkTheme(), - scaffoldMessengerKey: globalSnackbarKey, // <= this + scaffoldMessengerKey: globalSnackbarKey, + navigatorKey: globalNavigatorKey, themeMode: EasyDynamicTheme.of(context).themeMode, initialRoute: SplashScreen.routeName, routes: { diff --git a/lib/model/api_results/pi_server_results/pi_server_result_value.dart b/lib/model/api_results/pi_server_results/pi_server_result_value.dart index 6322941a0..836eca156 100644 --- a/lib/model/api_results/pi_server_results/pi_server_result_value.dart +++ b/lib/model/api_results/pi_server_results/pi_server_result_value.dart @@ -34,18 +34,20 @@ sealed class PiServerResultValue extends PiServerResult { @override bool get status => true; - static T fromJsonOfType(Map json) { - Logger.debug('PiServerResultValue.fromJsonOfType<$T>'); + static T uriMapOfType(Map uriMap) { + Logger.debug('PiServerResultValue.uriMapOfType<$T>'); return switch (T) { - const (ContainerChallenge) => ContainerChallenge.fromJson(json) as T, - const (ContainerFinalizationResponse) => ContainerFinalizationResponse.fromJson(json) as T, - const (ContainerSyncResult) => ContainerSyncResult.fromJson(json) as T, - const (TransferQrData) => TransferQrData.fromJson(json) as T, - const (UnregisterContainerResultValue) => UnregisterContainerResultValue.fromJson(json) as T, - _ => throw UnimplementedError('PiServerResultValue.fromJsonOfType<$T>'), + const (ContainerChallenge) => ContainerChallenge.fromUriMap(uriMap) as T, + const (ContainerFinalizationResponse) => ContainerFinalizationResponse.fromUriMap(uriMap) as T, + const (ContainerSyncResult) => ContainerSyncResult.fromUriMap(uriMap) as T, + const (TransferQrData) => TransferQrData.fromUriMap(uriMap) as T, + const (UnregisterContainerResult) => UnregisterContainerResult.fromUriMap(uriMap) as T, + _ => throw UnimplementedError('PiServerResultValue.fromUriMapOfType<$T>'), }; } + Map toUriMap(); + const PiServerResultValue(); } @@ -68,15 +70,15 @@ class ContainerChallenge extends PiServerResultValue { required this.timeStamp, }); - factory ContainerChallenge.fromJson(Map json) { + factory ContainerChallenge.fromUriMap(Map uriMap) { final map = validateMap( - map: json, + map: uriMap, validators: { KEY_ALGORITHM: const ObjectValidator(), NONCE: const ObjectValidator(), TIMESTAMP: const ObjectValidator(), }, - name: 'ContainerChallenge#fromJson', + name: 'ContainerChallenge#fromUriMap', ); return ContainerChallenge( keyAlgorithm: map[KEY_ALGORITHM] as String, @@ -84,6 +86,15 @@ class ContainerChallenge extends PiServerResultValue { timeStamp: map[TIMESTAMP] as String, ); } + + @override + Map toUriMap() { + return { + KEY_ALGORITHM: keyAlgorithm, + NONCE: nonce, + TIMESTAMP: timeStamp, + }; + } } class ContainerFinalizationResponse extends PiServerResultValue { @@ -95,20 +106,28 @@ class ContainerFinalizationResponse extends PiServerResultValue { required this.policies, }); - static ContainerFinalizationResponse fromJson(Map json) { + static ContainerFinalizationResponse fromUriMap(Map uriMap) { final map = validateMap( - map: json, + map: uriMap, validators: { TokenContainer.SYNC_PUBLIC_SERVER_KEY: ObjectValidator(transformer: (v) => const EccUtils().deserializeECPublicKey(v)), TokenContainer.SYNC_POLICIES: ObjectValidator(transformer: (v) => ContainerPolicies.fromUriMap(v)), }, - name: 'ContainerFinalizationResponse#fromJson', + name: 'ContainerFinalizationResponse#fromUriMap', ); return ContainerFinalizationResponse( publicServerKey: map[TokenContainer.SYNC_PUBLIC_SERVER_KEY] as ECPublicKey, policies: map[TokenContainer.SYNC_POLICIES] as ContainerPolicies, ); } + + @override + Map toUriMap() { + return { + TokenContainer.SYNC_PUBLIC_SERVER_KEY: const EccUtils().serializeECPublicKey(publicServerKey), + TokenContainer.SYNC_POLICIES: policies.toJson(), + }; + } } class ContainerSyncResult extends PiServerResultValue { @@ -130,18 +149,18 @@ class ContainerSyncResult extends PiServerResultValue { required this.serverUrl, }); - static ContainerSyncResult fromJson(Map json) { + static ContainerSyncResult fromUriMap(Map uriMap) { final map = validateMap( - map: json, + map: uriMap, validators: { TokenContainer.SYNC_DICT_SERVER: const ObjectValidator(), TokenContainer.SYNC_ENC_ALGORITHM: const ObjectValidator(), - TokenContainer.SYNC_ENC_PARAMS: ObjectValidator(transformer: (v) => EncryptionParams.fromParams(v)), + TokenContainer.SYNC_ENC_PARAMS: ObjectValidator(transformer: (v) => EncryptionParams.fromUriMap(v)), TokenContainer.SYNC_POLICIES: ObjectValidator(transformer: (v) => ContainerPolicies.fromUriMap(v)), TokenContainer.SYNC_PUBLIC_SERVER_KEY: const ObjectValidator(), TokenContainer.SYNC_SERVER_URL: const ObjectValidator(), }, - name: 'ContainerSyncResult#fromJson', + name: 'ContainerSyncResult#fromUriMap', ); return ContainerSyncResult( containerDictEncrypted: map[TokenContainer.SYNC_DICT_SERVER] as String, @@ -152,46 +171,74 @@ class ContainerSyncResult extends PiServerResultValue { serverUrl: map[TokenContainer.SYNC_SERVER_URL] as String, ); } + + @override + Map toUriMap() { + return { + TokenContainer.SYNC_DICT_SERVER: containerDictEncrypted, + TokenContainer.SYNC_ENC_ALGORITHM: encryptionAlgorithm, + TokenContainer.SYNC_ENC_PARAMS: encryptionParams.toUriMap(), + TokenContainer.SYNC_POLICIES: policies.toUriMap(), + TokenContainer.SYNC_PUBLIC_SERVER_KEY: publicServerKey, + TokenContainer.SYNC_SERVER_URL: serverUrl, + }; + } } class TransferQrData extends PiServerResultValue { final String description; final String value; - TransferQrData(this.description, this.value); + const TransferQrData({required this.description, required this.value}); - factory TransferQrData.fromJson(Map json) { - Logger.debug(jsonEncode(json)); + factory TransferQrData.fromUriMap(Map uriMap) { final map = validateMap( - map: json['container_url'] as Map, + map: uriMap['container_url'] as Map, validators: { 'description': const ObjectValidator(), 'value': const ObjectValidator(), }, name: 'TransferQrData', ); - return TransferQrData(map['description'] as String, map['value'] as String); + return TransferQrData(description: map['description'] as String, value: map['value'] as String); + } + + @override + Map toUriMap() { + return { + 'container_url': { + 'description': description, + 'value': value, + } + }; } } -class UnregisterContainerResultValue extends PiServerResultValue { +class UnregisterContainerResult extends PiServerResultValue { static const String CONTAINER_UNREGISTER_SUCCESS = 'success'; final bool success; - const UnregisterContainerResultValue({ + const UnregisterContainerResult({ required this.success, }); - factory UnregisterContainerResultValue.fromJson(Map json) { + factory UnregisterContainerResult.fromUriMap(Map uriMap) { final map = validateMap( - map: json, + map: uriMap, validators: { CONTAINER_UNREGISTER_SUCCESS: const ObjectValidator(), }, - name: 'UnregisterContainerResultValue#fromJson', + name: 'UnregisterContainerResultValue#fromUriMap', ); - return UnregisterContainerResultValue( + return UnregisterContainerResult( success: map[CONTAINER_UNREGISTER_SUCCESS] as bool, ); } + + @override + Map toUriMap() { + return { + CONTAINER_UNREGISTER_SUCCESS: success, + }; + } } diff --git a/lib/model/container_policies.dart b/lib/model/container_policies.dart index 3971ac8be..ed0c10e35 100644 --- a/lib/model/container_policies.dart +++ b/lib/model/container_policies.dart @@ -32,6 +32,8 @@ class ContainerPolicies with _$ContainerPolicies { static const ROLLOVER_ALLOWED = 'container_client_rollover'; static const INITIAL_TOKEN_TRANSFER = 'container_initial_token_transfer'; + const ContainerPolicies._(); + const factory ContainerPolicies({ required bool rolloverAllowed, required bool initialTokenTransfer, @@ -65,78 +67,12 @@ class ContainerPolicies with _$ContainerPolicies { ); } - factory ContainerPolicies.fromJson(Map json) => _$ContainerPoliciesFromJson(json); -} -/** - * - * /* - * privacyIDEA Authenticator - * - * Author: Frank Merkel - * - * Copyright (c) 2024 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 'package:freezed_annotation/freezed_annotation.dart'; - -import '../utils/object_validator.dart'; - -part 'container_policies.freezed.dart'; -part 'container_policies.g.dart'; - -@Freezed(toStringOverride: false, addImplicitFinal: true, toJson: true, fromJson: true) -class ContainerPolicies with _$ContainerPolicies { - static const ROLLOVER_ALLOWED = 'container_client_rollover'; - static const INITIAL_TOKEN_TRANSFER = 'container_initial_token_transfer'; - static const TOKENS_DELETABLE = 'client_token_deletable'; - static const UNREGISTER_ALLOWED = 'client_container_unregister'; - - const factory ContainerPolicies({ - required bool rolloverAllowed, - required bool initialTokenTransfer, - required bool tokensDeletable, - required bool unregisterAllowed, - }) = _ContainerPolicies; - - static const ContainerPolicies defaultSetting = ContainerPolicies( - rolloverAllowed: false, - initialTokenTransfer: false, - tokensDeletable: false, - unregisterAllowed: false, - ); - - static ContainerPolicies fromUriMap(Map map) { - final validated = validateMap( - map: map, - validators: { - ROLLOVER_ALLOWED: stringToBoolValidator, - INITIAL_TOKEN_TRANSFER: stringToBoolValidator, - TOKENS_DELETABLE: stringToBoolValidator, - UNREGISTER_ALLOWED: stringToBoolValidator, - }, - name: 'ContainerPolicies', - ); - return ContainerPolicies( - rolloverAllowed: validated[ROLLOVER_ALLOWED]!, - initialTokenTransfer: validated[INITIAL_TOKEN_TRANSFER]!, - tokensDeletable: validated[TOKENS_DELETABLE]!, - unregisterAllowed: validated[UNREGISTER_ALLOWED]!, - ); - } + Map toUriMap() => { + ROLLOVER_ALLOWED: rolloverAllowed, + INITIAL_TOKEN_TRANSFER: initialTokenTransfer, + TOKENS_DELETABLE: tokensDeletable, + UNREGISTER_ALLOWED: unregisterAllowed, + }; factory ContainerPolicies.fromJson(Map json) => _$ContainerPoliciesFromJson(json); } - - */ \ No newline at end of file diff --git a/lib/model/container_policies.freezed.dart b/lib/model/container_policies.freezed.dart index 3a7ae0d93..d9605ca23 100644 --- a/lib/model/container_policies.freezed.dart +++ b/lib/model/container_policies.freezed.dart @@ -145,12 +145,13 @@ class __$$ContainerPoliciesImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _$ContainerPoliciesImpl implements _ContainerPolicies { +class _$ContainerPoliciesImpl extends _ContainerPolicies { const _$ContainerPoliciesImpl( {required this.rolloverAllowed, required this.initialTokenTransfer, required this.tokensDeletable, - required this.unregisterAllowed}); + required this.unregisterAllowed}) + : super._(); factory _$ContainerPoliciesImpl.fromJson(Map json) => _$$ContainerPoliciesImplFromJson(json); @@ -201,12 +202,13 @@ class _$ContainerPoliciesImpl implements _ContainerPolicies { } } -abstract class _ContainerPolicies implements ContainerPolicies { +abstract class _ContainerPolicies extends ContainerPolicies { const factory _ContainerPolicies( {required final bool rolloverAllowed, required final bool initialTokenTransfer, required final bool tokensDeletable, required final bool unregisterAllowed}) = _$ContainerPoliciesImpl; + const _ContainerPolicies._() : super._(); factory _ContainerPolicies.fromJson(Map json) = _$ContainerPoliciesImpl.fromJson; diff --git a/lib/model/encryption/encryption_params.dart b/lib/model/encryption/encryption_params.dart index 72573e227..377e0ff87 100644 --- a/lib/model/encryption/encryption_params.dart +++ b/lib/model/encryption/encryption_params.dart @@ -20,6 +20,13 @@ import '../../utils/object_validator.dart'; class EncryptionParams { + static const String SYNC_ENC_PARAMS_ALGORITHM = 'algorithm'; + static const String SYNC_ENC_PARAMS_IV = 'init_vector'; + static const String SYNC_ENC_PARAMS_MODE = 'mode'; + static const String SYNC_ENC_PARAMS_TAG = 'tag'; + + // static const String SYNC_DICT_ENCRYPTED = 'container_dict_encrypted'; + final String algorithm; final String initVector; final String mode; @@ -32,22 +39,29 @@ class EncryptionParams { required this.tag, }); - static EncryptionParams fromParams(Map json) { + static EncryptionParams fromUriMap(Map responseBody) { final map = validateMap( - map: json, + map: responseBody, validators: { - 'algorithm': const ObjectValidator(), - 'init_vector': const ObjectValidator(), - 'mode': const ObjectValidator(), - 'tag': const ObjectValidator(), + SYNC_ENC_PARAMS_ALGORITHM: const ObjectValidator(), + SYNC_ENC_PARAMS_IV: const ObjectValidator(), + SYNC_ENC_PARAMS_MODE: const ObjectValidator(), + SYNC_ENC_PARAMS_TAG: const ObjectValidator(), }, - name: 'EncryptionParams#fromJson', + name: 'EncryptionParams#fromResponseBody', ); return EncryptionParams( - algorithm: map['algorithm'] as String, - initVector: map['init_vector'] as String, - mode: map['mode'] as String, - tag: map['tag'] as String, + algorithm: map[SYNC_ENC_PARAMS_ALGORITHM] as String, + initVector: map[SYNC_ENC_PARAMS_IV] as String, + mode: map[SYNC_ENC_PARAMS_MODE] as String, + tag: map[SYNC_ENC_PARAMS_TAG] as String, ); } + + Map toUriMap() => { + SYNC_ENC_PARAMS_ALGORITHM: algorithm, + SYNC_ENC_PARAMS_IV: initVector, + SYNC_ENC_PARAMS_MODE: mode, + SYNC_ENC_PARAMS_TAG: tag, + }; } diff --git a/lib/model/enums/rollout_state.dart b/lib/model/enums/rollout_state.dart index 9017b26ad..a09b67a7f 100644 --- a/lib/model/enums/rollout_state.dart +++ b/lib/model/enums/rollout_state.dart @@ -17,7 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -enum RolloutState { +enum FinalizationState { notStarted, generatingKeyPair, generatingKeyPairFailed, diff --git a/lib/model/extensions/enums/introduction_extension.dart b/lib/model/extensions/enums/introduction_extension.dart index a8a94556e..d78f824ad 100644 --- a/lib/model/extensions/enums/introduction_extension.dart +++ b/lib/model/extensions/enums/introduction_extension.dart @@ -1,11 +1,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:privacyidea_authenticator/model/riverpod_states/settings_state.dart'; import '../../../l10n/app_localizations.dart'; import '../../../utils/riverpod/riverpod_providers/generated_providers/settings_notifier.dart'; import '../../../utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart'; import '../../enums/introduction.dart'; import '../../riverpod_states/introduction_state.dart'; +import '../../riverpod_states/settings_state.dart'; extension IntroductionX on Introduction { /// Checks if the condition for the given state is fulfilled. diff --git a/lib/model/extensions/enums/rollout_state_extension.dart b/lib/model/extensions/enums/rollout_state_extension.dart index fc5185b16..6dbbf0c90 100644 --- a/lib/model/extensions/enums/rollout_state_extension.dart +++ b/lib/model/extensions/enums/rollout_state_extension.dart @@ -22,90 +22,90 @@ import '../../../../../../../l10n/app_localizations.dart'; import '../../../../../../../l10n/app_localizations_en.dart'; import '../../enums/rollout_state.dart'; -extension RolloutStateX on RolloutState { - bool get rolloutStarted => this != RolloutState.notStarted; +extension RolloutStateX on FinalizationState { + bool get rolloutStarted => this != FinalizationState.notStarted; bool get rollOutInProgress => switch (this) { - RolloutState.notStarted => false, - RolloutState.generatingKeyPair => true, - RolloutState.generatingKeyPairFailed => false, - RolloutState.generatingKeyPairCompleted => false, - RolloutState.sendingPublicKey => true, - RolloutState.sendingPublicKeyFailed => false, - RolloutState.sendingPublicKeyCompleted => false, - RolloutState.parsingResponse => true, - RolloutState.parsingResponseFailed => false, - RolloutState.parsingResponseCompleted => false, - RolloutState.completed => false, + FinalizationState.notStarted => false, + FinalizationState.generatingKeyPair => true, + FinalizationState.generatingKeyPairFailed => false, + FinalizationState.generatingKeyPairCompleted => false, + FinalizationState.sendingPublicKey => true, + FinalizationState.sendingPublicKeyFailed => false, + FinalizationState.sendingPublicKeyCompleted => false, + FinalizationState.parsingResponse => true, + FinalizationState.parsingResponseFailed => false, + FinalizationState.parsingResponseCompleted => false, + FinalizationState.completed => false, }; - RolloutState get asFailed => switch (this) { - RolloutState.notStarted => RolloutState.notStarted, - RolloutState.generatingKeyPair => RolloutState.generatingKeyPairFailed, - RolloutState.generatingKeyPairFailed => RolloutState.generatingKeyPairFailed, - RolloutState.generatingKeyPairCompleted => RolloutState.generatingKeyPairFailed, - RolloutState.sendingPublicKey => RolloutState.sendingPublicKeyFailed, - RolloutState.sendingPublicKeyFailed => RolloutState.sendingPublicKeyFailed, - RolloutState.sendingPublicKeyCompleted => RolloutState.sendingPublicKeyFailed, - RolloutState.parsingResponse => RolloutState.parsingResponseFailed, - RolloutState.parsingResponseFailed => RolloutState.parsingResponseFailed, - RolloutState.parsingResponseCompleted => RolloutState.parsingResponseFailed, - RolloutState.completed => RolloutState.completed, + FinalizationState get asFailed => switch (this) { + FinalizationState.notStarted => FinalizationState.notStarted, + FinalizationState.generatingKeyPair => FinalizationState.generatingKeyPairFailed, + FinalizationState.generatingKeyPairFailed => FinalizationState.generatingKeyPairFailed, + FinalizationState.generatingKeyPairCompleted => FinalizationState.generatingKeyPairFailed, + FinalizationState.sendingPublicKey => FinalizationState.sendingPublicKeyFailed, + FinalizationState.sendingPublicKeyFailed => FinalizationState.sendingPublicKeyFailed, + FinalizationState.sendingPublicKeyCompleted => FinalizationState.sendingPublicKeyFailed, + FinalizationState.parsingResponse => FinalizationState.parsingResponseFailed, + FinalizationState.parsingResponseFailed => FinalizationState.parsingResponseFailed, + FinalizationState.parsingResponseCompleted => FinalizationState.parsingResponseFailed, + FinalizationState.completed => FinalizationState.completed, }; - RolloutState get asCompleted => switch (this) { - RolloutState.notStarted => RolloutState.notStarted, - RolloutState.generatingKeyPair => RolloutState.generatingKeyPairCompleted, - RolloutState.generatingKeyPairFailed => RolloutState.generatingKeyPairCompleted, - RolloutState.generatingKeyPairCompleted => RolloutState.generatingKeyPairCompleted, - RolloutState.sendingPublicKey => RolloutState.sendingPublicKeyCompleted, - RolloutState.sendingPublicKeyFailed => RolloutState.sendingPublicKeyCompleted, - RolloutState.sendingPublicKeyCompleted => RolloutState.sendingPublicKeyCompleted, - RolloutState.parsingResponse => RolloutState.parsingResponseCompleted, - RolloutState.parsingResponseFailed => RolloutState.parsingResponseCompleted, - RolloutState.parsingResponseCompleted => RolloutState.parsingResponseCompleted, - RolloutState.completed => RolloutState.completed, + FinalizationState get asCompleted => switch (this) { + FinalizationState.notStarted => FinalizationState.notStarted, + FinalizationState.generatingKeyPair => FinalizationState.generatingKeyPairCompleted, + FinalizationState.generatingKeyPairFailed => FinalizationState.generatingKeyPairCompleted, + FinalizationState.generatingKeyPairCompleted => FinalizationState.generatingKeyPairCompleted, + FinalizationState.sendingPublicKey => FinalizationState.sendingPublicKeyCompleted, + FinalizationState.sendingPublicKeyFailed => FinalizationState.sendingPublicKeyCompleted, + FinalizationState.sendingPublicKeyCompleted => FinalizationState.sendingPublicKeyCompleted, + FinalizationState.parsingResponse => FinalizationState.parsingResponseCompleted, + FinalizationState.parsingResponseFailed => FinalizationState.parsingResponseCompleted, + FinalizationState.parsingResponseCompleted => FinalizationState.parsingResponseCompleted, + FinalizationState.completed => FinalizationState.completed, }; - RolloutState get next => switch (this) { - RolloutState.notStarted => RolloutState.generatingKeyPair, - RolloutState.generatingKeyPair => RolloutState.sendingPublicKey, - RolloutState.generatingKeyPairFailed => RolloutState.generatingKeyPair, - RolloutState.generatingKeyPairCompleted => RolloutState.sendingPublicKey, - RolloutState.sendingPublicKey => RolloutState.parsingResponse, - RolloutState.sendingPublicKeyFailed => RolloutState.sendingPublicKey, - RolloutState.sendingPublicKeyCompleted => RolloutState.parsingResponse, - RolloutState.parsingResponse => RolloutState.completed, - RolloutState.parsingResponseFailed => RolloutState.parsingResponse, - RolloutState.parsingResponseCompleted => RolloutState.completed, - RolloutState.completed => RolloutState.completed, + FinalizationState get next => switch (this) { + FinalizationState.notStarted => FinalizationState.generatingKeyPair, + FinalizationState.generatingKeyPair => FinalizationState.sendingPublicKey, + FinalizationState.generatingKeyPairFailed => FinalizationState.generatingKeyPair, + FinalizationState.generatingKeyPairCompleted => FinalizationState.sendingPublicKey, + FinalizationState.sendingPublicKey => FinalizationState.parsingResponse, + FinalizationState.sendingPublicKeyFailed => FinalizationState.sendingPublicKey, + FinalizationState.sendingPublicKeyCompleted => FinalizationState.parsingResponse, + FinalizationState.parsingResponse => FinalizationState.completed, + FinalizationState.parsingResponseFailed => FinalizationState.parsingResponse, + FinalizationState.parsingResponseCompleted => FinalizationState.completed, + FinalizationState.completed => FinalizationState.completed, }; bool get isFailed => switch (this) { - RolloutState.notStarted => false, - RolloutState.generatingKeyPair => false, - RolloutState.generatingKeyPairFailed => true, - RolloutState.generatingKeyPairCompleted => false, - RolloutState.sendingPublicKey => false, - RolloutState.sendingPublicKeyFailed => true, - RolloutState.sendingPublicKeyCompleted => false, - RolloutState.parsingResponse => false, - RolloutState.parsingResponseFailed => true, - RolloutState.parsingResponseCompleted => false, - RolloutState.completed => false, + FinalizationState.notStarted => false, + FinalizationState.generatingKeyPair => false, + FinalizationState.generatingKeyPairFailed => true, + FinalizationState.generatingKeyPairCompleted => false, + FinalizationState.sendingPublicKey => false, + FinalizationState.sendingPublicKeyFailed => true, + FinalizationState.sendingPublicKeyCompleted => false, + FinalizationState.parsingResponse => false, + FinalizationState.parsingResponseFailed => true, + FinalizationState.parsingResponseCompleted => false, + FinalizationState.completed => false, }; String get rolloutMsg => rolloutMsgLocalized(AppLocalizationsEn()); String rolloutMsgLocalized(AppLocalizations localizations) => switch (this) { - RolloutState.notStarted => localizations.rolloutStateNotStarted, - RolloutState.generatingKeyPair => localizations.rolloutStateGeneratingKeyPair, - RolloutState.generatingKeyPairFailed => localizations.rolloutStateGeneratingKeyPairFailed, - RolloutState.generatingKeyPairCompleted => localizations.rolloutStateGeneratingKeyPairCompleted, - RolloutState.sendingPublicKey => localizations.rolloutStateSendingPublicKey, - RolloutState.sendingPublicKeyFailed => localizations.rolloutStateSendingPublicKeyFailed, - RolloutState.sendingPublicKeyCompleted => localizations.rolloutStateSendingPublicKeyCompleted, - RolloutState.parsingResponse => localizations.rolloutStateParsingResponse, - RolloutState.parsingResponseFailed => localizations.rolloutStateParsingResponseFailed, - RolloutState.parsingResponseCompleted => localizations.rolloutStateParsingResponseCompleted, - RolloutState.completed => localizations.rolloutStateCompleted, + FinalizationState.notStarted => localizations.rolloutStateNotStarted, + FinalizationState.generatingKeyPair => localizations.rolloutStateGeneratingKeyPair, + FinalizationState.generatingKeyPairFailed => localizations.rolloutStateGeneratingKeyPairFailed, + FinalizationState.generatingKeyPairCompleted => localizations.rolloutStateGeneratingKeyPairCompleted, + FinalizationState.sendingPublicKey => localizations.rolloutStateSendingPublicKey, + FinalizationState.sendingPublicKeyFailed => localizations.rolloutStateSendingPublicKeyFailed, + FinalizationState.sendingPublicKeyCompleted => localizations.rolloutStateSendingPublicKeyCompleted, + FinalizationState.parsingResponse => localizations.rolloutStateParsingResponse, + FinalizationState.parsingResponseFailed => localizations.rolloutStateParsingResponseFailed, + FinalizationState.parsingResponseCompleted => localizations.rolloutStateParsingResponseCompleted, + FinalizationState.completed => localizations.rolloutStateCompleted, }; } diff --git a/lib/model/pi_server_response.dart b/lib/model/pi_server_response.dart index 758a76d58..7f51c8263 100644 --- a/lib/model/pi_server_response.dart +++ b/lib/model/pi_server_response.dart @@ -105,7 +105,7 @@ class PiServerResponse with _$PiServerResponse { statusCode: statisCode, id: map[ID], jsonrpc: map[JSONRPC], - resultValue: PiServerResultValue.fromJsonOfType(result[RESULT_VALUE]), + resultValue: PiServerResultValue.uriMapOfType(result[RESULT_VALUE]), time: map[TIME], version: map[VERSION], versionNumber: map[VERSION_NUMBER] ?? map[VERSION].split(' ')[1], diff --git a/lib/model/processor_result.dart b/lib/model/processor_result.dart index 9c383b574..96f61c985 100644 --- a/lib/model/processor_result.dart +++ b/lib/model/processor_result.dart @@ -19,8 +19,8 @@ */ import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations.dart'; -import '../l10n/app_localizations.dart'; import '../utils/globals.dart'; import '../utils/logger.dart'; import '../utils/object_validator.dart'; @@ -37,7 +37,7 @@ abstract class ProcessorResult with _$ProcessorResult { ObjectValidator? resultHandlerType, }) = ProcessorResultSuccess; const factory ProcessorResult.failed( - String message, { + String Function(AppLocalizations) message, { dynamic error, ObjectValidator? resultHandlerType, }) = ProcessorResultFailed; @@ -55,7 +55,7 @@ extension ListProcessorResult on List> { List getData() { final results = toList(); if (results.isEmpty) { - showStatusMessage(message: 'No data found in QR code.'); + showStatusMessage(message: (_) => 'No data found in QR code.'); // TODO: Localize Logger.warning('No data found in QR code.'); return []; } @@ -64,7 +64,10 @@ extension ListProcessorResult on List> { WidgetsBinding.instance.addPostFrameCallback((_) async { for (var failedResult in failedResults) { - globalRef?.read(statusMessageProvider.notifier).state = (AppLocalizations.of((await globalContext))!.malformedData, failedResult.message); + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.malformedData, + details: failedResult.message, + ); } }); diff --git a/lib/model/processor_result.freezed.dart b/lib/model/processor_result.freezed.dart index 6b28ff6f7..2f80d926c 100644 --- a/lib/model/processor_result.freezed.dart +++ b/lib/model/processor_result.freezed.dart @@ -23,8 +23,8 @@ mixin _$ProcessorResult { required TResult Function( T resultData, ObjectValidator? resultHandlerType) success, - required TResult Function(String message, dynamic error, - ObjectValidator? resultHandlerType) + required TResult Function(String Function(AppLocalizations) message, + dynamic error, ObjectValidator? resultHandlerType) failed, }) => throw _privateConstructorUsedError; @@ -33,7 +33,7 @@ mixin _$ProcessorResult { TResult? Function( T resultData, ObjectValidator? resultHandlerType)? success, - TResult? Function(String message, dynamic error, + TResult? Function(String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType)? failed, }) => @@ -43,7 +43,7 @@ mixin _$ProcessorResult { TResult Function( T resultData, ObjectValidator? resultHandlerType)? success, - TResult Function(String message, dynamic error, + TResult Function(String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType)? failed, required TResult orElse(), @@ -200,8 +200,8 @@ class _$ProcessorResultSuccessImpl extends ProcessorResultSuccess { required TResult Function( T resultData, ObjectValidator? resultHandlerType) success, - required TResult Function(String message, dynamic error, - ObjectValidator? resultHandlerType) + required TResult Function(String Function(AppLocalizations) message, + dynamic error, ObjectValidator? resultHandlerType) failed, }) { return success(resultData, resultHandlerType); @@ -213,7 +213,7 @@ class _$ProcessorResultSuccessImpl extends ProcessorResultSuccess { TResult? Function( T resultData, ObjectValidator? resultHandlerType)? success, - TResult? Function(String message, dynamic error, + TResult? Function(String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType)? failed, }) { @@ -226,7 +226,7 @@ class _$ProcessorResultSuccessImpl extends ProcessorResultSuccess { TResult Function( T resultData, ObjectValidator? resultHandlerType)? success, - TResult Function(String message, dynamic error, + TResult Function(String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType)? failed, required TResult orElse(), @@ -297,7 +297,7 @@ abstract class _$$ProcessorResultFailedImplCopyWith @override @useResult $Res call( - {String message, + {String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType}); } @@ -325,7 +325,7 @@ class __$$ProcessorResultFailedImplCopyWithImpl null == message ? _value.message : message // ignore: cast_nullable_to_non_nullable - as String, + as String Function(AppLocalizations), error: freezed == error ? _value.error : error // ignore: cast_nullable_to_non_nullable @@ -346,7 +346,7 @@ class _$ProcessorResultFailedImpl extends ProcessorResultFailed { : super._(); @override - final String message; + final String Function(AppLocalizations) message; @override final dynamic error; @override @@ -387,8 +387,8 @@ class _$ProcessorResultFailedImpl extends ProcessorResultFailed { required TResult Function( T resultData, ObjectValidator? resultHandlerType) success, - required TResult Function(String message, dynamic error, - ObjectValidator? resultHandlerType) + required TResult Function(String Function(AppLocalizations) message, + dynamic error, ObjectValidator? resultHandlerType) failed, }) { return failed(message, error, resultHandlerType); @@ -400,7 +400,7 @@ class _$ProcessorResultFailedImpl extends ProcessorResultFailed { TResult? Function( T resultData, ObjectValidator? resultHandlerType)? success, - TResult? Function(String message, dynamic error, + TResult? Function(String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType)? failed, }) { @@ -413,7 +413,7 @@ class _$ProcessorResultFailedImpl extends ProcessorResultFailed { TResult Function( T resultData, ObjectValidator? resultHandlerType)? success, - TResult Function(String message, dynamic error, + TResult Function(String Function(AppLocalizations) message, dynamic error, ObjectValidator? resultHandlerType)? failed, required TResult orElse(), @@ -457,13 +457,14 @@ class _$ProcessorResultFailedImpl extends ProcessorResultFailed { } abstract class ProcessorResultFailed extends ProcessorResult { - const factory ProcessorResultFailed(final String message, + const factory ProcessorResultFailed( + final String Function(AppLocalizations) message, {final dynamic error, final ObjectValidator? resultHandlerType}) = _$ProcessorResultFailedImpl; const ProcessorResultFailed._() : super._(); - String get message; + String Function(AppLocalizations) get message; dynamic get error; @override ObjectValidator? get resultHandlerType; diff --git a/lib/model/token_container.dart b/lib/model/token_container.dart index 164adfb81..d7b12f4ce 100644 --- a/lib/model/token_container.dart +++ b/lib/model/token_container.dart @@ -54,10 +54,6 @@ class TokenContainer with _$TokenContainer { static const String SYNC_POLICIES = 'policies'; static const String SYNC_PUBLIC_SERVER_KEY = 'public_server_key'; static const String SYNC_SERVER_URL = 'server_url'; -// static const String SYNC_ENC_PARAMS_MODE = 'mode'; -// static const String SYNC_ENC_PARAMS_IV = 'init_vector'; -// static const String SYNC_ENC_PARAMS_TAG = 'tag'; -// static const String SYNC_DICT_ENCRYPTED = 'container_dict_encrypted'; // Container Mapping: static const String DICT_CONTAINER = 'container'; @@ -150,7 +146,7 @@ class TokenContainer with _$TokenContainer { required Algorithms hashAlgorithm, required bool sslVerify, @Default('privacyIDEA') String serverName, - @Default(RolloutState.completed) RolloutState finalizationState, + @Default(FinalizationState.completed) FinalizationState finalizationState, @Default(ContainerPolicies.defaultSetting) ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -169,7 +165,7 @@ class TokenContainer with _$TokenContainer { required Algorithms hashAlgorithm, required bool sslVerify, @Default('privacyIDEA') String serverName, - @Default(RolloutState.completed) RolloutState finalizationState, + @Default(FinalizationState.completed) FinalizationState finalizationState, @Default(SyncState.notStarted) SyncState syncState, @Default(ContainerPolicies.defaultSetting) ContainerPolicies policies, String? passphraseQuestion, @@ -202,7 +198,7 @@ class TokenContainer with _$TokenContainer { hashAlgorithm: hashAlgorithm, sslVerify: sslVerify, passphraseQuestion: passphraseQuestion, - finalizationState: RolloutState.completed, + finalizationState: FinalizationState.completed, serverName: serverName, policies: policies, syncState: SyncState.notStarted, @@ -223,13 +219,18 @@ class TokenContainer with _$TokenContainer { TokenContainer withClientKeyPair(AsymmetricKeyPair keyPair) => copyWith( publicClientKey: eccUtils.serializeECPublicKey(keyPair.publicKey), privateClientKey: eccUtils.serializeECPrivateKey(keyPair.privateKey), - finalizationState: RolloutState.generatingKeyPairCompleted, + finalizationState: FinalizationState.generatingKeyPairCompleted, ); - factory TokenContainer.fromJson(Map json) => json["runtimeType"] == "finalized" - ? (_$TokenContainerFromJson(json) as TokenContainerFinalized) - .copyWith(syncState: json["syncState"] == "syncing" ? SyncState.failed : SyncState.values.byName(json["syncState"])) - : _$TokenContainerFromJson(json); + factory TokenContainer.fromJson(Map json) { + print('PublicServerKey: ${json['publicServerKey']}'); + print('PrivateClientKey: ${json['privateClientKey']}'); + print('PublicClientKey: ${json['publicClientKey']}'); + return json["runtimeType"] == "finalized" + ? (_$TokenContainerFromJson(json) as TokenContainerFinalized) + .copyWith(syncState: json["syncState"] == "syncing" ? SyncState.failed : SyncState.values.byName(json["syncState"])) + : _$TokenContainerFromJson(json); + } @override String toString() => '$runtimeType(' diff --git a/lib/model/token_container.freezed.dart b/lib/model/token_container.freezed.dart index f074b0bc7..5d9b4f066 100644 --- a/lib/model/token_container.freezed.dart +++ b/lib/model/token_container.freezed.dart @@ -38,7 +38,7 @@ mixin _$TokenContainer { Algorithms get hashAlgorithm => throw _privateConstructorUsedError; bool get sslVerify => throw _privateConstructorUsedError; String get serverName => throw _privateConstructorUsedError; - RolloutState get finalizationState => throw _privateConstructorUsedError; + FinalizationState get finalizationState => throw _privateConstructorUsedError; ContainerPolicies get policies => throw _privateConstructorUsedError; String? get passphraseQuestion => throw _privateConstructorUsedError; String? get publicServerKey => throw _privateConstructorUsedError; @@ -57,7 +57,7 @@ mixin _$TokenContainer { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -75,7 +75,7 @@ mixin _$TokenContainer { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -98,7 +98,7 @@ mixin _$TokenContainer { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -116,7 +116,7 @@ mixin _$TokenContainer { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -139,7 +139,7 @@ mixin _$TokenContainer { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -157,7 +157,7 @@ mixin _$TokenContainer { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -214,7 +214,7 @@ abstract class $TokenContainerCopyWith<$Res> { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, String? passphraseQuestion, String publicServerKey, @@ -295,7 +295,7 @@ class _$TokenContainerCopyWithImpl<$Res, $Val extends TokenContainer> finalizationState: null == finalizationState ? _value.finalizationState : finalizationState // ignore: cast_nullable_to_non_nullable - as RolloutState, + as FinalizationState, policies: null == policies ? _value.policies : policies // ignore: cast_nullable_to_non_nullable @@ -350,7 +350,7 @@ abstract class _$$TokenContainerUnfinalizedImplCopyWith<$Res> Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -438,7 +438,7 @@ class __$$TokenContainerUnfinalizedImplCopyWithImpl<$Res> finalizationState: null == finalizationState ? _value.finalizationState : finalizationState // ignore: cast_nullable_to_non_nullable - as RolloutState, + as FinalizationState, policies: null == policies ? _value.policies : policies // ignore: cast_nullable_to_non_nullable @@ -481,7 +481,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { required this.hashAlgorithm, required this.sslVerify, this.serverName = 'privacyIDEA', - this.finalizationState = RolloutState.completed, + this.finalizationState = FinalizationState.completed, this.policies = ContainerPolicies.defaultSetting, this.addDeviceInfos, this.passphraseQuestion, @@ -518,7 +518,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { final String serverName; @override @JsonKey() - final RolloutState finalizationState; + final FinalizationState finalizationState; @override @JsonKey() final ContainerPolicies policies; @@ -618,7 +618,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -636,7 +636,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -679,7 +679,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -697,7 +697,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -740,7 +740,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -758,7 +758,7 @@ class _$TokenContainerUnfinalizedImpl extends TokenContainerUnfinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -842,7 +842,7 @@ abstract class TokenContainerUnfinalized extends TokenContainer { required final Algorithms hashAlgorithm, required final bool sslVerify, final String serverName, - final RolloutState finalizationState, + final FinalizationState finalizationState, final ContainerPolicies policies, final bool? addDeviceInfos, final String? passphraseQuestion, @@ -874,7 +874,7 @@ abstract class TokenContainerUnfinalized extends TokenContainer { @override String get serverName; @override - RolloutState get finalizationState; + FinalizationState get finalizationState; @override ContainerPolicies get policies; bool? get addDeviceInfos; @@ -914,7 +914,7 @@ abstract class _$$TokenContainerFinalizedImplCopyWith<$Res> Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -997,7 +997,7 @@ class __$$TokenContainerFinalizedImplCopyWithImpl<$Res> finalizationState: null == finalizationState ? _value.finalizationState : finalizationState // ignore: cast_nullable_to_non_nullable - as RolloutState, + as FinalizationState, syncState: null == syncState ? _value.syncState : syncState // ignore: cast_nullable_to_non_nullable @@ -1039,7 +1039,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { required this.hashAlgorithm, required this.sslVerify, this.serverName = 'privacyIDEA', - this.finalizationState = RolloutState.completed, + this.finalizationState = FinalizationState.completed, this.syncState = SyncState.notStarted, this.policies = ContainerPolicies.defaultSetting, this.passphraseQuestion, @@ -1074,7 +1074,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { final String serverName; @override @JsonKey() - final RolloutState finalizationState; + final FinalizationState finalizationState; @override @JsonKey() final SyncState syncState; @@ -1173,7 +1173,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -1191,7 +1191,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -1233,7 +1233,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -1251,7 +1251,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -1293,7 +1293,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, ContainerPolicies policies, bool? addDeviceInfos, String? passphraseQuestion, @@ -1311,7 +1311,7 @@ class _$TokenContainerFinalizedImpl extends TokenContainerFinalized { Algorithms hashAlgorithm, bool sslVerify, String serverName, - RolloutState finalizationState, + FinalizationState finalizationState, SyncState syncState, ContainerPolicies policies, String? passphraseQuestion, @@ -1393,7 +1393,7 @@ abstract class TokenContainerFinalized extends TokenContainer { required final Algorithms hashAlgorithm, required final bool sslVerify, final String serverName, - final RolloutState finalizationState, + final FinalizationState finalizationState, final SyncState syncState, final ContainerPolicies policies, final String? passphraseQuestion, @@ -1424,7 +1424,7 @@ abstract class TokenContainerFinalized extends TokenContainer { @override String get serverName; @override - RolloutState get finalizationState; + FinalizationState get finalizationState; SyncState get syncState; @override ContainerPolicies get policies; diff --git a/lib/model/token_container.g.dart b/lib/model/token_container.g.dart index f103d291e..7d7753309 100644 --- a/lib/model/token_container.g.dart +++ b/lib/model/token_container.g.dart @@ -21,8 +21,8 @@ _$TokenContainerUnfinalizedImpl _$$TokenContainerUnfinalizedImplFromJson( sslVerify: json['sslVerify'] as bool, serverName: json['serverName'] as String? ?? 'privacyIDEA', finalizationState: $enumDecodeNullable( - _$RolloutStateEnumMap, json['finalizationState']) ?? - RolloutState.completed, + _$FinalizationStateEnumMap, json['finalizationState']) ?? + FinalizationState.completed, policies: json['policies'] == null ? ContainerPolicies.defaultSetting : ContainerPolicies.fromJson( @@ -48,7 +48,8 @@ Map _$$TokenContainerUnfinalizedImplToJson( 'hashAlgorithm': _$AlgorithmsEnumMap[instance.hashAlgorithm]!, 'sslVerify': instance.sslVerify, 'serverName': instance.serverName, - 'finalizationState': _$RolloutStateEnumMap[instance.finalizationState]!, + 'finalizationState': + _$FinalizationStateEnumMap[instance.finalizationState]!, 'policies': instance.policies, 'addDeviceInfos': instance.addDeviceInfos, 'passphraseQuestion': instance.passphraseQuestion, @@ -108,18 +109,18 @@ const _$AlgorithmsEnumMap = { Algorithms.SHA512: 'SHA512', }; -const _$RolloutStateEnumMap = { - RolloutState.notStarted: 'notStarted', - RolloutState.generatingKeyPair: 'generatingKeyPair', - RolloutState.generatingKeyPairFailed: 'generatingKeyPairFailed', - RolloutState.generatingKeyPairCompleted: 'generatingKeyPairCompleted', - RolloutState.sendingPublicKey: 'sendingPublicKey', - RolloutState.sendingPublicKeyFailed: 'sendingPublicKeyFailed', - RolloutState.sendingPublicKeyCompleted: 'sendingPublicKeyCompleted', - RolloutState.parsingResponse: 'parsingResponse', - RolloutState.parsingResponseFailed: 'parsingResponseFailed', - RolloutState.parsingResponseCompleted: 'parsingResponseCompleted', - RolloutState.completed: 'completed', +const _$FinalizationStateEnumMap = { + FinalizationState.notStarted: 'notStarted', + FinalizationState.generatingKeyPair: 'generatingKeyPair', + FinalizationState.generatingKeyPairFailed: 'generatingKeyPairFailed', + FinalizationState.generatingKeyPairCompleted: 'generatingKeyPairCompleted', + FinalizationState.sendingPublicKey: 'sendingPublicKey', + FinalizationState.sendingPublicKeyFailed: 'sendingPublicKeyFailed', + FinalizationState.sendingPublicKeyCompleted: 'sendingPublicKeyCompleted', + FinalizationState.parsingResponse: 'parsingResponse', + FinalizationState.parsingResponseFailed: 'parsingResponseFailed', + FinalizationState.parsingResponseCompleted: 'parsingResponseCompleted', + FinalizationState.completed: 'completed', }; _$TokenContainerFinalizedImpl _$$TokenContainerFinalizedImplFromJson( @@ -136,8 +137,8 @@ _$TokenContainerFinalizedImpl _$$TokenContainerFinalizedImplFromJson( sslVerify: json['sslVerify'] as bool, serverName: json['serverName'] as String? ?? 'privacyIDEA', finalizationState: $enumDecodeNullable( - _$RolloutStateEnumMap, json['finalizationState']) ?? - RolloutState.completed, + _$FinalizationStateEnumMap, json['finalizationState']) ?? + FinalizationState.completed, syncState: $enumDecodeNullable(_$SyncStateEnumMap, json['syncState']) ?? SyncState.notStarted, policies: json['policies'] == null @@ -163,7 +164,8 @@ Map _$$TokenContainerFinalizedImplToJson( 'hashAlgorithm': _$AlgorithmsEnumMap[instance.hashAlgorithm]!, 'sslVerify': instance.sslVerify, 'serverName': instance.serverName, - 'finalizationState': _$RolloutStateEnumMap[instance.finalizationState]!, + 'finalizationState': + _$FinalizationStateEnumMap[instance.finalizationState]!, 'syncState': _$SyncStateEnumMap[instance.syncState]!, 'policies': instance.policies, 'passphraseQuestion': instance.passphraseQuestion, diff --git a/lib/model/tokens/hotp_token.dart b/lib/model/tokens/hotp_token.dart index 7f7aa175f..627c3d4f2 100644 --- a/lib/model/tokens/hotp_token.dart +++ b/lib/model/tokens/hotp_token.dart @@ -206,18 +206,18 @@ class HOTPToken extends OTPToken { /// This is used to create a map that typically was created from a uri. /// ```dart /// -------------------------- [Token] -------------------------------- - /// | Token.SERIAL: serial, (optional) | - /// | Token.LABEL: label, | - /// | Token.ISSUER: issuer, | + /// | Token.SERIAL: serial, (optional) | + /// | Token.LABEL: label, | + /// | Token.ISSUER: issuer, | /// | CONTAINER_SERIAL: containerSerial, (optional) | - /// | CHECKED_CONTAINERS: checkedContainer, | + /// | CHECKED_CONTAINERS: checkedContainer, | /// | TOKEN_ID: id, | - /// | Token.TYPE: type, | - /// | Token.IMAGE: tokenImage, (optional) | + /// | Token.TYPE: type, | + /// | Token.IMAGE: tokenImage, (optional) | /// | SORTABLE_INDEX: sortIndex, (optional) | /// | FOLDER_ID: folderId, (optional) | /// | TOKEN_ORIGIN: origin, (optional) | - /// | Token.PIN: pin, | + /// | Token.PIN: pin, | /// | TOKEN_HIDDEN: isHidden, | /// ------------------------------------------------------------------- /// ------------------------- [OTPToken] ------------------------------ @@ -227,7 +227,7 @@ class HOTPToken extends OTPToken { /// | OTPToken.OTP_VALUES: [otpValue, nextValue], (if serial is null) | /// ------------------------------------------------------------------- /// ------------------------ [HOTPToken] ------------------------------ - /// | COUNTER: counter, | + /// | COUNTER: counter, | /// ------------------------------------------------------------------- /// ``` @override diff --git a/lib/processors/scheme_processors/home_widget_processor.dart b/lib/processors/scheme_processors/home_widget_processor.dart index 7a6848220..6b2036364 100644 --- a/lib/processors/scheme_processors/home_widget_processor.dart +++ b/lib/processors/scheme_processors/home_widget_processor.dart @@ -38,7 +38,7 @@ class HomeWidgetProcessor implements SchemeProcessor { if (processor == null) { return [ ProcessorResult.failed( - 'No processor found for host: ${uri.host}', + (_) => 'No processor found for host: ${uri.host}', // TODO: Localize resultHandlerType: null, ) ]; @@ -54,7 +54,7 @@ class HomeWidgetProcessor implements SchemeProcessor { if (uri.host != 'show') { return [ ProcessorResult.failed( - 'Invalid host: ${uri.host} for scheme: ${uri.scheme}', + (_) => 'Invalid host: ${uri.host} for scheme: ${uri.scheme}', // TODO: Localize resultHandlerType: null, ) ]; @@ -62,8 +62,8 @@ class HomeWidgetProcessor implements SchemeProcessor { final widgetId = uri.queryParameters['widgetId']; if (widgetId == null) { return [ - const ProcessorResult.failed( - 'Missing widgetId', + ProcessorResult.failed( + (_) => 'Missing widgetId', // TODO: Localize resultHandlerType: null, ) ]; @@ -77,7 +77,7 @@ class HomeWidgetProcessor implements SchemeProcessor { if (uri.host != 'copy') { return [ ProcessorResult.failed( - 'Invalid host: ${uri.host} for scheme: ${uri.scheme}', + (_) => 'Invalid host: ${uri.host} for scheme: ${uri.scheme}', // TODO: Localize resultHandlerType: null, ) ]; @@ -85,8 +85,8 @@ class HomeWidgetProcessor implements SchemeProcessor { final widgetId = uri.queryParameters['widgetId']; if (widgetId == null) { return [ - const ProcessorResult.failed( - 'Missing widgetId', + ProcessorResult.failed( + (_) => 'Missing widgetId', // TODO: Localize resultHandlerType: null, ) ]; @@ -100,7 +100,7 @@ class HomeWidgetProcessor implements SchemeProcessor { if (uri.host != 'action') { return [ ProcessorResult.failed( - 'Invalid host: ${uri.host} for scheme: ${uri.scheme}', + (_) => 'Invalid host: ${uri.host} for scheme: ${uri.scheme}', // TODO: Localize resultHandlerType: null, ) ]; @@ -108,8 +108,8 @@ class HomeWidgetProcessor implements SchemeProcessor { final widgetId = uri.queryParameters['widgetId']; if (widgetId == null) { return [ - const ProcessorResult.failed( - 'Missing widgetId', + ProcessorResult.failed( + (_) => 'Missing widgetId', // TODO: Localize resultHandlerType: null, ) ]; diff --git a/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart b/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart index 79dd4e8f8..7c24e2df3 100644 --- a/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart +++ b/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart @@ -47,8 +47,8 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { stackTrace: StackTrace.current, ); return [ - const ProcessorResult.failed( - 'Cannot Navigate without context', + ProcessorResult.failed( + (_) => 'Cannot Navigate without context', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; @@ -59,7 +59,7 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { Logger.warning('HomeWidgetNavigateProcessor: No processor found for host: ${uri.host}'); return [ ProcessorResult.failed( - 'No processor found for host: ${uri.host}', + (_) => 'No processor found for host: ${uri.host}', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; @@ -79,7 +79,7 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { Logger.warning('HomeWidgetNavigateProcessor: Missing id for link: ${uri.host}'); return [ ProcessorResult.failed( - 'Missing id for link: ${uri.host}', + (_) => 'Missing id for link: ${uri.host}', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; @@ -106,7 +106,7 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { Logger.warning('Invalid query parameters for showlocked: ${uri.queryParameters}'); return [ ProcessorResult.failed( - 'Missing id for showlocked: ${uri.host}', + (_) => 'Missing id for showlocked: ${uri.host}', resultHandlerType: resultHandlerType, ) ]; @@ -119,7 +119,7 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { Logger.warning('Could not find token for widget id: ${uri.queryParameters['id']}'); return [ ProcessorResult.failed( - 'Could not find token for widget id: ${uri.queryParameters['id}']}', + (_) => 'Could not find token for widget id: ${uri.queryParameters['id}']}', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; @@ -128,8 +128,8 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { if (globalRef == null) { Logger.warning('Could not find globalRef'); return [ - const ProcessorResult.failed( - 'Could not find globalRef', + ProcessorResult.failed( + (_) => 'Could not find globalRef', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; diff --git a/lib/processors/scheme_processors/token_container_processor.dart b/lib/processors/scheme_processors/token_container_processor.dart index 8d0b4b190..2e3583e03 100644 --- a/lib/processors/scheme_processors/token_container_processor.dart +++ b/lib/processors/scheme_processors/token_container_processor.dart @@ -17,9 +17,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import '../../../../../../../l10n/app_localizations.dart'; import '../../../../../../../model/exception_errors/localized_argument_error.dart'; -import '../../../../../../../utils/globals.dart'; import '../../model/processor_result.dart'; import '../../model/token_container.dart'; import '../../utils/logger.dart'; @@ -68,7 +66,7 @@ class TokenContainerProcessor extends SchemeProcessor { Logger.warning('Error while processing URI ${uri.scheme}', error: e.message); return [ ProcessorResult.failed( - e.localizedMessage(AppLocalizations.of(await globalContext)!), + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, ) ]; diff --git a/lib/processors/scheme_processors/token_import_scheme_processors/google_authenticator_qr_processor.dart b/lib/processors/scheme_processors/token_import_scheme_processors/google_authenticator_qr_processor.dart index e831b51e4..fb4fe7eb0 100644 --- a/lib/processors/scheme_processors/token_import_scheme_processors/google_authenticator_qr_processor.dart +++ b/lib/processors/scheme_processors/token_import_scheme_processors/google_authenticator_qr_processor.dart @@ -123,7 +123,7 @@ class GoogleAuthenticatorQrProcessor extends TokenImportSchemeProcessor { error: e, stackTrace: StackTrace.current, ); - results.add(ProcessorResultFailed(e.toString(), resultHandlerType: resultHandlerType)); + results.add(ProcessorResultFailed((_) => e.toString(), resultHandlerType: resultHandlerType)); continue; } } diff --git a/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart b/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart index fb8aabe2e..2f52fae13 100644 --- a/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart +++ b/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart @@ -48,7 +48,7 @@ class OtpAuthProcessor extends TokenImportSchemeProcessor { if (!supportedSchemes.contains(uri.scheme)) { return [ ProcessorResultFailed( - 'The scheme [${uri.scheme}] not supported', + (_) => 'The scheme [${uri.scheme}] not supported', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; @@ -70,7 +70,7 @@ class OtpAuthProcessor extends TokenImportSchemeProcessor { if (twoStepSecretString == null) { return [ ProcessorResultFailed( - 'The two step secret could not be generated, or was canceled.', + (_) => 'The two step secret could not be generated, or was canceled.', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; @@ -88,7 +88,7 @@ class OtpAuthProcessor extends TokenImportSchemeProcessor { } catch (e) { return [ ProcessorResultFailed( - 'The token could not be created.', + (_) => 'The token could not be created.', // TODO: Localize error: e, resultHandlerType: resultHandlerType, ) diff --git a/lib/processors/scheme_processors/token_import_scheme_processors/privacyidea_authenticator_qr_processor.dart b/lib/processors/scheme_processors/token_import_scheme_processors/privacyidea_authenticator_qr_processor.dart index b800c1582..7ffbe0569 100644 --- a/lib/processors/scheme_processors/token_import_scheme_processors/privacyidea_authenticator_qr_processor.dart +++ b/lib/processors/scheme_processors/token_import_scheme_processors/privacyidea_authenticator_qr_processor.dart @@ -50,7 +50,7 @@ class PrivacyIDEAAuthenticatorQrProcessor extends TokenImportSchemeProcessor { Logger.error('Error while processing URI ${uri.scheme}', error: e); return [ ProcessorResult.failed( - 'Invalid URI', + (_) => 'Invalid URI', // TODO: Localize resultHandlerType: resultHandlerType, ) ]; diff --git a/lib/processors/token_import_file_processor/aegis_import_file_processor.dart b/lib/processors/token_import_file_processor/aegis_import_file_processor.dart index ca7b6322d..a7b5f85f0 100644 --- a/lib/processors/token_import_file_processor/aegis_import_file_processor.dart +++ b/lib/processors/token_import_file_processor/aegis_import_file_processor.dart @@ -27,7 +27,6 @@ import 'package:encrypt/encrypt.dart'; import 'package:file_selector/file_selector.dart'; import 'package:pointycastle/export.dart'; -import '../../l10n/app_localizations.dart'; import '../../model/enums/encodings.dart'; import '../../model/enums/token_origin_source_type.dart'; import '../../model/exception_errors/localized_argument_error.dart'; @@ -39,7 +38,6 @@ import '../../model/tokens/hotp_token.dart'; import '../../model/tokens/otp_token.dart'; import '../../model/tokens/token.dart'; import '../../model/tokens/totp_token.dart'; -import '../../utils/globals.dart'; import '../../utils/logger.dart'; import '../../utils/object_validator.dart'; import '../../utils/token_import_origins.dart'; @@ -185,7 +183,6 @@ class AegisImportFileProcessor extends TokenImportFileProcessor { Future>> _processPlainV2(Map json) { final results = >[]; - final localization = globalContextSync != null ? AppLocalizations.of(globalContextSync!)! : null; for (Map entry in json[AEGIS_JSON_DB][AEGIS_DB_ENTRIES]) { try { Map info = entry[AEGIS_ENTRY_INFO]; @@ -228,13 +225,13 @@ class AegisImportFileProcessor extends TokenImportFileProcessor { )); } on LocalizedException catch (e) { results.add(ProcessorResult.failed( - localization != null ? e.localizedMessage(localization) : e.unlocalizedMessage, + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, )); } catch (e) { Logger.error('Failed to parse token.', error: e, stackTrace: StackTrace.current); results.add(ProcessorResult.failed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } @@ -244,7 +241,6 @@ class AegisImportFileProcessor extends TokenImportFileProcessor { Future>> _processPlainV3(Map json) { final results = >[]; - final localization = globalContextSync != null ? AppLocalizations.of(globalContextSync!)! : null; final entries = json[AEGIS_JSON_DB][AEGIS_DB_ENTRIES] as List; for (Map entry in entries) { try { @@ -289,13 +285,13 @@ class AegisImportFileProcessor extends TokenImportFileProcessor { )); } on LocalizedException catch (e) { results.add(ProcessorResultFailed( - localization != null ? e.localizedMessage(localization) : e.unlocalizedMessage, + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, )); } catch (e) { Logger.error('Failed to parse token.', error: e, stackTrace: StackTrace.current); results.add(ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } diff --git a/lib/processors/token_import_file_processor/authenticator_pro_import_file_processor.dart b/lib/processors/token_import_file_processor/authenticator_pro_import_file_processor.dart index 84a9a1107..1625b0c5d 100644 --- a/lib/processors/token_import_file_processor/authenticator_pro_import_file_processor.dart +++ b/lib/processors/token_import_file_processor/authenticator_pro_import_file_processor.dart @@ -24,7 +24,6 @@ import 'dart:convert'; import 'package:cryptography/cryptography.dart'; import 'package:file_selector/file_selector.dart'; -import '../../l10n/app_localizations.dart'; import '../../model/encryption/uint_8_buffer.dart'; import '../../model/enums/algorithms.dart'; import '../../model/enums/token_origin_source_type.dart'; @@ -39,7 +38,6 @@ import '../../model/tokens/totp_token.dart'; import '../../processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart'; import '../../processors/token_import_file_processor/two_fas_import_file_processor.dart'; import '../../utils/encryption/aes_encrypted.dart'; -import '../../utils/globals.dart'; import '../../utils/logger.dart'; import '../../utils/object_validator.dart'; import '../../utils/token_import_origins.dart'; @@ -253,14 +251,14 @@ class AuthenticatorProImportFileProcessor extends TokenImportFileProcessor { final newResults = await const OtpAuthProcessor().processUri(uri); results.addAll(newResults); } on LocalizedException catch (e) { - results.add(ProcessorResultFailed( - e.localizedMessage(AppLocalizations.of(await globalContext)!), + results.add(ProcessorResult.failed( + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, )); } catch (e) { Logger.error('Failed to parse token.', error: e, stackTrace: StackTrace.current); results.add(ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } @@ -295,14 +293,14 @@ class AuthenticatorProImportFileProcessor extends TokenImportFileProcessor { } } } on LocalizedException catch (e) { - results.add(ProcessorResultFailed( - e.localizedMessage(AppLocalizations.of(await globalContext)!), + results.add(ProcessorResult.failed( + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, )); } catch (e) { Logger.error('Failed to parse token.', error: e, stackTrace: StackTrace.current); results.add(ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } @@ -360,14 +358,14 @@ class AuthenticatorProImportFileProcessor extends TokenImportFileProcessor { resultHandlerType: resultHandlerType, )); } on LocalizedException catch (e) { - result.add(ProcessorResultFailed( - e.localizedMessage(AppLocalizations.of(await globalContext)!), + result.add(ProcessorResult.failed( + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, )); } catch (e) { Logger.error('Failed to parse token.', error: e, stackTrace: StackTrace.current); result.add(ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } diff --git a/lib/processors/token_import_file_processor/free_otp_plus_import_file_processor.dart b/lib/processors/token_import_file_processor/free_otp_plus_import_file_processor.dart index 161c7097f..5dd5faeda 100644 --- a/lib/processors/token_import_file_processor/free_otp_plus_import_file_processor.dart +++ b/lib/processors/token_import_file_processor/free_otp_plus_import_file_processor.dart @@ -25,7 +25,6 @@ import 'package:file_selector/file_selector.dart'; import '../../../../../../../model/enums/encodings.dart'; import '../../../../../../../model/extensions/enums/encodings_extension.dart'; -import '../../l10n/app_localizations.dart'; import '../../model/enums/token_origin_source_type.dart'; import '../../model/exception_errors/localized_exception.dart'; import '../../model/extensions/enums/token_origin_source_type.dart'; @@ -34,7 +33,6 @@ import '../../model/tokens/hotp_token.dart'; import '../../model/tokens/otp_token.dart'; import '../../model/tokens/token.dart'; import '../../model/tokens/totp_token.dart'; -import '../../utils/globals.dart'; import '../../utils/logger.dart'; import '../../utils/object_validator.dart'; import '../../utils/token_import_origins.dart'; @@ -94,7 +92,7 @@ class FreeOtpPlusImportFileProcessor extends TokenImportFileProcessor { } catch (e) { Logger.error('Failed to process line: $line', error: e, stackTrace: StackTrace.current); results.add(ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } @@ -141,14 +139,14 @@ class FreeOtpPlusImportFileProcessor extends TokenImportFileProcessor { resultHandlerType: resultHandlerType, ); } on LocalizedException catch (e) { - return ProcessorResultFailed( - e.localizedMessage(AppLocalizations.of(await globalContext)!), + return ProcessorResult.failed( + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, ); } catch (e, s) { Logger.warning('Failed to parse token.', error: e, stackTrace: s); return ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, ); } diff --git a/lib/processors/token_import_file_processor/privacyidea_authenticator_import_file_processor.dart b/lib/processors/token_import_file_processor/privacyidea_authenticator_import_file_processor.dart index f32f6ebff..8cba305b2 100644 --- a/lib/processors/token_import_file_processor/privacyidea_authenticator_import_file_processor.dart +++ b/lib/processors/token_import_file_processor/privacyidea_authenticator_import_file_processor.dart @@ -72,7 +72,7 @@ class PrivacyIDEAAuthenticatorImportFileProcessor extends TokenImportFileProcess Logger.error('Failed to process file', error: e, stackTrace: StackTrace.current); return [ ProcessorResult.failed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, ) ]; diff --git a/lib/processors/token_import_file_processor/two_fas_import_file_processor.dart b/lib/processors/token_import_file_processor/two_fas_import_file_processor.dart index 4a8867a3c..2c5a36101 100644 --- a/lib/processors/token_import_file_processor/two_fas_import_file_processor.dart +++ b/lib/processors/token_import_file_processor/two_fas_import_file_processor.dart @@ -23,7 +23,6 @@ import 'dart:convert'; import 'package:cryptography/cryptography.dart'; import 'package:file_selector/file_selector.dart'; -import '../../l10n/app_localizations.dart'; import '../../model/enums/token_origin_source_type.dart'; import '../../model/exception_errors/localized_exception.dart'; import '../../model/extensions/enums/token_origin_source_type.dart'; @@ -33,7 +32,6 @@ import '../../model/tokens/otp_token.dart'; import '../../model/tokens/token.dart'; import '../../model/tokens/totp_token.dart'; import '../../utils/encryption/aes_encrypted.dart'; -import '../../utils/globals.dart'; import '../../utils/logger.dart'; import '../../utils/object_validator.dart'; import '../../utils/token_import_origins.dart'; @@ -156,14 +154,14 @@ class TwoFasAuthenticatorImportFileProcessor extends TokenImportFileProcessor { resultHandlerType: resultHandlerType, )); } on LocalizedException catch (e) { - results.add(ProcessorResultFailed( - e.localizedMessage(AppLocalizations.of(await globalContext)!), + results.add(ProcessorResult.failed( + (localization) => e.localizedMessage(localization), resultHandlerType: resultHandlerType, )); } catch (e) { Logger.error('Failed to parse token.', error: e, stackTrace: StackTrace.current); results.add(ProcessorResultFailed( - e.toString(), + (_) => e.toString(), resultHandlerType: resultHandlerType, )); } diff --git a/lib/utils/ecc_utils.dart b/lib/utils/ecc_utils.dart index 62f8a8b54..c2183534d 100644 --- a/lib/utils/ecc_utils.dart +++ b/lib/utils/ecc_utils.dart @@ -44,4 +44,9 @@ class EccUtils { final private = keyPair.privateKey; return AsymmetricKeyPair(public as ECPublicKey, private as ECPrivateKey); } + + bool validateSignature(ECPublicKey publicKey, String signature, String message) { + final ecSignature = CryptoUtils.ecSignatureFromBase64(signature); + return CryptoUtils.ecVerify(publicKey, Uint8List.fromList(message.codeUnits), ecSignature, algorithm: 'SHA-256/ECDSA'); + } } diff --git a/lib/utils/firebase_utils.dart b/lib/utils/firebase_utils.dart index b9883c326..cf3615742 100644 --- a/lib/utils/firebase_utils.dart +++ b/lib/utils/firebase_utils.dart @@ -64,22 +64,21 @@ class FirebaseUtils { updateFirebaseToken(firebaseToken); } } catch (error, stackTrace) { - String? errorMessage; - String? subMessage; if (error is PlatformException) { if (error.code == FIREBASE_TOKEN_ERROR_CODE) return; // ignore - errorMessage = 'Push cant be initialized, restart the app and try again.'; - subMessage = '${error.code}: ${error.message ?? 'no error message'}'; + showStatusMessage( + message: (_) => 'Push cant be initialized, restart the app and try again.', // TODO: localize + details: (_) => '${error.code}: ${error.message ?? 'no error message'}', + ); } if (error is FirebaseException) { if (error.code == FIREBASE_TOKEN_ERROR_CODE) return; // ignore - errorMessage = 'Push cant be initialized, restart the app and try again.'; - subMessage = '${error.code}: ${error.message ?? 'no error message'}'; - } - if (errorMessage != null) { - showStatusMessage(message: errorMessage, subMessage: subMessage); - return; + showStatusMessage( + message: (_) => 'Push cant be initialized, restart the app and try again.', // TODO: localize + details: (_) => '${error.code}: ${error.message ?? 'no error message'}', + ); } + Logger.error('Unknown Firebase error', error: error, stackTrace: stackTrace); } diff --git a/lib/utils/lock_auth.dart b/lib/utils/lock_auth.dart index c1c85dce6..90e87383e 100644 --- a/lib/utils/lock_auth.dart +++ b/lib/utils/lock_auth.dart @@ -28,7 +28,6 @@ import 'package:local_auth_darwin/local_auth_darwin.dart'; import '../l10n/app_localizations.dart'; import '../widgets/dialog_widgets/default_dialog.dart'; -import 'globals.dart'; import 'logger.dart'; import 'view_utils.dart'; @@ -37,7 +36,7 @@ bool _authenticationInProgress = false; /// Sends a request to the OS to authenticate the user. Returns true if the user was authenticated, false otherwise. /// If the device does not support authentication or authentication is not set up, a dialog is shown to the user. /// If [autoAuthIfUnsupported] is set to true and the device does not support authentication, the function will return true. -Future lockAuth({required String localizedReason, bool autoAuthIfUnsupported = false}) async { +Future lockAuth({required String Function(AppLocalizations) reason, required AppLocalizations localization, bool autoAuthIfUnsupported = false}) async { bool didAuthenticate = false; LocalAuthentication localAuth = LocalAuthentication(); final isDeviceSupported = await localAuth.isDeviceSupported(); @@ -67,29 +66,29 @@ Future lockAuth({required String localizedReason, bool autoAuthIfUnsupport } AndroidAuthMessages androidAuthStrings = AndroidAuthMessages( - biometricRequiredTitle: AppLocalizations.of(globalNavigatorKey.currentContext!)!.biometricRequiredTitle, - biometricHint: AppLocalizations.of(globalNavigatorKey.currentContext!)!.biometricHint, - biometricNotRecognized: AppLocalizations.of(globalNavigatorKey.currentContext!)!.biometricNotRecognized, - biometricSuccess: AppLocalizations.of(globalNavigatorKey.currentContext!)!.biometricSuccess, - deviceCredentialsRequiredTitle: AppLocalizations.of(globalNavigatorKey.currentContext!)!.deviceCredentialsRequiredTitle, - deviceCredentialsSetupDescription: AppLocalizations.of(globalNavigatorKey.currentContext!)!.deviceCredentialsSetupDescription, - signInTitle: AppLocalizations.of(globalNavigatorKey.currentContext!)!.signInTitle, - goToSettingsButton: AppLocalizations.of(globalNavigatorKey.currentContext!)!.goToSettingsButton, - goToSettingsDescription: AppLocalizations.of(globalNavigatorKey.currentContext!)!.goToSettingsDescription, - cancelButton: AppLocalizations.of(globalNavigatorKey.currentContext!)!.cancel, + biometricRequiredTitle: localization.biometricRequiredTitle, + biometricHint: localization.biometricHint, + biometricNotRecognized: localization.biometricNotRecognized, + biometricSuccess: localization.biometricSuccess, + deviceCredentialsRequiredTitle: localization.deviceCredentialsRequiredTitle, + deviceCredentialsSetupDescription: localization.deviceCredentialsSetupDescription, + signInTitle: localization.signInTitle, + goToSettingsButton: localization.goToSettingsButton, + goToSettingsDescription: localization.goToSettingsDescription, + cancelButton: localization.cancel, ); IOSAuthMessages iOSAuthStrings = IOSAuthMessages( - lockOut: AppLocalizations.of(globalNavigatorKey.currentContext!)!.lockOut, - goToSettingsButton: AppLocalizations.of(globalNavigatorKey.currentContext!)!.goToSettingsButton, - goToSettingsDescription: AppLocalizations.of(globalNavigatorKey.currentContext!)!.goToSettingsDescription, - cancelButton: AppLocalizations.of(globalNavigatorKey.currentContext!)!.cancel, + lockOut: localization.lockOut, + goToSettingsButton: localization.goToSettingsButton, + goToSettingsDescription: localization.goToSettingsDescription, + cancelButton: localization.cancel, ); try { if (!_authenticationInProgress) { _authenticationInProgress = true; - didAuthenticate = await localAuth.authenticate(localizedReason: localizedReason, authMessages: [ + didAuthenticate = await localAuth.authenticate(localizedReason: reason(localization), authMessages: [ androidAuthStrings, iOSAuthStrings, ]); diff --git a/lib/utils/patch_notes_utils.dart b/lib/utils/patch_notes_utils.dart index 39cc6cbe4..02d88ade3 100644 --- a/lib/utils/patch_notes_utils.dart +++ b/lib/utils/patch_notes_utils.dart @@ -29,8 +29,6 @@ import 'logger.dart'; class PatchNotesUtils { static Map>> _getNewPatchNotes({required BuildContext context, required Version latestStartedVersion}) { - final context = globalNavigatorKey.currentContext; - if (context == null) return {}; final Map>> newNotes = {}; final allNotes = getLocalizedPatchNotes(AppLocalizations.of(context)!); for (Version noteVersion in allNotes.keys) { diff --git a/lib/utils/privacyidea_io_client.dart b/lib/utils/privacyidea_io_client.dart index 2e2fe1ea0..60d7e53d0 100644 --- a/lib/utils/privacyidea_io_client.dart +++ b/lib/utils/privacyidea_io_client.dart @@ -32,7 +32,6 @@ import '../model/api_results/pi_server_results/pi_server_result_value.dart'; import '../utils/globals.dart'; import '../utils/logger.dart'; import '../utils/view_utils.dart'; -import 'riverpod/riverpod_providers/state_providers/status_message_provider.dart'; class PrivacyideaIOClient { const PrivacyideaIOClient(); @@ -56,10 +55,9 @@ class PrivacyideaIOClient { } on ClientException { Logger.warning('ClientException'); ioClient.close(); - if (globalNavigatorKey.currentState?.context == null) return false; - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(await globalContext)!.connectionFailed, - AppLocalizations.of(await globalContext)!.checkYourNetwork, + showStatusMessage( + message: (localization) => localization.connectionFailed, + details: (localization) => localization.checkYourNetwork, ); return false; } catch (e, _) { @@ -68,12 +66,11 @@ class PrivacyideaIOClient { } if (isRetry) { Logger.warning('SocketException while retrying'); - if (globalNavigatorKey.currentState?.context != null) { - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(await globalContext)!.connectionFailed, - AppLocalizations.of(await globalContext)!.checkYourNetwork, - ); - } + showStatusMessage( + message: (localization) => localization.connectionFailed, + details: (localization) => localization.checkYourNetwork, + ); + ioClient.close(); return false; } @@ -117,7 +114,7 @@ class PrivacyideaIOClient { response = await ioClient.post(url, body: body).timeout(const Duration(seconds: 15)); } on HandshakeException catch (e, _) { Logger.info('Handshake failed. sslVerify: $sslVerify'); - showStatusMessage(message: 'Handshake failed, please check the server certificate and try again.'); + showStatusMessage(message: (_) => 'Handshake failed, please check the server certificate and try again.'); // TODO: Localize ioClient.close(); return ResponseBuilder.fromStatusCode(525); } on TimeoutException catch (e, _) { @@ -185,8 +182,8 @@ class PrivacyideaIOClient { } on HandshakeException catch (e, _) { Logger.warning('Handshake failed. sslVerify: $sslVerify'); showStatusMessage( - message: AppLocalizations.of(await globalContext)!.handshakeFailed, - subMessage: AppLocalizations.of(await globalContext)!.checkServerCertificate, + message: (localization) => localization.handshakeFailed, + details: (localization) => localization.checkServerCertificate, ); response = Response('${e.runtimeType}', 525); } on TimeoutException catch (e, _) { diff --git a/lib/utils/push_provider.dart b/lib/utils/push_provider.dart index d582690f5..830c97bda 100644 --- a/lib/utils/push_provider.dart +++ b/lib/utils/push_provider.dart @@ -26,10 +26,10 @@ import 'package:collection/collection.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:http/http.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import '../../../../../../../repo/secure_push_request_repository.dart'; import '../../../../../../../utils/pi_notifications.dart'; -import '../l10n/app_localizations.dart'; import '../model/push_request.dart'; import '../model/tokens/push_token.dart'; import '../repo/secure_token_repository.dart'; @@ -176,7 +176,7 @@ class PushProvider { return _handleIncomingRequestForeground(data); } catch (e, s) { Logger.error( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.unexpectedError, + AppLocalizationsEn().unexpectedError, error: e, stackTrace: s, ); @@ -213,9 +213,9 @@ class PushProvider { // PiNotifications.show('Push request', 'A new push request has been received.'); if (remoteMessage.notification == null) { PiNotifications.show( - // AppLocalizations.of(globalNavigatorKey.currentContext!)!.notificationTitle, + // message: (localization) => localization.notificationTitle, 'Notification Title', - // AppLocalizations.of(globalNavigatorKey.currentContext!)!.notificationBody, + // message: (localization) => localization.notificationBody, 'Notification Body', ); } @@ -305,9 +305,9 @@ class PushProvider { if (connectivityResult.contains(ConnectivityResult.none)) { if (isManually) { Logger.info('Tried to poll without any internet connection available.'); - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailed, - AppLocalizations.of(globalNavigatorKey.currentContext!)!.noNetworkConnection, + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.pollingFailed, + details: (localization) => localization.noNetworkConnection, ); } return; @@ -336,9 +336,9 @@ class PushProvider { Logger.info(rsaUtils.runtimeType.toString()); String? signature = await rsaUtils.trySignWithToken(token, message); if (signature == null) { - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailedFor(token.serial), - AppLocalizations.of(globalNavigatorKey.currentContext!)!.couldNotSignMessage, + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.pollingFailedFor(token.serial), + details: (localization) => localization.couldNotSignMessage, ); Logger.warning('Polling push tokens failed because signing the message failed.'); return; @@ -357,9 +357,9 @@ class PushProvider { : await const PrivacyideaIOClient().doGet(url: token.url!, parameters: parameters, sslVerify: token.sslVerify); } catch (_) { if (isManually) { - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial), - AppLocalizations.of(globalNavigatorKey.currentContext!)!.couldNotConnectToServer, + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorWhenPullingChallenges(token.serial), + details: (localization) => localization.couldNotConnectToServer, ); } return; @@ -372,9 +372,9 @@ class PushProvider { challengeList = _getAndValidateDataFromResponse(response); } catch (_) { if (isManually) { - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial), - AppLocalizations.of(globalNavigatorKey.currentContext!)!.pushRequestParseError, + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorWhenPullingChallenges(token.serial), + details: (localization) => localization.pushRequestParseError, ); } return; @@ -385,9 +385,9 @@ class PushProvider { case 403: final error = getErrorMessageFromResponse(response); if (isManually) { - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailedFor(token.serial), - error ?? AppLocalizations.of(globalNavigatorKey.currentContext!)!.statusCode(response.statusCode), + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.pollingFailedFor(token.serial), + details: error != null ? (_) => error : (localization) => localization.statusCode(response.statusCode), ); } Logger.warning('Polling push token failed with status code ${response.statusCode}', error: getErrorMessageFromResponse(response)); @@ -396,9 +396,9 @@ class PushProvider { default: final error = getErrorMessageFromResponse(response); if (isManually) { - globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.pollingFailedFor(token.serial), - error ?? AppLocalizations.of(globalNavigatorKey.currentContext!)!.statusCode(response.statusCode), + globalRef?.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.pollingFailedFor(token.serial), + details: error != null ? (_) => error : (localization) => localization.statusCode(response.statusCode), ); } return; diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.dart new file mode 100644 index 000000000..d789a50ae --- /dev/null +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.dart @@ -0,0 +1,42 @@ +/* + * privacyIDEA Authenticator + * + * Author: Frank Merkel + * + * Copyright (c) 2024 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 'package:privacyidea_authenticator/l10n/app_localizations.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../../../l10n/app_localizations_en.dart'; +import '../../../logger.dart'; + +part 'localization_notifier.g.dart'; + +@Riverpod(keepAlive: true) +class LocalizationNotifier extends _$LocalizationNotifier { + @override + AppLocalizations build() { + Logger.info("New AppConstraints created"); + return AppLocalizationsEn(); + } + + void update(AppLocalizations localizations) { + if (state == localizations) return; + Logger.debug("AppLocalizations updated"); + state = localizations; + } +} diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.g.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.g.dart new file mode 100644 index 000000000..39cc4bdec --- /dev/null +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'localization_notifier.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$localizationNotifierHash() => + r'2e87d7cfb7470b202d97fa2bf766a72f2a4debe3'; + +/// See also [LocalizationNotifier]. +@ProviderFor(LocalizationNotifier) +final localizationNotifierProvider = + NotifierProvider.internal( + LocalizationNotifier.new, + name: r'localizationNotifierProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$localizationNotifierHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$LocalizationNotifier = Notifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.dart index a59ba2aa3..e858864a2 100644 --- a/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.dart +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.dart @@ -25,12 +25,10 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../../../../interfaces/repo/push_request_repository.dart'; import '../../../../../../../utils/rsa_utils.dart'; -import '../../../../l10n/app_localizations.dart'; import '../../../../model/push_request.dart'; import '../../../../model/riverpod_states/push_request_state.dart'; import '../../../../model/tokens/push_token.dart'; import '../../../../repo/secure_push_request_repository.dart'; -import '../../../globals.dart'; import '../../../logger.dart'; import '../../../privacyidea_io_client.dart'; import '../../../push_provider.dart'; @@ -390,15 +388,14 @@ class PushRequestNotifier extends _$PushRequestNotifier { response = await _ioClient.doPost(sslVerify: pushRequest.sslVerify, url: pushRequest.uri, body: body); } catch (e) { Logger.warning('Sending push request response failed consistently.', error: e); - ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(await globalContext)!.connectionFailed, null); + ref.read(statusMessageProvider.notifier).state = StatusMessage(message: (l) => l.connectionFailed, details: null); return false; } } if (response.statusCode != 200) { - final appLocalizations = AppLocalizations.of(await globalContext)!; - ref.read(statusMessageProvider.notifier).state = ( - '${appLocalizations.sendPushRequestResponseFailed}\n${appLocalizations.statusCode(response.statusCode)}', - tryJsonDecode(response.body)?["result"]?["error"]?["message"], + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (l) => '${l.sendPushRequestResponseFailed}\n${l.statusCode(response.statusCode)}', + details: tryJsonDecode(response.body)?['result']?['error']?['message'], ); Logger.warning('Sending push request response failed.'); return false; diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.g.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.g.dart index 9ddf61493..92e8b5dbc 100644 --- a/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.g.dart +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/push_request_provider.g.dart @@ -7,7 +7,7 @@ part of 'push_request_provider.dart'; // ************************************************************************** String _$pushRequestNotifierHash() => - r'1a2cb2d1f2e197a93cc7a836346c1a2ff749c676'; + r'11d2a0b2d28cb73adc3d3b801aff5bcd501aa280'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart index 3196dde21..9fbd011a1 100644 --- a/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart @@ -20,7 +20,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; -import 'package:http/http.dart'; import 'package:mutex/mutex.dart'; import 'package:privacyidea_authenticator/model/container_policies.dart'; import 'package:privacyidea_authenticator/model/extensions/enums/rollout_state_extension.dart'; @@ -30,7 +29,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../../../../model/exception_errors/pi_server_result_error.dart'; import '../../../../../../../model/processor_result.dart'; import '../../../../../../../model/tokens/token.dart'; -import '../../../../../../../utils/globals.dart'; import '../../../../../../../utils/privacyidea_io_client.dart'; import '../../../../../../../utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart'; import '../../../../../../../utils/riverpod/riverpod_providers/state_providers/status_message_provider.dart'; @@ -38,7 +36,6 @@ import '../../../../../../../utils/view_utils.dart'; import '../../../../api/impl/privacy_idea_container_api.dart'; import '../../../../api/interfaces/container_api.dart'; import '../../../../interfaces/repo/token_container_repository.dart'; -import '../../../../l10n/app_localizations.dart'; import '../../../../model/api_results/pi_server_results/pi_server_result_value.dart'; import '../../../../model/enums/rollout_state.dart'; import '../../../../model/enums/sync_state.dart'; @@ -192,8 +189,8 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler if (!isManually) return null; Logger.debug('Failed to sync container ${error.runtimeType}', error: error, stackTrace: stackTrace); showStatusMessage( - message: AppLocalizations.of(await globalContext)!.failedToSyncContainer(finalizedContainer.serial), - subMessage: error is PiServerResultError ? error.message : error.toString(), + message: (localization) => localization.failedToSyncContainer(finalizedContainer.serial), + details: error is PiServerResultError ? (_) => error.message : (_) => error.toString(), ); return null; }), @@ -226,7 +223,7 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler required TokenState tokenState, required TokenContainerFinalized container, }) async { - final rollover = await getTransferQrData(container); + final rollover = await getRolloverQrData(container); final uri = Uri.tryParse(rollover); if (uri == null) throw ArgumentError('Invalid rollover uri'); final result = (await TokenContainerProcessor().processUri(uri, fromInit: false))?.firstOrNull; @@ -235,11 +232,11 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler return success; } - Future getTransferQrData(TokenContainerFinalized container) async { + Future getRolloverQrData(TokenContainerFinalized container) async { final currentContainer = (await future).currentOf(container); if (currentContainer == null) throw StateError('Container was removed'); - final qrCode = await _containerApi.getTransferQrData(currentContainer); - return qrCode; + final qrCodeData = await _containerApi.getRolloverQrData(currentContainer); + return qrCodeData.value; } // ADD CONTAINER @@ -338,7 +335,7 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler // DELETE CONTAINER Future unregisterDelete(TokenContainerFinalized container) async { - if (!await _containerApi.unregister(container)) return await future; + if (!(await _containerApi.unregister(container)).success) return await future; await _stateMutex.acquire(); final newState = await _deleteContainerFromRepo(container); @@ -447,7 +444,7 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler throw ArgumentError('Container must not be finalized'); } if (container.expirationDate != null && container.expirationDate!.isBefore(DateTime.now())) { - showStatusMessage(message: 'Container ${container.serial} has expired and can not be rolled out anymore'); + showStatusMessage(message: (_) => 'Container ${container.serial} has expired and can not be rolled out anymore'); // TODO: Localize await deleteContainer(container); _finalizationMutex.release(); return; @@ -456,41 +453,37 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler try { container = await _generateKeyPair(container); container = await _curentOf(container); - final Response response = await _sendPublicKey(container); + final ContainerFinalizationResponse response = await _sendPublicKey(container); container = await _curentOf(container); container = await _applyFinalizationResponse(await _curentOf(container), response); } on StateError catch (e) { - final applocalizations = AppLocalizations.of(await globalContext)!; if (isManually) { - ref.read(statusMessageProvider.notifier).state = ( - container.finalizationState.asFailed.rolloutMsgLocalized(applocalizations), - e.toString(), + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => container.finalizationState.asFailed.rolloutMsgLocalized(localization), + details: (localization) => e.toString(), ); } } on LocalizedArgumentError catch (e) { if (isManually) { - final applocalizations = AppLocalizations.of(await globalContext)!; - ref.read(statusMessageProvider.notifier).state = ( - container.finalizationState.asFailed.rolloutMsgLocalized(applocalizations), - e.localizedMessage(applocalizations), + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => container.finalizationState.asFailed.rolloutMsgLocalized(localization), + details: (localization) => e.localizedMessage(localization), ); } await updateContainer(container, (TokenContainerFinalized c) => c.copyWith(finalizationState: c.finalizationState.asFailed)); } on PiErrorResponse catch (e) { if (isManually) { - final applocalizations = AppLocalizations.of(await globalContext)!; - ref.read(statusMessageProvider.notifier).state = ( - container.finalizationState.asFailed.rolloutMsgLocalized(applocalizations), - e.piServerResultError.message, + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => container.finalizationState.asFailed.rolloutMsgLocalized(localization), + details: (localization) => e.piServerResultError.message, ); } await updateContainer(container, (TokenContainerFinalized c) => c.copyWith(finalizationState: c.finalizationState.asFailed)); } on ResponseError catch (e) { if (isManually) { - final applocalizations = AppLocalizations.of(await globalContext)!; - ref.read(statusMessageProvider.notifier).state = ( - container.finalizationState.asFailed.rolloutMsgLocalized(applocalizations), - e.toString(), + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => container.finalizationState.asFailed.rolloutMsgLocalized(localization), + details: (localization) => e.toString(), ); } } catch (e) { @@ -524,7 +517,7 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler // generatingKeyPairCompleted, TokenContainerUnfinalized? container = tokenContainer; container = await updateContainer( - container, (c) => c.copyWith(finalizationState: RolloutState.generatingKeyPair)); + container, (c) => c.copyWith(finalizationState: FinalizationState.generatingKeyPair)); if (container == null) throw StateError('Container was removed'); final keyPair = eccUtils.generateKeyPair(container.ecKeyAlgorithm); container = await updateContainer( @@ -534,7 +527,7 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler } /// Finalization substep 2: Send public key - Future _sendPublicKey(TokenContainerUnfinalized tokenContainer) async { + Future _sendPublicKey(TokenContainerUnfinalized tokenContainer) async { // sendingPublicKey, // sendingPublicKeyFailed, // sendingPublicKeyCompleted, @@ -548,62 +541,37 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler TokenContainerUnfinalized? container = tokenContainer; - final Response response; - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.sendingPublicKey)); + final ContainerFinalizationResponse response; + container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: FinalizationState.sendingPublicKey)); if (container == null) throw StateError('Container was removed'); - - response = (await _containerApi.finalizeContainer(container, eccUtils)); - if (response.statusCode != 200) { - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.sendingPublicKeyFailed)); - throw ResponseError(response); + try { + response = (await _containerApi.finalizeContainer(container, eccUtils)); + } catch (_) { + container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: FinalizationState.sendingPublicKeyFailed)); + rethrow; } - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.sendingPublicKeyCompleted)); + container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: FinalizationState.sendingPublicKeyCompleted)); return response; } /// Finalization substep 3: Apply finalization response to container - Future _applyFinalizationResponse(TokenContainer tokenContainer, Response response) async { + Future _applyFinalizationResponse(TokenContainer tokenContainer, ContainerFinalizationResponse response) async { // parsingResponse, // parsingResponseFailed, // parsingResponseCompleted, TokenContainer? container = tokenContainer; - PiServerResponse? piResponse; - try { - piResponse = response.asPiServerResponse(); - } catch (e) { - Logger.error('Failed to parse response', error: e); - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.parsingResponseFailed)); - rethrow; - } - if (piResponse == null || piResponse.isError) { - Logger.debug('Status code: ${response.statusCode}'); - Logger.debug('Response body: ${response.body}'); - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.sendingPublicKeyFailed)); - final error = piResponse?.asError; - if (error != null) throw error; - throw ResponseError(response); - } - - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.sendingPublicKeyCompleted)); + container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: FinalizationState.sendingPublicKeyCompleted)); if (container == null) throw StateError('Container was removed'); - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.parsingResponse)); + container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: FinalizationState.parsingResponse)); if (container == null) throw StateError('Container was removed'); - ContainerFinalizationResponse finalizationResponse = piResponse.asSuccess!.resultValue; - try { - finalizationResponse = piResponse.asSuccess!.resultValue; - } catch (e) { - Logger.error('Failed to parse response', error: e); - container = await updateContainer(container, (TokenContainerUnfinalized c) => c.copyWith(finalizationState: RolloutState.parsingResponseFailed)); - rethrow; - } // final signature = finalizationResponse.signature; final finalizedContainer = await updateContainer( container, - (TokenContainerUnfinalized c) => c.copyWith(policies: finalizationResponse.policies).finalize(publicServerKey: finalizationResponse.publicServerKey)!, + (TokenContainerUnfinalized c) => c.copyWith(policies: response.policies).finalize(publicServerKey: response.publicServerKey)!, ); if (finalizedContainer == null) throw StateError('Container was removed'); return finalizedContainer; diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.g.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.g.dart index 28325a405..885fddfe5 100644 --- a/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.g.dart +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.g.dart @@ -7,7 +7,7 @@ part of 'token_container_notifier.dart'; // ************************************************************************** String _$tokenContainerNotifierHash() => - r'a54590476e980e31528f4b833b50bb5786168964'; + r'c6c5bf723ca21b6dfdad59a42ef3678b4f960996'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart index a239d97f1..0300b34c4 100644 --- a/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart @@ -29,6 +29,7 @@ import 'package:flutter/services.dart'; import 'package:http/http.dart'; import 'package:mutex/mutex.dart'; import 'package:pointycastle/asymmetric/api.dart'; +import 'package:privacyidea_authenticator/utils/riverpod/riverpod_providers/generated_providers/localization_notifier.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../../../model/extensions/enums/push_token_rollout_state_extension.dart'; @@ -55,7 +56,6 @@ import '../../../logger.dart'; import '../../../privacyidea_io_client.dart'; import '../../../rsa_utils.dart'; import '../../../utils.dart'; -import '../../../view_utils.dart'; import '../state_providers/status_message_provider.dart'; import 'settings_notifier.dart'; @@ -391,7 +391,10 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { /// Shows a token and returns the updated token if successful, the old token if not and null if the token does not exist or the user is not authenticated. Future showToken(T token) async { - final authenticated = await lockAuth(localizedReason: AppLocalizations.of(globalNavigatorKey.currentContext!)!.authenticateToShowOtp); + final authenticated = await lockAuth( + localization: ref.read(localizationNotifierProvider), + reason: (localization) => localization.authenticateToShowOtp, + ); if (!authenticated) return null; final updated = await _updateToken(token, (p0) => p0.copyWith(isHidden: false) as T); if (updated?.isHidden == false) { @@ -487,9 +490,9 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { await _firebaseUtils.deleteFirebaseToken(); } on SocketException { Logger.warning('Could not delete firebase token.'); - ref.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorUnlinkingPushToken(token.label), - AppLocalizations.of(globalNavigatorKey.currentContext!)!.checkYourNetwork, + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorUnlinkingPushToken(token.label), + details: (localization) => localization.checkYourNetwork, ); return false; } @@ -504,9 +507,9 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { if (fbToken == null) { await _updateTokens(state.pushTokens, (p0) => p0.copyWith(fbToken: null)); Logger.warning('Could not update firebase token because no firebase token is available.'); - ref.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorSynchronizationNoNetworkConnection, - AppLocalizations.of(globalNavigatorKey.currentContext!)!.syncFbTokenManuallyWhenNetworkIsAvailable, + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorSynchronizationNoNetworkConnection, + details: (localization) => localization.syncFbTokenManuallyWhenNetworkIsAvailable, ); return deleted; } @@ -515,9 +518,9 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { await _updateTokens(notUpdated, (p0) => p0.copyWith(fbToken: null)); if (notUpdated.isNotEmpty) { Logger.warning('Could not update firebase token for ${notUpdated.length} tokens.'); - ref.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorSynchronizationNoNetworkConnection, - AppLocalizations.of(globalNavigatorKey.currentContext!)!.syncFbTokenManuallyWhenNetworkIsAvailable, + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorSynchronizationNoNetworkConnection, + details: (localization) => localization.syncFbTokenManuallyWhenNetworkIsAvailable, ); } return deleted; @@ -544,12 +547,11 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { if (pushToken.expirationDate?.isBefore(DateTime.now()) == true) { Logger.info('Ignoring rollout request: Token "${pushToken.id}" is expired. '); - if (globalNavigatorKey.currentContext != null) { - ref.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutNotPossibleAnymore, - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorTokenExpired(pushToken.label), - ); - } + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorRollOutNotPossibleAnymore, + details: (localization) => localization.errorTokenExpired(pushToken!.label), + ); + await _removeToken(pushToken); return false; } @@ -594,7 +596,7 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { } if (!kIsWeb && Platform.isIOS) { Logger.warning('Triggering network access permission for token "${pushToken.id}"'); - if (await _ioClient.triggerNetworkAccessPermission(url: pushToken.url!, sslVerify: pushToken.sslVerify) == false) { + if (!await _ioClient.triggerNetworkAccessPermission(url: pushToken.url!, sslVerify: pushToken.sslVerify)) { Logger.warning('Network access permission for token "${pushToken.id}" failed.'); _updateToken(pushToken, (p0) => p0.copyWith(rolloutState: PushTokenRollOutState.sendRSAPublicKeyFailed)); return false; @@ -652,16 +654,16 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { try { final message = response.body.isNotEmpty ? (json.decode(response.body)['result']?['error']?['message']) : ''; - ref.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(pushToken.label), - message, + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorRollOutFailed(pushToken!.label), + details: message, ); } on FormatException { // Format Exception is thrown if the response body is not a valid json. This happens if the server is not reachable. - ref.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutFailed(pushToken.label), - AppLocalizations.of(globalNavigatorKey.currentContext!)!.statusCode(response.statusCode) + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorRollOutFailed(pushToken!.label), + details: (localization) => localization.statusCode(response.statusCode), ); } @@ -680,14 +682,18 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler { } if (e is PlatformException && e.code == FIREBASE_TOKEN_ERROR_CODE || e is SocketException || e is TimeoutException || e is FirebaseException) { Logger.warning('Connection error: Roll out push token failed.', error: e, stackTrace: s); - showSnackBar(AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutNoConnectionToServer(pushToken.label)); + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorRollOutNoConnectionToServer(pushToken!.label), + ); } else if (e is HandshakeException) { Logger.warning('SSL error: Roll out push token failed.', error: e, stackTrace: s); - showSnackBar(AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutSSLHandshakeFailed); + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorRollOutSSLHandshakeFailed, + ); } else { - if (globalNavigatorKey.currentContext != null) { - showSnackBar(AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorRollOutUnknownError('$e')); - } + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localization) => localization.errorRollOutUnknownError(pushToken!.label), + ); Logger.error('Roll out push token failed.', error: e, stackTrace: s); } return false; diff --git a/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.g.dart b/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.g.dart index afaabfb47..c29a09a16 100644 --- a/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.g.dart +++ b/lib/utils/riverpod/riverpod_providers/generated_providers/token_notifier.g.dart @@ -6,7 +6,7 @@ part of 'token_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$tokenNotifierHash() => r'e1caf9d769fe53c8ecdd382d9c414a3252d0ec49'; +String _$tokenNotifierHash() => r'd5ee1c8cb58e467fe230dd97341996d08e2b9dba'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/utils/riverpod/riverpod_providers/state_providers/status_message_provider.dart b/lib/utils/riverpod/riverpod_providers/state_providers/status_message_provider.dart index 6a9587bdb..58ef3639f 100644 --- a/lib/utils/riverpod/riverpod_providers/state_providers/status_message_provider.dart +++ b/lib/utils/riverpod/riverpod_providers/state_providers/status_message_provider.dart @@ -18,12 +18,29 @@ * limitations under the License. */ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import '../../../logger.dart'; -final statusMessageProvider = StateProvider<(String, String?)?>( +final statusMessageProvider = StateProvider( (ref) { Logger.info("New statusMessageProvider created"); return null; }, ); + +class StatusMessage { + String Function(AppLocalizations localization) message; + String Function(AppLocalizations localization)? details; + + StatusMessage({ + required this.message, + this.details, + }); + + @override + String toString() { + return 'StatusMessage{message: ${message(AppLocalizationsEn())}, details: ${details?.call(AppLocalizationsEn())}}'; + } +} diff --git a/lib/utils/riverpod/riverpod_providers/stream_providers/connectivity_provider.dart b/lib/utils/riverpod/riverpod_providers/stream_providers/connectivity_provider.dart index 4a1f975c6..7afd1ddfe 100644 --- a/lib/utils/riverpod/riverpod_providers/stream_providers/connectivity_provider.dart +++ b/lib/utils/riverpod/riverpod_providers/stream_providers/connectivity_provider.dart @@ -20,8 +20,6 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../../../l10n/app_localizations.dart'; -import '../../../globals.dart'; import '../../../logger.dart'; import '../generated_providers/token_notifier.dart'; import '../state_providers/status_message_provider.dart'; @@ -34,8 +32,8 @@ final connectivityProvider = StreamProvider>( Connectivity().checkConnectivity().then((connectivity) { Logger.info("First connectivity check: $connectivity"); final hasNoConnection = connectivity.contains(ConnectivityResult.none); - if (hasNoConnection && newState.hasPushTokens && globalNavigatorKey.currentContext != null) { - ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(globalNavigatorKey.currentContext!)!.noNetworkConnection, null); + if (hasNoConnection && newState.hasPushTokens) { + ref.read(statusMessageProvider.notifier).state = StatusMessage(message: (localization) => localization.noNetworkConnection); } }); }, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index ea4619670..6083a5efa 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -219,7 +219,7 @@ Future scanQrCode({BuildContext? context, required List res _ => throw ArgumentError('Invalid type for qrCode: $qrCode'), }; } catch (e) { - showStatusMessage(message: 'The scanned QR code is not a valid URI.'); + showStatusMessage(message: (_) => 'The scanned QR code is not a valid URI.'); // TODO: Localize Logger.warning('Scanned Data: $qrCode'); return; } diff --git a/lib/utils/view_utils.dart b/lib/utils/view_utils.dart index 6b5bd1567..c08ccb93d 100644 --- a/lib/utils/view_utils.dart +++ b/lib/utils/view_utils.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; +import '../l10n/app_localizations.dart'; import 'globals.dart'; import 'logger.dart'; import 'riverpod/riverpod_providers/state_providers/status_message_provider.dart'; @@ -51,14 +52,14 @@ ScaffoldFeatureController? _showSnackBar( ); } -void showStatusMessage({required String message, String? subMessage}) { +void showStatusMessage({required String Function(AppLocalizations) message, String Function(AppLocalizations)? details}) { final ref = globalRef; - Logger.warning('$message : $subMessage'); + Logger.warning('$message : $details'); if (ref == null) { Logger.error('Could not show status message: globalRef is null'); return; } - ref.read(statusMessageProvider.notifier).state = (message, subMessage); + ref.read(statusMessageProvider.notifier).state = StatusMessage(message: message, details: details); } Future showAsyncDialog({ diff --git a/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_field.dart b/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_field.dart index 17cdf8519..710a463af 100644 --- a/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_field.dart +++ b/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_field.dart @@ -37,7 +37,7 @@ class _LinkInputViewState extends ConsumerState { Future addToken(Uri link) async { if (link.scheme != 'otpauth') { - ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.linkMustOtpAuth, ''); + ref.read(statusMessageProvider.notifier).state = StatusMessage(message: (localization) => localization.linkMustOtpAuth); return; } await ref.read(tokenProvider.notifier).handleLink(link); @@ -72,7 +72,7 @@ class _LinkInputViewState extends ConsumerState { onPressed: () async { ClipboardData? data = await Clipboard.getData('text/plain'); if (data == null || data.text == null || data.text!.isEmpty) { - if (context.mounted) ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.clipboardEmpty, null); + if (context.mounted) ref.read(statusMessageProvider.notifier).state = StatusMessage(message: (localization) => localization.clipboardEmpty); return; } setState(() => textController.text = data.text ?? ''); diff --git a/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_view.dart b/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_view.dart index 0bb2b1e9a..7cae1a893 100644 --- a/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_view.dart +++ b/lib/views/add_token_manually_view/add_token_manually_view_widgets/link_input_view.dart @@ -37,7 +37,7 @@ class _LinkInputViewState extends ConsumerState { Future addToken(Uri link) async { if (link.scheme != 'otpauth') { - ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.linkMustOtpAuth, null); + ref.read(statusMessageProvider.notifier).state = StatusMessage(message: (localization) => localization.linkMustOtpAuth); return; } await ref.read(tokenProvider.notifier).handleLink(link); @@ -76,7 +76,11 @@ class _LinkInputViewState extends ConsumerState { onPressed: () async { ClipboardData? data = await Clipboard.getData('text/plain'); if (data == null || data.text == null || data.text!.isEmpty) { - if (context.mounted) ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.clipboardEmpty, null); + if (context.mounted) { + ref.read(statusMessageProvider.notifier).state = StatusMessage( + message: (localizations) => localizations.clipboardEmpty, + ); + } return; } setState(() => textController.text = data.text ?? ''); diff --git a/lib/views/container_view/container_widgets/container_actions/transfer_container_action_dialog.dart b/lib/views/container_view/container_widgets/container_actions/transfer_container_action_dialog.dart index 5b2156403..e43dad163 100644 --- a/lib/views/container_view/container_widgets/container_actions/transfer_container_action_dialog.dart +++ b/lib/views/container_view/container_widgets/container_actions/transfer_container_action_dialog.dart @@ -68,12 +68,12 @@ class _TransferContainerDialogState extends ConsumerState _startTransfer(TokenContainerFinalized container) async { final String qrData; try { - qrData = await ref.read(tokenContainerProvider.notifier).getTransferQrData(container); + qrData = await ref.read(tokenContainerProvider.notifier).getRolloverQrData(container); } catch (e) { if (!mounted) return; return showStatusMessage( - message: AppLocalizations.of(context)!.transferContainerFailed, - subMessage: e.toString(), + message: (localization) => localization.transferContainerFailed, + details: (_) => e.toString(), ); } if (!mounted) return; diff --git a/lib/views/container_view/container_widgets/container_actions/transfer_dialogs/transfer_delete_dontainer_dialog.dart b/lib/views/container_view/container_widgets/container_actions/transfer_dialogs/transfer_delete_dontainer_dialog.dart index d89b1d7f2..4279e4739 100644 --- a/lib/views/container_view/container_widgets/container_actions/transfer_dialogs/transfer_delete_dontainer_dialog.dart +++ b/lib/views/container_view/container_widgets/container_actions/transfer_dialogs/transfer_delete_dontainer_dialog.dart @@ -134,6 +134,6 @@ class _TransferDeleteContainerDialogState extends ConsumerState route.isFirst); - showStatusMessage(message: 'Container and corresponding tokens successfully removed from this device.'); + showStatusMessage(message: (_) => 'Container and corresponding tokens successfully removed from this device.'); // TODO: Add to localization } } diff --git a/lib/views/container_view/container_widgets/container_widget_tile.dart b/lib/views/container_view/container_widgets/container_widget_tile.dart index df5ca4e7d..95aab2836 100644 --- a/lib/views/container_view/container_widgets/container_widget_tile.dart +++ b/lib/views/container_view/container_widgets/container_widget_tile.dart @@ -143,7 +143,7 @@ class _RolloverContainerTokensDialogState extends ConsumerState 'Failed to renew secrets', details: (_) => e.toString()); // TODO: localize } } } diff --git a/lib/views/import_tokens_view/pages/import_plain_tokens_page.dart b/lib/views/import_tokens_view/pages/import_plain_tokens_page.dart index 2b23c9845..951b83d44 100644 --- a/lib/views/import_tokens_view/pages/import_plain_tokens_page.dart +++ b/lib/views/import_tokens_view/pages/import_plain_tokens_page.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import '../../../l10n/app_localizations.dart'; import '../../../model/enums/token_import_type.dart'; @@ -49,7 +50,7 @@ class ImportPlainTokensPage extends ConsumerStatefulWidget { return ImportPlainTokensPage._( key: key, importedTokens: importedTokens, - failedImports: failedImports, + failedImports: failedImports.map((failedImport) => failedImport(AppLocalizationsEn())).toList(), titleName: titleName, selectedType: selectedType, // numOfDuplicates: duplicates.length, diff --git a/lib/views/import_tokens_view/pages/import_start_page.dart b/lib/views/import_tokens_view/pages/import_start_page.dart index f1ddf31ca..2d10be229 100644 --- a/lib/views/import_tokens_view/pages/import_start_page.dart +++ b/lib/views/import_tokens_view/pages/import_start_page.dart @@ -158,7 +158,7 @@ class _ImportStartPageState extends ConsumerState { Logger.warning("No file selected"); return null; } - if (await fileProcessor.fileIsValid(file) == false) { + if (!await fileProcessor.fileIsValid(file)) { return localizations.invalidBackupFile(widget.appName); } setState(() => _errorText = null); diff --git a/lib/views/import_tokens_view/widgets/dialogs/qr_not_found_dialog.dart b/lib/views/import_tokens_view/widgets/dialogs/qr_not_found_dialog.dart index 7a4eaf933..4cc2f482b 100644 --- a/lib/views/import_tokens_view/widgets/dialogs/qr_not_found_dialog.dart +++ b/lib/views/import_tokens_view/widgets/dialogs/qr_not_found_dialog.dart @@ -73,7 +73,7 @@ class QrNotFoundDialog extends StatelessWidget { } catch (e) { if (!context.mounted) return; Navigator.of(context).pop(); - showStatusMessage(message: "File not currently available! Please try again."); + showStatusMessage(message: (_) => "File not currently available! Please try again."); // TODO: Add to localization return; } if (!context.mounted) return; diff --git a/lib/views/main_view/main_view_widgets/connectivity_listener.dart b/lib/views/main_view/main_view_widgets/connectivity_listener.dart index 78478b939..0fc1fed1c 100644 --- a/lib/views/main_view/main_view_widgets/connectivity_listener.dart +++ b/lib/views/main_view/main_view_widgets/connectivity_listener.dart @@ -39,7 +39,7 @@ class ConnectivityListener extends ConsumerWidget { if (newState.hasPushTokens) { Logger.info("Connectivity changed: $connectivity"); if (!context.mounted) return; - ref.read(statusMessageProvider.notifier).state = (AppLocalizations.of(context)!.noNetworkConnection, null); + ref.read(statusMessageProvider.notifier).state = StatusMessage(message: (localization) => AppLocalizations.of(context)!.noNetworkConnection); } }); } diff --git a/lib/views/main_view/main_view_widgets/folder_widgets/add_token_folder_dialog.dart b/lib/views/main_view/main_view_widgets/folder_widgets/add_token_folder_dialog.dart index 80533777c..2500bd898 100644 --- a/lib/views/main_view/main_view_widgets/folder_widgets/add_token_folder_dialog.dart +++ b/lib/views/main_view/main_view_widgets/folder_widgets/add_token_folder_dialog.dart @@ -60,7 +60,7 @@ class AddTokenFolderDialog extends ConsumerWidget { softWrap: false, ), onPressed: () async { - if ((await ref.read(introductionNotifierProvider.future)).isCompleted(Introduction.addFolder) == false) { + if (!(await ref.read(introductionNotifierProvider.future)).isCompleted(Introduction.addFolder)) { ref.read(introductionNotifierProvider.notifier).complete(Introduction.addFolder); } ref.read(tokenFolderProvider.notifier).addNewFolder(textController.text); diff --git a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/delete_token_folder_action.dart b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/delete_token_folder_action.dart index 105486d38..e28dfa584 100644 --- a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/delete_token_folder_action.dart +++ b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/delete_token_folder_action.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../l10n/app_localizations.dart'; import '../../../../../model/token_folder.dart'; @@ -40,7 +41,9 @@ class DeleteTokenFolderAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.deleteColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (folder.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.unlock) == false) return; + if (folder.isLocked && !await lockAuth(reason: (localization) => localization.unlock, localization: AppLocalizations.of(context)!)) { + return; + } _showDialog(); }, child: Column( @@ -58,11 +61,8 @@ class DeleteTokenFolderAction extends ConsumerSlideableAction { ); } - void _showDialog() => showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, - builder: (BuildContext context) { - return DefaultDialog( + void _showDialog() => showAsyncDialog( + builder: (BuildContext context) => DefaultDialog( scrollable: true, title: Text( AppLocalizations.of(context)!.confirmDeletion, @@ -92,6 +92,6 @@ class DeleteTokenFolderAction extends ConsumerSlideableAction { ), ), ], - ); - }); + ), + ); } diff --git a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/lock_token_folder_action.dart b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/lock_token_folder_action.dart index 09d6f0031..70cc16bcb 100644 --- a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/lock_token_folder_action.dart +++ b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/lock_token_folder_action.dart @@ -38,7 +38,7 @@ class LockTokenFolderAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.lockColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (await lockAuth(localizedReason: AppLocalizations.of(context)!.unlock) == false) return; + if (await lockAuth(reason: (localization) => localization.unlock, localization: AppLocalizations.of(context)!) == false) return; globalRef?.read(tokenFolderProvider.notifier).toggleFolderLock(folder); }, child: Column( diff --git a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/rename_token_folder_action.dart b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/rename_token_folder_action.dart index e2a31e9af..41725eace 100644 --- a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/rename_token_folder_action.dart +++ b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_actions.dart/rename_token_folder_action.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../../../widgets/pi_text_field.dart'; import '../../../../../l10n/app_localizations.dart'; @@ -41,7 +42,9 @@ class RenameTokenFolderAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.editColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (folder.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.unlock) == false) return; + if (folder.isLocked && !await lockAuth(reason: (localization) => localization.unlock, localization: AppLocalizations.of(context)!)) { + return; + } _showDialog(); }, child: Column( @@ -60,61 +63,58 @@ class RenameTokenFolderAction extends ConsumerSlideableAction { void _showDialog() { TextEditingController nameInputController = TextEditingController(text: folder.label); - showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, - builder: (BuildContext context) { - return DefaultDialog( - scrollable: true, - title: Text( - AppLocalizations.of(context)!.renameTokenFolder, + showAsyncDialog(builder: (BuildContext context) { + return DefaultDialog( + scrollable: true, + title: Text( + AppLocalizations.of(context)!.renameTokenFolder, + ), + content: PiTextField( + autofocus: true, + controller: nameInputController, + labelText: AppLocalizations.of(context)!.name, + validator: (value) { + if (value!.isEmpty) { + return AppLocalizations.of(context)!.name; + } + return null; + }, + ), + actions: [ + TextButton( + child: Text( + AppLocalizations.of(context)!.cancel, + overflow: TextOverflow.fade, + softWrap: false, ), - content: PiTextField( - autofocus: true, - controller: nameInputController, - labelText: AppLocalizations.of(context)!.name, - validator: (value) { - if (value!.isEmpty) { - return AppLocalizations.of(context)!.name; - } - return null; - }, + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: Text( + AppLocalizations.of(context)!.rename, + overflow: TextOverflow.fade, + softWrap: false, ), - actions: [ - TextButton( - child: Text( - AppLocalizations.of(context)!.cancel, - overflow: TextOverflow.fade, - softWrap: false, - ), - onPressed: () => Navigator.of(context).pop(), - ), - TextButton( - child: Text( - AppLocalizations.of(context)!.rename, - overflow: TextOverflow.fade, - softWrap: false, - ), - onPressed: () async { - final newLabel = nameInputController.text.trim(); - if (newLabel.isEmpty) return; - final success = await globalRef?.read(tokenFolderProvider.notifier).updateLabel(folder, newLabel); - if (success != null) { - Logger.info( - 'Renamed token:', - error: '\'${folder.label}\' changed to \'$newLabel\'', - ); - } else { - Logger.warning( - 'Failed to rename token', - error: '\'${folder.label}\' to \'$newLabel\'', - ); - } - if (context.mounted) Navigator.of(context).pop(); - }, - ), - ], - ); - }); + onPressed: () async { + final newLabel = nameInputController.text.trim(); + if (newLabel.isEmpty) return; + final success = await globalRef?.read(tokenFolderProvider.notifier).updateLabel(folder, newLabel); + if (success != null) { + Logger.info( + 'Renamed token:', + error: '\'${folder.label}\' changed to \'$newLabel\'', + ); + } else { + Logger.warning( + 'Failed to rename token', + error: '\'${folder.label}\' to \'$newLabel\'', + ); + } + if (context.mounted) Navigator.of(context).pop(); + }, + ), + ], + ); + }); } } diff --git a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_expandable_widgets/token_folder_expandable_header.dart b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_expandable_widgets/token_folder_expandable_header.dart index 32a298d10..42571de36 100644 --- a/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_expandable_widgets/token_folder_expandable_header.dart +++ b/lib/views/main_view/main_view_widgets/folder_widgets/token_folder_expandable_widgets/token_folder_expandable_header.dart @@ -120,7 +120,8 @@ class _TokenFolderExpandableHeaderState extends ConsumerState localization.expandLockedFolder, localization: AppLocalizations.of(context)!)) { return; } if (!mounted) return; diff --git a/lib/views/main_view/main_view_widgets/main_view_navigation_buttons/qr_scanner_button.dart b/lib/views/main_view/main_view_widgets/main_view_navigation_buttons/qr_scanner_button.dart index 7f975a752..96983c217 100644 --- a/lib/views/main_view/main_view_widgets/main_view_navigation_buttons/qr_scanner_button.dart +++ b/lib/views/main_view/main_view_widgets/main_view_navigation_buttons/qr_scanner_button.dart @@ -24,7 +24,6 @@ import 'package:permission_handler/permission_handler.dart'; import '../../../../../../../utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../../model/processor_result.dart'; -import '../../../../utils/globals.dart'; import '../../../../utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart'; import '../../../../utils/utils.dart'; import '../../../../utils/view_utils.dart'; @@ -48,10 +47,10 @@ class QrScannerButton extends ConsumerWidget { ); return; } - if (globalNavigatorKey.currentContext == null) return; + if (!context.mounted) return; /// Open the QR-code scanner and call `handleQrCode`, with the scanned code as the argument. - Navigator.pushNamed(globalNavigatorKey.currentContext!, QRScannerView.routeName).then((qrCode) { + Navigator.pushNamed(context, QRScannerView.routeName).then((qrCode) { final resultHandlers = [ ref.read(tokenProvider.notifier), ref.read(tokenContainerProvider.notifier), diff --git a/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/actions/edit_day_password_token_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/actions/edit_day_password_token_action.dart index 9731e7e13..19eede46b 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/actions/edit_day_password_token_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/actions/edit_day_password_token_action.dart @@ -19,13 +19,13 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../../../l10n/app_localizations.dart'; import '../../../../../../model/enums/introduction.dart'; import '../../../../../../model/tokens/day_password_token.dart'; import '../../../../../../utils/customization/theme_extentions/action_theme.dart'; -import '../../../../../../utils/globals.dart'; import '../../../../../../utils/lock_auth.dart'; import '../../../../../../utils/riverpod/riverpod_providers/generated_providers/introduction_provider.dart'; import '../../../../../../widgets/focused_item_as_overlay.dart'; @@ -45,7 +45,7 @@ class EditDayPassowrdTokenAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.editColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.editLockedToken) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.editLockedToken, localization: AppLocalizations.of(context)!)) { return; } _showDialog(); @@ -74,9 +74,7 @@ class EditDayPassowrdTokenAction extends ConsumerSlideableAction { ), )); - void _showDialog() => showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, + void _showDialog() => showAsyncDialog( builder: (BuildContext context) => DefaultEditActionDialog( token: token, additionalChildren: [ diff --git a/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_delete_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_delete_action.dart index eb651f122..076b016f4 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_delete_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_delete_action.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../l10n/app_localizations.dart'; import '../../../../../model/tokens/token.dart'; @@ -43,7 +44,7 @@ class DefaultDeleteAction extends ConsumerSlideableAction { foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: isEnabled ? (_) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.deleteLockedToken) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.deleteLockedToken, localization: AppLocalizations.of(context)!)) { return; } _showDialog(); @@ -64,53 +65,47 @@ class DefaultDeleteAction extends ConsumerSlideableAction { ); } - void _showDialog() => globalNavigatorKey.currentContext == null - ? null - : showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, - builder: (BuildContext context) { - return DefaultDialog( - scrollable: true, - title: Text( - AppLocalizations.of(context)!.confirmDeletion, - style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).colorScheme.error), - ), - content: Column( - children: [ - Text(AppLocalizations.of(context)!.confirmDeletionOf(token.label), style: Theme.of(context).textTheme.bodyMedium), - const SizedBox(height: 8), - Text( - AppLocalizations.of(context)!.confirmTokenDeletionHint, - style: Theme.of(context).textTheme.bodySmall, - ), - ], - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text( - AppLocalizations.of(context)!.cancel, - overflow: TextOverflow.fade, - softWrap: false, - ), - ), - TextButton( - onPressed: () { - LoadingIndicator.show( - context: context, - action: () async => globalRef?.read(tokenProvider.notifier).removeToken(token), - ); - Navigator.of(context).pop(); - }, - child: Text( - AppLocalizations.of(context)!.delete, - style: TextStyle(color: Theme.of(context).colorScheme.error), - overflow: TextOverflow.fade, - softWrap: false, - ), + void _showDialog() => showAsyncDialog( + builder: (BuildContext context) => DefaultDialog( + scrollable: true, + title: Text( + AppLocalizations.of(context)!.confirmDeletion, + style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).colorScheme.error), + ), + content: Column( + children: [ + Text(AppLocalizations.of(context)!.confirmDeletionOf(token.label), style: Theme.of(context).textTheme.bodyMedium), + const SizedBox(height: 8), + Text( + AppLocalizations.of(context)!.confirmTokenDeletionHint, + style: Theme.of(context).textTheme.bodySmall, ), ], - ); - }); + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text( + AppLocalizations.of(context)!.cancel, + overflow: TextOverflow.fade, + softWrap: false, + ), + ), + TextButton( + onPressed: () { + LoadingIndicator.show( + context: context, + action: () async => globalRef?.read(tokenProvider.notifier).removeToken(token), + ); + Navigator.of(context).pop(); + }, + child: Text( + AppLocalizations.of(context)!.delete, + style: TextStyle(color: Theme.of(context).colorScheme.error), + overflow: TextOverflow.fade, + softWrap: false, + ), + ), + ], + )); } diff --git a/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_edit_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_edit_action.dart index a9864a9e9..762e1e262 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_edit_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_edit_action.dart @@ -20,6 +20,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../l10n/app_localizations.dart'; import '../../../../../model/enums/introduction.dart'; @@ -44,7 +45,7 @@ class DefaultEditAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.editColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.editLockedToken) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.editLockedToken, localization: AppLocalizations.of(context)!)) { return; } _showDialog(); @@ -73,12 +74,8 @@ class DefaultEditAction extends ConsumerSlideableAction { )); } - void _showDialog() { - showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, - builder: (BuildContext context) { - return DefaultEditActionDialog( + void _showDialog() => showAsyncDialog( + builder: (BuildContext context) => DefaultEditActionDialog( token: token, onSaveButtonPressed: ({required newLabel, newImageUrl}) async { if (newLabel.isEmpty) return; @@ -90,8 +87,6 @@ class DefaultEditAction extends ConsumerSlideableAction { Logger.info('Token edited: ${token.label} -> ${edited.label}, ${token.tokenImage} -> ${edited.tokenImage}'); if (context.mounted) Navigator.of(context).pop(); }, - ); - }, - ); - } + ), + ); } diff --git a/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_lock_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_lock_action.dart index 195a954f6..3393f4b09 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_lock_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_lock_action.dart @@ -44,7 +44,7 @@ class DefaultLockAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.lockColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (await lockAuth(localizedReason: AppLocalizations.of(context)!.authenticateToUnLockToken) == false) return; + if (!await lockAuth(reason: (localization) => localization.authenticateToUnLockToken, localization: AppLocalizations.of(context)!)) return; Logger.info('Changing lock status of token to isLocked = ${!token.isLocked}'); globalRef?.read(tokenProvider.notifier).updateToken(token, (p0) => p0.copyWith(isLocked: !token.isLocked, isHidden: !token.isLocked)); diff --git a/lib/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/actions/edit_hotp_token_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/actions/edit_hotp_token_action.dart index 894770668..720add6f4 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/actions/edit_hotp_token_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/actions/edit_hotp_token_action.dart @@ -20,12 +20,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../../l10n/app_localizations.dart'; import '../../../../../../model/enums/introduction.dart'; import '../../../../../../model/tokens/hotp_token.dart'; import '../../../../../../utils/customization/theme_extentions/action_theme.dart'; -import '../../../../../../utils/globals.dart'; import '../../../../../../utils/lock_auth.dart'; import '../../../../../../utils/riverpod/riverpod_providers/generated_providers/introduction_provider.dart'; import '../../../../../../widgets/focused_item_as_overlay.dart'; @@ -45,7 +45,7 @@ class EditHOTPTokenAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.editColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.editLockedToken) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.editLockedToken, localization: AppLocalizations.of(context)!)) { return; } _showDialog(); @@ -75,9 +75,7 @@ class EditHOTPTokenAction extends ConsumerSlideableAction { ), ); - void _showDialog() => showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, + void _showDialog() => showAsyncDialog( builder: (BuildContext context) => DefaultEditActionDialog( token: token, additionalChildren: [ diff --git a/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/actions/edit_push_token_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/actions/edit_push_token_action.dart index 325af311b..5d691644e 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/actions/edit_push_token_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/actions/edit_push_token_action.dart @@ -20,6 +20,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../../l10n/app_localizations.dart'; import '../../../../../../model/enums/introduction.dart'; @@ -49,7 +50,7 @@ class EditPushTokenAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.editColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (token.isLocked && await lockAuth(localizedReason: appLocalizations.editLockedToken) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.editLockedToken, localization: appLocalizations)) { return; } _showDialog(); @@ -88,9 +89,7 @@ class EditPushTokenAction extends ConsumerSlideableAction { return null; } - void _showDialog() => showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, + void _showDialog() => showAsyncDialog( builder: (BuildContext context) { final pushUrl = TextEditingController(text: token.url.toString()); final appLocalizations = AppLocalizations.of(context)!; diff --git a/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart b/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart index 5642e5326..9e7719516 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/push_token_widgets/rollout_failed_widget.dart @@ -19,6 +19,7 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import '../../../../../l10n/app_localizations.dart'; import '../../../../../model/extensions/enums/push_token_rollout_state_extension.dart'; @@ -97,10 +98,7 @@ class StartRolloutWidget extends ConsumerWidget { ); } - Future _showDialog() => showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, - builder: (BuildContext context) { + Future _showDialog() => showAsyncDialog(builder: (BuildContext context) { final localizations = AppLocalizations.of(context)!; return DefaultDialog( scrollable: true, diff --git a/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/actions/edit_totp_token_action.dart b/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/actions/edit_totp_token_action.dart index 271f8008c..9d614a292 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/actions/edit_totp_token_action.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/actions/edit_totp_token_action.dart @@ -19,13 +19,13 @@ */ import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../../../l10n/app_localizations.dart'; import '../../../../../../model/enums/introduction.dart'; import '../../../../../../model/tokens/totp_token.dart'; import '../../../../../../utils/customization/theme_extentions/action_theme.dart'; -import '../../../../../../utils/globals.dart'; import '../../../../../../utils/lock_auth.dart'; import '../../../../../../utils/riverpod/riverpod_providers/generated_providers/introduction_provider.dart'; import '../../../../../../widgets/focused_item_as_overlay.dart'; @@ -45,7 +45,7 @@ class EditTOTPTokenAction extends ConsumerSlideableAction { backgroundColor: Theme.of(context).extension()!.editColor, foregroundColor: Theme.of(context).extension()!.foregroundColor, onPressed: (context) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.editLockedToken) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.editLockedToken, localization: AppLocalizations.of(context)!)) { return; } _showDialog(); @@ -74,9 +74,7 @@ class EditTOTPTokenAction extends ConsumerSlideableAction { ), )); - void _showDialog() => showDialog( - useRootNavigator: false, - context: globalNavigatorKey.currentContext!, + void _showDialog() => showAsyncDialog( builder: (BuildContext context) => DefaultEditActionDialog( token: token, additionalChildren: [ diff --git a/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/export_tokens_to_file_dialog.dart b/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/export_tokens_to_file_dialog.dart index 3c1b97a59..2816ea64e 100644 --- a/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/export_tokens_to_file_dialog.dart +++ b/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/export_tokens_to_file_dialog.dart @@ -141,7 +141,8 @@ class _ExportTokensToFileDialogState extends ConsumerState localization.exportLockedTokenReason, + localization: appLocalizations, autoAuthIfUnsupported: true, ); if (!authenticated || !mounted) return; @@ -175,7 +176,7 @@ class _ExportTokensToFileDialogState extends ConsumerState l.errorSavingFile); setState(() => _exportPressed = false); return false; } @@ -190,7 +191,7 @@ class _ExportTokensToFileDialogState extends ConsumerState l.errorSavingFile); setState(() => _exportPressed = false); return false; } diff --git a/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/select_export_type_dialog.dart b/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/select_export_type_dialog.dart index 44f3ed6a4..f9a4f848d 100644 --- a/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/select_export_type_dialog.dart +++ b/lib/views/settings_view/settings_groups/import_export_tokens_widgets/dialogs/select_export_type_dialog.dart @@ -71,8 +71,10 @@ class SelectExportTypeDialog extends StatelessWidget { } void _selectTokenDialog(BuildContext context) async { + final localization = AppLocalizations.of(context)!; final authenticated = await lockAuth( - localizedReason: AppLocalizations.of(context)!.exportLockedTokenReason, + reason: (l) => l.exportLockedTokenReason, + localization: localization, autoAuthIfUnsupported: true, ); if (!authenticated || !context.mounted) return; @@ -84,7 +86,7 @@ class SelectExportTypeDialog extends StatelessWidget { exportDialogBuilder: (tokens) { if (tokens.isEmpty) { return DefaultDialog( - content: Text(AppLocalizations.of(context)!.noTokenToExport), + content: Text(localization.noTokenToExport), ); } diff --git a/lib/views/settings_view/settings_groups/settings_group_push_token.dart b/lib/views/settings_view/settings_groups/settings_group_push_token.dart index ec0cfe719..c25947150 100644 --- a/lib/views/settings_view/settings_groups/settings_group_push_token.dart +++ b/lib/views/settings_view/settings_groups/settings_group_push_token.dart @@ -83,7 +83,7 @@ class SettingsGroupPushTokenDialog extends ConsumerWidget { useRootNavigator: false, context: context, barrierDismissible: false, - builder: (context) => const UpdateFirebaseTokenDialog(), + builder: (context) => UpdateFirebaseTokenDialog(AppLocalizations.of(context)!), ), child: Text( AppLocalizations.of(context)!.sync, diff --git a/lib/views/settings_view/settings_view_widgets/update_firebase_token_dialog.dart b/lib/views/settings_view/settings_view_widgets/update_firebase_token_dialog.dart index 69fc038a6..1ac6c0836 100644 --- a/lib/views/settings_view/settings_view_widgets/update_firebase_token_dialog.dart +++ b/lib/views/settings_view/settings_view_widgets/update_firebase_token_dialog.dart @@ -23,14 +23,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../l10n/app_localizations.dart'; import '../../../model/tokens/push_token.dart'; -import '../../../utils/globals.dart'; import '../../../utils/logger.dart'; import '../../../utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart'; import '../../../utils/view_utils.dart'; import '../../../widgets/dialog_widgets/default_dialog.dart'; class UpdateFirebaseTokenDialog extends ConsumerStatefulWidget { - const UpdateFirebaseTokenDialog({super.key}); + final AppLocalizations appLocalizations; + const UpdateFirebaseTokenDialog(this.appLocalizations, {super.key}); @override ConsumerState createState() => _UpdateFirebaseTokenDialogState(); @@ -45,7 +45,7 @@ class _UpdateFirebaseTokenDialogState extends ConsumerState l.firebaseToken, + details: (l) => l.errorSynchronizationNoNetworkConnection, ); return; } @@ -89,7 +89,7 @@ class _UpdateFirebaseTokenDialogState extends ConsumerState { final hidden = await HomeWidgetUtils().hideAllOtps(); if (hidden) Logger.info('Hid all HomeWidget OTPs on resume'); }, - // onInactive: () => log('App inactive'), onHide: () async { if (await ref.read(tokenProvider.notifier).onMinimizeApp() == false) { Logger.error('Failed to save tokens on Hide'); @@ -64,10 +63,6 @@ class _AppWrapperState extends ConsumerState<_AppWrapper> { await FlutterLocalNotificationsPlugin().cancelAll(); Logger.info('Collapsed locked folders on Hide'); }, - // onShow: () => log('App shown'), - // onPause: () => log('App paused'), - // onRestart: () => log('App restarted'), - // onDetach: () => log('App detached'), onExitRequested: () async { Logger.info('Exit requested'); return AppExitResponse.exit; @@ -90,23 +85,13 @@ class _AppWrapperState extends ConsumerState<_AppWrapper> { stateNotifierProviderListeners: const [], buildlessProviderListener: [ HomeWidgetTokenStateListener(provider: tokenProvider), - // ContainerListensToTokenState(provider: tokenProvider, ref: ref), ], streamNotifierProviderListeners: [ NavigationDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider), HomeWidgetDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider), TokenImportDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider), ], - // asyncNotifierProviderListeners: [ - // ...container.map( - // (container) { - // return TokenStateListensToContainer( - // containerProvider: tokenContainerNotifierProviderOf(container: container), - // ref: ref, - // ); - // }, - // ), - // ], + asyncNotifierProviderListeners: [], child: EasyDynamicThemeWidget( child: widget.child, ), @@ -114,26 +99,3 @@ class _AppWrapperState extends ConsumerState<_AppWrapper> { ); } } - -// class TokenStateListensToContainer extends AsyncContainerListener { -// final WidgetRef ref; -// TokenStateListensToContainer({ -// required super.containerProvider, -// required this.ref, -// }) : super(onNewState: (previous, next) => _onNewState(previous, next, ref)); - -// static Future _onNewState(AsyncValue? previous, AsyncValue next, WidgetRef ref) async { -// Logger.info('TokenState got new container state', name: 'TokenStateListensToContainer'); -// final value = next.value; -// if (value == null) return; -// final provider = ref.read(tokenProvider.notifier); -// provider.updateContainerTokens(value); -// } -// } - -// abstract class AsyncContainerListener extends AsyncNotifierProviderListener { -// const AsyncContainerListener({ -// required TokenContainerNotifierProvider containerProvider, -// required super.onNewState, -// }) : super(provider: containerProvider); -// } diff --git a/lib/widgets/dialog_widgets/push_request_dialog/push_request_dialog.dart b/lib/widgets/dialog_widgets/push_request_dialog/push_request_dialog.dart index b47be4868..3001a79fb 100644 --- a/lib/widgets/dialog_widgets/push_request_dialog/push_request_dialog.dart +++ b/lib/widgets/dialog_widgets/push_request_dialog/push_request_dialog.dart @@ -123,7 +123,7 @@ class _PushRequestDialogState extends ConsumerState { } Future _onAccept(PushToken token, {String? answer}) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.authToAcceptPushRequest) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.authToAcceptPushRequest, localization: AppLocalizations.of(context)!)) { return; } await ref.read(pushRequestProvider.notifier).accept(token, widget.pushRequest, selectedAnswer: answer); @@ -131,7 +131,7 @@ class _PushRequestDialogState extends ConsumerState { } Future _onDecline(PushToken token) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.authToDeclinePushRequest) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.authToDeclinePushRequest, localization: AppLocalizations.of(context)!)) { return; } await ref.read(pushRequestProvider.notifier).decline(token, widget.pushRequest); @@ -139,7 +139,7 @@ class _PushRequestDialogState extends ConsumerState { } Future _onDiscard(PushToken token) async { - if (token.isLocked && await lockAuth(localizedReason: AppLocalizations.of(context)!.authToDeclinePushRequest) == false) { + if (token.isLocked && !await lockAuth(reason: (localization) => localization.authToDeclinePushRequest, localization: AppLocalizations.of(context)!)) { return; } await ref.read(pushRequestProvider.notifier).remove(widget.pushRequest); diff --git a/lib/widgets/status_bar.dart b/lib/widgets/status_bar.dart index a81eebc39..ad94af02b 100644 --- a/lib/widgets/status_bar.dart +++ b/lib/widgets/status_bar.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../l10n/app_localizations.dart'; import '../utils/riverpod/riverpod_providers/state_providers/status_message_provider.dart'; import '../utils/utils.dart'; @@ -15,9 +16,9 @@ class StatusBar extends ConsumerStatefulWidget { } class _StatusBarState extends ConsumerState { - (String, String?)? previousStatusMessage; - (String, String?)? currentStatusMessage; - Queue<(String, String?)> statusbarQueue = Queue(); + StatusMessage? previousStatusMessage; + StatusMessage? currentStatusMessage; + Queue statusbarQueue = Queue(); late Function(DismissDirection) onDismissed; @@ -47,7 +48,7 @@ class _StatusBarState extends ConsumerState { super.initState(); } - void _addToQueueIfNotInQueue((String, String?)? statusMessage) { + void _addToQueueIfNotInQueue(StatusMessage? statusMessage) { if (statusMessage == null) return; if (!statusbarQueue.contains(statusMessage) && currentStatusMessage != statusMessage) { statusbarQueue.add(statusMessage); @@ -62,9 +63,10 @@ class _StatusBarState extends ConsumerState { } } - void _showStatusbarOverlay((String, String?) statusMessage) { - final statusText = statusMessage.$1; - final statusSubText = statusMessage.$2; + void _showStatusbarOverlay(StatusMessage statusMessage) { + final localizations = AppLocalizations.of(context)!; + final statusText = statusMessage.message(localizations); + final statusSubText = statusMessage.details?.call(localizations); if (statusbarOverlay != null) { statusbarOverlay?.remove(); statusbarOverlay = null; diff --git a/test/tests_app_wrapper.mocks.dart b/test/tests_app_wrapper.mocks.dart index 90fb6250a..513ff4fec 100644 --- a/test/tests_app_wrapper.mocks.dart +++ b/test/tests_app_wrapper.mocks.dart @@ -3,32 +3,34 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i13; -import 'dart:typed_data' as _i26; +import 'dart:async' as _i14; +import 'dart:typed_data' as _i27; -import 'package:firebase_core/firebase_core.dart' as _i30; -import 'package:firebase_messaging/firebase_messaging.dart' as _i29; -import 'package:http/http.dart' as _i7; +import 'package:firebase_core/firebase_core.dart' as _i31; +import 'package:firebase_messaging/firebase_messaging.dart' as _i30; +import 'package:http/http.dart' as _i8; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i24; -import 'package:pointycastle/export.dart' as _i8; +import 'package:mockito/src/dummies.dart' as _i26; +import 'package:pointycastle/export.dart' as _i9; import 'package:privacyidea_authenticator/api/interfaces/container_api.dart' - as _i22; + as _i23; import 'package:privacyidea_authenticator/interfaces/repo/introduction_repository.dart' - as _i17; -import 'package:privacyidea_authenticator/interfaces/repo/push_request_repository.dart' as _i18; +import 'package:privacyidea_authenticator/interfaces/repo/push_request_repository.dart' + as _i19; import 'package:privacyidea_authenticator/interfaces/repo/settings_repository.dart' - as _i15; + as _i16; import 'package:privacyidea_authenticator/interfaces/repo/token_container_repository.dart' - as _i20; + as _i21; import 'package:privacyidea_authenticator/interfaces/repo/token_folder_repository.dart' - as _i16; + as _i17; import 'package:privacyidea_authenticator/interfaces/repo/token_repository.dart' - as _i12; + as _i13; +import 'package:privacyidea_authenticator/model/api_results/pi_server_results/pi_server_result_value.dart' + as _i7; import 'package:privacyidea_authenticator/model/enums/ec_key_algorithm.dart' - as _i28; -import 'package:privacyidea_authenticator/model/push_request.dart' as _i19; + as _i29; +import 'package:privacyidea_authenticator/model/push_request.dart' as _i20; import 'package:privacyidea_authenticator/model/riverpod_states/introduction_state.dart' as _i4; import 'package:privacyidea_authenticator/model/riverpod_states/push_request_state.dart' @@ -41,15 +43,15 @@ import 'package:privacyidea_authenticator/model/riverpod_states/token_folder_sta as _i3; import 'package:privacyidea_authenticator/model/riverpod_states/token_state.dart' as _i25; -import 'package:privacyidea_authenticator/model/token_container.dart' as _i21; -import 'package:privacyidea_authenticator/model/tokens/push_token.dart' as _i27; -import 'package:privacyidea_authenticator/model/tokens/token.dart' as _i14; -import 'package:privacyidea_authenticator/utils/ecc_utils.dart' as _i23; -import 'package:privacyidea_authenticator/utils/firebase_utils.dart' as _i9; +import 'package:privacyidea_authenticator/model/token_container.dart' as _i22; +import 'package:privacyidea_authenticator/model/tokens/push_token.dart' as _i28; +import 'package:privacyidea_authenticator/model/tokens/token.dart' as _i15; +import 'package:privacyidea_authenticator/utils/ecc_utils.dart' as _i24; +import 'package:privacyidea_authenticator/utils/firebase_utils.dart' as _i10; import 'package:privacyidea_authenticator/utils/privacyidea_io_client.dart' - as _i10; -import 'package:privacyidea_authenticator/utils/push_provider.dart' as _i31; -import 'package:privacyidea_authenticator/utils/rsa_utils.dart' as _i11; + as _i11; +import 'package:privacyidea_authenticator/utils/push_provider.dart' as _i32; +import 'package:privacyidea_authenticator/utils/rsa_utils.dart' as _i12; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -118,8 +120,9 @@ class _FakeTokenContainerState_4 extends _i1.SmartFake ); } -class _FakeResponse_5 extends _i1.SmartFake implements _i7.Response { - _FakeResponse_5( +class _FakeContainerFinalizationResponse_5 extends _i1.SmartFake + implements _i7.ContainerFinalizationResponse { + _FakeContainerFinalizationResponse_5( Object parent, Invocation parentInvocation, ) : super( @@ -128,8 +131,9 @@ class _FakeResponse_5 extends _i1.SmartFake implements _i7.Response { ); } -class _FakeRSAPublicKey_6 extends _i1.SmartFake implements _i8.RSAPublicKey { - _FakeRSAPublicKey_6( +class _FakeTransferQrData_6 extends _i1.SmartFake + implements _i7.TransferQrData { + _FakeTransferQrData_6( Object parent, Invocation parentInvocation, ) : super( @@ -138,8 +142,9 @@ class _FakeRSAPublicKey_6 extends _i1.SmartFake implements _i8.RSAPublicKey { ); } -class _FakeRSAPrivateKey_7 extends _i1.SmartFake implements _i8.RSAPrivateKey { - _FakeRSAPrivateKey_7( +class _FakeUnregisterContainerResult_7 extends _i1.SmartFake + implements _i7.UnregisterContainerResult { + _FakeUnregisterContainerResult_7( Object parent, Invocation parentInvocation, ) : super( @@ -148,10 +153,8 @@ class _FakeRSAPrivateKey_7 extends _i1.SmartFake implements _i8.RSAPrivateKey { ); } -class _FakeAsymmetricKeyPair_8 extends _i1.SmartFake - implements _i8.AsymmetricKeyPair { - _FakeAsymmetricKeyPair_8( +class _FakeResponse_8 extends _i1.SmartFake implements _i8.Response { + _FakeResponse_8( Object parent, Invocation parentInvocation, ) : super( @@ -160,8 +163,8 @@ class _FakeAsymmetricKeyPair_8 extends _i1.SmartFake + implements _i9.AsymmetricKeyPair { + _FakeAsymmetricKeyPair_11( Object parent, Invocation parentInvocation, ) : super( @@ -190,9 +195,8 @@ class _FakeFirebaseUtils_11 extends _i1.SmartFake implements _i9.FirebaseUtils { ); } -class _FakePrivacyideaIOClient_12 extends _i1.SmartFake - implements _i10.PrivacyideaIOClient { - _FakePrivacyideaIOClient_12( +class _FakeECPublicKey_12 extends _i1.SmartFake implements _i9.ECPublicKey { + _FakeECPublicKey_12( Object parent, Invocation parentInvocation, ) : super( @@ -201,8 +205,40 @@ class _FakePrivacyideaIOClient_12 extends _i1.SmartFake ); } -class _FakeRsaUtils_13 extends _i1.SmartFake implements _i11.RsaUtils { - _FakeRsaUtils_13( +class _FakeECPrivateKey_13 extends _i1.SmartFake implements _i9.ECPrivateKey { + _FakeECPrivateKey_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFirebaseUtils_14 extends _i1.SmartFake + implements _i10.FirebaseUtils { + _FakeFirebaseUtils_14( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePrivacyideaIOClient_15 extends _i1.SmartFake + implements _i11.PrivacyideaIOClient { + _FakePrivacyideaIOClient_15( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRsaUtils_16 extends _i1.SmartFake implements _i12.RsaUtils { + _FakeRsaUtils_16( Object parent, Invocation parentInvocation, ) : super( @@ -214,96 +250,96 @@ class _FakeRsaUtils_13 extends _i1.SmartFake implements _i11.RsaUtils { /// A class which mocks [TokenRepository]. /// /// See the documentation for Mockito's code generation for more information. -class MockTokenRepository extends _i1.Mock implements _i12.TokenRepository { +class MockTokenRepository extends _i1.Mock implements _i13.TokenRepository { @override - _i13.Future<_i14.Token?> loadToken(String? id) => (super.noSuchMethod( + _i14.Future<_i15.Token?> loadToken(String? id) => (super.noSuchMethod( Invocation.method( #loadToken, [id], ), - returnValue: _i13.Future<_i14.Token?>.value(), - returnValueForMissingStub: _i13.Future<_i14.Token?>.value(), - ) as _i13.Future<_i14.Token?>); + returnValue: _i14.Future<_i15.Token?>.value(), + returnValueForMissingStub: _i14.Future<_i15.Token?>.value(), + ) as _i14.Future<_i15.Token?>); @override - _i13.Future> loadTokens() => (super.noSuchMethod( + _i14.Future> loadTokens() => (super.noSuchMethod( Invocation.method( #loadTokens, [], ), - returnValue: _i13.Future>.value(<_i14.Token>[]), + returnValue: _i14.Future>.value(<_i15.Token>[]), returnValueForMissingStub: - _i13.Future>.value(<_i14.Token>[]), - ) as _i13.Future>); + _i14.Future>.value(<_i15.Token>[]), + ) as _i14.Future>); @override - _i13.Future saveOrReplaceToken(_i14.Token? token) => + _i14.Future saveOrReplaceToken(_i15.Token? token) => (super.noSuchMethod( Invocation.method( #saveOrReplaceToken, [token], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future> saveOrReplaceTokens( + _i14.Future> saveOrReplaceTokens( List? tokens) => (super.noSuchMethod( Invocation.method( #saveOrReplaceTokens, [tokens], ), - returnValue: _i13.Future>.value([]), - returnValueForMissingStub: _i13.Future>.value([]), - ) as _i13.Future>); + returnValue: _i14.Future>.value([]), + returnValueForMissingStub: _i14.Future>.value([]), + ) as _i14.Future>); @override - _i13.Future deleteToken(_i14.Token? token) => (super.noSuchMethod( + _i14.Future deleteToken(_i15.Token? token) => (super.noSuchMethod( Invocation.method( #deleteToken, [token], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future> deleteTokens(List? tokens) => + _i14.Future> deleteTokens(List? tokens) => (super.noSuchMethod( Invocation.method( #deleteTokens, [tokens], ), - returnValue: _i13.Future>.value([]), - returnValueForMissingStub: _i13.Future>.value([]), - ) as _i13.Future>); + returnValue: _i14.Future>.value([]), + returnValueForMissingStub: _i14.Future>.value([]), + ) as _i14.Future>); } /// A class which mocks [SettingsRepository]. /// /// See the documentation for Mockito's code generation for more information. class MockSettingsRepository extends _i1.Mock - implements _i15.SettingsRepository { + implements _i16.SettingsRepository { @override - _i13.Future saveSettings(_i2.SettingsState? settings) => + _i14.Future saveSettings(_i2.SettingsState? settings) => (super.noSuchMethod( Invocation.method( #saveSettings, [settings], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future<_i2.SettingsState> loadSettings() => (super.noSuchMethod( + _i14.Future<_i2.SettingsState> loadSettings() => (super.noSuchMethod( Invocation.method( #loadSettings, [], ), - returnValue: _i13.Future<_i2.SettingsState>.value(_FakeSettingsState_0( + returnValue: _i14.Future<_i2.SettingsState>.value(_FakeSettingsState_0( this, Invocation.method( #loadSettings, @@ -311,40 +347,40 @@ class MockSettingsRepository extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i2.SettingsState>.value(_FakeSettingsState_0( + _i14.Future<_i2.SettingsState>.value(_FakeSettingsState_0( this, Invocation.method( #loadSettings, [], ), )), - ) as _i13.Future<_i2.SettingsState>); + ) as _i14.Future<_i2.SettingsState>); } /// A class which mocks [TokenFolderRepository]. /// /// See the documentation for Mockito's code generation for more information. class MockTokenFolderRepository extends _i1.Mock - implements _i16.TokenFolderRepository { + implements _i17.TokenFolderRepository { @override - _i13.Future saveState(_i3.TokenFolderState? state) => + _i14.Future saveState(_i3.TokenFolderState? state) => (super.noSuchMethod( Invocation.method( #saveState, [state], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future<_i3.TokenFolderState> loadState() => (super.noSuchMethod( + _i14.Future<_i3.TokenFolderState> loadState() => (super.noSuchMethod( Invocation.method( #loadState, [], ), returnValue: - _i13.Future<_i3.TokenFolderState>.value(_FakeTokenFolderState_1( + _i14.Future<_i3.TokenFolderState>.value(_FakeTokenFolderState_1( this, Invocation.method( #loadState, @@ -352,42 +388,42 @@ class MockTokenFolderRepository extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i3.TokenFolderState>.value(_FakeTokenFolderState_1( + _i14.Future<_i3.TokenFolderState>.value(_FakeTokenFolderState_1( this, Invocation.method( #loadState, [], ), )), - ) as _i13.Future<_i3.TokenFolderState>); + ) as _i14.Future<_i3.TokenFolderState>); } /// A class which mocks [IntroductionRepository]. /// /// See the documentation for Mockito's code generation for more information. class MockIntroductionRepository extends _i1.Mock - implements _i17.IntroductionRepository { + implements _i18.IntroductionRepository { @override - _i13.Future saveCompletedIntroductions( + _i14.Future saveCompletedIntroductions( _i4.IntroductionState? introductions) => (super.noSuchMethod( Invocation.method( #saveCompletedIntroductions, [introductions], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future<_i4.IntroductionState> loadCompletedIntroductions() => + _i14.Future<_i4.IntroductionState> loadCompletedIntroductions() => (super.noSuchMethod( Invocation.method( #loadCompletedIntroductions, [], ), returnValue: - _i13.Future<_i4.IntroductionState>.value(_FakeIntroductionState_2( + _i14.Future<_i4.IntroductionState>.value(_FakeIntroductionState_2( this, Invocation.method( #loadCompletedIntroductions, @@ -395,29 +431,29 @@ class MockIntroductionRepository extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i4.IntroductionState>.value(_FakeIntroductionState_2( + _i14.Future<_i4.IntroductionState>.value(_FakeIntroductionState_2( this, Invocation.method( #loadCompletedIntroductions, [], ), )), - ) as _i13.Future<_i4.IntroductionState>); + ) as _i14.Future<_i4.IntroductionState>); } /// A class which mocks [PushRequestRepository]. /// /// See the documentation for Mockito's code generation for more information. class MockPushRequestRepository extends _i1.Mock - implements _i18.PushRequestRepository { + implements _i19.PushRequestRepository { @override - _i13.Future<_i5.PushRequestState> loadState() => (super.noSuchMethod( + _i14.Future<_i5.PushRequestState> loadState() => (super.noSuchMethod( Invocation.method( #loadState, [], ), returnValue: - _i13.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( + _i14.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( this, Invocation.method( #loadState, @@ -425,39 +461,39 @@ class MockPushRequestRepository extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( + _i14.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( this, Invocation.method( #loadState, [], ), )), - ) as _i13.Future<_i5.PushRequestState>); + ) as _i14.Future<_i5.PushRequestState>); @override - _i13.Future saveState(_i5.PushRequestState? pushRequestState) => + _i14.Future saveState(_i5.PushRequestState? pushRequestState) => (super.noSuchMethod( Invocation.method( #saveState, [pushRequestState], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future clearState() => (super.noSuchMethod( + _i14.Future clearState() => (super.noSuchMethod( Invocation.method( #clearState, [], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future<_i5.PushRequestState> addRequest( - _i19.PushRequest? pushRequest, { + _i14.Future<_i5.PushRequestState> addRequest( + _i20.PushRequest? pushRequest, { _i5.PushRequestState? state, }) => (super.noSuchMethod( @@ -467,7 +503,7 @@ class MockPushRequestRepository extends _i1.Mock {#state: state}, ), returnValue: - _i13.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( + _i14.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( this, Invocation.method( #addRequest, @@ -476,7 +512,7 @@ class MockPushRequestRepository extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( + _i14.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( this, Invocation.method( #addRequest, @@ -484,11 +520,11 @@ class MockPushRequestRepository extends _i1.Mock {#state: state}, ), )), - ) as _i13.Future<_i5.PushRequestState>); + ) as _i14.Future<_i5.PushRequestState>); @override - _i13.Future<_i5.PushRequestState> removeRequest( - _i19.PushRequest? pushRequest, { + _i14.Future<_i5.PushRequestState> removeRequest( + _i20.PushRequest? pushRequest, { _i5.PushRequestState? state, }) => (super.noSuchMethod( @@ -498,7 +534,7 @@ class MockPushRequestRepository extends _i1.Mock {#state: state}, ), returnValue: - _i13.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( + _i14.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( this, Invocation.method( #removeRequest, @@ -507,7 +543,7 @@ class MockPushRequestRepository extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( + _i14.Future<_i5.PushRequestState>.value(_FakePushRequestState_3( this, Invocation.method( #removeRequest, @@ -515,22 +551,22 @@ class MockPushRequestRepository extends _i1.Mock {#state: state}, ), )), - ) as _i13.Future<_i5.PushRequestState>); + ) as _i14.Future<_i5.PushRequestState>); } /// A class which mocks [TokenContainerRepository]. /// /// See the documentation for Mockito's code generation for more information. class MockTokenContainerRepository extends _i1.Mock - implements _i20.TokenContainerRepository { + implements _i21.TokenContainerRepository { @override - _i13.Future<_i6.TokenContainerState> loadContainerState() => + _i14.Future<_i6.TokenContainerState> loadContainerState() => (super.noSuchMethod( Invocation.method( #loadContainerState, [], ), - returnValue: _i13.Future<_i6.TokenContainerState>.value( + returnValue: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -538,7 +574,7 @@ class MockTokenContainerRepository extends _i1.Mock [], ), )), - returnValueForMissingStub: _i13.Future<_i6.TokenContainerState>.value( + returnValueForMissingStub: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -546,17 +582,17 @@ class MockTokenContainerRepository extends _i1.Mock [], ), )), - ) as _i13.Future<_i6.TokenContainerState>); + ) as _i14.Future<_i6.TokenContainerState>); @override - _i13.Future<_i6.TokenContainerState> saveContainerState( + _i14.Future<_i6.TokenContainerState> saveContainerState( _i6.TokenContainerState? containerState) => (super.noSuchMethod( Invocation.method( #saveContainerState, [containerState], ), - returnValue: _i13.Future<_i6.TokenContainerState>.value( + returnValue: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -564,7 +600,7 @@ class MockTokenContainerRepository extends _i1.Mock [containerState], ), )), - returnValueForMissingStub: _i13.Future<_i6.TokenContainerState>.value( + returnValueForMissingStub: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -572,17 +608,17 @@ class MockTokenContainerRepository extends _i1.Mock [containerState], ), )), - ) as _i13.Future<_i6.TokenContainerState>); + ) as _i14.Future<_i6.TokenContainerState>); @override - _i13.Future<_i6.TokenContainerState> saveContainerList( - List<_i21.TokenContainer>? containerList) => + _i14.Future<_i6.TokenContainerState> saveContainerList( + List<_i22.TokenContainer>? containerList) => (super.noSuchMethod( Invocation.method( #saveContainerList, [containerList], ), - returnValue: _i13.Future<_i6.TokenContainerState>.value( + returnValue: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -590,7 +626,7 @@ class MockTokenContainerRepository extends _i1.Mock [containerList], ), )), - returnValueForMissingStub: _i13.Future<_i6.TokenContainerState>.value( + returnValueForMissingStub: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -598,16 +634,16 @@ class MockTokenContainerRepository extends _i1.Mock [containerList], ), )), - ) as _i13.Future<_i6.TokenContainerState>); + ) as _i14.Future<_i6.TokenContainerState>); @override - _i13.Future<_i6.TokenContainerState> deleteContainer(String? serial) => + _i14.Future<_i6.TokenContainerState> deleteContainer(String? serial) => (super.noSuchMethod( Invocation.method( #deleteContainer, [serial], ), - returnValue: _i13.Future<_i6.TokenContainerState>.value( + returnValue: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -615,7 +651,7 @@ class MockTokenContainerRepository extends _i1.Mock [serial], ), )), - returnValueForMissingStub: _i13.Future<_i6.TokenContainerState>.value( + returnValueForMissingStub: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -623,16 +659,16 @@ class MockTokenContainerRepository extends _i1.Mock [serial], ), )), - ) as _i13.Future<_i6.TokenContainerState>); + ) as _i14.Future<_i6.TokenContainerState>); @override - _i13.Future<_i6.TokenContainerState> deleteAllContainer() => + _i14.Future<_i6.TokenContainerState> deleteAllContainer() => (super.noSuchMethod( Invocation.method( #deleteAllContainer, [], ), - returnValue: _i13.Future<_i6.TokenContainerState>.value( + returnValue: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -640,7 +676,7 @@ class MockTokenContainerRepository extends _i1.Mock [], ), )), - returnValueForMissingStub: _i13.Future<_i6.TokenContainerState>.value( + returnValueForMissingStub: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -648,28 +684,28 @@ class MockTokenContainerRepository extends _i1.Mock [], ), )), - ) as _i13.Future<_i6.TokenContainerState>); + ) as _i14.Future<_i6.TokenContainerState>); @override - _i13.Future<_i21.TokenContainer?> loadContainer(String? serial) => + _i14.Future<_i22.TokenContainer?> loadContainer(String? serial) => (super.noSuchMethod( Invocation.method( #loadContainer, [serial], ), - returnValue: _i13.Future<_i21.TokenContainer?>.value(), - returnValueForMissingStub: _i13.Future<_i21.TokenContainer?>.value(), - ) as _i13.Future<_i21.TokenContainer?>); + returnValue: _i14.Future<_i22.TokenContainer?>.value(), + returnValueForMissingStub: _i14.Future<_i22.TokenContainer?>.value(), + ) as _i14.Future<_i22.TokenContainer?>); @override - _i13.Future<_i6.TokenContainerState> saveContainer( - _i21.TokenContainer? container) => + _i14.Future<_i6.TokenContainerState> saveContainer( + _i22.TokenContainer? container) => (super.noSuchMethod( Invocation.method( #saveContainer, [container], ), - returnValue: _i13.Future<_i6.TokenContainerState>.value( + returnValue: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -677,7 +713,7 @@ class MockTokenContainerRepository extends _i1.Mock [container], ), )), - returnValueForMissingStub: _i13.Future<_i6.TokenContainerState>.value( + returnValueForMissingStub: _i14.Future<_i6.TokenContainerState>.value( _FakeTokenContainerState_4( this, Invocation.method( @@ -685,17 +721,17 @@ class MockTokenContainerRepository extends _i1.Mock [container], ), )), - ) as _i13.Future<_i6.TokenContainerState>); + ) as _i14.Future<_i6.TokenContainerState>); } /// A class which mocks [TokenContainerApi]. /// /// See the documentation for Mockito's code generation for more information. -class MockTokenContainerApi extends _i1.Mock implements _i22.TokenContainerApi { +class MockTokenContainerApi extends _i1.Mock implements _i23.TokenContainerApi { @override - _i13.Future<_i7.Response> finalizeContainer( - _i21.TokenContainerUnfinalized? container, - _i23.EccUtils? eccUtils, + _i14.Future<_i7.ContainerFinalizationResponse> finalizeContainer( + _i22.TokenContainerUnfinalized? container, + _i24.EccUtils? eccUtils, ) => (super.noSuchMethod( Invocation.method( @@ -705,7 +741,8 @@ class MockTokenContainerApi extends _i1.Mock implements _i22.TokenContainerApi { eccUtils, ], ), - returnValue: _i13.Future<_i7.Response>.value(_FakeResponse_5( + returnValue: _i14.Future<_i7.ContainerFinalizationResponse>.value( + _FakeContainerFinalizationResponse_5( this, Invocation.method( #finalizeContainer, @@ -716,7 +753,8 @@ class MockTokenContainerApi extends _i1.Mock implements _i22.TokenContainerApi { ), )), returnValueForMissingStub: - _i13.Future<_i7.Response>.value(_FakeResponse_5( + _i14.Future<_i7.ContainerFinalizationResponse>.value( + _FakeContainerFinalizationResponse_5( this, Invocation.method( #finalizeContainer, @@ -726,36 +764,37 @@ class MockTokenContainerApi extends _i1.Mock implements _i22.TokenContainerApi { ], ), )), - ) as _i13.Future<_i7.Response>); + ) as _i14.Future<_i7.ContainerFinalizationResponse>); @override - _i13.Future getTransferQrData( - _i21.TokenContainerFinalized? container) => + _i14.Future<_i7.TransferQrData> getRolloverQrData( + _i22.TokenContainerFinalized? container) => (super.noSuchMethod( Invocation.method( - #getTransferQrData, + #getRolloverQrData, [container], ), - returnValue: _i13.Future.value(_i24.dummyValue( + returnValue: + _i14.Future<_i7.TransferQrData>.value(_FakeTransferQrData_6( this, Invocation.method( - #getTransferQrData, + #getRolloverQrData, [container], ), )), returnValueForMissingStub: - _i13.Future.value(_i24.dummyValue( + _i14.Future<_i7.TransferQrData>.value(_FakeTransferQrData_6( this, Invocation.method( - #getTransferQrData, + #getRolloverQrData, [container], ), )), - ) as _i13.Future); + ) as _i14.Future<_i7.TransferQrData>); @override - _i13.Future<_i22.ContainerSyncUpdates?> sync( - _i21.TokenContainerFinalized? container, + _i14.Future<_i23.ContainerSyncUpdates?> sync( + _i22.TokenContainerFinalized? container, _i25.TokenState? tokenState, ) => (super.noSuchMethod( @@ -766,30 +805,46 @@ class MockTokenContainerApi extends _i1.Mock implements _i22.TokenContainerApi { tokenState, ], ), - returnValue: _i13.Future<_i22.ContainerSyncUpdates?>.value(), + returnValue: _i14.Future<_i23.ContainerSyncUpdates?>.value(), returnValueForMissingStub: - _i13.Future<_i22.ContainerSyncUpdates?>.value(), - ) as _i13.Future<_i22.ContainerSyncUpdates?>); + _i14.Future<_i23.ContainerSyncUpdates?>.value(), + ) as _i14.Future<_i23.ContainerSyncUpdates?>); @override - _i13.Future unregister(_i21.TokenContainerFinalized? container) => + _i14.Future<_i7.UnregisterContainerResult> unregister( + _i22.TokenContainerFinalized? container) => (super.noSuchMethod( Invocation.method( #unregister, [container], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future<_i7.UnregisterContainerResult>.value( + _FakeUnregisterContainerResult_7( + this, + Invocation.method( + #unregister, + [container], + ), + )), + returnValueForMissingStub: + _i14.Future<_i7.UnregisterContainerResult>.value( + _FakeUnregisterContainerResult_7( + this, + Invocation.method( + #unregister, + [container], + ), + )), + ) as _i14.Future<_i7.UnregisterContainerResult>); } /// A class which mocks [PrivacyideaIOClient]. /// /// See the documentation for Mockito's code generation for more information. class MockPrivacyideaIOClient extends _i1.Mock - implements _i10.PrivacyideaIOClient { + implements _i11.PrivacyideaIOClient { @override - _i13.Future triggerNetworkAccessPermission({ + _i14.Future triggerNetworkAccessPermission({ required Uri? url, bool? sslVerify = true, bool? isRetry = false, @@ -804,12 +859,12 @@ class MockPrivacyideaIOClient extends _i1.Mock #isRetry: isRetry, }, ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future<_i7.Response> doPost({ + _i14.Future<_i8.Response> doPost({ required Uri? url, required Map? body, bool? sslVerify = true, @@ -824,7 +879,7 @@ class MockPrivacyideaIOClient extends _i1.Mock #sslVerify: sslVerify, }, ), - returnValue: _i13.Future<_i7.Response>.value(_FakeResponse_5( + returnValue: _i14.Future<_i8.Response>.value(_FakeResponse_8( this, Invocation.method( #doPost, @@ -837,7 +892,7 @@ class MockPrivacyideaIOClient extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i7.Response>.value(_FakeResponse_5( + _i14.Future<_i8.Response>.value(_FakeResponse_8( this, Invocation.method( #doPost, @@ -849,10 +904,10 @@ class MockPrivacyideaIOClient extends _i1.Mock }, ), )), - ) as _i13.Future<_i7.Response>); + ) as _i14.Future<_i8.Response>); @override - _i13.Future<_i7.Response> doGet({ + _i14.Future<_i8.Response> doGet({ required Uri? url, required Map? parameters, bool? sslVerify = true, @@ -867,7 +922,7 @@ class MockPrivacyideaIOClient extends _i1.Mock #sslVerify: sslVerify, }, ), - returnValue: _i13.Future<_i7.Response>.value(_FakeResponse_5( + returnValue: _i14.Future<_i8.Response>.value(_FakeResponse_8( this, Invocation.method( #doGet, @@ -880,7 +935,7 @@ class MockPrivacyideaIOClient extends _i1.Mock ), )), returnValueForMissingStub: - _i13.Future<_i7.Response>.value(_FakeResponse_5( + _i14.Future<_i8.Response>.value(_FakeResponse_8( this, Invocation.method( #doGet, @@ -892,51 +947,51 @@ class MockPrivacyideaIOClient extends _i1.Mock }, ), )), - ) as _i13.Future<_i7.Response>); + ) as _i14.Future<_i8.Response>); } /// A class which mocks [RsaUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { +class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { @override - _i8.RSAPublicKey deserializeRSAPublicKeyPKCS1(String? keyStr) => + _i9.RSAPublicKey deserializeRSAPublicKeyPKCS1(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPublicKeyPKCS1, [keyStr], ), - returnValue: _FakeRSAPublicKey_6( + returnValue: _FakeRSAPublicKey_9( this, Invocation.method( #deserializeRSAPublicKeyPKCS1, [keyStr], ), ), - returnValueForMissingStub: _FakeRSAPublicKey_6( + returnValueForMissingStub: _FakeRSAPublicKey_9( this, Invocation.method( #deserializeRSAPublicKeyPKCS1, [keyStr], ), ), - ) as _i8.RSAPublicKey); + ) as _i9.RSAPublicKey); @override - String serializeRSAPublicKeyPKCS1(_i8.RSAPublicKey? publicKey) => + String serializeRSAPublicKeyPKCS1(_i9.RSAPublicKey? publicKey) => (super.noSuchMethod( Invocation.method( #serializeRSAPublicKeyPKCS1, [publicKey], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS1, [publicKey], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS1, @@ -946,43 +1001,43 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { ) as String); @override - _i8.RSAPublicKey deserializeRSAPublicKeyPKCS8(String? keyStr) => + _i9.RSAPublicKey deserializeRSAPublicKeyPKCS8(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPublicKeyPKCS8, [keyStr], ), - returnValue: _FakeRSAPublicKey_6( + returnValue: _FakeRSAPublicKey_9( this, Invocation.method( #deserializeRSAPublicKeyPKCS8, [keyStr], ), ), - returnValueForMissingStub: _FakeRSAPublicKey_6( + returnValueForMissingStub: _FakeRSAPublicKey_9( this, Invocation.method( #deserializeRSAPublicKeyPKCS8, [keyStr], ), ), - ) as _i8.RSAPublicKey); + ) as _i9.RSAPublicKey); @override - String serializeRSAPublicKeyPKCS8(_i8.RSAPublicKey? key) => + String serializeRSAPublicKeyPKCS8(_i9.RSAPublicKey? key) => (super.noSuchMethod( Invocation.method( #serializeRSAPublicKeyPKCS8, [key], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS8, [key], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS8, @@ -992,20 +1047,20 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { ) as String); @override - String serializeRSAPrivateKeyPKCS1(_i8.RSAPrivateKey? key) => + String serializeRSAPrivateKeyPKCS1(_i9.RSAPrivateKey? key) => (super.noSuchMethod( Invocation.method( #serializeRSAPrivateKeyPKCS1, [key], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #serializeRSAPrivateKeyPKCS1, [key], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #serializeRSAPrivateKeyPKCS1, @@ -1015,33 +1070,33 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { ) as String); @override - _i8.RSAPrivateKey deserializeRSAPrivateKeyPKCS1(String? keyStr) => + _i9.RSAPrivateKey deserializeRSAPrivateKeyPKCS1(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPrivateKeyPKCS1, [keyStr], ), - returnValue: _FakeRSAPrivateKey_7( + returnValue: _FakeRSAPrivateKey_10( this, Invocation.method( #deserializeRSAPrivateKeyPKCS1, [keyStr], ), ), - returnValueForMissingStub: _FakeRSAPrivateKey_7( + returnValueForMissingStub: _FakeRSAPrivateKey_10( this, Invocation.method( #deserializeRSAPrivateKeyPKCS1, [keyStr], ), ), - ) as _i8.RSAPrivateKey); + ) as _i9.RSAPrivateKey); @override bool verifyRSASignature( - _i8.RSAPublicKey? publicKey, - _i26.Uint8List? signedMessage, - _i26.Uint8List? signature, + _i9.RSAPublicKey? publicKey, + _i27.Uint8List? signedMessage, + _i27.Uint8List? signature, ) => (super.noSuchMethod( Invocation.method( @@ -1057,8 +1112,8 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { ) as bool); @override - _i13.Future trySignWithToken( - _i27.PushToken? token, + _i14.Future trySignWithToken( + _i28.PushToken? token, String? message, ) => (super.noSuchMethod( @@ -1069,44 +1124,44 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { message, ], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future<_i8.AsymmetricKeyPair<_i8.RSAPublicKey, _i8.RSAPrivateKey>> + _i14.Future<_i9.AsymmetricKeyPair<_i9.RSAPublicKey, _i9.RSAPrivateKey>> generateRSAKeyPair() => (super.noSuchMethod( Invocation.method( #generateRSAKeyPair, [], ), - returnValue: _i13.Future< - _i8.AsymmetricKeyPair<_i8.RSAPublicKey, - _i8.RSAPrivateKey>>.value( - _FakeAsymmetricKeyPair_8<_i8.RSAPublicKey, _i8.RSAPrivateKey>( + returnValue: _i14.Future< + _i9.AsymmetricKeyPair<_i9.RSAPublicKey, + _i9.RSAPrivateKey>>.value( + _FakeAsymmetricKeyPair_11<_i9.RSAPublicKey, _i9.RSAPrivateKey>( this, Invocation.method( #generateRSAKeyPair, [], ), )), - returnValueForMissingStub: _i13.Future< - _i8.AsymmetricKeyPair<_i8.RSAPublicKey, - _i8.RSAPrivateKey>>.value( - _FakeAsymmetricKeyPair_8<_i8.RSAPublicKey, _i8.RSAPrivateKey>( + returnValueForMissingStub: _i14.Future< + _i9.AsymmetricKeyPair<_i9.RSAPublicKey, + _i9.RSAPrivateKey>>.value( + _FakeAsymmetricKeyPair_11<_i9.RSAPublicKey, _i9.RSAPrivateKey>( this, Invocation.method( #generateRSAKeyPair, [], ), )), - ) as _i13.Future< - _i8.AsymmetricKeyPair<_i8.RSAPublicKey, _i8.RSAPrivateKey>>); + ) as _i14.Future< + _i9.AsymmetricKeyPair<_i9.RSAPublicKey, _i9.RSAPrivateKey>>); @override String createBase32Signature( - _i8.RSAPrivateKey? privateKey, - _i26.Uint8List? dataToSign, + _i9.RSAPrivateKey? privateKey, + _i27.Uint8List? dataToSign, ) => (super.noSuchMethod( Invocation.method( @@ -1116,7 +1171,7 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { dataToSign, ], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #createBase32Signature, @@ -1126,7 +1181,7 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { ], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #createBase32Signature, @@ -1139,9 +1194,9 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { ) as String); @override - _i26.Uint8List createRSASignature( - _i8.RSAPrivateKey? privateKey, - _i26.Uint8List? dataToSign, + _i27.Uint8List createRSASignature( + _i9.RSAPrivateKey? privateKey, + _i27.Uint8List? dataToSign, ) => (super.noSuchMethod( Invocation.method( @@ -1151,30 +1206,30 @@ class MockRsaUtils extends _i1.Mock implements _i11.RsaUtils { dataToSign, ], ), - returnValue: _i26.Uint8List(0), - returnValueForMissingStub: _i26.Uint8List(0), - ) as _i26.Uint8List); + returnValue: _i27.Uint8List(0), + returnValueForMissingStub: _i27.Uint8List(0), + ) as _i27.Uint8List); } /// A class which mocks [EccUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockEccUtils extends _i1.Mock implements _i23.EccUtils { +class MockEccUtils extends _i1.Mock implements _i24.EccUtils { @override - String serializeECPublicKey(_i8.ECPublicKey? publicKey) => + String serializeECPublicKey(_i9.ECPublicKey? publicKey) => (super.noSuchMethod( Invocation.method( #serializeECPublicKey, [publicKey], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #serializeECPublicKey, [publicKey], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #serializeECPublicKey, @@ -1184,43 +1239,43 @@ class MockEccUtils extends _i1.Mock implements _i23.EccUtils { ) as String); @override - _i8.ECPublicKey deserializeECPublicKey(String? ecPublicKey) => + _i9.ECPublicKey deserializeECPublicKey(String? ecPublicKey) => (super.noSuchMethod( Invocation.method( #deserializeECPublicKey, [ecPublicKey], ), - returnValue: _FakeECPublicKey_9( + returnValue: _FakeECPublicKey_12( this, Invocation.method( #deserializeECPublicKey, [ecPublicKey], ), ), - returnValueForMissingStub: _FakeECPublicKey_9( + returnValueForMissingStub: _FakeECPublicKey_12( this, Invocation.method( #deserializeECPublicKey, [ecPublicKey], ), ), - ) as _i8.ECPublicKey); + ) as _i9.ECPublicKey); @override - String serializeECPrivateKey(_i8.ECPrivateKey? ecPrivateKey) => + String serializeECPrivateKey(_i9.ECPrivateKey? ecPrivateKey) => (super.noSuchMethod( Invocation.method( #serializeECPrivateKey, [ecPrivateKey], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #serializeECPrivateKey, [ecPrivateKey], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #serializeECPrivateKey, @@ -1230,31 +1285,31 @@ class MockEccUtils extends _i1.Mock implements _i23.EccUtils { ) as String); @override - _i8.ECPrivateKey deserializeECPrivateKey(String? ecPrivateKey) => + _i9.ECPrivateKey deserializeECPrivateKey(String? ecPrivateKey) => (super.noSuchMethod( Invocation.method( #deserializeECPrivateKey, [ecPrivateKey], ), - returnValue: _FakeECPrivateKey_10( + returnValue: _FakeECPrivateKey_13( this, Invocation.method( #deserializeECPrivateKey, [ecPrivateKey], ), ), - returnValueForMissingStub: _FakeECPrivateKey_10( + returnValueForMissingStub: _FakeECPrivateKey_13( this, Invocation.method( #deserializeECPrivateKey, [ecPrivateKey], ), ), - ) as _i8.ECPrivateKey); + ) as _i9.ECPrivateKey); @override String signWithPrivateKey( - _i8.ECPrivateKey? privateKey, + _i9.ECPrivateKey? privateKey, String? message, ) => (super.noSuchMethod( @@ -1265,7 +1320,7 @@ class MockEccUtils extends _i1.Mock implements _i23.EccUtils { message, ], ), - returnValue: _i24.dummyValue( + returnValue: _i26.dummyValue( this, Invocation.method( #signWithPrivateKey, @@ -1275,7 +1330,7 @@ class MockEccUtils extends _i1.Mock implements _i23.EccUtils { ], ), ), - returnValueForMissingStub: _i24.dummyValue( + returnValueForMissingStub: _i26.dummyValue( this, Invocation.method( #signWithPrivateKey, @@ -1288,15 +1343,15 @@ class MockEccUtils extends _i1.Mock implements _i23.EccUtils { ) as String); @override - _i8.AsymmetricKeyPair<_i8.ECPublicKey, _i8.ECPrivateKey> generateKeyPair( - _i28.EcKeyAlgorithm? keyAlgorithm) => + _i9.AsymmetricKeyPair<_i9.ECPublicKey, _i9.ECPrivateKey> generateKeyPair( + _i29.EcKeyAlgorithm? keyAlgorithm) => (super.noSuchMethod( Invocation.method( #generateKeyPair, [keyAlgorithm], ), returnValue: - _FakeAsymmetricKeyPair_8<_i8.ECPublicKey, _i8.ECPrivateKey>( + _FakeAsymmetricKeyPair_11<_i9.ECPublicKey, _i9.ECPrivateKey>( this, Invocation.method( #generateKeyPair, @@ -1304,24 +1359,43 @@ class MockEccUtils extends _i1.Mock implements _i23.EccUtils { ), ), returnValueForMissingStub: - _FakeAsymmetricKeyPair_8<_i8.ECPublicKey, _i8.ECPrivateKey>( + _FakeAsymmetricKeyPair_11<_i9.ECPublicKey, _i9.ECPrivateKey>( this, Invocation.method( #generateKeyPair, [keyAlgorithm], ), ), - ) as _i8.AsymmetricKeyPair<_i8.ECPublicKey, _i8.ECPrivateKey>); + ) as _i9.AsymmetricKeyPair<_i9.ECPublicKey, _i9.ECPrivateKey>); + + @override + bool validateSignature( + _i9.ECPublicKey? publicKey, + String? signature, + String? message, + ) => + (super.noSuchMethod( + Invocation.method( + #validateSignature, + [ + publicKey, + signature, + message, + ], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); } /// A class which mocks [FirebaseUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockFirebaseUtils extends _i1.Mock implements _i9.FirebaseUtils { +class MockFirebaseUtils extends _i1.Mock implements _i10.FirebaseUtils { @override - _i13.Future initFirebase({ - required _i13.Future Function(_i29.RemoteMessage)? foregroundHandler, - required _i13.Future Function(_i29.RemoteMessage)? backgroundHandler, + _i14.Future initFirebase({ + required _i14.Future Function(_i30.RemoteMessage)? foregroundHandler, + required _i14.Future Function(_i30.RemoteMessage)? backgroundHandler, required dynamic Function(String?)? updateFirebaseToken, }) => (super.noSuchMethod( @@ -1334,74 +1408,74 @@ class MockFirebaseUtils extends _i1.Mock implements _i9.FirebaseUtils { #updateFirebaseToken: updateFirebaseToken, }, ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future getFBToken() => (super.noSuchMethod( + _i14.Future getFBToken() => (super.noSuchMethod( Invocation.method( #getFBToken, [], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future deleteFirebaseToken() => (super.noSuchMethod( + _i14.Future deleteFirebaseToken() => (super.noSuchMethod( Invocation.method( #deleteFirebaseToken, [], ), - returnValue: _i13.Future.value(false), - returnValueForMissingStub: _i13.Future.value(false), - ) as _i13.Future); + returnValue: _i14.Future.value(false), + returnValueForMissingStub: _i14.Future.value(false), + ) as _i14.Future); @override - _i13.Future setCurrentFirebaseToken(String? str) => (super.noSuchMethod( + _i14.Future setCurrentFirebaseToken(String? str) => (super.noSuchMethod( Invocation.method( #setCurrentFirebaseToken, [str], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future getCurrentFirebaseToken() => (super.noSuchMethod( + _i14.Future getCurrentFirebaseToken() => (super.noSuchMethod( Invocation.method( #getCurrentFirebaseToken, [], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future setNewFirebaseToken(String? str) => (super.noSuchMethod( + _i14.Future setNewFirebaseToken(String? str) => (super.noSuchMethod( Invocation.method( #setNewFirebaseToken, [str], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future getNewFirebaseToken() => (super.noSuchMethod( + _i14.Future getNewFirebaseToken() => (super.noSuchMethod( Invocation.method( #getNewFirebaseToken, [], ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future<_i30.FirebaseApp?> initializeApp({ + _i14.Future<_i31.FirebaseApp?> initializeApp({ required String? name, - required _i30.FirebaseOptions? options, + required _i31.FirebaseOptions? options, }) => (super.noSuchMethod( Invocation.method( @@ -1412,15 +1486,15 @@ class MockFirebaseUtils extends _i1.Mock implements _i9.FirebaseUtils { #options: options, }, ), - returnValue: _i13.Future<_i30.FirebaseApp?>.value(), - returnValueForMissingStub: _i13.Future<_i30.FirebaseApp?>.value(), - ) as _i13.Future<_i30.FirebaseApp?>); + returnValue: _i14.Future<_i31.FirebaseApp?>.value(), + returnValueForMissingStub: _i14.Future<_i31.FirebaseApp?>.value(), + ) as _i14.Future<_i31.FirebaseApp?>); } /// A class which mocks [PushProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockPushProvider extends _i1.Mock implements _i31.PushProvider { +class MockPushProvider extends _i1.Mock implements _i32.PushProvider { @override bool get pollingIsEnabled => (super.noSuchMethod( Invocation.getter(#pollingIsEnabled), @@ -1438,43 +1512,43 @@ class MockPushProvider extends _i1.Mock implements _i31.PushProvider { ); @override - _i9.FirebaseUtils get firebaseUtils => (super.noSuchMethod( + _i10.FirebaseUtils get firebaseUtils => (super.noSuchMethod( Invocation.getter(#firebaseUtils), - returnValue: _FakeFirebaseUtils_11( + returnValue: _FakeFirebaseUtils_14( this, Invocation.getter(#firebaseUtils), ), - returnValueForMissingStub: _FakeFirebaseUtils_11( + returnValueForMissingStub: _FakeFirebaseUtils_14( this, Invocation.getter(#firebaseUtils), ), - ) as _i9.FirebaseUtils); + ) as _i10.FirebaseUtils); @override - _i10.PrivacyideaIOClient get ioClient => (super.noSuchMethod( + _i11.PrivacyideaIOClient get ioClient => (super.noSuchMethod( Invocation.getter(#ioClient), - returnValue: _FakePrivacyideaIOClient_12( + returnValue: _FakePrivacyideaIOClient_15( this, Invocation.getter(#ioClient), ), - returnValueForMissingStub: _FakePrivacyideaIOClient_12( + returnValueForMissingStub: _FakePrivacyideaIOClient_15( this, Invocation.getter(#ioClient), ), - ) as _i10.PrivacyideaIOClient); + ) as _i11.PrivacyideaIOClient); @override - _i11.RsaUtils get rsaUtils => (super.noSuchMethod( + _i12.RsaUtils get rsaUtils => (super.noSuchMethod( Invocation.getter(#rsaUtils), - returnValue: _FakeRsaUtils_13( + returnValue: _FakeRsaUtils_16( this, Invocation.getter(#rsaUtils), ), - returnValueForMissingStub: _FakeRsaUtils_13( + returnValueForMissingStub: _FakeRsaUtils_16( this, Invocation.getter(#rsaUtils), ), - ) as _i11.RsaUtils); + ) as _i12.RsaUtils); @override void setPollingEnabled(bool? enablePolling) => super.noSuchMethod( @@ -1486,20 +1560,20 @@ class MockPushProvider extends _i1.Mock implements _i31.PushProvider { ); @override - _i13.Future pollForChallenges({required bool? isManually}) => + _i14.Future pollForChallenges({required bool? isManually}) => (super.noSuchMethod( Invocation.method( #pollForChallenges, [], {#isManually: isManually}, ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future pollForChallenge( - _i27.PushToken? token, { + _i14.Future pollForChallenge( + _i28.PushToken? token, { bool? isManually = true, }) => (super.noSuchMethod( @@ -1508,13 +1582,13 @@ class MockPushProvider extends _i1.Mock implements _i31.PushProvider { [token], {#isManually: isManually}, ), - returnValue: _i13.Future.value(), - returnValueForMissingStub: _i13.Future.value(), - ) as _i13.Future); + returnValue: _i14.Future.value(), + returnValueForMissingStub: _i14.Future.value(), + ) as _i14.Future); @override - _i13.Future< - (List<_i27.PushToken>, List<_i27.PushToken>)?> updateFirebaseToken( + _i14.Future< + (List<_i28.PushToken>, List<_i28.PushToken>)?> updateFirebaseToken( [String? firebaseToken]) => (super.noSuchMethod( Invocation.method( @@ -1522,13 +1596,13 @@ class MockPushProvider extends _i1.Mock implements _i31.PushProvider { [firebaseToken], ), returnValue: - _i13.Future<(List<_i27.PushToken>, List<_i27.PushToken>)?>.value(), + _i14.Future<(List<_i28.PushToken>, List<_i28.PushToken>)?>.value(), returnValueForMissingStub: - _i13.Future<(List<_i27.PushToken>, List<_i27.PushToken>)?>.value(), - ) as _i13.Future<(List<_i27.PushToken>, List<_i27.PushToken>)?>); + _i14.Future<(List<_i28.PushToken>, List<_i28.PushToken>)?>.value(), + ) as _i14.Future<(List<_i28.PushToken>, List<_i28.PushToken>)?>); @override - void unsubscribe(void Function(_i19.PushRequest)? newRequest) => + void unsubscribe(void Function(_i20.PushRequest)? newRequest) => super.noSuchMethod( Invocation.method( #unsubscribe, @@ -1538,7 +1612,7 @@ class MockPushProvider extends _i1.Mock implements _i31.PushProvider { ); @override - void subscribe(void Function(_i19.PushRequest)? newRequest) => + void subscribe(void Function(_i20.PushRequest)? newRequest) => super.noSuchMethod( Invocation.method( #subscribe, diff --git a/test/unit_test/api/privacy_idea_container_api_test.dart b/test/unit_test/api/privacy_idea_container_api_test.dart new file mode 100644 index 000000000..a039f9a1c --- /dev/null +++ b/test/unit_test/api/privacy_idea_container_api_test.dart @@ -0,0 +1,516 @@ +/* + * privacyIDEA Authenticator + * + * Author: Frank Merkel + * + * Copyright (c) 2024 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 'package:cryptography/cryptography.dart' as crypto; +import 'package:http/http.dart'; +import 'package:mockito/mockito.dart'; +import 'package:privacyidea_authenticator/api/impl/privacy_idea_container_api.dart'; +import 'package:privacyidea_authenticator/model/container_policies.dart'; +import 'package:privacyidea_authenticator/model/enums/algorithms.dart'; +import 'package:privacyidea_authenticator/model/enums/ec_key_algorithm.dart'; +import 'package:privacyidea_authenticator/model/enums/rollout_state.dart'; +import 'package:privacyidea_authenticator/model/enums/sync_state.dart'; +import 'package:privacyidea_authenticator/model/riverpod_states/token_state.dart'; +import 'package:privacyidea_authenticator/model/token_container.dart'; +import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart'; +import 'package:privacyidea_authenticator/utils/ecc_utils.dart'; +import 'package:privacyidea_authenticator/utils/logger.dart'; +import 'package:test/test.dart'; + +import '../../tests_app_wrapper.mocks.dart'; + +void main() { + _testPrivacyIdeaContainerApi(); +} + +void _testPrivacyIdeaContainerApi() { + final exampleError = { + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': false, + 'error': { + 'message': 'Error message', + 'code': 400, + }, + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }; + + final containerChallengeNonce = 'b33d3a11c8d1b45f19640035e27944ccf0b2383d'; + final containerChallengeTimeStamp = '2024-12-06T11:14:26.885409+00:00'; + final containerChallengeResponse = Response( + jsonEncode({ + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': true, + 'value': { + 'enc_key_algorithm': 'secp384r1', + 'nonce': 'b33d3a11c8d1b45f19640035e27944ccf0b2383d', + 'time_stamp': '2024-12-06T11:14:26.885409+00:00', + } + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }), + 200, + ); + + final publicServerKey = '-----BEGIN PUBLIC KEY-----\n' + 'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEr5dFYneCv+m3a8oRjp7vdDslT+VPiLVW\n' + 'apxWTv1vf/LLmwfRAcmwTzFHDrHS77yVU2Sa4h9UEicIbpkSZKla7EAQYosCsvl7\n' + '/3wxx9pCWKZm+dygmKARfmzVoZ2KsYgG\n' + '-----END PUBLIC KEY-----'; + final privateClientKey = '-----BEGIN EC PRIVATE KEY-----\n' + 'MIGkAgEBBDDK5sxOyduuSYwxwHFbAOcFZP2LHh/x5fi/Z6jENzXoKHEVWUNh3wqa\n' + '8Y5f7iNN7c6gBwYFK4EEACKhZANiAAR8ZnmJ78AaRXcLmKbXSwXqieo2Wr0vq6MV\n' + 'siFdT04cydzB51P6kMU7QuRjSVMI/2/NuP1pw8UWKMkuEo5Znmqs+A9Sva+jUL8o\n' + 'U2V+vfX0bMjKhlBLBhOtn6jcQWQ/B/s=\n' + '-----END EC PRIVATE KEY-----'; + + final publicClientKey = '-----BEGIN PUBLIC KEY-----\n' + 'MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEfGZ5ie/AGkV3C5im10sF6onqNlq9L6uj\n' + 'FbIhXU9OHMncwedT+pDFO0LkY0lTCP9vzbj9acPFFijJLhKOWZ5qrPgPUr2vo1C/\n' + 'KFNlfr319GzIyoZQSwYTrZ+o3EFkPwf7\n' + '-----END PUBLIC KEY-----'; + + TokenContainerUnfinalized getNewTokenContainer({ + String? withIssuer, + String? withNonce, + DateTime? withTimestamp, + Uri? withServerUrl, + String? withSerial, + EcKeyAlgorithm? withEcKeyAlgorithm, + Algorithms? withHashAlgorithm, + bool? withSslVerify, + String? withPassphraseQuestion, + String? withPublicServerKey, + String? withPublicClientKey, + String? withPrivateClientKey, + ContainerPolicies? withPolicies, + SyncState? withSyncState, + String? withServerName, + Duration? withTtl, + bool? addDeviceInfos, + FinalizationState? withFinalizationState, + }) => + TokenContainerUnfinalized( + issuer: withIssuer ?? 'privacyIDEA', + nonce: withNonce ?? 'b33d3a11c8d1b45f19640035e27944ccf0b2383d', + timestamp: withTimestamp ?? DateTime(2024, 12, 6, 11, 14, 26, 885, 409), + serverUrl: withServerUrl ?? Uri.parse('http://example.com'), + serial: withSerial ?? 'SMPH00067A2F', + ecKeyAlgorithm: withEcKeyAlgorithm ?? EcKeyAlgorithm.secp384r1, + hashAlgorithm: withHashAlgorithm ?? Algorithms.SHA256, + sslVerify: withSslVerify ?? false, + passphraseQuestion: withPassphraseQuestion, + publicServerKey: publicServerKey, + publicClientKey: publicClientKey, + privateClientKey: privateClientKey, + policies: withPolicies ?? + ContainerPolicies( + rolloverAllowed: true, + initialTokenTransfer: true, + tokensDeletable: true, + unregisterAllowed: true, + ), + serverName: withServerName ?? 'privacyIDEA', + ttl: withTtl ?? Duration(minutes: 10), + addDeviceInfos: addDeviceInfos ?? false, + finalizationState: withFinalizationState ?? FinalizationState.notStarted, + ); + + TokenContainerFinalized getFinalizedTokenContainer({ + String? withIssuer, + String? withNonce, + DateTime? withTimestamp, + Uri? withServerUrl, + String? withSerial, + EcKeyAlgorithm? withEcKeyAlgorithm, + Algorithms? withHashAlgorithm, + bool? withSslVerify, + String? withPassphraseQuestion, + String? withPublicServerKey, + String? withPublicClientKey, + String? withPrivateClientKey, + ContainerPolicies? withPolicies, + SyncState? withSyncState, + String? withServerName, + }) => + TokenContainerFinalized( + issuer: withIssuer ?? 'privacyIDEA', + nonce: withNonce ?? 'b33d3a11c8d1b45f19640035e27944ccf0b2383d', + timestamp: withTimestamp ?? DateTime(2024, 12, 6, 11, 14, 26, 885, 409), + serverUrl: withServerUrl ?? Uri.parse('http://example.com'), + serial: withSerial ?? 'SMPH00067A2F', + ecKeyAlgorithm: withEcKeyAlgorithm ?? EcKeyAlgorithm.secp384r1, + hashAlgorithm: withHashAlgorithm ?? Algorithms.SHA256, + sslVerify: withSslVerify ?? false, + passphraseQuestion: withPassphraseQuestion, + publicServerKey: publicServerKey, + publicClientKey: publicClientKey, + privateClientKey: privateClientKey, + policies: withPolicies ?? + ContainerPolicies( + rolloverAllowed: true, + initialTokenTransfer: true, + tokensDeletable: true, + unregisterAllowed: true, + ), + syncState: withSyncState ?? SyncState.completed, + serverName: withServerName ?? 'privacyIDEA', + ); + group('PrivacyIdeaContainerApi', () { + test('finalizeContainer', () async { + final tokenContainer = getNewTokenContainer(); + + final message = '${tokenContainer.nonce}' + '|${tokenContainer.timestamp.toIso8601String().replaceFirst('Z', '+00:00')}' + '|${tokenContainer.serial}' + '|${tokenContainer.registrationUrl}'; + + final EccUtils eccUtils = EccUtils(); + final signature = eccUtils.signWithPrivateKey(tokenContainer.ecPrivateClientKey!, message); + final body = { + 'container_serial': tokenContainer.serial, + 'public_client_key': tokenContainer.publicClientKey, + 'signature': signature, + }; + + // Arrange + final mockIoClient = MockPrivacyideaIOClient(); + final containerApi = PiContainerApi(ioClient: mockIoClient); + when(mockIoClient.doPost(url: anyNamed('url'), body: anyNamed('body'), sslVerify: anyNamed('sslVerify'))).thenAnswer((invocation) async { + final invocationUrl = invocation.namedArguments[Symbol('url')]; + final invocationBody = invocation.namedArguments[Symbol('body')]; + Logger.info('Body: $invocationBody'); + if (invocationUrl.toString() == tokenContainer.registrationUrl.toString() && + invocationBody['container_serial'] == body['container_serial'] && + invocationBody['public_client_key'] == body['public_client_key'] && + eccUtils.validateSignature(tokenContainer.ecPublicClientKey!, invocationBody['signature'], message)) { + final exampleSuccess = { + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': true, + 'value': { + 'public_server_key': publicServerKey, + 'policies': { + 'container_client_rollover': false, + 'container_initial_token_transfer': false, + 'client_token_deletable': true, + 'client_container_unregister': true, + }, + }, + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }; + + return Response(jsonEncode(exampleSuccess), 200); + } + + final exampleError = { + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': false, + 'error': { + 'message': 'Error message', + 'code': 400, + }, + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }; + + return Response(jsonEncode(exampleError), 400); + }); + // Act + final data = await containerApi.finalizeContainer(tokenContainer, EccUtils()); + final policies = data.policies; + final responsePublicServerKey = data.publicServerKey; + // Assert + expect(policies, isA()); + expect(policies.rolloverAllowed, false); + expect(policies.initialTokenTransfer, false); + expect(policies.tokensDeletable, true); + expect(policies.unregisterAllowed, true); + expect(eccUtils.serializeECPublicKey(responsePublicServerKey), publicServerKey); + }); + test('getRolloverQrData', () async { + // Arrange + final qrCodeDescription = "This should be the data for the QR code"; + final qrCodeDataValue = "qrCodeDataValue"; + final mockIoClient = MockPrivacyideaIOClient(); + final containerApi = PiContainerApi(ioClient: mockIoClient); + final tokenContainer = getFinalizedTokenContainer(); + when(mockIoClient.doPost(url: anyNamed('url'), body: anyNamed('body'), sslVerify: anyNamed('sslVerify'))).thenAnswer((invocation) async { + final Uri invocationUrl = invocation.namedArguments[Symbol('url')]; + final Map invocationBody = invocation.namedArguments[Symbol('body')]; + Logger.info('Body: $invocationBody'); + + if (invocationUrl.toString() == 'http://example.com/container/${tokenContainer.serial}/challenge' && + invocationBody['scope'] == 'http://example.com/container/${tokenContainer.serial}/rollover') { + return containerChallengeResponse; + } + + final signMessage = '$containerChallengeNonce|$containerChallengeTimeStamp|${tokenContainer.serial}|$invocationUrl'; + if (invocationUrl.toString() == 'http://example.com/container/${tokenContainer.serial}/rollover' && + invocationBody['scope'] == 'http://example.com/container/${tokenContainer.serial}/rollover' && + EccUtils().validateSignature(tokenContainer.ecPublicClientKey!, invocationBody['signature']!, signMessage)) { + return Response( + jsonEncode({ + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': true, + 'value': { + 'container_url': { + 'description': qrCodeDescription, + 'value': qrCodeDataValue, + } + } + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }), + 200, + ); + } + + return Response(jsonEncode(exampleError), 400); + }); + // // Act + final responseTransferQrData = await containerApi.getRolloverQrData(tokenContainer); + // // Assert + expect(responseTransferQrData.description, qrCodeDescription); + expect(responseTransferQrData.value, qrCodeDataValue); + }); + test('unregister', () async { + // Arrange + final mockIoClient = MockPrivacyideaIOClient(); + final containerApi = PiContainerApi(ioClient: mockIoClient); + final tokenContainer = getFinalizedTokenContainer(); + + when(mockIoClient.doPost(url: anyNamed('url'), body: anyNamed('body'), sslVerify: anyNamed('sslVerify'))).thenAnswer((invocation) async { + final Uri invocationUrl = invocation.namedArguments[Symbol('url')]; + final Map invocationBody = invocation.namedArguments[Symbol('body')]; + Logger.info('Body: $invocationBody'); + if (invocationUrl.toString() == 'http://example.com/container/${tokenContainer.serial}/challenge' && + invocationBody['scope'] == 'http://example.com/container/register/${tokenContainer.serial}/terminate/client') { + return containerChallengeResponse; + } + final signMessage = '$containerChallengeNonce|$containerChallengeTimeStamp|${tokenContainer.serial}|$invocationUrl'; + if (invocationUrl.toString() == 'http://example.com/container/register/${tokenContainer.serial}/terminate/client' && + invocationBody['scope'] == 'http://example.com/container/register/${tokenContainer.serial}/terminate/client' && + EccUtils().validateSignature(tokenContainer.ecPublicClientKey!, invocationBody['signature']!, signMessage)) { + return Response( + jsonEncode({ + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': true, + 'value': { + 'success': true, + } + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }), + 200, + ); + } + return Response(jsonEncode(exampleError), 400); + }); + + // Act + final result = await containerApi.unregister(tokenContainer); + // Assert + expect(result.success, true); + }); + + test('sync', () async { + // Arrange + final mockIoClient = MockPrivacyideaIOClient(); + final containerApi = PiContainerApi(ioClient: mockIoClient); + final tokenContainer = getFinalizedTokenContainer(); + final tokenState = TokenState( + tokens: [ + HOTPToken(label: "label1", issuer: "privacyIDEA", counter: 5, id: 'id1', algorithm: Algorithms.SHA1, digits: 6, secret: 'AAAAAAAA'), + ], + ); + when(mockIoClient.doPost(url: anyNamed('url'), body: anyNamed('body'), sslVerify: anyNamed('sslVerify'))).thenAnswer((invocation) async { + final Uri invocationUrl = invocation.namedArguments[Symbol('url')]; + final Map invocationBody = invocation.namedArguments[Symbol('body')]; + Logger.info('Body: $invocationBody'); + if (invocationUrl.toString() == 'http://example.com/container/${tokenContainer.serial}/challenge' && + invocationBody['scope'] == 'http://example.com/container/${tokenContainer.serial}/sync') { + return containerChallengeResponse; + } + final publicEncKeyClientB64 = invocationBody['public_enc_key_client']; + final containerDictClient = + '{"serial":"SMPH00067A2F","type":"smartphone","tokens":[{"type":"HOTP","label":"label1","issuer":"privacyIDEA","pin":"False","algorithm":"SHA1","digits":"6","otp":["435986","964213"],"counter":"5"}]}'; + final signMessage = + '$containerChallengeNonce|$containerChallengeTimeStamp|${tokenContainer.serial}|$invocationUrl|$publicEncKeyClientB64|$containerDictClient}'; + final publicEncKeyClientuint8list = base64.decode(publicEncKeyClientB64!); + + // final encKeyPair = await X25519().newKeyPair(); + final crypto.SimplePublicKey publicEncKeyClient = crypto.SimplePublicKey(publicEncKeyClientuint8list, type: crypto.KeyPairType.x25519); + final signature = invocationBody['signature']!; + final isVeryfied = await crypto.Ed25519().verify( + utf8.encode(signMessage), + signature: crypto.Signature(base64.decode(signature), publicKey: publicEncKeyClient), + ); + if (invocationUrl.toString() == 'http://example.com/container/${tokenContainer.serial}/sync' && + invocationBody['container_dict_client'] == containerDictClient && + isVeryfied) { + return Response( + jsonEncode({ + 'id': 5, + 'jsonrpc': '2.0', + 'result': { + 'status': true, + 'value': { + 'success': true, + } + }, + 'time': 1.0, + 'version': 'privacyIDEA 3.6.2', + 'versionnumber': '3.6.2', + 'detail': null, + 'signature': 'signature', + }), + 200, + ); + } + return Response(jsonEncode(exampleError), 400); + }); + + // Act + final result = await containerApi.sync(tokenContainer, tokenState); + // Assert + expect(result, isNull); + }); + }); + group('Unallowed', () { + test('rollover', () {}); + test('finalizeContainer', () { + // Arrange + final containerApi = PiContainerApi(ioClient: MockPrivacyideaIOClient()); + final tokenContainer = getNewTokenContainer( + withPolicies: ContainerPolicies( + rolloverAllowed: true, + initialTokenTransfer: true, + tokensDeletable: true, + unregisterAllowed: false, + ), + ); + // Act & Assert + expect(() => containerApi.finalizeContainer(tokenContainer), throwsA(isA())); + }); + test('rollover', () { + // Arrange + final containerApi = PiContainerApi(ioClient: MockPrivacyideaIOClient()); + final tokenContainer = getFinalizedTokenContainer( + withPolicies: ContainerPolicies( + rolloverAllowed: false, + initialTokenTransfer: true, + tokensDeletable: true, + unregisterAllowed: true, + ), + ); + // Act & Assert + expect(() => containerApi.getRolloverQrData(tokenContainer), throwsA(isA())); + }); + + test('unregister', () { + // Arrange + final containerApi = PiContainerApi(ioClient: MockPrivacyideaIOClient()); + final tokenContainer = getFinalizedTokenContainer( + withPolicies: ContainerPolicies( + rolloverAllowed: true, + initialTokenTransfer: true, + tokensDeletable: true, + unregisterAllowed: false, + ), + ); + // Act & Assert + expect(() => containerApi.unregister(tokenContainer), throwsA(isA())); + }); + }); +} + // when(mockIoClient.doPost(url: anyNamed('url'), body: anyNamed('body'), sslVerify: anyNamed('sslVerify'))).thenAnswer((invocation) async { + // final Uri invocationUrl = invocation.namedArguments[Symbol('url')]; + // final Map invocationBody = invocation.namedArguments[Symbol('body')]; + // Logger.info('Body: $invocationBody'); + // if (invocationUrl.toString() == 'http://example.com/container/${tokenContainer.serial}/challenge' && + // invocationBody['scope'] == 'http://example.com/container/register/${tokenContainer.serial}/terminate/client') { + // return containerChallengeResponse; + // } + // final signMessage = '$containerChallengeNonce|$containerChallengeTimeStamp|${tokenContainer.serial}|$invocationUrl'; + // if (invocationUrl.toString() == 'http://example.com/container/register/${tokenContainer.serial}/terminate/client' && + // invocationBody['scope'] == 'http://example.com/container/register/${tokenContainer.serial}/terminate/client' && + // EccUtils().validateSignature(tokenContainer.ecPublicClientKey!, invocationBody['signature']!, signMessage)) { + // return Response( + // jsonEncode({ + // 'id': 5, + // 'jsonrpc': '2.0', + // 'result': { + // 'status': true, + // 'value': { + // 'success': true, + // } + // }, + // 'time': 1.0, + // 'version': 'privacyIDEA 3.6.2', + // 'versionnumber': '3.6.2', + // 'detail': null, + // 'signature': 'signature', + // }), + // 200, + // ); + // } + // return Response(jsonEncode(exampleError), 400); + // }); + + // Act \ No newline at end of file diff --git a/test/unit_test/model/processor_result_test.dart b/test/unit_test/model/processor_result_test.dart index c38abdbcd..8631bd1f2 100644 --- a/test/unit_test/model/processor_result_test.dart +++ b/test/unit_test/model/processor_result_test.dart @@ -13,7 +13,7 @@ void _testProcessorResult() { expect(result.resultData, 'data'); }); test('error', () { - const result = ProcessorResultFailed('error'); + final result = ProcessorResultFailed((_) => 'error'); expect(result, isA()); expect(result.message, 'error'); }); @@ -25,7 +25,7 @@ void _testProcessorResult() { expect((result as ProcessorResultSuccess).resultData, 'data'); }); test('error', () { - final ProcessorResult result = ProcessorResult.failed('error'); + final ProcessorResult result = ProcessorResult.failed((_) => 'error'); expect(result, isA()); expect((result as ProcessorResultFailed).message, 'error'); }); @@ -33,12 +33,12 @@ void _testProcessorResult() { group('is', () { test('success', () { - const ProcessorResult result = ProcessorResultSuccess('data'); + final ProcessorResult result = ProcessorResultSuccess('data'); expect(result.isSuccess, isTrue); expect(result.isFailed, isFalse); }); test('error', () { - const ProcessorResult result = ProcessorResultFailed('error'); + final ProcessorResult result = ProcessorResultFailed((_) => 'error'); expect(result.isSuccess, isFalse); expect(result.isFailed, isTrue); }); @@ -51,7 +51,7 @@ void _testProcessorResult() { expect(() => result.asFailed, throwsA(isA())); }); test('error', () { - const ProcessorResult result = ProcessorResultFailed('error'); + final ProcessorResult result = ProcessorResultFailed((_) => 'error'); expect(result.asFailed, 'error'); expect(() => result.asSuccess, throwsA(isA())); }); diff --git a/test/unit_test/processors/scheme_processors/token_container_scheme_processor_test.dart b/test/unit_test/processors/scheme_processors/token_container_scheme_processor_test.dart index 9bbb0e0de..f11191ef0 100644 --- a/test/unit_test/processors/scheme_processors/token_container_scheme_processor_test.dart +++ b/test/unit_test/processors/scheme_processors/token_container_scheme_processor_test.dart @@ -24,14 +24,13 @@ import 'package:privacyidea_authenticator/model/enums/ec_key_algorithm.dart'; import 'package:privacyidea_authenticator/model/token_container.dart'; import 'package:privacyidea_authenticator/processors/scheme_processors/token_container_processor.dart'; -final processor = TokenContainerProcessor(); - void main() { _testTokenContainerProcessor(); } void _testTokenContainerProcessor() { - test('TokenContainerProcessor', () { + final processor = TokenContainerProcessor(); + group('TokenContainerProcessor', () { group('processUri', () { test('valid uri', () async { final uriString = "pia://container/SMPH00067A2F" @@ -44,7 +43,7 @@ void _testTokenContainerProcessor() { "&key_algorithm=secp384r1" "&hash_algorithm=SHA256" "&ssl_verify=False" - "&passphrase=Enter%20your%20password"; + "&passphrase="; final uri = Uri.parse(uriString); final result = await processor.processUri(uri); expect(result?.length, 1); @@ -63,6 +62,50 @@ void _testTokenContainerProcessor() { expect(container.sslVerify, false); expect(container.passphraseQuestion, ""); }); + test('other values', () async { + final uriString = "pia://container/SMPH00067A2F2" + "?issuer=privacyIDEA2" + "&ttl=100" + "&nonce=b33d3a11c8d1b45f19640035e27944ccf0b2383d22" + "&time=2024-12-07T11%3A14%3A26.885409%2B00%3A00" + "&url=http://192.168.0.231:5000/" + "&serial=SMPH00067A2F2" + "&key_algorithm=secp112r1" + "&hash_algorithm=SHA1" + "&ssl_verify=True" + "&passphrase=Enter%20your%20password"; + final uri = Uri.parse(uriString); + final result = await processor.processUri(uri); + expect(result?.length, 1); + expect(result![0].isSuccess, true); + expect(result[0].asSuccess, isNotNull); + final container = result[0].asSuccess!.resultData; + expect(container, isA()); + expect(container.issuer, "privacyIDEA2"); + expect((container as TokenContainerUnfinalized).ttl, Duration(minutes: 100)); + expect(container.nonce, "b33d3a11c8d1b45f19640035e27944ccf0b2383d22"); + expect(container.timestamp, DateTime.parse("2024-12-07T11:14:26.885409+00:00")); + expect(container.serverUrl, Uri.parse("http://192.168.0.231:5000/")); + expect(container.serial, "SMPH00067A2F2"); + expect(container.ecKeyAlgorithm, EcKeyAlgorithm.secp112r1); + expect(container.hashAlgorithm, Algorithms.SHA1); + expect(container.sslVerify, true); + expect(container.passphraseQuestion, "Enter your password"); + }); + test('missing nonce', () { + final uriString = "pia://container/SMPH00067A2F2" + "?issuer=privacyIDEA2" + "&ttl=100" + "&time=2024-12-07T11%3A14%3A26.885409%2B00%3A00" + "&url=http://192.168.0.231:5000/" + "&serial=SMPH00067A2F2" + "&key_algorithm=secp112r1" + "&hash_algorithm=SHA1" + "&ssl_verify=True" + "&passphrase=Enter%20your%20password"; + final uri = Uri.parse(uriString); + expect(processor.processUri(uri), completion(isNull)); + }); }); }); } diff --git a/test/unit_test/processors/scheme_processors/token_import_scheme_processors/free_otp_plus_qr_processor_test.dart b/test/unit_test/processors/scheme_processors/token_import_scheme_processors/free_otp_plus_qr_processor_test.dart index 50ee1677d..1d1769a8e 100644 --- a/test/unit_test/processors/scheme_processors/token_import_scheme_processors/free_otp_plus_qr_processor_test.dart +++ b/test/unit_test/processors/scheme_processors/token_import_scheme_processors/free_otp_plus_qr_processor_test.dart @@ -1,4 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import 'package:privacyidea_authenticator/model/processor_result.dart'; import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart'; import 'package:privacyidea_authenticator/model/tokens/token.dart'; @@ -36,7 +37,7 @@ void _testFreeOtpPlusQrProcessor() { expect(results.length, equals(1)); expect(results.first, isA>()); final firstResult = results.first as ProcessorResultFailed; - expect(firstResult.message.isNotEmpty, equals(true)); + expect(firstResult.message(AppLocalizationsEn()).isNotEmpty, equals(true)); }); test('processUri without counter', () async { // Arrange @@ -47,7 +48,7 @@ void _testFreeOtpPlusQrProcessor() { expect(results.length, equals(1)); final result0 = results[0]; expect(result0, isA>()); - final message = result0.asFailed!.message; + final message = result0.asFailed!.message(AppLocalizationsEn()); final error = result0.asFailed!.error; expect(message.toLowerCase().contains(HOTPToken.COUNTER) || error.toString().toLowerCase().contains(HOTPToken.COUNTER), isTrue); }); diff --git a/test/unit_test/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor_test.dart b/test/unit_test/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor_test.dart index 757dfb891..00beb24a9 100644 --- a/test/unit_test/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor_test.dart +++ b/test/unit_test/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor_test.dart @@ -1,4 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import 'package:privacyidea_authenticator/model/processor_result.dart'; import 'package:privacyidea_authenticator/model/tokens/day_password_token.dart'; import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart'; @@ -122,7 +123,7 @@ void _testOtpAuthProcessor() { expect(results.length, equals(1)); final result0 = results[0]; expect(result0, isA()); - final message = result0.asFailed!.message; + final message = result0.asFailed!.message(AppLocalizationsEn()); final error = result0.asFailed!.error; expect(message.toLowerCase().contains('secret') || error.toString().toLowerCase().contains('secret'), isTrue); }); @@ -236,7 +237,7 @@ void _testOtpAuthProcessor() { expect(results.length, equals(1)); final result0 = results[0]; expect(result0, isA()); - final message = result0.asFailed!.message; + final message = result0.asFailed!.message(AppLocalizationsEn()); final error = result0.asFailed!.error; expect(message.toLowerCase().contains(HOTPToken.COUNTER) || error.toString().toLowerCase().contains(HOTPToken.COUNTER), isTrue); }); @@ -251,7 +252,7 @@ void _testOtpAuthProcessor() { expect(results.length, equals(1)); final result0 = results[0]; expect(result0, isA()); - final message = result0.asFailed!.message; + final message = result0.asFailed!.message(AppLocalizationsEn()); final error = result0.asFailed!.error; expect(message.toLowerCase().contains(OTPToken.SECRET_BASE32) || error.toString().toLowerCase().contains(OTPToken.SECRET_BASE32), isTrue); }); @@ -406,7 +407,7 @@ void _testOtpAuthProcessor() { expect(results.length, equals(1)); final result0 = results[0]; expect(result0, isA()); - final message = result0.asFailed!.message; + final message = result0.asFailed!.message(AppLocalizationsEn()); final error = result0.asFailed!.error; expect(message.toLowerCase().contains('secret') || error.toString().toLowerCase().contains('secret'), isTrue); }); diff --git a/test/unit_test/state_notifiers/token_container_notifier_test.dart b/test/unit_test/state_notifiers/token_container_notifier_test.dart index fa402ec73..e0a55ad10 100644 --- a/test/unit_test/state_notifiers/token_container_notifier_test.dart +++ b/test/unit_test/state_notifiers/token_container_notifier_test.dart @@ -1,11 +1,9 @@ -import 'dart:convert'; - import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart'; import 'package:mockito/mockito.dart'; import 'package:privacyidea_authenticator/api/interfaces/container_api.dart'; +import 'package:privacyidea_authenticator/model/api_results/pi_server_results/pi_server_result_value.dart'; import 'package:privacyidea_authenticator/model/container_policies.dart'; import 'package:privacyidea_authenticator/model/enums/algorithms.dart'; import 'package:privacyidea_authenticator/model/enums/ec_key_algorithm.dart'; @@ -62,7 +60,7 @@ TokenContainerState _buildFinalizedContainerState() => TokenContainerState( publicServerKey: 'publicServerKey', publicClientKey: 'publicClientKey', privateClientKey: 'privateClientKey', - finalizationState: RolloutState.completed, + finalizationState: FinalizationState.completed, syncState: SyncState.notStarted, passphraseQuestion: null, policies: ContainerPolicies( @@ -76,13 +74,29 @@ TokenContainerState _buildFinalizedContainerState() => TokenContainerState( ); void _testTokenContainerNotifier() { + final publicServerKeyExample = "-----BEGIN PUBLIC KEY-----\n" + "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEaik84am1122p15/8Z6FYW+q5DYe22RV0\n" + "6jimjkz/5J6vDkafcP1oq5fRgIEXTTU6uKarvSrOxcV8nNuW6o80L55iT1ZmZJ+q\n" + "tx/ncmaloOEFY4dMh1XNs0TayAxKrCNg\n" + "-----END PUBLIC KEY-----"; + final ecPublicServerKeyExample = EccUtils().deserializeECPublicKey(publicServerKeyExample); + + final ContainerFinalizationResponse containerFinalizationResponseExample = ContainerFinalizationResponse( + publicServerKey: ecPublicServerKeyExample, + policies: ContainerPolicies( + rolloverAllowed: false, + initialTokenTransfer: false, + tokensDeletable: false, + unregisterAllowed: false, + ), + ); group('Token Container Notifier Test', () { test('load state from repo on creation', () async { final container = ProviderContainer(); var containerRepoState = _buildUnfinalizedContainerState(); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainer(any)).thenAnswer((invocation) { final container = invocation.positionalArguments[0] as TokenContainer; @@ -112,7 +126,7 @@ void _testTokenContainerNotifier() { var containerRepoState = _buildUnfinalizedContainerState(); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainer(any)).thenAnswer((invocation) { final container = invocation.positionalArguments[0] as TokenContainer; @@ -162,7 +176,7 @@ void _testTokenContainerNotifier() { var containerRepoState = _buildUnfinalizedContainerState(); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainer(any)).thenAnswer((invocation) { final container = invocation.positionalArguments[0] as TokenContainer; @@ -228,7 +242,7 @@ void _testTokenContainerNotifier() { var containerRepoState = _buildUnfinalizedContainerState(); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainer(any)).thenAnswer((invocation) { final container = invocation.positionalArguments[0] as TokenContainer; @@ -285,7 +299,7 @@ void _testTokenContainerNotifier() { ); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainer(any)).thenAnswer((invocation) { final container = invocation.positionalArguments[0] as TokenContainer; @@ -337,7 +351,7 @@ void _testTokenContainerNotifier() { var containerRepoState = _buildUnfinalizedContainerState(); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainer(any)).thenAnswer((invocation) { final container = invocation.positionalArguments[0] as TokenContainer; @@ -410,7 +424,7 @@ void _testTokenContainerNotifier() { ); final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => Response('{}', 404)); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async => containerFinalizationResponseExample); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainerState(any)).thenAnswer((invocation) { containerRepoState = invocation.positionalArguments[0] as TokenContainerState; @@ -452,36 +466,42 @@ void _testTokenContainerNotifier() { var containerRepoState = TokenContainerState(containerList: []); final mockContainerRepo = MockTokenContainerRepository(); final mockContainerApi = MockTokenContainerApi(); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer( (_) async { - final json = { - "id": 1, - "jsonrpc": "2.0", - "result": { - "status": true, - "value": { - "policies": { - "client_container_unregister": true, - "client_token_deletable": true, - "container_client_rollover": true, - "container_initial_token_transfer": true - }, - "public_server_key": "-----BEGIN PUBLIC KEY-----\n" - "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEaik84am1122p15/8Z6FYW+q5DYe22RV0\n" - "6jimjkz/5J6vDkafcP1oq5fRgIEXTTU6uKarvSrOxcV8nNuW6o80L55iT1ZmZJ+q\n" - "tx/ncmaloOEFY4dMh1XNs0TayAxKrCNg\n" - "-----END PUBLIC KEY-----", - } - }, - "time": 1731941387.8622696, - "version": "privacyIDEA 3.10", - "versionnumber": "3.10", - "signature": - "rsa_sha256_pss:5d71b5a47b9330cdb26f090b8197fdf76df34919636cc54acf476185e1282beba9a8a800dbcfd834e0068a7d3f653e2193c9b0d2c80b44801d4f85d18c0a7af554d1d9659600a5570106a595687c5131b20a4793e0992dd11c138990c47e959aa391845010f4c85dbeb35aded5a57e85d20f544d79e35b06c3231323e50c2699af6758aeeb6aafb3c9e507c7c2c0d23230e5ec09b7c26b535cd43f51368b3edc1cf4148125f8b92263c7a52eaa0def49db1a6347edc12aa9151ede67a5ab114a72b860ebdd7d9c8b48851e981fa8582a4865389bc14eea26143d36b891278be097bf841bf75697cdb16ce1bba15dcb9ffe9d4717b6f8e8780040fe078f55f7d0" - }; - return Response( - jsonEncode(json), - 200, + // final json = { + // "id": 1, + // "jsonrpc": "2.0", + // "result": { + // "status": true, + // "value": { + // "policies": { + // "client_container_unregister": true, + // "client_token_deletable": true, + // "container_client_rollover": true, + // "container_initial_token_transfer": true + // }, + // "public_server_key": "-----BEGIN PUBLIC KEY-----\n" + // "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEaik84am1122p15/8Z6FYW+q5DYe22RV0\n" + // "6jimjkz/5J6vDkafcP1oq5fRgIEXTTU6uKarvSrOxcV8nNuW6o80L55iT1ZmZJ+q\n" + // "tx/ncmaloOEFY4dMh1XNs0TayAxKrCNg\n" + // "-----END PUBLIC KEY-----", + // } + // }, + // "time": 1731941387.8622696, + // "version": "privacyIDEA 3.10", + // "versionnumber": "3.10", + // "signature": + // "rsa_sha256_pss:5d71b5a47b9330cdb26f090b8197fdf76df34919636cc54acf476185e1282beba9a8a800dbcfd834e0068a7d3f653e2193c9b0d2c80b44801d4f85d18c0a7af554d1d9659600a5570106a595687c5131b20a4793e0992dd11c138990c47e959aa391845010f4c85dbeb35aded5a57e85d20f544d79e35b06c3231323e50c2699af6758aeeb6aafb3c9e507c7c2c0d23230e5ec09b7c26b535cd43f51368b3edc1cf4148125f8b92263c7a52eaa0def49db1a6347edc12aa9151ede67a5ab114a72b860ebdd7d9c8b48851e981fa8582a4865389bc14eea26143d36b891278be097bf841bf75697cdb16ce1bba15dcb9ffe9d4717b6f8e8780040fe078f55f7d0" + // }; + return ContainerFinalizationResponse( + policies: ContainerPolicies( + unregisterAllowed: true, + tokensDeletable: true, + rolloverAllowed: true, + initialTokenTransfer: true, + ), + publicServerKey: EccUtils().deserializeECPublicKey(publicServerKeyExample), ); }, ); @@ -502,6 +522,9 @@ void _testTokenContainerNotifier() { containerRepoState = TokenContainerState(containerList: newList); return Future.value(containerRepoState); }); + when(mockContainerRepo.deleteContainer(any)).thenAnswer((invocation) { + return Future.value(containerRepoState); + }); final Uri uri = Uri.parse( 'pia://container/SMPH00067A2F?' @@ -560,7 +583,7 @@ void _testTokenContainerNotifier() { serial: "SMPH00067A2F", ecKeyAlgorithm: EcKeyAlgorithm.secp384r1, hashAlgorithm: Algorithms.SHA256, - finalizationState: RolloutState.completed, + finalizationState: FinalizationState.completed, syncState: SyncState.notStarted, passphraseQuestion: "", sslVerify: true, @@ -593,39 +616,37 @@ void _testTokenContainerNotifier() { var containerRepoState = _buildUnfinalizedContainerState(); final mockContainerRepo = MockTokenContainerRepository(); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.finalizeContainer(any, any)).thenAnswer( - (_) async { - final json = { - "id": 1, - "jsonrpc": "2.0", - "result": { - "status": true, - "value": { - "policies": { - "client_container_unregister": true, - "client_token_deletable": true, - "container_client_rollover": true, - "container_initial_token_transfer": true - }, - "public_server_key": "-----BEGIN PUBLIC KEY-----\n" - "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEaik84am1122p15/8Z6FYW+q5DYe22RV0\n" - "6jimjkz/5J6vDkafcP1oq5fRgIEXTTU6uKarvSrOxcV8nNuW6o80L55iT1ZmZJ+q\n" - "tx/ncmaloOEFY4dMh1XNs0TayAxKrCNg\n" - "-----END PUBLIC KEY-----", - } - }, - "time": 1731941387.8622696, - "version": "privacyIDEA 3.10", - "versionnumber": "3.10", - "signature": - "rsa_sha256_pss:5d71b5a47b9330cdb26f090b8197fdf76df34919636cc54acf476185e1282beba9a8a800dbcfd834e0068a7d3f653e2193c9b0d2c80b44801d4f85d18c0a7af554d1d9659600a5570106a595687c5131b20a4793e0992dd11c138990c47e959aa391845010f4c85dbeb35aded5a57e85d20f544d79e35b06c3231323e50c2699af6758aeeb6aafb3c9e507c7c2c0d23230e5ec09b7c26b535cd43f51368b3edc1cf4148125f8b92263c7a52eaa0def49db1a6347edc12aa9151ede67a5ab114a72b860ebdd7d9c8b48851e981fa8582a4865389bc14eea26143d36b891278be097bf841bf75697cdb16ce1bba15dcb9ffe9d4717b6f8e8780040fe078f55f7d0" - }; - return Response( - jsonEncode(json), - 200, - ); - }, - ); + when(mockContainerApi.finalizeContainer(any, any)).thenAnswer((_) async { + // final json = { + // "id": 1, + // "jsonrpc": "2.0", + // "result": { + // "status": true, + // "value": { + // "policies": { + // "client_container_unregister": true, + // "client_token_deletable": true, + // "container_client_rollover": true, + // "container_initial_token_transfer": true + // }, + // "public_server_key": "-----BEGIN PUBLIC KEY-----\n" + // "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEaik84am1122p15/8Z6FYW+q5DYe22RV0\n" + // "6jimjkz/5J6vDkafcP1oq5fRgIEXTTU6uKarvSrOxcV8nNuW6o80L55iT1ZmZJ+q\n" + // "tx/ncmaloOEFY4dMh1XNs0TayAxKrCNg\n" + // "-----END PUBLIC KEY-----", + // } + // }, + // "time": 1731941387.8622696, + // "version": "privacyIDEA 3.10", + // "versionnumber": "3.10", + // "signature": + // "rsa_sha256_pss:5d71b5a47b9330cdb26f090b8197fdf76df34919636cc54acf476185e1282beba9a8a800dbcfd834e0068a7d3f653e2193c9b0d2c80b44801d4f85d18c0a7af554d1d9659600a5570106a595687c5131b20a4793e0992dd11c138990c47e959aa391845010f4c85dbeb35aded5a57e85d20f544d79e35b06c3231323e50c2699af6758aeeb6aafb3c9e507c7c2c0d23230e5ec09b7c26b535cd43f51368b3edc1cf4148125f8b92263c7a52eaa0def49db1a6347edc12aa9151ede67a5ab114a72b860ebdd7d9c8b48851e981fa8582a4865389bc14eea26143d36b891278be097bf841bf75697cdb16ce1bba15dcb9ffe9d4717b6f8e8780040fe078f55f7d0" + // }; + return ContainerFinalizationResponse( + policies: ContainerPolicies(initialTokenTransfer: true, rolloverAllowed: true, tokensDeletable: true, unregisterAllowed: true), + publicServerKey: EccUtils().deserializeECPublicKey(publicServerKeyExample), + ); + }); when(mockContainerRepo.loadContainerState()).thenAnswer((_) => Future.value(containerRepoState)); when(mockContainerRepo.saveContainerState(any)).thenAnswer((invocation) { containerRepoState = invocation.positionalArguments[0] as TokenContainerState; @@ -679,7 +700,7 @@ void _testTokenContainerNotifier() { serial: "serial", ecKeyAlgorithm: EcKeyAlgorithm.secp521r1, hashAlgorithm: Algorithms.SHA512, - finalizationState: RolloutState.completed, + finalizationState: FinalizationState.completed, syncState: SyncState.notStarted, passphraseQuestion: null, sslVerify: true, @@ -837,14 +858,17 @@ void _testTokenContainerNotifier() { expect(tokenState.tokens.length, 3); expect(tokenState.tokens, unorderedEquals(expectedStateUnordered.tokens)); }); - test('getTransferQrData', () async { + test('getRolloverQrData', () async { // prepare final providerContainer = ProviderContainer(); var containerRepoState = _buildFinalizedContainerState(); final qrDataContainer = containerRepoState.containerList.first as TokenContainerFinalized; final mockContainerRepo = _setupMockContainerRepo(() => containerRepoState, (state) => containerRepoState = state); final mockContainerApi = MockTokenContainerApi(); - when(mockContainerApi.getTransferQrData(any)).thenAnswer((_) async => 'Some Random Data to be transferred'); + when(mockContainerApi.getRolloverQrData(any)).thenAnswer((_) async => TransferQrData( + description: 'Some Random Data to be transferred', + value: 'Some Random Data to be transferred', + )); final tokenContainerProvider = tokenContainerNotifierProviderOf( repo: mockContainerRepo, containerApi: mockContainerApi, @@ -854,10 +878,10 @@ void _testTokenContainerNotifier() { // act // TODO: implement test - final qrData = await providerContainer.read(tokenContainerProvider.notifier).getTransferQrData(qrDataContainer); + final qrData = await providerContainer.read(tokenContainerProvider.notifier).getRolloverQrData(qrDataContainer); // assert - verify(mockContainerApi.getTransferQrData(any)).called(1); + verify(mockContainerApi.getRolloverQrData(any)).called(1); expect(qrData, 'Some Random Data to be transferred'); }); });