From 5788184d9ff7b7c494ae7266c600fad2a6916cb4 Mon Sep 17 00:00:00 2001 From: Mohamad Jaara Date: Thu, 9 Jan 2025 12:08:38 +0100 Subject: [PATCH] feat: harden ClientCapabilityDTO for api v7 [WPB-14882] (#3151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: harden ClientCapabilityDTO for api v7 * add tests * detekt * detekt * chore: disable folders update handler (#3161) * fix: Login to second device does not have MLS capabilities RC (#3165) * feat: add usecase to get team url (WPB-14872) (#3157) (#3168) * feat: add usecase to get team url * feat:detekt * feat: detekt * feat: detekt (cherry picked from commit abcd037802987a6964464b7c0e96eac6e512bd4f) * chore: upgrade to cc3, fixes new_transaction in paralell (#3170) * test: add tests for old format parser in capabilities (#3173) * Update libs.versions.toml * Update UserPropertiesEventReceiver.kt * Update UserPropertiesEventReceiver.kt * Update UserPropertiesEventReceiverTest.kt * fix * fix tests * missing imports * fix circular dependency * Trigger CI * remove not needed dependency --------- Co-authored-by: Jakub Żerko Co-authored-by: Yamil Medina Co-authored-by: boris Co-authored-by: Oussama Hassine --- .../kalium/logic/data/client/ClientModel.kt | 5 +- .../kalium/logic/data/client/ClientMapper.kt | 2 + .../kalium/logic/feature/UserSessionScope.kt | 9 +-- .../UserPropertiesEventReceiverTest.kt | 2 +- .../mocks/responses/ClientResponseJson.kt | 8 ++- network-model/build.gradle.kts | 1 - .../api/authenticated/client/ClientRequest.kt | 37 +++++++++-- .../ClientCapabilityDTOSerializerTest.kt | 66 +++++++++++++++++++ .../api/v0/user/client/ClientApiV0Test.kt | 8 ++- 9 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 network-model/src/commonTest/kotlin/com/wire/kalium/network/api/authenticated/client/ClientCapabilityDTOSerializerTest.kt diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt index 14e1bb132b0..ec562bd9319 100644 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientModel.kt @@ -69,8 +69,9 @@ enum class DeviceType { Unknown; } -enum class ClientCapability { - LegalHoldImplicitConsent; +sealed class ClientCapability { + data object LegalHoldImplicitConsent : ClientCapability() + data class Unknown(val name: String) : ClientCapability() } data class OtherUserClient( diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientMapper.kt index 57b6d6c4148..f53feb17f5b 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientMapper.kt @@ -198,10 +198,12 @@ class ClientMapper( private fun toClientCapabilityDTO(clientCapability: ClientCapability): ClientCapabilityDTO = when (clientCapability) { ClientCapability.LegalHoldImplicitConsent -> ClientCapabilityDTO.LegalHoldImplicitConsent + is ClientCapability.Unknown -> ClientCapabilityDTO.Unknown(clientCapability.name) } private fun fromClientCapabilityDTO(clientCapabilityDTO: ClientCapabilityDTO): ClientCapability = when (clientCapabilityDTO) { ClientCapabilityDTO.LegalHoldImplicitConsent -> ClientCapability.LegalHoldImplicitConsent + is ClientCapabilityDTO.Unknown -> ClientCapability.Unknown(clientCapabilityDTO.name) } fun fromOtherUsersClientsDTO(otherUsersClients: List): List = diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt index 42f48f36d69..9f0beaf9225 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/UserSessionScope.kt @@ -2164,14 +2164,15 @@ class UserSessionScope internal constructor( kaliumLogger = userScopedLogger, ) - val getTeamUrlUseCase: GetTeamUrlUseCase by lazy { - GetTeamUrlUseCase( + val getTeamUrlUseCase: GetTeamUrlUseCase + get() = GetTeamUrlUseCase( userId, authenticationScope.serverConfigRepository, ) - } - private val inCallReactionsRepository: InCallReactionsRepository = InCallReactionsDataSource() + private val inCallReactionsRepository: InCallReactionsRepository by lazy { + InCallReactionsDataSource() + } /** * This will start subscribers of observable work per user session, as long as the user is logged in. diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt index af93f739d10..fefc2842738 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserPropertiesEventReceiverTest.kt @@ -85,7 +85,7 @@ class UserPropertiesEventReceiverTest { suspend fun withUpdateConversationFolders() = apply { coEvery { conversationFolderRepository.updateConversationFolders(any()) - }.returns(Either.Right(Unit)) + }.returns(Either.Right(Unit)) } fun arrange() = this to userPropertiesEventReceiver diff --git a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/ClientResponseJson.kt b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/ClientResponseJson.kt index d3d7e0010e1..1093ac64f5c 100644 --- a/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/ClientResponseJson.kt +++ b/mocks/src/commonMain/kotlin/com/wire/kalium/mocks/responses/ClientResponseJson.kt @@ -22,6 +22,8 @@ import com.wire.kalium.network.api.authenticated.client.ClientCapabilityDTO import com.wire.kalium.network.api.authenticated.client.ClientDTO import com.wire.kalium.network.api.authenticated.client.ClientTypeDTO import com.wire.kalium.network.api.authenticated.client.DeviceTypeDTO +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json object ClientResponseJson { private val jsonProvider = { serializable: ClientDTO -> @@ -36,7 +38,7 @@ object ClientResponseJson { | "cookie": "${serializable.cookie}", | "model": "${serializable.model}", | "capabilities": [ - | "${serializable.capabilities[0]}" + | ${Json.encodeToString(serializable.capabilities[0])} | ], | "mls_public_keys": ${serializable.mlsPublicKeys} |} @@ -57,9 +59,9 @@ object ClientResponseJson { | "model": "${serializable.model}", | "capabilities": { | "capabilities": [ - | "${serializable.capabilities[0]}" + | ${Json.encodeToString(serializable.capabilities[0])} | ] - | } + | }, | "mls_public_keys": ${serializable.mlsPublicKeys} |} """.trimMargin() diff --git a/network-model/build.gradle.kts b/network-model/build.gradle.kts index 4f2eb1c7497..259092609dc 100644 --- a/network-model/build.gradle.kts +++ b/network-model/build.gradle.kts @@ -45,7 +45,6 @@ kotlin { // KTX implementation(libs.ktxDateTime) - } } } diff --git a/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/client/ClientRequest.kt b/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/client/ClientRequest.kt index 19021e85746..feb0c29fa24 100644 --- a/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/client/ClientRequest.kt +++ b/network-model/src/commonMain/kotlin/com/wire/kalium/network/api/authenticated/client/ClientRequest.kt @@ -22,8 +22,14 @@ import com.wire.kalium.network.api.authenticated.client.DeviceTypeDTO.Unknown import com.wire.kalium.network.api.authenticated.prekey.PreKeyDTO import com.wire.kalium.network.api.model.MLSPublicKey import com.wire.kalium.network.api.model.UserId +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder @Serializable data class RegisterClientRequest( @@ -86,12 +92,33 @@ enum class DeviceTypeDTO { } } -@Serializable -enum class ClientCapabilityDTO { +@Serializable(with = ClientCapabilityDTOSerializer::class) +sealed class ClientCapabilityDTO { @SerialName("legalhold-implicit-consent") - LegalHoldImplicitConsent { - override fun toString(): String { - return "legalhold-implicit-consent" + data object LegalHoldImplicitConsent : ClientCapabilityDTO() + data class Unknown(val name: String) : ClientCapabilityDTO() +} + +object ClientCapabilityDTOSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( + serialName = "ClientCapabilityDTO", + kind = PrimitiveKind.STRING + ) + + override fun serialize(encoder: Encoder, value: ClientCapabilityDTO) { + when (value) { + is ClientCapabilityDTO.LegalHoldImplicitConsent -> + encoder.encodeString("legalhold-implicit-consent") + + is ClientCapabilityDTO.Unknown -> + encoder.encodeString(value.name) + } + } + + override fun deserialize(decoder: Decoder): ClientCapabilityDTO { + return when (val value = decoder.decodeString()) { + "legalhold-implicit-consent" -> ClientCapabilityDTO.LegalHoldImplicitConsent + else -> ClientCapabilityDTO.Unknown(value) } } } diff --git a/network-model/src/commonTest/kotlin/com/wire/kalium/network/api/authenticated/client/ClientCapabilityDTOSerializerTest.kt b/network-model/src/commonTest/kotlin/com/wire/kalium/network/api/authenticated/client/ClientCapabilityDTOSerializerTest.kt new file mode 100644 index 00000000000..408490c1b44 --- /dev/null +++ b/network-model/src/commonTest/kotlin/com/wire/kalium/network/api/authenticated/client/ClientCapabilityDTOSerializerTest.kt @@ -0,0 +1,66 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.network.api.authenticated.client + +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + + +class ClientCapabilityDTOSerializerTest { + + private val json = Json { ignoreUnknownKeys = true } + + @Test + fun serialize_legal_hold_implicit_consent() { + val capability = ClientCapabilityDTO.LegalHoldImplicitConsent + val result = json.encodeToString(ClientCapabilityDTO.serializer(), capability) + assertEquals("\"legalhold-implicit-consent\"", result) + } + + @Test + fun serialize_unknown_capability() { + val capability = ClientCapabilityDTO.Unknown("custom-capability") + val result = json.encodeToString(ClientCapabilityDTO.serializer(), capability) + assertEquals("\"custom-capability\"", result) + } + + @Test + fun deserialize_legal_hold_implicit_consent() { + val jsonString = "\"legalhold-implicit-consent\"" + val result = json.decodeFromString(ClientCapabilityDTO.serializer(), jsonString) + assertEquals(ClientCapabilityDTO.LegalHoldImplicitConsent, result) + } + + @Test + fun deserialize_unknown_capability() { + val jsonString = "\"unknown-capability\"" + val result = json.decodeFromString(ClientCapabilityDTO.serializer(), jsonString) + assertEquals(ClientCapabilityDTO.Unknown("unknown-capability"), result) + } + + @Test + fun deserialization_fails_on_invalid_json_format() { + val invalidJson = "12345" // Invalid format for a string + assertFailsWith { + json.decodeFromString(ClientCapabilityDTO.serializer(), invalidJson) + } + } +} diff --git a/network/src/commonTest/kotlin/com/wire/kalium/api/v0/user/client/ClientApiV0Test.kt b/network/src/commonTest/kotlin/com/wire/kalium/api/v0/user/client/ClientApiV0Test.kt index 574ef891390..59635e69549 100644 --- a/network/src/commonTest/kotlin/com/wire/kalium/api/v0/user/client/ClientApiV0Test.kt +++ b/network/src/commonTest/kotlin/com/wire/kalium/api/v0/user/client/ClientApiV0Test.kt @@ -64,8 +64,12 @@ internal class ClientApiV0Test : ApiTest() { @Test fun givenAValidRegisterClientRequest_whenCallingTheRegisterClientEndpointWithOldFormat_theRequestShouldBeConfiguredCorrectly() = runTest { + + val rowJson = VALID_REGISTER_CLIENT_OLD_RESPONSE.rawJson + val data = VALID_REGISTER_CLIENT_OLD_RESPONSE.serializableData + val networkClient = mockAuthenticatedNetworkClient( - VALID_REGISTER_CLIENT_OLD_RESPONSE.rawJson, + rowJson, statusCode = HttpStatusCode.Created, assertion = { assertPost() @@ -77,7 +81,7 @@ internal class ClientApiV0Test : ApiTest() { val clientApi: ClientApi = ClientApiV0(networkClient) val response = clientApi.registerClient(REGISTER_CLIENT_REQUEST.serializableData) assertTrue(response.isSuccessful()) - assertEquals(VALID_REGISTER_CLIENT_OLD_RESPONSE.serializableData, response.value) + assertEquals(data, response.value) } @Test