Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'origin/fix/5092-Missing-tracing-duratio…
Browse files Browse the repository at this point in the history
…n-on-low-risk-detail-screen' into fix/5092-Missing-tracing-duration-on-low-risk-detail-screen
  • Loading branch information
LukasLechnerDev committed Feb 12, 2021
2 parents 62c877e + 7bfba19 commit 4c11f21
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.rki.coronawarnapp.datadonation

import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import org.joda.time.Instant
import java.util.UUID

@Keep
data class OTPAuthorizationResult(
@SerializedName("uuid")
val uuid: UUID,
@SerializedName("authorized")
val authorized: Boolean,
@SerializedName("redeemedAt")
val redeemedAt: Instant
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.rki.coronawarnapp.datadonation.storage

import de.rki.coronawarnapp.datadonation.OTPAuthorizationResult
import de.rki.coronawarnapp.datadonation.OneTimePassword
import de.rki.coronawarnapp.datadonation.survey.SurveySettings
import javax.inject.Inject
Expand All @@ -10,14 +11,25 @@ class OTPRepository @Inject constructor(
private val surveySettings: SurveySettings
) {

val lastOTP: OneTimePassword?
val otp: OneTimePassword?
get() = surveySettings.oneTimePassword

var otpAuthorizationResult: OTPAuthorizationResult?
get() = surveySettings.otpAuthorizationResult
set(value) {
surveySettings.otpAuthorizationResult = value
// since we have a result from authorization server we must not use the generated otp again
surveySettings.oneTimePassword = null
}

fun generateOTP(): OneTimePassword = OneTimePassword().also {
surveySettings.oneTimePassword = it
// only one otp can be stored at a time - remove authorization result of older otp
surveySettings.otpAuthorizationResult = null
}

fun clear() {
surveySettings.oneTimePassword = null
surveySettings.otpAuthorizationResult = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.datadonation.survey

import android.content.Context
import com.google.gson.Gson
import de.rki.coronawarnapp.datadonation.OTPAuthorizationResult
import de.rki.coronawarnapp.datadonation.OneTimePassword
import de.rki.coronawarnapp.util.di.AppContext
import de.rki.coronawarnapp.util.preferences.clearAndNotify
Expand Down Expand Up @@ -42,7 +43,31 @@ class SurveySettings @Inject constructor(
.putString(KEY_OTP, if (value == null) null else gson.toJson(value))
.apply()

var otpAuthorizationResult: OTPAuthorizationResult?
get() {
try {
val json = preferences.getString(KEY_OTP_RESULT, null)
if (json != null) {
val result = gson.fromJson(json, OTPAuthorizationResult::class.java)
requireNotNull(result.uuid)
requireNotNull(result.authorized)
requireNotNull(result.redeemedAt)
return result
}
return null
} catch (t: Throwable) {
Timber.e(t, "failed to parse OTP from preferences")
return null
}
}
set(value) =
preferences
.edit()
.putString(KEY_OTP_RESULT, if (value == null) null else gson.toJson(value))
.apply()

fun clear() = preferences.clearAndNotify()
}

private const val KEY_OTP = "one_time_password"
private const val KEY_OTP_RESULT = "otp_result"
4 changes: 0 additions & 4 deletions Corona-Warn-App/src/main/res/values-de/legal_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,5 @@
<string name="datadonation_survey_consent_details_title">"Prüfung der Echtheit und Drittlandsübermittlung"</string>
<!-- XTXT: Text for the information box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_text">"Um die Echtheit Ihrer App zu bestätigen, erzeugt Ihr Smartphone eine eindeutige Kennung, die Informationen über die Version Ihres Smartphones und der App enthält. Das ist erforderlich, um zu verhindern, dass Nutzer mehrfach der Befragung teilnehmen und so die Ergebnisse der Befragung verfälschen. Die Kennung wird hier einmalig an Google übermittelt. Dabei kann es auch zu einer Datenübermittlung in die USA kommen. Dort besteht kein dem europäischen Recht angemessenes Datenschutzniveau und Ihre europäischen Datenschutzrechte können eventuell nicht durchgesetzt werden. Insbesondere besteht die Möglichkeit, dass US-Sicherheitsbehörden, auch ohne einen konkreten Verdacht, auf die übermittelten Daten bei Google zugreifen und diese auswerten, beispielsweise indem sie Daten mit anderen Informationen verknüpfen. Dies betrifft nur die an Google übermittelte Kennung. Die weiteren Angaben über Ihre Teilnahme an der Befragung erhält Google nicht. Wenn Sie mit der Drittlandsübermittlung nicht einverstanden sind, tippen Sie bitte nicht „Einverstanden“ an. Sie können die App weiterhin nutzen, eine Teilnahme an dieser Befragung ist dann jedoch nicht möglich."</string>
<!-- XTXT: Title for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_title_below">"<b>Zur Befragung durch das RKI</b>"</string>
<!-- XHED: Text for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_text_below">"Nachdem die Echtheit Ihrer App geprüft wurde, werden Sie auf eine Internetseite des RKI mit einem für Sie generierten Teilnahmelink zur Befragung weitergeleitet. Die Internetseite wird im Browser Ihres Smartphones geöffnet. Der Teilnahmelink enthält ein vorab durch die Corona-Warn-App generiertes Einmal-Passwort. Wenn Sie den Teilnahmelink antippen und die Internetseite mit der Befragung aufrufen, wird das Einmal-Passwort in Ihrem Browser zwischengespeichert. Auf dieser Seite werden weiterführenden Informationen zur Befragung und Handlungshinweise, z.B. wie die Befragung gestartet werden kann, dargestellt. Zum Start der Befragung wird das Einmal-Passwort an Server der Corona-Warn-App übermittelt und dort als verbraucht markiert. Durch den Server wird geprüft, ob Sie an der Befragung teilnehmen können oder nicht. So wird sichergestellt, dass jede Person nur einmal an der Befragung teilnehmen kann."</string>

</resources>
5 changes: 5 additions & 0 deletions Corona-Warn-App/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,11 @@
<!-- XHED: Dialog progress message for survey request -->
<string name="datadonation_details_survey_consent_progress_dialog_msg">"Befragung wird geladen…"</string>

<!-- XTXT: Title for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_title_below">"<b>Zur Befragung durch das RKI</b>"</string>
<!-- XHED: Text for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_text_below">"Nachdem die Echtheit Ihrer App geprüft wurde, werden Sie auf eine Internetseite des RKI mit einem für Sie generierten Teilnahmelink zur Befragung weitergeleitet. Die Internetseite wird im Browser Ihres Smartphones geöffnet. Der Teilnahmelink enthält ein vorab durch die Corona-Warn-App generiertes Einmal-Passwort. Wenn Sie den Teilnahmelink antippen und die Internetseite mit der Befragung aufrufen, wird das Einmal-Passwort in Ihrem Browser zwischengespeichert. Auf dieser Seite werden weiterführenden Informationen zur Befragung und Handlungshinweise, z.B. wie die Befragung gestartet werden kann, dargestellt. Zum Start der Befragung wird das Einmal-Passwort an Server der Corona-Warn-App übermittelt und dort als verbraucht markiert. Durch den Server wird geprüft, ob Sie an der Befragung teilnehmen können oder nicht. So wird sichergestellt, dass jede Person nur einmal an der Befragung teilnehmen kann."</string>

<!-- XHED: Analytics voluntary user input, age group toolbar title -->
<string name="analytics_userinput_agegroup_title">Ihr Alter</string>
<!-- XTXT: Analytics voluntary user input, age group: AGE_GROUP_UNSPECIFIED -->
Expand Down
4 changes: 0 additions & 4 deletions Corona-Warn-App/src/main/res/values/legal_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,5 @@
<string name="datadonation_survey_consent_details_title" translatable="false">"Prüfung der Echtheit und Drittlandsübermittlung"</string>
<!-- XTXT: Text for the information box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_text" translatable="false">"Um die Echtheit Ihrer App zu bestätigen, erzeugt Ihr Smartphone eine eindeutige Kennung, die Informationen über die Version Ihres Smartphones und der App enthält. Das ist erforderlich, um zu verhindern, dass Nutzer mehrfach der Befragung teilnehmen und so die Ergebnisse der Befragung verfälschen. Die Kennung wird hier einmalig an Google übermittelt. Dabei kann es auch zu einer Datenübermittlung in die USA kommen. Dort besteht kein dem europäischen Recht angemessenes Datenschutzniveau und Ihre europäischen Datenschutzrechte können eventuell nicht durchgesetzt werden. Insbesondere besteht die Möglichkeit, dass US-Sicherheitsbehörden, auch ohne einen konkreten Verdacht, auf die übermittelten Daten bei Google zugreifen und diese auswerten, beispielsweise indem sie Daten mit anderen Informationen verknüpfen. Dies betrifft nur die an Google übermittelte Kennung. Die weiteren Angaben über Ihre Teilnahme an der Befragung erhält Google nicht. Wenn Sie mit der Drittlandsübermittlung nicht einverstanden sind, tippen Sie bitte nicht „Einverstanden“ an. Sie können die App weiterhin nutzen, eine Teilnahme an dieser Befragung ist dann jedoch nicht möglich."</string>
<!-- XTXT: Title for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_title_below" translatable="false">"<b>Zur Befragung durch das RKI</b>"</string>
<!-- XHED: Text for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_text_below" translatable="false">"Nachdem die Echtheit Ihrer App geprüft wurde, werden Sie auf eine Internetseite des RKI mit einem für Sie generierten Teilnahmelink zur Befragung weitergeleitet. Die Internetseite wird im Browser Ihres Smartphones geöffnet. Der Teilnahmelink enthält ein vorab durch die Corona-Warn-App generiertes Einmal-Passwort. Wenn Sie den Teilnahmelink antippen und die Internetseite mit der Befragung aufrufen, wird das Einmal-Passwort in Ihrem Browser zwischengespeichert. Auf dieser Seite werden weiterführenden Informationen zur Befragung und Handlungshinweise, z.B. wie die Befragung gestartet werden kann, dargestellt. Zum Start der Befragung wird das Einmal-Passwort an Server der Corona-Warn-App übermittelt und dort als verbraucht markiert. Durch den Server wird geprüft, ob Sie an der Befragung teilnehmen können oder nicht. So wird sichergestellt, dass jede Person nur einmal an der Befragung teilnehmen kann."</string>

</resources>
5 changes: 5 additions & 0 deletions Corona-Warn-App/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,11 @@
<!-- XHED: Dialog progress message for survey request -->
<string name="datadonation_details_survey_consent_progress_dialog_msg">"Befragung wird geladen…"</string>

<!-- XTXT: Title for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_title_below" translatable="false">"<b>Zur Befragung durch das RKI</b>"</string>
<!-- XHED: Text for the information below the box in the survey consent detail screen -->
<string name="datadonation_survey_consent_details_text_below" translatable="false">"Nachdem die Echtheit Ihrer App geprüft wurde, werden Sie auf eine Internetseite des RKI mit einem für Sie generierten Teilnahmelink zur Befragung weitergeleitet. Die Internetseite wird im Browser Ihres Smartphones geöffnet. Der Teilnahmelink enthält ein vorab durch die Corona-Warn-App generiertes Einmal-Passwort. Wenn Sie den Teilnahmelink antippen und die Internetseite mit der Befragung aufrufen, wird das Einmal-Passwort in Ihrem Browser zwischengespeichert. Auf dieser Seite werden weiterführenden Informationen zur Befragung und Handlungshinweise, z.B. wie die Befragung gestartet werden kann, dargestellt. Zum Start der Befragung wird das Einmal-Passwort an Server der Corona-Warn-App übermittelt und dort als verbraucht markiert. Durch den Server wird geprüft, ob Sie an der Befragung teilnehmen können oder nicht. So wird sichergestellt, dass jede Person nur einmal an der Befragung teilnehmen kann."</string>

<!-- XHED: Analytics voluntary user input, age group toolbar title -->
<string name="analytics_userinput_agegroup_title">Ihr Alter</string>
<!-- XTXT: Analytics voluntary user input, age group: AGE_GROUP_UNSPECIFIED -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.datadonation.storage

import android.content.Context
import com.google.gson.Gson
import de.rki.coronawarnapp.datadonation.OTPAuthorizationResult
import de.rki.coronawarnapp.datadonation.OneTimePassword
import de.rki.coronawarnapp.datadonation.survey.SurveySettings
import io.kotest.matchers.shouldBe
Expand Down Expand Up @@ -38,7 +39,7 @@ class OTPRepositoryTest : BaseTest() {
val uuid = UUID.fromString("e103c755-0975-4588-a639-d0cd1ba421a0")
val time = Instant.ofEpochMilli(1612381131014)
every { surveySettings.oneTimePassword } returns OneTimePassword(uuid, time)
val lastOTP = OTPRepository(surveySettings).lastOTP
val lastOTP = OTPRepository(surveySettings).otp
lastOTP shouldNotBe null
lastOTP!!.apply {
uuid shouldBe uuid
Expand All @@ -58,6 +59,36 @@ class OTPRepositoryTest : BaseTest() {
@Test
fun `no last otp`() {
every { surveySettings.oneTimePassword } returns null
OTPRepository(surveySettings).lastOTP shouldBe null
OTPRepository(surveySettings).otp shouldBe null
}

@Test
fun `no otp auth result after generating new otp`() {
every { context.getSharedPreferences("survey_localdata", Context.MODE_PRIVATE) } returns MockSharedPreferences()
val settings = SurveySettings(context, Gson())
settings.otpAuthorizationResult = OTPAuthorizationResult(
UUID.randomUUID(),
true,
Instant.now()
)

settings.otpAuthorizationResult shouldNotBe null
OTPRepository(settings).generateOTP()
settings.otpAuthorizationResult shouldBe null
}

@Test
fun `no otp after storing otp auth result`() {
every { context.getSharedPreferences("survey_localdata", Context.MODE_PRIVATE) } returns MockSharedPreferences()
val settings = SurveySettings(context, Gson())
settings.oneTimePassword = OneTimePassword(UUID.randomUUID(), Instant.now())

settings.oneTimePassword shouldNotBe null
OTPRepository(settings).otpAuthorizationResult = OTPAuthorizationResult(
UUID.randomUUID(),
true,
Instant.now()
)
settings.oneTimePassword shouldBe null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.datadonation.survey

import android.content.Context
import com.google.gson.Gson
import de.rki.coronawarnapp.datadonation.OTPAuthorizationResult
import de.rki.coronawarnapp.datadonation.OneTimePassword
import de.rki.coronawarnapp.util.serialization.SerializationModule
import io.kotest.matchers.shouldBe
Expand Down Expand Up @@ -39,7 +40,7 @@ class SurveySettingsTest : BaseTest() {
}

@Test
fun `load and deserialize json`() {
fun `load and deserialize otp json`() {
val instance = SurveySettings(context, baseGson)
instance.oneTimePassword shouldBe null

Expand All @@ -60,7 +61,7 @@ class SurveySettingsTest : BaseTest() {
}

@Test
fun `parsing error`() {
fun `otp parsing error`() {
val instance = SurveySettings(context, baseGson)
instance.oneTimePassword shouldBe null

Expand All @@ -73,7 +74,7 @@ class SurveySettingsTest : BaseTest() {
}

@Test
fun `save and serialize json`() {
fun `save and serialize otp json`() {
val uuid = UUID.fromString("e103c755-0975-4588-a639-d0cd1ba421a0")
val time = Instant.ofEpochMilli(1612381567242)

Expand All @@ -88,4 +89,59 @@ class SurveySettingsTest : BaseTest() {
}
""".trimIndent()
}

@Test
fun `load and deserialize auth result json`() {
val instance = SurveySettings(context, baseGson)
instance.otpAuthorizationResult shouldBe null

preferences.edit().putString(
"otp_result",
"""
{
"uuid":"e103c755-0975-4588-a639-d0cd1ba421a1",
"authorized": true,
"redeemedAt": 1612381217443
}
""".trimIndent()
).apply()

val value = instance.otpAuthorizationResult
value shouldNotBe null
value!!.uuid.toString() shouldBe "e103c755-0975-4588-a639-d0cd1ba421a1"
value.authorized shouldBe true
value.redeemedAt.millis shouldBe 1612381217443
}

@Test
fun `auth result parsing error`() {
val instance = SurveySettings(context, baseGson)
instance.otpAuthorizationResult shouldBe null

preferences
.edit()
.putString("otp_result", "invalid value")
.apply()

instance.otpAuthorizationResult shouldBe null
}

@Test
fun `save and serialize auth result json`() {
val uuid = UUID.fromString("e103c755-0975-4588-a639-d0cd1ba421a0")
val authorized = false
val redeemedAt = Instant.ofEpochMilli(1612381217445)

val instance = SurveySettings(context, baseGson)
instance.otpAuthorizationResult = OTPAuthorizationResult(uuid, authorized, redeemedAt)

val value = preferences.getString("otp_result", null)
value shouldBe """
{
"uuid": "e103c755-0975-4588-a639-d0cd1ba421a0",
"authorized": false,
"redeemedAt": 1612381217445
}
""".trimIndent()
}
}

0 comments on commit 4c11f21

Please sign in to comment.