Skip to content
Snippets Groups Projects
Unverified Commit 967ba51e authored by Mohamed Metwalli's avatar Mohamed Metwalli Committed by GitHub
Browse files

Error message after positive test result (EXPOSUREAPP-4515) (#2161)

* Replace with lambda

* Add specific error handling for tan retrieval

* Extend from CwaClientError

* Handle BadRequestException for Tan and keys exchange

* lint

* lint

* Fix typo

* Add unit tests

* Check for cause
parent eb8070fa
No related branches found
No related tags found
No related merge requests found
package de.rki.coronawarnapp.exception
import android.content.Context
import de.rki.coronawarnapp.R
import de.rki.coronawarnapp.exception.http.CwaClientError
import de.rki.coronawarnapp.util.HasHumanReadableError
import de.rki.coronawarnapp.util.HumanReadableError
/**
* Specific Exception type to identify an error case happening when TAN is retrieved.
* @see <a href="https://jira-ibs.wbs.net.sap/browse/EXPOSUREAPP-4515">EXPOSUREAPP-4515</a>
*/
class TanPairingException(
override val code: Int,
override val message: String?,
override val cause: Throwable?
) : CwaClientError(code, message, cause), HasHumanReadableError {
override fun toHumanReadableError(context: Context): HumanReadableError {
return HumanReadableError(
title = context.getString(R.string.submission_error_dialog_web_paring_invalid_title),
description = context.getString(R.string.submission_error_dialog_web_paring_invalid_body)
)
}
}
...@@ -28,11 +28,7 @@ class HttpModule { ...@@ -28,11 +28,7 @@ class HttpModule {
fun defaultHttpClient(): OkHttpClient { fun defaultHttpClient(): OkHttpClient {
val interceptors: List<Interceptor> = listOf( val interceptors: List<Interceptor> = listOf(
WebSecurityVerificationInterceptor(), WebSecurityVerificationInterceptor(),
HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) }.apply {
override fun log(message: String) {
Timber.tag("OkHttp").v(message)
}
}).apply {
if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY) if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY)
}, },
RetryInterceptor(), RetryInterceptor(),
......
package de.rki.coronawarnapp.playbook package de.rki.coronawarnapp.playbook
import de.rki.coronawarnapp.exception.TanPairingException
import de.rki.coronawarnapp.exception.http.BadRequestException
import de.rki.coronawarnapp.submission.server.SubmissionServer import de.rki.coronawarnapp.submission.server.SubmissionServer
import de.rki.coronawarnapp.util.formatter.TestResult import de.rki.coronawarnapp.util.formatter.TestResult
import de.rki.coronawarnapp.verification.server.VerificationKeyType import de.rki.coronawarnapp.verification.server.VerificationKeyType
...@@ -90,23 +92,46 @@ class DefaultPlaybook @Inject constructor( ...@@ -90,23 +92,46 @@ class DefaultPlaybook @Inject constructor(
// fake verification // fake verification
ignoreExceptions { verificationServer.retrieveTanFake() } ignoreExceptions { verificationServer.retrieveTanFake() }
// real submission // submitKeysToServer could throw BadRequestException too.
if (authCode != null) { try {
val serverSubmissionData = SubmissionServer.SubmissionData( // real submission
authCode = authCode, if (authCode != null) {
keyList = data.temporaryExposureKeys, val serverSubmissionData = SubmissionServer.SubmissionData(
consentToFederation = data.consentToFederation, authCode = authCode,
visistedCountries = data.visistedCountries keyList = data.temporaryExposureKeys,
consentToFederation = data.consentToFederation,
visistedCountries = data.visistedCountries
)
submissionServer.submitKeysToServer(serverSubmissionData)
coroutineScope.launch { followUpPlaybooks() }
} else {
submissionServer.submitKeysToServerFake()
coroutineScope.launch { followUpPlaybooks() }
propagateException(wrapException(exception))
}
} catch (exception: BadRequestException) {
propagateException(
TanPairingException(
code = exception.statusCode,
message = "Invalid payload or missing header",
cause = exception
)
) )
submissionServer.submitKeysToServer(serverSubmissionData)
coroutineScope.launch { followUpPlaybooks() }
} else {
submissionServer.submitKeysToServerFake()
coroutineScope.launch { followUpPlaybooks() }
propagateException(exception)
} }
} }
/**
* Distinguish BadRequestException to present more insightful message to the end user
*/
private fun wrapException(exception: Exception?) = when (exception) {
is BadRequestException -> TanPairingException(
code = exception.statusCode,
message = "Tan has been retrieved before for this registration token",
cause = exception
)
else -> exception
}
private suspend fun dummy(launchFollowUp: Boolean) { private suspend fun dummy(launchFollowUp: Boolean) {
// fake verification // fake verification
ignoreExceptions { verificationServer.retrieveTanFake() } ignoreExceptions { verificationServer.retrieveTanFake() }
......
...@@ -421,7 +421,7 @@ object LocalData { ...@@ -421,7 +421,7 @@ object LocalData {
) )
} }
fun devicePairingSuccessfulTimestamp(): Long? { fun devicePairingSuccessfulTimestamp(): Long {
return getSharedPreferenceInstance().getLong( return getSharedPreferenceInstance().getLong(
CoronaWarnApplication.getAppContext() CoronaWarnApplication.getAppContext()
.getString(R.string.preference_device_pairing_successful_time), .getString(R.string.preference_device_pairing_successful_time),
......
package de.rki.coronawarnapp.http.playbook package de.rki.coronawarnapp.http.playbook
import de.rki.coronawarnapp.exception.TanPairingException
import de.rki.coronawarnapp.exception.http.BadRequestException
import de.rki.coronawarnapp.playbook.DefaultPlaybook import de.rki.coronawarnapp.playbook.DefaultPlaybook
import de.rki.coronawarnapp.playbook.Playbook import de.rki.coronawarnapp.playbook.Playbook
import de.rki.coronawarnapp.submission.server.SubmissionServer import de.rki.coronawarnapp.submission.server.SubmissionServer
...@@ -8,6 +10,7 @@ import de.rki.coronawarnapp.verification.server.VerificationKeyType ...@@ -8,6 +10,7 @@ import de.rki.coronawarnapp.verification.server.VerificationKeyType
import de.rki.coronawarnapp.verification.server.VerificationServer import de.rki.coronawarnapp.verification.server.VerificationServer
import io.kotest.assertions.throwables.shouldThrow import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.clearAllMocks import io.mockk.clearAllMocks
import io.mockk.coEvery import io.mockk.coEvery
...@@ -102,6 +105,44 @@ class DefaultPlaybookTest : BaseTest() { ...@@ -102,6 +105,44 @@ class DefaultPlaybookTest : BaseTest() {
} }
} }
@Test
fun `tan retrieval throws human readable exception`(): Unit = runBlocking {
coEvery { verificationServer.retrieveTan(any()) } throws BadRequestException(null)
try {
createPlaybook().submit(
Playbook.SubmissionData(
registrationToken = "token",
temporaryExposureKeys = listOf(),
consentToFederation = true,
visistedCountries = listOf("DE")
)
)
} catch (e: Exception) {
e.shouldBeInstanceOf<TanPairingException>()
e.cause.shouldBeInstanceOf<BadRequestException>()
e.message shouldBe "Tan has been retrieved before for this registration token"
}
}
@Test
fun `keys submission throws human readable exception`(): Unit = runBlocking {
coEvery { submissionServer.submitKeysToServer(any()) } throws BadRequestException(null)
try {
createPlaybook().submit(
Playbook.SubmissionData(
registrationToken = "token",
temporaryExposureKeys = listOf(),
consentToFederation = true,
visistedCountries = listOf("DE")
)
)
} catch (e: Exception) {
e.shouldBeInstanceOf<TanPairingException>()
e.cause.shouldBeInstanceOf<BadRequestException>()
e.message shouldBe "Invalid payload or missing header"
}
}
@Test @Test
fun `submission matches request pattern despite missing authcode`(): Unit = runBlocking { fun `submission matches request pattern despite missing authcode`(): Unit = runBlocking {
coEvery { verificationServer.retrieveTan(any()) } throws TestException() coEvery { verificationServer.retrieveTan(any()) } throws TestException()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment