From c52fd043f43493dc81e49b3b62b5bdfcee924e9d Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Fri, 23 Jul 2021 11:52:41 +0300 Subject: [PATCH 01/20] Added CheckFesServerViewModel. Added a new logic to check FES.| #1202 --- .../java/com/flowcrypt/email/Constants.kt | 5 + .../flowcrypt/email/api/retrofit/ApiHelper.kt | 87 +++++++------- .../email/api/retrofit/ApiRepository.kt | 11 ++ .../email/api/retrofit/ApiService.kt | 14 ++- .../api/retrofit/FlowcryptApiRepository.kt | 31 ++++- .../response/api/FesServerResponse.kt | 59 ++++++++++ .../viewmodel/CheckFesServerViewModel.kt | 46 ++++++++ .../viewmodel/DomainOrgRulesViewModel.kt | 4 +- .../activity/fragment/MainSignInFragment.kt | 108 ++++++++++++++++-- FlowCrypt/src/main/res/values/strings.xml | 1 + 10 files changed, 313 insertions(+), 53 deletions(-) create mode 100644 FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/FesServerResponse.kt create mode 100644 FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt index 140e1b5afd..8b2cc0fdbe 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" ) + + /** + * Flavours. + */ + 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..ca24da4fff 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 @@ -12,6 +12,7 @@ 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 @@ -43,10 +44,12 @@ 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, + fesUrl: String, loginModel: LoginModel ): Result @@ -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..68425f9f7d 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 @@ -13,6 +13,7 @@ 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.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 @@ -106,8 +107,11 @@ interface ApiService { * * @param body POJO model for requests */ - @POST(BuildConfig.API_URL + "account/get") - suspend fun getDomainOrgRules(@Body body: LoginModel): Response + @POST() + suspend fun getDomainOrgRules( + @Url fesUrl: String, + @Body body: LoginModel + ): Response /** * This method calls API "https://flowcrypt.com/attester/initial/legacy_submit" via coroutines @@ -149,4 +153,10 @@ 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 } 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..ca906dcfcb 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 @@ -12,6 +12,7 @@ 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 @@ -21,6 +22,10 @@ import com.flowcrypt.email.api.retrofit.response.oauth2.MicrosoftOAuth2TokenResp 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,6 +51,7 @@ class FlowcryptApiRepository : ApiRepository { override suspend fun getDomainOrgRules( context: Context, + fesUrl: String, loginModel: LoginModel ): Result = withContext(Dispatchers.IO) { @@ -53,7 +59,7 @@ class FlowcryptApiRepository : ApiRepository { getResult( context = context, expectedResultClass = DomainOrgRulesResponse::class.java - ) { apiService.getDomainOrgRules(loginModel) } + ) { apiService.getDomainOrgRules(fesUrl = fesUrl, body = loginModel) } } override suspend fun submitPubKey( @@ -160,4 +166,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/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/jetpack/viewmodel/CheckFesServerViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt new file mode 100644 index 0000000000..7428908f22 --- /dev/null +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt @@ -0,0 +1,46 @@ +/* + * © 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 kotlinx.coroutines.launch + +/** + * @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 { + checkFesServerLiveData.value = repository.checkFes( + context = getApplication(), + domain = EmailUtil.getDomain(account) + ) + } 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..1abdb63bab 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 @@ -10,6 +10,7 @@ import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.FlowcryptApiRepository import com.flowcrypt.email.api.retrofit.request.model.LoginModel @@ -30,7 +31,7 @@ class DomainOrgRulesViewModel(application: Application) : BaseAndroidViewModel(a 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,6 +40,7 @@ class DomainOrgRulesViewModel(application: Application) : BaseAndroidViewModel(a try { domainOrgRulesLiveData.value = repository.getDomainOrgRules( context = context, + fesUrl = fesUrl ?: BuildConfig.API_URL + "account/get", loginModel = LoginModel(account, uuid) ) } catch (e: Exception) { 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..8ce67ebdff 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,12 @@ 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.FesServerResponse 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 +31,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 @@ -60,7 +63,9 @@ 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.net.UnknownHostException import java.util.* /** @@ -75,6 +80,7 @@ class MainSignInFragment : BaseSingInFragment() { private var uuid: String = SecurityUtils.generateRandomUUID() private var orgRules: OrgRules? = 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() @@ -165,6 +171,16 @@ class MainSignInFragment : BaseSingInFragment() { } } + REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY -> { + when (resultCode) { + TwoWayDialogFragment.RESULT_OK -> { + val account = googleSignInAccount?.account?.name ?: return + orgRules = null + checkFesServerViewModel.checkFesServerAvailability(account) + } + } + } + else -> super.onActivityResult(requestCode, resultCode, data) } } @@ -221,12 +237,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 +247,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 +258,7 @@ class MainSignInFragment : BaseSingInFragment() { onSignSuccess(googleSignInAccount) } else { orgRules = null - loginViewModel.login(account, uuid, idToken) + checkFesServerViewModel.checkFesServerAvailability(account) } } else { val error = task.exception @@ -424,11 +435,89 @@ 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 -> { + continueWithRegularFlow() + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + baseActivity.countingIdlingResource.decrementSafely() + } + + Result.Status.ERROR -> { + showContent() + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + baseActivity.countingIdlingResource.decrementSafely() + } + + Result.Status.EXCEPTION -> { + showContent() + when (it.exception) { + is UnknownHostException -> { + showInfoDialog( + dialogTitle = "", + dialogMsg = getString(R.string.no_connection_or_server_is_not_reachable), + isCancelable = true + ) + } + + is SocketTimeoutException -> { + continueBasedOnFlavorSettings(it) + } + + is com.flowcrypt.email.util.exception.ApiException -> { + when (it.exception.apiError?.code) { + HttpURLConnection.HTTP_NOT_FOUND -> { + continueWithRegularFlow() + } + + in 400..500 -> { + continueBasedOnFlavorSettings(it) + } + } + } + + else -> { + showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) + } + } + + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + baseActivity.countingIdlingResource.decrementSafely() + } + } + }) + } + + private fun continueBasedOnFlavorSettings(it: Result) { + if (BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE) { + showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) + } 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) { @@ -441,6 +530,8 @@ class MainSignInFragment : BaseSingInFragment() { if (it.data?.isVerified == true) { val account = googleSignInAccount?.account?.name if (account != null) { + /*val fesUrl = + if (isFesAvailable) "https://fes.$domain/api/v1/client-configuration?domain=$domain" else null*/ domainOrgRulesViewModel.fetchOrgRules(account, uuid) } else { showContent() @@ -659,5 +750,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/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 From 37315b72d26f8ebe4ed927254de2be8dedd3939f Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Mon, 26 Jul 2021 10:24:20 +0300 Subject: [PATCH 02/20] Added fetching OrgRules via FES for a simple success case.| #1202 --- .../email/api/retrofit/ApiRepository.kt | 4 ++-- .../email/api/retrofit/ApiService.kt | 15 +++++++++----- .../api/retrofit/FlowcryptApiRepository.kt | 12 ++++++++--- .../response/api/DomainOrgRulesResponse.kt | 2 +- .../viewmodel/DomainOrgRulesViewModel.kt | 5 ++--- .../activity/fragment/MainSignInFragment.kt | 20 ++++++++++++++++--- FlowCrypt/src/main/res/values/strings.xml | 1 + 7 files changed, 42 insertions(+), 17 deletions(-) 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 ca24da4fff..312aeaeeae 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 @@ -49,8 +49,8 @@ interface ApiRepository : BaseApiRepository { */ suspend fun getDomainOrgRules( context: Context, - fesUrl: String, - loginModel: LoginModel + loginModel: LoginModel, + fesUrl: String? = null ): 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 68425f9f7d..8b46a23fd0 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 @@ -107,11 +107,16 @@ interface ApiService { * * @param body POJO model for requests */ - @POST() - suspend fun getDomainOrgRules( - @Url fesUrl: String, - @Body body: LoginModel - ): Response + @POST(BuildConfig.API_URL + "account/get") + suspend fun getDomainOrgRulesInternal(@Body body: LoginModel): Response + + /** + * This method calls "https://fes.$domain/api/v1/client-configuration?domain=$domain" + * + * @param fesUrl URL of FES + */ + @GET + suspend fun getDomainOrgRules(@Url fesUrl: String): Response /** * This method calls API "https://flowcrypt.com/attester/initial/legacy_submit" via coroutines 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 ca906dcfcb..ed830fe462 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 @@ -51,15 +51,21 @@ class FlowcryptApiRepository : ApiRepository { override suspend fun getDomainOrgRules( context: Context, - fesUrl: String, - loginModel: LoginModel + 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(fesUrl = fesUrl, body = loginModel) } + ) { + if (fesUrl != null) { + apiService.getDomainOrgRules(fesUrl = fesUrl) + } else { + apiService.getDomainOrgRulesInternal(body = loginModel) + } + } } override suspend fun submitPubKey( 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..b8a39229ee 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 @@ -24,7 +24,7 @@ import com.google.gson.annotations.SerializedName data class DomainOrgRulesResponse constructor( @SerializedName("error") @Expose override val apiError: ApiError? = null, - @SerializedName("domain_org_rules") + @SerializedName("domain_org_rules", alternate = ["clientConfiguration"]) @Expose val orgRules: OrgRules? ) : ApiResponse { constructor(parcel: Parcel) : this( 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 1abdb63bab..062ac2fbc5 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 @@ -10,7 +10,6 @@ import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.flowcrypt.email.BuildConfig import com.flowcrypt.email.R import com.flowcrypt.email.api.retrofit.FlowcryptApiRepository import com.flowcrypt.email.api.retrofit.request.model.LoginModel @@ -40,8 +39,8 @@ class DomainOrgRulesViewModel(application: Application) : BaseAndroidViewModel(a try { domainOrgRulesLiveData.value = repository.getDomainOrgRules( context = context, - fesUrl = fesUrl ?: BuildConfig.API_URL + "account/get", - 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 8ce67ebdff..8aa1f57e5b 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 @@ -450,7 +450,23 @@ class MainSignInFragment : BaseSingInFragment() { } Result.Status.SUCCESS -> { - continueWithRegularFlow() + if ("enterprise-server" == it.data?.service) { + googleSignInAccount?.account?.name?.let { account -> + val domain = EmailUtil.getDomain(account) + domainOrgRulesViewModel.fetchOrgRules( + account = account, + uuid = uuid, + fesUrl = "https://fes.$domain/api/v1/client-configuration?domain=$domain" + ) + } + } else { + continueBasedOnFlavorSettings( + it.copy( + exception = IllegalStateException(getString(R.string.fes_has_wrong_configuration)) + ) + ) + } + checkFesServerViewModel.checkFesServerLiveData.value = Result.none() baseActivity.countingIdlingResource.decrementSafely() } @@ -530,8 +546,6 @@ class MainSignInFragment : BaseSingInFragment() { if (it.data?.isVerified == true) { val account = googleSignInAccount?.account?.name if (account != null) { - /*val fesUrl = - if (isFesAvailable) "https://fes.$domain/api/v1/client-configuration?domain=$domain" else null*/ domainOrgRulesViewModel.fetchOrgRules(account, uuid) } else { showContent() diff --git a/FlowCrypt/src/main/res/values/strings.xml b/FlowCrypt/src/main/res/values/strings.xml index 5a66c3cc4b..c12d91b900 100644 --- a/FlowCrypt/src/main/res/values/strings.xml +++ b/FlowCrypt/src/main/res/values/strings.xml @@ -527,4 +527,5 @@ 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 + Your FES server has wrong configuration. Please ask your systems administrator or help desk From 1efe1752b52e9437be868ad1a5b1fbbfd2851e76 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Mon, 26 Jul 2021 15:08:15 +0300 Subject: [PATCH 03/20] Added handling 'retry' for "https://fes.$domain/api/v1/client-configuration?domain=$domain".| #1202 --- .../email/ui/activity/fragment/MainSignInFragment.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 8aa1f57e5b..b86150d113 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 @@ -79,6 +79,7 @@ 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() @@ -157,7 +158,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 + ) } } } @@ -176,6 +181,7 @@ class MainSignInFragment : BaseSingInFragment() { TwoWayDialogFragment.RESULT_OK -> { val account = googleSignInAccount?.account?.name ?: return orgRules = null + fesUrl = null checkFesServerViewModel.checkFesServerAvailability(account) } } @@ -258,6 +264,7 @@ class MainSignInFragment : BaseSingInFragment() { onSignSuccess(googleSignInAccount) } else { orgRules = null + fesUrl = null checkFesServerViewModel.checkFesServerAvailability(account) } } else { @@ -453,10 +460,11 @@ class MainSignInFragment : BaseSingInFragment() { 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 = "https://fes.$domain/api/v1/client-configuration?domain=$domain" + fesUrl = fesUrl ) } } else { From 826a7942518037f9638bae568c395aef948ef985 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Mon, 26 Jul 2021 15:29:17 +0300 Subject: [PATCH 04/20] Added handling 'retry' for "service == enterprise-server".| #1202 --- .../email/ui/activity/fragment/MainSignInFragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 b86150d113..3eb86db312 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 @@ -146,9 +146,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) } } @@ -179,9 +179,9 @@ class MainSignInFragment : BaseSingInFragment() { REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY -> { when (resultCode) { TwoWayDialogFragment.RESULT_OK -> { - val account = googleSignInAccount?.account?.name ?: return orgRules = null fesUrl = null + val account = googleSignInAccount?.account?.name ?: return checkFesServerViewModel.checkFesServerAvailability(account) } } @@ -526,6 +526,7 @@ class MainSignInFragment : BaseSingInFragment() { private fun continueBasedOnFlavorSettings(it: Result) { if (BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE) { + showContent() showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) } else { continueWithRegularFlow() From db85a35472a023f1eab658276f06173bd73389a8 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 15:02:29 +0300 Subject: [PATCH 05/20] Added completed logic to MainSignInFragment.| #1202 --- .../email/api/retrofit/ApiService.kt | 7 +++ .../com/flowcrypt/email/extensions/Context.kt | 12 ++++- .../lifecycle/ConnectionLifecycleObserver.kt | 1 - .../viewmodel/CheckFesServerViewModel.kt | 32 ++++++++++++- .../activity/fragment/MainSignInFragment.kt | 47 ++++++++++--------- .../com/flowcrypt/email/util/GeneralUtil.kt | 28 +++++++++++ .../exception/CommonConnectionException.kt | 3 +- 7 files changed, 103 insertions(+), 27 deletions(-) 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 8b46a23fd0..f2ff2862c0 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 @@ -20,6 +20,7 @@ import com.flowcrypt.email.api.retrofit.response.attester.InitialLegacySubmitRes 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 @@ -164,4 +165,10 @@ interface ApiService { */ @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/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 index 7428908f22..526c9e441d 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/CheckFesServerViewModel.kt @@ -14,7 +14,12 @@ 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 @@ -34,10 +39,35 @@ class CheckFesServerViewModel(application: Application) : BaseAndroidViewModel(a Result.loading(progressMsg = context.getString(R.string.loading)) try { - checkFesServerLiveData.value = repository.checkFes( + 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/ui/activity/fragment/MainSignInFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/MainSignInFragment.kt index 3eb86db312..299c0fdd70 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 @@ -18,7 +18,6 @@ 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.FesServerResponse 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 @@ -49,6 +48,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 @@ -65,7 +65,6 @@ import com.sun.mail.util.MailConnectException import org.pgpainless.util.Passphrase import java.net.HttpURLConnection import java.net.SocketTimeoutException -import java.net.UnknownHostException import java.util.* /** @@ -468,11 +467,7 @@ class MainSignInFragment : BaseSingInFragment() { ) } } else { - continueBasedOnFlavorSettings( - it.copy( - exception = IllegalStateException(getString(R.string.fes_has_wrong_configuration)) - ) - ) + continueBasedOnFlavorSettings() } checkFesServerViewModel.checkFesServerLiveData.value = Result.none() @@ -482,22 +477,22 @@ class MainSignInFragment : BaseSingInFragment() { Result.Status.ERROR -> { showContent() checkFesServerViewModel.checkFesServerLiveData.value = Result.none() + showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) baseActivity.countingIdlingResource.decrementSafely() } Result.Status.EXCEPTION -> { showContent() when (it.exception) { - is UnknownHostException -> { - showInfoDialog( - dialogTitle = "", - dialogMsg = getString(R.string.no_connection_or_server_is_not_reachable), - isCancelable = true - ) - } - - is SocketTimeoutException -> { - continueBasedOnFlavorSettings(it) + is CommonConnectionException -> { + if (it.exception.hasInternetAccess == true) { + continueBasedOnFlavorSettings() + } else { + showDialogWithRetryButton( + getString(R.string.no_connection_or_server_is_not_reachable), + REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY + ) + } } is com.flowcrypt.email.util.exception.ApiException -> { @@ -506,8 +501,8 @@ class MainSignInFragment : BaseSingInFragment() { continueWithRegularFlow() } - in 400..500 -> { - continueBasedOnFlavorSettings(it) + else -> { + continueBasedOnFlavorSettings() } } } @@ -524,10 +519,14 @@ class MainSignInFragment : BaseSingInFragment() { }) } - private fun continueBasedOnFlavorSettings(it: Result) { + private fun continueBasedOnFlavorSettings() { if (BuildConfig.FLAVOR == Constants.FLAVOR_NAME_ENTERPRISE) { - showContent() - showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) + /* + 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() } @@ -702,6 +701,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 = "", 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) From 2c7c78fc060c6322f19c375973138fcbe5f04b04 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 15:13:32 +0300 Subject: [PATCH 06/20] Removed unused resources.| #1202 --- FlowCrypt/src/main/res/values/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/FlowCrypt/src/main/res/values/strings.xml b/FlowCrypt/src/main/res/values/strings.xml index c12d91b900..5a66c3cc4b 100644 --- a/FlowCrypt/src/main/res/values/strings.xml +++ b/FlowCrypt/src/main/res/values/strings.xml @@ -527,5 +527,4 @@ 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 - Your FES server has wrong configuration. Please ask your systems administrator or help desk From db6feda6a8bc27f3b1c1c760778e67e660089ab0 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 15:22:28 +0300 Subject: [PATCH 07/20] Fixed names.| #1202 --- FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt index 8b2cc0fdbe..33aa69bc69 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/Constants.kt @@ -139,7 +139,7 @@ class Constants { ) /** - * Flavours. + * Product Flavors. */ const val FLAVOR_NAME_ENTERPRISE = "enterprise" } From a8992800252b8cb66375045695ac2b61e1031270 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 15:29:56 +0300 Subject: [PATCH 08/20] MainSignInFragment. Fixed showing content if an error was received.| #1202 --- .../flowcrypt/email/ui/activity/fragment/MainSignInFragment.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 299c0fdd70..48be3056d6 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 @@ -482,12 +482,12 @@ class MainSignInFragment : BaseSingInFragment() { } Result.Status.EXCEPTION -> { - showContent() 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 @@ -508,6 +508,7 @@ class MainSignInFragment : BaseSingInFragment() { } else -> { + showContent() showDialogWithRetryButton(it, REQUEST_CODE_RETRY_CHECK_FES_AVAILABILITY) } } From 06e04c6c930ce5aeb7b17ae504815076063c9537 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 15:51:22 +0300 Subject: [PATCH 09/20] ApiService. Renamed API(s).| #1202 --- .../main/java/com/flowcrypt/email/api/retrofit/ApiService.kt | 4 ++-- .../flowcrypt/email/api/retrofit/FlowcryptApiRepository.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 f2ff2862c0..8a434a8c2b 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 @@ -109,7 +109,7 @@ interface ApiService { * @param body POJO model for requests */ @POST(BuildConfig.API_URL + "account/get") - suspend fun getDomainOrgRulesInternal(@Body body: LoginModel): Response + suspend fun getOrgRulesFromFlowCryptComBackend(@Body body: LoginModel): Response /** * This method calls "https://fes.$domain/api/v1/client-configuration?domain=$domain" @@ -117,7 +117,7 @@ interface ApiService { * @param fesUrl URL of FES */ @GET - suspend fun getDomainOrgRules(@Url fesUrl: String): Response + suspend fun getOrgRulesFromFes(@Url fesUrl: String): Response /** * This method calls API "https://flowcrypt.com/attester/initial/legacy_submit" via coroutines 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 ed830fe462..e3d8f9cf3e 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 @@ -61,9 +61,9 @@ class FlowcryptApiRepository : ApiRepository { expectedResultClass = DomainOrgRulesResponse::class.java ) { if (fesUrl != null) { - apiService.getDomainOrgRules(fesUrl = fesUrl) + apiService.getOrgRulesFromFes(fesUrl = fesUrl) } else { - apiService.getDomainOrgRulesInternal(body = loginModel) + apiService.getOrgRulesFromFlowCryptComBackend(body = loginModel) } } } From f93bdcac068086d0aeaf383a9abe8c705699287d Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 16:28:36 +0300 Subject: [PATCH 10/20] Added ClientConfigurationResponse. Refactored code.| #1202 --- .../email/api/retrofit/ApiRepository.kt | 4 +- .../email/api/retrofit/ApiService.kt | 3 +- .../api/retrofit/FlowcryptApiRepository.kt | 9 ++-- .../api/ClientConfigurationResponse.kt | 48 +++++++++++++++++++ .../response/api/DomainOrgRulesResponse.kt | 11 ++--- .../viewmodel/DomainOrgRulesViewModel.kt | 4 +- .../activity/fragment/MainSignInFragment.kt | 10 ++-- 7 files changed, 67 insertions(+), 22 deletions(-) create mode 100644 FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/api/ClientConfigurationResponse.kt 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 312aeaeeae..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,13 +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 @@ -51,7 +51,7 @@ interface ApiRepository : BaseApiRepository { context: Context, loginModel: LoginModel, fesUrl: String? = null - ): Result + ): Result /** * @param context Interface to global information about an application environment. 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 8a434a8c2b..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,6 +11,7 @@ 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 @@ -117,7 +118,7 @@ interface ApiService { * @param fesUrl URL of FES */ @GET - suspend fun getOrgRulesFromFes(@Url fesUrl: String): Response + suspend fun getOrgRulesFromFes(@Url fesUrl: String): Response /** * This method calls API "https://flowcrypt.com/attester/initial/legacy_submit" via coroutines 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 e3d8f9cf3e..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,13 +10,13 @@ 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 @@ -53,13 +53,10 @@ class FlowcryptApiRepository : ApiRepository { context: Context, loginModel: LoginModel, fesUrl: String? - ): Result = + ): Result = withContext(Dispatchers.IO) { val apiService = ApiHelper.getInstance(context).retrofit.create(ApiService::class.java) - getResult( - context = context, - expectedResultClass = DomainOrgRulesResponse::class.java - ) { + getResult(context = context) { if (fesUrl != null) { apiService.getOrgRulesFromFes(fesUrl = fesUrl) } else { 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 b8a39229ee..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 @@ -24,7 +24,7 @@ import com.google.gson.annotations.SerializedName data class DomainOrgRulesResponse constructor( @SerializedName("error") @Expose override val apiError: ApiError? = null, - @SerializedName("domain_org_rules", alternate = ["clientConfiguration"]) + @SerializedName("domain_org_rules") @Expose val orgRules: OrgRules? ) : ApiResponse { constructor(parcel: Parcel) : this( @@ -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/jetpack/viewmodel/DomainOrgRulesViewModel.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/jetpack/viewmodel/DomainOrgRulesViewModel.kt index 062ac2fbc5..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,7 +27,7 @@ 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, fesUrl: String? = null) { 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 48be3056d6..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 @@ -18,6 +18,8 @@ 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 @@ -592,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() From ed577cffd82907aa3240b097c17ad2044a1e4459 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 27 Jul 2021 18:08:51 +0300 Subject: [PATCH 11/20] SignInActivityEnterpriseTest. Added test request timeout.| #1202 --- .../java/com/flowcrypt/email/base/BaseTest.kt | 8 ++++++++ .../enterprise/SignInActivityEnterpriseTest.kt | 15 +++++++++++++++ 2 files changed, 23 insertions(+) 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..4f43d9a5d9 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 @@ -214,6 +214,20 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { .check(matches(not(isDisplayed()))) } + @Test + fun testFesAvailabilityRequestTimeOut() { + try { + changeConnectionState(false) + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) + isDialogWithTextDisplayed( + decorView = decorView, + message = getResString(R.string.no_connection_or_server_is_not_reachable) + ) + } finally { + changeConnectionState(true) + } + } + 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 +250,7 @@ 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_REQUEST_TIME_OUT = "fes_request_timeout@flowcrypt.test" private val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, From 26e536d127d409f2deafebbb1b79de20179d4cdb Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 11:35:17 +0300 Subject: [PATCH 12/20] Fixed openssl-ca.cnf.| #1202 --- .../androidTest/resources/ssl/openssl-ca.cnf | 420 ++++++------------ 1 file changed, 125 insertions(+), 295 deletions(-) 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) From 2e2f0aecfb6f94fc0234f4c513144e7247917792 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 12:27:58 +0300 Subject: [PATCH 13/20] Regen a cert for localhost to allow https connection for *.localhost.| #1202 --- .../resources/ssl/localhost-cert.pem | 109 +++++++++--------- .../resources/ssl/localhost-key.pem | 52 ++++----- .../resources/ssl/localhost-req.pem | 28 ++--- .../androidTest/resources/ssl/openssl-req.cnf | 6 +- .../resources/ssl/server_combined.pem | 87 +++++++------- docker-mailserver/config/ssl/demoCA/index.txt | 2 +- ...4AF21E00D03265899A7406166CD11402A576F6.pem | 109 +++++++++--------- 7 files changed, 200 insertions(+), 193 deletions(-) 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-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----- -MIIEFDCCAvygAwIBAgIUeUryHgDQMmWJmnQGFmzRFAKldvYwDQYJKoZIhvcNAQEL +MIIEITCCAwmgAwIBAgIUeUryHgDQMmWJmnQGFmzRFAKldvYwDQYJKoZIhvcNAQEL BQAwfDELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBURlYnVnMRIwEAYDVQQKDAlGbG93 Q3J5cHQxCzAJBgNVBAsMAkNBMRcwFQYDVQQDDA5mbG93Y3J5cHQudGVzdDEjMCEG -CSqGSIb3DQEJARYUYWRtaW5AZmxvd2NyeXB0LnRlc3QwHhcNMjEwNTAzMDcxMDIx -WhcNMzEwNTAxMDcxMDIxWjBqMQswCQYDVQQGEwJDWjEOMAwGA1UECAwFRGVidWcx +CSqGSIb3DQEJARYUYWRtaW5AZmxvd2NyeXB0LnRlc3QwHhcNMjEwNzI4MDgzODE3 +WhcNMzEwNzI2MDgzODE3WjBqMQswCQYDVQQGEwJDWjEOMAwGA1UECAwFRGVidWcx EjAQBgNVBAoMCUZsb3dDcnlwdDESMBAGA1UEAwwJbG9jYWxob3N0MSMwIQYJKoZI hvcNAQkBFhRhZG1pbkBmbG93Y3J5cHQudGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAMf/jjXDYTimPd/yTu6z27RDd18JaMu+toJ6F0v4uib6OSfT -5Ol1/zTO9AY/X4p9P2PO2sgPQ4TeRdCcYYMtnp6OBIvj2dg5Pz0lXMS4D57rUccv -WhZnx3wGtNJzNfYa7zgURZa/OyvKjvrDgvNMpFabt1CQ04mySuh9owYqRDGy8Go/ -0qvwHEeyzb6o5DPqLlrlbGNTxhGRIZU1Mxz7sGheemofm7aT53bGE8OTLIUNhGYt -sfhP+ePy3ryahWHFxzlvKkbV3juvRuAIlPt63kyS/kEpUN5rMupbsJBZlYQpAbB3 -TFpISKMzzwpdGs2GwwEXx9OYbF5W7+R0oCc/jsMCAwEAAaOBnzCBnDAJBgNVHRME +ggEPADCCAQoCggEBAK24yVxZh3oIVp0kU/LEaz3OnFcj5++9+M7Wu3pFjeaGfMW0 +CC7Fczi/vz6rXXsJyKVAy1jWQX8Gj5RTBmG7oCgGd8XOJXyY/vlotQzScHlc9XtD +mdhR7X6zje0yYSfYi1buQqaoy/URcH5uueeIwC/mDkhP+4GlSsd+x6so/RTEOdpo +y/6Zm2EM5K24W5WTvcrqONBbDe2WV9uzadSTe0e+nFvdbmxth4mtkG+5PJaArAlS +VL3wSYvIZhwo7XFkwOVgJDkecCh3eEBIpKaZfJitQBp2eVEkQy7+GXJDajbkWRNI +k6NRvPiFmNVCR0tqu8DPp3/I41zAhdmjJh3o4qkCAwEAAaOBrDCBqTAJBgNVHRME AjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0 -ZTAdBgNVHQ4EFgQUjyx8Vy4eA2Xvhn6uf7sZDgySmSQwHwYDVR0jBBgwFoAUVxtw -lLPktSn6UZw/d1qPFfiCGrYwCwYDVR0PBAQDAgXgMBQGA1UdEQQNMAuCCWxvY2Fs -aG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAxRMmp7h6pdD8CH6dmmXRlbMaroWJv+J0 -ZYJijf7MgIlWZoIpvCEv7r5EG50ZXWUgQuoXYclFm+6EVCKJB2RAD0vqKTCIDG5E -fl0nPgMVpuT+B21YJ/Aw2izvZGpT0uFCh89gA7HmTwYLuGDGlCvb+5H6iYbVtwZN -SFzYFX8f1IVR72Jox30+t8YIKFnKYgI5HJ+uBlIPvlJwQBNB7RniD/zCUBV3t1wN -XeucO2D4/qTNfo9j3DNfAgNL3Uj7NojOH4AKT6dZlejoAXrQ11Z3jCMjYEha/sJy -qKtTDlbQ0XAt0yfgmYdEEUD+xzf0PAQi5n0JpBKeOqCMvfpqPBDUjg== +ZTAdBgNVHQ4EFgQUuRH49yYdZJ8NK49/HmnwuJs0P5QwHwYDVR0jBBgwFoAUVxtw +lLPktSn6UZw/d1qPFfiCGrYwCwYDVR0PBAQDAgXgMCEGA1UdEQQaMBiCCWxvY2Fs +aG9zdIILKi5sb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBABcrZHifZ9e0bkl9 +V0u0KmT0wweL2scVoerPevoG35kXBjDQz6DFsrCd45U0LR15ebcYssu4NJGKGT1+ +kAgGQgKLm1a3ee9Qf7elCrmUoFGC06ld7HkuUllFn6Qf8R3STy0lZ7NF7lyVQLJT +pTQLxiSN7QKykrXbFCSEoWqi0VNSZ6WPMqgXHH7nC5V9xs5MAxJSf/SJcnwFEvUz +f5g6zp1aLSmZVk8VzCaHdbr6EcFDChzvS5ltvbqZJ5T1KgHlzE4t3OhpwIZZBnL0 +eKjwy0EztEmtRlrlH/LTQD9mmT7kJ3qSnC077VUxzSu8ss+7FBPz3tV7Fnd5OCSr +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/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----- From 3ccd2d381f004feaa641687dc3adab5aa9e5ac8d Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 13:29:58 +0300 Subject: [PATCH 14/20] Added one more case to test "fes availability == false && request timeout == true && no connection == false". Refactored code.| #1202 --- .../pgp/fes@flowcrypt.test_prv_decrypted.asc | 15 ++ .../SignInActivityEnterpriseTest.kt | 133 +++++++++++++----- 2 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 FlowCrypt/src/androidTest/assets/pgp/fes@flowcrypt.test_prv_decrypted.asc 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/ui/activity/enterprise/SignInActivityEnterpriseTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/activity/enterprise/SignInActivityEnterpriseTest.kt index 4f43d9a5d9..b97ff24691 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 @@ -22,6 +22,7 @@ import com.flowcrypt.email.api.retrofit.ApiHelper 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.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 @@ -215,10 +216,10 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } @Test - fun testFesAvailabilityRequestTimeOut() { + fun testFesAvailabilityNoConnection() { try { changeConnectionState(false) - setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NO_CONNECTION)) isDialogWithTextDisplayed( decorView = decorView, message = getResString(R.string.no_connection_or_server_is_not_reachable) @@ -228,6 +229,18 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } } + @Test + fun testFesAvailabilityRequestTimeOut() { + try { + fesTimeOutEnabled = true + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } finally { + fesTimeOutEnabled = false + } + } + 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/" @@ -250,7 +263,8 @@ 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_REQUEST_TIME_OUT = "fes_request_timeout@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 val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, @@ -278,6 +292,26 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { message = EKM_ERROR ) + private val EKM_FES_RESPONSE = EkmPrivateKeysResponse( + privateKeys = listOf( + Key( + TestGeneralUtil.readFileFromAssetsAsString("pgp/fes@flowcrypt.test_prv_decrypted.asc") + ) + ) + ) + + private val FES_SUCCESS_RESPONSE = FesServerResponse( + apiError = null, + vendor = "FlowCrypt", + service = "enterprise-server", + orgId = "localhost", + version = "2021", + endUserApiVersion = "v1", + adminApiVersion = "v1" + ) + + internal var fesTimeOutEnabled = false + @get:ClassRule @JvmStatic val mockWebServerRule = @@ -286,37 +320,12 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { 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("/api/")) { + return handleFesAvailabilityAPI(gson) } - 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" - ) - ) - ) - ) - ) - ) + if (request.path?.startsWith("/ekm") == true) { + handleEkmAPI(request, gson)?.let { return it } } val model = @@ -334,6 +343,55 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } }) + private fun handleFesAvailabilityAPI(gson: Gson) = if (fesTimeOutEnabled) { + val delayInMilliseconds = 6000 + val initialTimeMillis = System.currentTimeMillis() + while (System.currentTimeMillis() - initialTimeMillis <= delayInMilliseconds + && fesTimeOutEnabled + ) { + Thread.sleep(100) + } + MockResponse().setResponseCode(404) + } else { + MockResponse().setResponseCode(200) + .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) + } + + 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) @@ -349,8 +407,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { ) ) - EMAIL_MUST_AUTOGEN_PASS_PHRASE_QUIETLY_EXISTED - -> return successMockResponseForOrgRules( + EMAIL_MUST_AUTOGEN_PASS_PHRASE_QUIETLY_EXISTED -> return successMockResponseForOrgRules( gson = gson, orgRules = OrgRules( flags = listOf( @@ -418,6 +475,14 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { ) ) + EMAIL_FES_REQUEST_TIME_OUT -> return successMockResponseForOrgRules( + gson = gson, + orgRules = OrgRules( + flags = ACCEPTED_ORG_RULES, + keyManagerUrl = EMAIL_EKM_URL_SUCCESS, + ) + ) + else -> return MockResponse().setResponseCode(404) } } From 0dca576d355779db282afb4ec2fe151507e2fe1a Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 14:51:29 +0300 Subject: [PATCH 15/20] Added testing "fes availability == false && connection_issue == false && HTTP status code == 404".| #1202 --- .../SignInActivityEnterpriseTest.kt | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) 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 b97ff24691..04b3a111ad 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 @@ -216,7 +216,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } @Test - fun testFesAvailabilityNoConnection() { + fun testFesAvailabilityServerDownNoConnection() { try { changeConnectionState(false) setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NO_CONNECTION)) @@ -230,7 +230,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } @Test - fun testFesAvailabilityRequestTimeOut() { + fun testFesAvailabilityServerUpRequestTimeOut() { try { fesTimeOutEnabled = true setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) @@ -241,6 +241,18 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } } + @Test + fun testFesServerUpHasConnectionHttpCode404() { + try { + fesExpect404 = true + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_404)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } finally { + fesExpect404 = false + } + } + 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/" @@ -265,6 +277,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { "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 val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, @@ -311,6 +324,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { ) internal var fesTimeOutEnabled = false + internal var fesExpect404 = false @get:ClassRule @JvmStatic @@ -343,18 +357,24 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } }) - private fun handleFesAvailabilityAPI(gson: Gson) = if (fesTimeOutEnabled) { - val delayInMilliseconds = 6000 - val initialTimeMillis = System.currentTimeMillis() - while (System.currentTimeMillis() - initialTimeMillis <= delayInMilliseconds - && fesTimeOutEnabled - ) { - Thread.sleep(100) + private fun handleFesAvailabilityAPI(gson: Gson): MockResponse { + return if (fesTimeOutEnabled) { + val delayInMilliseconds = 6000 + val initialTimeMillis = System.currentTimeMillis() + while (System.currentTimeMillis() - initialTimeMillis <= delayInMilliseconds + && fesTimeOutEnabled + ) { + Thread.sleep(100) + } + MockResponse().setResponseCode(404) + } else { + if (fesExpect404) { + MockResponse().setResponseCode(404) + } else { + MockResponse().setResponseCode(200) + .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) + } } - MockResponse().setResponseCode(404) - } else { - MockResponse().setResponseCode(200) - .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) } private fun handleEkmAPI(request: RecordedRequest, gson: Gson): MockResponse? { @@ -475,7 +495,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { ) ) - EMAIL_FES_REQUEST_TIME_OUT -> return successMockResponseForOrgRules( + EMAIL_FES_REQUEST_TIME_OUT, EMAIL_FES_HTTP_404 -> return successMockResponseForOrgRules( gson = gson, orgRules = OrgRules( flags = ACCEPTED_ORG_RULES, From dc3a10331417336f35fac573068c3def282ec19c Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 14:57:47 +0300 Subject: [PATCH 16/20] Added testing "fes availability == false && connection_issue == false && HTTP status code != 404".| #1202 --- .../SignInActivityEnterpriseTest.kt | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) 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 04b3a111ad..ba9ad7f115 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 @@ -253,6 +253,18 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } } + @Test + fun testFesServerUpHasConnectionHttpCodeNotSuccess() { + try { + fesExpectNot404AndNotSuccess = true + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } finally { + fesExpectNot404AndNotSuccess = false + } + } + 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/" @@ -278,6 +290,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { 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 val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, @@ -325,6 +338,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { internal var fesTimeOutEnabled = false internal var fesExpect404 = false + internal var fesExpectNot404AndNotSuccess = false @get:ClassRule @JvmStatic @@ -368,11 +382,19 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } MockResponse().setResponseCode(404) } else { - if (fesExpect404) { - MockResponse().setResponseCode(404) - } else { - MockResponse().setResponseCode(200) - .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) + when { + fesExpect404 -> { + MockResponse().setResponseCode(404) + } + + fesExpectNot404AndNotSuccess -> { + MockResponse().setResponseCode(500) + } + + else -> { + MockResponse().setResponseCode(200) + .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) + } } } } @@ -495,7 +517,9 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { ) ) - EMAIL_FES_REQUEST_TIME_OUT, EMAIL_FES_HTTP_404 -> return successMockResponseForOrgRules( + EMAIL_FES_REQUEST_TIME_OUT, + EMAIL_FES_HTTP_404, + EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS -> return successMockResponseForOrgRules( gson = gson, orgRules = OrgRules( flags = ACCEPTED_ORG_RULES, From b396aaa401d3e252b6e3bd8bef0091591b72ab81 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 15:07:09 +0300 Subject: [PATCH 17/20] Added testing "fes availability == true && service != "enterprise-server"".| #1202 --- .../SignInActivityEnterpriseTest.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) 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 ba9ad7f115..4f70c09987 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 @@ -265,6 +265,18 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } } + @Test + fun testFesServerUpNotEnterpriseServer() { + try { + fesNotEnterpriseServer = true + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NOT_ENTERPRISE_SERVER)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } finally { + fesNotEnterpriseServer = false + } + } + 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/" @@ -291,6 +303,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { 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 val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, @@ -339,6 +352,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { internal var fesTimeOutEnabled = false internal var fesExpect404 = false internal var fesExpectNot404AndNotSuccess = false + internal var fesNotEnterpriseServer = false @get:ClassRule @JvmStatic @@ -391,6 +405,11 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { MockResponse().setResponseCode(500) } + fesNotEnterpriseServer -> { + MockResponse().setResponseCode(200) + .setBody(gson.toJson(FES_SUCCESS_RESPONSE.copy(service = "hello"))) + } + else -> { MockResponse().setResponseCode(200) .setBody(gson.toJson(FES_SUCCESS_RESPONSE)) @@ -519,7 +538,8 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { EMAIL_FES_REQUEST_TIME_OUT, EMAIL_FES_HTTP_404, - EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS -> return successMockResponseForOrgRules( + EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS, + EMAIL_FES_NOT_ENTERPRISE_SERVER -> return successMockResponseForOrgRules( gson = gson, orgRules = OrgRules( flags = ACCEPTED_ORG_RULES, From ec9fff39cc24259e35a8665e663626499cf4592f Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 15:51:28 +0300 Subject: [PATCH 18/20] Added testing "fes availability == true && service == "enterprise-server" && GET "https://fes.$domain/api/v1/client-configuration?domain=$domain" == SUCCESS".| #1202 --- .../SignInActivityEnterpriseTest.kt | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) 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 4f70c09987..d5899afec7 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,6 +20,7 @@ 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 @@ -30,7 +31,6 @@ import com.flowcrypt.email.api.retrofit.response.model.OrgRules import com.flowcrypt.email.junit.annotations.NotReadyForCI import com.flowcrypt.email.rules.ClearAppSettingsRule import com.flowcrypt.email.rules.FlowCryptMockWebServerRule -import com.flowcrypt.email.rules.RetryRule import com.flowcrypt.email.rules.ScreenshotTestRule import com.flowcrypt.email.ui.activity.CreateOrImportKeyActivity import com.flowcrypt.email.ui.activity.SignInActivity @@ -75,7 +75,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain .outerRule(ClearAppSettingsRule()) - .around(RetryRule.DEFAULT) + //.around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) @@ -277,6 +277,20 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } } + @Test + fun testFesServerUpGetClientConfigurationSuccess() { + try { + setupAndClickSignInButton( + genMockGoogleSignInAccountJson( + EMAIL_FES_CLIENT_CONFIGURATION_SUCCESS + ) + ) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } finally { + } + } + 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/" @@ -304,6 +318,8 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { 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 val ACCEPTED_ORG_RULES = listOf( OrgRules.DomainRule.PRV_AUTOIMPORT_OR_AUTOGEN, @@ -362,8 +378,15 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { val gson = ApiHelper.getInstance(InstrumentationRegistry.getInstrumentation().targetContext).gson - if (request.path.equals("/api/")) { - return handleFesAvailabilityAPI(gson) + if (request.path?.startsWith("/api") == true) { + if (request.path.equals("/api/")) { + //https://fes.localhost:1212/api/v1/client-configuration?domain=localhost:1212 + return handleFesAvailabilityAPI(gson) + } + + if (request.path.equals("/api/v1/client-configuration?domain=localhost:1212")) { + return handleClientConfigurationAPI(gson) + } } if (request.path?.startsWith("/ekm") == true) { @@ -418,6 +441,20 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } } + private fun handleClientConfigurationAPI(gson: Gson): MockResponse { + return MockResponse().setResponseCode(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) From cd93bab88cbf8f0f89afc79feeb2a8db783e77c7 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Wed, 28 Jul 2021 16:01:34 +0300 Subject: [PATCH 19/20] Added testing "fes availability == true && service == "enterprise-server" && GET "https://fes.$domain/api/v1/client-configuration?domain=$domain" == FAILED".| #1202 --- .../SignInActivityEnterpriseTest.kt | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) 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 d5899afec7..766eb99400 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 @@ -31,12 +31,14 @@ import com.flowcrypt.email.api.retrofit.response.model.OrgRules import com.flowcrypt.email.junit.annotations.NotReadyForCI import com.flowcrypt.email.rules.ClearAppSettingsRule import com.flowcrypt.email.rules.FlowCryptMockWebServerRule +import com.flowcrypt.email.rules.RetryRule import com.flowcrypt.email.rules.ScreenshotTestRule import com.flowcrypt.email.ui.activity.CreateOrImportKeyActivity 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 @@ -75,7 +77,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { @get:Rule var ruleChain: TestRule = RuleChain .outerRule(ClearAppSettingsRule()) - //.around(RetryRule.DEFAULT) + .around(RetryRule.DEFAULT) .around(activityScenarioRule) .around(ScreenshotTestRule()) @@ -279,15 +281,24 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { @Test fun testFesServerUpGetClientConfigurationSuccess() { + expectClientConfigurationFailed = false + setupAndClickSignInButton( + genMockGoogleSignInAccountJson(EMAIL_FES_CLIENT_CONFIGURATION_SUCCESS) + ) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) + } + + @Test + fun testFesServerUpGetClientConfigurationFailed() { try { + expectClientConfigurationFailed = true setupAndClickSignInButton( - genMockGoogleSignInAccountJson( - EMAIL_FES_CLIENT_CONFIGURATION_SUCCESS - ) + genMockGoogleSignInAccountJson(EMAIL_FES_CLIENT_CONFIGURATION_FAILED) ) - onView(withText(R.string.set_pass_phrase)) - .check(matches(isDisplayed())) + isDialogWithTextDisplayed(decorView, ApiException(ApiError(code = 403, msg = "")).message!!) } finally { + expectClientConfigurationFailed = false } } @@ -320,6 +331,8 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { 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, @@ -369,6 +382,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { internal var fesExpect404 = false internal var fesExpectNot404AndNotSuccess = false internal var fesNotEnterpriseServer = false + internal var expectClientConfigurationFailed = false @get:ClassRule @JvmStatic @@ -442,7 +456,7 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { } private fun handleClientConfigurationAPI(gson: Gson): MockResponse { - return MockResponse().setResponseCode(200) + return MockResponse().setResponseCode(if (expectClientConfigurationFailed) 403 else 200) .setBody( gson.toJson( ClientConfigurationResponse( From 081d472789d3c20583862c06c3002e4ce6373164 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Thu, 29 Jul 2021 10:25:08 +0300 Subject: [PATCH 20/20] Improved tests in SignInActivityEnterpriseTest.| #1202 --- .../SignInActivityEnterpriseTest.kt | 564 +++++++++--------- 1 file changed, 266 insertions(+), 298 deletions(-) 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 766eb99400..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 @@ -45,10 +45,10 @@ 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 @@ -69,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)) @@ -233,55 +272,34 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { @Test fun testFesAvailabilityServerUpRequestTimeOut() { - try { - fesTimeOutEnabled = true - setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) - onView(withText(R.string.set_pass_phrase)) - .check(matches(isDisplayed())) - } finally { - fesTimeOutEnabled = false - } + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_REQUEST_TIME_OUT)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) } @Test fun testFesServerUpHasConnectionHttpCode404() { - try { - fesExpect404 = true - setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_404)) - onView(withText(R.string.set_pass_phrase)) - .check(matches(isDisplayed())) - } finally { - fesExpect404 = false - } + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_404)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) } @Test fun testFesServerUpHasConnectionHttpCodeNotSuccess() { - try { - fesExpectNot404AndNotSuccess = true - setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS)) - onView(withText(R.string.set_pass_phrase)) - .check(matches(isDisplayed())) - } finally { - fesExpectNot404AndNotSuccess = false - } + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_HTTP_NOT_404_NOT_SUCCESS)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) } @Test fun testFesServerUpNotEnterpriseServer() { - try { - fesNotEnterpriseServer = true - setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NOT_ENTERPRISE_SERVER)) - onView(withText(R.string.set_pass_phrase)) - .check(matches(isDisplayed())) - } finally { - fesNotEnterpriseServer = false - } + setupAndClickSignInButton(genMockGoogleSignInAccountJson(EMAIL_FES_NOT_ENTERPRISE_SERVER)) + onView(withText(R.string.set_pass_phrase)) + .check(matches(isDisplayed())) } @Test fun testFesServerUpGetClientConfigurationSuccess() { - expectClientConfigurationFailed = false setupAndClickSignInButton( genMockGoogleSignInAccountJson(EMAIL_FES_CLIENT_CONFIGURATION_SUCCESS) ) @@ -291,17 +309,220 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { @Test fun testFesServerUpGetClientConfigurationFailed() { - try { - expectClientConfigurationFailed = true - setupAndClickSignInButton( - genMockGoogleSignInAccountJson(EMAIL_FES_CLIENT_CONFIGURATION_FAILED) + 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, + ) + ) ) - isDialogWithTextDisplayed(decorView, ApiException(ApiError(code = 403, msg = "")).message!!) - } finally { - expectClientConfigurationFailed = false + ) + } + + 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/" @@ -377,258 +598,5 @@ class SignInActivityEnterpriseTest : BaseSignActivityTest() { endUserApiVersion = "v1", adminApiVersion = "v1" ) - - internal var fesTimeOutEnabled = false - internal var fesExpect404 = false - internal var fesExpectNot404AndNotSuccess = false - internal var fesNotEnterpriseServer = false - internal var expectClientConfigurationFailed = false - - @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?.startsWith("/api") == true) { - if (request.path.equals("/api/")) { - //https://fes.localhost:1212/api/v1/client-configuration?domain=localhost:1212 - 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) - } - }) - - private fun handleFesAvailabilityAPI(gson: Gson): MockResponse { - return if (fesTimeOutEnabled) { - val delayInMilliseconds = 6000 - val initialTimeMillis = System.currentTimeMillis() - while (System.currentTimeMillis() - initialTimeMillis <= delayInMilliseconds - && fesTimeOutEnabled - ) { - Thread.sleep(100) - } - MockResponse().setResponseCode(404) - } else { - when { - fesExpect404 -> { - MockResponse().setResponseCode(404) - } - - fesExpectNot404AndNotSuccess -> { - MockResponse().setResponseCode(500) - } - - fesNotEnterpriseServer -> { - 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 (expectClientConfigurationFailed) 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 - ) - ) - ) } }