From c459517e469f0837c581dba18e848055e9967c93 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 22 Jun 2022 18:00:42 +0300 Subject: [PATCH] Added some tests.| #1768 --- .../email/ui/RefreshKeysFromEkmFlowTest.kt | 71 +------ ...kedKeysFromEkmDeleteNotMatchingFlowTest.kt | 144 +++++++++++++ .../ui/RefreshRevokedKeysFromEkmFlowTest.kt | 198 ++++++++++++++++++ .../ui/base/BaseRefreshKeysFromEkmFlowTest.kt | 89 ++++++++ .../RefreshPrivateKeysFromEkmViewModel.kt | 40 ++-- 5 files changed, 462 insertions(+), 80 deletions(-) create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmDeleteNotMatchingFlowTest.kt create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmFlowTest.kt create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseRefreshKeysFromEkmFlowTest.kt diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshKeysFromEkmFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshKeysFromEkmFlowTest.kt index 111b8b0dfc..df1bb2fd3d 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshKeysFromEkmFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshKeysFromEkmFlowTest.kt @@ -12,42 +12,32 @@ import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.RootMatchers.isDialog import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.ext.junit.rules.activityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.flowcrypt.email.R import com.flowcrypt.email.TestConstants -import com.flowcrypt.email.api.retrofit.ApiHelper import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse import com.flowcrypt.email.api.retrofit.response.model.Key -import com.flowcrypt.email.api.retrofit.response.model.OrgRules -import com.flowcrypt.email.base.BaseTest import com.flowcrypt.email.database.entity.KeyEntity import com.flowcrypt.email.extensions.org.pgpainless.util.asString import com.flowcrypt.email.model.KeyImportDetails -import com.flowcrypt.email.rules.AddAccountToDatabaseRule import com.flowcrypt.email.rules.AddPrivateKeyToDatabaseRule import com.flowcrypt.email.rules.ClearAppSettingsRule -import com.flowcrypt.email.rules.FlowCryptMockWebServerRule import com.flowcrypt.email.rules.RetryRule import com.flowcrypt.email.rules.ScreenshotTestRule import com.flowcrypt.email.security.KeysStorageImpl import com.flowcrypt.email.security.model.PgpKeyDetails import com.flowcrypt.email.security.pgp.PgpKey -import com.flowcrypt.email.ui.activity.MainActivity -import com.flowcrypt.email.util.AccountDaoManager +import com.flowcrypt.email.ui.base.BaseRefreshKeysFromEkmFlowTest import com.flowcrypt.email.util.PrivateKeysManager import com.flowcrypt.email.util.exception.ApiException import com.google.gson.Gson -import okhttp3.mockwebserver.Dispatcher import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.RecordedRequest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain -import org.junit.rules.TestName import org.junit.rules.TestRule import org.junit.runner.RunWith import org.pgpainless.util.Passphrase @@ -61,22 +51,7 @@ import java.net.HttpURLConnection */ @MediumTest @RunWith(AndroidJUnit4::class) -class RefreshKeysFromEkmFlowTest : BaseTest() { - private val userWithOrgRules = AccountDaoManager.getUserWithOrgRules( - OrgRules( - flags = listOf( - OrgRules.DomainRule.NO_PRV_CREATE, - OrgRules.DomainRule.NO_PRV_BACKUP, - OrgRules.DomainRule.FORBID_STORING_PASS_PHRASE - ), - customKeyserverUrl = null, - keyManagerUrl = EKM_URL, - enforceKeygenAlgo = null, - enforceKeygenExpireMonths = null - ) - ).copy(email = "ekm@localhost:1212") - - private val addAccountToDatabaseRule = AddAccountToDatabaseRule(userWithOrgRules) +class RefreshKeysFromEkmFlowTest : BaseRefreshKeysFromEkmFlowTest() { private val addPrivateKeyToDatabaseRule = AddPrivateKeyToDatabaseRule( accountEntity = addAccountToDatabaseRule.account, keyPath = "pgp/expired@flowcrypt.test_prv_default.asc", @@ -84,23 +59,6 @@ class RefreshKeysFromEkmFlowTest : BaseTest() { sourceType = KeyImportDetails.SourceType.EMAIL, passphraseType = KeyEntity.PassphraseType.RAM ) - private val mockWebServerRule = FlowCryptMockWebServerRule(TestConstants.MOCK_WEB_SERVER_PORT, - object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse { - val gson = ApiHelper.getInstance(getTargetContext()).gson - - if (request.path?.startsWith("/ekm") == true) { - return handleEkmAPI(gson) - } - - return MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND) - } - }) - - override val activityScenarioRule = activityScenarioRule() - - @get:Rule - val testNameRule = TestName() @get:Rule var ruleChain: TestRule = RuleChain @@ -115,7 +73,10 @@ class RefreshKeysFromEkmFlowTest : BaseTest() { @Test fun testUpdatePrvKeyFromEkmSuccessSilent() { val keysStorage = KeysStorageImpl.getInstance(getTargetContext()) - addPassphraseToRamCache(keysStorage) + addPassphraseToRamCache( + keysStorage = keysStorage, + fingerprint = addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint + ) //check existing key before updating val existingPgpKeyDetailsBeforeUpdating = checkExistingKeyBeforeUpdating(keysStorage) @@ -169,7 +130,10 @@ class RefreshKeysFromEkmFlowTest : BaseTest() { @Test fun testUpdatePrvKeyFromEkmShowApiError() { val keysStorage = KeysStorageImpl.getInstance(getTargetContext()) - addPassphraseToRamCache(keysStorage) + addPassphraseToRamCache( + keysStorage = keysStorage, + fingerprint = addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint + ) //check existing key before updating val existingPgpKeyDetailsBeforeUpdating = checkExistingKeyBeforeUpdating(keysStorage) @@ -200,19 +164,7 @@ class RefreshKeysFromEkmFlowTest : BaseTest() { return existingPgpKeyDetailsBeforeUpdating } - private fun addPassphraseToRamCache(keysStorage: KeysStorageImpl) { - keysStorage.putPassphraseToCache( - fingerprint = addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint, - passphrase = Passphrase.fromPassword(TestConstants.DEFAULT_PASSWORD), - validUntil = KeysStorageImpl.calculateLifeTimeForPassphrase(), - passphraseType = KeyEntity.PassphraseType.RAM - ) - } - - private fun handleEkmAPI(gson: Gson): MockResponse { - //simulate network operation to prevent too fast response - Thread.sleep(500) - + override fun handleEkmAPI(gson: Gson): MockResponse { return when (testNameRule.methodName) { "testUpdatePrvKeyFromEkmSuccessSilent", "testUpdatePrvKeyFromEkmShowFixMissingPassphrase" -> MockResponse().setResponseCode(HttpURLConnection.HTTP_OK) @@ -227,7 +179,6 @@ class RefreshKeysFromEkmFlowTest : BaseTest() { } companion object { - private const val EKM_URL = "https://localhost:1212/ekm/" private val EKM_KEY_WITH_EXTENDED_EXPIRATION = PrivateKeysManager.getPgpKeyDetailsFromAssets( "pgp/expired_extended@flowcrypt.test_prv_default.asc" ) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmDeleteNotMatchingFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmDeleteNotMatchingFlowTest.kt new file mode 100644 index 0000000000..b61fb3aa6e --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmDeleteNotMatchingFlowTest.kt @@ -0,0 +1,144 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.ui + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.TestConstants +import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse +import com.flowcrypt.email.api.retrofit.response.model.Key +import com.flowcrypt.email.database.entity.KeyEntity +import com.flowcrypt.email.model.KeyImportDetails +import com.flowcrypt.email.rules.AddPrivateKeyToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import com.flowcrypt.email.security.KeysStorageImpl +import com.flowcrypt.email.security.pgp.PgpKey +import com.flowcrypt.email.ui.base.BaseRefreshKeysFromEkmFlowTest +import com.flowcrypt.email.util.PrivateKeysManager +import com.google.gson.Gson +import okhttp3.mockwebserver.MockResponse +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule +import org.junit.runner.RunWith +import org.pgpainless.util.Passphrase +import java.net.HttpURLConnection + +/** + * @author Denis Bondarenko + * Date: 6/22/22 + * Time: 6:39 PM + * E-mail: DenBond7@gmail.com + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +class RefreshRevokedKeysFromEkmDeleteNotMatchingFlowTest : BaseRefreshKeysFromEkmFlowTest() { + private val addPrivateKeyToDatabaseRule = AddPrivateKeyToDatabaseRule( + accountEntity = addAccountToDatabaseRule.account, + keyPath = "pgp/default@flowcrypt.test_fisrtKey_prv_default_revoked.asc", + passphrase = TestConstants.DEFAULT_PASSWORD, + sourceType = KeyImportDetails.SourceType.EMAIL, + passphraseType = KeyEntity.PassphraseType.RAM + ) + private val addSecondPrivateKeyToDatabaseRule = AddPrivateKeyToDatabaseRule( + accountEntity = addAccountToDatabaseRule.account, + keyPath = TestConstants.DEFAULT_SECOND_KEY_PRV_STRONG, + passphrase = TestConstants.DEFAULT_STRONG_PASSWORD, + sourceType = KeyImportDetails.SourceType.EMAIL, + passphraseType = KeyEntity.PassphraseType.RAM + ) + + @get:Rule + var ruleChain: TestRule = RuleChain + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(mockWebServerRule) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(addSecondPrivateKeyToDatabaseRule) + .around(activityScenarioRule) + .around(ScreenshotTestRule()) + + override fun handleEkmAPI(gson: Gson): MockResponse { + return when (testNameRule.methodName) { + "testDeleteNotMatchingKeys" -> + MockResponse().setResponseCode(HttpURLConnection.HTTP_OK) + .setBody(gson.toJson(EKM_RESPONSE_SUCCESS_WITH_KEY_WHERE_MODIFICATION_DATE_AFTER_REVOKED)) + + else -> MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND) + } + } + + @Test + fun testDeleteNotMatchingKeys() { + val keysStorage = KeysStorageImpl.getInstance(getTargetContext()) + addPassphraseToRamCache( + keysStorage = keysStorage, + fingerprint = addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint + ) + + //check existing keys before updating + val firstPgpKeyDetailsBeforeUpdating = keysStorage.getPgpKeyDetailsList() + .first { it.fingerprint == addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint } + assertTrue(firstPgpKeyDetailsBeforeUpdating.isRevoked) + + assertNotNull(keysStorage.getPgpKeyDetailsList() + .firstOrNull { it.fingerprint == addSecondPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint }) + + //we need to make a delay to wait while [KeysStorageImpl] will update internal data + Thread.sleep(2000) + + //check existing keys after updating. We should have only the first key. + assertEquals(keysStorage.getPgpKeyDetailsList().size, 1) + assertNull(keysStorage.getPgpKeyDetailsList() + .firstOrNull { it.fingerprint == addSecondPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint }) + + val firstPgpKeyDetailsAfterUpdating = keysStorage.getPgpKeyDetailsList() + .first { it.fingerprint == firstPgpKeyDetailsBeforeUpdating.fingerprint } + assertTrue(firstPgpKeyDetailsAfterUpdating.isRevoked) + assertEquals( + firstPgpKeyDetailsBeforeUpdating.fingerprint, + firstPgpKeyDetailsAfterUpdating.fingerprint + ) + assertEquals( + firstPgpKeyDetailsBeforeUpdating.lastModified, + firstPgpKeyDetailsAfterUpdating.lastModified + ) + + onView(withId(R.id.toolbar)) + .check(matches(isDisplayed())) + } + + companion object { + private val EKM_KEY_WITH_MODIFICATION_DATE_AFTER_REVOKED = + PrivateKeysManager.getPgpKeyDetailsFromAssets( + "pgp/default@flowcrypt.test_fisrtKey_prv_default_mod_06_17_2022.asc" + ) + private val EKM_RESPONSE_SUCCESS_WITH_KEY_WHERE_MODIFICATION_DATE_AFTER_REVOKED = + EkmPrivateKeysResponse( + privateKeys = listOf( + Key( + PgpKey.decryptKey( + requireNotNull(EKM_KEY_WITH_MODIFICATION_DATE_AFTER_REVOKED.privateKey), + Passphrase.fromPassword(TestConstants.DEFAULT_PASSWORD) + ) + ) + ) + ) + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmFlowTest.kt new file mode 100644 index 0000000000..774035f84e --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/RefreshRevokedKeysFromEkmFlowTest.kt @@ -0,0 +1,198 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.ui + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.TestConstants +import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse +import com.flowcrypt.email.api.retrofit.response.model.Key +import com.flowcrypt.email.database.entity.KeyEntity +import com.flowcrypt.email.extensions.org.pgpainless.util.asString +import com.flowcrypt.email.model.KeyImportDetails +import com.flowcrypt.email.rules.AddPrivateKeyToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import com.flowcrypt.email.security.KeysStorageImpl +import com.flowcrypt.email.security.model.PgpKeyDetails +import com.flowcrypt.email.security.pgp.PgpKey +import com.flowcrypt.email.ui.base.BaseRefreshKeysFromEkmFlowTest +import com.flowcrypt.email.util.PrivateKeysManager +import com.google.gson.Gson +import okhttp3.mockwebserver.MockResponse +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule +import org.junit.runner.RunWith +import org.pgpainless.util.Passphrase +import java.net.HttpURLConnection + +/** + * @author Denis Bondarenko + * Date: 6/22/22 + * Time: 5:45 PM + * E-mail: DenBond7@gmail.com + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +class RefreshRevokedKeysFromEkmFlowTest : BaseRefreshKeysFromEkmFlowTest() { + private val addPrivateKeyToDatabaseRule = AddPrivateKeyToDatabaseRule( + accountEntity = addAccountToDatabaseRule.account, + keyPath = "pgp/default@flowcrypt.test_fisrtKey_prv_default_revoked.asc", + passphrase = TestConstants.DEFAULT_PASSWORD, + sourceType = KeyImportDetails.SourceType.EMAIL, + passphraseType = KeyEntity.PassphraseType.RAM + ) + + @get:Rule + var ruleChain: TestRule = RuleChain + .outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(mockWebServerRule) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(activityScenarioRule) + .around(ScreenshotTestRule()) + + override fun handleEkmAPI(gson: Gson): MockResponse { + return when (testNameRule.methodName) { + "testDisallowUpdateRevokedKeys" -> + MockResponse().setResponseCode(HttpURLConnection.HTTP_OK) + .setBody(gson.toJson(EKM_RESPONSE_SUCCESS_WITH_KEY_WHERE_MODIFICATION_DATE_AFTER_REVOKED)) + + "testDisallowDeleteRevokedKeys" -> + MockResponse().setResponseCode(HttpURLConnection.HTTP_OK) + .setBody(gson.toJson(EKM_RESPONSE_SUCCESS_DIFFERENT_FINGERPRINT_KEY)) + + else -> MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND) + } + } + + @Test + fun testDisallowUpdateRevokedKeys() { + val keysStorage = KeysStorageImpl.getInstance(getTargetContext()) + addPassphraseToRamCache( + keysStorage = keysStorage, + fingerprint = addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint + ) + + //check existing key before updating + val existingPgpKeyDetailsBeforeUpdating = checkExistingKeyBeforeUpdating(keysStorage) + + //we need to make a delay to wait while [KeysStorageImpl] will update internal data + Thread.sleep(2000) + + //check existing key after updating. We should have the same key as before. + val existingPgpKeyDetailsAfterUpdating = keysStorage.getPgpKeyDetailsList().first() + assertTrue(existingPgpKeyDetailsAfterUpdating.isRevoked) + assertEquals( + existingPgpKeyDetailsBeforeUpdating.fingerprint, + existingPgpKeyDetailsAfterUpdating.fingerprint + ) + assertEquals( + existingPgpKeyDetailsBeforeUpdating.lastModified, + existingPgpKeyDetailsAfterUpdating.lastModified + ) + + onView(withId(R.id.toolbar)) + .check(matches(isDisplayed())) + } + + @Test + fun testDisallowDeleteRevokedKeys() { + val keysStorage = KeysStorageImpl.getInstance(getTargetContext()) + addPassphraseToRamCache( + keysStorage = keysStorage, + fingerprint = addPrivateKeyToDatabaseRule.pgpKeyDetails.fingerprint + ) + + //check existing key before updating + val firstPgpKeyDetailsBeforeUpdating = checkExistingKeyBeforeUpdating(keysStorage) + + //we need to make a delay to wait while [KeysStorageImpl] will update internal data + Thread.sleep(2000) + + //check existing keys after updating. We should have the same key as before + one new. + assertEquals(keysStorage.getPgpKeyDetailsList().size, 2) + + val firstPgpKeyDetailsAfterUpdating = keysStorage.getPgpKeyDetailsList() + .first { it.fingerprint == firstPgpKeyDetailsBeforeUpdating.fingerprint } + assertTrue(firstPgpKeyDetailsAfterUpdating.isRevoked) + assertEquals( + firstPgpKeyDetailsBeforeUpdating.fingerprint, + firstPgpKeyDetailsAfterUpdating.fingerprint + ) + assertEquals( + firstPgpKeyDetailsBeforeUpdating.lastModified, + firstPgpKeyDetailsAfterUpdating.lastModified + ) + + val secondPgpKeyDetailsAfterUpdating = keysStorage.getPgpKeyDetailsList() + .first { it.fingerprint == EKM_KEY_DIFFERENT_FINGERPRINT.fingerprint } + assertFalse(secondPgpKeyDetailsAfterUpdating.isRevoked) + assertEquals( + secondPgpKeyDetailsAfterUpdating.fingerprint, + EKM_KEY_DIFFERENT_FINGERPRINT.fingerprint + ) + + onView(withId(R.id.toolbar)) + .check(matches(isDisplayed())) + } + + private fun checkExistingKeyBeforeUpdating(keysStorage: KeysStorageImpl): PgpKeyDetails { + val existingPgpKeyDetailsBeforeUpdating = keysStorage.getPgpKeyDetailsList().first() + assertTrue(existingPgpKeyDetailsBeforeUpdating.isRevoked) + assertEquals( + addPrivateKeyToDatabaseRule.passphrase, + keysStorage.getPassphraseByFingerprint(existingPgpKeyDetailsBeforeUpdating.fingerprint)?.asString + ) + return existingPgpKeyDetailsBeforeUpdating + } + + companion object { + private val EKM_KEY_WITH_MODIFICATION_DATE_AFTER_REVOKED = + PrivateKeysManager.getPgpKeyDetailsFromAssets( + "pgp/default@flowcrypt.test_fisrtKey_prv_default_mod_06_17_2022.asc" + ) + private val EKM_KEY_DIFFERENT_FINGERPRINT = + PrivateKeysManager.getPgpKeyDetailsFromAssets( + "pgp/default@flowcrypt.test_secondKey_prv_default.asc" + ) + private val EKM_RESPONSE_SUCCESS_WITH_KEY_WHERE_MODIFICATION_DATE_AFTER_REVOKED = + EkmPrivateKeysResponse( + privateKeys = listOf( + Key( + PgpKey.decryptKey( + requireNotNull(EKM_KEY_WITH_MODIFICATION_DATE_AFTER_REVOKED.privateKey), + Passphrase.fromPassword(TestConstants.DEFAULT_PASSWORD) + ) + ) + ) + ) + + private val EKM_RESPONSE_SUCCESS_DIFFERENT_FINGERPRINT_KEY = + EkmPrivateKeysResponse( + privateKeys = listOf( + Key( + PgpKey.decryptKey( + requireNotNull(EKM_KEY_DIFFERENT_FINGERPRINT.privateKey), + Passphrase.fromPassword(TestConstants.DEFAULT_PASSWORD) + ) + ) + ) + ) + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseRefreshKeysFromEkmFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseRefreshKeysFromEkmFlowTest.kt new file mode 100644 index 0000000000..77d1621313 --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseRefreshKeysFromEkmFlowTest.kt @@ -0,0 +1,89 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.ui.base + +import androidx.test.ext.junit.rules.activityScenarioRule +import com.flowcrypt.email.TestConstants +import com.flowcrypt.email.api.retrofit.ApiHelper +import com.flowcrypt.email.api.retrofit.response.model.OrgRules +import com.flowcrypt.email.base.BaseTest +import com.flowcrypt.email.database.entity.KeyEntity +import com.flowcrypt.email.rules.AddAccountToDatabaseRule +import com.flowcrypt.email.rules.FlowCryptMockWebServerRule +import com.flowcrypt.email.security.KeysStorageImpl +import com.flowcrypt.email.ui.activity.MainActivity +import com.flowcrypt.email.util.AccountDaoManager +import com.google.gson.Gson +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest +import org.junit.Rule +import org.junit.rules.TestName +import org.pgpainless.util.Passphrase +import java.net.HttpURLConnection + +/** + * @author Denis Bondarenko + * Date: 6/22/22 + * Time: 6:47 PM + * E-mail: DenBond7@gmail.com + */ +abstract class BaseRefreshKeysFromEkmFlowTest : BaseTest() { + abstract fun handleEkmAPI(gson: Gson): MockResponse + + @get:Rule + val testNameRule = TestName() + + override val activityScenarioRule = activityScenarioRule() + + protected val userWithOrgRules = AccountDaoManager.getUserWithOrgRules( + OrgRules( + flags = listOf( + OrgRules.DomainRule.NO_PRV_CREATE, + OrgRules.DomainRule.NO_PRV_BACKUP, + OrgRules.DomainRule.FORBID_STORING_PASS_PHRASE + ), + customKeyserverUrl = null, + keyManagerUrl = EKM_URL, + enforceKeygenAlgo = null, + enforceKeygenExpireMonths = null + ) + ).copy(email = "ekm@localhost:1212") + + protected val addAccountToDatabaseRule = AddAccountToDatabaseRule(userWithOrgRules) + + protected val mockWebServerRule = FlowCryptMockWebServerRule(TestConstants.MOCK_WEB_SERVER_PORT, + object : Dispatcher() { + override fun dispatch(request: RecordedRequest): MockResponse { + val gson = ApiHelper.getInstance(getTargetContext()).gson + + if (request.path?.startsWith("/ekm") == true) { + //simulate network operation to prevent too fast response + Thread.sleep(500) + return handleEkmAPI(gson) + } + + return MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_FOUND) + } + }) + + protected fun addPassphraseToRamCache( + keysStorage: KeysStorageImpl, + fingerprint: String, + passphrase: String = TestConstants.DEFAULT_PASSWORD, + ) { + keysStorage.putPassphraseToCache( + fingerprint = fingerprint, + passphrase = Passphrase.fromPassword(passphrase), + validUntil = KeysStorageImpl.calculateLifeTimeForPassphrase(), + passphraseType = KeyEntity.PassphraseType.RAM + ) + } + + companion object { + private const val EKM_URL = "https://localhost:1212/ekm/" + } +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RefreshPrivateKeysFromEkmViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RefreshPrivateKeysFromEkmViewModel.kt index 94550fed2c..394e4ce7f7 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RefreshPrivateKeysFromEkmViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/RefreshPrivateKeysFromEkmViewModel.kt @@ -158,6 +158,26 @@ class RefreshPrivateKeysFromEkmViewModel(application: Application) : AccountView val keysToUpdate = mutableListOf() val keysToAdd = mutableListOf() + val fingerprintsOfFetchedKeys = fetchedPgpKeyDetailsList.map { it.fingerprint.uppercase() } + val fingerprintsOfKeysToDelete = existingPgpKeyDetailsList.filter { + !it.isRevoked && it.fingerprint.uppercase() !in fingerprintsOfFetchedKeys + }.map { it.fingerprint } + + val keysToDelete = existingKeyEntities.filter { + it.fingerprint.uppercase() in fingerprintsOfKeysToDelete + } + + if (keysToDelete.isNotEmpty() && keysToDelete.size == existingKeyEntities.size) { + /* + it means we are going to delete all existing keys. It looks like a bug on EKM side. + To prevent unexpected issues we should be sure that at least one private key + is existing in the app. + */ + roomDatabase.keysDao().deleteSuspend(keysToDelete.dropLast(1)) + } else { + roomDatabase.keysDao().deleteSuspend(keysToDelete) + } + for (fetchedPgpKeyDetails in fetchedPgpKeyDetailsList) { val existingPgpKeyDetails = existingPgpKeyDetailsList.firstOrNull { it.fingerprint == fetchedPgpKeyDetails.fingerprint @@ -201,26 +221,6 @@ class RefreshPrivateKeysFromEkmViewModel(application: Application) : AccountView ) roomDatabase.keysDao().insertSuspend(keyEntity) } - - val fingerprintsOfFetchedKeys = fetchedPgpKeyDetailsList.map { it.fingerprint.uppercase() } - val fingerprintsOfKeysToDelete = existingPgpKeyDetailsList.filter { - !it.isRevoked && it.fingerprint.uppercase() !in fingerprintsOfFetchedKeys - }.map { it.fingerprint } - - val keysToDelete = existingKeyEntities.filter { - it.fingerprint.uppercase() in fingerprintsOfKeysToDelete - } - - if (keysToDelete.isNotEmpty() && keysToDelete.size == existingKeyEntities.size) { - /* - it means we are going to delete all existing keys. It looks like a bug on EKM side. - To prevent unexpected issues we should be sure that at least one private key - is existing in the app. - */ - roomDatabase.keysDao().deleteSuspend(keysToDelete.dropLast(1)) - } else { - roomDatabase.keysDao().deleteSuspend(keysToDelete) - } } /**