diff --git a/core/lib/data/network/download/download_client.dart b/core/lib/data/network/download/download_client.dart index 8686739f1c..14d53cb361 100644 --- a/core/lib/data/network/download/download_client.dart +++ b/core/lib/data/network/download/download_client.dart @@ -19,11 +19,11 @@ class DownloadClient { Future downloadFile( String url, - String basicAuth, + String authenticationHeader, CancelToken? cancelToken, ) async { final headerParam = _dioClient.getHeaders(); - headerParam[HttpHeaders.authorizationHeader] = basicAuth; + headerParam[HttpHeaders.authorizationHeader] = authenticationHeader; headerParam[HttpHeaders.acceptHeader] = DioClient.jmapHeader; final responseBody = await _dioClient.get( diff --git a/core/lib/data/network/download/download_manager.dart b/core/lib/data/network/download/download_manager.dart index 9de12ed21d..b5bce9e20d 100644 --- a/core/lib/data/network/download/download_manager.dart +++ b/core/lib/data/network/download/download_manager.dart @@ -23,14 +23,14 @@ class DownloadManager { String downloadUrl, Future directoryToSave, String filename, - String basicAuth, + String authenticationHeader, {CancelToken? cancelToken} ) async { final streamController = StreamController(); try { await Future.wait([ - _downloadClient.downloadFile(downloadUrl, basicAuth, cancelToken), + _downloadClient.downloadFile(downloadUrl, authenticationHeader, cancelToken), directoryToSave ]).then((values) { final response = (values[0] as ResponseBody); diff --git a/core/lib/domain/exceptions/file_exception.dart b/core/lib/domain/exceptions/file_exception.dart new file mode 100644 index 0000000000..16e8980a86 --- /dev/null +++ b/core/lib/domain/exceptions/file_exception.dart @@ -0,0 +1,21 @@ +import 'package:equatable/equatable.dart'; + +abstract class FileException with EquatableMixin implements Exception { + final String message; + + FileException(this.message); + + @override + String toString() => message; + + @override + List get props => [message]; +} + +class NotFoundFileInFolderException extends FileException { + NotFoundFileInFolderException() : super('No files found in the folder'); +} + +class UserCancelShareFileException extends FileException { + UserCancelShareFileException() : super('User cancel share file'); +} \ No newline at end of file diff --git a/core/lib/presentation/views/loading/cupertino_loading_widget.dart b/core/lib/presentation/views/loading/cupertino_loading_widget.dart index 36544a955b..10ab0a7349 100644 --- a/core/lib/presentation/views/loading/cupertino_loading_widget.dart +++ b/core/lib/presentation/views/loading/cupertino_loading_widget.dart @@ -6,12 +6,14 @@ class CupertinoLoadingWidget extends StatelessWidget { final double? size; final EdgeInsetsGeometry? padding; final bool isCenter; + final Color? progressColor; const CupertinoLoadingWidget({ super.key, this.size, this.padding, this.isCenter = true, + this.progressColor, }); @override @@ -21,8 +23,8 @@ class CupertinoLoadingWidget extends StatelessWidget { child: SizedBox( width: size ?? CupertinoLoadingWidgetStyles.size, height: size ?? CupertinoLoadingWidgetStyles.size, - child: const CupertinoActivityIndicator( - color: CupertinoLoadingWidgetStyles.progressColor + child: CupertinoActivityIndicator( + color: progressColor ?? CupertinoLoadingWidgetStyles.progressColor ) ) ) @@ -31,8 +33,8 @@ class CupertinoLoadingWidget extends StatelessWidget { child: SizedBox( width: size ?? CupertinoLoadingWidgetStyles.size, height: size ?? CupertinoLoadingWidgetStyles.size, - child: const CupertinoActivityIndicator( - color: CupertinoLoadingWidgetStyles.progressColor + child: CupertinoActivityIndicator( + color: progressColor ?? CupertinoLoadingWidgetStyles.progressColor ) ), ); diff --git a/core/lib/utils/app_logger.dart b/core/lib/utils/app_logger.dart index 7664a65b18..35ca521c8d 100644 --- a/core/lib/utils/app_logger.dart +++ b/core/lib/utils/app_logger.dart @@ -1,13 +1,14 @@ import 'dart:async'; +import 'package:core/utils/log_tracking.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; final logHistory = _Dispatcher(''); -void log(String? value, {Level level = Level.info}) { - if (!kDebugMode) return; +Future log(String? value, {Level level = Level.info}) async { + if (!kDebugMode && !LogTracking().enableTraceLog) return; String logsStr = value ?? ''; logHistory.value = '$logsStr\n${logHistory.value}'; @@ -41,11 +42,16 @@ void log(String? value, {Level level = Level.info}) { break; } } + // ignore: avoid_print print('[TwakeMail] $logsStr'); + + if (LogTracking().enableTraceLog) { + await LogTracking().addLog(message: logsStr); + } } -void logError(String? value) => log(value, level: Level.error); +Future logError(String? value) => log(value, level: Level.error); // Take from: https://flutter.dev/docs/testing/errors void initLogger(VoidCallback runApp) { diff --git a/core/lib/utils/file_utils.dart b/core/lib/utils/file_utils.dart index f513fec900..c40184520a 100644 --- a/core/lib/utils/file_utils.dart +++ b/core/lib/utils/file_utils.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:core/domain/exceptions/download_file_exception.dart'; +import 'package:core/domain/exceptions/file_exception.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/services.dart'; @@ -68,6 +69,18 @@ class FileUtils { } } + Future deleteFileByFolderName({required String nameFile, String? folderPath}) async { + final internalStorageDirPath = await _getInternalStorageDirPath( + nameFile: nameFile, + folderPath: folderPath); + + final file = File(internalStorageDirPath); + + if (await file.exists()) { + await file.delete(); + } + } + Future getContentFromFile({ required String nameFile, String? folderPath, @@ -115,10 +128,56 @@ class FileUtils { } } + static Future removeFolderPath(String path) async { + final dir = Directory(path); + if (await dir.exists()) { + await dir.delete(recursive: true); + } + } + Future convertImageAssetToBase64(String assetImage) async { ByteData bytes = await rootBundle.load(assetImage); final buffer = bytes.buffer; final base64Data = base64Encode(Uint8List.view(buffer)); return base64Data; } + + static Future getExternalDocumentPath({String? folderPath}) async { + Directory directory = Directory(''); + if (Platform.isAndroid) { + if (folderPath?.isNotEmpty == true) { + directory = Directory('/storage/emulated/0/Download/$folderPath'); + } else { + directory = Directory('/storage/emulated/0/Download'); + } + } else { + directory = await getApplicationDocumentsDirectory(); + if (folderPath?.isNotEmpty == true) { + directory = Directory('${directory.absolute.path}/$folderPath'); + } + } + + final exPath = directory.path; + await Directory(exPath).create(recursive: true); + return exPath; + } + + static Future copyInternalFilesToDownloadExternal(List listFilePaths) async { + final externalPath = await getExternalDocumentPath(); + + List externalListPaths = []; + for (var filePath in listFilePaths) { + final file = File(filePath); + final fileName = filePath.substring(filePath.lastIndexOf('/') + 1); + final externalFile = File('$externalPath/$fileName'); + await externalFile.writeAsBytes(file.readAsBytesSync()); + externalListPaths.add(externalFile.path); + } + + if (externalListPaths.isNotEmpty) { + return externalPath; + } else { + throw NotFoundFileInFolderException(); + } + } } \ No newline at end of file diff --git a/core/lib/utils/log_tracking.dart b/core/lib/utils/log_tracking.dart new file mode 100644 index 0000000000..cefca6e185 --- /dev/null +++ b/core/lib/utils/log_tracking.dart @@ -0,0 +1,198 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:io'; + +import 'package:core/domain/exceptions/download_file_exception.dart'; +import 'package:core/utils/file_utils.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:dartz/dartz.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; +import 'package:path_provider/path_provider.dart'; + +class LogTracking { + static const String logFolder = 'TraceLogs'; + static const String logFileNameDatePattern = 'yyyy-MM-dd'; + static const String logMessageDatePattern = 'yyyy-MM-dd, HH:mm:ss'; + + LogTracking._(); + + factory LogTracking() => _instance ??= LogTracking._(); + + static LogTracking? _instance; + + bool enableTraceLog = PlatformInfo.isMobile; + + final Queue _messagesQueue = Queue(); + bool _isScheduled = false; + + Future addLog({required String message}) async { + _messagesQueue.add(message); + + if (!_isScheduled) { + _isScheduled = true; + await _executeTraceLog(); + } + } + + Future _executeTraceLog() async { + while (true) { + try { + if (_messagesQueue.isEmpty) { + _isScheduled = false; + return; + } + + final message = _messagesQueue.removeFirst(); + await saveLog(message: message); + } catch (_) {} + } + } + + Future saveLog({required String message}) async { + try { + final currentDate = DateTime.timestamp(); + + final logFileName = generateLogFileName(currentDate: currentDate); + + final messageSanitized = sanitizeMessage( + message: message, + currentDate: currentDate); + + await saveToFile( + nameFile: logFileName, + folderPath: logFolder, + content: messageSanitized + ); + } catch (_) {} + } + + String sanitizeMessage({ + required String message, + required DateTime currentDate + }) { + final dateFormat = getDateFormatAsString( + pattern: logMessageDatePattern, + currentDate: currentDate); + + return '($dateFormat): $message \n'; + } + + String getDateFormatAsString({ + required String pattern, + required DateTime currentDate + }) { + final dateFormat = DateFormat(pattern); + return dateFormat.format(currentDate); + } + + String generateLogFileName({required DateTime currentDate}) { + final dateFormat = getDateFormatAsString( + pattern: logFileNameDatePattern, + currentDate: currentDate); + + return '${dateFormat}_log'; + } + + Future _getInternalStorageDirPath({ + String? nameFile, + String? folderPath, + String? extensionFile + }) async { + if (PlatformInfo.isMobile) { + String fileDirectory = (await getApplicationDocumentsDirectory()).absolute.path; + + if (folderPath != null) { + fileDirectory = '$fileDirectory/$folderPath'; + } + + Directory directory = Directory(fileDirectory); + + if (!await directory.exists()) { + await directory.create(recursive: true); + } + + if (nameFile != null) { + fileDirectory = '$fileDirectory/$nameFile'; + } + + if (extensionFile != null) { + fileDirectory = '$fileDirectory.$extensionFile'; + } + + return fileDirectory; + } else { + throw DeviceNotSupportedException(); + } + } + + Future saveToFile({ + required String nameFile, + required String content, + String? folderPath, + String? extensionFile, + FileMode fileMode = FileMode.append, + }) async { + final internalStorageDirPath = await _getInternalStorageDirPath( + nameFile: nameFile, + folderPath: folderPath, + extensionFile: extensionFile); + + final file = File(internalStorageDirPath); + + return await file.writeAsString(content, mode: fileMode); + } + + Future getTraceLog() async { + final folderPath = await _getInternalStorageDirPath(folderPath: logFolder); + final directory = Directory(folderPath); + if (directory.existsSync()) { + final directoryInfo = await getDirInfo(directory); + return TraceLog( + path: folderPath, + size: directoryInfo.value1, + listFilePaths: directoryInfo.value2); + } else { + throw Exception('Trace folder not exist'); + } + } + + Future>> getDirInfo(Directory dir) async { + var files = await dir.list(recursive: true).toList(); + var dirSize = files.fold(0, (int sum, file) => sum + file.statSync().size); + var listPath = files.map((file) => file.path).toList(); + return Tuple2(dirSize, listPath); + } + + Future exportTraceLog(TraceLog traceLog) async { + if (PlatformInfo.isIOS) { + final savePath = FileUtils.getExternalDocumentPath(folderPath: logFolder); + return savePath; + } else { + final savePath = await compute( + FileUtils.copyInternalFilesToDownloadExternal, + traceLog.listFilePaths); + return savePath; + } + } + + Future deleteTraceLog(String path) async { + return await FileUtils.removeFolderPath(path); + } +} + +class TraceLog with EquatableMixin { + final String path; + final int size; + final List listFilePaths; + + TraceLog({ + required this.path, + required this.size, + required this.listFilePaths + }); + + @override + List get props => [path, size, listFilePaths]; +} \ No newline at end of file diff --git a/core/test/utils/log_tracking_test.dart b/core/test/utils/log_tracking_test.dart new file mode 100644 index 0000000000..1dc69b1791 --- /dev/null +++ b/core/test/utils/log_tracking_test.dart @@ -0,0 +1,86 @@ +@TestOn('vm') + +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/file_utils.dart'; +import 'package:core/utils/log_tracking.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; + +import 'file_utils_test.dart'; + +void main() { + final fileUtils = FileUtils(); + + group('log_tracking_test', () { + setUp(() async { + PathProviderPlatform.instance = FakePathProviderPlatform(); + }); + + test('app_logger::log:test', () async { + final fileName = LogTracking().generateLogFileName(currentDate: DateTime.timestamp()); + + await fileUtils.deleteFileByFolderName( + nameFile: fileName, + folderPath: LogTracking.logFolder); + + await Future.wait([ + log('hello 1'), + log('hello 2'), + log('hello 3'), + log('hello 4'), + ]); + + await log('hello 9'); + + await Future.wait([ + log('hello 5'), + log('hello 6'), + log('hello 7'), + log('hello 8'), + ]); + + final content = await fileUtils.getContentFromFile( + nameFile: fileName, + folderPath: LogTracking.logFolder); + + final listMessage= content + .split('\n') + .map((message) => message.trim()) + .where((message) => message.isNotEmpty); + + expect(listMessage.length, equals(9)); + expect(listMessage.first, contains('hello 1')); + expect(listMessage.last, contains('hello 8')); + }); + + test('app_logger::logError:test', () async { + final fileName = LogTracking().generateLogFileName(currentDate: DateTime.timestamp()); + + await fileUtils.deleteFileByFolderName( + nameFile: fileName, + folderPath: LogTracking.logFolder); + + await Future.wait([ + logError('Error 1'), + logError('Error 2'), + logError('Error 3'), + logError('Error 4'), + ]); + + await logError('Error 5'); + + final content = await fileUtils.getContentFromFile( + nameFile: fileName, + folderPath: LogTracking.logFolder); + + final listMessage= content + .split('\n') + .map((message) => message.trim()) + .where((message) => message.isNotEmpty); + + expect(listMessage.length, equals(5)); + expect(listMessage.first, contains('Error 1')); + expect(listMessage.last, contains('Error 5')); + }); + }); +} \ No newline at end of file diff --git a/lib/features/base/base_controller.dart b/lib/features/base/base_controller.dart index e44350eedf..d4f0c4d7ac 100644 --- a/lib/features/base/base_controller.dart +++ b/lib/features/base/base_controller.dart @@ -9,11 +9,9 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/views/toast/tmail_toast.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/application_manager.dart'; -import 'package:core/utils/fps_manager.dart'; import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart'; import 'package:fcm/model/firebase_capability.dart'; -import 'package:fcm/model/firebase_registration_id.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:forward/forward/capability_forward.dart'; @@ -29,22 +27,20 @@ import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixi import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/state/logout_current_account_basic_auth_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/logout_current_account_oidc_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/logout_current_account_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_interactor.dart'; import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/login/presentation/model/login_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/contact_autocomplete_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/tmail_autocomplete_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/log_out_oidc_state.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/email_rules/bindings/email_rules_interactor_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/forward/bindings/forwarding_interactors_bindings.dart'; import 'package:tmail_ui_user/features/push_notification/domain/exceptions/fcm_exception.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/destroy_firebase_registration_state.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/state/get_stored_firebase_registration_state.dart'; import 'package:tmail_ui_user/features/push_notification/domain/usecases/destroy_firebase_registration_interactor.dart'; -import 'package:tmail_ui_user/features/push_notification/domain/usecases/get_stored_firebase_registration_interactor.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/config/fcm_configuration.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/controller/fcm_message_controller.dart'; @@ -71,9 +67,7 @@ abstract class BaseController extends GetxController final AuthorizationInterceptors authorizationInterceptors = Get.find(); final AuthorizationInterceptors authorizationIsolateInterceptors = Get.find(tag: BindingTag.isolateTag); final DynamicUrlInterceptors dynamicUrlInterceptors = Get.find(); - final DeleteCredentialInteractor deleteCredentialInteractor = Get.find(); - final LogoutOidcInteractor logoutOidcInteractor = Get.find(); - final DeleteAuthorityOidcInteractor deleteAuthorityOidcInteractor = Get.find(); + final LogoutCurrentAccountInteractor _logoutCurrentAccountInteractor = Get.find(); final AppToast appToast = Get.find(); final ImagePaths imagePaths = Get.find(); final ResponsiveUtils responsiveUtils = Get.find(); @@ -82,11 +76,9 @@ abstract class BaseController extends GetxController bool _isFcmEnabled = false; - GetStoredFirebaseRegistrationInteractor? _getStoredFirebaseRegistrationInteractor; DestroyFirebaseRegistrationInteractor? _destroyFirebaseRegistrationInteractor; final viewState = Rx>(Right(UIState.idle)); - FpsCallback? fpsCallback; void consumeState(Stream> newStateStream) async { newStateStream.listen(onData, onError: onError, onDone: onDone); @@ -184,50 +176,33 @@ abstract class BaseController extends GetxController void handleFailureViewState(Failure failure) async { logError('BaseController::handleFailureViewState(): ${failure.runtimeType}'); - if (failure is LogoutOidcFailure) { + if (failure is LogoutCurrentAccountOidcFailure || + failure is LogoutCurrentAccountBasicAuthFailure || + failure is LogoutCurrentAccountFailure) { if (_isFcmEnabled) { - _getStoredFirebaseRegistrationFromCache(); + await _removeFirebaseRegistration(); } else { await clearDataAndGoToLoginPage(); } - } else if (failure is GetStoredFirebaseRegistrationFailure || - failure is DestroyFirebaseRegistrationFailure) { + } else if (failure is DestroyFirebaseRegistrationFailure) { await clearDataAndGoToLoginPage(); } } void handleSuccessViewState(Success success) async { log('BaseController::handleSuccessViewState(): ${success.runtimeType}'); - if (success is LogoutOidcSuccess) { + if (success is LogoutCurrentAccountOidcSuccess + || success is LogoutCurrentAccountBasicAuthSuccess) { if (_isFcmEnabled) { - _getStoredFirebaseRegistrationFromCache(); + await _removeFirebaseRegistration(); } else { await clearDataAndGoToLoginPage(); } - } else if (success is GetStoredFirebaseRegistrationSuccess) { - _destroyFirebaseRegistration(success.firebaseRegistration.id!); } else if (success is DestroyFirebaseRegistrationSuccess) { await clearDataAndGoToLoginPage(); } } - void startFpsMeter() { - FpsManager().start(); - fpsCallback = (fpsInfo) { - log('BaseController::startFpsMeter(): $fpsInfo'); - }; - if (fpsCallback != null) { - FpsManager().addFpsCallback(fpsCallback!); - } - } - - void stopFpsMeter() { - FpsManager().stop(); - if (fpsCallback != null) { - FpsManager().removeFpsCallback(fpsCallback!); - } - } - void injectAutoCompleteBindings(Session? session, AccountId? accountId) { try { ContactAutoCompleteBindings().dependencies(); @@ -307,36 +282,18 @@ abstract class BaseController extends GetxController } } - void logout(Session? session, AccountId? accountId) async { - if (session == null || accountId == null) { - await clearDataAndGoToLoginPage(); - return; - } + void logout({ + required Session session, + required AccountId accountId + }) async { _isFcmEnabled = _isFcmActivated(session, accountId); - if (isAuthenticatedWithOidc) { - consumeState(logoutOidcInteractor.execute()); - } else { - if (_isFcmEnabled) { - _getStoredFirebaseRegistrationFromCache(); - } else { - await clearDataAndGoToLoginPage(); - } - } + consumeState(_logoutCurrentAccountInteractor.execute()); } - void _destroyFirebaseRegistration(FirebaseRegistrationId firebaseRegistrationId) async { + Future _removeFirebaseRegistration() async { _destroyFirebaseRegistrationInteractor = getBinding(); if (_destroyFirebaseRegistrationInteractor != null) { - consumeState(_destroyFirebaseRegistrationInteractor!.execute(firebaseRegistrationId)); - } else { - await clearDataAndGoToLoginPage(); - } - } - - void _getStoredFirebaseRegistrationFromCache() async { - _getStoredFirebaseRegistrationInteractor = getBinding(); - if (_getStoredFirebaseRegistrationInteractor != null) { - consumeState(_getStoredFirebaseRegistrationInteractor!.execute()); + consumeState(_destroyFirebaseRegistrationInteractor!.execute()); } else { await clearDataAndGoToLoginPage(); } @@ -354,38 +311,19 @@ abstract class BaseController extends GetxController } Future clearAllData() async { - if (isAuthenticatedWithOidc) { - await _clearOidcAuthData(); - } else { - await _clearBasicAuthData(); - } - } - - Future _clearBasicAuthData() async { - await Future.wait([ - deleteCredentialInteractor.execute(), - cachingManager.clearAll(), - languageCacheManager.removeLanguage(), - ]); - if (PlatformInfo.isMobile) { - await cachingManager.clearAllFileInStorage(); - } - authorizationInterceptors.clear(); - authorizationIsolateInterceptors.clear(); - await cachingManager.closeHive(); - } - - Future _clearOidcAuthData() async { - await Future.wait([ - deleteAuthorityOidcInteractor.execute(), - cachingManager.clearAll(), - languageCacheManager.removeLanguage(), - ]); - if (PlatformInfo.isMobile) { - await cachingManager.clearAllFileInStorage(); + try { + await Future.wait([ + cachingManager.clearAll(), + languageCacheManager.removeLanguage(), + ]); + if (PlatformInfo.isMobile) { + await cachingManager.clearAllFileInStorage(); + } + authorizationInterceptors.clear(); + authorizationIsolateInterceptors.clear(); + await cachingManager.closeHive(); + } catch (e, s) { + logError('BaseController::clearAllData: Exception: $e | Stack: $s'); } - authorizationIsolateInterceptors.clear(); - authorizationInterceptors.clear(); - await cachingManager.closeHive(); } -} +} \ No newline at end of file diff --git a/lib/features/base/reloadable/reloadable_controller.dart b/lib/features/base/reloadable/reloadable_controller.dart index 71ecafff7d..476f3be070 100644 --- a/lib/features/base/reloadable/reloadable_controller.dart +++ b/lib/features/base/reloadable/reloadable_controller.dart @@ -5,17 +5,16 @@ import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/extensions/session_extension.dart'; +import 'package:model/account/authentication_type.dart'; +import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; -import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_current_account_cache_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/update_current_account_cache_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/update_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_interactors_bindings.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception.dart'; @@ -25,15 +24,12 @@ import 'package:tmail_ui_user/main/utils/message_toast_utils.dart'; abstract class ReloadableController extends BaseController { final GetSessionInteractor _getSessionInteractor = Get.find(); - final GetAuthenticatedAccountInteractor _getAuthenticatedAccountInteractor = Get.find(); - final UpdateAuthenticationAccountInteractor _updateAuthenticationAccountInteractor = Get.find(); + final GetCurrentAccountCacheInteractor _getCurrentAccountCacheInteractor = Get.find(); + final UpdateCurrentAccountCacheInteractor _updateCurrentAccountCacheInteractor = Get.find(); @override void handleFailureViewState(Failure failure) { - if (failure is GetCredentialFailure || - failure is GetStoredTokenOidcFailure || - failure is GetAuthenticatedAccountFailure) { - log('ReloadableController::handleFailureViewState(): failure: $failure'); + if (failure is GetCurrentAccountCacheFailure) { goToLogin(); } else if (failure is GetSessionFailure) { _handleGetSessionFailure(failure.exception); @@ -44,12 +40,14 @@ abstract class ReloadableController extends BaseController { @override void handleSuccessViewState(Success success) { - if (success is GetCredentialViewState) { - _handleGetCredentialSuccess(success); + if (success is GetCurrentAccountCacheSuccess) { + setUpInterceptors(success.account); + getSessionAction(); } else if (success is GetSessionSuccess) { - _handleGetSessionSuccess(success); - } else if (success is GetStoredTokenOidcSuccess) { - _handleGetStoredTokenOIDCSuccess(success); + updateCurrentAccountCache(success.session); + } else if (success is UpdateCurrentAccountCacheSuccess) { + dynamicUrlInterceptors.changeBaseUrl(success.apiUrl); + handleReloaded(success.session); } else { super.handleSuccessViewState(success); } @@ -59,29 +57,41 @@ abstract class ReloadableController extends BaseController { * trigger reload by getting Credential again then setting up Interceptor and retrieving session * */ void reload() { - getAuthenticatedAccountAction(); + getCurrentAccountCache(); } - void getAuthenticatedAccountAction() { - consumeState(_getAuthenticatedAccountInteractor.execute()); + void getCurrentAccountCache() { + consumeState(_getCurrentAccountCacheInteractor.execute()); } - void _setUpInterceptors(GetCredentialViewState credentialViewState) { - dynamicUrlInterceptors.setJmapUrl(credentialViewState.baseUrl.origin); - dynamicUrlInterceptors.changeBaseUrl(credentialViewState.baseUrl.origin); - authorizationInterceptors.setBasicAuthorization( - credentialViewState.userName, - credentialViewState.password, - ); - authorizationIsolateInterceptors.setBasicAuthorization( - credentialViewState.userName, - credentialViewState.password, - ); - } + void setUpInterceptors(PersonalAccount personalAccount) { + dynamicUrlInterceptors.setJmapUrl(personalAccount.baseUrl); + dynamicUrlInterceptors.changeBaseUrl(personalAccount.baseUrl); - void _handleGetCredentialSuccess(GetCredentialViewState credentialViewState) { - _setUpInterceptors(credentialViewState); - getSessionAction(); + switch(personalAccount.authenticationType) { + case AuthenticationType.oidc: + authorizationInterceptors.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration + ); + authorizationIsolateInterceptors.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration + ); + break; + case AuthenticationType.basic: + authorizationInterceptors.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password, + ); + authorizationIsolateInterceptors.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password, + ); + break; + default: + break; + } } void getSessionAction() { @@ -98,37 +108,8 @@ abstract class ReloadableController extends BaseController { clearDataAndGoToLoginPage(); } - void _handleGetSessionSuccess(GetSessionSuccess success) { - final session = success.session; - final personalAccount = session.personalAccount; - final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl); - if (apiUrl.isNotEmpty) { - dynamicUrlInterceptors.changeBaseUrl(apiUrl); - updateAuthenticationAccount(session, personalAccount.accountId, session.username); - handleReloaded(session); - } else { - clearDataAndGoToLoginPage(); - } - } - void handleReloaded(Session session) {} - void _handleGetStoredTokenOIDCSuccess(GetStoredTokenOidcSuccess tokenOidcSuccess) { - _setUpInterceptorsOidc(tokenOidcSuccess); - getSessionAction(); - } - - void _setUpInterceptorsOidc(GetStoredTokenOidcSuccess tokenOidcSuccess) { - dynamicUrlInterceptors.setJmapUrl(tokenOidcSuccess.baseUrl.toString()); - dynamicUrlInterceptors.changeBaseUrl(tokenOidcSuccess.baseUrl.toString()); - authorizationInterceptors.setTokenAndAuthorityOidc( - newToken: tokenOidcSuccess.tokenOidc, - newConfig: tokenOidcSuccess.oidcConfiguration); - authorizationIsolateInterceptors.setTokenAndAuthorityOidc( - newToken: tokenOidcSuccess.tokenOidc, - newConfig: tokenOidcSuccess.oidcConfiguration); - } - void injectVacationBindings(Session? session, AccountId? accountId) { try { requireCapability(session!, accountId!, [CapabilityIdentifier.jmapVacationResponse]); @@ -138,10 +119,7 @@ abstract class ReloadableController extends BaseController { } } - void updateAuthenticationAccount(Session session, AccountId accountId, UserName userName) { - final apiUrl = session.getQualifiedApiUrl(baseUrl: dynamicUrlInterceptors.jmapUrl); - if (apiUrl.isNotEmpty) { - consumeState(_updateAuthenticationAccountInteractor.execute(accountId, apiUrl, userName)); - } + void updateCurrentAccountCache(Session session) { + consumeState(_updateCurrentAccountCacheInteractor.execute(session)); } } \ No newline at end of file diff --git a/lib/features/caching/clients/authentication_info_cache_client.dart b/lib/features/caching/clients/authentication_info_cache_client.dart deleted file mode 100644 index dd2a89e7e1..0000000000 --- a/lib/features/caching/clients/authentication_info_cache_client.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart'; -import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; - -class AuthenticationInfoCacheClient extends HiveCacheClient { - - @override - String get tableName => 'AuthenticationInfoCache'; - - @override - bool get encryption => true; -} \ No newline at end of file diff --git a/lib/features/caching/clients/token_oidc_cache_client.dart b/lib/features/caching/clients/token_oidc_cache_client.dart deleted file mode 100644 index bded2ee12d..0000000000 --- a/lib/features/caching/clients/token_oidc_cache_client.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:tmail_ui_user/features/caching/config/hive_cache_client.dart'; -import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; - -class TokenOidcCacheClient extends HiveCacheClient { - - @override - String get tableName => 'TokenOidcCache'; - - @override - bool get encryption => true; -} \ No newline at end of file diff --git a/lib/features/caching/config/hive_cache_config.dart b/lib/features/caching/config/hive_cache_config.dart index 40f2333b0f..18fd34b300 100644 --- a/lib/features/caching/config/hive_cache_config.dart +++ b/lib/features/caching/config/hive_cache_config.dart @@ -15,7 +15,7 @@ import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; import 'package:tmail_ui_user/features/home/data/model/session_hive_obj.dart'; import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; -import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; +import 'package:tmail_ui_user/features/login/data/model/basic_auth_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/encryption_key_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/recent_login_url_cache.dart'; import 'package:tmail_ui_user/features/login/data/model/recent_login_username_cache.dart'; @@ -147,9 +147,9 @@ class HiveCacheConfig { EncryptionKeyCacheAdapter(), CachingConstants.ENCRYPTION_KEY_HIVE_CACHE_IDENTIFY ); - registerCacheAdapter( - AuthenticationInfoCacheAdapter(), - CachingConstants.AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY + registerCacheAdapter( + BasicAuthCacheAdapter(), + CachingConstants.BASIC_AUTH_HIVE_CACHE_IDENTIFY ); registerCacheAdapter( RecentLoginUrlCacheAdapter(), diff --git a/lib/features/caching/utils/caching_constants.dart b/lib/features/caching/utils/caching_constants.dart index 4e1260cc84..33dae4d70c 100644 --- a/lib/features/caching/utils/caching_constants.dart +++ b/lib/features/caching/utils/caching_constants.dart @@ -10,7 +10,6 @@ class CachingConstants { static const int TOKEN_OIDC_HIVE_CACHE_IDENTIFY = 8; static const int ACCOUNT_HIVE_CACHE_IDENTIFY = 9; static const int ENCRYPTION_KEY_HIVE_CACHE_IDENTIFY = 10; - static const int AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY = 11; static const int RECENT_LOGIN_URL_HIVE_CACHE_IDENTITY = 12; static const int RECENT_LOGIN_USERNAME_HIVE_CACHE_IDENTITY = 13; static const int FIREBASE_REGISTRATION_HIVE_CACHE_IDENTITY = 14; @@ -19,6 +18,7 @@ class CachingConstants { static const int DETAILED_EMAIL_HIVE_CACHE_ID = 17; static const int SENDING_EMAIL_HIVE_CACHE_ID = 18; static const int SESSION_HIVE_CACHE_ID = 19; + static const int BASIC_AUTH_HIVE_CACHE_IDENTIFY = 20; static const String fcmCacheBoxName = 'fcm_cache_box'; static const String newEmailCacheBoxName = 'new_email_cache_box'; diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 612468edf6..d06da6db5b 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -12,7 +12,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/account/account_request.dart'; +import 'package:model/account/personal_account.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; @@ -44,14 +44,14 @@ abstract class EmailDataSource { List attachments, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest + PersonalAccount personalAccount ); Future exportAttachment( Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, CancelToken cancelToken ); @@ -60,7 +60,7 @@ abstract class EmailDataSource { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, StreamController> onReceiveController, {CancelToken? cancelToken} ); diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index 407c1c1a07..6952f3975e 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -81,10 +81,10 @@ class EmailDataSourceImpl extends EmailDataSource { List attachments, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest + PersonalAccount personalAccount ) { return Future.sync(() async { - return await emailAPI.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest); + return await emailAPI.downloadAttachments(attachments, accountId, baseDownloadUrl, personalAccount); }).catchError(_exceptionThrower.throwException); } @@ -93,11 +93,11 @@ class EmailDataSourceImpl extends EmailDataSource { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, CancelToken cancelToken ) { return Future.sync(() async { - return await emailAPI.exportAttachment(attachment, accountId, baseDownloadUrl, accountRequest, cancelToken); + return await emailAPI.exportAttachment(attachment, accountId, baseDownloadUrl, personalAccount, cancelToken); }).catchError(_exceptionThrower.throwException); } @@ -174,7 +174,7 @@ class EmailDataSourceImpl extends EmailDataSource { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, StreamController> onReceiveController, {CancelToken? cancelToken} ) { @@ -184,7 +184,7 @@ class EmailDataSourceImpl extends EmailDataSource { attachment, accountId, baseDownloadUrl, - accountRequest, + personalAccount, onReceiveController, cancelToken: cancelToken); }).catchError(_exceptionThrower.throwException); diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart index fe9a6eade6..d457bb08ee 100644 --- a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -15,7 +15,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/account/account_request.dart'; +import 'package:model/account/personal_account.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; @@ -91,7 +91,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, StreamController> onReceiveController, {CancelToken? cancelToken} ) { @@ -99,12 +99,12 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { } @override - Future> downloadAttachments(List attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) { + Future> downloadAttachments(List attachments, AccountId accountId, String baseDownloadUrl, PersonalAccount personalAccount) { throw UnimplementedError(); } @override - Future exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) { + Future exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, PersonalAccount personalAccount, CancelToken cancelToken) { throw UnimplementedError(); } diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index 4a148a663e..6219ff55cc 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -39,8 +39,8 @@ import 'package:jmap_dart_client/jmap/mail/email/submission/set/set_email_submis import 'package:jmap_dart_client/jmap/mail/email/submission/set/set_email_submission_response.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/set/set_mailbox_method.dart'; -import 'package:model/account/account_request.dart'; import 'package:model/account/authentication_type.dart'; +import 'package:model/account/personal_account.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/email_action_type.dart'; @@ -54,7 +54,6 @@ import 'package:model/extensions/list_email_extension.dart'; import 'package:model/extensions/list_email_id_extension.dart'; import 'package:model/extensions/mailbox_id_extension.dart'; import 'package:model/extensions/session_extension.dart'; -import 'package:model/oidc/token_oidc.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; @@ -66,6 +65,8 @@ import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/personal_account_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; @@ -273,12 +274,12 @@ class EmailAPI with HandleSetErrorMixin { List attachments, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest + PersonalAccount personalAccount ) async { - if (accountRequest.authenticationType == AuthenticationType.oidc && - accountRequest.token?.isExpired == true && - accountRequest.token?.refreshToken.isNotEmpty == true) { - throw DownloadAttachmentHasTokenExpiredException(accountRequest.token!.refreshToken); + if (personalAccount.authenticationType == AuthenticationType.oidc + && personalAccount.tokenOidc?.isExpired == true + && personalAccount.tokenOidc?.refreshToken.isNotEmpty == true) { + throw DownloadAttachmentHasTokenExpiredException(personalAccount.tokenOidc!.refreshToken); } String externalStorageDirPath; @@ -290,16 +291,12 @@ class EmailAPI with HandleSetErrorMixin { throw DeviceNotSupportedException(); } - final authentication = accountRequest.authenticationType == AuthenticationType.oidc - ? accountRequest.bearerToken - : accountRequest.basicAuth; - final taskIds = await Future.wait( attachments.map((attachment) async => await FlutterDownloader.enqueue( url: attachment.getDownloadUrl(baseDownloadUrl, accountId), savedDir: externalStorageDirPath, headers: { - HttpHeaders.authorizationHeader: authentication, + HttpHeaders.authorizationHeader: personalAccount.authenticationHeader ?? '', HttpHeaders.acceptHeader: DioClient.jmapHeader }, fileName: attachment.name, @@ -316,18 +313,14 @@ class EmailAPI with HandleSetErrorMixin { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, CancelToken cancelToken ) async { - final authentication = accountRequest.authenticationType == AuthenticationType.oidc - ? accountRequest.bearerToken - : accountRequest.basicAuth; - return _downloadManager.downloadFile( attachment.getDownloadUrl(baseDownloadUrl, accountId), getTemporaryDirectory(), attachment.name ?? '', - authentication, + personalAccount.authenticationHeader ?? '', cancelToken: cancelToken); } @@ -336,18 +329,15 @@ class EmailAPI with HandleSetErrorMixin { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, StreamController> onReceiveController, {CancelToken? cancelToken} ) async { - final authentication = accountRequest.authenticationType == AuthenticationType.oidc - ? accountRequest.bearerToken - : accountRequest.basicAuth; final downloadUrl = attachment.getDownloadUrl(baseDownloadUrl, accountId); log('EmailAPI::downloadAttachmentForWeb(): downloadUrl: $downloadUrl'); final headerParam = _dioClient.getHeaders(); - headerParam[HttpHeaders.authorizationHeader] = authentication; + headerParam[HttpHeaders.authorizationHeader] = personalAccount.authenticationHeader; headerParam[HttpHeaders.acceptHeader] = DioClient.jmapHeader; final bytesDownloaded = await _dioClient.get( diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index 2d699dc11e..b92d69219c 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -14,7 +14,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/account/account_request.dart'; +import 'package:model/account/personal_account.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/email_content.dart'; @@ -87,9 +87,13 @@ class EmailRepositoryImpl extends EmailRepository { List attachments, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest + PersonalAccount personalAccount, ) { - return emailDataSource[DataSourceType.network]!.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest); + return emailDataSource[DataSourceType.network]!.downloadAttachments( + attachments, + accountId, + baseDownloadUrl, + personalAccount); } @override @@ -97,14 +101,14 @@ class EmailRepositoryImpl extends EmailRepository { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, CancelToken cancelToken ) { return emailDataSource[DataSourceType.network]!.exportAttachment( attachment, accountId, baseDownloadUrl, - accountRequest, + personalAccount, cancelToken); } @@ -193,7 +197,7 @@ class EmailRepositoryImpl extends EmailRepository { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, StreamController> onReceiveController, {CancelToken? cancelToken} ) { @@ -202,7 +206,7 @@ class EmailRepositoryImpl extends EmailRepository { attachment, accountId, baseDownloadUrl, - accountRequest, + personalAccount, onReceiveController, cancelToken: cancelToken); } diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index 38488b0581..6e067dd225 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -13,7 +13,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/account/account_request.dart'; +import 'package:model/account/personal_account.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/email_content.dart'; @@ -46,14 +46,14 @@ abstract class EmailRepository { List attachments, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest + PersonalAccount personalAccount, ); Future exportAttachment( Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, CancelToken cancelToken ); @@ -62,7 +62,7 @@ abstract class EmailRepository { Attachment attachment, AccountId accountId, String baseDownloadUrl, - AccountRequest accountRequest, + PersonalAccount personalAccount, StreamController> onReceiveController, {CancelToken? cancelToken} ); diff --git a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart index 106d95dc0b..eab45ab02f 100644 --- a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart +++ b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart @@ -5,29 +5,17 @@ import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/account/account_request.dart'; -import 'package:model/account/authentication_type.dart'; -import 'package:model/account/password.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; class DownloadAttachmentForWebInteractor { - final EmailRepository emailRepository; - final CredentialRepository credentialRepository; + final EmailRepository _emailRepository; final AccountRepository _accountRepository; - final AuthenticationOIDCRepository _authenticationOIDCRepository; - DownloadAttachmentForWebInteractor( - this.emailRepository, - this.credentialRepository, - this._accountRepository, - this._authenticationOIDCRepository); + DownloadAttachmentForWebInteractor(this._emailRepository, this._accountRepository); Stream> execute( DownloadTaskId taskId, @@ -42,25 +30,13 @@ class DownloadAttachmentForWebInteractor { onReceiveController.add(Right(StartDownloadAttachmentForWeb(taskId, attachment))); final currentAccount = await _accountRepository.getCurrentAccount(); - AccountRequest? accountRequest; - if (currentAccount.authenticationType == AuthenticationType.oidc) { - final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest.withOidc(token: tokenOidc); - } else { - final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - accountRequest = AccountRequest.withBasic( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - ); - } - - final bytesDownloaded = await emailRepository.downloadAttachmentForWeb( + final bytesDownloaded = await _emailRepository.downloadAttachmentForWeb( taskId, attachment, accountId, baseDownloadUrl, - accountRequest, + currentAccount, onReceiveController, cancelToken: cancelToken ); diff --git a/lib/features/email/domain/usecases/download_attachments_interactor.dart b/lib/features/email/domain/usecases/download_attachments_interactor.dart index 70484be106..dbe6effa09 100644 --- a/lib/features/email/domain/usecases/download_attachments_interactor.dart +++ b/lib/features/email/domain/usecases/download_attachments_interactor.dart @@ -3,32 +3,24 @@ import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/account/account_request.dart'; -import 'package:model/account/authentication_type.dart'; -import 'package:model/account/password.dart'; -import 'package:model/account/personal_account.dart'; import 'package:model/email/attachment.dart'; -import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachments_state.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/personal_account_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; class DownloadAttachmentsInteractor { - final EmailRepository emailRepository; - final CredentialRepository credentialRepository; + final EmailRepository _emailRepository; final AccountRepository _accountRepository; final AuthenticationOIDCRepository _authenticationOIDCRepository; final AuthorizationInterceptors _authorizationInterceptors; DownloadAttachmentsInteractor( - this.emailRepository, - this.credentialRepository, + this._emailRepository, this._accountRepository, this._authenticationOIDCRepository, this._authorizationInterceptors, @@ -42,24 +34,11 @@ class DownloadAttachmentsInteractor { try { final currentAccount = await _accountRepository.getCurrentAccount(); - AccountRequest? accountRequest; - - if (currentAccount.authenticationType == AuthenticationType.oidc) { - final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest.withOidc(token: tokenOidc); - } else { - final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - accountRequest = AccountRequest.withBasic( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - ); - } - - final taskIds = await emailRepository.downloadAttachments( + final taskIds = await _emailRepository.downloadAttachments( attachments, accountId, baseDownloadUrl, - accountRequest + currentAccount ); yield Right(DownloadAttachmentsSuccess(taskIds)); @@ -85,41 +64,29 @@ class DownloadAttachmentsInteractor { String refreshToken) async* { log('DownloadAttachmentsInteractor::_retryDownloadAttachments(): $refreshToken'); try { - final accountCurrent = await _accountRepository.getCurrentAccount(); - final oidcConfig = await _authenticationOIDCRepository.getStoredOidcConfiguration(); + final currentAccount = await _accountRepository.getCurrentAccount(); + final newTokenOIDC = await _authenticationOIDCRepository.refreshingTokensOIDC( - oidcConfig.clientId, - oidcConfig.redirectUrl, - oidcConfig.discoveryUrl, - oidcConfig.scopes, - refreshToken); + currentAccount.tokenOidc!.oidcConfiguration, + refreshToken + ); - await _accountRepository.deleteCurrentAccount(accountCurrent.id); + final newAccount = currentAccount.updateToken(newTokenOIDC); - await _authenticationOIDCRepository.persistTokenOIDC(newTokenOIDC); - - await _accountRepository.setCurrentAccount( - PersonalAccount( - newTokenOIDC.tokenIdHash, - AuthenticationType.oidc, - isSelected: true, - accountId: accountId, - apiUrl: accountCurrent.apiUrl, - userName: accountCurrent.userName - ) - ); + await _accountRepository.deleteCurrentAccount(currentAccount.id); + await _accountRepository.setCurrentAccount(newAccount); _authorizationInterceptors.setTokenAndAuthorityOidc( - newToken: newTokenOIDC, - newConfig: oidcConfig); - - final accountRequest = AccountRequest.withOidc(token: newTokenOIDC); + newToken: newTokenOIDC, + newConfig: newTokenOIDC.oidcConfiguration + ); - final taskIds = await emailRepository.downloadAttachments( - attachments, - accountId, - baseDownloadUrl, - accountRequest); + final taskIds = await _emailRepository.downloadAttachments( + attachments, + accountId, + baseDownloadUrl, + newAccount + ); yield Right(DownloadAttachmentsSuccess(taskIds)); } catch (e) { diff --git a/lib/features/email/domain/usecases/export_attachment_interactor.dart b/lib/features/email/domain/usecases/export_attachment_interactor.dart index 1dc8d86f54..dfac919f22 100644 --- a/lib/features/email/domain/usecases/export_attachment_interactor.dart +++ b/lib/features/email/domain/usecases/export_attachment_interactor.dart @@ -1,28 +1,23 @@ import 'dart:async'; -import 'package:core/core.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:model/model.dart'; +import 'package:rich_text_composer/views/commons/logger.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; class ExportAttachmentInteractor { - final EmailRepository emailRepository; - final CredentialRepository credentialRepository; + final EmailRepository _emailRepository; final AccountRepository _accountRepository; - final AuthenticationOIDCRepository _authenticationOIDCRepository; ExportAttachmentInteractor( - this.emailRepository, - this.credentialRepository, + this._emailRepository, this._accountRepository, - this._authenticationOIDCRepository, ); Stream> execute( @@ -34,30 +29,17 @@ class ExportAttachmentInteractor { try { final currentAccount = await _accountRepository.getCurrentAccount(); - AccountRequest? accountRequest; - - if (currentAccount.authenticationType == AuthenticationType.oidc) { - final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest.withOidc(token: tokenOidc); - } else { - final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - accountRequest = AccountRequest.withBasic( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - ); - } - - final downloadedResponse = await emailRepository.exportAttachment( + final downloadedResponse = await _emailRepository.exportAttachment( attachment, accountId, baseDownloadUrl, - accountRequest, + currentAccount, cancelToken ); yield Right(ExportAttachmentSuccess(downloadedResponse)); } catch (exception) { - log('ExportAttachmentInteractor::execute(): exception: $exception'); + logError('ExportAttachmentInteractor::execute(): exception: $exception'); yield Left(ExportAttachmentFailure(exception)); } } diff --git a/lib/features/email/presentation/bindings/email_bindings.dart b/lib/features/email/presentation/bindings/email_bindings.dart index a589ded969..b2eb518bad 100644 --- a/lib/features/email/presentation/bindings/email_bindings.dart +++ b/lib/features/email/presentation/bindings/email_bindings.dart @@ -28,7 +28,6 @@ import 'package:tmail_ui_user/features/email/presentation/controller/single_emai import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart'; @@ -127,16 +126,13 @@ class EmailBindings extends BaseBindings { Get.find())); Get.lazyPut(() => DownloadAttachmentsInteractor( Get.find(), - Get.find(), Get.find(), Get.find(), Get.find(), )); Get.lazyPut(() => ExportAttachmentInteractor( Get.find(), - Get.find(), Get.find(), - Get.find(), )); Get.lazyPut(() => MoveToMailboxInteractor( Get.find(), @@ -144,9 +140,7 @@ class EmailBindings extends BaseBindings { Get.lazyPut(() => MarkAsStarEmailInteractor(Get.find())); Get.lazyPut(() => DownloadAttachmentForWebInteractor( Get.find(), - Get.find(), - Get.find(), - Get.find())); + Get.find())); Get.lazyPut(() => GetStoredEmailStateInteractor(Get.find())); Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find())); Get.lazyPut(() => PrintEmailInteractor(Get.find())); diff --git a/lib/features/home/presentation/home_controller.dart b/lib/features/home/presentation/home_controller.dart index c7a40a7d93..8f7c3633a9 100644 --- a/lib/features/home/presentation/home_controller.dart +++ b/lib/features/home/presentation/home_controller.dart @@ -90,7 +90,7 @@ class HomeController extends ReloadableController { static void downloadCallback(String id, DownloadTaskStatus status, int progress) {} - void _cleanupCache() async { + Future _cleanupCache() async { await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager); await Future.wait([ @@ -98,7 +98,7 @@ class HomeController extends ReloadableController { _cleanupRecentSearchCacheInteractor.execute(RecentSearchCleanupRule()), _cleanupRecentLoginUrlCacheInteractor.execute(RecentLoginUrlCleanupRule()), _cleanupRecentLoginUsernameCacheInteractor.execute(RecentLoginUsernameCleanupRule()), - ]).then((value) => getAuthenticatedAccountAction()); + ]).then((_) => getCurrentAccountCache()); } void _registerReceivingSharingIntent() { diff --git a/lib/features/login/data/datasource/authentication_oidc_datasource.dart b/lib/features/login/data/datasource/authentication_oidc_datasource.dart index d64a642430..5858f60d9d 100644 --- a/lib/features/login/data/datasource/authentication_oidc_datasource.dart +++ b/lib/features/login/data/datasource/authentication_oidc_datasource.dart @@ -1,41 +1,23 @@ -import 'package:model/model.dart'; +import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/request/oidc_request.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; +import 'package:model/oidc/response/oidc_response.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; abstract class AuthenticationOIDCDataSource { Future checkOIDCIsAvailable(OIDCRequest oidcRequest); - Future getOIDCConfiguration(OIDCResponse oidcResponse); - Future discoverOIDC(OIDCConfiguration oidcConfiguration); - Future getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List scopes); - - Future persistTokenOIDC(TokenOIDC tokenOidc); - - Future deleteTokenOIDC(); - - Future getStoredTokenOIDC(String tokenIdHash); - - Future persistAuthorityOidc(String authority); - - Future deleteAuthorityOidc(); - - Future getStoredOidcConfiguration(); + Future getTokenOIDC(OIDCConfiguration oidcConfiguration); - Future refreshingTokensOIDC( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes, - String refreshToken); + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken); - Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery); + Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcDiscoveryResponse); - Future authenticateOidcOnBrowser( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes); + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration); - Future getAuthenticationInfo(); + Future getAuthResponseUrlBrowser(); } \ No newline at end of file diff --git a/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart b/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart index 7f5a83b91a..85f4855297 100644 --- a/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart +++ b/lib/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart @@ -1,7 +1,10 @@ -import 'package:model/model.dart'; +import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/request/oidc_request.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; +import 'package:model/oidc/response/oidc_response.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart'; -import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; @@ -10,16 +13,12 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { final OIDCHttpClient _oidcHttpClient; final AuthenticationClientBase _authenticationClient; - final TokenOidcCacheManager _tokenOidcCacheManager; - final OidcConfigurationCacheManager _oidcConfigurationCacheManager; final ExceptionThrower _exceptionThrower; final ExceptionThrower _cacheExceptionThrower; AuthenticationOIDCDataSourceImpl( this._oidcHttpClient, this._authenticationClient, - this._tokenOidcCacheManager, - this._oidcConfigurationCacheManager, this._exceptionThrower, this._cacheExceptionThrower ); @@ -31,13 +30,6 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { }).catchError(_exceptionThrower.throwException); } - @override - Future getOIDCConfiguration(OIDCResponse oidcResponse) { - return Future.sync(() async { - return await _oidcHttpClient.getOIDCConfiguration(oidcResponse); - }).catchError(_exceptionThrower.throwException); - } - @override Future discoverOIDC(OIDCConfiguration oidcConfiguration) { return Future.sync(() async { @@ -46,99 +38,37 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { } @override - Future getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List scopes) { + Future getTokenOIDC(OIDCConfiguration oidcConfiguration) { return Future.sync(() async { - return await _authenticationClient.getTokenOIDC(clientId, redirectUrl, discoveryUrl, scopes); + return await _authenticationClient.getTokenOIDC(oidcConfiguration); }).catchError(_exceptionThrower.throwException); } @override - Future getStoredTokenOIDC(String tokenIdHash) { + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken) { return Future.sync(() async { - return await _tokenOidcCacheManager.getTokenOidc(tokenIdHash); + return await _authenticationClient.refreshingTokensOIDC(oidcConfiguration, refreshToken); }).catchError(_cacheExceptionThrower.throwException); } @override - Future persistTokenOIDC(TokenOIDC tokenOidc) { + Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcDiscoveryResponse) { return Future.sync(() async { - return await _tokenOidcCacheManager.persistOneTokenOidc(tokenOidc); + return await _authenticationClient.logoutOidc(tokenId, config, oidcDiscoveryResponse); }).catchError(_cacheExceptionThrower.throwException); } @override - Future getStoredOidcConfiguration() { + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration) { return Future.sync(() async { - return await _oidcConfigurationCacheManager.getOidcConfiguration(); + return await _authenticationClient.authenticateOidcOnBrowser(oidcConfiguration); }).catchError(_cacheExceptionThrower.throwException); } @override - Future persistAuthorityOidc(String authority) { + Future getAuthResponseUrlBrowser() { return Future.sync(() async { - return await _oidcConfigurationCacheManager.persistAuthorityOidc(authority); + return await _authenticationClient.getAuthResponseUrlBrowser(); }).catchError(_cacheExceptionThrower.throwException); } - - @override - Future refreshingTokensOIDC( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes, - String refreshToken - ) { - return Future.sync(() async { - return await _authenticationClient.refreshingTokensOIDC( - clientId, - redirectUrl, - discoveryUrl, - scopes, - refreshToken); - }).catchError(_exceptionThrower.throwException); - } - - @override - Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) { - return Future.sync(() async { - return await _authenticationClient.logoutOidc(tokenId, config, oidcRescovery); - }).catchError(_exceptionThrower.throwException); - } - - @override - Future deleteAuthorityOidc() { - return Future.sync(() async { - return await _oidcConfigurationCacheManager.deleteAuthorityOidc(); - }).catchError(_exceptionThrower.throwException); - } - - @override - Future authenticateOidcOnBrowser( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes - ) { - return Future.sync(() async { - return await _authenticationClient.authenticateOidcOnBrowser( - clientId, - redirectUrl, - discoveryUrl, - scopes); - }).catchError(_exceptionThrower.throwException); - } - - @override - Future getAuthenticationInfo() { - return Future.sync(() async { - return await _authenticationClient.getAuthenticationInfo(); - }).catchError(_exceptionThrower.throwException); - } - - @override - Future deleteTokenOIDC() { - return Future.sync(() async { - return await _tokenOidcCacheManager.deleteTokenOidc(); - }).catchError(_exceptionThrower.throwException); - } } \ No newline at end of file diff --git a/lib/features/login/data/extensions/account_cache_extensions.dart b/lib/features/login/data/extensions/account_cache_extensions.dart index 1217c03b02..8986b80cb1 100644 --- a/lib/features/login/data/extensions/account_cache_extensions.dart +++ b/lib/features/login/data/extensions/account_cache_extensions.dart @@ -1,51 +1,43 @@ +import 'package:collection/collection.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:model/account/authentication_type.dart'; import 'package:model/account/personal_account.dart'; import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; +import 'package:tmail_ui_user/features/login/data/model/basic_auth_cache.dart'; +import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; extension AccountCacheExtension on AccountCache { - AuthenticationType fromAuthenticationTypeString() { - if (authenticationType == 'basic') { - return AuthenticationType.basic; - } else if (authenticationType == 'oidc') { - return AuthenticationType.oidc; - } else { - return AuthenticationType.none; - } - } + AuthenticationType toAuthenticationType() => + AuthenticationType.values.firstWhereOrNull((type) => type.name == authenticationType) + ?? AuthenticationType.none; PersonalAccount toAccount() { - final authenticationType = fromAuthenticationTypeString(); return PersonalAccount( - id, - authenticationType, + id: id, + authenticationType: toAuthenticationType(), isSelected: isSelected, + baseUrl: baseUrl, accountId: accountId != null ? AccountId(Id(accountId!)) : null, apiUrl: apiUrl, - userName: userName != null ? UserName(userName!) : null); - } - - AccountCache unselected() { - return AccountCache( - id, - authenticationType, - isSelected: false, - accountId: accountId, - apiUrl: apiUrl, - userName: userName + userName: userName != null ? UserName(userName!) : null, + tokenOidc: tokenOidc?.toTokenOIDC(), + basicAuth: basicAuth?.toBasicAuth() ); } - AccountCache emptyId() { + AccountCache unselected() { return AccountCache( - '', - authenticationType, + id: id, + authenticationType: authenticationType, isSelected: false, + baseUrl: baseUrl, accountId: accountId, apiUrl: apiUrl, - userName: userName + userName: userName, + tokenOidc: tokenOidc, + basicAuth: basicAuth, ); } } \ No newline at end of file diff --git a/lib/features/login/data/extensions/authentication_token_extension.dart b/lib/features/login/data/extensions/authentication_token_extension.dart index fed776cd75..9be6239707 100644 --- a/lib/features/login/data/extensions/authentication_token_extension.dart +++ b/lib/features/login/data/extensions/authentication_token_extension.dart @@ -1,14 +1,15 @@ import 'package:flutter_appauth/flutter_appauth.dart'; -import 'package:model/model.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; extension AuthorizationTokenResponseExtension on AuthorizationTokenResponse { - - TokenOIDC toTokenOIDC() { + TokenOIDC toTokenOIDC({required String authority}) { return TokenOIDC( - accessToken ?? '', - TokenId(idToken ?? ''), - refreshToken ?? '', + token: accessToken ?? '', + tokenId: TokenId(idToken ?? ''), + refreshToken: refreshToken ?? '', + authority: authority, expiredTime: accessTokenExpirationDateTime ?? DateTime.now()); } } \ No newline at end of file diff --git a/lib/features/login/data/extensions/basic_auth_extension.dart b/lib/features/login/data/extensions/basic_auth_extension.dart new file mode 100644 index 0000000000..2f9eff50ae --- /dev/null +++ b/lib/features/login/data/extensions/basic_auth_extension.dart @@ -0,0 +1,12 @@ +import 'dart:convert'; + +import 'package:model/account/basic_auth.dart'; +import 'package:tmail_ui_user/features/login/data/model/basic_auth_cache.dart'; + +extension BasicAuthExtension on BasicAuth { + BasicAuthCache toBasicAuthCache() { + return BasicAuthCache(userName.value, password.value); + } + + String get authenticationHeader => 'Basic ${base64Encode(utf8.encode('${userName.value}:${password.value}'))}'; +} \ No newline at end of file diff --git a/lib/features/login/data/extensions/personal_account_extension.dart b/lib/features/login/data/extensions/personal_account_extension.dart index c36dcb93cc..8014e0934d 100644 --- a/lib/features/login/data/extensions/personal_account_extension.dart +++ b/lib/features/login/data/extensions/personal_account_extension.dart @@ -1,14 +1,61 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:model/account/authentication_type.dart'; import 'package:model/account/personal_account.dart'; +import 'package:model/extensions/account_id_extensions.dart'; +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/basic_auth_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/model/account_cache.dart'; extension PersonalAccountExtension on PersonalAccount { AccountCache toCache() { return AccountCache( - id, - authenticationType.name, + id: id, + authenticationType: authenticationType.name, isSelected: isSelected, - accountId: accountId?.id.value, + baseUrl: baseUrl, + accountId: accountId?.asString, apiUrl: apiUrl, - userName: userName?.value); + userName: userName?.value, + tokenOidc: tokenOidc?.toTokenOidcCache(), + basicAuth: basicAuth?.toBasicAuthCache(), + ); } + + PersonalAccount updateToken(TokenOIDC newTokenOIDC) { + return PersonalAccount( + id: newTokenOIDC.tokenIdHash, + authenticationType: authenticationType, + isSelected: isSelected, + baseUrl: baseUrl, + accountId: accountId, + apiUrl: apiUrl, + userName: userName, + tokenOidc: newTokenOIDC, + basicAuth: basicAuth + ); + } + + PersonalAccount updateAccountId({ + required AccountId accountId, + required String apiUrl, + required UserName userName, + }) { + return PersonalAccount( + id: id, + authenticationType: authenticationType, + isSelected: isSelected, + baseUrl: baseUrl, + accountId: accountId, + apiUrl: apiUrl, + userName: userName, + tokenOidc: tokenOidc, + basicAuth: basicAuth + ); + } + + String? get authenticationHeader => authenticationType == AuthenticationType.oidc + ? tokenOidc?.authenticationHeader + : basicAuth?.authenticationHeader; } \ No newline at end of file diff --git a/lib/features/login/data/extensions/token_oidc_cache_extension.dart b/lib/features/login/data/extensions/token_oidc_cache_extension.dart deleted file mode 100644 index f859f597d4..0000000000 --- a/lib/features/login/data/extensions/token_oidc_cache_extension.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:model/oidc/token_id.dart'; -import 'package:model/oidc/token_oidc.dart'; -import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; - -extension TokenOidcCacheExtension on TokenOidcCache { - TokenOIDC toTokenOidc() { - return TokenOIDC(token, TokenId(tokenId), refreshToken, expiredTime: expiredTime); - } -} \ No newline at end of file diff --git a/lib/features/login/data/extensions/token_oidc_extension.dart b/lib/features/login/data/extensions/token_oidc_extension.dart index 5fbcce7d41..fd77b22af5 100644 --- a/lib/features/login/data/extensions/token_oidc_extension.dart +++ b/lib/features/login/data/extensions/token_oidc_extension.dart @@ -1,8 +1,39 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:model/oidc/oidc_configuration.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; +import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart'; +import 'package:tmail_ui_user/main/utils/app_config.dart'; extension TokenOidcExtension on TokenOIDC { TokenOidcCache toTokenOidcCache() { - return TokenOidcCache(token, tokenId.uuid, refreshToken, expiredTime: expiredTime); + return TokenOidcCache( + token: token, + tokenId: tokenId.uuid, + refreshToken: refreshToken, + authority: authority, + expiredTime: expiredTime + ); } + + bool isTokenValid() => token.isNotEmpty && tokenId.uuid.isNotEmpty; + + String get tokenIdHash => tokenId.uuid.hashCode.toString(); + + bool get isExpired { + if (expiredTime != null) { + final currentTime = DateTime.now(); + log('TokenOIDC::isExpired(): currentTime: $currentTime | expiredTime: $expiredTime'); + return expiredTime!.isBefore(currentTime); + } + return false; + } + + String get authenticationHeader => 'Bearer $token'; + + OIDCConfiguration get oidcConfiguration => OIDCConfiguration( + authority: authority, + clientId: OIDCConstant.clientId, + scopes: AppConfig.oidcScopes + ); } \ No newline at end of file diff --git a/lib/features/login/data/extensions/token_response_extension.dart b/lib/features/login/data/extensions/token_response_extension.dart index fbfff79109..013c5c04cd 100644 --- a/lib/features/login/data/extensions/token_response_extension.dart +++ b/lib/features/login/data/extensions/token_response_extension.dart @@ -1,14 +1,18 @@ import 'package:flutter_appauth/flutter_appauth.dart'; -import 'package:model/model.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; extension TokenResponseExtension on TokenResponse { - - TokenOIDC toTokenOIDC({String? maybeAvailableRefreshToken}) { + TokenOIDC toTokenOIDC({ + required String authority, + String? maybeAvailableRefreshToken + }) { return TokenOIDC( - accessToken ?? '', - TokenId(idToken ?? ''), - refreshToken ?? maybeAvailableRefreshToken ?? '', + token: accessToken ?? '', + tokenId: TokenId(idToken ?? ''), + refreshToken: refreshToken ?? maybeAvailableRefreshToken ?? '', + authority: authority, expiredTime: accessTokenExpirationDateTime ?? DateTime.now()); } } \ No newline at end of file diff --git a/lib/features/login/data/local/authentication_info_cache_manager.dart b/lib/features/login/data/local/authentication_info_cache_manager.dart deleted file mode 100644 index a9089267f9..0000000000 --- a/lib/features/login/data/local/authentication_info_cache_manager.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:tmail_ui_user/features/caching/clients/authentication_info_cache_client.dart'; -import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; - -class AuthenticationInfoCacheManager { - final AuthenticationInfoCacheClient _authenticationInfoCacheClient; - - AuthenticationInfoCacheManager(this._authenticationInfoCacheClient); - - Future storeAuthenticationInfo(AuthenticationInfoCache authenticationInfoCache) { - return _authenticationInfoCacheClient.insertItem( - AuthenticationInfoCache.keyCacheValue, - authenticationInfoCache); - } - - Future getAuthenticationInfoStored() async { - final authenticationInfoCache = await _authenticationInfoCacheClient.getItem(AuthenticationInfoCache.keyCacheValue); - if (authenticationInfoCache != null) { - return authenticationInfoCache; - } else { - throw NotFoundAuthenticationInfoCache(); - } - } - - Future removeAuthenticationInfo() { - return _authenticationInfoCacheClient.deleteItem(AuthenticationInfoCache.keyCacheValue); - } -} \ No newline at end of file diff --git a/lib/features/login/data/local/token_oidc_cache_manager.dart b/lib/features/login/data/local/token_oidc_cache_manager.dart deleted file mode 100644 index b65b1523ea..0000000000 --- a/lib/features/login/data/local/token_oidc_cache_manager.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:core/utils/app_logger.dart'; -import 'package:model/oidc/token_oidc.dart'; -import 'package:tmail_ui_user/features/caching/clients/token_oidc_cache_client.dart'; -import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_cache_extension.dart'; -import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; - -class TokenOidcCacheManager { - final TokenOidcCacheClient _tokenOidcCacheClient; - - TokenOidcCacheManager(this._tokenOidcCacheClient); - - Future getTokenOidc(String tokenIdHash) async { - final tokenCache = await _tokenOidcCacheClient.getItem(tokenIdHash); - log('TokenOidcCacheManager::getTokenOidc(): tokenCache: $tokenCache'); - if (tokenCache == null) { - throw NotFoundStoredTokenException(); - } else { - return tokenCache.toTokenOidc(); - } - } - - Future persistOneTokenOidc(TokenOIDC tokenOIDC) async { - log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); - await _tokenOidcCacheClient.clearAllData(); - log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); - log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenIdHash}'); - log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); - await _tokenOidcCacheClient.insertItem(tokenOIDC.tokenIdHash, tokenOIDC.toTokenOidcCache()); - } - - Future deleteTokenOidc() async { - await _tokenOidcCacheClient.clearAllData(); - } -} \ No newline at end of file diff --git a/lib/features/login/data/model/account_cache.dart b/lib/features/login/data/model/account_cache.dart index 5553d78b56..ff26d87af9 100644 --- a/lib/features/login/data/model/account_cache.dart +++ b/lib/features/login/data/model/account_cache.dart @@ -1,6 +1,8 @@ import 'package:equatable/equatable.dart'; import 'package:hive/hive.dart'; import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; +import 'package:tmail_ui_user/features/login/data/model/basic_auth_cache.dart'; +import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; part 'account_cache.g.dart'; @@ -24,24 +26,37 @@ class AccountCache extends HiveObject with EquatableMixin { @HiveField(5) final String? userName; - AccountCache( - this.id, - this.authenticationType, - { - required this.isSelected, - this.accountId, - this.apiUrl, - this.userName - } - ); + @HiveField(6) + final String baseUrl; + + @HiveField(7) + final BasicAuthCache? basicAuth; + + @HiveField(8) + final TokenOidcCache? tokenOidc; + + AccountCache({ + required this.id, + required this.authenticationType, + required this.isSelected, + required this.baseUrl, + this.accountId, + this.apiUrl, + this.userName, + this.basicAuth, + this.tokenOidc, + }); @override List get props => [ id, authenticationType, isSelected, + baseUrl, accountId, apiUrl, - userName + userName, + basicAuth, + tokenOidc, ]; } \ No newline at end of file diff --git a/lib/features/login/data/model/authentication_info_cache.dart b/lib/features/login/data/model/authentication_info_cache.dart deleted file mode 100644 index 6dd9ac7b9d..0000000000 --- a/lib/features/login/data/model/authentication_info_cache.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:hive/hive.dart'; -import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; - -part 'authentication_info_cache.g.dart'; - -@HiveType(typeId: CachingConstants.AUTHENTICATION_INFO_HIVE_CACHE_IDENTIFY) -class AuthenticationInfoCache extends HiveObject with EquatableMixin { - - static const String keyCacheValue = 'authenticationInfoCache'; - - @HiveField(0) - final String username; - - @HiveField(1) - final String password; - - AuthenticationInfoCache(this.username, this.password); - - @override - List get props => [username, password]; -} \ No newline at end of file diff --git a/lib/features/login/data/model/basic_auth_cache.dart b/lib/features/login/data/model/basic_auth_cache.dart new file mode 100644 index 0000000000..55dfa23772 --- /dev/null +++ b/lib/features/login/data/model/basic_auth_cache.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:model/account/basic_auth.dart'; +import 'package:model/account/password.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; + +part 'basic_auth_cache.g.dart'; + +@HiveType(typeId: CachingConstants.BASIC_AUTH_HIVE_CACHE_IDENTIFY) +class BasicAuthCache extends HiveObject with EquatableMixin { + + @HiveField(0) + final String username; + + @HiveField(1) + final String password; + + BasicAuthCache(this.username, this.password); + + @override + List get props => [username, password]; +} + +extension BasicAuthCacheExtension on BasicAuthCache { + BasicAuth toBasicAuth() => BasicAuth(UserName(username), Password(password)); +} \ No newline at end of file diff --git a/lib/features/login/data/model/token_oidc_cache.dart b/lib/features/login/data/model/token_oidc_cache.dart index 26b738bf91..496eabc21a 100644 --- a/lib/features/login/data/model/token_oidc_cache.dart +++ b/lib/features/login/data/model/token_oidc_cache.dart @@ -1,5 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:hive/hive.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; part 'token_oidc_cache.g.dart'; @@ -19,8 +21,34 @@ class TokenOidcCache extends HiveObject with EquatableMixin { @HiveField(3) final String refreshToken; - TokenOidcCache(this.token, this.tokenId, this.refreshToken, {this.expiredTime}); + @HiveField(4) + final String authority; + + TokenOidcCache({ + required this.token, + required this.tokenId, + required this.refreshToken, + required this.authority, + this.expiredTime + }); @override - List get props => [token, tokenId, expiredTime, refreshToken]; + List get props => [ + tokenId, + token, + refreshToken, + expiredTime, + authority, + ]; +} + +extension TokenOidcCacheExtension on TokenOidcCache { + TokenOIDC toTokenOIDC() => + TokenOIDC( + token: token, + tokenId: TokenId(tokenId), + refreshToken: refreshToken, + authority: authority, + expiredTime: expiredTime + ); } \ No newline at end of file diff --git a/lib/features/login/data/network/authentication_client/authentication_client_base.dart b/lib/features/login/data/network/authentication_client/authentication_client_base.dart index a1a097644b..d0d00a195a 100644 --- a/lib/features/login/data/network/authentication_client/authentication_client_base.dart +++ b/lib/features/login/data/network/authentication_client/authentication_client_base.dart @@ -7,28 +7,15 @@ import 'package:tmail_ui_user/features/login/data/network/authentication_client/ if (dart.library.html) 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_web.dart'; abstract class AuthenticationClientBase { - Future authenticateOidcOnBrowser( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes); + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration); - Future getAuthenticationInfo(); + Future getAuthResponseUrlBrowser(); - Future getTokenOIDC( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes); + Future getTokenOIDC(OIDCConfiguration oidcConfiguration); - Future refreshingTokensOIDC( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes, - String refreshToken); + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken); - Future logoutOidc(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery); + Future logoutOidc(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcDiscoveryResponse); factory AuthenticationClientBase({String? tag}) => getAuthenticationClientImplementation(tag: tag); } \ No newline at end of file diff --git a/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart b/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart index f22968b8a1..2fa1826fcd 100644 --- a/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart +++ b/lib/features/login/data/network/authentication_client/authentication_client_mobile.dart @@ -1,5 +1,4 @@ -import 'package:core/utils/app_logger.dart'; import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:get/get.dart'; import 'package:model/oidc/oidc_configuration.dart'; @@ -7,6 +6,7 @@ import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:model/oidc/token_id.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/login/data/extensions/authentication_token_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/extensions/token_response_extension.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; @@ -19,20 +19,19 @@ class AuthenticationClientMobile implements AuthenticationClientBase { AuthenticationClientMobile(this._appAuth); @override - Future getTokenOIDC(String clientId, String redirectUrl, - String discoveryUrl, List scopes) async { + Future getTokenOIDC(OIDCConfiguration oidcConfiguration) async { final authorizationTokenResponse = await _appAuth.authorizeAndExchangeCode( - AuthorizationTokenRequest( - clientId, - redirectUrl, - discoveryUrl: discoveryUrl, - scopes: scopes, - preferEphemeralSession: true)); - - log('AuthenticationClientMobile::getTokenOIDC(): token: ${authorizationTokenResponse?.accessToken}'); + AuthorizationTokenRequest( + oidcConfiguration.clientId, + oidcConfiguration.redirectUrl, + discoveryUrl: oidcConfiguration.discoveryUrl, + scopes: oidcConfiguration.scopes, + preferEphemeralSession: true + ) + ); if (authorizationTokenResponse != null) { - final tokenOIDC = authorizationTokenResponse.toTokenOIDC(); + final tokenOIDC = authorizationTokenResponse.toTokenOIDC(authority: oidcConfiguration.authority); if (tokenOIDC.isTokenValid()) { return tokenOIDC; } else { @@ -44,38 +43,46 @@ class AuthenticationClientMobile implements AuthenticationClientBase { } @override - Future logoutOidc(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) async { - final authorizationServiceConfiguration = oidcRescovery.authorizationEndpoint == null || oidcRescovery.tokenEndpoint == null + Future logoutOidc( + TokenId tokenId, + OIDCConfiguration config, + OIDCDiscoveryResponse oidcDiscoveryResponse + ) async { + final authorizationServiceConfiguration = oidcDiscoveryResponse.authorizationEndpoint == null || oidcDiscoveryResponse.tokenEndpoint == null ? null : AuthorizationServiceConfiguration( - authorizationEndpoint: oidcRescovery.authorizationEndpoint!, - tokenEndpoint: oidcRescovery.tokenEndpoint!, - endSessionEndpoint: oidcRescovery.endSessionEndpoint); + authorizationEndpoint: oidcDiscoveryResponse.authorizationEndpoint!, + tokenEndpoint: oidcDiscoveryResponse.tokenEndpoint!, + endSessionEndpoint: oidcDiscoveryResponse.endSessionEndpoint); - final endSession = await _appAuth.endSession(EndSessionRequest( + final endSession = await _appAuth.endSession( + EndSessionRequest( idTokenHint: tokenId.uuid, postLogoutRedirectUrl: config.logoutRedirectUrl, discoveryUrl: config.discoveryUrl, serviceConfiguration: authorizationServiceConfiguration - )); - log('AuthenticationClientMobile::logoutOidc(): ${endSession?.state}'); + ) + ); return endSession?.state?.isNotEmpty == true; } @override - Future refreshingTokensOIDC(String clientId, String redirectUrl, - String discoveryUrl, List scopes, String refreshToken) async { - final tokenResponse = await _appAuth.token(TokenRequest( - clientId, - redirectUrl, - discoveryUrl: discoveryUrl, + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken) async { + final tokenResponse = await _appAuth.token( + TokenRequest( + oidcConfiguration.clientId, + oidcConfiguration.redirectUrl, + discoveryUrl: oidcConfiguration.discoveryUrl, refreshToken: refreshToken, - scopes: scopes)); - - log('AuthenticationClientMobile::refreshingTokensOIDC(): refreshToken: ${tokenResponse?.accessToken}'); + scopes: oidcConfiguration.scopes + ) + ); if (tokenResponse != null) { - final tokenOIDC = tokenResponse.toTokenOIDC(maybeAvailableRefreshToken: refreshToken); + final tokenOIDC = tokenResponse.toTokenOIDC( + authority: oidcConfiguration.authority, + maybeAvailableRefreshToken: refreshToken + ); if (tokenOIDC.isTokenValid()) { return tokenOIDC; } else { @@ -87,14 +94,13 @@ class AuthenticationClientMobile implements AuthenticationClientBase { } @override - Future authenticateOidcOnBrowser(String clientId, String redirectUrl, - String discoveryUrl, List scopes) { - return Future.value(null); + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration) { + throw UnimplementedError(); } @override - Future getAuthenticationInfo() { - return Future.value(''); + Future getAuthResponseUrlBrowser() { + throw UnimplementedError(); } } diff --git a/lib/features/login/data/network/authentication_client/authentication_client_web.dart b/lib/features/login/data/network/authentication_client/authentication_client_web.dart index 463f79a11e..938a315416 100644 --- a/lib/features/login/data/network/authentication_client/authentication_client_web.dart +++ b/lib/features/login/data/network/authentication_client/authentication_client_web.dart @@ -1,5 +1,4 @@ -import 'package:core/utils/app_logger.dart'; import 'package:flutter_appauth_platform_interface/flutter_appauth_platform_interface.dart'; import 'package:get/get.dart'; import 'package:model/oidc/oidc_configuration.dart'; @@ -7,6 +6,7 @@ import 'package:model/oidc/response/oidc_discovery_response.dart'; import 'package:model/oidc/token_id.dart'; import 'package:model/oidc/token_oidc.dart'; import 'package:tmail_ui_user/features/login/data/extensions/authentication_token_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/extensions/token_response_extension.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart'; @@ -22,19 +22,19 @@ class AuthenticationClientWeb implements AuthenticationClientBase { AuthenticationClientWeb(this._appAuthWeb); @override - Future getTokenOIDC(String clientId, String redirectUrl, - String discoveryUrl, List scopes) async { - final authorizationTokenResponse = await _appAuthWeb.authorizeAndExchangeCode(AuthorizationTokenRequest( - clientId, - redirectUrl, - discoveryUrl: discoveryUrl, - scopes: scopes, - preferEphemeralSession: true)); - - log('AuthClientMobile::getTokenOIDC(): token: ${authorizationTokenResponse?.accessToken}'); + Future getTokenOIDC(OIDCConfiguration oidcConfiguration) async { + final authorizationTokenResponse = await _appAuthWeb.authorizeAndExchangeCode( + AuthorizationTokenRequest( + oidcConfiguration.clientId, + oidcConfiguration.redirectUrl, + discoveryUrl: oidcConfiguration.discoveryUrl, + scopes: oidcConfiguration.scopes, + preferEphemeralSession: true + ) + ); if (authorizationTokenResponse != null) { - final tokenOIDC = authorizationTokenResponse.toTokenOIDC(); + final tokenOIDC = authorizationTokenResponse.toTokenOIDC(authority: oidcConfiguration.authority); if (tokenOIDC.isTokenValid()) { return tokenOIDC; } else { @@ -63,18 +63,23 @@ class AuthenticationClientWeb implements AuthenticationClientBase { } @override - Future refreshingTokensOIDC(String clientId, String redirectUrl, - String discoveryUrl, List scopes, String refreshToken) async { - final tokenResponse = await _appAuthWeb.token(TokenRequest( - clientId, - redirectUrl, - discoveryUrl: discoveryUrl, + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken) async { + final tokenResponse = await _appAuthWeb.token( + TokenRequest( + oidcConfiguration.clientId, + oidcConfiguration.redirectUrl, + discoveryUrl: oidcConfiguration.discoveryUrl, refreshToken: refreshToken, - grantType: 'refresh_token', - scopes: scopes)); + grantType: OIDCConstant.refreshTokenGrantType, + scopes: oidcConfiguration.scopes + ) + ); if (tokenResponse != null) { - final tokenOIDC = tokenResponse.toTokenOIDC(maybeAvailableRefreshToken: refreshToken); + final tokenOIDC = tokenResponse.toTokenOIDC( + authority: oidcConfiguration.authority, + maybeAvailableRefreshToken: refreshToken + ); if (tokenOIDC.isTokenValid()) { return tokenOIDC; } else { @@ -86,24 +91,24 @@ class AuthenticationClientWeb implements AuthenticationClientBase { } @override - Future authenticateOidcOnBrowser(String clientId, String redirectUrl, - String discoveryUrl, List scopes) async { + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration) async { await _appAuthWeb.authorizeAndExchangeCode( - AuthorizationTokenRequest( - clientId, - redirectUrl, - discoveryUrl: discoveryUrl, - scopes: scopes)); + AuthorizationTokenRequest( + oidcConfiguration.clientId, + oidcConfiguration.redirectUrl, + discoveryUrl: oidcConfiguration.discoveryUrl, + scopes: oidcConfiguration.scopes + ) + ); } @override - Future getAuthenticationInfo() async { + Future getAuthResponseUrlBrowser() async { final authUrl = html.window.sessionStorage[OIDCConstant.authResponseKey]; - log('AuthenticationClientWeb::getAuthenticationInfo(): authUrl: $authUrl'); - if (authUrl != null && authUrl.isNotEmpty) { - return authUrl; + if (authUrl?.isNotEmpty == true) { + return authUrl!; } else { - throw CanNotAuthenticationInfoOnWeb(); + throw NotFoundAuthResponseUrlBrowser(); } } } diff --git a/lib/features/login/data/network/config/oidc_constant.dart b/lib/features/login/data/network/config/oidc_constant.dart index 8940791367..b632913412 100644 --- a/lib/features/login/data/network/config/oidc_constant.dart +++ b/lib/features/login/data/network/config/oidc_constant.dart @@ -5,7 +5,8 @@ class OIDCConstant { static String get mobileOidcClientId => 'teammail-mobile'; static List get oidcScope => ['openid', 'profile', 'email', 'offline_access']; static const keyAuthorityOidc = 'KEY_AUTHORITY_OIDC'; - static const authResponseKey = "auth_info"; + static const authResponseKey = 'auth_info'; + static const refreshTokenGrantType = 'refresh_token'; static String get clientId => PlatformInfo.isWeb ? AppConfig.webOidcClientId : mobileOidcClientId; } \ No newline at end of file diff --git a/lib/features/login/data/network/interceptors/authorization_interceptors.dart b/lib/features/login/data/network/interceptors/authorization_interceptors.dart index 34d21b5db6..87a454e68f 100644 --- a/lib/features/login/data/network/interceptors/authorization_interceptors.dart +++ b/lib/features/login/data/network/interceptors/authorization_interceptors.dart @@ -11,10 +11,10 @@ import 'package:model/account/password.dart'; import 'package:model/account/personal_account.dart'; import 'package:model/oidc/oidc_configuration.dart'; import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/personal_account_extension.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart'; import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart'; @@ -22,7 +22,6 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { final Dio _dio; final AuthenticationClientBase _authenticationClient; - final TokenOidcCacheManager _tokenOidcCacheManager; final AccountCacheManager _accountCacheManager; final IOSSharingManager _iosSharingManager; @@ -34,7 +33,6 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { AuthorizationInterceptors( this._dio, this._authenticationClient, - this._tokenOidcCacheManager, this._accountCacheManager, this._iosSharingManager, ); @@ -211,22 +209,12 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { Future _updateCurrentAccount({required TokenOIDC tokenOIDC}) async { final currentAccount = await _accountCacheManager.getCurrentAccount(); + final newAccount = currentAccount.updateToken(tokenOIDC); await _accountCacheManager.deleteCurrentAccount(currentAccount.id); + await _accountCacheManager.setCurrentAccount(newAccount); - await _tokenOidcCacheManager.persistOneTokenOidc(tokenOIDC); - - final personalAccount = PersonalAccount( - tokenOIDC.tokenIdHash, - AuthenticationType.oidc, - isSelected: true, - accountId: currentAccount.accountId, - apiUrl: currentAccount.apiUrl, - userName: currentAccount.userName - ); - await _accountCacheManager.setCurrentAccount(personalAccount); - - return personalAccount; + return newAccount; } Future _getTokenInKeychain(TokenOIDC currentTokenOidc) async { @@ -251,13 +239,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper { } Future _invokeRefreshTokenFromServer() async { - final newToken = await _authenticationClient.refreshingTokensOIDC( - _configOIDC!.clientId, - _configOIDC!.redirectUrl, - _configOIDC!.discoveryUrl, - _configOIDC!.scopes, - _token!.refreshToken - ); + final newToken = await _authenticationClient.refreshingTokensOIDC(_configOIDC!, _token!.refreshToken); log('AuthorizationInterceptors::_invokeRefreshTokenFromServer:newToken: $newToken'); return newToken; } diff --git a/lib/features/login/data/repository/authentication_oidc_repository_impl.dart b/lib/features/login/data/repository/authentication_oidc_repository_impl.dart index 1d79b8cfbf..10fd84a786 100644 --- a/lib/features/login/data/repository/authentication_oidc_repository_impl.dart +++ b/lib/features/login/data/repository/authentication_oidc_repository_impl.dart @@ -17,81 +17,33 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository { return _oidcDataSource.checkOIDCIsAvailable(oidcRequest); } - @override - Future getOIDCConfiguration(OIDCResponse oidcResponse) { - return _oidcDataSource.getOIDCConfiguration(oidcResponse); - } - - @override Future discoverOIDC(OIDCConfiguration oidcConfiguration) { return _oidcDataSource.discoverOIDC(oidcConfiguration); } @override - Future getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List scopes) { - return _oidcDataSource.getTokenOIDC(clientId, redirectUrl, discoveryUrl, scopes); - } - - @override - Future getStoredTokenOIDC(String tokenIdHash) { - return _oidcDataSource.getStoredTokenOIDC(tokenIdHash); - } - - @override - Future persistTokenOIDC(TokenOIDC tokenOidc) { - return _oidcDataSource.persistTokenOIDC(tokenOidc); - } - - @override - Future getStoredOidcConfiguration() { - return _oidcDataSource.getStoredOidcConfiguration(); - } - - @override - Future persistAuthorityOidc(String authority) { - return _oidcDataSource.persistAuthorityOidc(authority); - } - - @override - Future refreshingTokensOIDC( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes, - String refreshToken - ) { - return _oidcDataSource.refreshingTokensOIDC( - clientId, - redirectUrl, - discoveryUrl, - scopes, - refreshToken); - } - - @override - Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery) { - return _oidcDataSource.logout(tokenId, config, oidcRescovery); + Future getTokenOIDC(OIDCConfiguration oidcConfiguration) { + return _oidcDataSource.getTokenOIDC(oidcConfiguration); } @override - Future deleteAuthorityOidc() { - return _oidcDataSource.deleteAuthorityOidc(); + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken) { + return _oidcDataSource.refreshingTokensOIDC(oidcConfiguration, refreshToken); } @override - Future authenticateOidcOnBrowser(String clientId, - String redirectUrl, String discoveryUrl, List scopes) { - return _oidcDataSource.authenticateOidcOnBrowser(clientId, redirectUrl, discoveryUrl, scopes); + Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcDiscoveryResponse) { + return _oidcDataSource.logout(tokenId, config, oidcDiscoveryResponse); } @override - Future getAuthenticationInfo() { - return _oidcDataSource.getAuthenticationInfo(); + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration) { + return _oidcDataSource.authenticateOidcOnBrowser(oidcConfiguration); } @override - Future deleteTokenOIDC() { - return _oidcDataSource.deleteTokenOIDC(); + Future getAuthResponseUrlBrowser() { + return _oidcDataSource.getAuthResponseUrlBrowser(); } } \ No newline at end of file diff --git a/lib/features/login/data/repository/credential_repository_impl.dart b/lib/features/login/data/repository/credential_repository_impl.dart deleted file mode 100644 index 283d542068..0000000000 --- a/lib/features/login/data/repository/credential_repository_impl.dart +++ /dev/null @@ -1,47 +0,0 @@ - -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; -import 'package:tmail_ui_user/features/login/domain/model/login_constants.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; - -class CredentialRepositoryImpl extends CredentialRepository { - - final SharedPreferences sharedPreferences; - final AuthenticationInfoCacheManager _authenticationInfoCacheManager; - - CredentialRepositoryImpl( - this.sharedPreferences, - this._authenticationInfoCacheManager - ); - - @override - Future getBaseUrl() async { - return Uri.parse(sharedPreferences.getString(LoginConstants.KEY_BASE_URL) ?? ''); - } - - @override - Future saveBaseUrl(Uri baseUrl) async { - await sharedPreferences.setString(LoginConstants.KEY_BASE_URL, baseUrl.toString()); - } - - @override - Future removeBaseUrl() async { - await sharedPreferences.remove(LoginConstants.KEY_BASE_URL); - } - - @override - Future storeAuthenticationInfo(AuthenticationInfoCache authenticationInfoCache) { - return _authenticationInfoCacheManager.storeAuthenticationInfo(authenticationInfoCache); - } - - @override - Future getAuthenticationInfoStored() { - return _authenticationInfoCacheManager.getAuthenticationInfoStored(); - } - - @override - Future removeAuthenticationInfo() { - return _authenticationInfoCacheManager.removeAuthenticationInfo(); - } -} \ No newline at end of file diff --git a/lib/features/login/domain/exceptions/authentication_exception.dart b/lib/features/login/domain/exceptions/authentication_exception.dart index a03da51e18..926d23f34b 100644 --- a/lib/features/login/domain/exceptions/authentication_exception.dart +++ b/lib/features/login/domain/exceptions/authentication_exception.dart @@ -25,8 +25,6 @@ class BadGateway extends AuthenticationException { class NotFoundAuthenticatedAccountException implements Exception {} -class NotFoundStoredTokenException implements Exception {} - class InvalidBaseUrl extends AuthenticationException { InvalidBaseUrl() : super(AuthenticationException.invalidBaseUrl); @@ -51,6 +49,6 @@ class CanNotFoundUserName implements Exception {} class CanNotFoundPassword implements Exception {} -class CanNotAuthenticationInfoOnWeb implements Exception {} +class NotFoundAuthResponseUrlBrowser implements Exception {} -class NotFoundAuthenticationInfoCache implements Exception {} \ No newline at end of file +class AuthenticationTypeNotSupportedException implements Exception {} \ No newline at end of file diff --git a/lib/features/login/domain/model/login_constants.dart b/lib/features/login/domain/model/login_constants.dart index ee177b3196..07206bf90e 100644 --- a/lib/features/login/domain/model/login_constants.dart +++ b/lib/features/login/domain/model/login_constants.dart @@ -1,5 +1,4 @@ class LoginConstants { - static const String KEY_BASE_URL = 'KEY_BASE_URL'; static const String AUTH_DESTINATION_KEY = "auth_destination_url"; } \ No newline at end of file diff --git a/lib/features/login/domain/repository/authentication_oidc_repository.dart b/lib/features/login/domain/repository/authentication_oidc_repository.dart index 11fa3fee3d..f321555474 100644 --- a/lib/features/login/domain/repository/authentication_oidc_repository.dart +++ b/lib/features/login/domain/repository/authentication_oidc_repository.dart @@ -1,41 +1,23 @@ -import 'package:model/model.dart'; +import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/request/oidc_request.dart'; +import 'package:model/oidc/response/oidc_discovery_response.dart'; +import 'package:model/oidc/response/oidc_response.dart'; +import 'package:model/oidc/token_id.dart'; +import 'package:model/oidc/token_oidc.dart'; abstract class AuthenticationOIDCRepository { Future checkOIDCIsAvailable(OIDCRequest oidcRequest); - Future getOIDCConfiguration(OIDCResponse oidcResponse); - Future discoverOIDC(OIDCConfiguration oidcConfiguration); - Future getTokenOIDC(String clientId, String redirectUrl, String discoveryUrl, List scopes); - - Future persistTokenOIDC(TokenOIDC tokenOidc); - - Future deleteTokenOIDC(); - - Future getStoredTokenOIDC(String tokenIdHash); - - Future persistAuthorityOidc(String authority); - - Future deleteAuthorityOidc(); - - Future getStoredOidcConfiguration(); + Future getTokenOIDC(OIDCConfiguration oidcConfiguration); - Future refreshingTokensOIDC( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes, - String refreshToken); + Future refreshingTokensOIDC(OIDCConfiguration oidcConfiguration, String refreshToken); - Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcRescovery); + Future logout(TokenId tokenId, OIDCConfiguration config, OIDCDiscoveryResponse oidcDiscoveryResponse); - Future authenticateOidcOnBrowser( - String clientId, - String redirectUrl, - String discoveryUrl, - List scopes); + Future authenticateOidcOnBrowser(OIDCConfiguration oidcConfiguration); - Future getAuthenticationInfo(); + Future getAuthResponseUrlBrowser(); } \ No newline at end of file diff --git a/lib/features/login/domain/repository/credential_repository.dart b/lib/features/login/domain/repository/credential_repository.dart deleted file mode 100644 index bf5deb6036..0000000000 --- a/lib/features/login/domain/repository/credential_repository.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; - -abstract class CredentialRepository { - Future saveBaseUrl(Uri baseUrl); - - Future removeBaseUrl(); - - Future getBaseUrl(); - - Future storeAuthenticationInfo(AuthenticationInfoCache authenticationInfoCache); - - Future getAuthenticationInfoStored(); - - Future removeAuthenticationInfo(); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/authenticate_oidc_on_browser_state.dart b/lib/features/login/domain/state/authenticate_oidc_on_browser_state.dart index d3fdd58c25..f6f0f24a7c 100644 --- a/lib/features/login/domain/state/authenticate_oidc_on_browser_state.dart +++ b/lib/features/login/domain/state/authenticate_oidc_on_browser_state.dart @@ -1,9 +1,18 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:model/oidc/oidc_configuration.dart'; class AuthenticateOidcOnBrowserLoading extends LoadingState {} -class AuthenticateOidcOnBrowserSuccess extends UIState {} +class AuthenticateOidcOnBrowserSuccess extends UIState { + final String baseUrl; + final OIDCConfiguration oidcConfiguration; + + AuthenticateOidcOnBrowserSuccess(this.baseUrl, this.oidcConfiguration); + + @override + List get props => [baseUrl, oidcConfiguration]; +} class AuthenticateOidcOnBrowserFailure extends FeatureFailure { diff --git a/lib/features/login/domain/state/check_oidc_is_available_state.dart b/lib/features/login/domain/state/check_oidc_is_available_state.dart index f826c3e8f8..d526f187b7 100644 --- a/lib/features/login/domain/state/check_oidc_is_available_state.dart +++ b/lib/features/login/domain/state/check_oidc_is_available_state.dart @@ -6,11 +6,12 @@ class CheckOIDCIsAvailableLoading extends LoadingState {} class CheckOIDCIsAvailableSuccess extends UIState { final OIDCResponse oidcResponse; + final String baseUrl; - CheckOIDCIsAvailableSuccess(this.oidcResponse); + CheckOIDCIsAvailableSuccess(this.oidcResponse, this.baseUrl); @override - List get props => [oidcResponse]; + List get props => [oidcResponse, baseUrl]; } class CheckOIDCIsAvailableFailure extends FeatureFailure { diff --git a/lib/features/login/domain/state/get_auth_response_url_browser_state.dart b/lib/features/login/domain/state/get_auth_response_url_browser_state.dart new file mode 100644 index 0000000000..65e3b53351 --- /dev/null +++ b/lib/features/login/domain/state/get_auth_response_url_browser_state.dart @@ -0,0 +1,15 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class GetAuthResponseUrlBrowserLoading extends LoadingState {} + +class GetAuthResponseUrlBrowserSuccess extends UIState { + final String authResponseUrl; + + GetAuthResponseUrlBrowserSuccess(this.authResponseUrl); +} + +class GetAuthResponseUrlBrowserFailure extends FeatureFailure { + + GetAuthResponseUrlBrowserFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_authenticated_account_state.dart b/lib/features/login/domain/state/get_authenticated_account_state.dart deleted file mode 100644 index 89319696f5..0000000000 --- a/lib/features/login/domain/state/get_authenticated_account_state.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:model/account/personal_account.dart'; - -class GetAuthenticatedAccountSuccess extends UIState { - final PersonalAccount account; - - GetAuthenticatedAccountSuccess(this.account); - - @override - List get props => [account]; -} - -class GetAuthenticatedAccountFailure extends FeatureFailure { - - GetAuthenticatedAccountFailure(dynamic exception) : super(exception: exception); -} diff --git a/lib/features/login/domain/state/get_authentication_info_state.dart b/lib/features/login/domain/state/get_authentication_info_state.dart deleted file mode 100644 index a9310c3ffc..0000000000 --- a/lib/features/login/domain/state/get_authentication_info_state.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; - -class GetAuthenticationInfoLoading extends LoadingState {} - -class GetAuthenticationInfoSuccess extends UIState {} - -class GetAuthenticationInfoFailure extends FeatureFailure { - - GetAuthenticationInfoFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_credential_state.dart b/lib/features/login/domain/state/get_credential_state.dart deleted file mode 100644 index fe2523833b..0000000000 --- a/lib/features/login/domain/state/get_credential_state.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; -import 'package:model/account/password.dart'; -import 'package:model/account/personal_account.dart'; - -class GetCredentialViewState extends UIState { - final Uri baseUrl; - final UserName userName; - final Password password; - final PersonalAccount personalAccount; - final StateChange? stateChange; - - GetCredentialViewState( - this.baseUrl, - this.userName, - this.password, - this.personalAccount, - {this.stateChange}); - - @override - List get props => [ - baseUrl, - userName, - password, - personalAccount, - stateChange]; -} - -class GetCredentialFailure extends FeatureFailure { - - GetCredentialFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_current_account_cache_state.dart b/lib/features/login/domain/state/get_current_account_cache_state.dart new file mode 100644 index 0000000000..48fe9838cd --- /dev/null +++ b/lib/features/login/domain/state/get_current_account_cache_state.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/push/state_change.dart'; +import 'package:model/account/personal_account.dart'; + +class GetCurrentAccountCacheLoading extends LoadingState {} + +class GetCurrentAccountCacheSuccess extends UIState { + final PersonalAccount account; + final StateChange? stateChange; + + GetCurrentAccountCacheSuccess(this.account, this.stateChange); + + @override + List get props => [account]; +} + +class GetCurrentAccountCacheFailure extends FeatureFailure { + + GetCurrentAccountCacheFailure(dynamic exception) : super(exception: exception); +} diff --git a/lib/features/login/domain/state/get_oidc_configuration_state.dart b/lib/features/login/domain/state/get_oidc_configuration_state.dart index af9f16b982..e78a95af72 100644 --- a/lib/features/login/domain/state/get_oidc_configuration_state.dart +++ b/lib/features/login/domain/state/get_oidc_configuration_state.dart @@ -1,19 +1,8 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:model/oidc/oidc_configuration.dart'; class GetOIDCConfigurationLoading extends LoadingState {} -class GetOIDCConfigurationSuccess extends UIState { - - final OIDCConfiguration oidcConfiguration; - - GetOIDCConfigurationSuccess(this.oidcConfiguration); - - @override - List get props => [oidcConfiguration]; -} - class GetOIDCConfigurationFailure extends FeatureFailure { GetOIDCConfigurationFailure(dynamic exception) : super(exception: exception); diff --git a/lib/features/login/domain/state/get_oidc_is_available_state.dart b/lib/features/login/domain/state/get_oidc_is_available_state.dart deleted file mode 100644 index 16ac225cfd..0000000000 --- a/lib/features/login/domain/state/get_oidc_is_available_state.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:model/oidc/response/oidc_response.dart'; - -class GetOIDCIsAvailableLoading extends LoadingState {} - -class GetOIDCIsAvailableSuccess extends UIState { - final OIDCResponse oidcResponse; - - GetOIDCIsAvailableSuccess(this.oidcResponse); - - @override - List get props => [oidcResponse]; -} - -class GetOIDCIsAvailableFailure extends FeatureFailure { - - GetOIDCIsAvailableFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_stored_oidc_configuration_state.dart b/lib/features/login/domain/state/get_stored_oidc_configuration_state.dart deleted file mode 100644 index 9f739fdaca..0000000000 --- a/lib/features/login/domain/state/get_stored_oidc_configuration_state.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:model/oidc/oidc_configuration.dart'; - -class GetStoredOidcConfigurationLoading extends LoadingState {} - -class GetStoredOidcConfigurationSuccess extends UIState { - final OIDCConfiguration oidcConfiguration; - - GetStoredOidcConfigurationSuccess(this.oidcConfiguration); - - @override - List get props => [oidcConfiguration]; -} - -class GetStoredOidcConfigurationFailure extends FeatureFailure { - - GetStoredOidcConfigurationFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_stored_token_oidc_state.dart b/lib/features/login/domain/state/get_stored_token_oidc_state.dart deleted file mode 100644 index 18236452b5..0000000000 --- a/lib/features/login/domain/state/get_stored_token_oidc_state.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; -import 'package:model/account/personal_account.dart'; -import 'package:model/oidc/oidc_configuration.dart'; -import 'package:model/oidc/token_oidc.dart'; - -class GetStoredTokenOidcSuccess extends UIState { - final Uri baseUrl; - final TokenOIDC tokenOidc; - final OIDCConfiguration oidcConfiguration; - final PersonalAccount personalAccount; - final StateChange? stateChange; - - GetStoredTokenOidcSuccess( - this.baseUrl, - this.tokenOidc, - this.oidcConfiguration, - this.personalAccount, - {this.stateChange} - ); - - @override - List get props => [ - baseUrl, - tokenOidc, - oidcConfiguration, - personalAccount, - stateChange]; -} - -class GetStoredTokenOidcFailure extends FeatureFailure { - - GetStoredTokenOidcFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/get_token_oidc_state.dart b/lib/features/login/domain/state/get_token_oidc_state.dart index 53d72d9412..695b3630d6 100644 --- a/lib/features/login/domain/state/get_token_oidc_state.dart +++ b/lib/features/login/domain/state/get_token_oidc_state.dart @@ -1,19 +1,17 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:model/oidc/oidc_configuration.dart'; -import 'package:model/oidc/token_oidc.dart'; +import 'package:model/account/personal_account.dart'; class GetTokenOIDCLoading extends LoadingState {} class GetTokenOIDCSuccess extends UIState { - final TokenOIDC tokenOIDC; - final OIDCConfiguration configuration; + final PersonalAccount personalAccount; - GetTokenOIDCSuccess(this.tokenOIDC, this.configuration); + GetTokenOIDCSuccess(this.personalAccount); @override - List get props => [tokenOIDC, configuration]; + List get props => [personalAccount]; } class GetTokenOIDCFailure extends FeatureFailure { diff --git a/lib/features/login/domain/state/logout_current_account_basic_auth_state.dart b/lib/features/login/domain/state/logout_current_account_basic_auth_state.dart new file mode 100644 index 0000000000..edeaa2367d --- /dev/null +++ b/lib/features/login/domain/state/logout_current_account_basic_auth_state.dart @@ -0,0 +1,24 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:model/account/personal_account.dart'; + +class LogoutCurrentAccountBasicAuthLoading extends LoadingState {} + +class LogoutCurrentAccountBasicAuthSuccess extends UIState { + final PersonalAccount deletedAccount; + + LogoutCurrentAccountBasicAuthSuccess(this.deletedAccount); + + @override + List get props => [deletedAccount]; +} + +class LogoutCurrentAccountBasicAuthFailure extends FeatureFailure { + + final PersonalAccount deletedAccount; + + LogoutCurrentAccountBasicAuthFailure({dynamic exception, required this.deletedAccount}) : super(exception: exception); + + @override + List get props => [...super.props, deletedAccount]; +} diff --git a/lib/features/login/domain/state/logout_current_account_oidc_state.dart b/lib/features/login/domain/state/logout_current_account_oidc_state.dart new file mode 100644 index 0000000000..3e28b0e995 --- /dev/null +++ b/lib/features/login/domain/state/logout_current_account_oidc_state.dart @@ -0,0 +1,25 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:model/account/personal_account.dart'; + +class LogoutCurrentAccountOidcLoading extends LoadingState {} + +class LogoutCurrentAccountOidcSuccess extends UIState { + final PersonalAccount deletedAccount; + + LogoutCurrentAccountOidcSuccess(this.deletedAccount); + + @override + List get props => [deletedAccount]; +} + +class LogoutCurrentAccountOidcFailure extends FeatureFailure { + + final PersonalAccount deletedAccount; + + LogoutCurrentAccountOidcFailure({dynamic exception, required this.deletedAccount}) : super(exception: exception); + + @override + List get props => [...super.props, deletedAccount]; +} \ No newline at end of file diff --git a/lib/features/login/domain/state/logout_current_account_state.dart b/lib/features/login/domain/state/logout_current_account_state.dart new file mode 100644 index 0000000000..a0ef382f57 --- /dev/null +++ b/lib/features/login/domain/state/logout_current_account_state.dart @@ -0,0 +1,15 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:model/account/personal_account.dart'; + +class LogoutCurrentAccountLoading extends LoadingState {} + +class LogoutCurrentAccountFailure extends FeatureFailure { + + final PersonalAccount? deletedAccount; + + LogoutCurrentAccountFailure({dynamic exception, this.deletedAccount}) : super(exception: exception); + + @override + List get props => [...super.props, deletedAccount]; +} diff --git a/lib/features/login/domain/state/update_authentication_account_state.dart b/lib/features/login/domain/state/update_authentication_account_state.dart deleted file mode 100644 index c5d1931edf..0000000000 --- a/lib/features/login/domain/state/update_authentication_account_state.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; - -class UpdateAuthenticationAccountLoading extends LoadingState {} - -class UpdateAuthenticationAccountSuccess extends UIState {} - -class UpdateAuthenticationAccountFailure extends FeatureFailure { - - UpdateAuthenticationAccountFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/login/domain/state/update_current_account_cache_state.dart b/lib/features/login/domain/state/update_current_account_cache_state.dart new file mode 100644 index 0000000000..5f0594edad --- /dev/null +++ b/lib/features/login/domain/state/update_current_account_cache_state.dart @@ -0,0 +1,20 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; + +class UpdateCurrentAccountCacheLoading extends LoadingState {} + +class UpdateCurrentAccountCacheSuccess extends UIState { + final Session session; + final String apiUrl; + + UpdateCurrentAccountCacheSuccess({required this.session, required this.apiUrl}); + + @override + List get props => [session, apiUrl]; +} + +class UpdateCurrentAccountCacheFailure extends FeatureFailure { + + UpdateCurrentAccountCacheFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart b/lib/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart index cf858e14c8..92ac818fa0 100644 --- a/lib/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart +++ b/lib/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart @@ -1,10 +1,9 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:model/oidc/oidc_configuration.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; +import 'package:rich_text_composer/views/commons/logger.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/state/authenticate_oidc_on_browser_state.dart'; @@ -14,15 +13,11 @@ class AuthenticateOidcOnBrowserInteractor { AuthenticateOidcOnBrowserInteractor(this.authenticationOIDCRepository); - Stream> execute(Uri baseUrl, OIDCConfiguration config) async* { + Stream> execute(String baseUrl, OIDCConfiguration oidcConfiguration) async* { try { yield Right(AuthenticateOidcOnBrowserLoading()); - await authenticationOIDCRepository.authenticateOidcOnBrowser( - config.clientId, - config.redirectUrl, - config.discoveryUrl, - config.scopes); - yield Right(AuthenticateOidcOnBrowserSuccess()); + await authenticationOIDCRepository.authenticateOidcOnBrowser(oidcConfiguration); + yield Right(AuthenticateOidcOnBrowserSuccess(baseUrl, oidcConfiguration)); } catch (e) { logError('AuthenticateOidcOnBrowserInteractor::execute(): $e'); yield Left(AuthenticateOidcOnBrowserFailure(e)); diff --git a/lib/features/login/domain/usecases/authentication_user_interactor.dart b/lib/features/login/domain/usecases/authentication_user_interactor.dart index c60c802521..1f91a9183a 100644 --- a/lib/features/login/domain/usecases/authentication_user_interactor.dart +++ b/lib/features/login/domain/usecases/authentication_user_interactor.dart @@ -3,22 +3,19 @@ import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:model/account/authentication_type.dart'; +import 'package:model/account/basic_auth.dart'; import 'package:model/account/password.dart'; import 'package:model/account/personal_account.dart'; -import 'package:tmail_ui_user/features/login/data/model/authentication_info_cache.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart'; class AuthenticationInteractor { final AuthenticationRepository authenticationRepository; - final CredentialRepository credentialRepository; final AccountRepository _accountRepository; AuthenticationInteractor( this.authenticationRepository, - this.credentialRepository, this._accountRepository ); @@ -29,24 +26,22 @@ class AuthenticationInteractor { }) async* { try { yield Right(AuthenticationUserLoading()); - final username = await authenticationRepository.authenticationUser(baseUrl, userName, password); - await Future.wait([ - credentialRepository.saveBaseUrl(baseUrl), - credentialRepository.storeAuthenticationInfo( - AuthenticationInfoCache( - username.value, - password.value - ) - ), - ]); + final userProfile = await authenticationRepository.authenticationUser( + baseUrl, + userName, + password); + await _accountRepository.setCurrentAccount( PersonalAccount( - username.value, - AuthenticationType.basic, - isSelected: true + id: userName.value, + authenticationType: AuthenticationType.basic, + isSelected: true, + baseUrl: baseUrl.toString(), + basicAuth: BasicAuth(userName, password) ) ); - yield Right(AuthenticationUserSuccess(username)); + + yield Right(AuthenticationUserSuccess(userProfile)); } catch (e) { yield Left(AuthenticationUserFailure(e)); } diff --git a/lib/features/login/domain/usecases/check_oidc_is_available_interactor.dart b/lib/features/login/domain/usecases/check_oidc_is_available_interactor.dart index 00a8a48c78..3ee20be67b 100644 --- a/lib/features/login/domain/usecases/check_oidc_is_available_interactor.dart +++ b/lib/features/login/domain/usecases/check_oidc_is_available_interactor.dart @@ -13,8 +13,8 @@ class CheckOIDCIsAvailableInteractor { Stream> execute(OIDCRequest oidcRequest) async* { try { yield Right(CheckOIDCIsAvailableLoading()); - final result = await _oidcRepository.checkOIDCIsAvailable(oidcRequest); - yield Right(CheckOIDCIsAvailableSuccess(result)); + final oidcResponse = await _oidcRepository.checkOIDCIsAvailable(oidcRequest); + yield Right(CheckOIDCIsAvailableSuccess(oidcResponse, oidcRequest.baseUrl)); } catch (e) { yield Left(CheckOIDCIsAvailableFailure(e)); } diff --git a/lib/features/login/domain/usecases/delete_authority_oidc_interactor.dart b/lib/features/login/domain/usecases/delete_authority_oidc_interactor.dart deleted file mode 100644 index 958ffb46a2..0000000000 --- a/lib/features/login/domain/usecases/delete_authority_oidc_interactor.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:core'; - -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/delete_authority_oidc_state.dart'; - -class DeleteAuthorityOidcInteractor { - final AuthenticationOIDCRepository _authenticationOIDCRepository; - final CredentialRepository _credentialRepository; - - DeleteAuthorityOidcInteractor(this._authenticationOIDCRepository, this._credentialRepository); - - Future> execute() async { - try { - await Future.wait([ - _credentialRepository.removeBaseUrl(), - _authenticationOIDCRepository.deleteAuthorityOidc(), - _authenticationOIDCRepository.deleteTokenOIDC(), - ]); - return Right(DeleteAuthorityOidcSuccess()); - } catch (exception) { - return Left(DeleteAuthorityOidcFailure(exception)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/delete_credential_interactor.dart b/lib/features/login/domain/usecases/delete_credential_interactor.dart deleted file mode 100644 index 3033f6d5ce..0000000000 --- a/lib/features/login/domain/usecases/delete_credential_interactor.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'dart:core'; - -import 'package:core/core.dart'; -import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/delete_credential_state.dart'; - -class DeleteCredentialInteractor { - final CredentialRepository credentialRepository; - - DeleteCredentialInteractor(this.credentialRepository); - - Future> execute() async { - try { - await Future.wait([ - credentialRepository.removeBaseUrl(), - credentialRepository.removeAuthenticationInfo(), - ]); - return Right(DeleteCredentialSuccess()); - } catch (exception) { - return Left(DeleteCredentialFailure(exception)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_auth_response_url_browser_interactor.dart b/lib/features/login/domain/usecases/get_auth_response_url_browser_interactor.dart new file mode 100644 index 0000000000..2749dff4ad --- /dev/null +++ b/lib/features/login/domain/usecases/get_auth_response_url_browser_interactor.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_auth_response_url_browser_state.dart'; + +class GetAuthResponseUrlBrowserInteractor { + final AuthenticationOIDCRepository _oidcRepository; + + GetAuthResponseUrlBrowserInteractor(this._oidcRepository); + + Stream> execute() async* { + try { + yield Right(GetAuthResponseUrlBrowserLoading()); + final authResponseUrl = await _oidcRepository.getAuthResponseUrlBrowser(); + yield Right(GetAuthResponseUrlBrowserSuccess(authResponseUrl)); + } catch (e) { + yield Left(GetAuthResponseUrlBrowserFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_authenticated_account_interactor.dart b/lib/features/login/domain/usecases/get_authenticated_account_interactor.dart deleted file mode 100644 index 0b2e3285ca..0000000000 --- a/lib/features/login/domain/usecases/get_authenticated_account_interactor.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; -import 'package:model/account/authentication_type.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_token_oidc_interactor.dart'; - -class GetAuthenticatedAccountInteractor { - final AccountRepository _accountRepository; - final GetCredentialInteractor _getCredentialInteractor; - final GetStoredTokenOidcInteractor _getStoredTokenOidcInteractor; - - GetAuthenticatedAccountInteractor( - this._accountRepository, - this._getCredentialInteractor, - this._getStoredTokenOidcInteractor - ); - - Stream> execute({StateChange? stateChange}) async* { - try { - yield Right(LoadingState()); - final account = await _accountRepository.getCurrentAccount(); - yield Right(GetAuthenticatedAccountSuccess(account)); - if (account.authenticationType == AuthenticationType.oidc) { - yield* _getStoredTokenOidcInteractor.execute( - personalAccount: account, - stateChange: stateChange); - } else { - yield await _getCredentialInteractor.execute( - personalAccount: account, - stateChange: stateChange); - } - } catch (e) { - yield Left(GetAuthenticatedAccountFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_authentication_info_interactor.dart b/lib/features/login/domain/usecases/get_authentication_info_interactor.dart deleted file mode 100644 index 75286c6405..0000000000 --- a/lib/features/login/domain/usecases/get_authentication_info_interactor.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_authentication_info_state.dart'; - -class GetAuthenticationInfoInteractor { - final AuthenticationOIDCRepository _oidcRepository; - - GetAuthenticationInfoInteractor(this._oidcRepository); - - Stream> execute() async* { - try { - yield Right(GetAuthenticationInfoLoading()); - final result = await _oidcRepository.getAuthenticationInfo(); - log('GetAuthenticationInfoInteractor::execute(): result: $result'); - yield Right(GetAuthenticationInfoSuccess()); - } catch (e) { - log('GetAuthenticationInfoInteractor::execute(): ERROR: $e'); - yield Left(GetAuthenticationInfoFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_credential_interactor.dart b/lib/features/login/domain/usecases/get_credential_interactor.dart deleted file mode 100644 index 5b71548054..0000000000 --- a/lib/features/login/domain/usecases/get_credential_interactor.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:core'; - -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; -import 'package:model/account/password.dart'; -import 'package:model/account/personal_account.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/uri_extension.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; - -class GetCredentialInteractor { - final CredentialRepository credentialRepository; - - GetCredentialInteractor(this.credentialRepository); - - Future> execute({ - required PersonalAccount personalAccount, - StateChange? stateChange - }) async { - try { - final baseUrl = await credentialRepository.getBaseUrl(); - final authenticationInfo = await credentialRepository.getAuthenticationInfoStored(); - if (isCredentialValid(baseUrl)) { - return Right(GetCredentialViewState( - baseUrl, - UserName(authenticationInfo.username), - Password(authenticationInfo.password), - personalAccount, - stateChange: stateChange)); - } else { - return Left(GetCredentialFailure(BadCredentials())); - } - } catch (exception) { - return Left(GetCredentialFailure(exception)); - } - } - - bool isCredentialValid(Uri baseUrl) => baseUrl.isBaseUrlValid(); -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_current_account_cache_interactor.dart b/lib/features/login/domain/usecases/get_current_account_cache_interactor.dart new file mode 100644 index 0000000000..0ca0da83e5 --- /dev/null +++ b/lib/features/login/domain/usecases/get_current_account_cache_interactor.dart @@ -0,0 +1,24 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/push/state_change.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_current_account_cache_state.dart'; + +class GetCurrentAccountCacheInteractor { + final AccountRepository _accountRepository; + + GetCurrentAccountCacheInteractor(this._accountRepository); + + Stream> execute({StateChange? stateChange}) async* { + try { + yield Right(GetCurrentAccountCacheLoading()); + final currentAccount = await _accountRepository.getCurrentAccount(); + yield Right(GetCurrentAccountCacheSuccess( + currentAccount, + stateChange)); + } catch (e) { + yield Left(GetCurrentAccountCacheFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_oidc_configuration_interactor.dart b/lib/features/login/domain/usecases/get_oidc_configuration_interactor.dart deleted file mode 100644 index a2d9458a9c..0000000000 --- a/lib/features/login/domain/usecases/get_oidc_configuration_interactor.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; -import 'package:model/oidc/response/oidc_response.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart'; - -class GetOIDCConfigurationInteractor { - final AuthenticationOIDCRepository _oidcRepository; - - GetOIDCConfigurationInteractor(this._oidcRepository); - - Stream> execute(OIDCResponse oidcResponse) async* { - try { - yield Right(GetOIDCConfigurationLoading()); - final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(oidcResponse); - await _oidcRepository.persistAuthorityOidc(oidcConfiguration.authority); - yield Right(GetOIDCConfigurationSuccess(oidcConfiguration)); - } catch (e) { - log('GetOIDCConfigurationInteractor::execute(): ERROR: $e'); - yield Left(GetOIDCConfigurationFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_oidc_is_available_interactor.dart b/lib/features/login/domain/usecases/get_oidc_is_available_interactor.dart deleted file mode 100644 index 7a4d96fa97..0000000000 --- a/lib/features/login/domain/usecases/get_oidc_is_available_interactor.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:model/oidc/request/oidc_request.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_oidc_is_available_state.dart'; - -class GetOIDCIsAvailableInteractor { - final AuthenticationOIDCRepository _oidcRepository; - - GetOIDCIsAvailableInteractor(this._oidcRepository); - - Stream> execute(OIDCRequest oidcRequest) async* { - try { - yield Right(GetOIDCIsAvailableLoading()); - final result = await _oidcRepository.checkOIDCIsAvailable(oidcRequest); - yield Right(GetOIDCIsAvailableSuccess(result)); - } catch (e) { - yield Left(GetOIDCIsAvailableFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart b/lib/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart deleted file mode 100644 index ca928db8ba..0000000000 --- a/lib/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_oidc_configuration_state.dart'; - -class GetStoredOidcConfigurationInteractor { - final AuthenticationOIDCRepository _authenticationOIDCRepository; - - GetStoredOidcConfigurationInteractor(this._authenticationOIDCRepository); - - Stream> execute() async* { - try { - yield Right(GetStoredOidcConfigurationLoading()); - final config = await _authenticationOIDCRepository.getStoredOidcConfiguration(); - yield Right(GetStoredOidcConfigurationSuccess(config)); - } catch (e) { - log('GetStoredOidcConfigurationInteractor::execute(): $e'); - yield Left(GetStoredOidcConfigurationFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_stored_token_oidc_interactor.dart b/lib/features/login/domain/usecases/get_stored_token_oidc_interactor.dart deleted file mode 100644 index d45e0a0d91..0000000000 --- a/lib/features/login/domain/usecases/get_stored_token_oidc_interactor.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; -import 'package:jmap_dart_client/jmap/push/state_change.dart'; -import 'package:model/account/personal_account.dart'; -import 'package:model/oidc/oidc_configuration.dart'; -import 'package:model/oidc/token_oidc.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/uri_extension.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; - -class GetStoredTokenOidcInteractor { - final AuthenticationOIDCRepository _authenticationOIDCRepository; - final CredentialRepository _credentialRepository; - - GetStoredTokenOidcInteractor(this._authenticationOIDCRepository, this._credentialRepository); - - Stream> execute({ - required PersonalAccount personalAccount, - StateChange? stateChange - }) async* { - try { - yield Right(LoadingState()); - final futureValue = await Future.wait([ - _credentialRepository.getBaseUrl(), - _authenticationOIDCRepository.getStoredTokenOIDC(personalAccount.id), - _authenticationOIDCRepository.getStoredOidcConfiguration(), - ], eagerError: true); - - final baseUrl = futureValue[0] as Uri; - final tokenOidc = futureValue[1] as TokenOIDC; - final oidcConfiguration = futureValue[2] as OIDCConfiguration; - log('GetStoredTokenOidcInteractor::execute(): $tokenOidc'); - log('GetStoredTokenOidcInteractor::execute(): oidcConfiguration: $oidcConfiguration'); - - if (_isCredentialValid(baseUrl)) { - yield Right(GetStoredTokenOidcSuccess( - baseUrl, - tokenOidc, - oidcConfiguration, - personalAccount, - stateChange: stateChange)); - } else { - yield Left(GetStoredTokenOidcFailure(InvalidBaseUrl())); - } - } catch (e) { - log('GetStoredTokenOidcInteractor::execute(): $e'); - yield Left(GetStoredTokenOidcFailure(e)); - } - } - - bool _isCredentialValid(Uri baseUrl) => baseUrl.isBaseUrlValid(); -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/get_token_oidc_interactor.dart b/lib/features/login/domain/usecases/get_token_oidc_interactor.dart index b04ef8b931..66c436bd9e 100644 --- a/lib/features/login/domain/usecases/get_token_oidc_interactor.dart +++ b/lib/features/login/domain/usecases/get_token_oidc_interactor.dart @@ -1,51 +1,40 @@ - import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/services.dart'; import 'package:model/account/authentication_type.dart'; import 'package:model/account/personal_account.dart'; import 'package:model/oidc/oidc_configuration.dart'; -import 'package:model/oidc/token_oidc.dart'; +import 'package:rich_text_composer/views/commons/logger.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/login_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_token_oidc_state.dart'; class GetTokenOIDCInteractor { - final AuthenticationOIDCRepository authenticationOIDCRepository; + final AuthenticationOIDCRepository _authenticationOIDCRepository; final AccountRepository _accountRepository; - final CredentialRepository _credentialRepository; - GetTokenOIDCInteractor(this._credentialRepository, this.authenticationOIDCRepository, this._accountRepository); + GetTokenOIDCInteractor(this._authenticationOIDCRepository, this._accountRepository); - Stream> execute(Uri baseUrl, OIDCConfiguration config) async* { + Stream> execute(String baseUrl, OIDCConfiguration oidcConfiguration) async* { try { yield Right(GetTokenOIDCLoading()); - final tokenOIDC = await authenticationOIDCRepository.getTokenOIDC( - config.clientId, - config.redirectUrl, - config.discoveryUrl, - config.scopes); - - await Future.wait([ - _credentialRepository.saveBaseUrl(baseUrl), - authenticationOIDCRepository.persistTokenOIDC(tokenOIDC), - authenticationOIDCRepository.persistAuthorityOidc(config.authority), - ]); - - await _accountRepository.setCurrentAccount( - PersonalAccount( - tokenOIDC.tokenIdHash, - AuthenticationType.oidc, - isSelected: true - ) - ); - yield Right(GetTokenOIDCSuccess(tokenOIDC, config)); + + final tokenOIDC = await _authenticationOIDCRepository.getTokenOIDC(oidcConfiguration); + + final newAccount = PersonalAccount( + id: tokenOIDC.tokenIdHash, + authenticationType: AuthenticationType.oidc, + isSelected: true, + baseUrl: baseUrl, + tokenOidc: tokenOIDC); + + await _accountRepository.setCurrentAccount(newAccount); + + yield Right(GetTokenOIDCSuccess(newAccount)); } on PlatformException catch (e) { logError('GetTokenOIDCInteractor::execute(): PlatformException ${e.message} - ${e.stacktrace}'); if (NoSuitableBrowserForOIDCException.verifyException(e)) { diff --git a/lib/features/login/domain/usecases/logout_current_account_basic_auth_interactor.dart b/lib/features/login/domain/usecases/logout_current_account_basic_auth_interactor.dart new file mode 100644 index 0000000000..3ac606cb85 --- /dev/null +++ b/lib/features/login/domain/usecases/logout_current_account_basic_auth_interactor.dart @@ -0,0 +1,24 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:model/account/personal_account.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/logout_current_account_basic_auth_state.dart'; + +class LogoutCurrentAccountBasicAuthInteractor { + final AccountRepository _accountRepository; + + LogoutCurrentAccountBasicAuthInteractor(this._accountRepository); + + Stream> execute(PersonalAccount personalAccount) async* { + try { + yield Right(LogoutCurrentAccountBasicAuthLoading()); + await _accountRepository.deleteCurrentAccount(personalAccount.id); + yield Right(LogoutCurrentAccountBasicAuthSuccess(personalAccount)); + } catch (e) { + yield Left(LogoutCurrentAccountBasicAuthFailure( + exception: e, + deletedAccount: personalAccount)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/logout_current_account_interactor.dart b/lib/features/login/domain/usecases/logout_current_account_interactor.dart new file mode 100644 index 0000000000..8bf528bb62 --- /dev/null +++ b/lib/features/login/domain/usecases/logout_current_account_interactor.dart @@ -0,0 +1,45 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:model/account/authentication_type.dart'; +import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/logout_current_account_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_basic_auth_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_oidc_interactor.dart'; + +class LogoutCurrentAccountInteractor { + final AccountRepository _accountRepository; + final LogoutCurrentAccountBasicAuthInteractor _logoutBasicAuthInteractor; + final LogoutCurrentAccountOidcInteractor _logoutOidcInteractor; + + LogoutCurrentAccountInteractor( + this._accountRepository, + this._logoutBasicAuthInteractor, + this._logoutOidcInteractor, + ); + + Stream> execute() async* { + try { + yield Right(LogoutCurrentAccountLoading()); + + final currentAccount = await _accountRepository.getCurrentAccount(); + + switch (currentAccount.authenticationType) { + case AuthenticationType.oidc: + yield* _logoutOidcInteractor.execute(currentAccount); + break; + case AuthenticationType.basic: + yield* _logoutBasicAuthInteractor.execute(currentAccount); + break; + default: + yield Left(LogoutCurrentAccountFailure( + exception: AuthenticationTypeNotSupportedException(), + deletedAccount: currentAccount)); + break; + } + } catch (e) { + yield Left(LogoutCurrentAccountFailure(exception: e)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/logout_current_account_oidc_interactor.dart b/lib/features/login/domain/usecases/logout_current_account_oidc_interactor.dart new file mode 100644 index 0000000000..09047ce562 --- /dev/null +++ b/lib/features/login/domain/usecases/logout_current_account_oidc_interactor.dart @@ -0,0 +1,44 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:model/account/personal_account.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/logout_current_account_oidc_state.dart'; + +class LogoutCurrentAccountOidcInteractor { + final AuthenticationOIDCRepository _authenticationOIDCRepository; + final AccountRepository _accountRepository; + + LogoutCurrentAccountOidcInteractor( + this._authenticationOIDCRepository, + this._accountRepository + ); + + Stream> execute(PersonalAccount currentAccount) async* { + try { + yield Right(LogoutCurrentAccountOidcLoading()); + + final oidcDiscoveryResponse = await _authenticationOIDCRepository.discoverOIDC(currentAccount.tokenOidc!.oidcConfiguration); + + final result = await _authenticationOIDCRepository.logout( + currentAccount.tokenOidc!.tokenId, + currentAccount.tokenOidc!.oidcConfiguration, + oidcDiscoveryResponse + ); + + await _accountRepository.deleteCurrentAccount(currentAccount.id); + + if (result) { + yield Right(LogoutCurrentAccountOidcSuccess(currentAccount)); + } else { + yield Left(LogoutCurrentAccountOidcFailure(deletedAccount: currentAccount)); + } + } catch (e) { + yield Left(LogoutCurrentAccountOidcFailure( + exception: e, + deletedAccount: currentAccount)); + } + } +} diff --git a/lib/features/login/domain/usecases/update_authentication_account_interactor.dart b/lib/features/login/domain/usecases/update_authentication_account_interactor.dart deleted file mode 100644 index d12cfd696a..0000000000 --- a/lib/features/login/domain/usecases/update_authentication_account_interactor.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/extensions/personal_account_extension.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/state/update_authentication_account_state.dart'; - -class UpdateAuthenticationAccountInteractor { - final AccountRepository _accountRepository; - - UpdateAuthenticationAccountInteractor(this._accountRepository); - - Stream> execute(AccountId accountId, String apiUrl, UserName userName) async* { - try{ - yield Right(UpdateAuthenticationAccountLoading()); - final currentAccount = await _accountRepository.getCurrentAccount(); - await _accountRepository.setCurrentAccount( - currentAccount.fromAccount( - accountId: accountId, - apiUrl: apiUrl, - userName: userName - ) - ); - yield Right(UpdateAuthenticationAccountSuccess()); - } catch(e) { - yield Left(UpdateAuthenticationAccountFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/login/domain/usecases/update_current_account_cache_interactor.dart b/lib/features/login/domain/usecases/update_current_account_cache_interactor.dart new file mode 100644 index 0000000000..c3c4bf9804 --- /dev/null +++ b/lib/features/login/domain/usecases/update_current_account_cache_interactor.dart @@ -0,0 +1,38 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:model/extensions/session_extension.dart'; +import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/personal_account_extension.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/state/update_current_account_cache_state.dart'; + +class UpdateCurrentAccountCacheInteractor { + final AccountRepository _accountRepository; + + UpdateCurrentAccountCacheInteractor(this._accountRepository); + + Stream> execute(Session session) async* { + try{ + yield Right(UpdateCurrentAccountCacheLoading()); + + final currentAccount = await _accountRepository.getCurrentAccount(); + final apiUrl = session.getQualifiedApiUrl(baseUrl: currentAccount.baseUrl); + + await _accountRepository.setCurrentAccount( + currentAccount.updateAccountId( + accountId: session.accountId, + apiUrl: apiUrl, + userName: session.username + ) + ); + + yield Right(UpdateCurrentAccountCacheSuccess( + session: session, + apiUrl: apiUrl)); + } catch(e) { + yield Left(UpdateCurrentAccountCacheFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/login/presentation/login_bindings.dart b/lib/features/login/presentation/login_bindings.dart index 980cc98e7e..d2b22587ce 100644 --- a/lib/features/login/presentation/login_bindings.dart +++ b/lib/features/login/presentation/login_bindings.dart @@ -10,7 +10,6 @@ import 'package:tmail_ui_user/features/login/data/network/dns_service.dart'; import 'package:tmail_ui_user/features/login/data/repository/login_repository_impl.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/login_repository.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; @@ -18,10 +17,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_avail import 'package:tmail_ui_user/features/login/domain/usecases/dns_lookup_to_get_jmap_url_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_is_available_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_auth_response_url_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart'; @@ -36,12 +32,9 @@ class LoginBindings extends BaseBindings { Get.create(() => LoginController( Get.find(), Get.find(), - Get.find(), - Get.find(), Get.find(), Get.find(), - Get.find(), - Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), @@ -73,24 +66,14 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => CheckOIDCIsAvailableInteractor( Get.find(), )); - Get.lazyPut(() => GetOIDCIsAvailableInteractor( - Get.find(), - )); - Get.lazyPut(() => GetOIDCConfigurationInteractor( - Get.find(), - )); Get.lazyPut(() => GetTokenOIDCInteractor( - Get.find(), Get.find(), Get.find() )); - Get.lazyPut(() => AuthenticateOidcOnBrowserInteractor( - Get.find(), - )); - Get.lazyPut(() => GetAuthenticationInfoInteractor( + Get.lazyPut(() => GetAuthResponseUrlBrowserInteractor( Get.find(), )); - Get.lazyPut(() => GetStoredOidcConfigurationInteractor( + Get.lazyPut(() => AuthenticateOidcOnBrowserInteractor( Get.find(), )); Get.lazyPut(() => SaveLoginUrlOnMobileInteractor(Get.find(),)); diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index 561ad3448e..c35568be6a 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -15,23 +15,21 @@ import 'package:model/oidc/request/oidc_request.dart'; import 'package:model/oidc/response/oidc_response.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; +import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/login_exception.dart'; import 'package:tmail_ui_user/features/login/domain/model/login_constants.dart'; import 'package:tmail_ui_user/features/login/domain/model/recent_login_url.dart'; import 'package:tmail_ui_user/features/login/domain/model/recent_login_username.dart'; -import 'package:tmail_ui_user/features/login/domain/state/authenticate_oidc_on_browser_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/authentication_user_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/check_oidc_is_available_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/dns_lookup_to_get_jmap_url_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_url_latest_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_username_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_authentication_info_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_auth_response_url_browser_state.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_current_account_cache_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_oidc_configuration_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_oidc_is_available_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_oidc_configuration_state.dart'; import 'package:tmail_ui_user/features/login/domain/state/get_token_oidc_state.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; @@ -39,10 +37,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_avail import 'package:tmail_ui_user/features/login/domain/usecases/dns_lookup_to_get_jmap_url_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_is_available_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_auth_response_url_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart'; @@ -58,12 +53,9 @@ class LoginController extends ReloadableController { final AuthenticationInteractor _authenticationInteractor; final CheckOIDCIsAvailableInteractor _checkOIDCIsAvailableInteractor; - final GetOIDCIsAvailableInteractor _getOIDCIsAvailableInteractor; - final GetOIDCConfigurationInteractor _getOIDCConfigurationInteractor; final GetTokenOIDCInteractor _getTokenOIDCInteractor; final AuthenticateOidcOnBrowserInteractor _authenticateOidcOnBrowserInteractor; - final GetAuthenticationInfoInteractor _getAuthenticationInfoInteractor; - final GetStoredOidcConfigurationInteractor _getStoredOidcConfigurationInteractor; + final GetAuthResponseUrlBrowserInteractor _getAuthResponseUrlBrowserInteractor; final SaveLoginUrlOnMobileInteractor _saveLoginUrlOnMobileInteractor; final GetAllRecentLoginUrlOnMobileInteractor _getAllRecentLoginUrlOnMobileInteractor; final SaveLoginUsernameOnMobileInteractor _saveLoginUsernameOnMobileInteractor; @@ -79,7 +71,7 @@ class LoginController extends ReloadableController { final loginFormType = LoginFormType.none.obs; - OIDCResponse? _oidcResponse; + OIDCConfiguration? _oidcConfiguration; UserName? _username; Password? _password; Uri? _baseUri; @@ -87,12 +79,9 @@ class LoginController extends ReloadableController { LoginController( this._authenticationInteractor, this._checkOIDCIsAvailableInteractor, - this._getOIDCIsAvailableInteractor, - this._getOIDCConfigurationInteractor, this._getTokenOIDCInteractor, this._authenticateOidcOnBrowserInteractor, - this._getAuthenticationInfoInteractor, - this._getStoredOidcConfigurationInteractor, + this._getAuthResponseUrlBrowserInteractor, this._saveLoginUrlOnMobileInteractor, this._getAllRecentLoginUrlOnMobileInteractor, this._saveLoginUsernameOnMobileInteractor, @@ -105,36 +94,31 @@ class LoginController extends ReloadableController { super.onReady(); final arguments = Get.arguments; if (arguments is LoginArguments) { - if (arguments.loginFormType == LoginFormType.passwordForm) { - loginFormType.value = LoginFormType.dnsLookupForm; - } else { - loginFormType.value = arguments.loginFormType; - } + loginFormType.value = arguments.loginFormType == LoginFormType.passwordForm + ? LoginFormType.dnsLookupForm + : arguments.loginFormType; + if (PlatformInfo.isWeb) { _checkOIDCIsAvailable(); } - } else { - if (PlatformInfo.isWeb) { - _getAuthenticationInfo(); - } + } else if (PlatformInfo.isWeb) { + _handleAuthenticationSSOBrowserCallbackAction(); } } @override void handleFailureViewState(Failure failure) { log('LoginController::handleFailureViewState(): $failure'); - if (failure is GetAuthenticationInfoFailure) { - getAuthenticatedAccountAction(); - } else if (failure is CheckOIDCIsAvailableFailure || - failure is GetStoredOidcConfigurationFailure || - failure is GetOIDCIsAvailableFailure || - failure is GetOIDCConfigurationFailure + if (failure is GetAuthResponseUrlBrowserFailure) { + getCurrentAccountCache(); + } else if (failure is CheckOIDCIsAvailableFailure + || failure is GetOIDCConfigurationFailure ) { _handleCommonOIDCFailure(); } else if (failure is GetTokenOIDCFailure) { _handleNoSuitableBrowserOIDC(failure) .map((stillFailed) => _handleCommonOIDCFailure()); - } else if (failure is GetAuthenticatedAccountFailure) { + } else if (failure is GetCurrentAccountCacheFailure) { _checkOIDCIsAvailable(); } else if (failure is GetSessionFailure) { clearAllData(); @@ -149,19 +133,17 @@ class LoginController extends ReloadableController { @override void handleSuccessViewState(Success success) { - if (success is GetAuthenticationInfoSuccess) { - _getStoredOidcConfiguration(); - } else if (success is GetStoredOidcConfigurationSuccess) { - _getTokenOIDCAction(success.oidcConfiguration); + if (success is GetAuthResponseUrlBrowserSuccess) { + if (_oidcConfiguration != null && _currentBaseUrl != null) { + _getTokenOIDCAction(_oidcConfiguration!, _currentBaseUrl!.toString()); + } else { + _showCredentialForm(); + } } else if (success is CheckOIDCIsAvailableSuccess) { - _redirectToSSOLoginScreen(success); - } else if (success is GetOIDCIsAvailableSuccess) { - _oidcResponse = success.oidcResponse; - _getOIDCConfiguration(); - } else if (success is GetOIDCConfigurationSuccess) { - _getOIDCConfigurationSuccess(success); + _handleWhenOIDCIsAvailable(success.oidcResponse, success.baseUrl); } else if (success is GetTokenOIDCSuccess) { - _getTokenOIDCSuccess(success); + setUpInterceptors(success.personalAccount); + getSessionAction(); } else if (success is AuthenticationUserSuccess) { _loginSuccessAction(success); } else if (success is DNSLookupToGetJmapUrlSuccess) { @@ -175,9 +157,7 @@ class LoginController extends ReloadableController { void handleExceptionAction({Failure? failure, Exception? exception}) { logError('LoginController::handleExceptionAction:exception: $exception | failure: ${failure.runtimeType}'); if (failure is CheckOIDCIsAvailableFailure || - failure is GetStoredOidcConfigurationFailure || - failure is GetOIDCConfigurationFailure || - failure is GetOIDCIsAvailableFailure) { + failure is GetOIDCConfigurationFailure) { _handleCommonOIDCFailure(); } else if (failure is GetTokenOIDCFailure) { _handleNoSuitableBrowserOIDC(failure) @@ -197,12 +177,8 @@ class LoginController extends ReloadableController { ); } - void _getAuthenticationInfo() { - consumeState(_getAuthenticationInfoInteractor.execute()); - } - - void _getStoredOidcConfiguration() { - consumeState(_getStoredOidcConfigurationInteractor.execute()); + void _handleAuthenticationSSOBrowserCallbackAction() { + consumeState(_getAuthResponseUrlBrowserInteractor.execute()); } void handleNextInUrlInputFormPress() { @@ -225,16 +201,6 @@ class LoginController extends ReloadableController { } } - void _redirectToSSOLoginScreen(CheckOIDCIsAvailableSuccess success) { - _oidcResponse = success.oidcResponse; - consumeState(_getOIDCIsAvailableInteractor.execute( - OIDCRequest( - baseUrl: _currentBaseUrl!.toString(), - resourceUrl: _currentBaseUrl!.origin - ) - )); - } - void handleBackButtonAction() { clearState(); if (loginFormType.value == LoginFormType.credentialForm) { @@ -284,38 +250,32 @@ class LoginController extends ReloadableController { } } - void _getOIDCConfiguration() { - if (_oidcResponse != null) { - consumeState(_getOIDCConfigurationInteractor.execute(_oidcResponse!)); - } else { - dispatchState(Left(GetOIDCConfigurationFailure(CanNotFoundOIDCLinks()))); + void _handleWhenOIDCIsAvailable(OIDCResponse oidcResponse, String baseUrl) { + if (oidcResponse.links.isEmpty) { + dispatchState(Left(GetOIDCConfigurationFailure(CanNotFoundOIDCAuthority()))); + return; } - } - void _getOIDCConfigurationSuccess(GetOIDCConfigurationSuccess success) { + _oidcConfiguration = OIDCConfiguration( + authority: oidcResponse.links[0].href.toString(), + clientId: OIDCConstant.clientId, + scopes: AppConfig.oidcScopes + ); + if (PlatformInfo.isWeb) { - _authenticateOidcOnBrowserAction(success.oidcConfiguration); + _authenticateOidcOnBrowserAction(_oidcConfiguration!, baseUrl); } else { - _getTokenOIDCAction(success.oidcConfiguration); + _getTokenOIDCAction(_oidcConfiguration!, baseUrl); } } - void _getTokenOIDCAction(OIDCConfiguration config) { - if (_currentBaseUrl != null) { - consumeState(_getTokenOIDCInteractor.execute(_currentBaseUrl!, config)); - } else { - dispatchState(Left(GetTokenOIDCFailure(CanNotFoundBaseUrl()))); - } + void _getTokenOIDCAction(OIDCConfiguration config, String baseUrl) { + consumeState(_getTokenOIDCInteractor.execute(baseUrl, config)); } - void _authenticateOidcOnBrowserAction(OIDCConfiguration config) async { + void _authenticateOidcOnBrowserAction(OIDCConfiguration config, String baseUrl) { _removeAuthDestinationUrlInSessionStorage(); - - if (_currentBaseUrl != null) { - consumeState(_authenticateOidcOnBrowserInteractor.execute(_currentBaseUrl!, config)); - } else { - dispatchState(Left(AuthenticateOidcOnBrowserFailure(CanNotFoundBaseUrl()))); - } + consumeState(_authenticateOidcOnBrowserInteractor.execute(baseUrl, config)); } void _removeAuthDestinationUrlInSessionStorage() { @@ -325,18 +285,6 @@ class LoginController extends ReloadableController { } } - void _getTokenOIDCSuccess(GetTokenOIDCSuccess success) { - dynamicUrlInterceptors.setJmapUrl(_currentBaseUrl?.toString()); - dynamicUrlInterceptors.changeBaseUrl(_currentBaseUrl?.toString()); - authorizationInterceptors.setTokenAndAuthorityOidc( - newToken: success.tokenOIDC, - newConfig: success.configuration); - authorizationIsolateInterceptors.setTokenAndAuthorityOidc( - newToken: success.tokenOIDC, - newConfig: success.configuration); - getSessionAction(); - } - void _loginSuccessAction(AuthenticationUserSuccess success) { dynamicUrlInterceptors.setJmapUrl(_currentBaseUrl?.toString()); dynamicUrlInterceptors.changeBaseUrl(_currentBaseUrl?.toString()); @@ -489,6 +437,10 @@ class LoginController extends ReloadableController { urlInputController.dispose(); usernameInputController.dispose(); passwordInputController.dispose(); + _oidcConfiguration = null; + _username = null; + _password = null; + _baseUri = null; super.onClose(); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 1c7103e456..7faf8dde76 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -500,12 +500,7 @@ class MailboxDashBoardController extends ReloadableController { } void _handleSessionFromArguments(Session session) { - log('MailboxDashBoardController::_handleSession:'); - updateAuthenticationAccount( - session, - session.personalAccount.accountId, - session.username - ); + updateCurrentAccountCache(session); _setUpComponentsFromSession(session); diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index 622fa1d07c..af80908529 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -341,7 +341,14 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { controller.sessionCurrent?.username, onLogoutAction: () { popBack(); - controller.logout(controller.sessionCurrent, controller.accountId.value); + + if (controller.sessionCurrent != null && + controller.accountId.value != null) { + controller.logout( + session: controller.sessionCurrent!, + accountId: controller.accountId.value! + ); + } }, onSettingAction: () { popBack(); diff --git a/lib/features/manage_account/data/datasource/trace_log_datasource.dart b/lib/features/manage_account/data/datasource/trace_log_datasource.dart new file mode 100644 index 0000000000..5f63fec44d --- /dev/null +++ b/lib/features/manage_account/data/datasource/trace_log_datasource.dart @@ -0,0 +1,9 @@ +import 'package:core/utils/log_tracking.dart'; + +abstract class TraceLogDataSource { + Future getTraceLog(); + + Future exportTraceLog(TraceLog traceLog); + + Future deleteTraceLog(String path); +} \ No newline at end of file diff --git a/lib/features/manage_account/data/datasource_impl/trace_log_data_source_impl.dart b/lib/features/manage_account/data/datasource_impl/trace_log_data_source_impl.dart new file mode 100644 index 0000000000..7dccfe6b88 --- /dev/null +++ b/lib/features/manage_account/data/datasource_impl/trace_log_data_source_impl.dart @@ -0,0 +1,70 @@ +import 'package:core/data/utils/device_manager.dart'; +import 'package:core/domain/exceptions/file_exception.dart'; +import 'package:core/utils/log_tracking.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:tmail_ui_user/features/manage_account/data/datasource/trace_log_datasource.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; +import 'package:tmail_ui_user/main/exceptions/permission_exception.dart'; +import 'package:tmail_ui_user/main/permissions/permission_service.dart'; + +class TraceLogDataSourceImpl extends TraceLogDataSource { + + final LogTracking _logTracking; + final DeviceManager _deviceManager; + final PermissionService _permissionService; + final ExceptionThrower _exceptionThrower; + + TraceLogDataSourceImpl( + this._logTracking, + this._deviceManager, + this._permissionService, + this._exceptionThrower + ); + + @override + Future getTraceLog() { + return Future.sync(() async { + return await _logTracking.getTraceLog(); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future exportTraceLog(TraceLog traceLog) { + return Future.sync(() async { + if (PlatformInfo.isAndroid) { + final permissionGranted = await _validateStoragePermissionOnAndroid(); + if (permissionGranted) { + return await _logTracking.exportTraceLog(traceLog); + } else { + throw const NotGrantedPermissionStorageException(); + } + } else { + final savePath = await _logTracking.exportTraceLog(traceLog); + final result = await Share.shareXFiles([XFile(savePath)]); + if (result.status == ShareResultStatus.success) { + return savePath; + } + throw UserCancelShareFileException(); + } + }).catchError(_exceptionThrower.throwException); + } + + Future _validateStoragePermissionOnAndroid() async { + final needRequestPermission = await _deviceManager.isNeedRequestStoragePermissionOnAndroid(); + if (needRequestPermission) { + final isGranted = await _permissionService.isGranted(Permission.storage); + return isGranted; + } else { + return true; + } + } + + @override + Future deleteTraceLog(String path) { + return Future.sync(() async { + return await _logTracking.deleteTraceLog(path); + }).catchError(_exceptionThrower.throwException); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/data/repository/trace_log_repository_impl.dart b/lib/features/manage_account/data/repository/trace_log_repository_impl.dart new file mode 100644 index 0000000000..852e182f60 --- /dev/null +++ b/lib/features/manage_account/data/repository/trace_log_repository_impl.dart @@ -0,0 +1,24 @@ +import 'package:core/utils/log_tracking.dart'; +import 'package:tmail_ui_user/features/manage_account/data/datasource/trace_log_datasource.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/trace_log_repository.dart'; + +class TraceLogRepositoryImpl extends TraceLogRepository { + final TraceLogDataSource _traceLogDataSource; + + TraceLogRepositoryImpl(this._traceLogDataSource); + + @override + Future getTraceLog() { + return _traceLogDataSource.getTraceLog(); + } + + @override + Future exportTraceLog(TraceLog traceLog) { + return _traceLogDataSource.exportTraceLog(traceLog); + } + + @override + Future deleteTraceLog(String path) { + return _traceLogDataSource.deleteTraceLog(path); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/repository/trace_log_repository.dart b/lib/features/manage_account/domain/repository/trace_log_repository.dart new file mode 100644 index 0000000000..0b5d922a1c --- /dev/null +++ b/lib/features/manage_account/domain/repository/trace_log_repository.dart @@ -0,0 +1,9 @@ +import 'package:core/utils/log_tracking.dart'; + +abstract class TraceLogRepository { + Future getTraceLog(); + + Future exportTraceLog(TraceLog traceLog); + + Future deleteTraceLog(String path); +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/state/delete_trace_log_state.dart b/lib/features/manage_account/domain/state/delete_trace_log_state.dart new file mode 100644 index 0000000000..c680d1efbe --- /dev/null +++ b/lib/features/manage_account/domain/state/delete_trace_log_state.dart @@ -0,0 +1,11 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class DeleteTraceLogLoading extends LoadingState {} + +class DeleteTraceLogSuccess extends UIState {} + +class DeleteTraceLogFailure extends FeatureFailure { + + DeleteTraceLogFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/state/export_trace_log_state.dart b/lib/features/manage_account/domain/state/export_trace_log_state.dart new file mode 100644 index 0000000000..41f79aa4e3 --- /dev/null +++ b/lib/features/manage_account/domain/state/export_trace_log_state.dart @@ -0,0 +1,19 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class ExportTraceLogLoading extends LoadingState {} + +class ExportTraceLogSuccess extends UIState { + + String savePath; + + ExportTraceLogSuccess(this.savePath); + + @override + List get props => [savePath]; +} + +class ExportTraceLogFailure extends FeatureFailure { + + ExportTraceLogFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/state/get_trace_log_state.dart b/lib/features/manage_account/domain/state/get_trace_log_state.dart new file mode 100644 index 0000000000..86941f730c --- /dev/null +++ b/lib/features/manage_account/domain/state/get_trace_log_state.dart @@ -0,0 +1,19 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/log_tracking.dart'; + +class GetTraceLogLoading extends LoadingState {} + +class GetTraceLogSuccess extends UIState { + final TraceLog traceLog; + + GetTraceLogSuccess(this.traceLog); + + @override + List get props => [traceLog]; +} + +class GetTraceLogFailure extends FeatureFailure { + + GetTraceLogFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/state/log_out_oidc_state.dart b/lib/features/manage_account/domain/state/log_out_oidc_state.dart deleted file mode 100644 index e2ddfde137..0000000000 --- a/lib/features/manage_account/domain/state/log_out_oidc_state.dart +++ /dev/null @@ -1,10 +0,0 @@ - -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; - -class LogoutOidcSuccess extends UIState {} - -class LogoutOidcFailure extends FeatureFailure { - - LogoutOidcFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/manage_account/domain/usecases/delete_trace_log_interactor.dart b/lib/features/manage_account/domain/usecases/delete_trace_log_interactor.dart new file mode 100644 index 0000000000..5032f056d5 --- /dev/null +++ b/lib/features/manage_account/domain/usecases/delete_trace_log_interactor.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/trace_log_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/delete_trace_log_state.dart'; + +class DeleteTraceLogInteractor { + final TraceLogRepository _traceLogRepository; + + DeleteTraceLogInteractor(this._traceLogRepository); + + Stream> execute(String path) async* { + try { + yield Right(DeleteTraceLogLoading()); + await _traceLogRepository.deleteTraceLog(path); + yield Right(DeleteTraceLogSuccess()); + } catch (exception) { + yield Left(DeleteTraceLogFailure(exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/usecases/export_trace_log_interactor.dart b/lib/features/manage_account/domain/usecases/export_trace_log_interactor.dart new file mode 100644 index 0000000000..6d4ca5623d --- /dev/null +++ b/lib/features/manage_account/domain/usecases/export_trace_log_interactor.dart @@ -0,0 +1,22 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/log_tracking.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/trace_log_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart'; + +class ExportTraceLogInteractor { + final TraceLogRepository _traceLogRepository; + + ExportTraceLogInteractor(this._traceLogRepository); + + Stream> execute(TraceLog traceLog) async* { + try { + yield Right(ExportTraceLogLoading()); + final savePath = await _traceLogRepository.exportTraceLog(traceLog); + yield Right(ExportTraceLogSuccess(savePath)); + } catch (exception) { + yield Left(ExportTraceLogFailure(exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/usecases/get_trace_log_interactor.dart b/lib/features/manage_account/domain/usecases/get_trace_log_interactor.dart new file mode 100644 index 0000000000..8058fc449e --- /dev/null +++ b/lib/features/manage_account/domain/usecases/get_trace_log_interactor.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/trace_log_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/get_trace_log_state.dart'; + +class GetTraceLogInteractor { + final TraceLogRepository _traceLogRepository; + + GetTraceLogInteractor(this._traceLogRepository); + + Stream> execute() async* { + try { + yield Right(GetTraceLogLoading()); + final response = await _traceLogRepository.getTraceLog(); + yield Right(GetTraceLogSuccess(response)); + } catch (exception) { + yield Left(GetTraceLogFailure(exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart b/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart deleted file mode 100644 index 48a6a8f2aa..0000000000 --- a/lib/features/manage_account/domain/usecases/log_out_oidc_interactor.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; -import 'package:model/account/authentication_type.dart'; -import 'package:model/oidc/oidc_configuration.dart'; -import 'package:model/oidc/response/oidc_discovery_response.dart'; -import 'package:model/oidc/token_oidc.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/state/log_out_oidc_state.dart'; - -class LogoutOidcInteractor { - final AccountRepository _accountRepository; - final AuthenticationOIDCRepository _authenticationOIDCRepository; - - LogoutOidcInteractor(this._accountRepository, this._authenticationOIDCRepository); - - Stream> execute() async* { - try { - final currentAccount = await _accountRepository.getCurrentAccount(); - log('LogoutOidcInteractor::execute(): currentAccount: $currentAccount'); - if (currentAccount.authenticationType == AuthenticationType.oidc) { - final result = await _authenticationOIDCRepository.getStoredOidcConfiguration() - .then((oidcConfig) => Future.wait([ - Future.value(oidcConfig), - _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id), - _authenticationOIDCRepository.discoverOIDC(oidcConfig) - ])) - .then((oidcParameters) async { - final oidcConfig = oidcParameters[0] as OIDCConfiguration; - final tokenOIDC = oidcParameters[1] as TokenOIDC; - final oidcDiscoveryResponse = oidcParameters[2] as OIDCDiscoveryResponse; - return await _authenticationOIDCRepository.logout(tokenOIDC.tokenId, oidcConfig, oidcDiscoveryResponse); - }); - log('LogoutOidcInteractor::execute(): statusSuccess: $result'); - if (result) { - yield Right(LogoutOidcSuccess()); - } else { - yield Left(LogoutOidcFailure(null)); - } - } else { - yield Left( - LogoutOidcFailure(NotFoundAuthenticatedAccountException())); - } - } catch (e) { - log('LogoutOidcInteractor::execute(): EXCEPTION: $e'); - yield Left(LogoutOidcFailure(e)); - } - } -} diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart index acff137cbe..711e61c20f 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_controller.dart @@ -31,6 +31,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_ import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/notification/bindings/notification_binding.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/trace_log/trace_log_bindings.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_controller_bindings.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -196,6 +197,9 @@ class ManageAccountDashBoardController extends ReloadableController { case AccountMenuItem.vacation: case AccountMenuItem.none: break; + case AccountMenuItem.traceLog: + TraceLogBindings().dependencies(); + break; } } diff --git a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart index 8d3a0096db..7d17bc1bba 100644 --- a/lib/features/manage_account/presentation/manage_account_dashboard_view.dart +++ b/lib/features/manage_account/presentation/manage_account_dashboard_view.dart @@ -4,6 +4,7 @@ import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/views/image/avatar_builder.dart'; import 'package:core/presentation/views/responsive/responsive_widget.dart'; import 'package:core/presentation/views/text/slogan_builder.dart'; +import 'package:core/utils/log_tracking.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -24,6 +25,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_view.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/trace_log/trace_log_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -148,7 +150,13 @@ class ManageAccountDashBoardView extends GetWidget { color: Colors.transparent, child: InkWell( onTap: () { - controller.dashBoardController.logout( - controller.dashBoardController.sessionCurrent, - controller.dashBoardController.accountId.value - ); + if (controller.dashBoardController.sessionCurrent != null && + controller.dashBoardController.accountId.value != null) { + controller.dashBoardController.logout( + session: controller.dashBoardController.sessionCurrent!, + accountId: controller.dashBoardController.accountId.value! + ); + } }, borderRadius: BorderRadius.circular(10), child: Padding( diff --git a/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart b/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart index c5de7de208..e11df1ace1 100644 --- a/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart +++ b/lib/features/manage_account/presentation/menu/settings/settings_first_level_view.dart @@ -159,12 +159,35 @@ class SettingsFirstLevelView extends GetWidget { indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils), endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils) ), + if (PlatformInfo.isMobile) + Column( + children: [ + SettingFirstLevelTileBuilder( + AccountMenuItem.traceLog.getName(context), + AccountMenuItem.traceLog.getIcon(controller.imagePaths), + subtitle: AppLocalizations.of(context).traceLogSettingExplanation, + () => controller.selectSettings(AccountMenuItem.traceLog) + ), + Divider( + color: AppColor.colorDividerHorizontal, + height: 1, + indent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils), + endIndent: SettingsUtils.getHorizontalPadding(context, controller.responsiveUtils) + ), + ] + ), SettingFirstLevelTileBuilder( AppLocalizations.of(context).sign_out, controller.imagePaths.icSignOut, - () => controller.manageAccountDashboardController.logout( - controller.manageAccountDashboardController.sessionCurrent, - controller.manageAccountDashboardController.accountId.value) + () { + if (controller.manageAccountDashboardController.sessionCurrent != null && + controller.manageAccountDashboardController.accountId.value != null) { + controller.manageAccountDashboardController.logout( + session: controller.manageAccountDashboardController.sessionCurrent!, + accountId: controller.manageAccountDashboardController.accountId.value! + ); + } + } ), ]), ); diff --git a/lib/features/manage_account/presentation/menu/settings/settings_view.dart b/lib/features/manage_account/presentation/menu/settings/settings_view.dart index 3a85a77e9d..5a5ed43cca 100644 --- a/lib/features/manage_account/presentation/menu/settings/settings_view.dart +++ b/lib/features/manage_account/presentation/menu/settings/settings_view.dart @@ -2,6 +2,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/utils/direction_utils.dart'; +import 'package:core/utils/log_tracking.dart'; import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -21,6 +22,7 @@ import 'package:tmail_ui_user/features/manage_account/presentation/model/account import 'package:tmail_ui_user/features/manage_account/presentation/model/settings_page_level.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/notification/notification_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/profiles_view.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/trace_log/trace_log_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/vacation_view.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -264,6 +266,12 @@ class SettingsView extends GetWidget { return const SafeArea( top: false, child: NotificationView()); + case AccountMenuItem.traceLog: + if (LogTracking().enableTraceLog) { + return const TraceLogView(); + } else { + return const SizedBox.shrink(); + } default: return const SizedBox.shrink(); } diff --git a/lib/features/manage_account/presentation/model/account_menu_item.dart b/lib/features/manage_account/presentation/model/account_menu_item.dart index e0915ee4f4..c97d9eea3a 100644 --- a/lib/features/manage_account/presentation/model/account_menu_item.dart +++ b/lib/features/manage_account/presentation/model/account_menu_item.dart @@ -1,5 +1,5 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/cupertino.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -12,6 +12,7 @@ enum AccountMenuItem { vacation, mailboxVisibility, notification, + traceLog, none; String getIcon(ImagePaths imagePaths) { @@ -34,6 +35,8 @@ enum AccountMenuItem { return imagePaths.icNotification; case AccountMenuItem.none: return imagePaths.icProfiles; + case AccountMenuItem.traceLog: + return imagePaths.icSetting; } } @@ -57,6 +60,8 @@ enum AccountMenuItem { return AppLocalizations.of(context).notification; case AccountMenuItem.none: return AppLocalizations.of(context).profiles; + case AccountMenuItem.traceLog: + return AppLocalizations.of(context).traceLog; } } @@ -80,6 +85,8 @@ enum AccountMenuItem { return 'notification'; case AccountMenuItem.none: return 'profiles'; + case AccountMenuItem.traceLog: + return 'trace-log'; } } } \ No newline at end of file diff --git a/lib/features/manage_account/presentation/trace_log/trace_log_bindings.dart b/lib/features/manage_account/presentation/trace_log/trace_log_bindings.dart new file mode 100644 index 0000000000..a1c99d306a --- /dev/null +++ b/lib/features/manage_account/presentation/trace_log/trace_log_bindings.dart @@ -0,0 +1,56 @@ +import 'package:core/core.dart'; +import 'package:core/data/utils/device_manager.dart'; +import 'package:core/utils/log_tracking.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/base_bindings.dart'; +import 'package:tmail_ui_user/features/manage_account/data/datasource/trace_log_datasource.dart'; +import 'package:tmail_ui_user/features/manage_account/data/datasource_impl/trace_log_data_source_impl.dart'; +import 'package:tmail_ui_user/features/manage_account/data/repository/trace_log_repository_impl.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/repository/trace_log_repository.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_trace_log_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/export_trace_log_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_trace_log_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/trace_log/trace_log_controller.dart'; +import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; +import 'package:tmail_ui_user/main/permissions/permission_service.dart'; + +class TraceLogBindings extends BaseBindings { + @override + void bindingsController() { + Get.lazyPut(() => TraceLogController( + Get.find(), + Get.find(), + Get.find())); + } + + @override + void bindingsDataSource() { + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsDataSourceImpl() { + Get.lazyPut(() => TraceLogDataSourceImpl( + LogTracking(), + Get.find(), + Get.find(), + Get.find())); + } + + @override + void bindingsInteractor() { + Get.lazyPut(() => GetTraceLogInteractor(Get.find())); + Get.lazyPut(() => ExportTraceLogInteractor(Get.find())); + Get.lazyPut(() => DeleteTraceLogInteractor(Get.find())); + } + + @override + void bindingsRepository() { + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsRepositoryImpl() { + Get.lazyPut(() => TraceLogRepositoryImpl(Get.find())); + } +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/trace_log/trace_log_controller.dart b/lib/features/manage_account/presentation/trace_log/trace_log_controller.dart new file mode 100644 index 0000000000..a008fc8a5c --- /dev/null +++ b/lib/features/manage_account/presentation/trace_log/trace_log_controller.dart @@ -0,0 +1,127 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/log_tracking.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/delete_trace_log_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/state/get_trace_log_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/delete_trace_log_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/export_trace_log_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_trace_log_interactor.dart'; +import 'package:tmail_ui_user/main/exceptions/permission_exception.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +class TraceLogController extends BaseController { + + final GetTraceLogInteractor _getTraceLogInteractor; + final ExportTraceLogInteractor _exportTraceLogInteractor; + final DeleteTraceLogInteractor _deleteTraceLogInteractor; + + TraceLogController( + this._getTraceLogInteractor, + this._exportTraceLogInteractor, + this._deleteTraceLogInteractor, + ); + + final tracLog = Rxn(); + + @override + void onInit() { + super.onInit(); + _getTraceLog(); + } + + @override + Future handleSuccessViewState(Success success) async { + if (success is GetTraceLogSuccess) { + tracLog.value = success.traceLog; + } else if (success is ExportTraceLogSuccess) { + _handleExportTraceLogSuccess(success); + } else if (success is DeleteTraceLogSuccess) { + _handleDeleteTraceLogSuccess(success); + } else { + super.handleSuccessViewState(success); + } + } + + @override + Future handleFailureViewState(Failure failure) async { + if (failure is GetTraceLogFailure) { + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + failure.exception.toString()); + } + } else if (failure is ExportTraceLogFailure) { + _handleExportTraceLogFailure(failure); + } else if (failure is DeleteTraceLogFailure) { + _handleDeleteTraceLogFailure(failure); + } else { + super.handleFailureViewState(failure); + } + } + + void _getTraceLog() { + consumeState(_getTraceLogInteractor.execute()); + } + + void exportTraceLogFile(TraceLog traceLog) { + consumeState(_exportTraceLogInteractor.execute(traceLog)); + } + + void _handleExportTraceLogSuccess(ExportTraceLogSuccess success) { + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastSuccessMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).exportTraceLogSuccess(success.savePath)); + } + } + + void _handleExportTraceLogFailure(ExportTraceLogFailure failure) { + if (failure.exception is NotGrantedPermissionStorageException) { + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastWarningMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).youNeedToGrantFilesPermissionToExportFile); + } + } else { + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).exportTraceLogFailed); + } + } + } + + void deleteTraceLogFile(String path) { + consumeState(_deleteTraceLogInteractor.execute(path)); + } + + void _handleDeleteTraceLogSuccess(DeleteTraceLogSuccess success) { + _getTraceLog(); + + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastSuccessMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).deleteTraceLogSuccess); + } + } + + void _handleDeleteTraceLogFailure(DeleteTraceLogFailure failure) { + if (currentContext != null && currentOverlayContext != null) { + appToast.showToastErrorMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).deleteTraceLogFailed); + } + } + + bool get isExporting => viewState.value.fold( + (failure) => false, + (success) => success is ExportTraceLogLoading); + + bool get isDeleting => viewState.value.fold( + (failure) => false, + (success) => success is DeleteTraceLogLoading); +} \ No newline at end of file diff --git a/lib/features/manage_account/presentation/trace_log/trace_log_view.dart b/lib/features/manage_account/presentation/trace_log/trace_log_view.dart new file mode 100644 index 0000000000..905cc7cc61 --- /dev/null +++ b/lib/features/manage_account/presentation/trace_log/trace_log_view.dart @@ -0,0 +1,132 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:core/presentation/views/container/tmail_container_widget.dart'; +import 'package:core/presentation/views/loading/cupertino_loading_widget.dart'; +import 'package:filesize/filesize.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/menu/settings_utils.dart'; +import 'package:tmail_ui_user/features/manage_account/presentation/trace_log/trace_log_controller.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class TraceLogView extends GetWidget with AppLoaderMixin { + const TraceLogView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: SettingsUtils.getBackgroundColor(context, controller.responsiveUtils), + body: Container( + width: double.infinity, + height: double.infinity, + color: SettingsUtils.getContentBackgroundColor(context, controller.responsiveUtils), + decoration: SettingsUtils.getBoxDecorationForContent(context, controller.responsiveUtils), + margin: SettingsUtils.getMarginViewForSettingDetails(context, controller.responsiveUtils), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context).traceLogSettingExplanation, + style: const TextStyle( + fontSize: 16, + height: 20 / 16, + color: AppColor.colorTextSettingDescriptions)), + const SizedBox(height: 24), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Text( + AppLocalizations.of(context).totalSize, + style: const TextStyle( + fontSize: 17, + height: 24 / 17, + fontWeight: FontWeight.w500, + color: Colors.black + ) + ), + const SizedBox(width: 16), + Expanded( + child: Obx(() => Text( + filesize(controller.tracLog.value?.size ?? 0), + style: const TextStyle( + fontSize: 17, + height: 24 / 17, + color: Colors.black + ) + )) + ) + ] + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded(child: Obx(() { + if (controller.tracLog.value != null) { + if (controller.isExporting) { + return const TMailContainerWidget( + borderRadius: 10, + width: 60, + backgroundColor: AppColor.primaryColor, + child: CupertinoLoadingWidget(size: 16)); + } else { + return TMailButtonWidget( + text: AppLocalizations.of(context).exportFile, + backgroundColor: AppColor.primaryColor, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + textStyle: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + fontSize: 17 + ), + textAlign: TextAlign.center, + borderRadius: 10, + onTapActionCallback: () => controller.exportTraceLogFile(controller.tracLog.value!), + ); + } + } else { + return const SizedBox.shrink(); + } + })), + const SizedBox(width: 16), + Expanded(child: Obx(() { + if (controller.tracLog.value != null) { + if (controller.isDeleting) { + return const TMailContainerWidget( + borderRadius: 10, + width: 60, + backgroundColor: AppColor.primaryColor, + child: CupertinoLoadingWidget(size: 16)); + } else { + return TMailButtonWidget( + text: AppLocalizations.of(context).clearLogCache, + backgroundColor: AppColor.toastErrorBackgroundColor, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + textStyle: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + fontSize: 17 + ), + textAlign: TextAlign.center, + borderRadius: 10, + onTapActionCallback: () => controller.deleteTraceLogFile(controller.tracLog.value!.path), + ); + } + } else { + return const SizedBox.shrink(); + } + })), + ], + ) + ], + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/offline_mode/work_manager/sending_email_worker.dart b/lib/features/offline_mode/work_manager/sending_email_worker.dart index cc8064914c..b2fcab5b67 100644 --- a/lib/features/offline_mode/work_manager/sending_email_worker.dart +++ b/lib/features/offline_mode/work_manager/sending_email_worker.dart @@ -8,6 +8,8 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/account/authentication_type.dart'; +import 'package:model/account/personal_account.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/extensions/account_id_extensions.dart'; import 'package:model/extensions/session_extension.dart'; @@ -21,10 +23,9 @@ import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_authenticated_account_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_current_account_cache_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/bindings/sending_email_interactor_bindings.dart'; @@ -36,14 +37,16 @@ import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email. import 'package:tmail_ui_user/features/sending_queue/presentation/bindings/sending_queue_interactor_bindings.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart'; import 'package:tmail_ui_user/main/bindings/main_bindings.dart'; +import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class SendingEmailWorker extends Worker { SendEmailInteractor? _sendEmailInteractor; - GetAuthenticatedAccountInteractor? _getAuthenticatedAccountInteractor; + GetCurrentAccountCacheInteractor? _getCurrentAccountCacheInteractor; DynamicUrlInterceptors? _dynamicUrlInterceptors; AuthorizationInterceptors? _authorizationInterceptors; + AuthorizationInterceptors? _authorizationIsolateInterceptors; GetSessionInteractor? _getSessionInteractor; SendingQueueIsolateManager? _sendingQueueIsolateManager; SendingEmailCacheManager? _sendingEmailCacheManager; @@ -70,7 +73,7 @@ class SendingEmailWorker extends Worker { sendingId: _sendingEmail.sendingId, sendingState: SendingState.running ); - _getAuthenticatedAccount(); + _getCurrentAccountCache(); return _completer.future; } @@ -97,10 +100,8 @@ class SendingEmailWorker extends Worker { log('SendingEmailObserver::_handleFailureViewState(): $failure'); if (failure is SendEmailFailure) { _handleSendEmailFailure(failure); - } else if (failure is GetAuthenticatedAccountFailure || - failure is GetSessionFailure || - failure is GetStoredTokenOidcFailure || - failure is GetCredentialFailure) { + } else if (failure is GetCurrentAccountCacheFailure + || failure is GetSessionFailure) { _handleWorkerTaskToRetry(); } else if (failure is UnsubscribeEmailFailure) { _handleWorkerTaskSuccess(); @@ -110,12 +111,11 @@ class SendingEmailWorker extends Worker { @override void handleSuccessViewState(Success success) { log('SendingEmailObserver::handleSuccessViewState(): $success'); - if (success is GetSessionSuccess) { + if (success is GetCurrentAccountCacheSuccess) { + _setUpInterceptor(success.account); + _getSessionAction(); + } else if (success is GetSessionSuccess) { _handleGetSessionSuccess(success); - } else if (success is GetStoredTokenOidcSuccess) { - _handleGetAccountByOidcSuccess(success); - } else if (success is GetCredentialViewState) { - _handleGetAccountByBasicAuthSuccess(success); } else if (success is SendEmailSuccess) { _handleSendEmailSuccess(success); } else if (success is UnsubscribeEmailSuccess) { @@ -130,9 +130,10 @@ class SendingEmailWorker extends Worker { } void _getInteractorBindings() { - _getAuthenticatedAccountInteractor = getBinding(); + _getCurrentAccountCacheInteractor = getBinding(); _dynamicUrlInterceptors = getBinding(); _authorizationInterceptors = getBinding(); + _authorizationIsolateInterceptors = getBinding(tag: BindingTag.isolateTag); _getSessionInteractor = getBinding(); _sendEmailInteractor = getBinding(); _sendingQueueIsolateManager = getBinding(); @@ -151,8 +152,8 @@ class SendingEmailWorker extends Worker { _sendingQueueIsolateManager?.addEvent(eventAction); } - void _getAuthenticatedAccount() { - consumeState(_getAuthenticatedAccountInteractor!.execute()); + void _getCurrentAccountCache() { + consumeState(_getCurrentAccountCacheInteractor!.execute()); } void _getSessionAction() { @@ -171,16 +172,6 @@ class SendingEmailWorker extends Worker { } } - void _handleGetAccountByBasicAuthSuccess(GetCredentialViewState credentialViewState) { - _dynamicUrlInterceptors?.setJmapUrl(credentialViewState.baseUrl.toString()); - _authorizationInterceptors?.setBasicAuthorization( - credentialViewState.userName, - credentialViewState.password, - ); - _dynamicUrlInterceptors?.changeBaseUrl(credentialViewState.baseUrl.toString()); - _getSessionAction(); - } - void _sendEmailAction(AccountId accountId, Session session) { consumeState( _sendEmailInteractor!.execute( @@ -201,14 +192,34 @@ class SendingEmailWorker extends Worker { } } - void _handleGetAccountByOidcSuccess(GetStoredTokenOidcSuccess storedTokenOidcSuccess) { - _dynamicUrlInterceptors?.setJmapUrl(storedTokenOidcSuccess.baseUrl.toString()); - _authorizationInterceptors?.setTokenAndAuthorityOidc( - newToken: storedTokenOidcSuccess.tokenOidc, - newConfig: storedTokenOidcSuccess.oidcConfiguration - ); - _dynamicUrlInterceptors?.changeBaseUrl(storedTokenOidcSuccess.baseUrl.toString()); - _getSessionAction(); + void _setUpInterceptor(PersonalAccount personalAccount) { + _dynamicUrlInterceptors?.setJmapUrl(personalAccount.baseUrl); + _dynamicUrlInterceptors?.changeBaseUrl(personalAccount.baseUrl); + + switch(personalAccount.authenticationType) { + case AuthenticationType.oidc: + _authorizationInterceptors?.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration + ); + _authorizationIsolateInterceptors?.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration + ); + break; + case AuthenticationType.basic: + _authorizationInterceptors?.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password, + ); + _authorizationIsolateInterceptors?.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password, + ); + break; + default: + break; + } } void _handleSendEmailSuccess(SendEmailSuccess success) { diff --git a/lib/features/push_notification/domain/usecases/destroy_firebase_registration_interactor.dart b/lib/features/push_notification/domain/usecases/destroy_firebase_registration_interactor.dart index 3178735b6b..2c1f2274cd 100644 --- a/lib/features/push_notification/domain/usecases/destroy_firebase_registration_interactor.dart +++ b/lib/features/push_notification/domain/usecases/destroy_firebase_registration_interactor.dart @@ -1,7 +1,6 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; -import 'package:fcm/model/firebase_registration_id.dart'; import 'package:tmail_ui_user/features/push_notification/domain/repository/fcm_repository.dart'; import 'package:tmail_ui_user/features/push_notification/domain/state/destroy_firebase_registration_state.dart'; @@ -11,10 +10,14 @@ class DestroyFirebaseRegistrationInteractor { DestroyFirebaseRegistrationInteractor(this._fcmRepository); - Stream> execute(FirebaseRegistrationId registrationId) async* { + Stream> execute() async* { try { yield Right(DestroyFirebaseRegistrationLoading()); - await _fcmRepository.destroyFirebaseRegistration(registrationId); + final registration = await _fcmRepository.getStoredFirebaseRegistration(); + await Future.wait([ + _fcmRepository.destroyFirebaseRegistration(registration.id!), + _fcmRepository.deleteFirebaseRegistrationCache(), + ], eagerError: true); yield Right(DestroyFirebaseRegistrationSuccess()); } catch (e) { yield Left(DestroyFirebaseRegistrationFailure(e)); diff --git a/lib/features/push_notification/presentation/controller/fcm_message_controller.dart b/lib/features/push_notification/presentation/controller/fcm_message_controller.dart index 9c69a7153d..086fb74c06 100644 --- a/lib/features/push_notification/presentation/controller/fcm_message_controller.dart +++ b/lib/features/push_notification/presentation/controller/fcm_message_controller.dart @@ -21,10 +21,10 @@ import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/home/presentation/home_bindings.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/token_oidc_extension.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_credential_state.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_current_account_cache_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/action/fcm_action.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart'; @@ -44,7 +44,7 @@ class FcmMessageController extends FcmBaseController { Session? _currentSession; UserName? _userName; - GetAuthenticatedAccountInteractor? _getAuthenticatedAccountInteractor; + GetCurrentAccountCacheInteractor? _getCurrentAccountCacheInteractor; DynamicUrlInterceptors? _dynamicUrlInterceptors; AuthorizationInterceptors? _authorizationInterceptors; GetSessionInteractor? _getSessionInteractor; @@ -190,7 +190,7 @@ class FcmMessageController extends FcmBaseController { } void _getInteractorBindings() { - _getAuthenticatedAccountInteractor = getBinding(); + _getCurrentAccountCacheInteractor = getBinding(); _dynamicUrlInterceptors = getBinding(); _authorizationInterceptors = getBinding(); _getSessionInteractor = getBinding(); @@ -199,59 +199,32 @@ class FcmMessageController extends FcmBaseController { } void _getAuthenticatedAccount({StateChange? stateChange}) { - if (_getAuthenticatedAccountInteractor != null) { - consumeState(_getAuthenticatedAccountInteractor!.execute(stateChange: stateChange)); + if (_getCurrentAccountCacheInteractor != null) { + consumeState(_getCurrentAccountCacheInteractor!.execute(stateChange: stateChange)); } } - void _handleGetAccountByOidcSuccess(GetStoredTokenOidcSuccess storedTokenOidcSuccess) { - _dynamicUrlInterceptors?.setJmapUrl(storedTokenOidcSuccess.baseUrl.toString()); - _authorizationInterceptors?.setTokenAndAuthorityOidc( - newToken: storedTokenOidcSuccess.tokenOidc, - newConfig: storedTokenOidcSuccess.oidcConfiguration - ); - - if (PlatformInfo.isAndroid) { - _dynamicUrlInterceptors?.changeBaseUrl(storedTokenOidcSuccess.baseUrl.toString()); - _getSessionAction(stateChange: storedTokenOidcSuccess.stateChange); + void _setUpInterceptors(PersonalAccount personalAccount) { + _dynamicUrlInterceptors?.setJmapUrl(personalAccount.baseUrl); + if (PlatformInfo.isIOS) { + _dynamicUrlInterceptors?.changeBaseUrl(personalAccount.apiUrl); } else { - _dynamicUrlInterceptors?.changeBaseUrl(storedTokenOidcSuccess.personalAccount.apiUrl); - - final accountId = storedTokenOidcSuccess.personalAccount.accountId; - final username = storedTokenOidcSuccess.personalAccount.userName; - final stateChange = storedTokenOidcSuccess.stateChange; - - if (accountId != null && username != null && stateChange != null) { - _pushActionFromRemoteMessageBackground( - accountId: accountId, - userName: username, - stateChange: stateChange); - } + _dynamicUrlInterceptors?.changeBaseUrl(personalAccount.baseUrl); } - } - void _handleGetAccountByBasicAuthSuccess(GetCredentialViewState credentialViewState) { - _dynamicUrlInterceptors?.setJmapUrl(credentialViewState.baseUrl.toString()); - _authorizationInterceptors?.setBasicAuthorization( - credentialViewState.userName, - credentialViewState.password, - ); - if (PlatformInfo.isAndroid) { - _dynamicUrlInterceptors?.changeBaseUrl(credentialViewState.baseUrl.toString()); - _getSessionAction(stateChange: credentialViewState.stateChange); - } else { - _dynamicUrlInterceptors?.changeBaseUrl(credentialViewState.personalAccount.apiUrl); - - final accountId = credentialViewState.personalAccount.accountId; - final username = credentialViewState.personalAccount.userName; - final stateChange = credentialViewState.stateChange; - - if (accountId != null && username != null && stateChange != null) { - _pushActionFromRemoteMessageBackground( - accountId: accountId, - userName: username, - stateChange: stateChange); - } + switch(personalAccount.authenticationType) { + case AuthenticationType.oidc: + _authorizationInterceptors?.setTokenAndAuthorityOidc( + newToken: personalAccount.tokenOidc, + newConfig: personalAccount.tokenOidc!.oidcConfiguration); + break; + case AuthenticationType.basic: + _authorizationInterceptors?.setBasicAuthorization( + personalAccount.basicAuth!.userName, + personalAccount.basicAuth!.password,); + break; + default: + break; } } @@ -306,10 +279,23 @@ class FcmMessageController extends FcmBaseController { log('FcmMessageController::_handleSuccessViewState(): $success'); if (success is GetSessionSuccess) { _handleGetSessionSuccess(success); - } else if (success is GetStoredTokenOidcSuccess) { - _handleGetAccountByOidcSuccess(success); - } else if (success is GetCredentialViewState) { - _handleGetAccountByBasicAuthSuccess(success); + } else if (success is GetCurrentAccountCacheSuccess) { + _setUpInterceptors(success.account); + + if (PlatformInfo.isIOS) { + final accountId = success.account.accountId; + final username = success.account.userName; + final stateChange = success.stateChange; + + if (accountId != null && username != null && stateChange != null) { + _pushActionFromRemoteMessageBackground( + accountId: accountId, + userName: username, + stateChange: stateChange); + } + } else { + _getSessionAction(stateChange: success.stateChange); + } } } } \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 3e90dab543..60e3fc0b59 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-03-19T12:10:23.549474", + "@@last_modified": "2024-07-11T08:42:47.523094", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3909,5 +3909,69 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "traceLog": "Trace Log", + "@traceLog": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "traceLogSettingExplanation": "Trace log to easily monitor log on mobile devices", + "@traceLogSettingExplanation": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "totalSize": "Total Size", + "@totalSize": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "exportFile": "Export file", + "@exportFile": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "youNeedToGrantFilesPermissionToExportFile": "You need to grant files permission to export files", + "@youNeedToGrantFilesPermissionToExportFile": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "exportTraceLogSuccess": "Export successful tracking logs at \"{path}\"", + "@exportTraceLogSuccess": { + "type": "text", + "placeholders_order": [ + "path" + ], + "placeholders": { + "path": {} + } + }, + "exportTraceLogFailed": "Export trace log failed", + "@exportTraceLogFailed": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "deleteTraceLogSuccess": "Delete successful tracking logs", + "@deleteTraceLogSuccess": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "deleteTraceLogFailed": "Delete trace log failed", + "@deleteTraceLogFailed": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "clearLogCache": "Clear log cache", + "@clearLogCache": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/bindings/core/core_bindings.dart b/lib/main/bindings/core/core_bindings.dart index 5feaf5f03c..9bdbf953a0 100644 --- a/lib/main/bindings/core/core_bindings.dart +++ b/lib/main/bindings/core/core_bindings.dart @@ -14,6 +14,7 @@ import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/base/before_unload_manager.dart'; import 'package:tmail_ui_user/features/sending_queue/presentation/utils/sending_queue_isolate_manager.dart'; +import 'package:tmail_ui_user/main/permissions/permission_service.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; import 'package:uuid/uuid.dart'; @@ -52,6 +53,7 @@ class CoreBindings extends Bindings { void _bindingDeviceManager() { Get.put(DeviceInfoPlugin()); Get.put(DeviceManager(Get.find())); + Get.put(PermissionService()); } void _bindingReceivingSharingStream() { diff --git a/lib/main/bindings/credential/credential_bindings.dart b/lib/main/bindings/credential/credential_bindings.dart index 82abf014d2..2c135a1584 100644 --- a/lib/main/bindings/credential/credential_bindings.dart +++ b/lib/main/bindings/credential/credential_bindings.dart @@ -1,5 +1,4 @@ import 'package:get/get.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; import 'package:tmail_ui_user/features/login/data/datasource/account_datasource.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_datasource.dart'; @@ -8,27 +7,20 @@ import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication import 'package:tmail_ui_user/features/login/data/datasource_impl/authentication_oidc_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/datasource_impl/hive_account_datasource_impl.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart'; import 'package:tmail_ui_user/features/login/data/repository/account_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/authentication_oidc_repository_impl.dart'; import 'package:tmail_ui_user/features/login/data/repository/authentication_repository_impl.dart'; -import 'package:tmail_ui_user/features/login/data/repository/credential_repository_impl.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_credential_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_token_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_basic_auth_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/update_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart'; @@ -37,31 +29,21 @@ class CredentialBindings extends InteractorsBindings { @override void bindingsInteractor() { - Get.put(GetCredentialInteractor(Get.find())); - Get.put(DeleteCredentialInteractor(Get.find())); - Get.put(LogoutOidcInteractor( - Get.find(), - Get.find(), - )); - Get.put(DeleteAuthorityOidcInteractor( + Get.put(LogoutCurrentAccountBasicAuthInteractor(Get.find())); + Get.put(LogoutCurrentAccountOidcInteractor( Get.find(), - Get.find()) - ); - Get.put(GetStoredTokenOidcInteractor( - Get.find(), - Get.find(), - )); - Get.put(GetAuthenticatedAccountInteractor( + Get.find())); + Get.put(LogoutCurrentAccountInteractor( Get.find(), - Get.find(), - Get.find(), + Get.find(), + Get.find(), )); + Get.put(GetCurrentAccountCacheInteractor(Get.find())); Get.put(AuthenticationInteractor( Get.find(), - Get.find(), Get.find() )); - Get.put(UpdateAuthenticationAccountInteractor(Get.find())); + Get.put(UpdateCurrentAccountCacheInteractor(Get.find())); } @override @@ -75,8 +57,6 @@ class CredentialBindings extends InteractorsBindings { Get.put(AuthenticationOIDCDataSourceImpl( Get.find(), Get.find(), - Get.find(), - Get.find(), Get.find(), Get.find(), )); @@ -91,7 +71,6 @@ class CredentialBindings extends InteractorsBindings { @override void bindingsRepository() { - Get.put(Get.find()); Get.put(Get.find()); Get.put(Get.find()); Get.put(Get.find()); @@ -99,10 +78,6 @@ class CredentialBindings extends InteractorsBindings { @override void bindingsRepositoryImpl() { - Get.put(CredentialRepositoryImpl( - Get.find(), - Get.find() - )); Get.put(AccountRepositoryImpl(Get.find())); Get.put(AuthenticationRepositoryImpl(Get.find())); Get.put(AuthenticationOIDCRepositoryImpl(Get.find())); diff --git a/lib/main/bindings/local/local_bindings.dart b/lib/main/bindings/local/local_bindings.dart index ae3a6faee0..37b099ef1f 100644 --- a/lib/main/bindings/local/local_bindings.dart +++ b/lib/main/bindings/local/local_bindings.dart @@ -5,7 +5,6 @@ import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/caching/clients/account_cache_client.dart'; -import 'package:tmail_ui_user/features/caching/clients/authentication_info_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/email_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/encryption_key_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/fcm_cache_client.dart'; @@ -20,15 +19,12 @@ import 'package:tmail_ui_user/features/caching/clients/recent_search_cache_clien import 'package:tmail_ui_user/features/caching/clients/sending_email_hive_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/session_hive_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/state_cache_client.dart'; -import 'package:tmail_ui_user/features/caching/clients/token_oidc_cache_client.dart'; import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_url_cache_manager.dart'; import 'package:tmail_ui_user/features/cleanup/data/local/recent_login_username_cache_manager.dart'; import 'package:tmail_ui_user/features/cleanup/data/local/recent_search_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/oidc_configuration_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/local_spam_report_manager.dart'; @@ -62,14 +58,10 @@ class LocalBindings extends Bindings { Get.put(EmailCacheManager(Get.find())); Get.put(RecentSearchCacheClient()); Get.put(RecentSearchCacheManager(Get.find())); - Get.put(TokenOidcCacheClient()); - Get.put(TokenOidcCacheManager(Get.find())); Get.put(AccountCacheClient()); Get.put(AccountCacheManager(Get.find())); Get.put(EncryptionKeyCacheClient()); Get.put(EncryptionKeyCacheManager(Get.find())); - Get.put(AuthenticationInfoCacheClient()); - Get.put(AuthenticationInfoCacheManager(Get.find())); Get.put(OidcConfigurationCacheManager(Get.find())); Get.put(LanguageCacheManager(Get.find())); Get.put(RecentLoginUrlCacheClient()); diff --git a/lib/main/bindings/local/local_isolate_bindings.dart b/lib/main/bindings/local/local_isolate_bindings.dart index 2777e5ad98..8691f3dac4 100644 --- a/lib/main/bindings/local/local_isolate_bindings.dart +++ b/lib/main/bindings/local/local_isolate_bindings.dart @@ -2,10 +2,8 @@ import 'package:get/get.dart'; import 'package:tmail_ui_user/features/caching/clients/account_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/encryption_key_cache_client.dart'; -import 'package:tmail_ui_user/features/caching/clients/token_oidc_cache_client.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/local/encryption_key_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; class LocalIsolateBindings extends Bindings { @@ -16,11 +14,6 @@ class LocalIsolateBindings extends Bindings { } void _bindingCaching() { - Get.put(TokenOidcCacheClient(), tag: BindingTag.isolateTag); - Get.put(TokenOidcCacheManager( - Get.find(tag: BindingTag.isolateTag)), - tag: BindingTag.isolateTag - ); Get.put(AccountCacheClient(), tag: BindingTag.isolateTag); Get.put(AccountCacheManager( Get.find(tag: BindingTag.isolateTag)), diff --git a/lib/main/bindings/network/network_bindings.dart b/lib/main/bindings/network/network_bindings.dart index 208c47854d..fc92bd8b73 100644 --- a/lib/main/bindings/network/network_bindings.dart +++ b/lib/main/bindings/network/network_bindings.dart @@ -15,8 +15,6 @@ import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/network/mdn_api.dart'; import 'package:tmail_ui_user/features/home/data/network/session_api.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/dns_service.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; @@ -75,8 +73,6 @@ class NetworkBindings extends Bindings { Get.put(IOSSharingManager( Get.find(), Get.find(), - Get.find(), - Get.find(), )); } @@ -85,7 +81,6 @@ class NetworkBindings extends Bindings { Get.put(AuthorizationInterceptors( Get.find(), Get.find(), - Get.find(), Get.find(), Get.find(), )); diff --git a/lib/main/bindings/network/network_isolate_binding.dart b/lib/main/bindings/network/network_isolate_binding.dart index a7b81d0fdb..d2ca1981ba 100644 --- a/lib/main/bindings/network/network_isolate_binding.dart +++ b/lib/main/bindings/network/network_isolate_binding.dart @@ -7,7 +7,6 @@ import 'package:get/get.dart'; import 'package:jmap_dart_client/http/http_client.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/data/utils/library_platform/app_auth_plugin/app_auth_plugin.dart'; @@ -41,7 +40,6 @@ class NetworkIsolateBindings extends Bindings { Get.put(AuthorizationInterceptors( dio, Get.find(tag: BindingTag.isolateTag), - Get.find(tag: BindingTag.isolateTag), Get.find(tag: BindingTag.isolateTag), Get.find(), ), tag: BindingTag.isolateTag); diff --git a/lib/main/exceptions/permission_exception.dart b/lib/main/exceptions/permission_exception.dart new file mode 100644 index 0000000000..c0b168e453 --- /dev/null +++ b/lib/main/exceptions/permission_exception.dart @@ -0,0 +1,16 @@ + +import 'package:equatable/equatable.dart'; + +abstract class PermissionException with EquatableMixin implements Exception { + + final String? message; + + const PermissionException({this.message}); +} + +class NotGrantedPermissionStorageException extends PermissionException { + const NotGrantedPermissionStorageException() : super(message: 'Permission Storage has not been granted access'); + + @override + List get props => [super.message]; +} \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 2f1733d719..295f9ef9bd 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4085,4 +4085,66 @@ class AppLocalizations { name: 'showNotifications', ); } + + String get traceLog { + return Intl.message( + 'Trace Log', + name: 'traceLog'); + } + + String get traceLogSettingExplanation { + return Intl.message( + 'Trace log to easily monitor log on mobile devices', + name: 'traceLogSettingExplanation'); + } + + String get totalSize { + return Intl.message( + 'Total Size', + name: 'totalSize'); + } + + String get exportFile { + return Intl.message( + 'Export file', + name: 'exportFile'); + } + + String get youNeedToGrantFilesPermissionToExportFile { + return Intl.message( + 'You need to grant files permission to export files', + name: 'youNeedToGrantFilesPermissionToExportFile' + ); + } + + String exportTraceLogSuccess(String path) { + return Intl.message( + 'Export successful tracking logs at "$path"', + name: 'exportTraceLogSuccess', + args: [path]); + } + + String get exportTraceLogFailed { + return Intl.message( + 'Export trace log failed', + name: 'exportTraceLogFailed'); + } + + String get deleteTraceLogSuccess { + return Intl.message( + 'Delete successful tracking logs', + name: 'deleteTraceLogSuccess'); + } + + String get deleteTraceLogFailed { + return Intl.message( + 'Delete trace log failed', + name: 'deleteTraceLogFailed'); + } + + String get clearLogCache { + return Intl.message( + 'Clear log cache', + name: 'clearLogCache'); + } } \ No newline at end of file diff --git a/lib/main/utils/ios_sharing_manager.dart b/lib/main/utils/ios_sharing_manager.dart index bb261bea1f..c31fb03253 100644 --- a/lib/main/utils/ios_sharing_manager.dart +++ b/lib/main/utils/ios_sharing_manager.dart @@ -1,14 +1,8 @@ - -import 'dart:convert'; - import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:model/account/personal_account.dart'; -import 'package:model/oidc/token_oidc.dart'; -import 'package:tmail_ui_user/features/login/data/local/authentication_info_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; +import 'package:tmail_ui_user/features/login/data/extensions/basic_auth_extension.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart'; import 'package:tmail_ui_user/features/push_notification/data/extensions/keychain_sharing_session_extension.dart'; @@ -18,14 +12,10 @@ import 'package:tmail_ui_user/features/push_notification/data/keychain/keychain_ class IOSSharingManager { final KeychainSharingManager _keychainSharingManager; final StateCacheManager _stateCacheManager; - final TokenOidcCacheManager _tokenOidcCacheManager; - final AuthenticationInfoCacheManager _authenticationInfoCacheManager; IOSSharingManager( this._keychainSharingManager, this._stateCacheManager, - this._tokenOidcCacheManager, - this._authenticationInfoCacheManager ); bool _validateToSaveKeychain(PersonalAccount personalAccount) { @@ -51,14 +41,6 @@ class IOSSharingManager { return Future.value(null); } - Tuple2 authenticationInfo = await Future.wait( - [ - _getTokenOidc(tokeHashId: personalAccount.id), - _getCredentialAuthentication() - ], - eagerError: true - ).then((listValue) => Tuple2(listValue[0] as TokenOIDC?, listValue[1] as String?)); - final emailDeliveryState = await _getEmailDeliveryState( accountId: personalAccount.accountId!, userName: personalAccount.userName! @@ -76,8 +58,8 @@ class IOSSharingManager { apiUrl: personalAccount.apiUrl!, emailState: emailState, emailDeliveryState: emailDeliveryState, - tokenOIDC: authenticationInfo.value1, - basicAuth: authenticationInfo.value2 + tokenOIDC: personalAccount.tokenOidc, + basicAuth: personalAccount.basicAuth?.authenticationHeader ); log('IOSSharingManager::_saveKeyChainSharingSession: $keychainSharingSession'); await _keychainSharingManager.save(keychainSharingSession); @@ -99,28 +81,6 @@ class IOSSharingManager { } } - Future _getTokenOidc({required String tokeHashId}) async { - try { - final tokenOidc = await _tokenOidcCacheManager.getTokenOidc(tokeHashId); - log('IOSSharingManager::_getTokenOidc:tokenOidc: $tokenOidc'); - return tokenOidc; - } catch (e) { - logError('IOSSharingManager::_getTokenOidc:Exception: $e'); - return null; - } - } - - Future _getCredentialAuthentication() async { - try { - final credentialInfo = await _authenticationInfoCacheManager.getAuthenticationInfoStored(); - log('IOSSharingManager::_getCredentialAuthentication:credentialInfo: $credentialInfo'); - return base64Encode(utf8.encode('${credentialInfo.username}:${credentialInfo.password}')); - } catch (e) { - logError('IOSSharingManager::_getCredentialAuthentication:Exception: $e'); - return null; - } - } - Future _getEmailDeliveryState({ required AccountId accountId, required UserName userName diff --git a/model/lib/account/account_request.dart b/model/lib/account/account_request.dart deleted file mode 100644 index b8b994fc20..0000000000 --- a/model/lib/account/account_request.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:convert'; - -import 'package:equatable/equatable.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/account/authentication_type.dart'; -import 'package:model/account/password.dart'; -import 'package:model/oidc/token_oidc.dart'; - -class AccountRequest with EquatableMixin { - final UserName? userName; - final Password? password; - final TokenOIDC? token; - final AuthenticationType authenticationType; - - AccountRequest({ - this.userName, - this.password, - this.token, - this.authenticationType = AuthenticationType.none, - }); - - factory AccountRequest.withOidc({required TokenOIDC token}) { - return AccountRequest( - token: token, - authenticationType: AuthenticationType.oidc - ); - } - - factory AccountRequest.withBasic({ - required UserName userName, - required Password password - }) { - return AccountRequest( - userName: userName, - password: password, - authenticationType: AuthenticationType.basic - ); - } - - String get basicAuth => 'Basic ${base64Encode(utf8.encode('${userName?.value}:${password?.value}'))}'; - - String get bearerToken => 'Bearer ${token?.token}'; - - @override - List get props => [userName, password]; -} \ No newline at end of file diff --git a/model/lib/account/account_type.dart b/model/lib/account/account_type.dart deleted file mode 100644 index bac6626b19..0000000000 --- a/model/lib/account/account_type.dart +++ /dev/null @@ -1,5 +0,0 @@ - -enum AccountType { - oauth, - email -} \ No newline at end of file diff --git a/model/lib/account/basic_auth.dart b/model/lib/account/basic_auth.dart new file mode 100644 index 0000000000..dc568582d8 --- /dev/null +++ b/model/lib/account/basic_auth.dart @@ -0,0 +1,13 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:model/account/password.dart'; + +class BasicAuth with EquatableMixin { + final UserName userName; + final Password password; + + BasicAuth(this.userName, this.password); + + @override + List get props => [userName, password]; +} \ No newline at end of file diff --git a/model/lib/account/personal_account.dart b/model/lib/account/personal_account.dart index 0042a33191..e5b0ac92a4 100644 --- a/model/lib/account/personal_account.dart +++ b/model/lib/account/personal_account.dart @@ -2,6 +2,8 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:model/account/authentication_type.dart'; +import 'package:model/account/basic_auth.dart'; +import 'package:model/oidc/token_oidc.dart'; class PersonalAccount with EquatableMixin { final String id; @@ -10,17 +12,21 @@ class PersonalAccount with EquatableMixin { final AccountId? accountId; final String? apiUrl; final UserName? userName; + final String baseUrl; + final BasicAuth? basicAuth; + final TokenOIDC? tokenOidc; - PersonalAccount( - this.id, - this.authenticationType, - { - required this.isSelected, - this.accountId, - this.apiUrl, - this.userName - } - ); + PersonalAccount({ + required this.id, + required this.authenticationType, + required this.isSelected, + required this.baseUrl, + this.accountId, + this.apiUrl, + this.userName, + this.basicAuth, + this.tokenOidc, + }); @override List get props => [ @@ -29,6 +35,9 @@ class PersonalAccount with EquatableMixin { isSelected, accountId, apiUrl, - userName + userName, + baseUrl, + basicAuth, + tokenOidc, ]; } \ No newline at end of file diff --git a/model/lib/extensions/personal_account_extension.dart b/model/lib/extensions/personal_account_extension.dart deleted file mode 100644 index aaada3c3b0..0000000000 --- a/model/lib/extensions/personal_account_extension.dart +++ /dev/null @@ -1,21 +0,0 @@ - -import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/account/personal_account.dart'; - -extension PersonalAccountExtension on PersonalAccount { - - PersonalAccount fromAccount({ - required AccountId accountId, - required String apiUrl, - required UserName userName, - }) { - return PersonalAccount( - id, - authenticationType, - isSelected: isSelected, - accountId: accountId, - apiUrl: apiUrl, - userName: userName); - } -} \ No newline at end of file diff --git a/model/lib/extensions/session_extension.dart b/model/lib/extensions/session_extension.dart index 48126265c0..f3661ca9b1 100644 --- a/model/lib/extensions/session_extension.dart +++ b/model/lib/extensions/session_extension.dart @@ -67,6 +67,8 @@ extension SessionExtension on Session { throw NotFoundPersonalAccountException(); } + AccountId get accountId => personalAccount.accountId; + ({ bool isAvailable, CalendarEventCapability? calendarEventCapability diff --git a/model/lib/model.dart b/model/lib/model.dart index 2835ec4dfa..98aad49bfa 100644 --- a/model/lib/model.dart +++ b/model/lib/model.dart @@ -1,8 +1,7 @@ library model; -export 'account/account_request.dart'; -export 'account/account_type.dart'; export 'account/authentication_type.dart'; +export 'account/basic_auth.dart'; export 'account/jmap_account.dart'; export 'account/password.dart'; // Account @@ -52,7 +51,6 @@ export 'extensions/mailbox_extension.dart'; export 'extensions/mailbox_id_extension.dart'; export 'extensions/mailbox_name_extension.dart'; export 'extensions/media_type_nullable_extension.dart'; -export 'extensions/personal_account_extension.dart'; export 'extensions/presentation_email_extension.dart'; export 'extensions/presentation_mailbox_extension.dart'; export 'extensions/properties_extension.dart'; diff --git a/model/lib/oidc/token_oidc.dart b/model/lib/oidc/token_oidc.dart index 05afa3448e..dafd644710 100644 --- a/model/lib/oidc/token_oidc.dart +++ b/model/lib/oidc/token_oidc.dart @@ -1,5 +1,4 @@ -import 'package:core/utils/app_logger.dart'; import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:model/oidc/converter/token_id_converter.dart'; @@ -15,35 +14,26 @@ class TokenOIDC with EquatableMixin { final TokenId tokenId; final DateTime? expiredTime; final String refreshToken; + final String authority; - TokenOIDC( - this.token, - this.tokenId, - this.refreshToken, - {this.expiredTime} - ); + TokenOIDC({ + required this.token, + required this.tokenId, + required this.refreshToken, + required this.authority, + this.expiredTime + }); factory TokenOIDC.fromJson(Map json) => _$TokenOIDCFromJson(json); Map toJson() => _$TokenOIDCToJson(this); @override - List get props => [token, tokenId, expiredTime, refreshToken]; -} - -extension TokenOIDCExtension on TokenOIDC { - - bool isTokenValid() => token.isNotEmpty && tokenId.uuid.isNotEmpty; - - String get tokenIdHash => tokenId.uuid.hashCode.toString(); - - bool get isExpired { - if (expiredTime != null) { - final now = DateTime.now(); - log('TokenOIDC::isExpired(): TIME_NOW: $now'); - log('TokenOIDC::isExpired(): EXPIRED_DATE: $expiredTime'); - return expiredTime!.isBefore(now); - } - return false; - } + List get props => [ + token, + tokenId, + expiredTime, + refreshToken, + authority + ]; } \ No newline at end of file diff --git a/test/features/caching/accountl_cache_test.dart b/test/features/caching/accountl_cache_test.dart index 7e75f126aa..d3ee206209 100644 --- a/test/features/caching/accountl_cache_test.dart +++ b/test/features/caching/accountl_cache_test.dart @@ -7,33 +7,37 @@ void main() { group('AccountCache test', () { test('removeDuplicated should remove duplicate accountId', () async { final account1 = AccountCache( - '1', - 'oidc', + id: '1', + authenticationType: 'oidc', isSelected: true, + baseUrl: 'https://example.com', accountId: '1', userName: '1', apiUrl: 'https://example.com/jmap' ); final account2 = AccountCache( - '2', - 'oidc', + id: '2', + authenticationType: 'oidc', isSelected: true, + baseUrl: 'https://example.com', accountId: '1', userName: '1', apiUrl: 'https://example.com/jmap' ); final account3 = AccountCache( - '3', - 'basic', + id: '3', + authenticationType: 'basic', isSelected: true, + baseUrl: 'https://example.com', accountId: '2', userName: '2', apiUrl: 'https://example.com/jmap' ); final account4 = AccountCache( - '4', - 'basic', + id: '4', + authenticationType: 'basic', isSelected: true, + baseUrl: 'https://example.com', accountId: '2', userName: '2', apiUrl: 'https://example.com/jmap' diff --git a/test/features/email/presentation/controller/single_email_controller_test.dart b/test/features/email/presentation/controller/single_email_controller_test.dart index 82ae55253c..b2d850ba83 100644 --- a/test/features/email/presentation/controller/single_email_controller_test.dart +++ b/test/features/email/presentation/controller/single_email_controller_test.dart @@ -39,12 +39,10 @@ import 'package:tmail_ui_user/features/email/presentation/controller/email_super import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_event.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; import 'package:uuid/uuid.dart'; @@ -75,9 +73,7 @@ const fallbackGenerators = { MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), - MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -111,9 +107,8 @@ void main() { final languageCacheManager = MockLanguageCacheManager(); final authorizationInterceptors = MockAuthorizationInterceptors(); final dynamicUrlInterceptors = MockDynamicUrlInterceptors(); - final deleteCredentialInteractor = MockDeleteCredentialInteractor(); - final logoutOidcInteractor = MockLogoutOidcInteractor(); - final deleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor(); + final logoutCurrentAccountInteractor = MockLogoutCurrentAccountInteractor(); + final appToast = MockAppToast(); final imagePaths = MockImagePaths(); final responsiveUtils = MockResponsiveUtils(); @@ -147,9 +142,7 @@ void main() { tag: BindingTag.isolateTag, ); Get.put(dynamicUrlInterceptors); - Get.put(deleteCredentialInteractor); - Get.put(logoutOidcInteractor); - Get.put(deleteAuthorityOidcInteractor); + Get.put(logoutCurrentAccountInteractor); Get.put(appToast); Get.put(imagePaths); Get.put(responsiveUtils); diff --git a/test/features/home/presentation/home_controller_test.dart b/test/features/home/presentation/home_controller_test.dart index fff4bf85f7..f9c2a7e539 100644 --- a/test/features/home/presentation/home_controller_test.dart +++ b/test/features/home/presentation/home_controller_test.dart @@ -19,13 +19,11 @@ import 'package:tmail_ui_user/features/cleanup/domain/usecases/cleanup_recent_se import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/home/presentation/home_controller.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/state/get_stored_token_oidc_state.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/state/get_current_account_cache_state.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/update_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; import 'package:tmail_ui_user/main/utils/email_receive_manager.dart'; import 'package:uuid/uuid.dart'; @@ -35,9 +33,6 @@ import 'home_controller_test.mocks.dart'; @GenerateNiceMocks([ MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), - MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -48,8 +43,9 @@ import 'home_controller_test.mocks.dart'; MockSpec(), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -65,16 +61,14 @@ void main() { late MockCleanupRecentLoginUsernameCacheInteractor cleanupRecentLoginUsernameCacheInteractor; late MockGetSessionInteractor mockGetSessionInteractor; - late MockGetAuthenticatedAccountInteractor mockGetAuthenticatedAccountInteractor; - late MockUpdateAuthenticationAccountInteractor mockUpdateAuthenticationAccountInteractor; + late MockGetCurrentAccountCacheInteractor mockGetCurrentAccountCacheInteractor; + late MockUpdateCurrentAccountCacheInteractor mockUpdateCurrentAccountCacheInteractor; + late MockLogoutCurrentAccountInteractor mockLogoutCurrentAccountInteractor; late MockCachingManager mockCachingManager; late MockLanguageCacheManager mockLanguageCacheManager; late MockAuthorizationInterceptors mockAuthorizationInterceptors; late MockDynamicUrlInterceptors mockDynamicUrlInterceptors; - late MockDeleteCredentialInteractor mockDeleteCredentialInteractor; - late MockLogoutOidcInteractor mockLogoutOidcInteractor; - late MockDeleteAuthorityOidcInteractor mockDeleteAuthorityOidcInteractor; late MockAppToast mockAppToast; late MockImagePaths mockImagePaths; late MockResponsiveUtils mockResponsiveUtils; @@ -90,17 +84,15 @@ void main() { // mock reloadable controller mockGetSessionInteractor = MockGetSessionInteractor(); - mockGetAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor(); - mockUpdateAuthenticationAccountInteractor = MockUpdateAuthenticationAccountInteractor(); + mockGetCurrentAccountCacheInteractor = MockGetCurrentAccountCacheInteractor(); + mockUpdateCurrentAccountCacheInteractor = MockUpdateCurrentAccountCacheInteractor(); + mockLogoutCurrentAccountInteractor = MockLogoutCurrentAccountInteractor(); //mock base controller mockCachingManager = MockCachingManager(); mockLanguageCacheManager = MockLanguageCacheManager(); mockAuthorizationInterceptors = MockAuthorizationInterceptors(); mockDynamicUrlInterceptors = MockDynamicUrlInterceptors(); - mockDeleteCredentialInteractor = MockDeleteCredentialInteractor(); - mockLogoutOidcInteractor = MockLogoutOidcInteractor(); - mockDeleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor(); mockAppToast = MockAppToast(); mockImagePaths = MockImagePaths(); mockResponsiveUtils = MockResponsiveUtils(); @@ -108,8 +100,9 @@ void main() { mockApplicationManager = MockApplicationManager(); Get.put(mockGetSessionInteractor); - Get.put(mockGetAuthenticatedAccountInteractor); - Get.put(mockUpdateAuthenticationAccountInteractor); + Get.put(mockGetCurrentAccountCacheInteractor); + Get.put(mockUpdateCurrentAccountCacheInteractor); + Get.put(mockLogoutCurrentAccountInteractor); Get.put(mockCachingManager); Get.put(mockLanguageCacheManager); @@ -119,9 +112,6 @@ void main() { tag: BindingTag.isolateTag, ); Get.put(mockDynamicUrlInterceptors); - Get.put(mockDeleteCredentialInteractor); - Get.put(mockLogoutOidcInteractor); - Get.put(mockDeleteAuthorityOidcInteractor); Get.put(mockAppToast); Get.put(mockImagePaths); Get.put(mockResponsiveUtils); @@ -139,10 +129,10 @@ void main() { }); group("HomeController::onData test", () { - test("WHEN onData receive `GetStoredTokenOidcFailure` " + test("WHEN onData receive `GetCurrentAccountCacheFailure` " "THEN handleFailureViewState should be called", () { // Arrange - final failure = Left(GetStoredTokenOidcFailure(Exception())); + final failure = Left(GetCurrentAccountCacheFailure(Exception())); when(mockAuthorizationInterceptors.authenticationType).thenReturn(AuthenticationType.oidc); // Act diff --git a/test/features/interceptor/authorization_interceptor_test.dart b/test/features/interceptor/authorization_interceptor_test.dart index fcc70b1cc5..e61e998b83 100644 --- a/test/features/interceptor/authorization_interceptor_test.dart +++ b/test/features/interceptor/authorization_interceptor_test.dart @@ -9,11 +9,9 @@ import 'package:http_mock_adapter/http_mock_adapter.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart'; -import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart'; import '../../fixtures/account_fixtures.dart'; @@ -22,7 +20,6 @@ import 'authorization_interceptor_test.mocks.dart'; @GenerateMocks([ AuthenticationClientBase, - TokenOidcCacheManager, AccountCacheManager, IOSSharingManager ]) @@ -30,7 +27,6 @@ void main() { late Dio dio; late DioAdapter dioAdapter; late AuthenticationClientBase authenticationClient; - late TokenOidcCacheManager tokenOidcCacheManager; late AccountCacheManager accountCacheManager; late IOSSharingManager iosSharingManager; late AuthorizationInterceptors authorizationInterceptors; @@ -62,14 +58,12 @@ void main() { ..options.baseUrl = baseUrl; authenticationClient = MockAuthenticationClientBase(); - tokenOidcCacheManager = MockTokenOidcCacheManager(); accountCacheManager = MockAccountCacheManager(); iosSharingManager = MockIOSSharingManager(); authorizationInterceptors = AuthorizationInterceptors( dio, authenticationClient, - tokenOidcCacheManager, accountCacheManager, iosSharingManager); @@ -178,10 +172,7 @@ void main() { ); when(authenticationClient.refreshingTokensOIDC( - OIDCFixtures.oidcConfiguration.clientId, - OIDCFixtures.oidcConfiguration.redirectUrl, - OIDCFixtures.oidcConfiguration.discoveryUrl, - OIDCFixtures.oidcConfiguration.scopes, + OIDCFixtures.oidcConfiguration, OIDCFixtures.tokenOidcExpiredTime.refreshToken )).thenAnswer((_) async { dioAdapter.onPost( @@ -218,10 +209,7 @@ void main() { ); when(authenticationClient.refreshingTokensOIDC( - OIDCFixtures.oidcConfiguration.clientId, - OIDCFixtures.oidcConfiguration.redirectUrl, - OIDCFixtures.oidcConfiguration.discoveryUrl, - OIDCFixtures.oidcConfiguration.scopes, + OIDCFixtures.oidcConfiguration, OIDCFixtures.tokenOidcExpiredTime.refreshToken )).thenAnswer((_) async { dioAdapter.onPost( @@ -285,10 +273,7 @@ void main() { ); when(authenticationClient.refreshingTokensOIDC( - OIDCFixtures.oidcConfiguration.clientId, - OIDCFixtures.oidcConfiguration.redirectUrl, - OIDCFixtures.oidcConfiguration.discoveryUrl, - OIDCFixtures.oidcConfiguration.scopes, + OIDCFixtures.oidcConfiguration, OIDCFixtures.tokenOidcExpiredTime.refreshToken )).thenAnswer((_) async { dioAdapter.onPost( @@ -311,10 +296,7 @@ void main() { ]); verify(authenticationClient.refreshingTokensOIDC( - OIDCFixtures.oidcConfiguration.clientId, - OIDCFixtures.oidcConfiguration.redirectUrl, - OIDCFixtures.oidcConfiguration.discoveryUrl, - OIDCFixtures.oidcConfiguration.scopes, + OIDCFixtures.oidcConfiguration, OIDCFixtures.tokenOidcExpiredTime.refreshToken )).called(1); @@ -345,10 +327,7 @@ void main() { ); when(authenticationClient.refreshingTokensOIDC( - OIDCFixtures.oidcConfiguration.clientId, - OIDCFixtures.oidcConfiguration.redirectUrl, - OIDCFixtures.oidcConfiguration.discoveryUrl, - OIDCFixtures.oidcConfiguration.scopes, + OIDCFixtures.oidcConfiguration, OIDCFixtures.tokenOidcExpiredTime.refreshToken )).thenAnswer((_) async { throw AccessTokenInvalidException(); diff --git a/test/features/login/data/local/account_cache_manager_test.dart b/test/features/login/data/local/account_cache_manager_test.dart index dd4647c248..bbd287f288 100644 --- a/test/features/login/data/local/account_cache_manager_test.dart +++ b/test/features/login/data/local/account_cache_manager_test.dart @@ -24,12 +24,13 @@ void main() { 'THEN cache will have only new account', () async { // arrange final personalAccount3 = PersonalAccount( - '834191067', - AuthenticationType.oidc, + id: '834191067', + authenticationType: AuthenticationType.oidc, isSelected: true, accountId: AccountId(Id( 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246')), apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: UserName('username@domain.com'), ); @@ -48,22 +49,24 @@ void main() { 'THEN cache will have only new account', () async { // arrange final account1 = AccountCache( - '253956617', - 'oidc', + id: '253956617', + authenticationType: 'oidc', isSelected: true, accountId: 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246', apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: 'username@domain.com', ); final personalAccount3 = PersonalAccount( - '834191067', - AuthenticationType.oidc, + id: '834191067', + authenticationType: AuthenticationType.oidc, isSelected: true, accountId: AccountId(Id( 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246')), apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: UserName('username@domain.com'), ); @@ -86,30 +89,33 @@ void main() { 'THEN cache will have only new account', () async { // arrange final account1 = AccountCache( - '253956617', - 'oidc', + id: '253956617', + authenticationType: 'oidc', isSelected: true, accountId: 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246', apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: 'username@domain.com', ); final account2 = AccountCache( - '60734964', - 'oidc', + id: '60734964', + authenticationType: 'oidc', isSelected: true, accountId: 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246', apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: 'username@domain.com', ); final personalAccount3 = PersonalAccount( - '834191067', - AuthenticationType.oidc, + id: '834191067', + authenticationType: AuthenticationType.oidc, isSelected: true, accountId: AccountId(Id( 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246')), apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: UserName('username@domain.com'), ); @@ -140,22 +146,24 @@ void main() { 'THEN cache will update with new selection', () async { // arrange final account1 = AccountCache( - '253956617', - 'oidc', + id: '253956617', + authenticationType: 'oidc', isSelected: true, accountId: '5c2e278644f9517aaf25e8246ae08b34da40b48f30ec0b94db05675894262fbc', apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: 'name@domain.com', ); final personalAccount3 = PersonalAccount( - '834191067', - AuthenticationType.oidc, + id: '834191067', + authenticationType: AuthenticationType.oidc, isSelected: true, accountId: AccountId(Id( 'ae08b34da40b48f30ec0b94db05675894262fbc5c2e278644f9517aaf25e8246')), apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: UserName('username@domain.com'), ); @@ -181,30 +189,33 @@ void main() { 'THEN cache will update with new selected account', () async { // arrange final account1 = AccountCache( - '253956617', - 'oidc', + id: '253956617', + authenticationType: 'oidc', isSelected: true, accountId: 'f30ec0b94db05675894262fbc5c2e27ae08b34da40b488644f9517aaf25e8246', apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: 'usernameA@domain.com', ); final account2 = AccountCache( - '60734964', - 'oidc', + id: '60734964', + authenticationType: 'oidc', isSelected: true, accountId: '4db05675894262fbc5c2e27ae08b34da40b48f30ec0b98644f9517aaf25e8246', apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: 'usernameB@domain.com', ); final personalAccount3 = PersonalAccount( - '834191067', - AuthenticationType.oidc, + id: '834191067', + authenticationType: AuthenticationType.oidc, isSelected: true, accountId: AccountId(Id( '5675894262fbc5c2e278644f9517aaf25e8246ae08b34da40b48f30ec0b94db0')), apiUrl: 'https://jmap.domain.com/oidc/jmap', + baseUrl: 'https://jmap.domain.com/', userName: UserName('usernameC@domain.com'), ); diff --git a/test/features/login/presentation/login_controller_test.dart b/test/features/login/presentation/login_controller_test.dart index 0c9750e576..6ef9769f07 100644 --- a/test/features/login/presentation/login_controller_test.dart +++ b/test/features/login/presentation/login_controller_test.dart @@ -15,24 +15,19 @@ import 'package:tmail_ui_user/features/login/domain/state/get_token_oidc_state.d import 'package:tmail_ui_user/features/login/domain/usecases/authenticate_oidc_on_browser_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/authentication_user_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/check_oidc_is_available_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/dns_lookup_to_get_jmap_url_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_all_recent_login_username_on_mobile_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authentication_info_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_configuration_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_oidc_is_available_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_auth_response_url_browser_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_token_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username_on_mobile_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/update_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/login/presentation/login_controller.dart'; import 'package:tmail_ui_user/features/login/presentation/login_form_type.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; import 'package:uuid/uuid.dart'; @@ -41,29 +36,24 @@ import 'login_controller_test.mocks.dart'; @GenerateNiceMocks([ MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), - MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -71,27 +61,22 @@ import 'login_controller_test.mocks.dart'; void main() { late MockAuthenticationInteractor mockAuthenticationInteractor; late MockCheckOIDCIsAvailableInteractor mockCheckOIDCIsAvailableInteractor; - late MockGetOIDCIsAvailableInteractor mockGetOIDCIsAvailableInteractor; - late MockGetOIDCConfigurationInteractor mockGetOIDCConfigurationInteractor; late MockGetTokenOIDCInteractor mockGetTokenOIDCInteractor; late MockAuthenticateOidcOnBrowserInteractor mockAuthenticateOidcOnBrowserInteractor; - late MockGetAuthenticationInfoInteractor mockGetAuthenticationInfoInteractor; - late MockGetStoredOidcConfigurationInteractor mockGetStoredOidcConfigurationInteractor; + late MockGetAuthResponseUrlBrowserInteractor mockGetAuthResponseUrlBrowserInteractor; late MockSaveLoginUrlOnMobileInteractor mockSaveLoginUrlOnMobileInteractor; late MockGetAllRecentLoginUrlOnMobileInteractor mockGetAllRecentLoginUrlOnMobileInteractor; late MockSaveLoginUsernameOnMobileInteractor mockSaveLoginUsernameOnMobileInteractor; late MockGetAllRecentLoginUsernameOnMobileInteractor mockGetAllRecentLoginUsernameOnMobileInteractor; late MockDNSLookupToGetJmapUrlInteractor mockDNSLookupToGetJmapUrlInteractor; late MockGetSessionInteractor mockGetSessionInteractor; - late MockGetAuthenticatedAccountInteractor mockGetAuthenticatedAccountInteractor; - late MockUpdateAuthenticationAccountInteractor mockUpdateAuthenticationAccountInteractor; + late MockGetCurrentAccountCacheInteractor mockGetCurrentAccountCacheInteractor; + late MockUpdateCurrentAccountCacheInteractor mockUpdateCurrentAccountCacheInteractor; + late LogoutCurrentAccountInteractor mockLogoutCurrentAccountInteractor; late CachingManager mockCachingManager; late LanguageCacheManager mockLanguageCacheManager; late MockAuthorizationInterceptors mockAuthorizationInterceptors; late MockDynamicUrlInterceptors mockDynamicUrlInterceptors; - late MockDeleteCredentialInteractor mockDeleteCredentialInteractor; - late MockLogoutOidcInteractor mockLogoutOidcInteractor; - late MockDeleteAuthorityOidcInteractor mockDeleteAuthorityOidcInteractor; late MockAppToast mockAppToast; late MockImagePaths mockImagePaths; late MockResponsiveUtils mockResponsiveUtils; @@ -104,12 +89,9 @@ void main() { setUp(() { mockAuthenticationInteractor = MockAuthenticationInteractor(); mockCheckOIDCIsAvailableInteractor = MockCheckOIDCIsAvailableInteractor(); - mockGetOIDCIsAvailableInteractor = MockGetOIDCIsAvailableInteractor(); - mockGetOIDCConfigurationInteractor = MockGetOIDCConfigurationInteractor(); + mockGetAuthResponseUrlBrowserInteractor = MockGetAuthResponseUrlBrowserInteractor(); mockGetTokenOIDCInteractor = MockGetTokenOIDCInteractor(); mockAuthenticateOidcOnBrowserInteractor = MockAuthenticateOidcOnBrowserInteractor(); - mockGetAuthenticationInfoInteractor = MockGetAuthenticationInfoInteractor(); - mockGetStoredOidcConfigurationInteractor = MockGetStoredOidcConfigurationInteractor(); mockSaveLoginUrlOnMobileInteractor = MockSaveLoginUrlOnMobileInteractor(); mockGetAllRecentLoginUrlOnMobileInteractor = MockGetAllRecentLoginUrlOnMobileInteractor(); mockSaveLoginUsernameOnMobileInteractor = MockSaveLoginUsernameOnMobileInteractor(); @@ -118,17 +100,15 @@ void main() { // mock reloadable controller mockGetSessionInteractor = MockGetSessionInteractor(); - mockGetAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor(); - mockUpdateAuthenticationAccountInteractor = MockUpdateAuthenticationAccountInteractor(); + mockGetCurrentAccountCacheInteractor = MockGetCurrentAccountCacheInteractor(); + mockUpdateCurrentAccountCacheInteractor = MockUpdateCurrentAccountCacheInteractor(); + mockLogoutCurrentAccountInteractor = MockLogoutCurrentAccountInteractor(); //mock base controller mockCachingManager = MockCachingManager(); mockLanguageCacheManager = MockLanguageCacheManager(); mockAuthorizationInterceptors = MockAuthorizationInterceptors(); mockDynamicUrlInterceptors = MockDynamicUrlInterceptors(); - mockDeleteCredentialInteractor = MockDeleteCredentialInteractor(); - mockLogoutOidcInteractor = MockLogoutOidcInteractor(); - mockDeleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor(); mockAppToast = MockAppToast(); mockImagePaths = MockImagePaths(); mockResponsiveUtils = MockResponsiveUtils(); @@ -136,8 +116,9 @@ void main() { mockApplicationManager = MockApplicationManager(); Get.put(mockGetSessionInteractor); - Get.put(mockGetAuthenticatedAccountInteractor); - Get.put(mockUpdateAuthenticationAccountInteractor); + Get.put(mockGetCurrentAccountCacheInteractor); + Get.put(mockUpdateCurrentAccountCacheInteractor); + Get.put(mockLogoutCurrentAccountInteractor); Get.put(mockCachingManager); Get.put(mockLanguageCacheManager); Get.put(mockAuthorizationInterceptors); @@ -146,9 +127,7 @@ void main() { tag: BindingTag.isolateTag, ); Get.put(mockDynamicUrlInterceptors); - Get.put(mockDeleteCredentialInteractor); - Get.put(mockLogoutOidcInteractor); - Get.put(mockDeleteAuthorityOidcInteractor); + Get.put(mockAppToast); Get.put(mockImagePaths); Get.put(mockResponsiveUtils); @@ -159,20 +138,15 @@ void main() { loginController = LoginController( mockAuthenticationInteractor, mockCheckOIDCIsAvailableInteractor, - mockGetOIDCIsAvailableInteractor, - mockGetOIDCConfigurationInteractor, mockGetTokenOIDCInteractor, mockAuthenticateOidcOnBrowserInteractor, - mockGetAuthenticationInfoInteractor, - mockGetStoredOidcConfigurationInteractor, + mockGetAuthResponseUrlBrowserInteractor, mockSaveLoginUrlOnMobileInteractor, mockGetAllRecentLoginUrlOnMobileInteractor, mockSaveLoginUsernameOnMobileInteractor, mockGetAllRecentLoginUsernameOnMobileInteractor, mockDNSLookupToGetJmapUrlInteractor, ); - - }); test('WHEN handleFailureViewState is called with GetTokenOIDCFailure \n' diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index dd1cc34f96..fc3e5fa35f 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -31,10 +31,9 @@ import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_i import 'package:tmail_ui_user/features/home/domain/usecases/get_session_interactor.dart'; import 'package:tmail_ui_user/features/home/domain/usecases/store_session_interactor.dart'; import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; -import 'package:tmail_ui_user/features/login/domain/usecases/update_authentication_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/get_current_account_cache_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/logout_current_account_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/update_current_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_default_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart'; @@ -67,7 +66,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/sear import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; -import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/delete_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/get_all_sending_email_interactor.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/usecases/store_sending_email_interactor.dart'; @@ -137,8 +135,9 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -157,9 +156,6 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), - MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -222,9 +218,6 @@ void main() { final languageCacheManager = MockLanguageCacheManager(); final authorizationInterceptors = MockAuthorizationInterceptors(); final dynamicUrlInterceptors = MockDynamicUrlInterceptors(); - final deleteCredentialInteractor = MockDeleteCredentialInteractor(); - final logoutOidcInteractor = MockLogoutOidcInteractor(); - final deleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor(); final appToast = MockAppToast(); final imagePaths = MockImagePaths(); final responsiveUtils = MockResponsiveUtils(); @@ -233,8 +226,9 @@ void main() { // mock reloadable controller Get dependencies final getSessionInteractor = MockGetSessionInteractor(); - final getAuthenticatedAccountInteractor = MockGetAuthenticatedAccountInteractor(); - final updateAuthenticationAccountInteractor = MockUpdateAuthenticationAccountInteractor(); + final getCurrentAccountCacheInteractor = MockGetCurrentAccountCacheInteractor(); + final updateCurrentAccountCacheInteractor = MockUpdateCurrentAccountCacheInteractor(); + final logoutCurrentAccountInteractor = MockLogoutCurrentAccountInteractor(); // mock mailbox controller direct dependencies final createNewMailboxInteractor = MockCreateNewMailboxInteractor(); @@ -289,17 +283,15 @@ void main() { tag: BindingTag.isolateTag, ); Get.put(dynamicUrlInterceptors); - Get.put(deleteCredentialInteractor); - Get.put(logoutOidcInteractor); - Get.put(deleteAuthorityOidcInteractor); Get.put(appToast); Get.put(imagePaths); Get.put(responsiveUtils); Get.put(uuid); Get.put(applicationManager); Get.put(getSessionInteractor); - Get.put(getAuthenticatedAccountInteractor); - Get.put(updateAuthenticationAccountInteractor); + Get.put(getCurrentAccountCacheInteractor); + Get.put(updateCurrentAccountCacheInteractor); + Get.put(logoutCurrentAccountInteractor); Get.put(getAllIdentitiesInteractor); Get.put(removeComposerCacheOnWebInteractor); diff --git a/test/fixtures/account_fixtures.dart b/test/fixtures/account_fixtures.dart index 583ce49358..dc57f42e75 100644 --- a/test/fixtures/account_fixtures.dart +++ b/test/fixtures/account_fixtures.dart @@ -7,10 +7,11 @@ import 'package:model/account/personal_account.dart'; class AccountFixtures { static final aliceAccountId = AccountId(Id('29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6')); static final aliceAccount = PersonalAccount( - 'dab', - AuthenticationType.oidc, + id: 'dab', + authenticationType: AuthenticationType.oidc, isSelected: true, accountId: aliceAccountId, + baseUrl: 'https://domain.com/jmap', apiUrl: 'https://domain.com/jmap', userName: UserName('Alice') ); diff --git a/test/fixtures/oidc_fixtures.dart b/test/fixtures/oidc_fixtures.dart index b31b0a409c..b7351fe740 100644 --- a/test/fixtures/oidc_fixtures.dart +++ b/test/fixtures/oidc_fixtures.dart @@ -5,27 +5,31 @@ import 'package:model/oidc/token_oidc.dart'; class OIDCFixtures { static final tokenOidcExpiredTime = TokenOIDC( - 'dab123', - TokenId('dab123'), - 'dab456', + token: 'dab123', + tokenId: TokenId('dab123'), + refreshToken: 'dab456', + authority: '', expiredTime: DateTime.now().subtract(const Duration(days: 1))); static final tokenOidcExpiredTimeAndRefreshTokenEmpty = TokenOIDC( - 'dab123', - TokenId('dab123'), - '', + token: 'dab123', + tokenId: TokenId('dab123'), + refreshToken: '', + authority: '', expiredTime: DateTime.now().subtract(const Duration(days: 1))); static final tokenOidcExpiredTimeAndTokenEmpty = TokenOIDC( - '', - TokenId('dab123'), - 'dab456', + token: '', + tokenId: TokenId('dab123'), + refreshToken: 'dab456', + authority: '', expiredTime: DateTime.now().subtract(const Duration(days: 1))); static final newTokenOidc = TokenOIDC( - 'test123', - TokenId('test123'), - 'test456', + token: 'test123', + tokenId: TokenId('test123'), + refreshToken: 'test456', + authority: '', expiredTime: DateTime.now().add(const Duration(days: 1))); static final oidcConfiguration = OIDCConfiguration(