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 {
fun defaultHttpClient(): OkHttpClient {
val interceptors: List<Interceptor> = listOf(
WebSecurityVerificationInterceptor(),
HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Timber.tag("OkHttp").v(message)
}
}).apply {
HttpLoggingInterceptor { message -> Timber.tag("OkHttp").v(message) }.apply {
if (BuildConfig.DEBUG) setLevel(HttpLoggingInterceptor.Level.BODY)
},
RetryInterceptor(),
......
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.util.formatter.TestResult
import de.rki.coronawarnapp.verification.server.VerificationKeyType
......@@ -90,23 +92,46 @@ class DefaultPlaybook @Inject constructor(
// fake verification
ignoreExceptions { verificationServer.retrieveTanFake() }
// real submission
if (authCode != null) {
val serverSubmissionData = SubmissionServer.SubmissionData(
authCode = authCode,
keyList = data.temporaryExposureKeys,
consentToFederation = data.consentToFederation,
visistedCountries = data.visistedCountries
// submitKeysToServer could throw BadRequestException too.
try {
// real submission
if (authCode != null) {
val serverSubmissionData = SubmissionServer.SubmissionData(
authCode = authCode,
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) {
// fake verification
ignoreExceptions { verificationServer.retrieveTanFake() }
......
......@@ -421,7 +421,7 @@ object LocalData {
)
}
fun devicePairingSuccessfulTimestamp(): Long? {
fun devicePairingSuccessfulTimestamp(): Long {
return getSharedPreferenceInstance().getLong(
CoronaWarnApplication.getAppContext()
.getString(R.string.preference_device_pairing_successful_time),
......
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.Playbook
import de.rki.coronawarnapp.submission.server.SubmissionServer
......@@ -8,6 +10,7 @@ import de.rki.coronawarnapp.verification.server.VerificationKeyType
import de.rki.coronawarnapp.verification.server.VerificationServer
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
import io.mockk.MockKAnnotations
import io.mockk.clearAllMocks
import io.mockk.coEvery
......@@ -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
fun `submission matches request pattern despite missing authcode`(): Unit = runBlocking {
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