Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.simprints.feature.clientapi.usecases

import com.fasterxml.jackson.databind.module.SimpleModule
import com.simprints.core.domain.tokenization.TokenizableString
import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameDeserializer
import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameSerializer
import com.simprints.core.tools.json.JsonHelper
import com.simprints.core.tools.utils.EncodingUtils
import com.simprints.infra.config.store.models.canCoSyncAllData
Expand Down Expand Up @@ -34,7 +38,7 @@ internal class GetEnrolmentCreationEventForSubjectUseCase @Inject constructor(
?.fromSubjectToEnrolmentCreationEvent()
?: return null

return jsonHelper.toJson(CoSyncEnrolmentRecordEvents(listOf(recordCreationEvent)))
return jsonHelper.toJson(CoSyncEnrolmentRecordEvents(listOf(recordCreationEvent)), coSyncSerializationModule)
}

private fun Subject.fromSubjectToEnrolmentCreationEvent() = EnrolmentRecordCreationEvent(
Expand All @@ -44,4 +48,11 @@ internal class GetEnrolmentCreationEventForSubjectUseCase @Inject constructor(
attendantId,
EnrolmentRecordCreationEvent.buildBiometricReferences(fingerprintSamples, faceSamples, encoder),
)

companion object {
val coSyncSerializationModule = SimpleModule().apply {
addSerializer(TokenizableString::class.java, TokenizationClassNameSerializer())
addDeserializer(TokenizableString::class.java, TokenizationClassNameDeserializer())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class GetEnrolmentCreationEventForSubjectUseCaseTest {
val result = useCase("projectId", "subjectId")

coVerify { enrolmentRecordRepository.load(any()) }
coVerify { jsonHelper.toJson(any()) }
coVerify { jsonHelper.toJson(any(), any()) }
assertThat(result).isNotNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,6 @@ internal class CommCareIdentityDataSource @Inject constructor(
@ApplicationContext private val context: Context,
@DispatcherIO private val dispatcher: CoroutineDispatcher,
) : IdentityDataSource {
companion object {
const val COLUMN_CASE_ID = "case_id"
const val COLUMN_DATUM_ID = "datum_id"
const val COLUMN_VALUE = "value"

const val ARG_CASE_ID = "caseId"
}

private fun getCaseMetadataUri(packageName: String): Uri = Uri.parse("content://$packageName.case/casedb/case")

private fun getCaseDataUri(packageName: String): Uri = Uri.parse("content://$packageName.case/casedb/data")
Expand Down Expand Up @@ -106,7 +98,9 @@ internal class CommCareIdentityDataSource @Inject constructor(
if (caseMetadataCursor.moveToPosition(range.first)) {
do {
caseMetadataCursor.getString(caseMetadataCursor.getColumnIndexOrThrow(COLUMN_CASE_ID))?.let { caseId ->
enrolmentRecordCreationEvents.addAll(loadEnrolmentRecordCreationEvents(caseId, callerPackageName, query, project))
enrolmentRecordCreationEvents.addAll(
loadEnrolmentRecordCreationEvents(caseId, callerPackageName, query, project),
)
onCandidateLoaded()
}
} while (caseMetadataCursor.moveToNext() && caseMetadataCursor.position < range.last)
Expand Down Expand Up @@ -160,7 +154,7 @@ internal class CommCareIdentityDataSource @Inject constructor(
caseId: String,
callerPackageName: String,
query: SubjectQuery,
project: Project
project: Project,
): List<EnrolmentRecordCreationEvent> {
// Access Case Data Listing for the caseId
val caseDataUri = getCaseDataUri(callerPackageName).buildUpon().appendPath(caseId).build()
Expand All @@ -181,15 +175,15 @@ internal class CommCareIdentityDataSource @Inject constructor(
(query.subjectId != null && query.subjectId != event.payload.subjectId) ||
!compareImplicitTokenizedStringsUseCase(
query.attendantId,
event.payload.attendantId.value,
event.payload.attendantId,
TokenKeyType.AttendantId,
project
project,
) ||
!compareImplicitTokenizedStringsUseCase(
query.moduleId,
event.payload.moduleId.value,
event.payload.moduleId,
TokenKeyType.ModuleId,
project
project,
)
}
}.orEmpty()
Expand Down Expand Up @@ -244,4 +238,12 @@ internal class CommCareIdentityDataSource @Inject constructor(
)?.use { caseMetadataCursor -> count = caseMetadataCursor.count }
count
}

companion object {
const val COLUMN_CASE_ID = "case_id"
const val COLUMN_DATUM_ID = "datum_id"
const val COLUMN_VALUE = "value"

const val ARG_CASE_ID = "caseId"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,24 @@ import com.simprints.infra.config.store.tokenization.TokenizationProcessor
import javax.inject.Inject

/**
* Use case that checks two plain strings values but considers that the tokenization state of one of the values
* Use case that checks two TokenizableStrings but considers that the tokenization state of one of the values
* might differ from another. In this case this use tries to bring both strings to the same tokenization state
* and compare their values.
* Example:
* s1 = 'abc' - untokenized value
* s2 = 'AWcDe/==seF1LkcF4' - tokenized value (result of tokenizing the 'abc' value)
*
* Even though plain string values are different, they represent the same entity. s1 is going to be encrypted and compared to the s2.
*
* Given the implementation of TokenizationClassNameDeserializer - if a value is TokenizableString.Tokenized, we can be sure that it is tokenized.
* Only deserialized TokenizableString.Raw values have uncertain tokenization state.
*/
class CompareImplicitTokenizedStringsUseCase @Inject constructor(
private val tokenizationProcessor: TokenizationProcessor,
) {
operator fun invoke(
s1: TokenizableString?,
s2: String,
s2: TokenizableString,
tokenKeyType: TokenKeyType,
project: Project,
): Boolean = when {
Expand All @@ -37,33 +40,23 @@ class CompareImplicitTokenizedStringsUseCase @Inject constructor(
project: Project,
): TokenizableString = when (s) {
is TokenizableString.Tokenized -> s
is TokenizableString.Raw -> tokenizationProcessor.encrypt(
decrypted = s,
tokenKeyType = tokenKeyType,
project = project,
)
}

private fun ensureTokenized(
s: String,
tokenKeyType: TokenKeyType,
project: Project,
): TokenizableString {
val isAlreadyTokenized = tokenizationProcessor.decrypt(
encrypted = s.asTokenizableEncrypted(),
tokenKeyType = tokenKeyType,
project = project,
logError = false,
) is TokenizableString.Raw

return if (isAlreadyTokenized) {
s.asTokenizableEncrypted()
} else {
tokenizationProcessor.encrypt(
decrypted = s.asTokenizableRaw(),
is TokenizableString.Raw -> {
val isAlreadyTokenized = tokenizationProcessor.decrypt(
encrypted = s.value.asTokenizableEncrypted(),
tokenKeyType = tokenKeyType,
project = project,
)
logError = false,
) is TokenizableString.Raw

if (isAlreadyTokenized) {
s.value.asTokenizableEncrypted()
} else {
tokenizationProcessor.encrypt(
decrypted = s.value.asTokenizableRaw(),
tokenKeyType = tokenKeyType,
project = project,
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should return false when s1 is null`() {
val s1 = null
val s2 = "s2"
val s2 = TokenizableString.Raw("s2")

val result = useCase(s1, s2, tokenKeyType, project)

Expand All @@ -44,7 +44,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should return true if strings are equal (both untokenized)`() {
val s1 = "s1".asTokenizableRaw()
val s2 = s1.value
val s2 = s1
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Tokenized("some value")

val result = useCase(s1, s2, tokenKeyType, project)
Expand All @@ -56,7 +56,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should return false if strings are not equal (both untokenized)`() {
val s1 = "s1".asTokenizableRaw()
val s2 = "s2"
val s2 = "s2".asTokenizableRaw()
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Tokenized("some value")

val result = useCase(s1, s2, tokenKeyType, project)
Expand All @@ -68,7 +68,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should call encrypt if only first is tokenized)`() {
val s1 = "s1".asTokenizableEncrypted()
val s2 = "s2"
val s2 = "s2".asTokenizableRaw()
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Tokenized("some value")

useCase(s1, s2, tokenKeyType, project)
Expand All @@ -79,8 +79,8 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should call encrypt if only second is tokenized)`() {
val s1 = "s1".asTokenizableRaw()
val s2 = "encrypted s1"
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Raw(s2)
val s2 = "encrypted s1".asTokenizableEncrypted()
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Tokenized("some value")

useCase(s1, s2, tokenKeyType, project)

Expand All @@ -90,7 +90,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should not call encrypt and return true if both strings are tokenized and equal`() {
val s1 = "s1".asTokenizableEncrypted()
val s2 = s1.value
val s2 = "s1".asTokenizableEncrypted()
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Raw("some value")

val result = useCase(s1, s2, tokenKeyType, project)
Expand All @@ -102,7 +102,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should not call encrypt and return false if both strings are tokenized but not equal`() {
val s1 = "s1".asTokenizableEncrypted()
val s2 = "s2"
val s2 = "s2".asTokenizableEncrypted()
every { tokenizationProcessor.decrypt(any(), any(), any(), any()) } returns TokenizableString.Raw("some value")

val result = useCase(s1, s2, tokenKeyType, project)
Expand All @@ -114,7 +114,7 @@ class CompareImplicitTokenizedStringsUseCaseTest {
@Test
fun `should return false if not equal`() {
val s1 = "s1".asTokenizableRaw()
val s2 = "s2"
val s2 = "s2".asTokenizableRaw()

val result = useCase(s1, s2, tokenKeyType, project)

Expand Down