From 471465e1bc28e63f3496600b94ecffeaa84c407a Mon Sep 17 00:00:00 2001 From: Dat PHAM HOANG Date: Tue, 31 May 2022 14:29:30 +0700 Subject: [PATCH] TF-571 Implement AuthenticationOIDC repository with Hive --- .../caching/token_oidc_cache_client.dart | 131 ++++++++++++++++++ .../authentication_oidc_datasource_impl.dart | 14 +- .../token_oidc_cache_extension.dart | 9 ++ .../data/extensions/token_oidc_extension.dart | 8 ++ .../data/local/token_oidc_cache_manager.dart | 33 +++++ .../login/data/model/token_oidc_cache.dart | 26 ++++ .../authentication_oidc_repository_impl.dart | 10 ++ .../exceptions/authentication_exception.dart | 16 +++ 8 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 lib/features/caching/token_oidc_cache_client.dart create mode 100644 lib/features/login/data/extensions/token_oidc_cache_extension.dart create mode 100644 lib/features/login/data/extensions/token_oidc_extension.dart create mode 100644 lib/features/login/data/local/token_oidc_cache_manager.dart create mode 100644 lib/features/login/data/model/token_oidc_cache.dart diff --git a/lib/features/caching/token_oidc_cache_client.dart b/lib/features/caching/token_oidc_cache_client.dart new file mode 100644 index 000000000..f7ed44500 --- /dev/null +++ b/lib/features/caching/token_oidc_cache_client.dart @@ -0,0 +1,131 @@ +import 'package:hive/hive.dart'; +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 + Future clearAllData() { + return Future.sync(() async { + final boxToken = await openBox(); + boxToken.clear(); + }).catchError((error) { + throw error; + }); + } + + @override + Future deleteItem(String key) { + return Future.sync(() async { + final boxToken = await openBox(); + return boxToken.delete(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future deleteMultipleItem(List listKey) { + return Future.sync(() async { + final boxToken = await openBox(); + return boxToken.deleteAll(listKey); + }).catchError((error) { + throw error; + }); + } + + @override + Future> getAll() { + return Future.sync(() async { + final boxToken = await openBox(); + return boxToken.values.toList(); + }).catchError((error) { + throw error; + }); + } + + @override + Future getItem(String key) { + return Future.sync(() async { + final boxToken = await openBox(); + return boxToken.get(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future insertItem(String key, TokenOidcCache newObject) { + return Future.sync(() async { + final boxToken = await openBox(); + boxToken.put(key, newObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future insertMultipleItem(Map mapObject) { + return Future.sync(() async { + final boxToken = await openBox(); + boxToken.putAll(mapObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future isExistItem(String key) { + return Future.sync(() async { + final boxToken = await openBox(); + return boxToken.containsKey(key); + }).catchError((error) { + throw error; + }); + } + + @override + Future isExistTable() { + return Future.sync(() async { + return await Hive.boxExists(tableName); + }).catchError((error) { + throw error; + }); + } + + @override + Future> openBox() { + return Future.sync(() async { + if (Hive.isBoxOpen(tableName)) { + return Hive.box(tableName); + } + return await Hive.openBox(tableName); + }).catchError((error) { + throw error; + }); + } + + @override + Future updateItem(String key, TokenOidcCache newObject) { + return Future.sync(() async { + final boxToken = await openBox(); + boxToken.put(key, newObject); + }).catchError((error) { + throw error; + }); + } + + @override + Future updateMultipleItem(Map mapObject) { + return Future.sync(() async { + final boxToken = await openBox(); + boxToken.putAll(mapObject); + }).catchError((error) { + throw error; + }); + } + +} \ 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 0066af245..c1fd03c0b 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,12 +1,14 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/features/login/data/datasource/authentication_oidc_datasource.dart'; +import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_http_client.dart'; class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { final OIDCHttpClient _oidcHttpClient; + final TokenOidcCacheManager _tokenOidcCacheManager; - AuthenticationOIDCDataSourceImpl(this._oidcHttpClient); + AuthenticationOIDCDataSourceImpl(this._oidcHttpClient, this._tokenOidcCacheManager); @override Future checkOIDCIsAvailable(OIDCRequest oidcRequest) { @@ -35,4 +37,14 @@ class AuthenticationOIDCDataSourceImpl extends AuthenticationOIDCDataSource { throw error; }); } + + @override + Future getStoredTokenOIDC(String tokenIdHash) async { + return _tokenOidcCacheManager.getTokenOidc(tokenIdHash); + } + + @override + Future persistTokenOIDC(TokenOIDC tokenOidc) async { + return _tokenOidcCacheManager.persistOneTokenOidc(tokenOidc); + } } \ 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 new file mode 100644 index 000000000..f859f597d --- /dev/null +++ b/lib/features/login/data/extensions/token_oidc_cache_extension.dart @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..5fbcce7d4 --- /dev/null +++ b/lib/features/login/data/extensions/token_oidc_extension.dart @@ -0,0 +1,8 @@ +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/login/data/model/token_oidc_cache.dart'; + +extension TokenOidcExtension on TokenOIDC { + TokenOidcCache toTokenOidcCache() { + return TokenOidcCache(token, tokenId.uuid, refreshToken, expiredTime: expiredTime); + } +} \ 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 new file mode 100644 index 000000000..8b92405a7 --- /dev/null +++ b/lib/features/login/data/local/token_oidc_cache_manager.dart @@ -0,0 +1,33 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:model/oidc/token_oidc.dart'; +import 'package:tmail_ui_user/features/caching/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); + if (tokenCache == null) { + throw NotFoundStoredTokenException(); + } else { + return tokenCache.toTokenOidc(); + } + } + + Future persistOneTokenOidc(TokenOIDC tokenOIDC) async { + log('TokenOidcCacheManager::persistOneTokenOidc(): $tokenOIDC'); + final emailCacheExist = await _tokenOidcCacheClient.isExistTable(); + if (emailCacheExist) { + await _tokenOidcCacheClient.clearAllData(); + } + log('TokenOidcCacheManager::persistOneTokenOidc(): key: ${tokenOIDC.tokenId.uuid}'); + log('TokenOidcCacheManager::persistOneTokenOidc(): key\'s hash: ${tokenOIDC.tokenId.hashCode.toString()}'); + log('TokenOidcCacheManager::persistOneTokenOidc(): token: ${tokenOIDC.token}'); + await _tokenOidcCacheClient.insertItem(tokenOIDC.tokenId.hashCode.toString(), tokenOIDC.toTokenOidcCache()); + } +} \ 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 new file mode 100644 index 000000000..26b738bf9 --- /dev/null +++ b/lib/features/login/data/model/token_oidc_cache.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; + +part 'token_oidc_cache.g.dart'; + +@HiveType(typeId: CachingConstants.TOKEN_OIDC_HIVE_CACHE_IDENTIFY) +class TokenOidcCache extends HiveObject with EquatableMixin { + + @HiveField(0) + final String token; + + @HiveField(1) + final String tokenId; + + @HiveField(2) + final DateTime? expiredTime; + + @HiveField(3) + final String refreshToken; + + TokenOidcCache(this.token, this.tokenId, this.refreshToken, {this.expiredTime}); + + @override + List get props => [token, tokenId, expiredTime, refreshToken]; +} \ No newline at end of file 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 6a1eb622a..41dc4cd75 100644 --- a/lib/features/login/data/repository/authentication_oidc_repository_impl.dart +++ b/lib/features/login/data/repository/authentication_oidc_repository_impl.dart @@ -24,4 +24,14 @@ class AuthenticationOIDCRepositoryImpl extends AuthenticationOIDCRepository { 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); + } } \ 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 38df1aa99..a783929c6 100644 --- a/lib/features/login/domain/exceptions/authentication_exception.dart +++ b/lib/features/login/domain/exceptions/authentication_exception.dart @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart'; abstract class AuthenticationException extends Equatable { static const wrongCredential = 'Credential is wrong'; + static const invalidBaseUrl = 'Invalid base URL'; const AuthenticationException(String message); } @@ -11,4 +12,19 @@ class BadCredentials extends AuthenticationException { @override List get props => []; +} + +class NotFoundAuthenticatedAccountException implements Exception { + NotFoundAuthenticatedAccountException(); +} + +class NotFoundStoredTokenException implements Exception { + NotFoundStoredTokenException(); +} + +class InvalidBaseUrl extends AuthenticationException { + const InvalidBaseUrl() : super(AuthenticationException.invalidBaseUrl); + + @override + List get props => []; } \ No newline at end of file