diff --git a/lib/features/base/reloadable/reloadable_controller.dart b/lib/features/base/reloadable/reloadable_controller.dart index 450c9f638..fbc60b49f 100644 --- a/lib/features/base/reloadable/reloadable_controller.dart +++ b/lib/features/base/reloadable/reloadable_controller.dart @@ -86,10 +86,10 @@ abstract class ReloadableController extends BaseController { * trigger reload by getting Credential again then setting up Interceptor and retrieving session * */ void reload() { - _getAuthenticatedAccountAction(); + getAuthenticatedAccountAction(); } - void _getAuthenticatedAccountAction() { + void getAuthenticatedAccountAction() { consumeState(_getAuthenticatedAccountInteractor.execute()); } diff --git a/lib/features/home/presentation/home_controller.dart b/lib/features/home/presentation/home_controller.dart index 2843c5207..15fd02a9f 100644 --- a/lib/features/home/presentation/home_controller.dart +++ b/lib/features/home/presentation/home_controller.dart @@ -144,7 +144,11 @@ class HomeController extends BaseController { void _handleFailureViewState(Failure failure) async { logError('HomeController::_handleFailureViewState(): ${failure.toString()}'); - _clearAllCacheAndCredential(); + if (failure is CheckOIDCIsAvailableFailure) { + _goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)); + } else { + _clearAllCacheAndCredential(); + } } void _handleSuccessViewState(Success success) { @@ -153,6 +157,8 @@ class HomeController extends BaseController { _goToSessionWithTokenOidc(success); } else if (success is GetCredentialViewState) { _goToSessionWithBasicAuth(success); + } else if (success is CheckOIDCIsAvailableSuccess) { + _goToLogin(arguments: LoginArguments(LoginFormType.ssoForm)); } } @@ -176,15 +182,10 @@ class HomeController extends BaseController { void _checkOIDCIsAvailable() async { final baseUri = _parseUri(AppConfig.baseUrl); - log('LoginController::_checkOIDCIsAvailable(): baseUri: $baseUri'); if (baseUri != null) { - await _checkOIDCIsAvailableInteractor - .execute(OIDCRequest(baseUrl: baseUri.toString(), resourceUrl: baseUri.origin)) - .then((response) => response.fold( - (failure) => _goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)), - (success) => success is CheckOIDCIsAvailableSuccess - ? _goToLogin(arguments: LoginArguments(LoginFormType.ssoForm)) - : _goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)))); + consumeState(_checkOIDCIsAvailableInteractor.execute(OIDCRequest( + baseUrl: baseUri.toString(), + resourceUrl: baseUri.origin))); } else { _goToLogin(arguments: LoginArguments(LoginFormType.credentialForm)); } 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 e1c6228ec..ec6cbeae4 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 @@ -2,6 +2,14 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +class AuthenticateOidcOnBrowserLoading extends LoadingState { + + AuthenticateOidcOnBrowserLoading(); + + @override + List get props => []; +} + class AuthenticateOidcOnBrowserSuccess extends UIState { AuthenticateOidcOnBrowserSuccess(); diff --git a/lib/features/login/domain/state/authentication_user_state.dart b/lib/features/login/domain/state/authentication_user_state.dart index c4bc5052f..bd0564876 100644 --- a/lib/features/login/domain/state/authentication_user_state.dart +++ b/lib/features/login/domain/state/authentication_user_state.dart @@ -1,17 +1,24 @@ import 'package:core/core.dart'; import 'package:model/model.dart'; -class AuthenticationUserViewState extends UIState { +class AuthenticationUserLoading extends LoadingState { + AuthenticationUserLoading(); + + @override + List get props => []; +} + +class AuthenticationUserSuccess extends UIState { final UserProfile userProfile; - AuthenticationUserViewState(this.userProfile); + AuthenticationUserSuccess(this.userProfile); @override List get props => [userProfile]; } class AuthenticationUserFailure extends FeatureFailure { - final exception; + final dynamic exception; AuthenticationUserFailure(this.exception); 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 8d125634e..0cc2439ac 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 @@ -1,6 +1,15 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:model/oidc/response/oidc_response.dart'; +class CheckOIDCIsAvailableLoading extends LoadingState { + + CheckOIDCIsAvailableLoading(); + + @override + List get props => []; +} + class CheckOIDCIsAvailableSuccess extends UIState { final OIDCResponse oidcResponse; diff --git a/lib/features/login/domain/state/get_authentication_info_state.dart b/lib/features/login/domain/state/get_authentication_info_state.dart index 55769fcb1..cbebcc417 100644 --- a/lib/features/login/domain/state/get_authentication_info_state.dart +++ b/lib/features/login/domain/state/get_authentication_info_state.dart @@ -2,6 +2,14 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +class GetAuthenticationInfoLoading extends LoadingState { + + GetAuthenticationInfoLoading(); + + @override + List get props => []; +} + class GetAuthenticationInfoSuccess extends UIState { GetAuthenticationInfoSuccess(); 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 135a80243..8b00294a0 100644 --- a/lib/features/login/domain/state/get_oidc_configuration_state.dart +++ b/lib/features/login/domain/state/get_oidc_configuration_state.dart @@ -1,6 +1,14 @@ import 'package:core/core.dart'; import 'package:model/model.dart'; +class GetOIDCConfigurationLoading extends LoadingState { + + GetOIDCConfigurationLoading(); + + @override + List get props => []; +} + class GetOIDCConfigurationSuccess extends UIState { final OIDCConfiguration oidcConfiguration; 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 new file mode 100644 index 000000000..7b455456e --- /dev/null +++ b/lib/features/login/domain/state/get_oidc_is_available_state.dart @@ -0,0 +1,29 @@ +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 { + + GetOIDCIsAvailableLoading(); + + @override + List get props => []; +} + +class GetOIDCIsAvailableSuccess extends UIState { + final OIDCResponse oidcResponse; + + GetOIDCIsAvailableSuccess(this.oidcResponse); + + @override + List get props => [oidcResponse]; +} + +class GetOIDCIsAvailableFailure extends FeatureFailure { + final dynamic exception; + + GetOIDCIsAvailableFailure(this.exception); + + @override + List get props => [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 index e0a743746..f35ae2402 100644 --- a/lib/features/login/domain/state/get_stored_oidc_configuration_state.dart +++ b/lib/features/login/domain/state/get_stored_oidc_configuration_state.dart @@ -2,6 +2,14 @@ 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 { + + GetStoredOidcConfigurationLoading(); + + @override + List get props => []; +} + class GetStoredOidcConfigurationSuccess extends UIState { final OIDCConfiguration oidcConfiguration; 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 6b0e8b5a1..6a26c31e0 100644 --- a/lib/features/login/domain/state/get_token_oidc_state.dart +++ b/lib/features/login/domain/state/get_token_oidc_state.dart @@ -1,14 +1,23 @@ import 'package:core/core.dart'; import 'package:model/model.dart'; +class GetTokenOIDCLoading extends LoadingState { + + GetTokenOIDCLoading(); + + @override + List get props => []; +} + class GetTokenOIDCSuccess extends UIState { final TokenOIDC tokenOIDC; + final OIDCConfiguration configuration; - GetTokenOIDCSuccess(this.tokenOIDC); + GetTokenOIDCSuccess(this.tokenOIDC, this.configuration); @override - List get props => [tokenOIDC]; + List get props => [tokenOIDC, configuration]; } class GetTokenOIDCFailure extends FeatureFailure { 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 764c3e886..cf858e14c 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 @@ -14,17 +14,18 @@ class AuthenticateOidcOnBrowserInteractor { AuthenticateOidcOnBrowserInteractor(this.authenticationOIDCRepository); - Future> execute(Uri baseUrl, OIDCConfiguration config) async { + Stream> execute(Uri baseUrl, OIDCConfiguration config) async* { try { + yield Right(AuthenticateOidcOnBrowserLoading()); await authenticationOIDCRepository.authenticateOidcOnBrowser( config.clientId, config.redirectUrl, config.discoveryUrl, config.scopes); - return Right(AuthenticateOidcOnBrowserSuccess()); + yield Right(AuthenticateOidcOnBrowserSuccess()); } catch (e) { logError('AuthenticateOidcOnBrowserInteractor::execute(): $e'); - return Left(AuthenticateOidcOnBrowserFailure(e)); + yield Left(AuthenticateOidcOnBrowserFailure(e)); } } } \ No newline at end of file diff --git a/lib/features/login/domain/usecases/authentication_user_interactor.dart b/lib/features/login/domain/usecases/authentication_user_interactor.dart index cc861afe1..6e1b14573 100644 --- a/lib/features/login/domain/usecases/authentication_user_interactor.dart +++ b/lib/features/login/domain/usecases/authentication_user_interactor.dart @@ -12,11 +12,15 @@ class AuthenticationInteractor { final CredentialRepository credentialRepository; final AccountRepository _accountRepository; - AuthenticationInteractor(this.authenticationRepository, this.credentialRepository, this._accountRepository); + AuthenticationInteractor( + this.authenticationRepository, + this.credentialRepository, + this._accountRepository + ); - Future> execute(Uri baseUrl, UserName userName, Password password) async { + Stream> execute(Uri baseUrl, UserName userName, Password password) async* { try { - log('AuthenticationInteractor::execute(): $_accountRepository'); + yield Right(AuthenticationUserLoading()); final user = await authenticationRepository.authenticationUser(baseUrl, userName, password); await Future.wait([ credentialRepository.saveBaseUrl(baseUrl), @@ -28,10 +32,10 @@ class AuthenticationInteractor { isSelected: true )) ]); - return Right(AuthenticationUserViewState(user)); + yield Right(AuthenticationUserSuccess(user)); } catch (e) { logError('AuthenticationInteractor::execute(): $e'); - return Left(AuthenticationUserFailure(e)); + yield Left(AuthenticationUserFailure(e)); } } } \ No newline at end of file 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 29797f37e..00a8a48c7 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 @@ -1,6 +1,7 @@ -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:model/model.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/check_oidc_is_available_state.dart'; @@ -9,14 +10,13 @@ class CheckOIDCIsAvailableInteractor { CheckOIDCIsAvailableInteractor(this._oidcRepository); - Future> execute(OIDCRequest oidcRequest) async { + Stream> execute(OIDCRequest oidcRequest) async* { try { + yield Right(CheckOIDCIsAvailableLoading()); final result = await _oidcRepository.checkOIDCIsAvailable(oidcRequest); - log('CheckOIDCIsAvailableInteractor::execute(): result: $result'); - return Right(CheckOIDCIsAvailableSuccess(result)); + yield Right(CheckOIDCIsAvailableSuccess(result)); } catch (e) { - log('CheckOIDCIsAvailableInteractor::execute(): ERROR: $e'); - return Left(CheckOIDCIsAvailableFailure(e)); + yield Left(CheckOIDCIsAvailableFailure(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 index 5e165b1e8..e33c8d707 100644 --- a/lib/features/login/domain/usecases/get_authentication_info_interactor.dart +++ b/lib/features/login/domain/usecases/get_authentication_info_interactor.dart @@ -10,18 +10,19 @@ class GetAuthenticationInfoInteractor { GetAuthenticationInfoInteractor(this._oidcRepository); - Future> execute() async { + Stream> execute() async* { try { + yield Right(GetAuthenticationInfoLoading()); final result = await _oidcRepository.getAuthenticationInfo(); log('GetAuthenticationInfoInteractor::execute(): result: $result'); if (result?.isNotEmpty == true) { - return Right(GetAuthenticationInfoSuccess()); + yield Right(GetAuthenticationInfoSuccess()); } else { - return Left(GetAuthenticationInfoFailure(null)); + yield Left(GetAuthenticationInfoFailure(null)); } } catch (e) { log('GetAuthenticationInfoInteractor::execute(): ERROR: $e'); - return Left(GetAuthenticationInfoFailure(e)); + yield Left(GetAuthenticationInfoFailure(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 index a1995110f..a2d9458a9 100644 --- a/lib/features/login/domain/usecases/get_oidc_configuration_interactor.dart +++ b/lib/features/login/domain/usecases/get_oidc_configuration_interactor.dart @@ -1,6 +1,8 @@ -import 'package:core/core.dart'; +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/model.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'; @@ -9,15 +11,15 @@ class GetOIDCConfigurationInteractor { GetOIDCConfigurationInteractor(this._oidcRepository); - Future> execute(OIDCResponse oidcResponse) async { + Stream> execute(OIDCResponse oidcResponse) async* { try { + yield Right(GetOIDCConfigurationLoading()); final oidcConfiguration = await _oidcRepository.getOIDCConfiguration(oidcResponse); - log('GetOIDCConfigurationInteractor::execute(): oidcConfiguration: $oidcConfiguration'); await _oidcRepository.persistAuthorityOidc(oidcConfiguration.authority); - return Right(GetOIDCConfigurationSuccess(oidcConfiguration)); + yield Right(GetOIDCConfigurationSuccess(oidcConfiguration)); } catch (e) { log('GetOIDCConfigurationInteractor::execute(): ERROR: $e'); - return Left(GetOIDCConfigurationFailure(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 new file mode 100644 index 000000000..7a4d96fa9 --- /dev/null +++ b/lib/features/login/domain/usecases/get_oidc_is_available_interactor.dart @@ -0,0 +1,22 @@ +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 index 7500bf44b..ca928db8b 100644 --- a/lib/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart +++ b/lib/features/login/domain/usecases/get_stored_oidc_configuration_interactor.dart @@ -10,14 +10,14 @@ class GetStoredOidcConfigurationInteractor { GetStoredOidcConfigurationInteractor(this._authenticationOIDCRepository); - Future> execute() async { + Stream> execute() async* { try { + yield Right(GetStoredOidcConfigurationLoading()); final config = await _authenticationOIDCRepository.getStoredOidcConfiguration(); - log('GetStoredOidcConfigurationInteractor::execute(): oidcConfiguration: $config'); - return Right(GetStoredOidcConfigurationSuccess(config)); + yield Right(GetStoredOidcConfigurationSuccess(config)); } catch (e) { log('GetStoredOidcConfigurationInteractor::execute(): $e'); - return Left(GetStoredOidcConfigurationFailure(e)); + yield Left(GetStoredOidcConfigurationFailure(e)); } } } \ 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 9428ac346..c44b4d885 100644 --- a/lib/features/login/domain/usecases/get_token_oidc_interactor.dart +++ b/lib/features/login/domain/usecases/get_token_oidc_interactor.dart @@ -21,11 +21,14 @@ class GetTokenOIDCInteractor { GetTokenOIDCInteractor(this._credentialRepository, this.authenticationOIDCRepository, this._accountRepository); - Future> execute(Uri baseUrl, OIDCConfiguration config) async { + Stream> execute(Uri baseUrl, OIDCConfiguration config) async* { try { - log('GetTokenOIDCInteractor::execute(): baseUrl: $baseUrl'); - final tokenOIDC = await authenticationOIDCRepository - .getTokenOIDC(config.clientId, config.redirectUrl, config.discoveryUrl, config.scopes); + yield Right(GetTokenOIDCLoading()); + final tokenOIDC = await authenticationOIDCRepository.getTokenOIDC( + config.clientId, + config.redirectUrl, + config.discoveryUrl, + config.scopes); await Future.wait([ _credentialRepository.saveBaseUrl(baseUrl), _accountRepository.setCurrentAccount(Account( @@ -35,10 +38,10 @@ class GetTokenOIDCInteractor { authenticationOIDCRepository.persistTokenOIDC(tokenOIDC), authenticationOIDCRepository.persistAuthorityOidc(config.authority), ]); - return Right(GetTokenOIDCSuccess(tokenOIDC)); + yield Right(GetTokenOIDCSuccess(tokenOIDC, config)); } catch (e) { logError('GetTokenOIDCInteractor::execute(): $e'); - return Left(GetTokenOIDCFailure(e)); + yield Left(GetTokenOIDCFailure(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 5ee4dc84f..953ce908b 100644 --- a/lib/features/login/presentation/login_bindings.dart +++ b/lib/features/login/presentation/login_bindings.dart @@ -33,15 +33,21 @@ import 'package:tmail_ui_user/features/login/domain/repository/login_username_re 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/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_credential_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_stored_token_oidc_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'; import 'package:tmail_ui_user/features/login/presentation/login_controller.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'; class LoginBindings extends BaseBindings { @@ -49,11 +55,15 @@ class LoginBindings extends BaseBindings { @override void bindingsController() { Get.lazyPut(() => LoginController( + Get.find(), + Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), Get.find(tag: BindingTag.isolateTag), Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), @@ -98,6 +108,23 @@ class LoginBindings extends BaseBindings { @override void bindingsInteractor() { + Get.lazyPut(() => LogoutOidcInteractor( + Get.find(), + Get.find(), + )); + Get.lazyPut(() => DeleteAuthorityOidcInteractor( + Get.find(), + Get.find()) + ); + Get.lazyPut(() => GetStoredTokenOidcInteractor( + Get.find(), + Get.find(), + )); + Get.lazyPut(() => GetAuthenticatedAccountInteractor( + Get.find(), + Get.find(), + Get.find(), + )); Get.lazyPut(() => AuthenticationInteractor( Get.find(), Get.find(), @@ -106,6 +133,9 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => CheckOIDCIsAvailableInteractor( Get.find(), )); + Get.lazyPut(() => GetOIDCIsAvailableInteractor( + Get.find(), + )); Get.lazyPut(() => GetOIDCConfigurationInteractor( Get.find(), )); @@ -142,7 +172,7 @@ class LoginBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); - + } @override diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index 8f9e40cc3..155f8b37a 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -1,10 +1,21 @@ -import 'package:core/core.dart'; +import 'package:core/data/network/config/dynamic_url_interceptors.dart'; +import 'package:core/presentation/extensions/url_extension.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/build_utils.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; -import 'package:model/model.dart'; -import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:model/account/password.dart'; +import 'package:model/account/user_name.dart'; +import 'package:model/oidc/oidc_configuration.dart'; +import 'package:model/oidc/request/oidc_request.dart'; +import 'package:model/oidc/response/oidc_response.dart'; +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/login/data/network/config/authorization_interceptors.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'; @@ -15,15 +26,19 @@ import 'package:tmail_ui_user/features/login/domain/state/get_all_recent_login_u 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_authentication_info_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'; 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/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_token_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/save_login_url_on_mobile_interactor.dart'; @@ -31,18 +46,20 @@ import 'package:tmail_ui_user/features/login/domain/usecases/save_login_username 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/login/presentation/state/login_state.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/network_status_handle/presentation/network_connnection_controller.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; -class LoginController extends BaseController { +class LoginController extends ReloadableController { final AuthenticationInteractor _authenticationInteractor; final DynamicUrlInterceptors _dynamicUrlInterceptors; final AuthorizationInterceptors _authorizationInterceptors; final AuthorizationInterceptors _authorizationIsolateInterceptors; final CheckOIDCIsAvailableInteractor _checkOIDCIsAvailableInteractor; + final GetOIDCIsAvailableInteractor _getOIDCIsAvailableInteractor; final GetOIDCConfigurationInteractor _getOIDCConfigurationInteractor; final GetTokenOIDCInteractor _getTokenOIDCInteractor; final AuthenticateOidcOnBrowserInteractor _authenticateOidcOnBrowserInteractor; @@ -53,16 +70,21 @@ class LoginController extends BaseController { final SaveLoginUsernameOnMobileInteractor _saveLoginUsernameOnMobileInteractor; final GetAllRecentLoginUsernameOnMobileInteractor _getAllRecentLoginUsernameOnMobileInteractor; + final TextEditingController urlInputController = TextEditingController(); final TextEditingController usernameInputController = TextEditingController(); final NetworkConnectionController networkConnectionController = Get.find(); LoginController( + LogoutOidcInteractor logoutOidcInteractor, + DeleteAuthorityOidcInteractor deleteAuthorityOidcInteractor, + GetAuthenticatedAccountInteractor getAuthenticatedAccountInteractor, this._authenticationInteractor, this._dynamicUrlInterceptors, this._authorizationInterceptors, this._authorizationIsolateInterceptors, this._checkOIDCIsAvailableInteractor, + this._getOIDCIsAvailableInteractor, this._getOIDCConfigurationInteractor, this._getTokenOIDCInteractor, this._authenticateOidcOnBrowserInteractor, @@ -73,6 +95,9 @@ class LoginController extends BaseController { this._saveLoginUsernameOnMobileInteractor, this._getAllRecentLoginUsernameOnMobileInteractor, ); + ) : super(logoutOidcInteractor, + deleteAuthorityOidcInteractor, + getAuthenticatedAccountInteractor); var loginState = LoginState(Right(LoginInitAction())).obs; final loginFormType = LoginFormType.baseUrlForm.obs; @@ -102,39 +127,80 @@ class LoginController extends BaseController { @override void onReady() { + super.onReady(); if (BuildUtils.isWeb) { final arguments = Get.arguments; if (arguments is LoginArguments) { loginFormType.value = arguments.loginFormType; + _checkOIDCIsAvailable(); } else { _getAuthenticationInfo(); } } - super.onReady(); } - void _getAuthenticationInfo() async { - await _getAuthenticationInfoInteractor.execute() - .then((result) => result.fold( - (failure) => _checkOIDCIsAvailable(), - (success) { - if (success is GetAuthenticationInfoSuccess) { - _getStoredOidcConfiguration(); - } else { - _checkOIDCIsAvailable(); - } - })); + @override + void onData(Either newState) { + super.onData(newState); + viewState.value.fold( + (failure) { + if (failure is GetAuthenticationInfoFailure) { + getAuthenticatedAccountAction(); + } else if (failure is CheckOIDCIsAvailableFailure || + failure is GetStoredOidcConfigurationFailure) { + _showFormLoginWithCredentialAction(); + } else if (failure is GetOIDCIsAvailableFailure) { + loginState.value = LoginState(Left(LoginSSONotAvailableAction())); + _showFormLoginWithCredentialAction(); + } else if (failure is AuthenticationUserFailure) { + _loginFailureAction(failure); + } else if (failure is GetOIDCConfigurationFailure || + failure is GetTokenOIDCFailure || + failure is AuthenticateOidcOnBrowserFailure) { + loginState.value = LoginState(Left(failure)); + } + }, + (success) { + if (success is GetAuthenticationInfoSuccess) { + _getStoredOidcConfiguration(); + } else if (success is GetStoredOidcConfigurationSuccess) { + _getTokenOIDCAction(success.oidcConfiguration); + } else if (success is CheckOIDCIsAvailableSuccess) { + _showFormLoginWithSSOAction(success); + } else if (success is GetOIDCIsAvailableSuccess) { + loginState.value = LoginState(Right(success)); + _oidcResponse = success.oidcResponse; + _getOIDCConfiguration(); + } else if (success is GetOIDCConfigurationSuccess) { + _getOIDCConfigurationSuccess(success); + } else if (success is GetTokenOIDCSuccess) { + _getTokenOIDCSuccess(success); + } else if (success is AuthenticationUserSuccess) { + _loginSuccessAction(success); + } else if (success is GetAuthenticationInfoLoading || + success is CheckOIDCIsAvailableLoading || + success is GetStoredOidcConfigurationLoading || + success is GetOIDCConfigurationLoading || + success is GetTokenOIDCLoading || + success is AuthenticationUserLoading || + success is GetOIDCIsAvailableLoading) { + loginState.value = LoginState(Right(LoginLoadingAction())); + } + } + ); } - void _getStoredOidcConfiguration() async { - await _getStoredOidcConfigurationInteractor.execute() - .then((result) => result.fold( - (failure) => {}, - (success) { - if (success is GetStoredOidcConfigurationSuccess) { - _getTokenOIDCAction(success.oidcConfiguration); - } - })); + @override + void handleReloaded(Session session) { + pushAndPop(AppRoutes.dashboard, arguments: session); + } + + void _getAuthenticationInfo() { + consumeState(_getAuthenticationInfoInteractor.execute()); + } + + void _getStoredOidcConfiguration() { + consumeState(_getStoredOidcConfigurationInteractor.execute()); } void handleNextInUrlInputFormPress() { @@ -142,21 +208,14 @@ class LoginController extends BaseController { _checkOIDCIsAvailable(); } - void _checkOIDCIsAvailable() async { + void _checkOIDCIsAvailable() { final baseUri = BuildUtils.isWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText); - log('LoginController::_checkOIDCIsAvailable(): baseUri: $baseUri'); if (baseUri == null) { loginState.value = LoginState(Left(LoginMissUrlAction())); } else { - loginState.value = LoginState(Right(LoginLoadingAction())); - log('LoginController::_checkOIDCIsAvailable(): origin: + ${baseUri.origin}'); - await _checkOIDCIsAvailableInteractor - .execute(OIDCRequest(baseUrl: baseUri.toString(), resourceUrl: baseUri.origin)) - .then((response) => response.fold( - (failure) => _showFormLoginWithCredentialAction(), - (success) => success is CheckOIDCIsAvailableSuccess - ? _showFormLoginWithSSOAction(success) - : _showFormLoginWithCredentialAction())); + consumeState(_checkOIDCIsAvailableInteractor.execute(OIDCRequest( + baseUrl: baseUri.toString(), + resourceUrl: baseUri.origin))); } } @@ -172,7 +231,6 @@ class LoginController extends BaseController { } void _showFormLoginWithCredentialAction() { - log('LoginController::_showFormLoginWithCredentialAction()'); loginState.value = LoginState(Right(InputUrlCompletion())); loginFormType.value = LoginFormType.credentialForm; } @@ -198,49 +256,22 @@ class LoginController extends BaseController { } } - void handleSSOPressed() async { + void handleSSOPressed() { final baseUri = _parseUri(AppConfig.baseUrl); - if (baseUri != null) { - log('LoginController::handleSSOPressed(): baseUri: ${baseUri.toString()}'); - loginState.value = LoginState(Right(LoginLoadingAction())); - await _checkOIDCIsAvailableInteractor - .execute(OIDCRequest(baseUrl: baseUri.toString(), resourceUrl: baseUri.origin)) - .then((response) => response.fold( - (failure) => loginState.value = LoginState(Left(LoginSSONotAvailableAction())), - (success) { - if (success is CheckOIDCIsAvailableSuccess) { - loginState.value = LoginState(Right(success)); - _oidcResponse = success.oidcResponse; - _getOIDCConfiguration(); - } else { - loginState.value = LoginState(Left(LoginSSONotAvailableAction())); - } - })); + consumeState(_getOIDCIsAvailableInteractor.execute(OIDCRequest( + baseUrl: baseUri.toString(), + resourceUrl: baseUri.origin))); + } else { + loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction())); } } - void _getOIDCConfiguration() async { - loginState.value = LoginState(Right(LoginLoadingAction())); + void _getOIDCConfiguration() { if (_oidcResponse != null) { - await _getOIDCConfigurationInteractor.execute(_oidcResponse!) - .then((response) => response.fold( - (failure) { - if (failure is GetOIDCConfigurationFailure) { - loginState.value = LoginState(Left(failure)); - } else { - loginState.value = LoginState( - Left(LoginCanNotVerifySSOConfigurationAction())); - } - }, - (success) { - if (success is GetOIDCConfigurationSuccess) { - _getOIDCConfigurationSuccess(success); - } else { - loginState.value = LoginState( - Left(LoginCanNotVerifySSOConfigurationAction())); - } - })); + consumeState(_getOIDCConfigurationInteractor.execute(_oidcResponse!)); + } else { + loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction())); } } @@ -254,77 +285,41 @@ class LoginController extends BaseController { } void _getTokenOIDCAction(OIDCConfiguration config) async { - loginState.value = LoginState(Right(LoginLoadingAction())); final baseUri = kIsWeb ? _parseUri(AppConfig.baseUrl) : _parseUri(_urlText); if (baseUri != null) { - await _getTokenOIDCInteractor - .execute(baseUri, config) - .then((response) => - response.fold( - (failure) { - if (failure is GetTokenOIDCFailure) { - loginState.value = LoginState(Left(failure)); - } else { - loginState.value = LoginState(Left(LoginCanNotGetTokenAction())); - } - }, - (success) { - if (success is GetTokenOIDCSuccess) { - _getTokenOIDCSuccess(success, config); - } else { - loginState.value = LoginState(Left(LoginCanNotGetTokenAction())); - } - })); + consumeState(_getTokenOIDCInteractor.execute(baseUri, config)); } else { loginState.value = LoginState(Left(LoginCanNotGetTokenAction())); } } void _authenticateOidcOnBrowserAction(OIDCConfiguration config) async { - loginState.value = LoginState(Right(LoginLoadingAction())); final baseUri = _parseUri(AppConfig.baseUrl); if (baseUri != null) { - await _authenticateOidcOnBrowserInteractor.execute(baseUri, config) - .then((response) => response.fold( - (failure) { - if (failure is AuthenticateOidcOnBrowserFailure) { - loginState.value = LoginState(Left(failure)); - } else { - loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction())); - } - }, - (success) { - if (success is! AuthenticateOidcOnBrowserSuccess) { - loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction())); - } - })); + consumeState(_authenticateOidcOnBrowserInteractor.execute(baseUri, config)); } else { loginState.value = LoginState(Left(LoginCanNotAuthenticationSSOAction())); } } - void _getTokenOIDCSuccess(GetTokenOIDCSuccess success, OIDCConfiguration config) { + void _getTokenOIDCSuccess(GetTokenOIDCSuccess success) { log('LoginController::_getTokenOIDCSuccess(): ${success.tokenOIDC.toString()}'); loginState.value = LoginState(Right(success)); _dynamicUrlInterceptors.changeBaseUrl(kIsWeb ? AppConfig.baseUrl : _urlText); _authorizationInterceptors.setTokenAndAuthorityOidc( newToken: success.tokenOIDC.toToken(), - newConfig: config); + newConfig: success.configuration); _authorizationIsolateInterceptors.setTokenAndAuthorityOidc( newToken: success.tokenOIDC.toToken(), - newConfig: config); + newConfig: success.configuration); pushAndPop(AppRoutes.session); } void _loginAction(Uri baseUrl, UserName userName, Password password) async { - loginState.value = LoginState(Right(LoginLoadingAction())); - await _authenticationInteractor.execute(baseUrl, userName, password) - .then((response) => response.fold( - (failure) => failure is AuthenticationUserFailure ? _loginFailureAction(failure) : null, - (success) => success is AuthenticationUserViewState ? _loginSuccessAction(success) : null)); + consumeState(_authenticationInteractor.execute(baseUrl, userName, password)); } - void _loginSuccessAction(AuthenticationUserViewState success) { + void _loginSuccessAction(AuthenticationUserSuccess success) { loginState.value = LoginState(Right(success)); _dynamicUrlInterceptors.changeBaseUrl(kIsWeb ? AppConfig.baseUrl : _urlText); _authorizationInterceptors.setBasicAuthorization(_userNameText, _passwordText); 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 b8adbbc57..bf924949b 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -121,7 +121,6 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { borderRadius: BorderRadius.circular(20), child: Column(children: [ Obx(() { - log('MailboxDashBoardView::build(): '); if (controller.isSelectionEnabled()) { return Padding( padding: const EdgeInsets.all(12.0),