diff --git a/FlowCrypt/src/androidTest/assets/pgp/fes@flowcrypt.test_prv_decrypted.asc b/FlowCrypt/src/androidTest/assets/pgp/fes@flowcrypt.test_prv_decrypted.asc new file mode 100644 index 0000000000..5cf952459e --- /dev/null +++ b/FlowCrypt/src/androidTest/assets/pgp/fes@flowcrypt.test_prv_decrypted.asc @@ -0,0 +1,15 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: PGPainless + +lFgEYQEt2hYJKwYBBAHaRw8BAQdABuJKFITCzpGGj80h5+gUnRd7pvJIm6AlA+fX +V45W30gAAQD83U7jx82N0U2p3B8H9Jynw0y97Nin4poW6z15TCV8XhLhtBJmZXNA +Zmxvd2NyeXB0LnRlc3SIeAQTFgoAIAUCYQEt2wIbAwUWAgMBAAQLCQgHBRUKCQgL +Ah4BAhkBAAoJEESQDK1IVvjzlyEBAJGoHsolKu8jg5hT5mBMOSb5XITnsPj/GHcl +TTRHFYrGAP43g1wz8veMOk2yqnLtt8lACLfBQjsEgwM0OIVB8IQrCpxdBGEBLdsS +CisGAQQBl1UBBQEBB0BZ9rSW4/F6UEp6tbwpk6G9+Vgd52V3cSJ6emKtN03QQgMB +CAcAAP9+QlakDhqGJhebzZ6/0a1ViPYIGvnEAznrs38JKUqesA+8iHUEGBYKAB0F +AmEBLdsCGwwFFgIDAQAECwkIBwUVCgkICwIeAQAKCRBEkAytSFb4841gAQDgNecq +7f5b1hqgPDpJc8Snr9KaRjZLCnm9uPFq9ywvXAEApYnSbi02T7UYeWOnOL2adzyL +rHRIpa4meIEV8C+Gng4= +=H5XH +-----END PGP PRIVATE KEY BLOCK----- diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt index 61e430732f..97fe761e8a 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/base/BaseTest.kt @@ -237,6 +237,14 @@ abstract class BaseTest : BaseActivityTestImplementation { return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY).toString() } + protected fun changeConnectionState(isConnected: Boolean) { + val state = if (isConnected) "enable" else "disable" + InstrumentationRegistry.getInstrumentation().uiAutomation + .executeShellCommand("svc wifi $state") + InstrumentationRegistry.getInstrumentation().uiAutomation + .executeShellCommand("svc data $state") + } + fun getTargetContext(): Context { return InstrumentationRegistry.getInstrumentation().targetContext } diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt index cf42903975..8e274e867e 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt @@ -20,8 +20,10 @@ import com.flowcrypt.email.R import com.flowcrypt.email.TestConstants import com.flowcrypt.email.api.retrofit.ApiHelper import com.flowcrypt.email.api.retrofit.request.model.LoginModel +import com.flowcrypt.email.api.retrofit.response.api.ClientConfigurationResponse import com.flowcrypt.email.api.retrofit.response.api.DomainOrgRulesResponse import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse +import com.flowcrypt.email.api.retrofit.response.api.FesServerResponse import com.flowcrypt.email.api.retrofit.response.api.LoginResponse import com.flowcrypt.email.api.retrofit.response.base.ApiError import com.flowcrypt.email.api.retrofit.response.model.Key @@ -36,16 +38,17 @@ import com.flowcrypt.email.ui.activity.SignInActivity import com.flowcrypt.email.ui.activity.base.BaseSignActivityTest import com.flowcrypt.email.util.PrivateKeysManager import com.flowcrypt.email.util.TestGeneralUtil +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.hamcrest.Matchers.not import org.junit.Before -import org.junit.ClassRule 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 java.io.InputStreamReader @@ -66,18 +69,57 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { ) ) - @Before - fun waitWhileToastWillBeDismissed() { - Thread.sleep(1000) - } + @get:Rule + val testNameRule = TestName() + + val mockWebServerRule = + FlowCryptMockWebServerRule(TestConstants.MOCK_WEB_SERVER_PORT, object : Dispatcher() { + override fun dispatch(request: RecordedRequest): MockResponse { + val gson = + ApiHelper.getInstance(InstrumentationRegistry.getInstrumentation().targetContext).gson + + if (request.path?.startsWith("/api") == true) { + if (request.path.equals("/api/")) { + return handleFesAvailabilityAPI(gson) + } + + if (request.path.equals("/api/v1/client-configuration?domain=localhost:1212")) { + return handleClientConfigurationAPI(gson) + } + } + + if (request.path?.startsWith("/ekm") == true) { + handleEkmAPI(request, gson)?.let { return it } + } + + val model = + gson.fromJson(InputStreamReader(request.body.inputStream()), LoginModel::class.java) + + if (request.path.equals("/account/login")) { + return handleLoginAPI(model, gson) + } + + if (request.path.equals("/account/get")) { + return handleGetDomainRulesAPI(model, gson) + } + + return MockResponse().setResponseCode(404) + } + }) @get:Rule var ruleChain: TestRule = RuleChain .outerRule(ClearAppSettingsRule()) + .around(mockWebServerRule) .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) + @Before + fun waitWhileToastWillBeDismissed() { + Thread.sleep(1000) + } + @Test fun testErrorLogin() { setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_LOGIN_ERROR)) @@ -214,6 +256,273 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { .check(matches(not(isDisplayed()))) } + @Test + fun testFesAvailabilityServerDownNoConnection() { + try { + changeConnectionState(false) + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NO_CONNECTION)) + isDialogWithTextDisplayed( + decorView = decorView, + message = getResString(R.string.no_connection_or_server_is_not_reachable) + ) + } finally { + changeConnectionState(true) + } + } + + @Test + fun testFesAvailabilityServerUpRequestTimeOut() { + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } + + @Test + fun testFesServerUpHasConnectionHttpCode404() { + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_404)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } + + @Test + fun testFesServerUpHasConnectionHttpCodeNotSuccess() { + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } + + @Test + fun testFesServerUpNotEnterpriseServer() { + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NOT_ENTERPRISE_SERVER)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } + + @Test + fun testFesServerUpGetClientConfigurationSuccess() { + setupAndClickSignInButton( + genMockGoogleSignInAccountJson(EMAIL_FES_CLIENT_CONFIGURATION_SUCCESS) + ) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } + + @Test + fun testFesServerUpGetClientConfigurationFailed() { + setupAndClickSignInButton( + genMockGoogleSignInAccountJson(EMAIL_FES_CLIENT_CONFIGURATION_FAILED) + ) + isDialogWithTextDisplayed(decorView, ApiException(ApiError(code = 403, msg = "")).message!!) + } + + private fun handleFesAvailabilityAPI(gson: Gson): MockResponse { + return if (testNameRule.methodName == "testFesAvailabilityServerUpRequestTimeOut") { + val delayInMilliseconds = 6000 + val initialTimeMillis = System.currentTimeMillis() + while (System.currentTimeMillis() - initialTimeMillis <= delayInMilliseconds) { + Thread.sleep(100) + } + MockResponse().setResponseCode(404) + } else { + when { + testNameRule.methodName == "testFesServerUpHasConnectionHttpCode404" -> { + MockResponse().setResponseCode(404) + } + + testNameRule.methodName == "testFesServerUpHasConnectionHttpCodeNotSuccess" -> { + MockResponse().setResponseCode(500) + } + + testNameRule.methodName == "testFesServerUpNotEnterpriseServer" -> { + MockResponse().setResponseCode(200) + .setBody(gson.toJson(FES_SUCCESS_RESPONSE.copy(service = "hello"))) + } + + else -> { + MockResponse().setResponseCode(200) + .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) + } + } + } + } + + private fun handleClientConfigurationAPI(gson: Gson): MockResponse { + return MockResponse().setResponseCode( + if (testNameRule.methodName == "testFesServerUpGetClientConfigurationFailed") 403 else 200 + ).setBody( + gson.toJson( + ClientConfigurationResponse( + orgRules = OrgRules( + flags = ACCEPTED_ORG_RULES, + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + ) + ) + } + + private fun handleEkmAPI(request: RecordedRequest, gson: Gson): MockResponse? { + if (request.path.equals("/ekm/error/v1/keys/private")) { + return MockResponse().setResponseCode(200) + .setBody(gson.toJson(EKM_ERROR_RESPONSE)) + } + + if (request.path.equals("/ekm/empty/v1/keys/private")) { + return MockResponse().setResponseCode(200) + .setBody(gson.toJson(EkmPrivateKeysResponse(privateKeys = emptyList()))) + } + + if (request.path.equals("/ekm/v1/keys/private")) { + return MockResponse().setResponseCode(200).setBody(gson.toJson(EKM_FES_RESPONSE)) + } + + if (request.path.equals("/ekm/not_fully_decrypted_key/v1/keys/private")) { + return MockResponse().setResponseCode(200) + .setBody( + gson.toJson( + EkmPrivateKeysResponse( + privateKeys = listOf( + Key( + TestGeneralUtil.readFileFromAssetsAsString( + "pgp/keys/user_with_not_fully_decrypted_prv_key@flowcrypt.test_prv_default.asc" + ) + ) + ) + ) + ) + ) + } + + return null + } + + private fun handleGetDomainRulesAPI(model: LoginModel, gson: Gson): MockResponse { + when (model.account) { + EMAIL_DOMAIN_ORG_RULES_ERROR -> return MockResponse().setResponseCode(200) + .setBody(gson.toJson(DOMAIN_ORG_RULES_ERROR_RESPONSE)) + + EMAIL_WITH_NO_PRV_CREATE_RULE -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = listOf( + OrgRules.DomainRule.NO_PRV_CREATE, + OrgRules.DomainRule.NO_PRV_BACKUP + ) + ) + ) + + EMAIL_MUST_AUTOGEN_PASS_PHRASE_QUIETLY_EXISTED -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = listOf( + OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, + OrgRules.DomainRule.PASS_PHRASE_QUIET_AUTOGEN + ), + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + + EMAIL_FORBID_STORING_PASS_PHRASE_MISSING -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = listOf( + OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN + ), + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + + EMAIL_MUST_SUBMIT_TO_ATTESTER_EXISTED -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = listOf( + OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, + OrgRules.DomainRule.FORBID_STORING_PASS_PHRASE, + OrgRules.DomainRule.ENFORCE_ATTESTER_SUBMIT + ), + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + + EMAIL_FORBID_CREATING_PRIVATE_KEY_MISSING -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = listOf( + OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, + OrgRules.DomainRule.FORBID_STORING_PASS_PHRASE + ), + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + + EMAIL_GET_KEYS_VIA_EKM_ERROR -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = ACCEPTED_ORG_RULES, + keyManagerUrl = EMAIL_EKM_URL_ERROR, + ) + ) + + EMAIL_GET_KEYS_VIA_EKM_EMPTY_LIST -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = ACCEPTED_ORG_RULES, + keyManagerUrl = EMAIL_EKM_URL_SUCCESS_EMPTY_LIST, + ) + ) + + EMAIL_GET_KEYS_VIA_EKM_NOT_FULLY_DECRYPTED -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = ACCEPTED_ORG_RULES, + keyManagerUrl = EMAIL_EKM_URL_SUCCESS_NOT_FULLY_DECRYPTED_KEY, + ) + ) + + EMAIL_FES_REQUEST_TIME_OUT, + EMAIL_FES_HTTP_404, + EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS, + EMAIL_FES_NOT_ENTERPRISE_SERVER -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = ACCEPTED_ORG_RULES, + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + + else -> return MockResponse().setResponseCode(404) + } + } + + private fun handleLoginAPI(model: LoginModel, gson: Gson): MockResponse { + when (model.account) { + EMAIL_LOGIN_ERROR -> return MockResponse().setResponseCode(200) + .setBody(gson.toJson(LOGIN_API_ERROR_RESPONSE)) + + EMAIL_LOGIN_NOT_VERIFIED -> return MockResponse().setResponseCode(200) + .setBody(gson.toJson(LoginResponse(null, isVerified = false))) + + EMAIL_DOMAIN_ORG_RULES_ERROR -> return MockResponse().setResponseCode(200) + .setBody(gson.toJson(LoginResponse(null, isVerified = true))) + + EMAIL_WITH_NO_PRV_CREATE_RULE -> return MockResponse().setResponseCode(200) + .setBody(gson.toJson(LoginResponse(null, isVerified = true))) + + else -> return MockResponse().setResponseCode(200) + .setBody(gson.toJson(LoginResponse(null, isVerified = true))) + } + } + + private fun successMockResponseForOrgRules(gson: Gson, orgRules: OrgRules) = + MockResponse().setResponseCode(200) + .setBody( + gson.toJson( + DomainOrgRulesResponse( + orgRules = orgRules + ) + ) + ) + companion object { private const val EMAIL_EKM_URL_SUCCESS = "https://localhost:1212/ekm/" private const val EMAIL_EKM_URL_SUCCESS_EMPTY_LIST = "https://localhost:1212/ekm/empty/" @@ -236,6 +545,15 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { private const val EMAIL_GET_KEYS_VIA_EKM_EMPTY_LIST = "keys_via_ekm_empty_list@flowcrypt.test" private const val EMAIL_GET_KEYS_VIA_EKM_NOT_FULLY_DECRYPTED = "user_with_not_fully_decrypted_prv_key@flowcrypt.test" + private const val EMAIL_FES_NO_CONNECTION = "fes_request_timeout@flowcrypt.test" + private const val EMAIL_FES_REQUEST_TIME_OUT = "fes_request_timeout@localhost:1212" + private const val EMAIL_FES_HTTP_404 = "fes_404@localhost:1212" + private const val EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS = "fes_not404_not_success@localhost:1212" + private const val EMAIL_FES_NOT_ENTERPRISE_SERVER = "fes_not_enterprise_server@localhost:1212" + private const val EMAIL_FES_CLIENT_CONFIGURATION_SUCCESS = + "fes_client_configuration_success@localhost:1212" + private const val EMAIL_FES_CLIENT_CONFIGURATION_FAILED = + "fes_client_configuration_failed@localhost:1212" private val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, @@ -263,177 +581,22 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { message = EKM_ERROR ) - @get:ClassRule - @JvmStatic - val mockWebServerRule = - FlowCryptMockWebServerRule(TestConstants.MOCK_WEB_SERVER_PORT, object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse { - val gson = - ApiHelper.getInstance(InstrumentationRegistry.getInstrumentation().targetContext).gson - - if (request.path.equals("/ekm/error/v1/keys/private")) { - return MockResponse().setResponseCode(200) - .setBody(gson.toJson(EKM_ERROR_RESPONSE)) - } - - if (request.path.equals("/ekm/empty/v1/keys/private")) { - return MockResponse().setResponseCode(200) - .setBody( - gson.toJson( - EkmPrivateKeysResponse( - privateKeys = emptyList() - ) - ) - ) - } - - if (request.path.equals("/ekm/not_fully_decrypted_key/v1/keys/private")) { - return MockResponse().setResponseCode(200) - .setBody( - gson.toJson( - EkmPrivateKeysResponse( - privateKeys = listOf( - Key( - TestGeneralUtil.readFileFromAssetsAsString( - "pgp/keys/user_with_not_fully_decrypted_prv_key@flowcrypt.test_prv_default.asc" - ) - ) - ) - ) - ) - ) - } - - val model = - gson.fromJson(InputStreamReader(request.body.inputStream()), LoginModel::class.java) - - if (request.path.equals("/account/login")) { - return handleLoginAPI(model, gson) - } - - if (request.path.equals("/account/get")) { - return handleGetDomainRulesAPI(model, gson) - } - - return MockResponse().setResponseCode(404) - } - }) - - private fun handleGetDomainRulesAPI(model: LoginModel, gson: Gson): MockResponse { - when (model.account) { - EMAIL_DOMAIN_ORG_RULES_ERROR -> return MockResponse().setResponseCode(200) - .setBody(gson.toJson(DOMAIN_ORG_RULES_ERROR_RESPONSE)) - - EMAIL_WITH_NO_PRV_CREATE_RULE -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = listOf( - OrgRules.DomainRule.NO_PRV_CREATE, - OrgRules.DomainRule.NO_PRV_BACKUP - ) - ) - ) - - EMAIL_MUST_AUTOGEN_PASS_PHRASE_QUIETLY_EXISTED - -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = listOf( - OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, - OrgRules.DomainRule.PASS_PHRASE_QUIET_AUTOGEN - ), - keyManagerUrl = EMAIL_EKM_URL_SUCCESS, - ) - ) - - EMAIL_FORBID_STORING_PASS_PHRASE_MISSING -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = listOf( - OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN - ), - keyManagerUrl = EMAIL_EKM_URL_SUCCESS, - ) - ) - - EMAIL_MUST_SUBMIT_TO_ATTESTER_EXISTED -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = listOf( - OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, - OrgRules.DomainRule.FORBID_STORING_PASS_PHRASE, - OrgRules.DomainRule.ENFORCE_ATTESTER_SUBMIT - ), - keyManagerUrl = EMAIL_EKM_URL_SUCCESS, - ) - ) - - EMAIL_FORBID_CREATING_PRIVATE_KEY_MISSING -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = listOf( - OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, - OrgRules.DomainRule.FORBID_STORING_PASS_PHRASE - ), - keyManagerUrl = EMAIL_EKM_URL_SUCCESS, - ) - ) - - EMAIL_GET_KEYS_VIA_EKM_ERROR -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = ACCEPTED_ORG_RULES, - keyManagerUrl = EMAIL_EKM_URL_ERROR, - ) - ) - - EMAIL_GET_KEYS_VIA_EKM_EMPTY_LIST -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = ACCEPTED_ORG_RULES, - keyManagerUrl = EMAIL_EKM_URL_SUCCESS_EMPTY_LIST, - ) - ) - - EMAIL_GET_KEYS_VIA_EKM_NOT_FULLY_DECRYPTED -> return successMockResponseForOrgRules( - gson = gson, - orgRules = OrgRules( - flags = ACCEPTED_ORG_RULES, - keyManagerUrl = EMAIL_EKM_URL_SUCCESS_NOT_FULLY_DECRYPTED_KEY, - ) + private val EKM_FES_RESPONSE = EkmPrivateKeysResponse( + privateKeys = listOf( + Key( + TestGeneralUtil.readFileFromAssetsAsString("pgp/fes@flowcrypt.test_prv_decrypted.asc") ) + ) + ) - else -> return MockResponse().setResponseCode(404) - } - } - - private fun handleLoginAPI(model: LoginModel, gson: Gson): MockResponse { - when (model.account) { - EMAIL_LOGIN_ERROR -> return MockResponse().setResponseCode(200) - .setBody(gson.toJson(LOGIN_API_ERROR_RESPONSE)) - - EMAIL_LOGIN_NOT_VERIFIED -> return MockResponse().setResponseCode(200) - .setBody(gson.toJson(LoginResponse(null, isVerified = false))) - - EMAIL_DOMAIN_ORG_RULES_ERROR -> return MockResponse().setResponseCode(200) - .setBody(gson.toJson(LoginResponse(null, isVerified = true))) - - EMAIL_WITH_NO_PRV_CREATE_RULE -> return MockResponse().setResponseCode(200) - .setBody(gson.toJson(LoginResponse(null, isVerified = true))) - - else -> return MockResponse().setResponseCode(200) - .setBody(gson.toJson(LoginResponse(null, isVerified = true))) - } - } - - private fun successMockResponseForOrgRules(gson: Gson, orgRules: OrgRules) = - MockResponse().setResponseCode(200) - .setBody( - gson.toJson( - DomainOrgRulesResponse( - orgRules = orgRules - ) - ) - ) + private val FES_SUCCESS_RESPONSE = FesServerResponse( + apiError = null, + vendor = "FlowCrypt", + service = "enterprise-server", + orgId = "localhost", + version = "2021", + endUserApiVersion = "v1", + adminApiVersion = "v1" + ) } } diff --git a/FlowCrypt/src/androidTest/resources/ssl/localhost-cert.pem b/FlowCrypt/src/androidTest/resources/ssl/localhost-cert.pem index 5112a78739..350f860a9b 100644 --- a/FlowCrypt/src/androidTest/resources/ssl/localhost-cert.pem +++ b/FlowCrypt/src/androidTest/resources/ssl/localhost-cert.pem @@ -6,31 +6,31 @@ Certificate: Signature Algorithm: sha256WithRSAEncryption Issuer: C=CZ, ST=Debug, O=FlowCrypt, OU=CA, CN=flowcrypt.test/emailAddress=admin@flowcrypt.test Validity - Not Before: May 3 07:10:21 2021 GMT - Not After : May 1 07:10:21 2031 GMT + Not Before: Jul 28 08:38:17 2021 GMT + Not After : Jul 26 08:38:17 2031 GMT Subject: C=CZ, ST=Debug, O=FlowCrypt, CN=localhost/emailAddress=admin@flowcrypt.test Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: - 00:c7:ff:8e:35:c3:61:38:a6:3d:df:f2:4e:ee:b3: - db:b4:43:77:5f:09:68:cb:be:b6:82:7a:17:4b:f8: - ba:26:fa:39:27:d3:e4:e9:75:ff:34:ce:f4:06:3f: - 5f:8a:7d:3f:63:ce:da:c8:0f:43:84:de:45:d0:9c: - 61:83:2d:9e:9e:8e:04:8b:e3:d9:d8:39:3f:3d:25: - 5c:c4:b8:0f:9e:eb:51:c7:2f:5a:16:67:c7:7c:06: - b4:d2:73:35:f6:1a:ef:38:14:45:96:bf:3b:2b:ca: - 8e:fa:c3:82:f3:4c:a4:56:9b:b7:50:90:d3:89:b2: - 4a:e8:7d:a3:06:2a:44:31:b2:f0:6a:3f:d2:ab:f0: - 1c:47:b2:cd:be:a8:e4:33:ea:2e:5a:e5:6c:63:53: - c6:11:91:21:95:35:33:1c:fb:b0:68:5e:7a:6a:1f: - 9b:b6:93:e7:76:c6:13:c3:93:2c:85:0d:84:66:2d: - b1:f8:4f:f9:e3:f2:de:bc:9a:85:61:c5:c7:39:6f: - 2a:46:d5:de:3b:af:46:e0:08:94:fb:7a:de:4c:92: - fe:41:29:50:de:6b:32:ea:5b:b0:90:59:95:84:29: - 01:b0:77:4c:5a:48:48:a3:33:cf:0a:5d:1a:cd:86: - c3:01:17:c7:d3:98:6c:5e:56:ef:e4:74:a0:27:3f: - 8e:c3 + 00:ad:b8:c9:5c:59:87:7a:08:56:9d:24:53:f2:c4: + 6b:3d:ce:9c:57:23:e7:ef:bd:f8:ce:d6:bb:7a:45: + 8d:e6:86:7c:c5:b4:08:2e:c5:73:38:bf:bf:3e:ab: + 5d:7b:09:c8:a5:40:cb:58:d6:41:7f:06:8f:94:53: + 06:61:bb:a0:28:06:77:c5:ce:25:7c:98:fe:f9:68: + b5:0c:d2:70:79:5c:f5:7b:43:99:d8:51:ed:7e:b3: + 8d:ed:32:61:27:d8:8b:56:ee:42:a6:a8:cb:f5:11: + 70:7e:6e:b9:e7:88:c0:2f:e6:0e:48:4f:fb:81:a5: + 4a:c7:7e:c7:ab:28:fd:14:c4:39:da:68:cb:fe:99: + 9b:61:0c:e4:ad:b8:5b:95:93:bd:ca:ea:38:d0:5b: + 0d:ed:96:57:db:b3:69:d4:93:7b:47:be:9c:5b:dd: + 6e:6c:6d:87:89:ad:90:6f:b9:3c:96:80:ac:09:52: + 54:bd:f0:49:8b:c8:66:1c:28:ed:71:64:c0:e5:60: + 24:39:1e:70:28:77:78:40:48:a4:a6:99:7c:98:ad: + 40:1a:76:79:51:24:43:2e:fe:19:72:43:6a:36:e4: + 59:13:48:93:a3:51:bc:f8:85:98:d5:42:47:4b:6a: + bb:c0:cf:a7:7f:c8:e3:5c:c0:85:d9:a3:26:1d:e8: + e2:a9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: @@ -38,51 +38,52 @@ Certificate: Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: - 8F:2C:7C:57:2E:1E:03:65:EF:86:7E:AE:7F:BB:19:0E:0C:92:99:24 + B9:11:F8:F7:26:1D:64:9F:0D:2B:8F:7F:1E:69:F0:B8:9B:34:3F:94 X509v3 Authority Key Identifier: keyid:57:1B:70:94:B3:E4:B5:29:FA:51:9C:3F:77:5A:8F:15:F8:82:1A:B6 X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment X509v3 Subject Alternative Name: - DNS:localhost + DNS:localhost, DNS:*.localhost Signature Algorithm: sha256WithRSAEncryption - c5:13:26:a7:b8:7a:a5:d0:fc:08:7e:9d:9a:65:d1:95:b3:1a: - ae:85:89:bf:e2:74:65:82:62:8d:fe:cc:80:89:56:66:82:29: - bc:21:2f:ee:be:44:1b:9d:19:5d:65:20:42:ea:17:61:c9:45: - 9b:ee:84:54:22:89:07:64:40:0f:4b:ea:29:30:88:0c:6e:44: - 7e:5d:27:3e:03:15:a6:e4:fe:07:6d:58:27:f0:30:da:2c:ef: - 64:6a:53:d2:e1:42:87:cf:60:03:b1:e6:4f:06:0b:b8:60:c6: - 94:2b:db:fb:91:fa:89:86:d5:b7:06:4d:48:5c:d8:15:7f:1f: - d4:85:51:ef:62:68:c7:7d:3e:b7:c6:08:28:59:ca:62:02:39: - 1c:9f:ae:06:52:0f:be:52:70:40:13:41:ed:19:e2:0f:fc:c2: - 50:15:77:b7:5c:0d:5d:eb:9c:3b:60:f8:fe:a4:cd:7e:8f:63: - dc:33:5f:02:03:4b:dd:48:fb:36:88:ce:1f:80:0a:4f:a7:59: - 95:e8:e8:01:7a:d0:d7:56:77:8c:23:23:60:48:5a:fe:c2:72: - a8:ab:53:0e:56:d0:d1:70:2d:d3:27:e0:99:87:44:11:40:fe: - c7:37:f4:3c:04:22:e6:7d:09:a4:12:9e:3a:a0:8c:bd:fa:6a: - 3c:10:d4:8e + 17:2b:64:78:9f:67:d7:b4:6e:49:7d:57:4b:b4:2a:64:f4:c3: + 07:8b:da:c7:15:a1:ea:cf:7a:fa:06:df:99:17:06:30:d0:cf: + a0:c5:b2:b0:9d:e3:95:34:2d:1d:79:79:b7:18:b2:cb:b8:34: + 91:8a:19:3d:7e:90:08:06:42:02:8b:9b:56:b7:79:ef:50:7f: + b7:a5:0a:b9:94:a0:51:82:d3:a9:5d:ec:79:2e:52:59:45:9f: + a4:1f:f1:1d:d2:4f:2d:25:67:b3:45:ee:5c:95:40:b2:53:a5: + 34:0b:c6:24:8d:ed:02:b2:92:b5:db:14:24:84:a1:6a:a2:d1: + 53:52:67:a5:8f:32:a8:17:1c:7e:e7:0b:95:7d:c6:ce:4c:03: + 12:52:7f:f4:89:72:7c:05:12:f5:33:7f:98:3a:ce:9d:5a:2d: + 29:99:56:4f:15:cc:26:87:75:ba:fa:11:c1:43:0a:1c:ef:4b: + 99:6d:bd:ba:99:27:94:f5:2a:01:e5:cc:4e:2d:dc:e8:69:c0: + 86:59:06:72:f4:78:a8:f0:cb:41:33:b4:49:ad:46:5a:e5:1f: + f2:d3:40:3f:66:99:3e:e4:27:7a:92:9c:2d:3b:ed:55:31:cd: + 2b:bc:b2:cf:bb:14:13:f3:de:d5:7b:16:77:79:38:24:ab:45: + 5a:cc:79:92 -----BEGIN CERTIFICATE----- -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 +RVrMeZI= -----END CERTIFICATE----- diff --git a/FlowCrypt/src/androidTest/resources/ssl/localhost-key.pem b/FlowCrypt/src/androidTest/resources/ssl/localhost-key.pem index 0b2d5f5ca7..53a87b1829 100644 --- a/FlowCrypt/src/androidTest/resources/ssl/localhost-key.pem +++ b/FlowCrypt/src/androidTest/resources/ssl/localhost-key.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH/441w2E4pj3f -8k7us9u0Q3dfCWjLvraCehdL+Lom+jkn0+Tpdf80zvQGP1+KfT9jztrID0OE3kXQ -nGGDLZ6ejgSL49nYOT89JVzEuA+e61HHL1oWZ8d8BrTSczX2Gu84FEWWvzsryo76 -w4LzTKRWm7dQkNOJskrofaMGKkQxsvBqP9Kr8BxHss2+qOQz6i5a5WxjU8YRkSGV -NTMc+7BoXnpqH5u2k+d2xhPDkyyFDYRmLbH4T/nj8t68moVhxcc5bypG1d47r0bg -CJT7et5Mkv5BKVDeazLqW7CQWZWEKQGwd0xaSEijM88KXRrNhsMBF8fTmGxeVu/k -dKAnP47DAgMBAAECggEAOOFewb2qleD58gpJFqyCjNcvSRE1A9VetOciXxIDZ3h8 -7u3dyeQsTAmb73bwpkOO1sULBiEJM+2/b2A6f4Nm/HMpqduIpm1JpgtNQgmTL8B/ -hqXZ+ktTudtya/QmYbMxXAKv8/q5SnAWkA0w3h9rEdLBpSE7eIsDsPGifutgb72S -VAfYARwKlczEojGWxk4jGRsbr78TCdTs7dysOsC5zWLIRbc+kCArjfoWS8Mr/qa4 -UaaCRXzKnwNbO71tu+jHg8BjajE2eiX4Vi9VyxWqyOL9wXMCh7h7guACb3M48Q5G -EAMtjqnWoEfGwahLUfz08f2LlODmdSZAUulX+w7L6QKBgQD3vQpgYj7Kv6xU2VdG -PkYTkRrZQHwdWX7CJQZnPqeYIivtpFgRCJuku3BxmkvfeEKL78Mz8Gciir2nnD+8 -OClp5l1IuoVUG6lQ2sI8mwAj6Jb3Ps1xrx3CKY/hnggFsxKXNwcnfhgGi2DDZvsR -9vpEzVcZjgJ9stP4FmmnN+K5hQKBgQDOqvQ01vdwImJS1m1ql+rDit69dFxduqsJ -YT62q4nUsr9/3nCVeExnSpd8wAmKJOMoXSkMPUuT+5RBdBlxjvAc0b7r6Oeqlr1I -zBMS+E8++imB+CvF/EOvh3zwlxIBlzh2ZGEqV6hSi6gm06xaXPA/pD8So4YlhoO0 -Aiga5TA1pwKBgAzh97grY7iHXx7Kbo9ojZxW316My+d6z+yooDyeNXjjzgT9ltbL -68wVMzO1IlzslAlS3oPE7JgPPu2IYogXI5AuOoudp3FIPvXFanRcWxWC8tbInUZc -JOAD6UeCQEiLl1vlsKmNFRLCDLYclNHKVhld1Dmv+NEwi3VLNRNNBK1BAoGANUl7 -AgzTbARRmc5UFAHrtFOgLvVLw5cX9qkuRGdieIdGPTAbk06bVJ61BKN3UjlEoOm0 -ZcWAMT3S8jV/Qfp7CtNCCQ3afe+0Cosj9+YyeKiD/1D/6GKtCRtEKbyqFeLp2gzz -yLklW3NK/gfiDsKb56zv3lxvgEl4t/c+ZADoN00CgYEAoDTO3KFjvMFDxbiHi1OY -NbFP6rN28eaO+zbfrEmN1Yx5UwDVUWyWlOYOHi6oxaaQ+/lkB/KGjO+AzTUy8MC/ -IJFY61TDPKi/c5yAqNVZZVVr327pVu2BvB5myyBAAYn/qgd54HmaAE6pIfl9uo3W -eVhCl89ZtDeNZuQi8tgBdYs= +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCtuMlcWYd6CFad +JFPyxGs9zpxXI+fvvfjO1rt6RY3mhnzFtAguxXM4v78+q117CcilQMtY1kF/Bo+U +UwZhu6AoBnfFziV8mP75aLUM0nB5XPV7Q5nYUe1+s43tMmEn2ItW7kKmqMv1EXB+ +brnniMAv5g5IT/uBpUrHfserKP0UxDnaaMv+mZthDOStuFuVk73K6jjQWw3tllfb +s2nUk3tHvpxb3W5sbYeJrZBvuTyWgKwJUlS98EmLyGYcKO1xZMDlYCQ5HnAod3hA +SKSmmXyYrUAadnlRJEMu/hlyQ2o25FkTSJOjUbz4hZjVQkdLarvAz6d/yONcwIXZ +oyYd6OKpAgMBAAECggEAOiOGmDaKRYf23L5krfGYZmX4Ip2sqKpuU2K/+Yq/k6/c +oBnBR03jYtGhQmr9bQj4Mn39z+xcnRNoB97FWv38+og3m2aDWzCk3HlzZigjZip7 +KVaVbTT2A5o53HEo3Ln24A/7FGWXZJwChv2tj9RPZXhbvU2vQVM9NIi0cA7WrBT2 +gvGF3B/kp2yN80BPHiQtI+p+ETfuKzqrXYag/cZhqK5HaSbuX5sfrFG+us9CexXw +OM/4H2onbqhsrlyDLx+hHv1oPweBbp2J7aS162mmthD8ZxLF6h4HOs1TZ9Zdn5H/ +pDDIQRJv1jnKjk4kH/Zu9hL4RQWFA54ccF0LXttoRQKBgQDZeeTUHI5erAKqujZw +Zzhsvt+cD9lPTX8rldQY50jwkpaeAMBjyC0EYP0yBLRHX5C69ppEqaqIz+an0vru +zPLdDTRmLMfSCngc2gNSfLoKaYnKentYMP8uefaMSqraLKhF8J3qMqEDD47cnzdK +Z84yYjxBHZ9LOljB8qYtKfPgJwKBgQDMfrjVKcVKOzHHMfv0sznugAGUFUepIrB8 ++17fVMgaaVYSveCNNYDMRz26DDhhg6PiSagMuZEbxunW83t0bYfdiMu0AVino9nu +N+QNZyTJkzSQpQtmckHlEHvt+KR8aJhEyRdqYOncfHRT1vqPebSpz7ldWXgoUN6n +l6YIEbkYrwKBgEZ0px3Q4o7pSzPkgB6KUk1oySjWOaDninukXsJyq77EvucRd0Lc +Zzkz8tNxkrZMz6bCndgA367TK/bS81jKLSRQUtmHSHVTzvYJ62md3ufgqTNf2vYH +aaS0/psU1aU5Mor2GTL2lBNZxv7S1ibU2oiYKs0tu52zmW28HvoizU+hAoGAKqeM +Gjp6Qn+SZIBSk7oGrW7Z5W+C4Q6xTYCJS4A9tPSf1Yzxl8K/ONzrTTFbWUvCFWNT +wEO8ttSI1iM3bxOhsV3lT4iwW1dcD8pssTcAf2NLJZinhhjzEbqcfwjMFn8is8ZW +MgRBA5KaGRrQbLjwLDN98LDG1XH433BMW4aG21UCgYAf+Qy8tZZkkx81f/jEyTyV +IgoAKF8Pg9kTcRSOdT7fBZzf40qg+1p6Ks59hjJd9V+oVzU/mT4gGL6J1iGuIIxN +OOW0N2xljKlUj/tgnD1wKEP4doOo/IQj65876/ZDJ+n4+W9K1WpKRImEcMDotZF2 +fYd7Vy6mmHYfV0cJiVzo+Q== -----END PRIVATE KEY----- diff --git a/FlowCrypt/src/androidTest/resources/ssl/localhost-req.pem b/FlowCrypt/src/androidTest/resources/ssl/localhost-req.pem index 395bb1ef63..4a1ead6820 100644 --- a/FlowCrypt/src/androidTest/resources/ssl/localhost-req.pem +++ b/FlowCrypt/src/androidTest/resources/ssl/localhost-req.pem @@ -1,18 +1,18 @@ -----BEGIN CERTIFICATE REQUEST----- -MIIC7jCCAdYCAQAwajELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBURlYnVnMRIwEAYD +MIIC+zCCAeMCAQAwajELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBURlYnVnMRIwEAYD VQQKDAlGbG93Q3J5cHQxEjAQBgNVBAMMCWxvY2FsaG9zdDEjMCEGCSqGSIb3DQEJ ARYUYWRtaW5AZmxvd2NyeXB0LnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDH/441w2E4pj3f8k7us9u0Q3dfCWjLvraCehdL+Lom+jkn0+Tpdf80 -zvQGP1+KfT9jztrID0OE3kXQnGGDLZ6ejgSL49nYOT89JVzEuA+e61HHL1oWZ8d8 -BrTSczX2Gu84FEWWvzsryo76w4LzTKRWm7dQkNOJskrofaMGKkQxsvBqP9Kr8BxH -ss2+qOQz6i5a5WxjU8YRkSGVNTMc+7BoXnpqH5u2k+d2xhPDkyyFDYRmLbH4T/nj -8t68moVhxcc5bypG1d47r0bgCJT7et5Mkv5BKVDeazLqW7CQWZWEKQGwd0xaSEij -M88KXRrNhsMBF8fTmGxeVu/kdKAnP47DAgMBAAGgPzA9BgkqhkiG9w0BCQ4xMDAu -MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAN -BgkqhkiG9w0BAQsFAAOCAQEAcjLR2c/0nlRM2agNuStWMPBAp6m12K9lw2phK24e -C7GWt5H6fkUgbh7twZDRtlLpKy9IVupHgrTBtjy9zIEoU2RQzxT8qRQ31J9IZSCG -v2JsOqXmzgSjtvxi/Sk0iqTUzHlEtacdS6b0nvCUVqdkMMf89SwVS+p29PDTSguX -4O3wj5orZzT/iDYE3jVFpmqjEka3uaIDn2L/81lSkO/GPeKYuiIOFJtr6vfmbVMd -097qlNKBynA9v/JvaHR8nvYcgWl7daYe+AaIo6lp7Qj8NDXZdtxP33sqeeUuX4Ng -KMTNpeosaqS8X2qdr623ccla35EwagCwh/aSJIf3QIqk8g== +ggEKAoIBAQCtuMlcWYd6CFadJFPyxGs9zpxXI+fvvfjO1rt6RY3mhnzFtAguxXM4 +v78+q117CcilQMtY1kF/Bo+UUwZhu6AoBnfFziV8mP75aLUM0nB5XPV7Q5nYUe1+ +s43tMmEn2ItW7kKmqMv1EXB+brnniMAv5g5IT/uBpUrHfserKP0UxDnaaMv+mZth +DOStuFuVk73K6jjQWw3tllfbs2nUk3tHvpxb3W5sbYeJrZBvuTyWgKwJUlS98EmL +yGYcKO1xZMDlYCQ5HnAod3hASKSmmXyYrUAadnlRJEMu/hlyQ2o25FkTSJOjUbz4 +hZjVQkdLarvAz6d/yONcwIXZoyYd6OKpAgMBAAGgTDBKBgkqhkiG9w0BCQ4xPTA7 +MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMCEGA1UdEQQaMBiCCWxvY2FsaG9zdIIL +Ki5sb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAKpJqZCtZ8nkvh47oR95zz+T +lLKZycbCqU6++jBT1hs+9uqHInITiN01U4bmgYgUtF1AxYAp5oybCqOeRqIhhrcM +3+QQeRxZ26aKl5RXhOEYsgJLC24Vr3JiLae+gCNtejYY4NG71rhqvC5dDnukb3D8 +1PIMtfXNUaJOZrdyDY18XKTANiP14gAx7PvtOdHVDDbHwWAPV/npG31U3vf/RwVN +mRjGu9GEKtPhB93z7E3N8f7wOPv5f2i5Qc0UginGDjXn+4jf7Q1ZykXb3ZD2RL/T +aLX1g7F2QEPlmrFMbQbEnHYqvk7AFXSA4Lz8tSDT07AoUGtkFMkEgkCWqcZIFks= -----END CERTIFICATE REQUEST----- diff --git a/FlowCrypt/src/androidTest/resources/ssl/openssl-ca.cnf b/FlowCrypt/src/androidTest/resources/ssl/openssl-ca.cnf index 9ad79fd693..5248d84c42 100644 --- a/FlowCrypt/src/androidTest/resources/ssl/openssl-ca.cnf +++ b/FlowCrypt/src/androidTest/resources/ssl/openssl-ca.cnf @@ -1,36 +1,26 @@ # - # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. # # Note that you can include other files from the main configuration # file using the .include directive. -#. -include filename +#.include filename # This definition stops the following lines choking if HOME isn't # defined. -HOME = -. +HOME = . # Extra OBJECT IDENTIFIER info: -#oid_file = $ENV::HOME/.oid -oid_section = new_oids +#oid_file = $ENV::HOME/.oid +oid_section = new_oids # To use this configuration file with the "-extfile" option of the -# "openssl x509" utility, -name here -the section -containing the +# "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: -# extensions = -# (Alternatively, -use a -configuration file -that has -only -# X.509v3 extensions in its main[= default] section.) +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) [ new_oids ] @@ -38,7 +28,7 @@ only # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: -# testoid2=${ testoid1 }.5.6 +# testoid2=${testoid1}.5.6 # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 @@ -47,139 +37,79 @@ tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] -default_ca = CA_default -# The default -ca section +default_ca = CA_default # The default ca section #################################################################### [ CA_default ] -dir = -./demoCA # -Where everything -is kept -certs = $dir / certs -# -Where the -issued certs -are kept -crl_dir = $dir / crl -# -Where the -issued crl -are kept -database = $dir / index.txt -# -database index -file. -#unique_subject = no # Set to 'no' to allow creation of +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of # several certs with same subject. -new_certs_dir = $dir / newcerts -# default place for new certs. +new_certs_dir = $dir/newcerts # default place for new certs. -certificate = $dir / cacert.pem -# -The CA -certificate - serial = $dir / serial -# -The current -serial number -crlnumber = $dir / crlnumber -# -the current -crl number +certificate = $dir/cacert.pem # The CA certificate + serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL -crl = $dir / crl.pem -# -The current -CRL - private_key = $dir / -private/cakey.pem# The private -key +crl = $dir/crl.pem # The current CRL + private_key = $dir/private/cakey.pem# The private key - x509_extensions = usr_cert -# -The extensions -to add -to the -cert + x509_extensions = usr_cert # The extensions to add to the cert # Comment out the following two lines for the "traditional" -# (and -highly broken -) format. -name_opt = ca_default -# -Subject Name -options - cert_opt = ca_default -# -Certificate field -options +# (and highly broken) format. +name_opt = ca_default # Subject Name options + cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. - copy_extensions = copy +copy_extensions = copy # denbond7: it was modified by me # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. -# crl_extensions = crl_ext +# crl_extensions = crl_ext -default_days = 365 -# -how long to -certify for -default_crl_days = 30 -# -how long before -next CRL -default_md = -default # use public key default -MD - preserve = no -# -keep passed -DN ordering + default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD + preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) -policy = policy_match +policy = policy_match # For the CA policy -[policy_match] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. -[policy_anything] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional #################################################################### [ req ] -default_bits = 2048 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca -# -The extensions -to add -to the -self signed cert +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extensions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret @@ -187,7 +117,7 @@ self signed cert # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) # utf8only: only UTF8Strings (PKIX recommendation after 2004). # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. @@ -196,68 +126,41 @@ string_mask = utf8only # req_extensions = v3_req # The extensions to add to a certificate request -[req_distinguished_name] -countryName = Country -Name (2 -letter code -) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province -Name (full -name) -stateOrProvinceName_default = Some - State - -localityName = Locality -Name (eg, city -) - -0.organizationName = -Organization Name(eg, company) -0.organizationName_default = -Internet Widgits -Pty Ltd +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd # we can do this but it is not needed normally :-) -#1.organizationName = -Second Organization -Name (eg, company -) -#1.organizationName_default = -World Wide -Web Pty -Ltd - - organizationalUnitName = Organizational -Unit Name(eg, section) -#organizationalUnitName_default = - -commonName = Common -Name (e -.g. -server FQDN -or -YOUR name -) -commonName_max = 64 - -emailAddress = Email -Address - emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[req_attributes] -challengePassword = A -challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An -optional company -name +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + + organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address + emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name [ usr_cert ] @@ -266,15 +169,13 @@ name # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. -basicConstraints = CA -: -FALSE +basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. -# nsCertType = server +# nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign @@ -289,11 +190,11 @@ FALSE # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. - nsComment = "OpenSSL Generated Certificate" + nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid, issuer +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. @@ -305,7 +206,7 @@ authorityKeyIdentifier = keyid, issuer # Copy subject details # issuerAltName=issuer:copy -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl @@ -313,18 +214,16 @@ authorityKeyIdentifier = keyid, issuer #nsSslServerName # This is required for TSA certificates. -# extendedKeyUsage = critical, timeStamping +# extendedKeyUsage = critical,timeStamping -[v3_req] +[ v3_req ] # Extensions to add to a certificate request -basicConstraints = CA -: -FALSE +basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment -[v3_ca] +[ v3_ca ] # Extensions for a typical CA @@ -332,14 +231,11 @@ FALSE # PKIX recommendation. -subjectKeyIdentifier = hash +subjectKeyIdentifier=hash -authorityKeyIdentifier = keyid -:always, -issuer +authorityKeyIdentifier=keyid:always,issuer - basicConstraints = critical, CA -:true + basicConstraints = critical,CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best @@ -366,8 +262,7 @@ issuer # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy -authorityKeyIdentifier = keyid -:always +authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate @@ -375,15 +270,13 @@ authorityKeyIdentifier = keyid # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. -basicConstraints = CA -: -FALSE +basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. -# nsCertType = server +# nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign @@ -398,11 +291,11 @@ FALSE # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. - nsComment = "OpenSSL Generated Certificate" + nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid, issuer +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. @@ -414,7 +307,7 @@ authorityKeyIdentifier = keyid, issuer # Copy subject details # issuerAltName=issuer:copy -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl @@ -422,99 +315,36 @@ authorityKeyIdentifier = keyid, issuer #nsSslServerName # This really needs to be in place for it to be a proxy certificate. -proxyCertInfo = critical, language -:id-ppl-anyLanguage,pathlen:3,policy:foo +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo #################################################################### [ tsa ] -default_tsa = tsa_config1 -# the default -TSA section +default_tsa = tsa_config1 # the default TSA section -[tsa_config1] +[ tsa_config1 ] # These are used by the TSA reply generation only. -dir = -./demoCA # -TSA root -directory - serial = $dir / tsaserial -# -The current - -serial number(mandatory) - -crypto_device = builtin -# -OpenSSL engine -to use -for -signing - signer_cert = $dir / tsacert.pem -# -The TSA -signing certificate +dir = ./demoCA # TSA root directory + serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing + signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) -certs = $dir / cacert.pem -# -Certificate chain -to include -in reply +certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) -signer_key = $dir / -private/tsakey.pem # -The TSA -private -key (optional) -signer_digest = sha256 -# -Signing digest -to use -. (Optional) -default_policy = tsa_policy1 -# Policy if -request did -not -specify it +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) +signer_digest = sha256 # Signing digest to use. (Optional) +default_policy = tsa_policy1 # Policy if request did not specify it # (optional) -other_policies = tsa_policy2, tsa_policy3 -# -acceptable policies(optional) -digests = sha1, sha256, sha384, sha512 -# -Acceptable message -digests (mandatory) -accuracy = secs -:1, millisecs:500, microsecs:100 # (optional) -clock_precision_digits = 0 -# -number of -digits after -dot. (optional) -ordering = yes -# -Is ordering -defined for timestamps? +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? # (optional, default: no) -tsa_name = yes -# -Must the -TSA name -be included -in the -reply? +tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) -ess_cert_id_chain = no -# -Must the -ESS cert -id chain -be included -? +ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no) -ess_cert_id_alg = sha1 -# -algorithm to -compute certificate +ess_cert_id_alg = sha1 # algorithm to compute certificate # identifier (optional, default: sha1) diff --git a/FlowCrypt/src/androidTest/resources/ssl/openssl-req.cnf b/FlowCrypt/src/androidTest/resources/ssl/openssl-req.cnf index 2fcbca9afc..5103e5dfc8 100644 --- a/FlowCrypt/src/androidTest/resources/ssl/openssl-req.cnf +++ b/FlowCrypt/src/androidTest/resources/ssl/openssl-req.cnf @@ -23,7 +23,11 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment ############################################### # Edit this line to set subjectAltName contents ############################################### -subjectAltName = DNS:localhost +subjectAltName = @alt_names + +[ alt_names ] +DNS.1 = localhost +DNS.2 = *.localhost [ CA_default ] default_days = 3650 diff --git a/FlowCrypt/src/androidTest/resources/ssl/server_combined.pem b/FlowCrypt/src/androidTest/resources/ssl/server_combined.pem index da3d3154b8..65684b80ad 100644 --- a/FlowCrypt/src/androidTest/resources/ssl/server_combined.pem +++ b/FlowCrypt/src/androidTest/resources/ssl/server_combined.pem @@ -1,52 +1,53 @@ -----BEGIN CERTIFICATE----- -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 +RVrMeZI= -----END CERTIFICATE----- -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDH/441w2E4pj3f -8k7us9u0Q3dfCWjLvraCehdL+Lom+jkn0+Tpdf80zvQGP1+KfT9jztrID0OE3kXQ -nGGDLZ6ejgSL49nYOT89JVzEuA+e61HHL1oWZ8d8BrTSczX2Gu84FEWWvzsryo76 -w4LzTKRWm7dQkNOJskrofaMGKkQxsvBqP9Kr8BxHss2+qOQz6i5a5WxjU8YRkSGV -NTMc+7BoXnpqH5u2k+d2xhPDkyyFDYRmLbH4T/nj8t68moVhxcc5bypG1d47r0bg -CJT7et5Mkv5BKVDeazLqW7CQWZWEKQGwd0xaSEijM88KXRrNhsMBF8fTmGxeVu/k -dKAnP47DAgMBAAECggEAOOFewb2qleD58gpJFqyCjNcvSRE1A9VetOciXxIDZ3h8 -7u3dyeQsTAmb73bwpkOO1sULBiEJM+2/b2A6f4Nm/HMpqduIpm1JpgtNQgmTL8B/ -hqXZ+ktTudtya/QmYbMxXAKv8/q5SnAWkA0w3h9rEdLBpSE7eIsDsPGifutgb72S -VAfYARwKlczEojGWxk4jGRsbr78TCdTs7dysOsC5zWLIRbc+kCArjfoWS8Mr/qa4 -UaaCRXzKnwNbO71tu+jHg8BjajE2eiX4Vi9VyxWqyOL9wXMCh7h7guACb3M48Q5G -EAMtjqnWoEfGwahLUfz08f2LlODmdSZAUulX+w7L6QKBgQD3vQpgYj7Kv6xU2VdG -PkYTkRrZQHwdWX7CJQZnPqeYIivtpFgRCJuku3BxmkvfeEKL78Mz8Gciir2nnD+8 -OClp5l1IuoVUG6lQ2sI8mwAj6Jb3Ps1xrx3CKY/hnggFsxKXNwcnfhgGi2DDZvsR -9vpEzVcZjgJ9stP4FmmnN+K5hQKBgQDOqvQ01vdwImJS1m1ql+rDit69dFxduqsJ -YT62q4nUsr9/3nCVeExnSpd8wAmKJOMoXSkMPUuT+5RBdBlxjvAc0b7r6Oeqlr1I -zBMS+E8++imB+CvF/EOvh3zwlxIBlzh2ZGEqV6hSi6gm06xaXPA/pD8So4YlhoO0 -Aiga5TA1pwKBgAzh97grY7iHXx7Kbo9ojZxW316My+d6z+yooDyeNXjjzgT9ltbL -68wVMzO1IlzslAlS3oPE7JgPPu2IYogXI5AuOoudp3FIPvXFanRcWxWC8tbInUZc -JOAD6UeCQEiLl1vlsKmNFRLCDLYclNHKVhld1Dmv+NEwi3VLNRNNBK1BAoGANUl7 -AgzTbARRmc5UFAHrtFOgLvVLw5cX9qkuRGdieIdGPTAbk06bVJ61BKN3UjlEoOm0 -ZcWAMT3S8jV/Qfp7CtNCCQ3afe+0Cosj9+YyeKiD/1D/6GKtCRtEKbyqFeLp2gzz -yLklW3NK/gfiDsKb56zv3lxvgEl4t/c+ZADoN00CgYEAoDTO3KFjvMFDxbiHi1OY -NbFP6rN28eaO+zbfrEmN1Yx5UwDVUWyWlOYOHi6oxaaQ+/lkB/KGjO+AzTUy8MC/ -IJFY61TDPKi/c5yAqNVZZVVr327pVu2BvB5myyBAAYn/qgd54HmaAE6pIfl9uo3W -eVhCl89ZtDeNZuQi8tgBdYs= +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCtuMlcWYd6CFad +JFPyxGs9zpxXI+fvvfjO1rt6RY3mhnzFtAguxXM4v78+q117CcilQMtY1kF/Bo+U +UwZhu6AoBnfFziV8mP75aLUM0nB5XPV7Q5nYUe1+s43tMmEn2ItW7kKmqMv1EXB+ +brnniMAv5g5IT/uBpUrHfserKP0UxDnaaMv+mZthDOStuFuVk73K6jjQWw3tllfb +s2nUk3tHvpxb3W5sbYeJrZBvuTyWgKwJUlS98EmLyGYcKO1xZMDlYCQ5HnAod3hA +SKSmmXyYrUAadnlRJEMu/hlyQ2o25FkTSJOjUbz4hZjVQkdLarvAz6d/yONcwIXZ +oyYd6OKpAgMBAAECggEAOiOGmDaKRYf23L5krfGYZmX4Ip2sqKpuU2K/+Yq/k6/c +oBnBR03jYtGhQmr9bQj4Mn39z+xcnRNoB97FWv38+og3m2aDWzCk3HlzZigjZip7 +KVaVbTT2A5o53HEo3Ln24A/7FGWXZJwChv2tj9RPZXhbvU2vQVM9NIi0cA7WrBT2 +gvGF3B/kp2yN80BPHiQtI+p+ETfuKzqrXYag/cZhqK5HaSbuX5sfrFG+us9CexXw +OM/4H2onbqhsrlyDLx+hHv1oPweBbp2J7aS162mmthD8ZxLF6h4HOs1TZ9Zdn5H/ +pDDIQRJv1jnKjk4kH/Zu9hL4RQWFA54ccF0LXttoRQKBgQDZeeTUHI5erAKqujZw +Zzhsvt+cD9lPTX8rldQY50jwkpaeAMBjyC0EYP0yBLRHX5C69ppEqaqIz+an0vru +zPLdDTRmLMfSCngc2gNSfLoKaYnKentYMP8uefaMSqraLKhF8J3qMqEDD47cnzdK +Z84yYjxBHZ9LOljB8qYtKfPgJwKBgQDMfrjVKcVKOzHHMfv0sznugAGUFUepIrB8 ++17fVMgaaVYSveCNNYDMRz26DDhhg6PiSagMuZEbxunW83t0bYfdiMu0AVino9nu +N+QNZyTJkzSQpQtmckHlEHvt+KR8aJhEyRdqYOncfHRT1vqPebSpz7ldWXgoUN6n +l6YIEbkYrwKBgEZ0px3Q4o7pSzPkgB6KUk1oySjWOaDninukXsJyq77EvucRd0Lc +Zzkz8tNxkrZMz6bCndgA367TK/bS81jKLSRQUtmHSHVTzvYJ62md3ufgqTNf2vYH +aaS0/psU1aU5Mor2GTL2lBNZxv7S1ibU2oiYKs0tu52zmW28HvoizU+hAoGAKqeM +Gjp6Qn+SZIBSk7oGrW7Z5W+C4Q6xTYCJS4A9tPSf1Yzxl8K/ONzrTTFbWUvCFWNT +wEO8ttSI1iM3bxOhsV3lT4iwW1dcD8pssTcAf2NLJZinhhjzEbqcfwjMFn8is8ZW +MgRBA5KaGRrQbLjwLDN98LDG1XH433BMW4aG21UCgYAf+Qy8tZZkkx81f/jEyTyV +IgoAKF8Pg9kTcRSOdT7fBZzf40qg+1p6Ks59hjJd9V+oVzU/mT4gGL6J1iGuIIxN +OOW0N2xljKlUj/tgnD1wKEP4doOo/IQj65876/ZDJ+n4+W9K1WpKRImEcMDotZF2 +fYd7Vy6mmHYfV0cJiVzo+Q== -----END PRIVATE KEY----- diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt index 140e1b5afd..33aa69bc69 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt @@ -137,5 +137,10 @@ class Constants { "set pass phrase", "set passphrase" ) + + /** + * Product Flavors. + */ + const val FLAVOR_NAME_ENTERPRISE = "enterprise" } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiHelper.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiHelper.kt index b6799ae2ca..84c0232f45 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiHelper.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiHelper.kt @@ -45,47 +45,7 @@ class ApiHelper private constructor(context: Context) { .writeTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS) okHttpClientBuilder.addInterceptor(ApiVersionInterceptor()) - - if (GeneralUtil.isDebugBuild()) { - val isHttpLogEnabled = - SharedPreferencesHelper.getBoolean( - PreferenceManager.getDefaultSharedPreferences(context), - Constants.PREF_KEY_IS_HTTP_LOG_ENABLED, BuildConfig.IS_HTTP_LOG_ENABLED - ) - - if (isHttpLogEnabled) { - val levelString = SharedPreferencesHelper.getString( - PreferenceManager - .getDefaultSharedPreferences(context), - Constants.PREF_KEY_HTTP_LOG_LEVEL, - BuildConfig.HTTP_LOG_LEVEL - ) - - val isWriteLogsEnabled = - SharedPreferencesHelper.getBoolean( - PreferenceManager.getDefaultSharedPreferences(context), - Constants.PREF_KEY_IS_WRITE_LOGS_TO_FILE_ENABLED, false - ) - - if (isWriteLogsEnabled) { - val loggingInFileInterceptor = LoggingInFileInterceptor(context, "API") - loggingInFileInterceptor.setLevel( - LoggingInFileInterceptor.Level.valueOf( - levelString - ?: LoggingInFileInterceptor.Level.NONE.name - ) - ) - okHttpClientBuilder.addInterceptor(loggingInFileInterceptor) - } - - val loggingInterceptor = HttpLoggingInterceptor() - loggingInterceptor.level = HttpLoggingInterceptor.Level.valueOf( - levelString - ?: HttpLoggingInterceptor.Level.NONE.name - ) - okHttpClientBuilder.addInterceptor(loggingInterceptor) - } - } + configureOkHttpClientForDebuggingIfAllowed(context, okHttpClientBuilder) okHttpClient = okHttpClientBuilder.build() gson = GsonBuilder() @@ -110,6 +70,51 @@ class ApiHelper private constructor(context: Context) { return ApiHelper(context) } + fun configureOkHttpClientForDebuggingIfAllowed( + context: Context, okHttpClientBuilder: OkHttpClient.Builder + ) { + if (GeneralUtil.isDebugBuild()) { + val isHttpLogEnabled = + SharedPreferencesHelper.getBoolean( + PreferenceManager.getDefaultSharedPreferences(context), + Constants.PREF_KEY_IS_HTTP_LOG_ENABLED, BuildConfig.IS_HTTP_LOG_ENABLED + ) + + if (isHttpLogEnabled) { + val levelString = SharedPreferencesHelper.getString( + PreferenceManager + .getDefaultSharedPreferences(context), + Constants.PREF_KEY_HTTP_LOG_LEVEL, + BuildConfig.HTTP_LOG_LEVEL + ) + + val isWriteLogsEnabled = + SharedPreferencesHelper.getBoolean( + PreferenceManager.getDefaultSharedPreferences(context), + Constants.PREF_KEY_IS_WRITE_LOGS_TO_FILE_ENABLED, false + ) + + if (isWriteLogsEnabled) { + val loggingInFileInterceptor = LoggingInFileInterceptor(context, "API") + loggingInFileInterceptor.setLevel( + LoggingInFileInterceptor.Level.valueOf( + levelString + ?: LoggingInFileInterceptor.Level.NONE.name + ) + ) + okHttpClientBuilder.addInterceptor(loggingInFileInterceptor) + } + + val loggingInterceptor = HttpLoggingInterceptor() + loggingInterceptor.level = HttpLoggingInterceptor.Level.valueOf( + levelString + ?: HttpLoggingInterceptor.Level.NONE.name + ) + okHttpClientBuilder.addInterceptor(loggingInterceptor) + } + } + } + inline fun parseAsError(context: Context, response: Response): T? { val retrofit = getInstance(context).retrofit try { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiRepository.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiRepository.kt index 326301f193..4fe369ff39 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiRepository.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiRepository.kt @@ -10,12 +10,13 @@ import com.flowcrypt.email.api.retrofit.base.BaseApiRepository import com.flowcrypt.email.api.retrofit.request.model.InitialLegacySubmitModel import com.flowcrypt.email.api.retrofit.request.model.LoginModel import com.flowcrypt.email.api.retrofit.request.model.TestWelcomeModel -import com.flowcrypt.email.api.retrofit.response.api.DomainOrgRulesResponse import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse +import com.flowcrypt.email.api.retrofit.response.api.FesServerResponse import com.flowcrypt.email.api.retrofit.response.api.LoginResponse import com.flowcrypt.email.api.retrofit.response.attester.InitialLegacySubmitResponse import com.flowcrypt.email.api.retrofit.response.attester.PubResponse import com.flowcrypt.email.api.retrofit.response.attester.TestWelcomeResponse +import com.flowcrypt.email.api.retrofit.response.base.ApiResponse import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.api.retrofit.response.model.OrgRules import com.flowcrypt.email.api.retrofit.response.oauth2.MicrosoftOAuth2TokenResponse @@ -43,12 +44,14 @@ interface ApiRepository : BaseApiRepository { /** * @param context Interface to global information about an application environment. + * @param fesUrl Url that will be used to fetch [OrgRules]. * @param loginModel An instance of [LoginModel]. */ suspend fun getDomainOrgRules( context: Context, - loginModel: LoginModel - ): Result + loginModel: LoginModel, + fesUrl: String? = null + ): Result /** * @param context Interface to global information about an application environment. @@ -120,4 +123,12 @@ interface ApiRepository : BaseApiRepository { suspend fun getPrivateKeysViaEkm( context: Context, ekmUrl: String, tokenId: String ): Result + + /** + * Check if "https://fes.$domain/api/" is available for interactions + * + * @param context Interface to global information about an application environment. + * @param domain A company domain. + */ + suspend fun checkFes(context: Context, domain: String): Result } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiService.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiService.kt index 9b3dfc20b4..50ab9e1ef7 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiService.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/ApiService.kt @@ -11,14 +11,17 @@ import com.flowcrypt.email.api.retrofit.request.model.InitialLegacySubmitModel import com.flowcrypt.email.api.retrofit.request.model.LoginModel import com.flowcrypt.email.api.retrofit.request.model.PostHelpFeedbackModel import com.flowcrypt.email.api.retrofit.request.model.TestWelcomeModel +import com.flowcrypt.email.api.retrofit.response.api.ClientConfigurationResponse import com.flowcrypt.email.api.retrofit.response.api.DomainOrgRulesResponse import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse +import com.flowcrypt.email.api.retrofit.response.api.FesServerResponse import com.flowcrypt.email.api.retrofit.response.api.LoginResponse import com.flowcrypt.email.api.retrofit.response.api.PostHelpFeedbackResponse import com.flowcrypt.email.api.retrofit.response.attester.InitialLegacySubmitResponse import com.flowcrypt.email.api.retrofit.response.attester.TestWelcomeResponse import com.flowcrypt.email.api.retrofit.response.oauth2.MicrosoftOAuth2TokenResponse import com.google.gson.JsonObject +import okhttp3.ResponseBody import retrofit2.Call import retrofit2.Response import retrofit2.http.Body @@ -107,7 +110,15 @@ interface ApiService { * @param body POJO model for requests */ @POST(BuildConfig.API_URL + "account/get") - suspend fun getDomainOrgRules(@Body body: LoginModel): Response + suspend fun getOrgRulesFromFlowCryptComBackend(@Body body: LoginModel): Response + + /** + * This method calls "https://fes.$domain/api/v1/client-configuration?domain=$domain" + * + * @param fesUrl URL of FES + */ + @GET + suspend fun getOrgRulesFromFes(@Url fesUrl: String): Response /** * This method calls API "https://flowcrypt.com/attester/initial/legacy_submit" via coroutines @@ -149,4 +160,16 @@ interface ApiService { @Url ekmUrl: String, @Header("Authorization") tokenId: String ): Response + + /** + * This method check if "https://fes.$domain/api/" is available for interactions + */ + @GET("https://fes.{domain}/api/") + suspend fun checkFes(@Path("domain") domain: String): Response + + /** + * This method check if "url" is available for interactions + */ + @GET() + suspend fun isAvailable(@Url url: String): Response } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt index 8fcb96415f..79911fcb3a 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt @@ -10,17 +10,22 @@ import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.request.model.InitialLegacySubmitModel import com.flowcrypt.email.api.retrofit.request.model.LoginModel import com.flowcrypt.email.api.retrofit.request.model.TestWelcomeModel -import com.flowcrypt.email.api.retrofit.response.api.DomainOrgRulesResponse import com.flowcrypt.email.api.retrofit.response.api.EkmPrivateKeysResponse +import com.flowcrypt.email.api.retrofit.response.api.FesServerResponse import com.flowcrypt.email.api.retrofit.response.api.LoginResponse import com.flowcrypt.email.api.retrofit.response.attester.InitialLegacySubmitResponse import com.flowcrypt.email.api.retrofit.response.attester.PubResponse import com.flowcrypt.email.api.retrofit.response.attester.TestWelcomeResponse +import com.flowcrypt.email.api.retrofit.response.base.ApiResponse import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.api.retrofit.response.oauth2.MicrosoftOAuth2TokenResponse import com.google.gson.JsonObject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit /** * Implementation of Flowcrypt API @@ -46,14 +51,18 @@ class FlowcryptApiRepository : ApiRepository { override suspend fun getDomainOrgRules( context: Context, - loginModel: LoginModel - ): Result = + loginModel: LoginModel, + fesUrl: String? + ): Result = withContext(Dispatchers.IO) { val apiService = ApiHelper.getInstance(context).retrofit.create(ApiService::class.java) - getResult( - context = context, - expectedResultClass = DomainOrgRulesResponse::class.java - ) { apiService.getDomainOrgRules(loginModel) } + getResult(context = context) { + if (fesUrl != null) { + apiService.getOrgRulesFromFes(fesUrl = fesUrl) + } else { + apiService.getOrgRulesFromFlowCryptComBackend(body = loginModel) + } + } } override suspend fun submitPubKey( @@ -160,4 +169,27 @@ class FlowcryptApiRepository : ApiRepository { expectedResultClass = EkmPrivateKeysResponse::class.java ) { apiService.getPrivateKeysViaEkm("${url}v1/keys/private", "Bearer $tokenId") } } + + override suspend fun checkFes(context: Context, domain: String): Result = + withContext(Dispatchers.IO) { + val connectionTimeoutInSeconds = 3L + val okHttpClient = OkHttpClient.Builder() + .connectTimeout(connectionTimeoutInSeconds, TimeUnit.SECONDS) + .writeTimeout(connectionTimeoutInSeconds, TimeUnit.SECONDS) + .readTimeout(connectionTimeoutInSeconds, TimeUnit.SECONDS) + .apply { + ApiHelper.configureOkHttpClientForDebuggingIfAllowed(context, this) + }.build() + + val retrofit = Retrofit.Builder() + .baseUrl("https://fes.$domain/api/") + .addConverterFactory(GsonConverterFactory.create(ApiHelper.getInstance(context).gson)) + .client(okHttpClient) + .build() + val apiService = retrofit.create(ApiService::class.java) + return@withContext getResult( + context = context, + expectedResultClass = FesServerResponse::class.java + ) { apiService.checkFes(domain) } + } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/ClientConfigurationResponse.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/ClientConfigurationResponse.kt new file mode 100644 index 0000000000..6c7839a7ff --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/ClientConfigurationResponse.kt @@ -0,0 +1,48 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.api.retrofit.response.api + +import android.os.Parcel +import android.os.Parcelable +import com.flowcrypt.email.api.retrofit.response.base.ApiError +import com.flowcrypt.email.api.retrofit.response.base.ApiResponse +import com.flowcrypt.email.api.retrofit.response.model.OrgRules +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +/** + * @author Denis Bondarenko + * Date: 7/27/21 + * Time: 4:16 PM + * E-mail: DenBond7@gmail.com + */ +data class ClientConfigurationResponse constructor( + @SerializedName("error") + @Expose override val apiError: ApiError? = null, + @SerializedName("clientConfiguration") + @Expose val orgRules: OrgRules? +) : ApiResponse { + constructor(parcel: Parcel) : this( + parcel.readParcelable(ApiError::class.java.classLoader), + parcel.readParcelable(OrgRules::class.java.classLoader) + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + with(parcel) { + writeParcelable(apiError, flags) + writeParcelable(orgRules, flags) + } + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = ClientConfigurationResponse(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/DomainOrgRulesResponse.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/DomainOrgRulesResponse.kt index e6fc9512bc..bab109aacf 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/DomainOrgRulesResponse.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/DomainOrgRulesResponse.kt @@ -44,12 +44,7 @@ data class DomainOrgRulesResponse constructor( } companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): DomainOrgRulesResponse { - return DomainOrgRulesResponse(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + override fun createFromParcel(parcel: Parcel) = DomainOrgRulesResponse(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/FesServerResponse.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/FesServerResponse.kt new file mode 100644 index 0000000000..84fc3f9d5c --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/FesServerResponse.kt @@ -0,0 +1,59 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.api.retrofit.response.api + +import android.os.Parcel +import android.os.Parcelable +import com.flowcrypt.email.api.retrofit.response.base.ApiError +import com.flowcrypt.email.api.retrofit.response.base.ApiResponse +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +/** + * @author Denis Bondarenko + * Date: 7/23/21 + * Time: 8:52 AM + * E-mail: DenBond7@gmail.com + */ +data class FesServerResponse constructor( + @SerializedName("error") + @Expose override val apiError: ApiError? = null, + @Expose val vendor: String?, + @Expose val service: String?, + @Expose val orgId: String?, + @Expose val version: String?, + @Expose val endUserApiVersion: String?, + @Expose val adminApiVersion: String?, +) : ApiResponse { + constructor(parcel: Parcel) : this( + parcel.readParcelable(ApiError::class.java.classLoader), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readString() + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeParcelable(apiError, flags) + parcel.writeString(vendor) + parcel.writeString(service) + parcel.writeString(orgId) + parcel.writeString(version) + parcel.writeString(endUserApiVersion) + parcel.writeString(adminApiVersion) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): FesServerResponse = FesServerResponse(parcel) + override fun newArray(size: Int): Array = arrayOfNulls(size) + } +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/Context.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/Context.kt index 61542f49af..a36ce3dbd2 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/Context.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/extensions/Context.kt @@ -7,6 +7,9 @@ package com.flowcrypt.email.extensions import android.content.Context import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.net.NetworkInfo +import android.os.Build import android.widget.Toast /** @@ -28,7 +31,12 @@ fun Context.toast(resId: Int, duration: Int = Toast.LENGTH_SHORT) { fun Context?.hasActiveConnection(): Boolean { return this?.let { val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager - val activeNetwork: android.net.NetworkInfo? = cm?.activeNetworkInfo - activeNetwork?.isConnectedOrConnecting == true + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + val cap = cm?.getNetworkCapabilities(cm.activeNetwork) ?: return false + return cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + } else { + val activeNetwork: NetworkInfo? = cm?.activeNetworkInfo + activeNetwork?.isConnectedOrConnecting == true + } } ?: false } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/lifecycle/ConnectionLifecycleObserver.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/lifecycle/ConnectionLifecycleObserver.kt index 092d440b96..2dd883f05e 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/lifecycle/ConnectionLifecycleObserver.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/lifecycle/ConnectionLifecycleObserver.kt @@ -27,7 +27,6 @@ class ConnectionLifecycleObserver(context: Context?) : LifecycleObserver { private val connectivityManager = context?.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager private val networkRequest: NetworkRequest = NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addTransportType(NetworkCapabilities.TRANSPORT_VPN) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt new file mode 100644 index 0000000000..526c9e441d --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt @@ -0,0 +1,76 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: DenBond7 + */ + +package com.flowcrypt.email.jetpack.viewmodel + +import android.app.Application +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.flowcrypt.email.R +import com.flowcrypt.email.api.email.EmailUtil +import com.flowcrypt.email.api.retrofit.FlowcryptApiRepository +import com.flowcrypt.email.api.retrofit.response.api.FesServerResponse +import com.flowcrypt.email.api.retrofit.response.base.Result +import com.flowcrypt.email.extensions.hasActiveConnection +import com.flowcrypt.email.util.GeneralUtil +import com.flowcrypt.email.util.exception.CommonConnectionException +import kotlinx.coroutines.launch +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +/** + * @author Denis Bondarenko + * Date: 7/23/21 + * Time: 9:54 AM + * E-mail: DenBond7@gmail.com + */ +class CheckFesServerViewModel(application: Application) : BaseAndroidViewModel(application) { + private val repository = FlowcryptApiRepository() + val checkFesServerLiveData: MutableLiveData> = + MutableLiveData(Result.none()) + + fun checkFesServerAvailability(account: String) { + viewModelScope.launch { + val context: Context = getApplication() + checkFesServerLiveData.value = + Result.loading(progressMsg = context.getString(R.string.loading)) + + try { + val result = repository.checkFes( + context = getApplication(), + domain = EmailUtil.getDomain(account) + ) + + if (result.status == Result.Status.EXCEPTION) { + val causedException = result.exception + if (causedException != null) { + val processedException = when (causedException) { + is UnknownHostException, is SocketTimeoutException -> { + if (context.hasActiveConnection()) { + CommonConnectionException( + cause = causedException, + hasInternetAccess = GeneralUtil.hasInternetAccess() + ) + } else { + CommonConnectionException(cause = causedException, hasInternetAccess = false) + } + } + + else -> causedException + } + + checkFesServerLiveData.value = Result.exception(processedException) + return@launch + } + } + + checkFesServerLiveData.value = result + } catch (e: Exception) { + checkFesServerLiveData.value = Result.exception(e) + } + } + } +} diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DomainOrgRulesViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DomainOrgRulesViewModel.kt index af710640aa..015fe6b5c9 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DomainOrgRulesViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DomainOrgRulesViewModel.kt @@ -13,7 +13,7 @@ import androidx.lifecycle.viewModelScope import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.FlowcryptApiRepository import com.flowcrypt.email.api.retrofit.request.model.LoginModel -import com.flowcrypt.email.api.retrofit.response.api.DomainOrgRulesResponse +import com.flowcrypt.email.api.retrofit.response.base.ApiResponse import com.flowcrypt.email.api.retrofit.response.base.Result import kotlinx.coroutines.launch @@ -27,10 +27,10 @@ import kotlinx.coroutines.launch */ class DomainOrgRulesViewModel(application: Application) : BaseAndroidViewModel(application) { private val repository = FlowcryptApiRepository() - val domainOrgRulesLiveData: MutableLiveData> = + val domainOrgRulesLiveData: MutableLiveData> = MutableLiveData(Result.none()) - fun fetchOrgRules(account: String, uuid: String) { + fun fetchOrgRules(account: String, uuid: String, fesUrl: String? = null) { viewModelScope.launch { val context: Context = getApplication() domainOrgRulesLiveData.value = @@ -39,7 +39,8 @@ class DomainOrgRulesViewModel(application: Application) : BaseAndroidViewModel(a try { domainOrgRulesLiveData.value = repository.getDomainOrgRules( context = context, - loginModel = LoginModel(account, uuid) + loginModel = LoginModel(account, uuid), + fesUrl = fesUrl ) } catch (e: Exception) { domainOrgRulesLiveData.value = Result.exception(e) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MainSignInFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MainSignInFragment.kt index 413d0f05a9..5d5bf7de0d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MainSignInFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MainSignInFragment.kt @@ -13,10 +13,13 @@ import android.view.View import android.widget.Toast import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.viewModels +import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.Constants import com.flowcrypt.email.R import com.flowcrypt.email.api.email.EmailUtil import com.flowcrypt.email.api.email.JavaEmailConstants +import com.flowcrypt.email.api.retrofit.response.api.ClientConfigurationResponse +import com.flowcrypt.email.api.retrofit.response.api.DomainOrgRulesResponse import com.flowcrypt.email.api.retrofit.response.base.ApiResponse import com.flowcrypt.email.api.retrofit.response.base.Result import com.flowcrypt.email.api.retrofit.response.model.OrgRules @@ -29,6 +32,7 @@ import com.flowcrypt.email.extensions.incrementSafely import com.flowcrypt.email.extensions.navController import com.flowcrypt.email.extensions.showInfoDialog import com.flowcrypt.email.extensions.showTwoWayDialog +import com.flowcrypt.email.jetpack.viewmodel.CheckFesServerViewModel import com.flowcrypt.email.jetpack.viewmodel.DomainOrgRulesViewModel import com.flowcrypt.email.jetpack.viewmodel.EkmViewModel import com.flowcrypt.email.jetpack.viewmodel.LoginViewModel @@ -46,6 +50,7 @@ import com.flowcrypt.email.ui.activity.fragment.dialog.TwoWayDialogFragment import com.flowcrypt.email.ui.activity.settings.FeedbackActivity import com.flowcrypt.email.util.GeneralUtil import com.flowcrypt.email.util.exception.AccountAlreadyAddedException +import com.flowcrypt.email.util.exception.CommonConnectionException import com.flowcrypt.email.util.exception.EkmNotSupportedException import com.flowcrypt.email.util.exception.ExceptionUtil import com.flowcrypt.email.util.exception.UnsupportedOrgRulesException @@ -60,6 +65,7 @@ import com.google.android.material.snackbar.Snackbar import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException import com.sun.mail.util.MailConnectException import org.pgpainless.util.Passphrase +import java.net.HttpURLConnection import java.net.SocketTimeoutException import java.util.* @@ -74,7 +80,9 @@ class MainSignInFragment : BaseSingInFragment() { private var googleSignInAccount: GoogleSignInAccount? = null private var uuid: String = SecurityUtils.generateRandomUUID() private var orgRules: OrgRules? = null + private var fesUrl: String? = null + private val checkFesServerViewModel: CheckFesServerViewModel by viewModels() private val loginViewModel: LoginViewModel by viewModels() private val domainOrgRulesViewModel: DomainOrgRulesViewModel by viewModels() private val ekmViewModel: EkmViewModel by viewModels() @@ -139,9 +147,9 @@ class MainSignInFragment : BaseSingInFragment() { REQUEST_CODE_RETRY_LOGIN -> { when (resultCode) { TwoWayDialogFragment.RESULT_OK -> { + orgRules = null val account = googleSignInAccount?.account?.name ?: return val idToken = googleSignInAccount?.idToken ?: return - orgRules = null loginViewModel.login(account, uuid, idToken) } } @@ -151,7 +159,11 @@ class MainSignInFragment : BaseSingInFragment() { when (resultCode) { TwoWayDialogFragment.RESULT_OK -> { val account = googleSignInAccount?.account?.name ?: return - domainOrgRulesViewModel.fetchOrgRules(account, uuid) + domainOrgRulesViewModel.fetchOrgRules( + account = account, + uuid = uuid, + fesUrl = fesUrl + ) } } } @@ -165,6 +177,17 @@ class MainSignInFragment : BaseSingInFragment() { } } + REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY -> { + when (resultCode) { + TwoWayDialogFragment.RESULT_OK -> { + orgRules = null + fesUrl = null + val account = googleSignInAccount?.account?.name ?: return + checkFesServerViewModel.checkFesServerAvailability(account) + } + } + } + else -> super.onActivityResult(requestCode, resultCode, data) } } @@ -221,12 +244,8 @@ class MainSignInFragment : BaseSingInFragment() { private fun signInWithGmail() { googleSignInAccount = null - if (GeneralUtil.isConnected(activity)) { - client.signOut() - startActivityForResult(client.signInIntent, REQUEST_CODE_SIGN_IN) - } else { - showInfoSnackbar(msgText = activity?.getString(R.string.internet_connection_is_not_available)) - } + client.signOut() + startActivityForResult(client.signInIntent, REQUEST_CODE_SIGN_IN) } private fun handleSignInResult(resultCode: Int, task: Task) { @@ -235,7 +254,6 @@ class MainSignInFragment : BaseSingInFragment() { googleSignInAccount = task.getResult(ApiException::class.java) val account = googleSignInAccount?.account?.name ?: return - val idToken = googleSignInAccount?.idToken ?: return uuid = SecurityUtils.generateRandomUUID() val publicEmailDomains = arrayOf( @@ -247,7 +265,8 @@ class MainSignInFragment : BaseSingInFragment() { onSignSuccess(googleSignInAccount) } else { orgRules = null - loginViewModel.login(account, uuid, idToken) + fesUrl = null + checkFesServerViewModel.checkFesServerAvailability(account) } } else { val error = task.exception @@ -424,11 +443,108 @@ class MainSignInFragment : BaseSingInFragment() { } private fun initEnterpriseViewModels() { + initCheckFesServerViewModel() initLoginViewModel() initDomainOrgRulesViewModel() initEkmViewModel() } + private fun initCheckFesServerViewModel() { + checkFesServerViewModel.checkFesServerLiveData.observe(viewLifecycleOwner, { + when (it.status) { + Result.Status.LOADING -> { + baseActivity.countingIdlingResource.incrementSafely() + showProgress(progressMsg = it.progressMsg) + } + + Result.Status.SUCCESS -> { + if ("enterprise-server" == it.data?.service) { + googleSignInAccount?.account?.name?.let { account -> + val domain = EmailUtil.getDomain(account) + fesUrl = "https://fes.$domain/api/v1/client-configuration?domain=$domain" + domainOrgRulesViewModel.fetchOrgRules( + account = account, + uuid = uuid, + fesUrl = fesUrl + ) + } + } else { + continueBasedOnFlavorSettings() + } + + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + baseActivity.countingIdlingResource.decrementSafely() + } + + Result.Status.ERROR -> { + showContent() + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) + baseActivity.countingIdlingResource.decrementSafely() + } + + Result.Status.EXCEPTION -> { + when (it.exception) { + is CommonConnectionException -> { + if (it.exception.hasInternetAccess == true) { + continueBasedOnFlavorSettings() + } else { + showContent() + showDialogWithRetryButton( + getString(R.string.no_connection_or_server_is_not_reachable), + REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY + ) + } + } + + is com.flowcrypt.email.util.exception.ApiException -> { + when (it.exception.apiError?.code) { + HttpURLConnection.HTTP_NOT_FOUND -> { + continueWithRegularFlow() + } + + else -> { + continueBasedOnFlavorSettings() + } + } + } + + else -> { + showContent() + showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) + } + } + + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + baseActivity.countingIdlingResource.decrementSafely() + } + } + }) + } + + private fun continueBasedOnFlavorSettings() { + if (BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE) { + /* + here we actually need to decide if we should show error or proceed with + regular setup flow based on exact customers that will skip to regular setup flow, + and the rest will be shown error. + */ + continueWithRegularFlow() + } else { + continueWithRegularFlow() + } + } + + private fun continueWithRegularFlow() { + val account = googleSignInAccount?.account?.name + val idToken = googleSignInAccount?.idToken + if (account != null && idToken != null) { + loginViewModel.login(account, uuid, idToken) + } else { + showContent() + } + } + private fun initLoginViewModel() { loginViewModel.loginLiveData.observe(viewLifecycleOwner, { when (it.status) { @@ -478,9 +594,11 @@ class MainSignInFragment : BaseSingInFragment() { Result.Status.SUCCESS -> { val idToken = googleSignInAccount?.idToken - if (it.data?.orgRules != null && idToken != null) { - orgRules = it.data.orgRules - ekmViewModel.fetchPrvKeys(it.data.orgRules, idToken) + orgRules = (it.data as? DomainOrgRulesResponse)?.orgRules + ?: (it.data as? ClientConfigurationResponse)?.orgRules + + if (idToken != null) { + orgRules?.let { fetchedOrgRules -> ekmViewModel.fetchPrvKeys(fetchedOrgRules, idToken) } } else { showContent() askUserToReLogin() @@ -588,6 +706,10 @@ class MainSignInFragment : BaseSingInFragment() { val errorMsg = it.data?.apiError?.msg ?: it.exception?.message ?: getString(R.string.unknown_error) + showDialogWithRetryButton(errorMsg, resultCode) + } + + private fun showDialogWithRetryButton(errorMsg: String, resultCode: Int) { showTwoWayDialog( requestCode = resultCode, dialogTitle = "", @@ -659,5 +781,6 @@ class MainSignInFragment : BaseSingInFragment() { private const val REQUEST_CODE_RETRY_LOGIN = 104 private const val REQUEST_CODE_RETRY_GET_DOMAIN_ORG_RULES = 105 private const val REQUEST_CODE_RETRY_FETCH_PRV_KEYS_VIA_EKM = 106 + private const val REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY = 107 } } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt index 8e2646d02c..9ebb475b28 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/GeneralUtil.kt @@ -30,11 +30,17 @@ import androidx.preference.PreferenceManager import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.Constants import com.flowcrypt.email.R +import com.flowcrypt.email.api.retrofit.ApiService +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient import org.apache.commons.io.IOUtils +import retrofit2.Retrofit import java.io.File import java.io.IOException import java.nio.charset.StandardCharsets import java.util.* +import java.util.concurrent.TimeUnit /** * General util methods. @@ -79,6 +85,28 @@ class GeneralUtil { return activeNetwork?.isConnected ?: false } + suspend fun hasInternetAccess(): Boolean = withContext(Dispatchers.IO) { + val url = "https://www.google.com" + val connectionTimeoutInSeconds = 2000L + val okHttpClient = OkHttpClient.Builder() + .connectTimeout(connectionTimeoutInSeconds, TimeUnit.MILLISECONDS) + .writeTimeout(connectionTimeoutInSeconds, TimeUnit.MILLISECONDS) + .readTimeout(connectionTimeoutInSeconds, TimeUnit.MILLISECONDS) + .build() + + val retrofit = Retrofit.Builder() + .baseUrl(url) + .client(okHttpClient) + .build() + val apiService = retrofit.create(ApiService::class.java) + try { + apiService.isAvailable(url) + return@withContext true + } catch (e: Exception) { + return@withContext false + } + } + /** * Show the application system settings screen. * diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/CommonConnectionException.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/CommonConnectionException.kt index e843316736..592340cff8 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/CommonConnectionException.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/util/exception/CommonConnectionException.kt @@ -15,4 +15,5 @@ import java.io.IOException * Time: 5:01 PM * E-mail: DenBond7@gmail.com */ -class CommonConnectionException(cause: Throwable?) : IOException(cause) +class CommonConnectionException(cause: Throwable?, val hasInternetAccess: Boolean? = null) : + IOException(cause) diff --git a/FlowCrypt/src/main/res/values/strings.xml b/FlowCrypt/src/main/res/values/strings.xml index 6ddab17bbf..5a66c3cc4b 100644 --- a/FlowCrypt/src/main/res/values/strings.xml +++ b/FlowCrypt/src/main/res/values/strings.xml @@ -526,4 +526,5 @@ Error. Key %1$s is not fully decrypted. Please ask your systems administrator or help desk Error. Key %1$s is not fully encrypted Couldn\'t parse one of the keys received from EKM + No connection or the server is not reachable diff --git a/docker-mailserver/config/ssl/demoCA/index.txt b/docker-mailserver/config/ssl/demoCA/index.txt index 9c249f6863..93fb161073 100644 --- a/docker-mailserver/config/ssl/demoCA/index.txt +++ b/docker-mailserver/config/ssl/demoCA/index.txt @@ -1,3 +1,3 @@ V 410428063337Z 794AF21E00D03265899A7406166CD11402A576F4 unknown /C=CZ/ST=Debug/O=FlowCrypt/OU=CA/CN=flowcrypt.test/emailAddress=admin@flowcrypt.test V 310501063529Z 794AF21E00D03265899A7406166CD11402A576F5 unknown /C=CZ/ST=Debug/O=FlowCrypt/CN=mail.flowcrypt.test/emailAddress=admin@flowcrypt.test -V 310501071021Z 794AF21E00D03265899A7406166CD11402A576F6 unknown /C=CZ/ST=Debug/O=FlowCrypt/CN=localhost/emailAddress=admin@flowcrypt.test +V 310726083817Z 794AF21E00D03265899A7406166CD11402A576F6 unknown /C=CZ/ST=Debug/O=FlowCrypt/CN=localhost/emailAddress=admin@flowcrypt.test diff --git a/docker-mailserver/config/ssl/demoCA/newcerts/794AF21E00D03265899A7406166CD11402A576F6.pem b/docker-mailserver/config/ssl/demoCA/newcerts/794AF21E00D03265899A7406166CD11402A576F6.pem index 5112a78739..350f860a9b 100644 --- a/docker-mailserver/config/ssl/demoCA/newcerts/794AF21E00D03265899A7406166CD11402A576F6.pem +++ b/docker-mailserver/config/ssl/demoCA/newcerts/794AF21E00D03265899A7406166CD11402A576F6.pem @@ -6,31 +6,31 @@ Certificate: Signature Algorithm: sha256WithRSAEncryption Issuer: C=CZ, ST=Debug, O=FlowCrypt, OU=CA, CN=flowcrypt.test/emailAddress=admin@flowcrypt.test Validity - Not Before: May 3 07:10:21 2021 GMT - Not After : May 1 07:10:21 2031 GMT + Not Before: Jul 28 08:38:17 2021 GMT + Not After : Jul 26 08:38:17 2031 GMT Subject: C=CZ, ST=Debug, O=FlowCrypt, CN=localhost/emailAddress=admin@flowcrypt.test Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: - 00:c7:ff:8e:35:c3:61:38:a6:3d:df:f2:4e:ee:b3: - db:b4:43:77:5f:09:68:cb:be:b6:82:7a:17:4b:f8: - ba:26:fa:39:27:d3:e4:e9:75:ff:34:ce:f4:06:3f: - 5f:8a:7d:3f:63:ce:da:c8:0f:43:84:de:45:d0:9c: - 61:83:2d:9e:9e:8e:04:8b:e3:d9:d8:39:3f:3d:25: - 5c:c4:b8:0f:9e:eb:51:c7:2f:5a:16:67:c7:7c:06: - b4:d2:73:35:f6:1a:ef:38:14:45:96:bf:3b:2b:ca: - 8e:fa:c3:82:f3:4c:a4:56:9b:b7:50:90:d3:89:b2: - 4a:e8:7d:a3:06:2a:44:31:b2:f0:6a:3f:d2:ab:f0: - 1c:47:b2:cd:be:a8:e4:33:ea:2e:5a:e5:6c:63:53: - c6:11:91:21:95:35:33:1c:fb:b0:68:5e:7a:6a:1f: - 9b:b6:93:e7:76:c6:13:c3:93:2c:85:0d:84:66:2d: - b1:f8:4f:f9:e3:f2:de:bc:9a:85:61:c5:c7:39:6f: - 2a:46:d5:de:3b:af:46:e0:08:94:fb:7a:de:4c:92: - fe:41:29:50:de:6b:32:ea:5b:b0:90:59:95:84:29: - 01:b0:77:4c:5a:48:48:a3:33:cf:0a:5d:1a:cd:86: - c3:01:17:c7:d3:98:6c:5e:56:ef:e4:74:a0:27:3f: - 8e:c3 + 00:ad:b8:c9:5c:59:87:7a:08:56:9d:24:53:f2:c4: + 6b:3d:ce:9c:57:23:e7:ef:bd:f8:ce:d6:bb:7a:45: + 8d:e6:86:7c:c5:b4:08:2e:c5:73:38:bf:bf:3e:ab: + 5d:7b:09:c8:a5:40:cb:58:d6:41:7f:06:8f:94:53: + 06:61:bb:a0:28:06:77:c5:ce:25:7c:98:fe:f9:68: + b5:0c:d2:70:79:5c:f5:7b:43:99:d8:51:ed:7e:b3: + 8d:ed:32:61:27:d8:8b:56:ee:42:a6:a8:cb:f5:11: + 70:7e:6e:b9:e7:88:c0:2f:e6:0e:48:4f:fb:81:a5: + 4a:c7:7e:c7:ab:28:fd:14:c4:39:da:68:cb:fe:99: + 9b:61:0c:e4:ad:b8:5b:95:93:bd:ca:ea:38:d0:5b: + 0d:ed:96:57:db:b3:69:d4:93:7b:47:be:9c:5b:dd: + 6e:6c:6d:87:89:ad:90:6f:b9:3c:96:80:ac:09:52: + 54:bd:f0:49:8b:c8:66:1c:28:ed:71:64:c0:e5:60: + 24:39:1e:70:28:77:78:40:48:a4:a6:99:7c:98:ad: + 40:1a:76:79:51:24:43:2e:fe:19:72:43:6a:36:e4: + 59:13:48:93:a3:51:bc:f8:85:98:d5:42:47:4b:6a: + bb:c0:cf:a7:7f:c8:e3:5c:c0:85:d9:a3:26:1d:e8: + e2:a9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: @@ -38,51 +38,52 @@ Certificate: Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: - 8F:2C:7C:57:2E:1E:03:65:EF:86:7E:AE:7F:BB:19:0E:0C:92:99:24 + B9:11:F8:F7:26:1D:64:9F:0D:2B:8F:7F:1E:69:F0:B8:9B:34:3F:94 X509v3 Authority Key Identifier: keyid:57:1B:70:94:B3:E4:B5:29:FA:51:9C:3F:77:5A:8F:15:F8:82:1A:B6 X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment X509v3 Subject Alternative Name: - DNS:localhost + DNS:localhost, DNS:*.localhost Signature Algorithm: sha256WithRSAEncryption - c5:13:26:a7:b8:7a:a5:d0:fc:08:7e:9d:9a:65:d1:95:b3:1a: - ae:85:89:bf:e2:74:65:82:62:8d:fe:cc:80:89:56:66:82:29: - bc:21:2f:ee:be:44:1b:9d:19:5d:65:20:42:ea:17:61:c9:45: - 9b:ee:84:54:22:89:07:64:40:0f:4b:ea:29:30:88:0c:6e:44: - 7e:5d:27:3e:03:15:a6:e4:fe:07:6d:58:27:f0:30:da:2c:ef: - 64:6a:53:d2:e1:42:87:cf:60:03:b1:e6:4f:06:0b:b8:60:c6: - 94:2b:db:fb:91:fa:89:86:d5:b7:06:4d:48:5c:d8:15:7f:1f: - d4:85:51:ef:62:68:c7:7d:3e:b7:c6:08:28:59:ca:62:02:39: - 1c:9f:ae:06:52:0f:be:52:70:40:13:41:ed:19:e2:0f:fc:c2: - 50:15:77:b7:5c:0d:5d:eb:9c:3b:60:f8:fe:a4:cd:7e:8f:63: - dc:33:5f:02:03:4b:dd:48:fb:36:88:ce:1f:80:0a:4f:a7:59: - 95:e8:e8:01:7a:d0:d7:56:77:8c:23:23:60:48:5a:fe:c2:72: - a8:ab:53:0e:56:d0:d1:70:2d:d3:27:e0:99:87:44:11:40:fe: - c7:37:f4:3c:04:22:e6:7d:09:a4:12:9e:3a:a0:8c:bd:fa:6a: - 3c:10:d4:8e + 17:2b:64:78:9f:67:d7:b4:6e:49:7d:57:4b:b4:2a:64:f4:c3: + 07:8b:da:c7:15:a1:ea:cf:7a:fa:06:df:99:17:06:30:d0:cf: + a0:c5:b2:b0:9d:e3:95:34:2d:1d:79:79:b7:18:b2:cb:b8:34: + 91:8a:19:3d:7e:90:08:06:42:02:8b:9b:56:b7:79:ef:50:7f: + b7:a5:0a:b9:94:a0:51:82:d3:a9:5d:ec:79:2e:52:59:45:9f: + a4:1f:f1:1d:d2:4f:2d:25:67:b3:45:ee:5c:95:40:b2:53:a5: + 34:0b:c6:24:8d:ed:02:b2:92:b5:db:14:24:84:a1:6a:a2:d1: + 53:52:67:a5:8f:32:a8:17:1c:7e:e7:0b:95:7d:c6:ce:4c:03: + 12:52:7f:f4:89:72:7c:05:12:f5:33:7f:98:3a:ce:9d:5a:2d: + 29:99:56:4f:15:cc:26:87:75:ba:fa:11:c1:43:0a:1c:ef:4b: + 99:6d:bd:ba:99:27:94:f5:2a:01:e5:cc:4e:2d:dc:e8:69:c0: + 86:59:06:72:f4:78:a8:f0:cb:41:33:b4:49:ad:46:5a:e5:1f: + f2:d3:40:3f:66:99:3e:e4:27:7a:92:9c:2d:3b:ed:55:31:cd: + 2b:bc:b2:cf:bb:14:13:f3:de:d5:7b:16:77:79:38:24:ab:45: + 5a:cc:79:92 -----BEGIN CERTIFICATE----- -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 +RVrMeZI= -----END CERTIFICATE-----