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..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 @@ -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 + suspend fun setLegalHoldRequest( + clientId: String, + lastPreKeyId: Int, + lastPreKey: String + ): Either + suspend fun deleteLegalHoldRequest(): Either } @Suppress("TooManyFunctions") @@ -383,4 +389,16 @@ class UserConfigDataSource( wrapStorageRequest { userConfigDAO.setMigrationConfiguration(configuration.toEntity()) } + + override suspend fun setLegalHoldRequest( + clientId: String, + lastPreKeyId: Int, + lastPreKey: String + ): Either = wrapStorageRequest { + userConfigDAO.persistLegalHoldRequest(clientId, lastPreKeyId, lastPreKey) + } + + override suspend fun deleteLegalHoldRequest(): Either = wrapStorageRequest { + userConfigDAO.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..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 @@ -92,8 +91,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..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 @@ -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 @@ -51,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, @@ -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 a4c702d9ad9..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 @@ -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 @@ -1307,6 +1313,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( @@ -1318,7 +1349,9 @@ class UserSessionScope internal constructor( oneOnOneResolver, userId, clientIdProvider, - lazy { conversations.newGroupConversationSystemMessagesCreator } + lazy { conversations.newGroupConversationSystemMessagesCreator }, + legalHoldRequestHandler, + legalHoldHandler ) private val userPropertiesEventReceiver: UserPropertiesEventReceiver @@ -1457,9 +1490,7 @@ class UserSessionScope internal constructor( authenticationScope.secondFactorVerificationRepository, slowSyncRepository, cachedClientIdClearer, - updateSupportedProtocolsAndResolveOneOnOnes, - conversationRepository, - persistMessage + updateSupportedProtocolsAndResolveOneOnOnes ) val conversations: ConversationScope by lazy { ConversationScope( @@ -1712,8 +1743,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..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 @@ -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..4b1094cf7d3 --- /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 + +internal interface LegalHoldHandler { + suspend fun handleEnable(legalHoldEnable: Event.User.LegalHoldEnabled): Either + suspend fun handleDisable(legalHoldEnable: Event.User.LegalHoldDisabled): Either +} + +internal 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 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 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 suspend 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..0c2f5a6fbd7 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandler.kt @@ -0,0 +1,49 @@ +/* + * 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 + +internal interface LegalHoldRequestHandler { + suspend fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either +} + +internal class LegalHoldRequestHandlerImpl internal constructor( + private val selfUserId: UserId, + private val userConfigRepository: UserConfigRepository +) : LegalHoldRequestHandler { + override suspend fun handle(legalHoldRequest: Event.User.LegalHoldRequest): Either { + if (selfUserId == legalHoldRequest.userId) { + kaliumLogger.i( + "Legal hold request received for user ${legalHoldRequest.userId.toLogString()}," + + " 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..daa19e78801 --- /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) + .suspendFunction(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) + .suspendFunction(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) + .suspendFunction(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..f46877a0ad9 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/handler/legalhold/LegalHoldRequestHandlerTest.kt @@ -0,0 +1,106 @@ +/* + * 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 kotlinx.coroutines.test.runTest +import kotlin.test.Test + +class LegalHoldRequestHandlerTest { + + @Test + fun givenLegalHoldRequestEvent_whenUserIdIsSelfUser_thenStoreRequestLocally() = runTest { + val (arrangement, handler) = Arrangement() + .withSetLegalHoldSuccess() + .arrange() + + val result = handler.handle(legalHoldRequestSelfUser) + + result.shouldSucceed() + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setLegalHoldRequest) + .with( + eq(legalHoldRequestSelfUser.clientId.value), + eq(legalHoldRequestSelfUser.lastPreKey.id), + eq(legalHoldRequestSelfUser.lastPreKey.key) + ) + .wasInvoked(once) + } + + @Test + fun givenLegalHoldRequestEvent_whenUserIdIsNotIsSelfUser_thenIgnoreEvent() = runTest { + val (arrangement, handler) = Arrangement() + .arrange() + + val result = handler.handle(legalHoldRequestOtherUser) + + result.shouldSucceed() + verify(arrangement.userConfigRepository) + .suspendFunction(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) + .suspendFunction(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..0bbb043dbd2 --- /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..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 @@ -213,6 +213,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 +254,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 +329,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 +366,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 +389,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 +476,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 +517,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 +528,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 { @@ -492,7 +550,8 @@ 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" 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..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 @@ -17,6 +17,8 @@ */ 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.dao.MetadataDAO @@ -39,6 +41,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 +89,25 @@ 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" } }