From 136cbf3ed10911b7684c3565008bb6b8f4bb745d Mon Sep 17 00:00:00 2001 From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com> Date: Wed, 10 Jul 2024 15:54:35 +0200 Subject: [PATCH] DROID-2660 Login | Tech | Seed phrase check (#1385) --- .../anytype/data/auth/repo/AuthCache.kt | 2 +- .../anytype/data/auth/repo/AuthDataStore.kt | 2 +- .../interactor/CheckAuthorizationStatus.kt | 3 +- .../domain/auth/interactor/GetMnemonic.kt | 1 + .../domain/auth/interactor/LaunchWallet.kt | 4 +- .../domain/auth/interactor/ResumeAccount.kt | 4 +- .../domain/auth/repo/AuthRepository.kt | 2 +- .../auth/CheckAuthorizationStatusTest.kt | 71 ++++++++++++++++++- .../persistence/repo/DefaultAuthCache.kt | 4 +- 9 files changed, 84 insertions(+), 9 deletions(-) diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthCache.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthCache.kt index 024ec20f9b..b2f4d1c990 100644 --- a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthCache.kt +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthCache.kt @@ -9,7 +9,7 @@ interface AuthCache { suspend fun updateAccount(account: AccountEntity) suspend fun saveMnemonic(mnemonic: String) - suspend fun getMnemonic(): String + suspend fun getMnemonic(): String? suspend fun getCurrentAccount(): AccountEntity suspend fun getCurrentAccountId(): String diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthDataStore.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthDataStore.kt index 3c441fd10e..e24b9a643a 100644 --- a/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthDataStore.kt +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/repo/AuthDataStore.kt @@ -31,7 +31,7 @@ interface AuthDataStore { suspend fun recoverWallet(path: String, mnemonic: String) suspend fun convertWallet(entropy: String): String suspend fun saveMnemonic(mnemonic: String) - suspend fun getMnemonic(): String + suspend fun getMnemonic(): String? suspend fun logout(clearLocalRepositoryData: Boolean) suspend fun getAccounts(): List diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/CheckAuthorizationStatus.kt b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/CheckAuthorizationStatus.kt index 93c8d9189a..8c9e5d87ba 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/CheckAuthorizationStatus.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/CheckAuthorizationStatus.kt @@ -16,7 +16,8 @@ class CheckAuthorizationStatus @Inject constructor( override suspend fun run(params: Unit) = safe { repository.getAccounts().let { accounts -> - if (accounts.isNotEmpty()) + val mnemonic= repository.getMnemonic() + if (accounts.isNotEmpty() && !mnemonic.isNullOrBlank()) AuthStatus.AUTHORIZED else AuthStatus.UNAUTHORIZED diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/GetMnemonic.kt b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/GetMnemonic.kt index 0df1448725..8f0483b9ee 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/GetMnemonic.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/GetMnemonic.kt @@ -13,6 +13,7 @@ class GetMnemonic( override suspend fun run(params: Unit) = try { repository.getMnemonic().let { mnemonic -> + if (mnemonic.isNullOrBlank()) throw IllegalStateException("Mnemonic is empty") Either.Right(mnemonic) } } catch (e: Throwable) { diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/LaunchWallet.kt b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/LaunchWallet.kt index 42790196d0..01c22772b0 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/LaunchWallet.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/LaunchWallet.kt @@ -16,8 +16,10 @@ class LaunchWallet( override suspend fun run(params: None) = try { withTimeout(TIMEOUT_DURATION) { + val mnemonic = repository.getMnemonic() + if (mnemonic.isNullOrBlank()) throw IllegalStateException("Mnemonic is empty") repository.recoverWallet( - mnemonic = repository.getMnemonic(), + mnemonic = mnemonic, path = pathProvider.providePath() ).let { Either.Right(it) diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/ResumeAccount.kt b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/ResumeAccount.kt index 747fd2b03c..49e37f405f 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/ResumeAccount.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/auth/interactor/ResumeAccount.kt @@ -32,9 +32,11 @@ class ResumeAccount @Inject constructor( version = metricsProvider.getVersion(), platform = metricsProvider.getPlatform() ) + val mnemonic = repository.getMnemonic() + if (mnemonic.isNullOrBlank()) throw IllegalStateException("Mnemonic is empty") repository.recoverWallet( path = pathProvider.providePath(), - mnemonic = repository.getMnemonic() + mnemonic = mnemonic ) val networkMode = repository.getNetworkMode() diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/auth/repo/AuthRepository.kt b/domain/src/main/java/com/anytypeio/anytype/domain/auth/repo/AuthRepository.kt index f04cd29910..7a2dc44b68 100644 --- a/domain/src/main/java/com/anytypeio/anytype/domain/auth/repo/AuthRepository.kt +++ b/domain/src/main/java/com/anytypeio/anytype/domain/auth/repo/AuthRepository.kt @@ -43,7 +43,7 @@ interface AuthRepository { suspend fun saveMnemonic(mnemonic: String) - suspend fun getMnemonic(): String + suspend fun getMnemonic(): String? suspend fun logout(clearLocalRepositoryData: Boolean) diff --git a/domain/src/test/java/com/anytypeio/anytype/domain/auth/CheckAuthorizationStatusTest.kt b/domain/src/test/java/com/anytypeio/anytype/domain/auth/CheckAuthorizationStatusTest.kt index 7f397e9beb..c24912f54e 100644 --- a/domain/src/test/java/com/anytypeio/anytype/domain/auth/CheckAuthorizationStatusTest.kt +++ b/domain/src/test/java/com/anytypeio/anytype/domain/auth/CheckAuthorizationStatusTest.kt @@ -45,16 +45,81 @@ class CheckAuthorizationStatusTest { onBlocking { getAccounts() } doReturn emptyList() } + repo.stub { + onBlocking { getMnemonic() } doReturn "mnemonic" + } + val result = checkAuthorizationStatus.run(params = Unit) assertTrue { result == Either.Right(AuthStatus.UNAUTHORIZED) } verify(repo, times(1)).getAccounts() + verify(repo, times(1)).getMnemonic() verifyNoMoreInteractions(repo) } @Test - fun `should return authorized status if account list is not empty`() = runBlocking { + fun `should return unauthorized status if phrase is null`() = runBlocking { + + repo.stub { + onBlocking { getAccounts() } doReturn emptyList() + } + + repo.stub { + onBlocking { getMnemonic() } doReturn null + } + + val result = checkAuthorizationStatus.run(params = Unit) + + assertTrue { result == Either.Right(AuthStatus.UNAUTHORIZED) } + + verify(repo, times(1)).getAccounts() + verify(repo, times(1)).getMnemonic() + verifyNoMoreInteractions(repo) + } + + @Test + fun `should return unauthorized status if phrase is empty`() = runBlocking { + + repo.stub { + onBlocking { getAccounts() } doReturn emptyList() + } + + repo.stub { + onBlocking { getMnemonic() } doReturn "" + } + + val result = checkAuthorizationStatus.run(params = Unit) + + assertTrue { result == Either.Right(AuthStatus.UNAUTHORIZED) } + + verify(repo, times(1)).getAccounts() + verify(repo, times(1)).getMnemonic() + verifyNoMoreInteractions(repo) + } + + @Test + fun `should return unauthorized status if phrase is blank`() = runBlocking { + + repo.stub { + onBlocking { getAccounts() } doReturn emptyList() + } + + repo.stub { + onBlocking { getMnemonic() } doReturn " " + } + + val result = checkAuthorizationStatus.run(params = Unit) + + assertTrue { result == Either.Right(AuthStatus.UNAUTHORIZED) } + + verify(repo, times(1)).getAccounts() + verify(repo, times(1)).getMnemonic() + verifyNoMoreInteractions(repo) + } + + @Test + fun `should return authorized status if account list is not empty and phrase is not empty`() = runBlocking { val account = Account( id = MockDataFactory.randomString() @@ -63,12 +128,16 @@ class CheckAuthorizationStatusTest { repo.stub { onBlocking { getAccounts() } doReturn listOf(account) } + repo.stub { + onBlocking { getMnemonic() } doReturn "1" + } val result = checkAuthorizationStatus.run(params = Unit) assertTrue { result == Either.Right(AuthStatus.AUTHORIZED) } verify(repo, times(1)).getAccounts() + verify(repo, times(1)).getMnemonic() verifyNoMoreInteractions(repo) } diff --git a/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultAuthCache.kt b/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultAuthCache.kt index 87ff8b7ac5..a1ac72cc8f 100644 --- a/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultAuthCache.kt +++ b/persistence/src/main/java/com/anytypeio/anytype/persistence/repo/DefaultAuthCache.kt @@ -55,7 +55,7 @@ class DefaultAuthCache( /** * N.B. Migrating sensitive data from default to encrypted prefs. */ - override suspend fun getMnemonic(): String { + override suspend fun getMnemonic(): String? { val nonEncryptedMnemonic = defaultPrefs.getString(MNEMONIC_KEY, null) return if (nonEncryptedMnemonic != null) { encryptedPrefs.edit().putString(MNEMONIC_KEY, nonEncryptedMnemonic).apply() @@ -63,7 +63,7 @@ class DefaultAuthCache( nonEncryptedMnemonic } else { val encryptedMnemonic = encryptedPrefs.getString(MNEMONIC_KEY, null) - encryptedMnemonic ?: throw IllegalStateException("Recovery phrase is missing") + encryptedMnemonic } }