From d549a6356f941db7ee3fcc0ba9c89697231ebc7a Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Mon, 20 Nov 2023 13:46:35 +0100 Subject: [PATCH 01/10] feat: handle legal hold events --- .../configuration/UserConfigRepository.kt | 24 ++- .../logic/data/client/ClientRepository.kt | 3 +- .../com/wire/kalium/logic/data/event/Event.kt | 46 ++++++ .../kalium/logic/data/event/EventMapper.kt | 55 ++++++- .../kalium/logic/data/legalhold/LastPreKey.kt | 23 +++ .../kalium/logic/feature/UserSessionScope.kt | 59 ++++++-- .../logic/feature/client/ClientScope.kt | 10 +- .../logic/sync/receiver/UserEventReceiver.kt | 11 +- .../handler/legalhold/LegalHoldHandler.kt | 69 +++++++++ .../legalhold/LegalHoldRequestHandler.kt | 46 ++++++ .../sync/receiver/UserEventReceiverTest.kt | 11 +- .../handler/legalhold/LegalHoldHandlerTest.kt | 139 +++++++++++++++++ .../legalhold/LegalHoldRequestHandlerTest.kt | 105 +++++++++++++ .../base/authenticated/client/ClientIdDTO.kt | 27 ++++ .../authenticated/keypackage/LastPreKeyDTO.kt | 29 ++++ .../notification/EventContentDTO.kt | 22 +++ .../persistence/config/UserConfigStorage.kt | 141 +++++++++++++++--- 17 files changed, 765 insertions(+), 55 deletions(-) create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt create mode 100644 network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt create mode 100644 network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/keypackage/LastPreKeyDTO.kt diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt index f4413d1ea8b..dbef7f9daaa 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt @@ -22,12 +22,12 @@ import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel import com.wire.kalium.logic.data.featureConfig.toEntity import com.wire.kalium.logic.data.featureConfig.toModel -import com.wire.kalium.logic.data.user.SupportedProtocol -import com.wire.kalium.logic.data.user.toDao -import com.wire.kalium.logic.data.user.toModel import com.wire.kalium.logic.data.message.SelfDeletionMapper.toSelfDeletionTimerEntity import com.wire.kalium.logic.data.message.SelfDeletionMapper.toTeamSelfDeleteTimer import com.wire.kalium.logic.data.message.TeamSettingsSelfDeletionStatus +import com.wire.kalium.logic.data.user.SupportedProtocol +import com.wire.kalium.logic.data.user.toDao +import com.wire.kalium.logic.data.user.toModel import com.wire.kalium.logic.featureFlags.BuildFileRestrictionState import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.functional.Either @@ -107,6 +107,12 @@ interface UserConfigRepository { fun setE2EINotificationTime(instant: Instant): Either suspend fun getMigrationConfiguration(): Either suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either + fun setLegalHoldRequest( + clientId: String, + lastPreKeyId: Int, + lastPreKey: String + ): Either + fun deleteLegalHoldRequest(): Either } @Suppress("TooManyFunctions") @@ -383,4 +389,16 @@ class UserConfigDataSource( wrapStorageRequest { userConfigDAO.setMigrationConfiguration(configuration.toEntity()) } + + override fun setLegalHoldRequest( + clientId: String, + lastPreKeyId: Int, + lastPreKey: String + ): Either = wrapStorageRequest { + userConfigStorage.persistLegalHoldRequest(clientId, lastPreKeyId, lastPreKey) + } + + override fun deleteLegalHoldRequest(): Either = wrapStorageRequest { + userConfigStorage.clearLegalHoldRequest() + } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt index 1a16bac4c59..bd5de79dfec 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt @@ -92,8 +92,7 @@ class ClientDataSource( private val newClientDAO: NewClientDAO, private val selfUserID: UserId, private val clientApi: ClientApi, - private val clientMapper: ClientMapper = MapperProvider.clientMapper(), - private val userMapper: UserMapper = MapperProvider.userMapper(), + private val clientMapper: ClientMapper = MapperProvider.clientMapper() ) : ClientRepository { override suspend fun registerClient(param: RegisterClientParam): Either { return clientRemoteRepository.registerClient(param) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/Event.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/Event.kt index 614545d17b5..5c0a809c8bb 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/Event.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/Event.kt @@ -39,6 +39,7 @@ import com.wire.kalium.logic.data.featureConfig.MLSModel import com.wire.kalium.logic.data.featureConfig.SelfDeletingMessagesModel import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.SubconversationId +import com.wire.kalium.logic.data.legalhold.LastPreKey import com.wire.kalium.logic.data.user.Connection import com.wire.kalium.logic.data.user.SupportedProtocol import com.wire.kalium.logic.data.user.UserId @@ -722,6 +723,51 @@ sealed class Event(open val id: String, open val transient: Boolean, open val li "isMLSCapable" to client.isMLSCapable ) } + + data class LegalHoldRequest( + override val transient: Boolean, + override val live: Boolean, + override val id: String, + val clientId: ClientId, + val lastPreKey: LastPreKey, + val userId: UserId + ) : User(id, transient, live) { + override fun toLogMap(): Map = mapOf( + typeKey to "User.LegalHold-request", + idKey to id.obfuscateId(), + "transient" to "$transient", + "clientId" to clientId.value.obfuscateId(), + "userId" to userId.toLogString(), + ) + } + + data class LegalHoldEnabled( + override val transient: Boolean, + override val live: Boolean, + override val id: String, + val userId: UserId + ) : User(id, transient, live) { + override fun toLogMap(): Map = mapOf( + typeKey to "User.LegalHold-enabled", + idKey to id.obfuscateId(), + "transient" to "$transient", + "userId" to userId.toLogString() + ) + } + + data class LegalHoldDisabled( + override val transient: Boolean, + override val live: Boolean, + override val id: String, + val userId: UserId + ) : User(id, transient, live) { + override fun toLogMap(): Map = mapOf( + typeKey to "User.LegalHold-disabled", + idKey to id.obfuscateId(), + "transient" to "$transient", + "userId" to userId.toLogString() + ) + } } sealed class UserProperty( diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt index e492f197c24..e89e1030bd3 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt @@ -31,8 +31,11 @@ import com.wire.kalium.logic.data.conversation.toModel import com.wire.kalium.logic.data.event.Event.UserProperty.ReadReceiptModeSet import com.wire.kalium.logic.data.event.Event.UserProperty.TypingIndicatorModeSet import com.wire.kalium.logic.data.featureConfig.FeatureConfigMapper +import com.wire.kalium.logic.data.id.QualifiedIdMapper +import com.wire.kalium.logic.data.id.QualifiedIdMapperImpl import com.wire.kalium.logic.data.id.SubconversationId import com.wire.kalium.logic.data.id.toModel +import com.wire.kalium.logic.data.legalhold.LastPreKey import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.toModel import com.wire.kalium.logic.di.MapperProvider @@ -59,7 +62,8 @@ class EventMapper( private val roleMapper: ConversationRoleMapper, private val selfUserId: UserId, private val receiptModeMapper: ReceiptModeMapper = MapperProvider.receiptModeMapper(), - private val clientMapper: ClientMapper = MapperProvider.clientMapper() + private val clientMapper: ClientMapper = MapperProvider.clientMapper(), + private val qualifiedIdMapper: QualifiedIdMapper = QualifiedIdMapperImpl(selfUserId) ) { fun fromDTO(eventResponse: EventResponse, live: Boolean = false): List { // TODO(edge-case): Multiple payloads in the same event have the same ID, is this an issue when marking lastProcessedEventId? @@ -82,8 +86,11 @@ class EventMapper( is EventContentDTO.User.NewConnectionDTO -> connectionUpdate(id, eventContentDTO, transient, live) is EventContentDTO.User.ClientRemoveDTO -> clientRemove(id, eventContentDTO, transient, live) is EventContentDTO.User.UserDeleteDTO -> userDelete(id, eventContentDTO, transient, live) - is EventContentDTO.FeatureConfig.FeatureConfigUpdatedDTO -> featureConfig(id, eventContentDTO, transient, live) is EventContentDTO.User.NewClientDTO -> newClient(id, eventContentDTO, transient, live) + is EventContentDTO.User.NewLegalHoldRequestDTO -> legalHoldRequest(id, transient, live, eventContentDTO) + is EventContentDTO.User.LegalHoldEnabledDTO -> legalHoldEnabled(id, transient, live, eventContentDTO) + is EventContentDTO.User.LegalHoldDisabledDTO -> legalHoldDisabled(id, transient, live, eventContentDTO) + is EventContentDTO.FeatureConfig.FeatureConfigUpdatedDTO -> featureConfig(id, eventContentDTO, transient, live) is EventContentDTO.Unknown -> unknown(id, transient, live, eventContentDTO) is EventContentDTO.Conversation.AccessUpdate -> unknown(id, transient, live, eventContentDTO) is EventContentDTO.Conversation.DeletedConversationDTO -> conversationDeleted(id, eventContentDTO, transient, live) @@ -355,6 +362,50 @@ class EventMapper( connectionMapper.fromApiToModel(eventConnectionDTO.connection) ) + private fun legalHoldRequest( + id: String, + transient: Boolean, + live: Boolean, + eventContentDTO: EventContentDTO.User.NewLegalHoldRequestDTO + ) : Event.User.LegalHoldRequest { + return Event.User.LegalHoldRequest( + transient = transient, + live = live, + id = id, + clientId = ClientId(eventContentDTO.id), + lastPreKey = LastPreKey(eventContentDTO.lastPreKey.id, eventContentDTO.lastPreKey.key), + userId = qualifiedIdMapper.fromStringToQualifiedID(eventContentDTO.id) + ) + } + + private fun legalHoldEnabled( + id: String, + transient: Boolean, + live: Boolean, + eventContentDTO: EventContentDTO.User.LegalHoldEnabledDTO + ) : Event.User.LegalHoldEnabled { + return Event.User.LegalHoldEnabled( + transient, + live, + id, + qualifiedIdMapper.fromStringToQualifiedID(eventContentDTO.id) + ) + } + + private fun legalHoldDisabled( + id: String, + transient: Boolean, + live: Boolean, + eventContentDTO: EventContentDTO.User.LegalHoldDisabledDTO + ) : Event.User.LegalHoldDisabled { + return Event.User.LegalHoldDisabled( + transient, + live, + id, + qualifiedIdMapper.fromStringToQualifiedID(eventContentDTO.id) + ) + } + private fun userDelete( id: String, eventUserDelete: EventContentDTO.User.UserDeleteDTO, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt new file mode 100644 index 00000000000..5901b76c12c --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt @@ -0,0 +1,23 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.data.legalhold + +data class LastPreKey( + val id: Int, + val key: String, +) 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 e80d7854e82..f11700d55f6 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 @@ -61,6 +61,14 @@ import com.wire.kalium.logic.data.conversation.ConversationDataSource import com.wire.kalium.logic.data.conversation.ConversationGroupRepository import com.wire.kalium.logic.data.conversation.ConversationGroupRepositoryImpl import com.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationUseCase +import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationUseCaseImpl +import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationsUseCase +import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationsUseCaseImpl +import com.wire.kalium.logic.data.conversation.JoinSubconversationUseCase +import com.wire.kalium.logic.data.conversation.JoinSubconversationUseCaseImpl +import com.wire.kalium.logic.data.conversation.LeaveSubconversationUseCase +import com.wire.kalium.logic.data.conversation.LeaveSubconversationUseCaseImpl import com.wire.kalium.logic.data.conversation.MLSConversationDataSource import com.wire.kalium.logic.data.conversation.MLSConversationRepository import com.wire.kalium.logic.data.conversation.NewConversationMembersRepository @@ -156,10 +164,14 @@ import com.wire.kalium.logic.feature.call.usecase.ConversationClientsInCallUpdat import com.wire.kalium.logic.feature.call.usecase.UpdateConversationClientsForCurrentCallUseCase import com.wire.kalium.logic.feature.call.usecase.UpdateConversationClientsForCurrentCallUseCaseImpl import com.wire.kalium.logic.feature.client.ClientScope +import com.wire.kalium.logic.feature.client.FetchSelfClientsFromRemoteUseCase +import com.wire.kalium.logic.feature.client.FetchSelfClientsFromRemoteUseCaseImpl import com.wire.kalium.logic.feature.client.IsAllowedToRegisterMLSClientUseCase import com.wire.kalium.logic.feature.client.IsAllowedToRegisterMLSClientUseCaseImpl import com.wire.kalium.logic.feature.client.MLSClientManager import com.wire.kalium.logic.feature.client.MLSClientManagerImpl +import com.wire.kalium.logic.feature.client.PersistOtherUserClientsUseCase +import com.wire.kalium.logic.feature.client.PersistOtherUserClientsUseCaseImpl import com.wire.kalium.logic.feature.client.RegisterMLSClientUseCaseImpl import com.wire.kalium.logic.feature.connection.ConnectionScope import com.wire.kalium.logic.feature.connection.SyncConnectionsUseCase @@ -167,14 +179,6 @@ import com.wire.kalium.logic.feature.connection.SyncConnectionsUseCaseImpl import com.wire.kalium.logic.feature.conversation.ConversationScope import com.wire.kalium.logic.feature.conversation.ConversationsRecoveryManager import com.wire.kalium.logic.feature.conversation.ConversationsRecoveryManagerImpl -import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationUseCase -import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationUseCaseImpl -import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationsUseCase -import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationsUseCaseImpl -import com.wire.kalium.logic.data.conversation.JoinSubconversationUseCase -import com.wire.kalium.logic.data.conversation.JoinSubconversationUseCaseImpl -import com.wire.kalium.logic.data.conversation.LeaveSubconversationUseCase -import com.wire.kalium.logic.data.conversation.LeaveSubconversationUseCaseImpl import com.wire.kalium.logic.feature.conversation.MLSConversationsRecoveryManager import com.wire.kalium.logic.feature.conversation.MLSConversationsRecoveryManagerImpl import com.wire.kalium.logic.feature.conversation.MLSConversationsVerificationStatusesHandler @@ -208,9 +212,9 @@ import com.wire.kalium.logic.feature.featureConfig.handler.E2EIConfigHandler import com.wire.kalium.logic.feature.featureConfig.handler.FileSharingConfigHandler import com.wire.kalium.logic.feature.featureConfig.handler.GuestRoomConfigHandler import com.wire.kalium.logic.feature.featureConfig.handler.MLSConfigHandler +import com.wire.kalium.logic.feature.featureConfig.handler.MLSMigrationConfigHandler import com.wire.kalium.logic.feature.featureConfig.handler.SecondFactorPasswordChallengeConfigHandler import com.wire.kalium.logic.feature.featureConfig.handler.SelfDeletingMessagesConfigHandler -import com.wire.kalium.logic.feature.featureConfig.handler.MLSMigrationConfigHandler import com.wire.kalium.logic.feature.keypackage.KeyPackageManager import com.wire.kalium.logic.feature.keypackage.KeyPackageManagerImpl import com.wire.kalium.logic.feature.message.AddSystemMessageToAllConversationsUseCase @@ -372,6 +376,8 @@ import com.wire.kalium.logic.sync.receiver.handler.MessageTextEditHandlerImpl import com.wire.kalium.logic.sync.receiver.handler.ReceiptMessageHandlerImpl import com.wire.kalium.logic.sync.receiver.handler.TypingIndicatorHandler import com.wire.kalium.logic.sync.receiver.handler.TypingIndicatorHandlerImpl +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandlerImpl +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldRequestHandlerImpl import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCase import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCaseImpl import com.wire.kalium.logic.sync.slow.SlowSlowSyncCriteriaProviderImpl @@ -1305,6 +1311,31 @@ class UserSessionScope internal constructor( protocolUpdateEventHandler ) } + override val coroutineContext: CoroutineContext = SupervisorJob() + + private val legalHoldRequestHandler = LegalHoldRequestHandlerImpl( + selfUserId = userId, + userConfigRepository = userConfigRepository + ) + + private val fetchSelfClientsFromRemote : FetchSelfClientsFromRemoteUseCase + get() = FetchSelfClientsFromRemoteUseCaseImpl( + clientRepository = clientRepository, + provideClientId = clientIdProvider + ) + private val persistOtherUserClients : PersistOtherUserClientsUseCase + get() = PersistOtherUserClientsUseCaseImpl( + clientRemoteRepository = clientRemoteRepository, + clientRepository = clientRepository + ) + + private val legalHoldHandler = LegalHoldHandlerImpl( + selfUserId = userId, + persistOtherUserClients = persistOtherUserClients, + fetchSelfClientsFromRemote = fetchSelfClientsFromRemote, + userConfigRepository = userConfigRepository, + coroutineContext = coroutineContext + ) private val userEventReceiver: UserEventReceiver get() = UserEventReceiverImpl( @@ -1316,7 +1347,9 @@ class UserSessionScope internal constructor( oneOnOneResolver, userId, clientIdProvider, - lazy { conversations.newGroupConversationSystemMessagesCreator } + lazy { conversations.newGroupConversationSystemMessagesCreator }, + legalHoldRequestHandler, + legalHoldHandler ) private val userPropertiesEventReceiver: UserPropertiesEventReceiver @@ -1455,9 +1488,7 @@ class UserSessionScope internal constructor( authenticationScope.secondFactorVerificationRepository, slowSyncRepository, cachedClientIdClearer, - updateSupportedProtocolsAndResolveOneOnOnes, - conversationRepository, - persistMessage + updateSupportedProtocolsAndResolveOneOnOnes ) val conversations: ConversationScope by lazy { ConversationScope( @@ -1710,8 +1741,6 @@ class UserSessionScope internal constructor( clientRepository, notificationTokenRepository, pushTokenRepository ) - override val coroutineContext: CoroutineContext = SupervisorJob() - private val mlsConversationsVerificationStatusesHandler: MLSConversationsVerificationStatusesHandler by lazy { MLSConversationsVerificationStatusesHandlerImpl(conversationRepository, persistMessage, mlsConversationRepository, userId) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/ClientScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/ClientScope.kt index face58d064b..99a7955a1b6 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/ClientScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/ClientScope.kt @@ -22,12 +22,12 @@ import com.wire.kalium.logic.configuration.notification.NotificationTokenReposit import com.wire.kalium.logic.data.auth.verification.SecondFactorVerificationRepository import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.client.MLSClientProvider +import com.wire.kalium.logic.data.client.ProteusClientProvider import com.wire.kalium.logic.data.client.remote.ClientRemoteRepository -import com.wire.kalium.logic.data.conversation.ConversationRepository +import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.data.keypackage.KeyPackageLimitsProvider import com.wire.kalium.logic.data.keypackage.KeyPackageRepository import com.wire.kalium.logic.data.logout.LogoutRepository -import com.wire.kalium.logic.data.message.PersistMessageUseCase import com.wire.kalium.logic.data.notification.PushTokenRepository import com.wire.kalium.logic.data.prekey.PreKeyRepository import com.wire.kalium.logic.data.session.SessionRepository @@ -35,8 +35,6 @@ import com.wire.kalium.logic.data.sync.SlowSyncRepository import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.UserRepository import com.wire.kalium.logic.feature.CachedClientIdClearer -import com.wire.kalium.logic.data.id.CurrentClientIdProvider -import com.wire.kalium.logic.data.client.ProteusClientProvider import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCase import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCaseImpl import com.wire.kalium.logic.feature.keypackage.RefillKeyPackagesUseCase @@ -70,9 +68,7 @@ class ClientScope @OptIn(DelicateKaliumApi::class) internal constructor( private val secondFactorVerificationRepository: SecondFactorVerificationRepository, private val slowSyncRepository: SlowSyncRepository, private val cachedClientIdClearer: CachedClientIdClearer, - private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase, - private val conversationRepository: ConversationRepository, - private val persistMessage: PersistMessageUseCase + private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase ) { @OptIn(DelicateKaliumApi::class) val register: RegisterClientUseCase diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt index 6844dee0612..b96e3b2a2f0 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt @@ -27,11 +27,11 @@ import com.wire.kalium.logic.data.conversation.NewGroupConversationSystemMessage import com.wire.kalium.logic.data.event.Event import com.wire.kalium.logic.data.event.EventLoggingStatus import com.wire.kalium.logic.data.event.logEventProcessing +import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.data.logout.LogoutReason import com.wire.kalium.logic.data.user.ConnectionState import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.data.user.UserRepository -import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.feature.auth.LogoutUseCase import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolver import com.wire.kalium.logic.functional.Either @@ -41,6 +41,8 @@ import com.wire.kalium.logic.functional.map import com.wire.kalium.logic.functional.onFailure import com.wire.kalium.logic.functional.onSuccess import com.wire.kalium.logic.kaliumLogger +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandler +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldRequestHandler import kotlin.time.Duration.Companion.ZERO import kotlin.time.Duration.Companion.seconds @@ -56,7 +58,9 @@ internal class UserEventReceiverImpl internal constructor( private val oneOnOneResolver: OneOnOneResolver, private val selfUserId: UserId, private val currentClientIdProvider: CurrentClientIdProvider, - private val newGroupConversationSystemMessagesCreator: Lazy + private val newGroupConversationSystemMessagesCreator: Lazy, + private val legalHoldRequestHandler: LegalHoldRequestHandler, + private val legalHoldHandler: LegalHoldHandler ) : UserEventReceiver { override suspend fun onEvent(event: Event.User): Either { @@ -66,6 +70,9 @@ internal class UserEventReceiverImpl internal constructor( is Event.User.UserDelete -> handleUserDelete(event) is Event.User.Update -> handleUserUpdate(event) is Event.User.NewClient -> handleNewClient(event) + is Event.User.LegalHoldRequest-> legalHoldRequestHandler.handle(event) + is Event.User.LegalHoldEnabled-> legalHoldHandler.handleEnable(event) + is Event.User.LegalHoldDisabled-> legalHoldHandler.handleDisable(event) } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt new file mode 100644 index 00000000000..34eaf5a167e --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt @@ -0,0 +1,69 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.sync.receiver.handler.legalhold + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.event.Event +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.client.FetchSelfClientsFromRemoteUseCase +import com.wire.kalium.logic.feature.client.PersistOtherUserClientsUseCase +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.kaliumLogger +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlin.coroutines.CoroutineContext + +interface LegalHoldHandler { + fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either + fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either +} + +class LegalHoldHandlerImpl internal constructor( + private val selfUserId: UserId, + private val persistOtherUserClients: PersistOtherUserClientsUseCase, + private val fetchSelfClientsFromRemote: FetchSelfClientsFromRemoteUseCase, + private val userConfigRepository: UserConfigRepository, + private val coroutineContext: CoroutineContext, + private val coroutineScope: CoroutineScope = CoroutineScope(coroutineContext) +) : LegalHoldHandler { + override fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either { + kaliumLogger.i("legal hold enabled for user ${legalHoldEnable.userId.toLogString()}") + processEvent(selfUserId, legalHoldEnable.userId) + return Either.Right(Unit) + } + + override fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either { + kaliumLogger.i("legal hold disabled for user ${legalHoldEnable.userId.toLogString()}") + processEvent(selfUserId, legalHoldEnable.userId) + return Either.Right(Unit) + } + + private fun processEvent(selfUserId: UserId, userId: UserId) { + if (selfUserId == userId) { + userConfigRepository.deleteLegalHoldRequest() + coroutineScope.launch { + fetchSelfClientsFromRemote() + } + } else { + coroutineScope.launch { + persistOtherUserClients(userId) + } + } + } +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt new file mode 100644 index 00000000000..4e928cd4b21 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt @@ -0,0 +1,46 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.sync.receiver.handler.legalhold + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.event.Event +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.kaliumLogger + +interface LegalHoldRequestHandler { + fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either +} + +class LegalHoldRequestHandlerImpl internal constructor( + private val selfUserId: UserId, + private val userConfigRepository: UserConfigRepository +) : LegalHoldRequestHandler { + override fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either { + if (selfUserId == legalHoldRequest.userId) { + kaliumLogger.i("Legal hold request received, storing it locally..") + userConfigRepository.setLegalHoldRequest( + legalHoldRequest.clientId.value, + legalHoldRequest.lastPreKey.id, + legalHoldRequest.lastPreKey.key + ) + } + return Either.Right(Unit) + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt index a24fa4ed765..ace49053405 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiverTest.kt @@ -33,6 +33,8 @@ import com.wire.kalium.logic.feature.auth.LogoutUseCase import com.wire.kalium.logic.framework.TestConversation import com.wire.kalium.logic.framework.TestEvent import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldHandler +import com.wire.kalium.logic.sync.receiver.handler.legalhold.LegalHoldRequestHandler import com.wire.kalium.logic.test_util.TestKaliumDispatcher import com.wire.kalium.logic.util.arrangement.UserRepositoryArrangement import com.wire.kalium.logic.util.arrangement.UserRepositoryArrangementImpl @@ -272,6 +274,11 @@ class UserEventReceiverTest { @Mock val newGroupConversationSystemMessagesCreator = mock(classOf()) + @Mock + val legalHoldRequestHandler = mock(classOf()) + @Mock + val legalHoldHandler = mock(classOf()) + private val userEventReceiver: UserEventReceiver = UserEventReceiverImpl( clientRepository, connectionRepository, @@ -281,7 +288,9 @@ class UserEventReceiverTest { oneOnOneResolver, SELF_USER_ID, currentClientIdProvider, - lazy { newGroupConversationSystemMessagesCreator } + lazy { newGroupConversationSystemMessagesCreator }, + legalHoldRequestHandler, + legalHoldHandler ) init { diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt new file mode 100644 index 00000000000..5371a82b2cb --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt @@ -0,0 +1,139 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.sync.receiver.handler.legalhold + +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.conversation.ClientId +import com.wire.kalium.logic.data.event.Event +import com.wire.kalium.logic.feature.client.FetchSelfClientsFromRemoteUseCase +import com.wire.kalium.logic.feature.client.PersistOtherUserClientsUseCase +import com.wire.kalium.logic.feature.client.SelfClientsResult +import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.functional.Either +import io.mockative.Mock +import io.mockative.any +import io.mockative.given +import io.mockative.mock +import io.mockative.once +import io.mockative.verify +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import kotlin.test.BeforeTest +import kotlin.test.Test + +class LegalHoldHandlerTest { + + @OptIn(ExperimentalCoroutinesApi::class) + @BeforeTest + fun setup() { + Dispatchers.setMain(StandardTestDispatcher()) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun givenLegalHoldEvent_whenUserIdIsSelfUserThenUpdateSelfUserClients() = runTest { + val (arrangement, handler) = Arrangement() + .withDeleteLegalHoldSuccess() + .withFetchSelfClientsFromRemoteSuccess() + .arrange() + + handler.handleEnable(legalHoldEventEnabled) + + verify(arrangement.userConfigRepository) + .function(arrangement.userConfigRepository::deleteLegalHoldRequest) + .wasInvoked(once) + + advanceUntilIdle() + verify(arrangement.fetchSelfClientsFromRemote) + .suspendFunction(arrangement.fetchSelfClientsFromRemote::invoke) + .wasInvoked(once) + } + + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun givenLegalHoldEvent_whenUserIdIsOtherUserThenUpdateOtherUserClients() = runTest { + val (arrangement, handler) = Arrangement().arrange() + + handler.handleDisable(legalHoldEventDisabled) + + verify(arrangement.userConfigRepository) + .function(arrangement.userConfigRepository::deleteLegalHoldRequest) + .wasNotInvoked() + + advanceUntilIdle() + verify(arrangement.persistOtherUserClients) + .suspendFunction(arrangement.persistOtherUserClients::invoke) + .with(any()) + .wasInvoked(once) + } + + private class Arrangement { + + @Mock + val persistOtherUserClients = mock(PersistOtherUserClientsUseCase::class) + + @Mock + val fetchSelfClientsFromRemote = mock(FetchSelfClientsFromRemoteUseCase::class) + + @Mock + val userConfigRepository = mock(UserConfigRepository::class) + + fun arrange() = + this to LegalHoldHandlerImpl( + selfUserId = TestUser.SELF.id, + persistOtherUserClients = persistOtherUserClients, + fetchSelfClientsFromRemote = fetchSelfClientsFromRemote, + userConfigRepository = userConfigRepository, + coroutineContext = StandardTestDispatcher() + ) + + fun withDeleteLegalHoldSuccess() = apply { + given(userConfigRepository) + .function(userConfigRepository::deleteLegalHoldRequest) + .whenInvoked() + .thenReturn(Either.Right(Unit)) + } + + fun withFetchSelfClientsFromRemoteSuccess() = apply { + given(fetchSelfClientsFromRemote) + .suspendFunction(fetchSelfClientsFromRemote::invoke) + .whenInvoked() + .thenReturn(SelfClientsResult.Success(emptyList(), ClientId("client-id"))) + } + } + + companion object { + val legalHoldEventEnabled = Event.User.LegalHoldEnabled( + transient = false, + live = false, + id = "id-1", + userId = TestUser.SELF.id, + ) + val legalHoldEventDisabled = Event.User.LegalHoldDisabled( + transient = false, + live = false, + id = "id-2", + userId = TestUser.OTHER_USER_ID + ) + } +} \ No newline at end of file diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt new file mode 100644 index 00000000000..84fcf55e599 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt @@ -0,0 +1,105 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.sync.receiver.handler.legalhold + +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.conversation.ClientId +import com.wire.kalium.logic.data.event.Event +import com.wire.kalium.logic.data.legalhold.LastPreKey +import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.util.shouldSucceed +import io.mockative.Mock +import io.mockative.any +import io.mockative.eq +import io.mockative.given +import io.mockative.mock +import io.mockative.once +import io.mockative.verify +import kotlin.test.Test + +class LegalHoldRequestHandlerTest { + + @Test + fun givenLegalHoldRequestEvent_whenUserIdIsSelfUser_thenStoreRequestLocally() { + val (arrangement, handler) = Arrangement() + .withSetLegalHoldSuccess() + .arrange() + + val result = handler.handle(legalHoldRequestSelfUser) + + result.shouldSucceed() + verify(arrangement.userConfigRepository) + .function(arrangement.userConfigRepository::setLegalHoldRequest) + .with( + eq(legalHoldRequestSelfUser.clientId.value), + eq(legalHoldRequestSelfUser.lastPreKey.id), + eq(legalHoldRequestSelfUser.lastPreKey.key) + ) + .wasInvoked(once) + } + + @Test + fun givenLegalHoldRequestEvent_whenUserIdIsNotIsSelfUser_thenIgnoreEvent() { + val (arrangement, handler) = Arrangement() + .arrange() + + val result = handler.handle(legalHoldRequestOtherUser) + + result.shouldSucceed() + verify(arrangement.userConfigRepository) + .function(arrangement.userConfigRepository::setLegalHoldRequest) + .with(any(), any(), any()) + .wasNotInvoked() + } + + private class Arrangement { + + @Mock + val userConfigRepository: UserConfigRepository = mock(UserConfigRepository::class) + + fun arrange() = + this to LegalHoldRequestHandlerImpl(TestUser.SELF.id, userConfigRepository) + + fun withSetLegalHoldSuccess() = apply { + given(userConfigRepository) + .function(userConfigRepository::setLegalHoldRequest) + .whenInvokedWith(any(), any(), any()) + .thenReturn(Either.Right(Unit)) + } + } + + companion object { + private val legalHoldRequestSelfUser = Event.User.LegalHoldRequest( + transient = false, + live = false, + id = "event-id", + clientId = ClientId("client-id"), + lastPreKey = LastPreKey(3, "key"), + userId = TestUser.SELF.id + ) + private val legalHoldRequestOtherUser = Event.User.LegalHoldRequest( + transient = false, + live = false, + id = "event-id", + clientId = ClientId("client-id"), + lastPreKey = LastPreKey(3, "key"), + userId = TestUser.OTHER_USER_ID + ) + } +} diff --git a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt new file mode 100644 index 00000000000..9b274e3db3b --- /dev/null +++ b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt @@ -0,0 +1,27 @@ +/* + * Wire + * Copyright (C) 2023 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.base.authenticated.client + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ClientIdDTO ( + @SerialName("id") + val clientId: String +) diff --git a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/keypackage/LastPreKeyDTO.kt b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/keypackage/LastPreKeyDTO.kt new file mode 100644 index 00000000000..e0e43ba5ec9 --- /dev/null +++ b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/keypackage/LastPreKeyDTO.kt @@ -0,0 +1,29 @@ +/* + * Wire + * Copyright (C) 2023 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.base.authenticated.keypackage + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class LastPreKeyDTO( + @SerialName("id") + val id: Int, + @SerialName("key") + val key: String, +) diff --git a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/notification/EventContentDTO.kt b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/notification/EventContentDTO.kt index 1eaab3731c1..84b3bc90621 100644 --- a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/notification/EventContentDTO.kt +++ b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/notification/EventContentDTO.kt @@ -19,6 +19,7 @@ package com.wire.kalium.network.api.base.authenticated.notification import com.wire.kalium.network.api.base.authenticated.client.ClientDTO +import com.wire.kalium.network.api.base.authenticated.client.ClientIdDTO import com.wire.kalium.network.api.base.authenticated.connection.ConnectionDTO import com.wire.kalium.network.api.base.authenticated.conversation.ConversationMembers import com.wire.kalium.network.api.base.authenticated.conversation.ConversationNameUpdateEvent @@ -33,6 +34,7 @@ import com.wire.kalium.network.api.base.authenticated.conversation.model.Convers import com.wire.kalium.network.api.base.authenticated.conversation.model.ConversationReceiptModeDTO import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigData import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureFlagStatusDTO +import com.wire.kalium.network.api.base.authenticated.keypackage.LastPreKeyDTO import com.wire.kalium.network.api.base.authenticated.notification.conversation.MessageEventData import com.wire.kalium.network.api.base.authenticated.notification.team.PermissionsData import com.wire.kalium.network.api.base.authenticated.notification.team.TeamMemberIdData @@ -343,6 +345,26 @@ sealed class EventContentDTO { @SerialName("connection") val connection: ConnectionDTO, ) : User() + @Serializable + @SerialName("user.legalhold-request") + data class NewLegalHoldRequestDTO( + @SerialName("client") val clientId: ClientIdDTO, + @SerialName("last_prekey") val lastPreKey: LastPreKeyDTO, + @SerialName("id") val id: String, + ) : User() + + @Serializable + @SerialName("user.legalhold-enable") + data class LegalHoldEnabledDTO( + @SerialName("id") val id: String + ) : User() + + @Serializable + @SerialName("user.legalhold-disable") + data class LegalHoldDisabledDTO( + @SerialName("id") val id: String + ) : User() + // TODO user.push-remove @Serializable diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt index 6bc36aa42e7..181ad069f41 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt @@ -173,6 +173,11 @@ interface UserConfigStorage { fun setE2EINotificationTime(timeStamp: Long) fun getE2EINotificationTime(): Long? fun e2EINotificationTimeFlow(): Flow + fun persistLegalHoldRequest(clientId: String, lastPreKeyId: Int, lastPreKey: String) + fun clearLegalHoldRequest() + fun getLegalHoldRequest(): LegalHoldRequestEntity? + fun observeLegalHoldRequest(): Flow + } @Serializable @@ -213,6 +218,18 @@ data class AppLockConfigEntity( @SerialName("isStatusChanged") val isStatusChanged: Boolean? ) +@Serializable +data class LegalHoldRequestEntity( + @SerialName("clientId") val clientId: String, + @SerialName("lastPrekey") val lastPreKey: LastPreKey, +) + +@Serializable +data class LastPreKey( + @SerialName("id") val id: Int, + @SerialName("key") val key: String, +) + @Serializable sealed class SelfDeletionTimerEntity { @@ -242,31 +259,64 @@ class UserConfigStorageImpl( ) : UserConfigStorage { private val areReadReceiptsEnabledFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val isTypingIndicatorEnabledFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val isFileSharingEnabledFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val isClassifiedDomainsEnabledFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val isGuestRoomLinkEnabledFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val e2EIFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val e2EINotificationFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val isScreenshotCensoringEnabledFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) private val appLockFlow = - MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + + private val legalHoldRequestFlow = + MutableSharedFlow( + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) override fun persistAppLockStatus( isEnforced: Boolean, @@ -284,7 +334,8 @@ class UserConfigStorageImpl( override fun setTeamAppLockAsNotified() { val newValue = - kaliumPreferences.getSerializable(APP_LOCK, AppLockConfigEntity.serializer())?.copy(isStatusChanged = false) + kaliumPreferences.getSerializable(APP_LOCK, AppLockConfigEntity.serializer()) + ?.copy(isStatusChanged = false) ?: return kaliumPreferences.putSerializable( APP_LOCK, @@ -320,14 +371,16 @@ class UserConfigStorageImpl( override fun isFileSharingEnabled(): IsFileSharingEnabledEntity? = kaliumPreferences.getSerializable(FILE_SHARING, IsFileSharingEnabledEntity.serializer()) - override fun isFileSharingEnabledFlow(): Flow = isFileSharingEnabledFlow - .map { isFileSharingEnabled() } - .onStart { emit(isFileSharingEnabled()) } - .distinctUntilChanged() + override fun isFileSharingEnabledFlow(): Flow = + isFileSharingEnabledFlow + .map { isFileSharingEnabled() } + .onStart { emit(isFileSharingEnabled()) } + .distinctUntilChanged() override fun setFileSharingAsNotified() { val newValue = - kaliumPreferences.getSerializable(FILE_SHARING, IsFileSharingEnabledEntity.serializer())?.copy(isStatusChanged = false) + kaliumPreferences.getSerializable(FILE_SHARING, IsFileSharingEnabledEntity.serializer()) + ?.copy(isStatusChanged = false) ?: return kaliumPreferences.putSerializable( FILE_SHARING, @@ -341,7 +394,10 @@ class UserConfigStorageImpl( override fun isClassifiedDomainsEnabledFlow(): Flow { return isClassifiedDomainsEnabledFlow .map { - kaliumPreferences.getSerializable(ENABLE_CLASSIFIED_DOMAINS, ClassifiedDomainsEntity.serializer())!! + kaliumPreferences.getSerializable( + ENABLE_CLASSIFIED_DOMAINS, + ClassifiedDomainsEntity.serializer() + )!! }.onStart { emit( kaliumPreferences.getSerializable( @@ -425,7 +481,10 @@ class UserConfigStorageImpl( } override fun isConferenceCallingEnabled(): Boolean = - kaliumPreferences.getBoolean(ENABLE_CONFERENCE_CALLING, DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE) + kaliumPreferences.getBoolean( + ENABLE_CONFERENCE_CALLING, + DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE + ) override fun areReadReceiptsEnabled(): Flow = areReadReceiptsEnabledFlow .map { kaliumPreferences.getBoolean(ENABLE_READ_RECEIPTS, true) } @@ -463,7 +522,10 @@ class UserConfigStorageImpl( } override fun isGuestRoomLinkEnabled(): IsGuestRoomLinkEnabledEntity? = - kaliumPreferences.getSerializable(GUEST_ROOM_LINK, IsGuestRoomLinkEnabledEntity.serializer()) + kaliumPreferences.getSerializable( + GUEST_ROOM_LINK, + IsGuestRoomLinkEnabledEntity.serializer() + ) override fun isGuestRoomLinkEnabledFlow(): Flow = isGuestRoomLinkEnabledFlow @@ -471,10 +533,11 @@ class UserConfigStorageImpl( .onStart { emit(isGuestRoomLinkEnabled()) } .distinctUntilChanged() - override fun isScreenshotCensoringEnabledFlow(): Flow = isScreenshotCensoringEnabledFlow - .map { kaliumPreferences.getBoolean(ENABLE_SCREENSHOT_CENSORING, false) } - .onStart { emit(kaliumPreferences.getBoolean(ENABLE_SCREENSHOT_CENSORING, false)) } - .distinctUntilChanged() + override fun isScreenshotCensoringEnabledFlow(): Flow = + isScreenshotCensoringEnabledFlow + .map { kaliumPreferences.getBoolean(ENABLE_SCREENSHOT_CENSORING, false) } + .onStart { emit(kaliumPreferences.getBoolean(ENABLE_SCREENSHOT_CENSORING, false)) } + .distinctUntilChanged() override fun persistScreenshotCensoring(enabled: Boolean) { kaliumPreferences.putBoolean(ENABLE_SCREENSHOT_CENSORING, enabled).also { @@ -482,6 +545,36 @@ class UserConfigStorageImpl( } } + override fun persistLegalHoldRequest( + clientId: String, + lastPreKeyId: Int, + lastPreKey: String + ) { + kaliumPreferences.putSerializable( + LEGAL_HOLD_REQUEST, + LegalHoldRequestEntity(clientId, LastPreKey(lastPreKeyId, lastPreKey)), + LegalHoldRequestEntity.serializer(), + ).also { + legalHoldRequestFlow.tryEmit(Unit) + } + } + + override fun getLegalHoldRequest(): LegalHoldRequestEntity? { + return kaliumPreferences.getSerializable( + LEGAL_HOLD_REQUEST, + LegalHoldRequestEntity.serializer() + ) + } + + override fun observeLegalHoldRequest(): Flow = legalHoldRequestFlow + .map { getLegalHoldRequest() } + .onStart { emit(getLegalHoldRequest()) } + .distinctUntilChanged() + + override fun clearLegalHoldRequest() { + kaliumPreferences.remove(LEGAL_HOLD_REQUEST) + } + private companion object { const val FILE_SHARING = "file_sharing" const val GUEST_ROOM_LINK = "guest_room_link" @@ -492,10 +585,12 @@ class UserConfigStorageImpl( const val ENABLE_CONFERENCE_CALLING = "enable_conference_calling" const val ENABLE_READ_RECEIPTS = "enable_read_receipts" const val DEFAULT_CONFERENCE_CALLING_ENABLED_VALUE = false - const val REQUIRE_SECOND_FACTOR_PASSWORD_CHALLENGE = "require_second_factor_password_challenge" + const val REQUIRE_SECOND_FACTOR_PASSWORD_CHALLENGE = + "require_second_factor_password_challenge" const val ENABLE_SCREENSHOT_CENSORING = "enable_screenshot_censoring" const val ENABLE_TYPING_INDICATOR = "enable_typing_indicator" const val APP_LOCK = "app_lock" const val DEFAULT_PROTOCOL = "default_protocol" + const val LEGAL_HOLD_REQUEST = "legal_hold_request" } } From 114e47a16832d50c6e51c30c5aa1e80cb715c02d Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 21 Nov 2023 11:39:34 +0100 Subject: [PATCH 02/10] chore: address comments --- .../configuration/UserConfigRepository.kt | 12 +++---- .../handler/legalhold/LegalHoldHandler.kt | 14 ++++---- .../legalhold/LegalHoldRequestHandler.kt | 8 ++--- .../handler/legalhold/LegalHoldHandlerTest.kt | 6 ++-- .../legalhold/LegalHoldRequestHandlerTest.kt | 11 +++--- .../persistence/config/UserConfigStorage.kt | 36 ------------------- .../persistence/dao/unread/UserConfigDAO.kt | 23 ++++++++++++ 7 files changed, 49 insertions(+), 61 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt index dbef7f9daaa..4a99a350cde 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt @@ -107,12 +107,12 @@ interface UserConfigRepository { fun setE2EINotificationTime(instant: Instant): Either suspend fun getMigrationConfiguration(): Either suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either - fun setLegalHoldRequest( + suspend fun setLegalHoldRequest( clientId: String, lastPreKeyId: Int, lastPreKey: String ): Either - fun deleteLegalHoldRequest(): Either + suspend fun deleteLegalHoldRequest(): Either } @Suppress("TooManyFunctions") @@ -390,15 +390,15 @@ class UserConfigDataSource( userConfigDAO.setMigrationConfiguration(configuration.toEntity()) } - override fun setLegalHoldRequest( + override suspend fun setLegalHoldRequest( clientId: String, lastPreKeyId: Int, lastPreKey: String ): Either = wrapStorageRequest { - userConfigStorage.persistLegalHoldRequest(clientId, lastPreKeyId, lastPreKey) + userConfigDAO.persistLegalHoldRequest(clientId, lastPreKeyId, lastPreKey) } - override fun deleteLegalHoldRequest(): Either = wrapStorageRequest { - userConfigStorage.clearLegalHoldRequest() + override suspend fun deleteLegalHoldRequest(): Either = wrapStorageRequest { + userConfigDAO.clearLegalHoldRequest() } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt index 34eaf5a167e..4b1094cf7d3 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandler.kt @@ -29,12 +29,12 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlin.coroutines.CoroutineContext -interface LegalHoldHandler { - fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either - fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either +internal interface LegalHoldHandler { + suspend fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either + suspend fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either } -class LegalHoldHandlerImpl internal constructor( +internal class LegalHoldHandlerImpl internal constructor( private val selfUserId: UserId, private val persistOtherUserClients: PersistOtherUserClientsUseCase, private val fetchSelfClientsFromRemote: FetchSelfClientsFromRemoteUseCase, @@ -42,19 +42,19 @@ class LegalHoldHandlerImpl internal constructor( private val coroutineContext: CoroutineContext, private val coroutineScope: CoroutineScope = CoroutineScope(coroutineContext) ) : LegalHoldHandler { - override fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either { + override suspend fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either { kaliumLogger.i("legal hold enabled for user ${legalHoldEnable.userId.toLogString()}") processEvent(selfUserId, legalHoldEnable.userId) return Either.Right(Unit) } - override fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either { + override suspend fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either { kaliumLogger.i("legal hold disabled for user ${legalHoldEnable.userId.toLogString()}") processEvent(selfUserId, legalHoldEnable.userId) return Either.Right(Unit) } - private fun processEvent(selfUserId: UserId, userId: UserId) { + private suspend fun processEvent(selfUserId: UserId, userId: UserId) { if (selfUserId == userId) { userConfigRepository.deleteLegalHoldRequest() coroutineScope.launch { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt index 4e928cd4b21..dc8620a7e73 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt @@ -24,15 +24,15 @@ import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.kaliumLogger -interface LegalHoldRequestHandler { - fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either +internal interface LegalHoldRequestHandler { + suspend fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either } -class LegalHoldRequestHandlerImpl internal constructor( +internal class LegalHoldRequestHandlerImpl internal constructor( private val selfUserId: UserId, private val userConfigRepository: UserConfigRepository ) : LegalHoldRequestHandler { - override fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either { + override suspend fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either { if (selfUserId == legalHoldRequest.userId) { kaliumLogger.i("Legal hold request received, storing it locally..") userConfigRepository.setLegalHoldRequest( diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt index 5371a82b2cb..daa19e78801 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldHandlerTest.kt @@ -59,7 +59,7 @@ class LegalHoldHandlerTest { handler.handleEnable(legalHoldEventEnabled) verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::deleteLegalHoldRequest) + .suspendFunction(arrangement.userConfigRepository::deleteLegalHoldRequest) .wasInvoked(once) advanceUntilIdle() @@ -77,7 +77,7 @@ class LegalHoldHandlerTest { handler.handleDisable(legalHoldEventDisabled) verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::deleteLegalHoldRequest) + .suspendFunction(arrangement.userConfigRepository::deleteLegalHoldRequest) .wasNotInvoked() advanceUntilIdle() @@ -109,7 +109,7 @@ class LegalHoldHandlerTest { fun withDeleteLegalHoldSuccess() = apply { given(userConfigRepository) - .function(userConfigRepository::deleteLegalHoldRequest) + .suspendFunction(userConfigRepository::deleteLegalHoldRequest) .whenInvoked() .thenReturn(Either.Right(Unit)) } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt index 84fcf55e599..f46877a0ad9 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt @@ -31,12 +31,13 @@ import io.mockative.given import io.mockative.mock import io.mockative.once import io.mockative.verify +import kotlinx.coroutines.test.runTest import kotlin.test.Test class LegalHoldRequestHandlerTest { @Test - fun givenLegalHoldRequestEvent_whenUserIdIsSelfUser_thenStoreRequestLocally() { + fun givenLegalHoldRequestEvent_whenUserIdIsSelfUser_thenStoreRequestLocally() = runTest { val (arrangement, handler) = Arrangement() .withSetLegalHoldSuccess() .arrange() @@ -45,7 +46,7 @@ class LegalHoldRequestHandlerTest { result.shouldSucceed() verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setLegalHoldRequest) + .suspendFunction(arrangement.userConfigRepository::setLegalHoldRequest) .with( eq(legalHoldRequestSelfUser.clientId.value), eq(legalHoldRequestSelfUser.lastPreKey.id), @@ -55,7 +56,7 @@ class LegalHoldRequestHandlerTest { } @Test - fun givenLegalHoldRequestEvent_whenUserIdIsNotIsSelfUser_thenIgnoreEvent() { + fun givenLegalHoldRequestEvent_whenUserIdIsNotIsSelfUser_thenIgnoreEvent() = runTest { val (arrangement, handler) = Arrangement() .arrange() @@ -63,7 +64,7 @@ class LegalHoldRequestHandlerTest { result.shouldSucceed() verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setLegalHoldRequest) + .suspendFunction(arrangement.userConfigRepository::setLegalHoldRequest) .with(any(), any(), any()) .wasNotInvoked() } @@ -78,7 +79,7 @@ class LegalHoldRequestHandlerTest { fun withSetLegalHoldSuccess() = apply { given(userConfigRepository) - .function(userConfigRepository::setLegalHoldRequest) + .suspendFunction(userConfigRepository::setLegalHoldRequest) .whenInvokedWith(any(), any(), any()) .thenReturn(Either.Right(Unit)) } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt index 181ad069f41..fc72ec8b6af 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/config/UserConfigStorage.kt @@ -173,11 +173,6 @@ interface UserConfigStorage { fun setE2EINotificationTime(timeStamp: Long) fun getE2EINotificationTime(): Long? fun e2EINotificationTimeFlow(): Flow - fun persistLegalHoldRequest(clientId: String, lastPreKeyId: Int, lastPreKey: String) - fun clearLegalHoldRequest() - fun getLegalHoldRequest(): LegalHoldRequestEntity? - fun observeLegalHoldRequest(): Flow - } @Serializable @@ -545,36 +540,6 @@ class UserConfigStorageImpl( } } - override fun persistLegalHoldRequest( - clientId: String, - lastPreKeyId: Int, - lastPreKey: String - ) { - kaliumPreferences.putSerializable( - LEGAL_HOLD_REQUEST, - LegalHoldRequestEntity(clientId, LastPreKey(lastPreKeyId, lastPreKey)), - LegalHoldRequestEntity.serializer(), - ).also { - legalHoldRequestFlow.tryEmit(Unit) - } - } - - override fun getLegalHoldRequest(): LegalHoldRequestEntity? { - return kaliumPreferences.getSerializable( - LEGAL_HOLD_REQUEST, - LegalHoldRequestEntity.serializer() - ) - } - - override fun observeLegalHoldRequest(): Flow = legalHoldRequestFlow - .map { getLegalHoldRequest() } - .onStart { emit(getLegalHoldRequest()) } - .distinctUntilChanged() - - override fun clearLegalHoldRequest() { - kaliumPreferences.remove(LEGAL_HOLD_REQUEST) - } - private companion object { const val FILE_SHARING = "file_sharing" const val GUEST_ROOM_LINK = "guest_room_link" @@ -591,6 +556,5 @@ class UserConfigStorageImpl( const val ENABLE_TYPING_INDICATOR = "enable_typing_indicator" const val APP_LOCK = "app_lock" const val DEFAULT_PROTOCOL = "default_protocol" - const val LEGAL_HOLD_REQUEST = "legal_hold_request" } } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt index e39e7149c83..e0defbdc68a 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt @@ -17,8 +17,11 @@ */ package com.wire.kalium.persistence.dao.unread +import com.wire.kalium.persistence.config.LastPreKey +import com.wire.kalium.persistence.config.LegalHoldRequestEntity import com.wire.kalium.persistence.config.MLSMigrationEntity import com.wire.kalium.persistence.config.TeamSettingsSelfDeletionStatusEntity +import com.wire.kalium.persistence.config.UserConfigStorageImpl import com.wire.kalium.persistence.dao.MetadataDAO import com.wire.kalium.persistence.dao.SupportedProtocolEntity import kotlinx.coroutines.flow.Flow @@ -39,6 +42,9 @@ interface UserConfigDAO { suspend fun getSupportedProtocols(): Set? suspend fun setSupportedProtocols(protocols: Set) + suspend fun persistLegalHoldRequest(clientId: String, lastPreKeyId: Int, lastPreKey: String) + suspend fun clearLegalHoldRequest() + suspend fun observeLegalHoldRequest(): Flow } internal class UserConfigDAOImpl internal constructor( @@ -84,9 +90,26 @@ internal class UserConfigDAOImpl internal constructor( override suspend fun setSupportedProtocols(protocols: Set) = metadataDAO.putSerializable(SUPPORTED_PROTOCOLS_KEY, protocols, SetSerializer(SupportedProtocolEntity.serializer())) + override suspend fun persistLegalHoldRequest(clientId: String, lastPreKeyId: Int, lastPreKey: String) { + metadataDAO.putSerializable( + LEGAL_HOLD_REQUEST, + LegalHoldRequestEntity(clientId, LastPreKey(lastPreKeyId, lastPreKey)), + LegalHoldRequestEntity.serializer(), + ) + } + + override suspend fun clearLegalHoldRequest() { + metadataDAO.deleteValue(LEGAL_HOLD_REQUEST) + } + + override suspend fun observeLegalHoldRequest(): Flow = + metadataDAO.observeSerializable(LEGAL_HOLD_REQUEST, LegalHoldRequestEntity.serializer()) + + private companion object { private const val SELF_DELETING_MESSAGES_KEY = "SELF_DELETING_MESSAGES" private const val MLS_MIGRATION_KEY = "MLS_MIGRATION" private const val SUPPORTED_PROTOCOLS_KEY = "SUPPORTED_PROTOCOLS" + const val LEGAL_HOLD_REQUEST = "legal_hold_request" } } From c82fb1013005734e968e00784d0741caafc59e42 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 21 Nov 2023 11:42:25 +0100 Subject: [PATCH 03/10] chore: detekt --- .../com/wire/kalium/logic/data/client/ClientRepository.kt | 1 - .../kotlin/com/wire/kalium/logic/data/event/EventMapper.kt | 6 +++--- .../wire/kalium/logic/sync/receiver/UserEventReceiver.kt | 6 +++--- .../network/api/base/authenticated/client/ClientIdDTO.kt | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt index bd5de79dfec..9ab87b388f9 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/client/ClientRepository.kt @@ -27,7 +27,6 @@ import com.wire.kalium.logic.data.event.Event import com.wire.kalium.logic.data.id.toApi import com.wire.kalium.logic.data.id.toDao import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.logic.data.user.UserMapper import com.wire.kalium.logic.di.MapperProvider import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt index e89e1030bd3..5cc09621cc8 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt @@ -367,7 +367,7 @@ class EventMapper( transient: Boolean, live: Boolean, eventContentDTO: EventContentDTO.User.NewLegalHoldRequestDTO - ) : Event.User.LegalHoldRequest { + ) : Event.User.LegalHoldRequest { return Event.User.LegalHoldRequest( transient = transient, live = live, @@ -383,7 +383,7 @@ class EventMapper( transient: Boolean, live: Boolean, eventContentDTO: EventContentDTO.User.LegalHoldEnabledDTO - ) : Event.User.LegalHoldEnabled { + ) : Event.User.LegalHoldEnabled { return Event.User.LegalHoldEnabled( transient, live, @@ -397,7 +397,7 @@ class EventMapper( transient: Boolean, live: Boolean, eventContentDTO: EventContentDTO.User.LegalHoldDisabledDTO - ) : Event.User.LegalHoldDisabled { + ) : Event.User.LegalHoldDisabled { return Event.User.LegalHoldDisabled( transient, live, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt index b96e3b2a2f0..c3daeb7cb7d 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/UserEventReceiver.kt @@ -70,9 +70,9 @@ internal class UserEventReceiverImpl internal constructor( is Event.User.UserDelete -> handleUserDelete(event) is Event.User.Update -> handleUserUpdate(event) is Event.User.NewClient -> handleNewClient(event) - is Event.User.LegalHoldRequest-> legalHoldRequestHandler.handle(event) - is Event.User.LegalHoldEnabled-> legalHoldHandler.handleEnable(event) - is Event.User.LegalHoldDisabled-> legalHoldHandler.handleDisable(event) + is Event.User.LegalHoldRequest -> legalHoldRequestHandler.handle(event) + is Event.User.LegalHoldEnabled -> legalHoldHandler.handleEnable(event) + is Event.User.LegalHoldDisabled -> legalHoldHandler.handleDisable(event) } } diff --git a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt index 9b274e3db3b..0bbb043dbd2 100644 --- a/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt +++ b/network/src/commonMain/kotlin/com/wire/kalium/network/api/base/authenticated/client/ClientIdDTO.kt @@ -21,7 +21,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class ClientIdDTO ( +data class ClientIdDTO( @SerialName("id") val clientId: String ) From 9423254f69a979c59ca9b996f41ed1c9a0e49d3c Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 21 Nov 2023 11:46:30 +0100 Subject: [PATCH 04/10] chore: detekt --- .../com/wire/kalium/logic/data/event/EventMapper.kt | 8 ++++---- .../com/wire/kalium/logic/feature/UserSessionScope.kt | 4 ++-- .../wire/kalium/persistence/dao/unread/UserConfigDAO.kt | 2 -- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt index 5cc09621cc8..5bc6dd20fed 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/event/EventMapper.kt @@ -54,7 +54,7 @@ import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.SerializationException import kotlinx.serialization.serializer -@Suppress("TooManyFunctions", "LongParameterList") +@Suppress("TooManyFunctions", "LongParameterList", "LargeClass") class EventMapper( private val memberMapper: MemberMapper, private val connectionMapper: ConnectionMapper, @@ -367,7 +367,7 @@ class EventMapper( transient: Boolean, live: Boolean, eventContentDTO: EventContentDTO.User.NewLegalHoldRequestDTO - ) : Event.User.LegalHoldRequest { + ): Event.User.LegalHoldRequest { return Event.User.LegalHoldRequest( transient = transient, live = live, @@ -383,7 +383,7 @@ class EventMapper( transient: Boolean, live: Boolean, eventContentDTO: EventContentDTO.User.LegalHoldEnabledDTO - ) : Event.User.LegalHoldEnabled { + ): Event.User.LegalHoldEnabled { return Event.User.LegalHoldEnabled( transient, live, @@ -397,7 +397,7 @@ class EventMapper( transient: Boolean, live: Boolean, eventContentDTO: EventContentDTO.User.LegalHoldDisabledDTO - ) : Event.User.LegalHoldDisabled { + ): Event.User.LegalHoldDisabled { return Event.User.LegalHoldDisabled( transient, live, 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 9bcc68ac2a3..1ad61690645 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 @@ -1320,12 +1320,12 @@ class UserSessionScope internal constructor( userConfigRepository = userConfigRepository ) - private val fetchSelfClientsFromRemote : FetchSelfClientsFromRemoteUseCase + private val fetchSelfClientsFromRemote: FetchSelfClientsFromRemoteUseCase get() = FetchSelfClientsFromRemoteUseCaseImpl( clientRepository = clientRepository, provideClientId = clientIdProvider ) - private val persistOtherUserClients : PersistOtherUserClientsUseCase + private val persistOtherUserClients: PersistOtherUserClientsUseCase get() = PersistOtherUserClientsUseCaseImpl( clientRemoteRepository = clientRemoteRepository, clientRepository = clientRepository diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt index e0defbdc68a..e2e310b253d 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt @@ -21,7 +21,6 @@ import com.wire.kalium.persistence.config.LastPreKey import com.wire.kalium.persistence.config.LegalHoldRequestEntity import com.wire.kalium.persistence.config.MLSMigrationEntity import com.wire.kalium.persistence.config.TeamSettingsSelfDeletionStatusEntity -import com.wire.kalium.persistence.config.UserConfigStorageImpl import com.wire.kalium.persistence.dao.MetadataDAO import com.wire.kalium.persistence.dao.SupportedProtocolEntity import kotlinx.coroutines.flow.Flow @@ -105,7 +104,6 @@ internal class UserConfigDAOImpl internal constructor( override suspend fun observeLegalHoldRequest(): Flow = metadataDAO.observeSerializable(LEGAL_HOLD_REQUEST, LegalHoldRequestEntity.serializer()) - private companion object { private const val SELF_DELETING_MESSAGES_KEY = "SELF_DELETING_MESSAGES" private const val MLS_MIGRATION_KEY = "MLS_MIGRATION" From ce7fa9b9b6c449a7f63ac8f7d6ccad07707b4e23 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 21 Nov 2023 16:35:15 +0100 Subject: [PATCH 05/10] feat: create use case to observe legal hold request --- .../ProteusClientCryptoBoxImpl.kt | 10 ++ .../ProteusClientCoreCryptoImpl.kt | 7 + .../ProteusClientCoreCryptoImpl.kt | 4 + .../wire/kalium/cryptography/ProteusClient.kt | 3 + .../ProteusClientCryptoBoxImpl.kt | 6 + .../ProteusClientCryptoBoxImpl.kt | 6 + .../configuration/UserConfigRepository.kt | 24 ++- .../kalium/logic/data/legalhold/LastPreKey.kt | 2 +- .../logic/data/legalhold/LegalHoldRequest.kt | 25 +++ .../logic/data/prekey/PreKeyRepository.kt | 9 ++ .../kalium/logic/feature/UserSessionScope.kt | 8 + .../legalhold/LegalHoldRequestObserver.kt | 75 +++++++++ .../legalhold/LegalHoldRequestObserverTest.kt | 148 ++++++++++++++++++ .../kalium/persistence/dao/MetadataDAO.kt | 2 +- .../kalium/persistence/dao/MetadataDAOImpl.kt | 2 +- .../persistence/dao/unread/UserConfigDAO.kt | 4 +- 16 files changed, 327 insertions(+), 8 deletions(-) create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LegalHoldRequest.kt create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt diff --git a/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt b/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt index 8233db701af..f539402bb14 100644 --- a/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt +++ b/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt @@ -20,13 +20,16 @@ package com.wire.kalium.cryptography import android.util.Base64 import com.wire.cryptobox.CryptoBox +import com.wire.cryptobox.CryptoBox.getFingerprintFromPrekey import com.wire.cryptobox.CryptoException import com.wire.kalium.cryptography.exceptions.ProteusException +import io.ktor.util.decodeBase64Bytes import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.io.File import kotlin.coroutines.CoroutineContext +import com.wire.cryptobox.PreKey as CryptoBoxPreKey @Suppress("TooManyFunctions") class ProteusClientCryptoBoxImpl constructor( @@ -54,6 +57,13 @@ class ProteusClientCryptoBoxImpl constructor( wrapException { box.getSession(sessionId.value).remoteFingerprint } } + override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray = + withContext(defaultContext) { + kaliumLogger.i("getFingerprintFromPreKey: called from Android") + val cryptoBoxPreKey = CryptoBoxPreKey(preKey.id, preKey.encodedData.decodeBase64Bytes()) + getFingerprintFromPrekey(cryptoBoxPreKey) + } + /** * Create the crypto files if missing and call box.open * this must be called only one time diff --git a/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt b/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt index 24417a75d82..16a44403370 100644 --- a/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt +++ b/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt @@ -45,6 +45,13 @@ class ProteusClientCoreCryptoImpl private constructor(private val coreCrypto: Co return wrapException { coreCrypto.proteusFingerprintRemote(sessionId.value).toByteArray() } } + override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray { + return wrapException { + // TODO this is a hack, we need to expose the fingerprint from the core + return@wrapException "".toByteArray() + } + } + override suspend fun newPreKeys(from: Int, count: Int): ArrayList { return wrapException { from.until(from + count).map { diff --git a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/ProteusClientCoreCryptoImpl.kt b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/ProteusClientCoreCryptoImpl.kt index 367db38fbf7..8404bf72f6e 100644 --- a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/ProteusClientCoreCryptoImpl.kt +++ b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/ProteusClientCoreCryptoImpl.kt @@ -20,6 +20,7 @@ package com.wire.kalium.cryptography import com.wire.crypto.CoreCrypto import com.wire.crypto.CoreCryptoException +import com.wire.crypto.client.toByteArray import com.wire.kalium.cryptography.exceptions.ProteusException import io.ktor.util.decodeBase64Bytes import io.ktor.util.encodeBase64 @@ -45,6 +46,9 @@ class ProteusClientCoreCryptoImpl private constructor( override suspend fun remoteFingerPrint(sessionId: CryptoSessionId): ByteArray = wrapException { coreCrypto.proteusFingerprintRemote(sessionId.value).toByteArray() } + override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray = wrapException { + coreCrypto.proteusFingerprintPrekeybundle(preKey.encodedData.decodeBase64Bytes()).toByteArray() + } override suspend fun newPreKeys(from: Int, count: Int): ArrayList { return wrapException { diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/ProteusClient.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/ProteusClient.kt index dc05db78d9e..8790fd1e345 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/ProteusClient.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/ProteusClient.kt @@ -67,6 +67,9 @@ interface ProteusClient { @Throws(ProteusException::class, CancellationException::class) suspend fun remoteFingerPrint(sessionId: CryptoSessionId): ByteArray + @Throws(ProteusException::class, CancellationException::class) + suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray + suspend fun newPreKeys(from: Int, count: Int): List @Throws(ProteusException::class, CancellationException::class) diff --git a/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt b/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt index dc0916b1075..1a937d67a69 100644 --- a/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt +++ b/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt @@ -26,6 +26,7 @@ import com.wire.kalium.cryptography.externals.PreKeyBundle import io.ktor.util.InternalAPI import io.ktor.util.decodeBase64Bytes import io.ktor.util.encodeBase64 +import io.ktor.utils.io.core.toByteArray import kotlinx.coroutines.await import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.Int8Array @@ -57,6 +58,11 @@ class ProteusClientCryptoBoxImpl : ProteusClient { return box.identity.public_key.fingerprint().encodeToByteArray() } + override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray { + // TODO ("we need to expose the fingerprint from the core") + "".toByteArray() + } + override suspend fun remoteFingerPrint(sessionId: CryptoSessionId): ByteArray { return box.session_load(sessionId.value).await().fingerprint_remote().encodeToByteArray() } diff --git a/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt b/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt index 9b1a9de2e9e..e7a1f722384 100644 --- a/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt +++ b/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt @@ -19,6 +19,7 @@ package com.wire.kalium.cryptography import com.wire.bots.cryptobox.CryptoBox +import com.wire.bots.cryptobox.CryptoBox.getFingerprintFromPrekey import com.wire.bots.cryptobox.CryptoException import com.wire.kalium.cryptography.exceptions.ProteusException import java.io.File @@ -63,6 +64,11 @@ class ProteusClientCryptoBoxImpl constructor( TODO("get session is private in Cryptobox4j") } + override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray = wrapException { + kaliumLogger.i("getFingerprintFromPreKey: called from JVM") + getFingerprintFromPrekey(toPreKey(preKey)) + } + override suspend fun newLastResortPreKey(): PreKeyCrypto { return wrapException { toPreKey(box.newLastPreKey()) } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt index 4a99a350cde..732116a7ae8 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt @@ -19,9 +19,12 @@ package com.wire.kalium.logic.configuration import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel import com.wire.kalium.logic.data.featureConfig.toEntity import com.wire.kalium.logic.data.featureConfig.toModel +import com.wire.kalium.logic.data.legalhold.LastPreKey +import com.wire.kalium.logic.data.legalhold.LegalHoldRequest import com.wire.kalium.logic.data.message.SelfDeletionMapper.toSelfDeletionTimerEntity import com.wire.kalium.logic.data.message.SelfDeletionMapper.toTeamSelfDeleteTimer import com.wire.kalium.logic.data.message.TeamSettingsSelfDeletionStatus @@ -38,6 +41,7 @@ import com.wire.kalium.logic.functional.mapRight import com.wire.kalium.logic.wrapFlowStorageRequest import com.wire.kalium.logic.wrapStorageRequest import com.wire.kalium.persistence.config.IsFileSharingEnabledEntity +import com.wire.kalium.persistence.config.LegalHoldRequestEntity import com.wire.kalium.persistence.config.TeamSettingsSelfDeletionStatusEntity import com.wire.kalium.persistence.config.UserConfigStorage import com.wire.kalium.persistence.dao.unread.UserConfigDAO @@ -112,6 +116,8 @@ interface UserConfigRepository { lastPreKeyId: Int, lastPreKey: String ): Either + + fun observeLegalHoldRequest(): Flow> suspend fun deleteLegalHoldRequest(): Either } @@ -398,7 +404,19 @@ class UserConfigDataSource( userConfigDAO.persistLegalHoldRequest(clientId, lastPreKeyId, lastPreKey) } - override suspend fun deleteLegalHoldRequest(): Either = wrapStorageRequest { - userConfigDAO.clearLegalHoldRequest() - } + override fun observeLegalHoldRequest(): Flow> = + userConfigDAO.observeLegalHoldRequest().wrapStorageRequest().mapRight { + LegalHoldRequest( + clientId = ClientId(it.clientId), + lastPreKey = LastPreKey( + it.lastPreKey.id, + it.lastPreKey.key + ) + ) + } + + override suspend fun deleteLegalHoldRequest(): Either = + wrapStorageRequest { + userConfigDAO.clearLegalHoldRequest() + } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt index 5901b76c12c..4dfcc494a87 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LastPreKey.kt @@ -19,5 +19,5 @@ package com.wire.kalium.logic.data.legalhold data class LastPreKey( val id: Int, - val key: String, + val key: String ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LegalHoldRequest.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LegalHoldRequest.kt new file mode 100644 index 00000000000..bdf6afdb7d7 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/legalhold/LegalHoldRequest.kt @@ -0,0 +1,25 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.data.legalhold + +import com.wire.kalium.logic.data.conversation.ClientId + +data class LegalHoldRequest( + val clientId: ClientId, + val lastPreKey: LastPreKey +) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt index 7e38cd32a01..4b72d63958e 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt @@ -121,6 +121,8 @@ interface PreKeyRepository { suspend fun establishSessions( missingContactClients: Map> ): Either + + suspend fun getFingerprintForPreKey(preKeyCrypto: PreKeyCrypto): Either } @Suppress("LongParameterList") @@ -218,6 +220,13 @@ class PreKeyDataSource( } } + override suspend fun getFingerprintForPreKey(preKeyCrypto: PreKeyCrypto): Either = + proteusClientProvider.getOrError().flatMap { proteusClient -> + wrapProteusRequest { + proteusClient.getFingerprintFromPreKey(preKeyCrypto) + } + } + internal suspend fun preKeysOfClientsByQualifiedUsers( qualifiedIdsMap: Map> ): Either = wrapApiRequest { 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 1ad61690645..186a41df59d 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 @@ -217,6 +217,8 @@ import com.wire.kalium.logic.feature.featureConfig.handler.SecondFactorPasswordC import com.wire.kalium.logic.feature.featureConfig.handler.SelfDeletingMessagesConfigHandler import com.wire.kalium.logic.feature.keypackage.KeyPackageManager import com.wire.kalium.logic.feature.keypackage.KeyPackageManagerImpl +import com.wire.kalium.logic.feature.legalhold.LegalHoldRequestObserver +import com.wire.kalium.logic.feature.legalhold.LegalHoldRequestObserverImpl import com.wire.kalium.logic.feature.message.AddSystemMessageToAllConversationsUseCase import com.wire.kalium.logic.feature.message.AddSystemMessageToAllConversationsUseCaseImpl import com.wire.kalium.logic.feature.message.EphemeralEventsNotificationManagerImpl @@ -1736,6 +1738,12 @@ class UserSessionScope internal constructor( } } + val legalHoldRequestObserver: LegalHoldRequestObserver + get() = LegalHoldRequestObserverImpl( + userConfigRepository = userConfigRepository, + preKeyRepository = preKeyRepository + ) + internal val getProxyCredentials: GetProxyCredentialsUseCase get() = GetProxyCredentialsUseCaseImpl(sessionManager) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt new file mode 100644 index 00000000000..963ce9e62b4 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt @@ -0,0 +1,75 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.feature.legalhold + +import com.wire.kalium.cryptography.PreKeyCrypto +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.prekey.PreKeyRepository +import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.kaliumLogger +import io.ktor.utils.io.charsets.Charsets +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +interface LegalHoldRequestObserver { + operator fun invoke(): Flow +} + +internal class LegalHoldRequestObserverImpl internal constructor( + val userConfigRepository: UserConfigRepository, + val preKeyRepository: PreKeyRepository +) : LegalHoldRequestObserver { + override fun invoke(): Flow = + userConfigRepository.observeLegalHoldRequest().map { + it.fold( + { failure -> + if (failure is StorageFailure.DataNotFound) { + kaliumLogger.i("No legal hold request found") + LegalHoldRequestObserverResult.NoLegalHoldRequest + } else { + kaliumLogger.i("Legal hold request failure: $failure") + LegalHoldRequestObserverResult.Failure(failure) + } + }, + { request -> + val preKeyCrypto = PreKeyCrypto(request.lastPreKey.id, request.lastPreKey.key) + val result = preKeyRepository.getFingerprintForPreKey(preKeyCrypto) + result.fold( + { failure -> + kaliumLogger.i("Legal hold request fingerprint failure: $failure") + LegalHoldRequestObserverResult.Failure(failure) + }, + { fingerprint -> + val fingerprintDecoded = fingerprint.toString(Charsets.UTF_8) + LegalHoldRequestObserverResult.LegalHoldRequestAvailable( + fingerprintDecoded + ) + } + ) + } + ) + } +} + +sealed class LegalHoldRequestObserverResult { + data class LegalHoldRequestAvailable(val fingerprint: String) : LegalHoldRequestObserverResult() + data object NoLegalHoldRequest : LegalHoldRequestObserverResult() + data class Failure(val failure: CoreFailure) : LegalHoldRequestObserverResult() +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt new file mode 100644 index 00000000000..9028de485a9 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt @@ -0,0 +1,148 @@ +/* + * Wire + * Copyright (C) 2023 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.logic.feature.legalhold + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.conversation.ClientId +import com.wire.kalium.logic.data.legalhold.LastPreKey +import com.wire.kalium.logic.data.legalhold.LegalHoldRequest +import com.wire.kalium.logic.data.prekey.PreKeyRepository +import com.wire.kalium.logic.functional.Either +import io.ktor.utils.io.core.toByteArray +import io.mockative.Mock +import io.mockative.any +import io.mockative.given +import io.mockative.mock +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import kotlin.test.Test +import kotlin.test.assertTrue + +class LegalHoldRequestObserverTest { + + @Test + fun givenUserConfigRepositoryDataNotFoundFailure_whenObserving_thenPropagateNoLegalHoldRequest() = + runTest { + val (_, legalHoldRequestObserver) = Arrangement() + .withUserConfigRepositoryDataNotFound() + .arrange() + + val result = legalHoldRequestObserver() + + assertTrue(result.first() is LegalHoldRequestObserverResult.NoLegalHoldRequest) + } + + @Test + fun givenUserConfigRepositoryOtherFailure_whenObserving_thenPropagateFailure() = runTest { + val (_, legalHoldRequestObserver) = Arrangement() + .withUserConfigRepositoryFailure() + .arrange() + + val result = legalHoldRequestObserver() + + assertTrue(result.first() is LegalHoldRequestObserverResult.Failure) + } + + @Test + fun givenPreKeyRepositoryFailure_whenObserving_thenPropagateFailure() = runTest { + val (_, legalHoldRequestObserver) = Arrangement() + .withUserConfigRepositorySuccess() + .withPreKeyRepositoryFailure() + .arrange() + + val result = legalHoldRequestObserver() + + assertTrue(result.first() is LegalHoldRequestObserverResult.Failure) + } + + @Test + fun givenPreKeyRepositorySuccess_whenObserving_thenPropagateLegalHoldRequestAvailable() = + runTest { + val (_, legalHoldRequestObserver) = Arrangement() + .withUserConfigRepositorySuccess() + .withPreKeyRepositorySuccess() + .arrange() + + val result = legalHoldRequestObserver() + + assertTrue(result.first() is LegalHoldRequestObserverResult.LegalHoldRequestAvailable) + } + + private class Arrangement { + + @Mock + val userConfigRepository = mock(UserConfigRepository::class) + + @Mock + val preKeyRepository = mock(PreKeyRepository::class) + + fun withUserConfigRepositorySuccess() = apply { + given(userConfigRepository) + .function(userConfigRepository::observeLegalHoldRequest) + .whenInvoked() + .thenReturn(flowOf(Either.Right(legalHoldRequest))) + } + + fun withUserConfigRepositoryDataNotFound() = apply { + given(userConfigRepository) + .function(userConfigRepository::observeLegalHoldRequest) + .whenInvoked() + .thenReturn(flowOf(Either.Left(StorageFailure.DataNotFound))) + } + + fun withUserConfigRepositoryFailure() = apply { + given(userConfigRepository) + .function(userConfigRepository::observeLegalHoldRequest) + .whenInvoked() + .thenReturn(flowOf(Either.Left(StorageFailure.Generic(IllegalStateException())))) + } + + fun withPreKeyRepositoryFailure() = apply { + given(preKeyRepository) + .suspendFunction(preKeyRepository::getFingerprintForPreKey) + .whenInvokedWith(any()) + .thenReturn(Either.Left(CoreFailure.SyncEventOrClientNotFound)) + } + + fun withPreKeyRepositorySuccess() = apply { + given(preKeyRepository) + .suspendFunction(preKeyRepository::getFingerprintForPreKey) + .whenInvokedWith(any()) + .thenReturn(Either.Right(fingerPrint)) + } + + fun arrange() = this to LegalHoldRequestObserverImpl( + userConfigRepository = userConfigRepository, + preKeyRepository = preKeyRepository + ) + } + + companion object { + val legalHoldRequest = LegalHoldRequest( + clientId = ClientId("clientId"), + lastPreKey = LastPreKey( + id = 1, + key = "key" + ) + ) + val fingerPrint = "fingerPrint".toByteArray() + } +} \ No newline at end of file diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAO.kt index 7a37aaab2b8..c492658e65c 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAO.kt @@ -29,5 +29,5 @@ interface MetadataDAO { suspend fun clear(keysToKeep: List?) suspend fun putSerializable(key: String, value: T, kSerializer: KSerializer) suspend fun getSerializable(key: String, kSerializer: KSerializer): T? - suspend fun observeSerializable(key: String, kSerializer: KSerializer): Flow + fun observeSerializable(key: String, kSerializer: KSerializer): Flow } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAOImpl.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAOImpl.kt index ec4d9e436cd..30c4e20981d 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAOImpl.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/MetadataDAOImpl.kt @@ -80,7 +80,7 @@ class MetadataDAOImpl internal constructor( } } - override suspend fun observeSerializable(key: String, kSerializer: KSerializer): Flow { + override fun observeSerializable(key: String, kSerializer: KSerializer): Flow { return metadataQueries.selectValueByKey(key) .asFlow() .mapToOneOrNull() diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt index e2e310b253d..38444610eb6 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/unread/UserConfigDAO.kt @@ -43,7 +43,7 @@ interface UserConfigDAO { suspend fun setSupportedProtocols(protocols: Set) suspend fun persistLegalHoldRequest(clientId: String, lastPreKeyId: Int, lastPreKey: String) suspend fun clearLegalHoldRequest() - suspend fun observeLegalHoldRequest(): Flow + fun observeLegalHoldRequest(): Flow } internal class UserConfigDAOImpl internal constructor( @@ -101,7 +101,7 @@ internal class UserConfigDAOImpl internal constructor( metadataDAO.deleteValue(LEGAL_HOLD_REQUEST) } - override suspend fun observeLegalHoldRequest(): Flow = + override fun observeLegalHoldRequest(): Flow = metadataDAO.observeSerializable(LEGAL_HOLD_REQUEST, LegalHoldRequestEntity.serializer()) private companion object { From 038ad4e1d8cd1b92d0b8c70244507fdddc873b31 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 21 Nov 2023 16:46:12 +0100 Subject: [PATCH 06/10] chore: detekt --- .../com/wire/kalium/logic/configuration/UserConfigRepository.kt | 1 - .../kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt index 732116a7ae8..e5c45d7ab68 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/configuration/UserConfigRepository.kt @@ -41,7 +41,6 @@ import com.wire.kalium.logic.functional.mapRight import com.wire.kalium.logic.wrapFlowStorageRequest import com.wire.kalium.logic.wrapStorageRequest import com.wire.kalium.persistence.config.IsFileSharingEnabledEntity -import com.wire.kalium.persistence.config.LegalHoldRequestEntity import com.wire.kalium.persistence.config.TeamSettingsSelfDeletionStatusEntity import com.wire.kalium.persistence.config.UserConfigStorage import com.wire.kalium.persistence.dao.unread.UserConfigDAO diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt index 4b72d63958e..c70bd20eb09 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt @@ -49,6 +49,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.datetime.Instant +@Suppress("TooManyFunctions") interface PreKeyRepository { /** * Fetches the IDs of the prekeys currently available on the backend. From 66d02fb4e470647754c2b70f10f24e76abf49ec0 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Tue, 21 Nov 2023 16:55:01 +0100 Subject: [PATCH 07/10] chore: detekt --- .../com/wire/kalium/logic/data/prekey/PreKeyRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt index c70bd20eb09..87c5838af75 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/prekey/PreKeyRepository.kt @@ -126,7 +126,7 @@ interface PreKeyRepository { suspend fun getFingerprintForPreKey(preKeyCrypto: PreKeyCrypto): Either } -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooManyFunctions") class PreKeyDataSource( private val preKeyApi: PreKeyApi, private val proteusClientProvider: ProteusClientProvider, From 93a64ab3512ff6340c0566a36b918e4aea7941b8 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Wed, 22 Nov 2023 17:22:59 +0100 Subject: [PATCH 08/10] chore: cleanup --- .../wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt | 1 - .../wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt | 6 ++---- .../wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt | 2 +- .../wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt | 1 - 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt b/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt index f539402bb14..40e62aac8b3 100644 --- a/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt +++ b/cryptography/src/androidMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt @@ -59,7 +59,6 @@ class ProteusClientCryptoBoxImpl constructor( override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray = withContext(defaultContext) { - kaliumLogger.i("getFingerprintFromPreKey: called from Android") val cryptoBoxPreKey = CryptoBoxPreKey(preKey.id, preKey.encodedData.decodeBase64Bytes()) getFingerprintFromPrekey(cryptoBoxPreKey) } diff --git a/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt b/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt index 16a44403370..5ac3c328386 100644 --- a/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt +++ b/cryptography/src/appleMain/kotlin/com/wire/kalium/cryptography/ProteusClientCoreCryptoImpl.kt @@ -46,10 +46,8 @@ class ProteusClientCoreCryptoImpl private constructor(private val coreCrypto: Co } override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray { - return wrapException { - // TODO this is a hack, we need to expose the fingerprint from the core - return@wrapException "".toByteArray() - } + // TODO this is a hack, we need to expose the fingerprint from the core + return "".toByteArray() } override suspend fun newPreKeys(from: Int, count: Int): ArrayList { diff --git a/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt b/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt index 1a937d67a69..f2603db7334 100644 --- a/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt +++ b/cryptography/src/jsMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt @@ -60,7 +60,7 @@ class ProteusClientCryptoBoxImpl : ProteusClient { override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray { // TODO ("we need to expose the fingerprint from the core") - "".toByteArray() + return "".toByteArray() } override suspend fun remoteFingerPrint(sessionId: CryptoSessionId): ByteArray { diff --git a/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt b/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt index e7a1f722384..765101643a6 100644 --- a/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt +++ b/cryptography/src/jvmMain/kotlin/com/wire/kalium/cryptography/ProteusClientCryptoBoxImpl.kt @@ -65,7 +65,6 @@ class ProteusClientCryptoBoxImpl constructor( } override suspend fun getFingerprintFromPreKey(preKey: PreKeyCrypto): ByteArray = wrapException { - kaliumLogger.i("getFingerprintFromPreKey: called from JVM") getFingerprintFromPrekey(toPreKey(preKey)) } From 72f508a0365a16bc762e70fed1eb1dcaf1a71121 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Thu, 23 Nov 2023 11:59:55 +0100 Subject: [PATCH 09/10] chore: address comments --- .../kalium/logic/feature/UserSessionScope.kt | 8 +++--- ...Observer.kt => LegalHoldRequestUseCase.kt} | 26 ++++++++++++++----- .../legalhold/LegalHoldRequestObserverTest.kt | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) rename logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/{LegalHoldRequestObserver.kt => LegalHoldRequestUseCase.kt} (80%) 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 186a41df59d..e95d0ce8239 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 @@ -217,8 +217,8 @@ import com.wire.kalium.logic.feature.featureConfig.handler.SecondFactorPasswordC import com.wire.kalium.logic.feature.featureConfig.handler.SelfDeletingMessagesConfigHandler import com.wire.kalium.logic.feature.keypackage.KeyPackageManager import com.wire.kalium.logic.feature.keypackage.KeyPackageManagerImpl -import com.wire.kalium.logic.feature.legalhold.LegalHoldRequestObserver -import com.wire.kalium.logic.feature.legalhold.LegalHoldRequestObserverImpl +import com.wire.kalium.logic.feature.legalhold.LegalHoldRequestUseCase +import com.wire.kalium.logic.feature.legalhold.LegalHoldRequestUseCaseImpl import com.wire.kalium.logic.feature.message.AddSystemMessageToAllConversationsUseCase import com.wire.kalium.logic.feature.message.AddSystemMessageToAllConversationsUseCaseImpl import com.wire.kalium.logic.feature.message.EphemeralEventsNotificationManagerImpl @@ -1738,8 +1738,8 @@ class UserSessionScope internal constructor( } } - val legalHoldRequestObserver: LegalHoldRequestObserver - get() = LegalHoldRequestObserverImpl( + val legalHoldRequestUseCase: LegalHoldRequestUseCase + get() = LegalHoldRequestUseCaseImpl( userConfigRepository = userConfigRepository, preKeyRepository = preKeyRepository ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt similarity index 80% rename from logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt rename to logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt index 963ce9e62b4..a507419f02a 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserver.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt @@ -24,18 +24,17 @@ import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.prekey.PreKeyRepository import com.wire.kalium.logic.functional.fold import com.wire.kalium.logic.kaliumLogger -import io.ktor.utils.io.charsets.Charsets import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -interface LegalHoldRequestObserver { +interface LegalHoldRequestUseCase { operator fun invoke(): Flow } -internal class LegalHoldRequestObserverImpl internal constructor( +internal class LegalHoldRequestUseCaseImpl internal constructor( val userConfigRepository: UserConfigRepository, val preKeyRepository: PreKeyRepository -) : LegalHoldRequestObserver { +) : LegalHoldRequestUseCase { override fun invoke(): Flow = userConfigRepository.observeLegalHoldRequest().map { it.fold( @@ -57,9 +56,8 @@ internal class LegalHoldRequestObserverImpl internal constructor( LegalHoldRequestObserverResult.Failure(failure) }, { fingerprint -> - val fingerprintDecoded = fingerprint.toString(Charsets.UTF_8) LegalHoldRequestObserverResult.LegalHoldRequestAvailable( - fingerprintDecoded + fingerprint ) } ) @@ -69,7 +67,21 @@ internal class LegalHoldRequestObserverImpl internal constructor( } sealed class LegalHoldRequestObserverResult { - data class LegalHoldRequestAvailable(val fingerprint: String) : LegalHoldRequestObserverResult() + data class LegalHoldRequestAvailable(val fingerprint: ByteArray) : LegalHoldRequestObserverResult() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as LegalHoldRequestAvailable + + return fingerprint.contentEquals(other.fingerprint) + } + + override fun hashCode(): Int { + return fingerprint.contentHashCode() + } + } + data object NoLegalHoldRequest : LegalHoldRequestObserverResult() data class Failure(val failure: CoreFailure) : LegalHoldRequestObserverResult() } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt index 9028de485a9..2061036d44e 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestObserverTest.kt @@ -129,7 +129,7 @@ class LegalHoldRequestObserverTest { .thenReturn(Either.Right(fingerPrint)) } - fun arrange() = this to LegalHoldRequestObserverImpl( + fun arrange() = this to LegalHoldRequestUseCaseImpl( userConfigRepository = userConfigRepository, preKeyRepository = preKeyRepository ) From 7651d7b4274526fc4d702e9bac00ea3df579e167 Mon Sep 17 00:00:00 2001 From: Oussama Hassine Date: Thu, 23 Nov 2023 12:19:52 +0100 Subject: [PATCH 10/10] chore: use case documentation --- .../kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt index a507419f02a..96bdccfb311 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/legalhold/LegalHoldRequestUseCase.kt @@ -27,6 +27,9 @@ import com.wire.kalium.logic.kaliumLogger import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +/** + * Use case that observes the legal hold request. + */ interface LegalHoldRequestUseCase { operator fun invoke(): Flow }