diff --git a/data/lib/src/datasource_impl/authentication_datasource_impl.dart b/data/lib/src/datasource_impl/authentication_datasource_impl.dart index bf998ac2b..83e1cf031 100644 --- a/data/lib/src/datasource_impl/authentication_datasource_impl.dart +++ b/data/lib/src/datasource_impl/authentication_datasource_impl.dart @@ -103,6 +103,11 @@ class AuthenticationDataSourceImpl implements AuthenticationDataSource { return userRes.toUser(); }).catchError((error) { _remoteExceptionThrower.throwRemoteException(error, handler: (DioError error) { + final errorCode = error.response?.headers.value(Constant.linShareAuthErrorCode) ?? '1'; + final authErrorCode = LinShareErrorCode(int.tryParse(errorCode) ?? 1); + if(authErrorCode.canNotGetJwt()){ + throw NotAuthorizedUser(); + } throw UnknownError(error.response?.statusMessage!); }); }); diff --git a/data/lib/src/network/config/retry_authentication_interceptors.dart b/data/lib/src/network/config/retry_authentication_interceptors.dart index 8659a596d..70aa6a242 100644 --- a/data/lib/src/network/config/retry_authentication_interceptors.dart +++ b/data/lib/src/network/config/retry_authentication_interceptors.dart @@ -29,6 +29,7 @@ // 3 and for // the Additional Terms applicable to LinShare software. +import 'package:data/src/util/constant.dart'; import 'package:dio/dio.dart'; import 'package:domain/domain.dart'; @@ -50,9 +51,11 @@ class RetryAuthenticationInterceptors extends InterceptorsWrapper { final requestOptions = dioError.requestOptions; final extraInRequest = requestOptions.extra; var retries = extraInRequest[RETRY_KEY] ?? 0; - if (_isAuthenticationError(dioError, retries)) { + var errorCode = dioError.response?.headers.value(Constant.linShareAuthErrorCode) ?? '1'; + final authErrorCode = LinShareErrorCode(int.tryParse(errorCode) ?? 1); + try{ + if (_isAuthenticationError(dioError, retries) && !authErrorCode.isTokenDeleted()) { retries++; - requestOptions.headers.addAll({AUTHORIZATION_KEY: _getTokenAsBearerHeader(_permanentToken?.token)}); requestOptions.extra = {RETRY_KEY: retries}; @@ -60,8 +63,11 @@ class RetryAuthenticationInterceptors extends InterceptorsWrapper { return handler.resolve(response); } else { - super.onError(dioError, handler); + return handler.reject(dioError); } + } catch(exception) { + super.onError(dioError, handler); + } } bool _isAuthenticationError(DioError dioError, int retryCount) { diff --git a/domain/lib/domain.dart b/domain/lib/domain.dart index 5f77d8504..cc57b3343 100644 --- a/domain/lib/domain.dart +++ b/domain/lib/domain.dart @@ -293,6 +293,7 @@ export 'src/usecases/authentication/create_permanent_token_interactor.dart'; export 'src/usecases/authentication/create_permanent_token_oidc_interactor.dart'; export 'src/usecases/authentication/credential_view_state.dart'; export 'src/usecases/authentication/delete_permanent_token_interactor.dart'; +export 'src/usecases/authentication/remove_permanent_token_interactor.dart'; export 'src/usecases/authentication/delete_token_oidc_interactor.dart'; export 'src/usecases/authentication/get_authorized_user_interactor.dart'; export 'src/usecases/authentication/get_credential_interactor.dart'; diff --git a/domain/lib/src/errorcode/business_error_code.dart b/domain/lib/src/errorcode/business_error_code.dart index e99ae33cf..904293fb5 100644 --- a/domain/lib/src/errorcode/business_error_code.dart +++ b/domain/lib/src/errorcode/business_error_code.dart @@ -39,4 +39,7 @@ class BusinessErrorCode { static final workspaceLimit = LinShareErrorCode(50016); static final nestedWorkgroupLimit = LinShareErrorCode(55508); static final uploadRequestLimitReach = LinShareErrorCode(31416); + static final noValidPermanentTokenFound = [LinShareErrorCode(1005)]; + static final noValidJwt = [LinShareErrorCode(1000)]; + } diff --git a/domain/lib/src/model/linshare_error_code.dart b/domain/lib/src/model/linshare_error_code.dart index 6b86d6a8d..d34e9f745 100644 --- a/domain/lib/src/model/linshare_error_code.dart +++ b/domain/lib/src/model/linshare_error_code.dart @@ -43,4 +43,11 @@ extension LinShareErrorCodeExtension on LinShareErrorCode { bool isAuthenticateErrorUserLocked() => BusinessErrorCode.authenErrorUserLocked.contains(this); -} \ No newline at end of file + + bool isTokenDeleted()=> + BusinessErrorCode.noValidPermanentTokenFound.contains(this); + + bool canNotGetJwt()=> + BusinessErrorCode.noValidJwt.contains(this); +} + diff --git a/domain/lib/src/usecases/authentication/get_authorized_user_interactor.dart b/domain/lib/src/usecases/authentication/get_authorized_user_interactor.dart index b1a96f64b..f86735907 100644 --- a/domain/lib/src/usecases/authentication/get_authorized_user_interactor.dart +++ b/domain/lib/src/usecases/authentication/get_authorized_user_interactor.dart @@ -42,7 +42,13 @@ class GetAuthorizedInteractor { Future> execute() async { try { final user = await authenticationRepository.getAuthorizedUser() - .onError((error, stackTrace) => authenticationRepository.getAuthorizedUserOffline()); + .onError((error, stackTrace) { + if(error is NotAuthorizedUser){ + throw error; + } else { + return authenticationRepository.getAuthorizedUserOffline(); + } + }); final baseUrl = (await credentialRepository.getBaseUrl()).toString(); if (_needSetup2FA(user)) { return Left(NeedSetup2FA()); diff --git a/domain/lib/src/usecases/authentication/remove_permanent_token_interactor.dart b/domain/lib/src/usecases/authentication/remove_permanent_token_interactor.dart new file mode 100644 index 000000000..d05438934 --- /dev/null +++ b/domain/lib/src/usecases/authentication/remove_permanent_token_interactor.dart @@ -0,0 +1,60 @@ +// LinShare is an open source filesharing software, part of the LinPKI software +// suite, developed by Linagora. +// +// Copyright (C) 2020 LINAGORA +// +// This program is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later version, +// provided you comply with the Additional Terms applicable for LinShare software by +// Linagora pursuant to Section 7 of the GNU Affero General Public License, +// subsections (b), (c), and (e), pursuant to which you must notably (i) retain the +// display in the interface of the “LinShare™” trademark/logo, the "Libre & Free" mention, +// the words “You are using the Free and Open Source version of LinShare™, powered by +// Linagora © 2009–2020. Contribute to Linshare R&D by subscribing to an Enterprise +// offer!”. You must also retain the latter notice in all asynchronous messages such as +// e-mails sent with the Program, (ii) retain all hypertext links between LinShare and +// http://www.linshare.org, between linagora.com and Linagora, and (iii) refrain from +// infringing Linagora intellectual property rights over its trademarks and commercial +// brands. Other Additional Terms apply, see +// +// for more details. +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +// more details. +// You should have received a copy of the GNU Affero General Public License and its +// applicable Additional Terms for LinShare along with this program. If not, see +// for the GNU Affero General Public License version +// 3 and for +// the Additional Terms applicable to LinShare software. + +import 'dart:developer' as developer; + +import 'package:dartz/dartz.dart'; +import 'package:domain/domain.dart'; + +import 'logout_view_state.dart'; + +class RemovePermanentTokenInteractor { + final TokenRepository tokenRepository; + final CredentialRepository credentialRepository; + + RemovePermanentTokenInteractor( + this.tokenRepository, + this.credentialRepository + ); + + Future> execute() async { + try { + Future.sync(() async { + await tokenRepository.removeToken(); + developer.log('execute(): deleteToken', name: 'DeletePermanentTokenInteractor'); + await credentialRepository.removeBaseUrl(); + }); + return Right(LogoutViewState()); + } catch (exception) { + return Left(LogoutFailure(exception)); + } + } +} diff --git a/lib/presentation/di/module/app_module.dart b/lib/presentation/di/module/app_module.dart index 61e5bd55a..c7e41ede8 100644 --- a/lib/presentation/di/module/app_module.dart +++ b/lib/presentation/di/module/app_module.dart @@ -332,6 +332,9 @@ class AppModule { getIt(), getIt(), getIt())); + getIt.registerFactory(() => RemovePermanentTokenInteractor( + getIt(), + getIt())); getIt.registerFactory(() => LogoutOidcInteractor( getIt(), getIt() diff --git a/lib/presentation/di/module/widget_module.dart b/lib/presentation/di/module/widget_module.dart index 0383565d9..ad389f46a 100644 --- a/lib/presentation/di/module/widget_module.dart +++ b/lib/presentation/di/module/widget_module.dart @@ -493,6 +493,7 @@ class WidgetModule { getIt(), getIt(), getIt(), + getIt() )); } diff --git a/lib/presentation/widget/authentication/authentication_viewmodel.dart b/lib/presentation/widget/authentication/authentication_viewmodel.dart index 36b09c89b..873acb56f 100644 --- a/lib/presentation/widget/authentication/authentication_viewmodel.dart +++ b/lib/presentation/widget/authentication/authentication_viewmodel.dart @@ -45,6 +45,7 @@ import 'authentication_arguments.dart'; class AuthenticationViewModel extends BaseViewModel { final GetAuthorizedInteractor _getAuthorizedInteractor; final DeletePermanentTokenInteractor deletePermanentTokenInteractor; + final RemovePermanentTokenInteractor _removePermanentTokenInteractor; final SaveAuthorizedUserInteractor _saveAuthorizedUserInteractor; final AppNavigation _appNavigation; AuthenticationArguments? _authenticationArguments; @@ -55,6 +56,7 @@ class AuthenticationViewModel extends BaseViewModel { this.deletePermanentTokenInteractor, this._appNavigation, this._saveAuthorizedUserInteractor, + this._removePermanentTokenInteractor ) : super(store) { _getAuthorizedUser(); } @@ -91,7 +93,13 @@ class AuthenticationViewModel extends BaseViewModel { _appNavigation.popAndPush( RoutePaths.second_factor_authentication, arguments: SecondFactorAuthenticationArguments(_authenticationArguments!.baseUrl)); - } else { + }else if (failure is GetAuthorizedUserFailure && + failure.exception is NotAuthorizedUser) { + store.dispatch(removeTokenAction()); + _appNavigation.pushAndRemoveAll(RoutePaths.loginRoute, + ); + } + else { store.dispatch(initializeHomeView(_appNavigation, _authenticationArguments!.baseUrl)); } } @@ -101,7 +109,11 @@ class AuthenticationViewModel extends BaseViewModel { await deletePermanentTokenInteractor.execute(); }; } - + ThunkAction removeTokenAction() { + return (Store store) async { + await _removePermanentTokenInteractor.execute(); + }; + } ThunkAction _saveAuthorizedUserAction(User user) { return (Store store) async { await _saveAuthorizedUserInteractor.execute(user);