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 a9c582c1bba..72cc39a7175 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 @@ -20,6 +20,9 @@ package com.wire.kalium.logic.configuration import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.featureConfig.AppLockConfigModel +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 @@ -62,6 +65,8 @@ interface UserConfigRepository { fun snoozeE2EINotification(duration: Duration): Either fun setDefaultProtocol(protocol: SupportedProtocol): Either fun getDefaultProtocol(): Either + suspend fun setSupportedProtocols(protocols: Set): Either + suspend fun getSupportedProtocols(): Either> fun setConferenceCallingEnabled(enabled: Boolean): Either fun isConferenceCallingEnabled(): Either fun setSecondFactorPasswordChallengeStatus(isRequired: Boolean): Either @@ -85,6 +90,8 @@ interface UserConfigRepository { suspend fun observeTeamSettingsSelfDeletingStatus(): Flow> fun observeE2EINotificationTime(): Flow> fun setE2EINotificationTime(instant: Instant): Either + suspend fun getMigrationConfiguration(): Either + suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either } @Suppress("TooManyFunctions") @@ -197,6 +204,11 @@ class UserConfigDataSource( override fun getDefaultProtocol(): Either = wrapStorageRequest { userConfigStorage.defaultProtocol().toModel() } + override suspend fun setSupportedProtocols(protocols: Set): Either = + wrapStorageRequest { userConfigDAO.setSupportedProtocols(protocols.toDao()) } + + override suspend fun getSupportedProtocols(): Either> = + wrapStorageRequest { userConfigDAO.getSupportedProtocols()?.toModel() } override fun setConferenceCallingEnabled(enabled: Boolean): Either = wrapStorageRequest { userConfigStorage.persistConferenceCalling(enabled) @@ -310,4 +322,14 @@ class UserConfigDataSource( } } } + + override suspend fun getMigrationConfiguration(): Either = + wrapStorageRequest { + userConfigDAO.getMigrationConfiguration()?.toModel() + } + + override suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either = + wrapStorageRequest { + userConfigDAO.setMigrationConfiguration(configuration.toEntity()) + } } 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 eeecc51db20..5e72aa53897 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 @@ -33,6 +33,7 @@ import com.wire.kalium.logic.data.featureConfig.ClassifiedDomainsModel import com.wire.kalium.logic.data.featureConfig.ConferenceCallingModel import com.wire.kalium.logic.data.featureConfig.ConfigsStatusModel import com.wire.kalium.logic.data.featureConfig.E2EIModel +import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel import com.wire.kalium.logic.data.featureConfig.MLSModel import com.wire.kalium.logic.data.featureConfig.SelfDeletingMessagesModel import com.wire.kalium.logic.data.id.ConversationId @@ -526,6 +527,21 @@ sealed class Event(open val id: String, open val transient: Boolean, open val li ) } + data class MLSMigrationUpdated( + override val id: String, + override val transient: Boolean, + override val live: Boolean, + val model: MLSMigrationModel + ) : FeatureConfig(id, transient, live) { + override fun toLogMap(): Map = mapOf( + typeKey to "FeatureConfig.MLSUpdated", + idKey to id.obfuscateId(), + featureStatusKey to model.status.name, + "startTime" to model.startTime, + "endTime" to model.endTime + ) + } + data class ClassifiedDomainsUpdated( override val id: String, override val transient: Boolean, 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 49c51487a56..569e0558b12 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 @@ -521,6 +521,13 @@ class EventMapper( featureConfigMapper.fromDTO(featureConfigUpdatedDTO.data as FeatureConfigData.MLS) ) + is FeatureConfigData.MLSMigration -> Event.FeatureConfig.MLSMigrationUpdated( + id, + transient, + live, + featureConfigMapper.fromDTO(featureConfigUpdatedDTO.data as FeatureConfigData.MLSMigration) + ) + is FeatureConfigData.ClassifiedDomains -> Event.FeatureConfig.ClassifiedDomainsUpdated( id, transient, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt index 65a958f3eba..c3c87e02e67 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigMapper.kt @@ -25,6 +25,7 @@ import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConf import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigResponse import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureFlagStatusDTO import com.wire.kalium.network.api.base.authenticated.featureConfigs.MLSMigrationConfigDTO +import com.wire.kalium.persistence.config.MLSMigrationEntity interface FeatureConfigMapper { fun fromDTO(featureConfigResponse: FeatureConfigResponse): FeatureConfigModel @@ -152,3 +153,17 @@ class FeatureConfigMapperImpl : FeatureConfigMapper { fromModel(model.status) ) } + +fun MLSMigrationModel.toEntity(): MLSMigrationEntity = + MLSMigrationEntity( + status = status.equals(Status.ENABLED), + startTime = startTime, + endTime = endTime + ) + +fun MLSMigrationEntity.toModel(): MLSMigrationModel = + MLSMigrationModel( + status = if (status) Status.ENABLED else Status.DISABLED, + startTime = startTime, + endTime = endTime + ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/mlsmigration/MLSMigrationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/mlsmigration/MLSMigrationRepository.kt deleted file mode 100644 index 27840b3e6c6..00000000000 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/mlsmigration/MLSMigrationRepository.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.mlsmigration - -import com.wire.kalium.logic.CoreFailure -import com.wire.kalium.logic.StorageFailure -import com.wire.kalium.logic.data.featureConfig.FeatureConfigMapper -import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel -import com.wire.kalium.logic.data.featureConfig.Status -import com.wire.kalium.logic.di.MapperProvider -import com.wire.kalium.logic.functional.Either -import com.wire.kalium.logic.functional.flatMap -import com.wire.kalium.logic.functional.flatMapLeft -import com.wire.kalium.logic.functional.map -import com.wire.kalium.logic.wrapApiRequest -import com.wire.kalium.logic.wrapStorageRequest -import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigApi -import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigData -import com.wire.kalium.persistence.dao.MetadataDAO -import kotlinx.datetime.Instant -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json - -interface MLSMigrationRepository { - suspend fun fetchMigrationConfiguration(): Either - suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either - suspend fun getMigrationConfiguration(): Either - suspend fun getOrFetchMigrationConfiguration(): Either -} - -internal class MLSMigrationRepositoryImpl( - private val featureConfigApi: FeatureConfigApi, - private val metadataDAO: MetadataDAO, - private val featureConfigMapper: FeatureConfigMapper = MapperProvider.featureConfigMapper() -) : MLSMigrationRepository { - - @Suppress("MagicNumber") - override suspend fun fetchMigrationConfiguration(): Either { - return wrapApiRequest { featureConfigApi.featureConfigs() } - .flatMap { - it.mlsMigration?.let { mlsMigration -> - setMigrationConfiguration(featureConfigMapper.fromDTO(mlsMigration)) - } ?: run { - setMigrationConfiguration( - MLSMigrationModel( // TODO jacob debugging values while not implemented on BE - Instant.DISTANT_PAST, - Instant.DISTANT_FUTURE, - Status.ENABLED - ) - ) - } - } - } - - override suspend fun getOrFetchMigrationConfiguration(): Either = - getMigrationConfiguration() - .flatMapLeft { - fetchMigrationConfiguration() - .flatMap { getMigrationConfiguration() } - } - - override suspend fun getMigrationConfiguration(): Either = - wrapStorageRequest { - metadataDAO.valueByKey(MLS_MIGRATION_CONFIGURATION_KEY) - }.map { encodedValue -> - featureConfigMapper.fromDTO(Json.decodeFromString(encodedValue)) - } - - override suspend fun setMigrationConfiguration(configuration: MLSMigrationModel): Either = - wrapStorageRequest { - metadataDAO.insertValue(Json.encodeToString(featureConfigMapper.fromModel(configuration)), MLS_MIGRATION_CONFIGURATION_KEY) - } - - companion object { - internal const val MLS_MIGRATION_CONFIGURATION_KEY = "mlsMigrationConfiguration" - } -} 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 947d17894a5..b9f4d19e33a 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 @@ -97,7 +97,6 @@ import com.wire.kalium.logic.data.message.ProtoContentMapperImpl import com.wire.kalium.logic.data.message.SystemMessageInserterImpl import com.wire.kalium.logic.data.message.reaction.ReactionRepositoryImpl import com.wire.kalium.logic.data.message.receipt.ReceiptRepositoryImpl -import com.wire.kalium.logic.data.mlsmigration.MLSMigrationRepositoryImpl import com.wire.kalium.logic.data.mlspublickeys.MLSPublicKeysRepository import com.wire.kalium.logic.data.mlspublickeys.MLSPublicKeysRepositoryImpl import com.wire.kalium.logic.data.notification.PushTokenDataSource @@ -201,6 +200,7 @@ import com.wire.kalium.logic.feature.featureConfig.handler.GuestRoomConfigHandle import com.wire.kalium.logic.feature.featureConfig.handler.MLSConfigHandler 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 @@ -262,6 +262,10 @@ import com.wire.kalium.logic.feature.user.SyncContactsUseCase import com.wire.kalium.logic.feature.user.SyncContactsUseCaseImpl import com.wire.kalium.logic.feature.user.SyncSelfUserUseCase import com.wire.kalium.logic.feature.user.SyncSelfUserUseCaseImpl +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCaseImpl +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCaseImpl import com.wire.kalium.logic.feature.user.UserScope import com.wire.kalium.logic.feature.user.guestroomlink.MarkGuestLinkFeatureFlagAsNotChangedUseCase import com.wire.kalium.logic.feature.user.guestroomlink.MarkGuestLinkFeatureFlagAsNotChangedUseCaseImpl @@ -904,12 +908,25 @@ class UserSessionScope internal constructor( incrementalSyncRepository ) + private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase + get() = UpdateSupportedProtocolsUseCaseImpl( + clientRepository, + userRepository, + userConfigRepository + ) + + private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase + get() = UpdateSupportedProtocolsAndResolveOneOnOnesUseCaseImpl( + updateSupportedProtocols, + oneOnOneResolver + ) + private val slowSyncWorker: SlowSyncWorker by lazy { SlowSyncWorkerImpl( eventRepository, syncSelfUser, syncFeatureConfigsUseCase, - users.updateSupportedProtocols, + updateSupportedProtocols, syncConversations, syncConnections, syncSelfTeamUseCase, @@ -1031,9 +1048,11 @@ class UserSessionScope internal constructor( internal val mlsMigrationWorker get() = MLSMigrationWorkerImpl( - mlsMigrationRepository, + userConfigRepository, + featureConfigRepository, + mlsConfigHandler, + mlsMigrationConfigHandler, mlsMigrator, - users.updateSupportedProtocols ) internal val mlsMigrationManager: MLSMigrationManager = MLSMigrationManagerImpl( @@ -1042,16 +1061,9 @@ class UserSessionScope internal constructor( incrementalSyncRepository, lazy { clientRepository }, lazy { users.timestampKeyRepository }, - lazy { mlsMigrationWorker }, - lazy { mlsMigrationRepository } + lazy { mlsMigrationWorker } ) - internal val mlsMigrationRepository get() = - MLSMigrationRepositoryImpl( - authenticatedNetworkContainer.featureConfigApi, - userStorage.database.metadataDAO - ) - private val mlsPublicKeysRepository: MLSPublicKeysRepository get() = MLSPublicKeysRepositoryImpl( authenticatedNetworkContainer.mlsPublicKeyApi, @@ -1296,17 +1308,48 @@ class UserSessionScope internal constructor( private val teamEventReceiver: TeamEventReceiver get() = TeamEventReceiverImpl(teamRepository, conversationRepository, userRepository, persistMessage, userId) + private val guestRoomConfigHandler + get() = GuestRoomConfigHandler(userConfigRepository, kaliumConfigs) + + private val fileSharingConfigHandler + get() = FileSharingConfigHandler(userConfigRepository) + + private val mlsConfigHandler + get() = MLSConfigHandler(userConfigRepository, updateSupportedProtocolsAndResolveOneOnOnes, userId) + + private val mlsMigrationConfigHandler + get() = MLSMigrationConfigHandler(userConfigRepository, updateSupportedProtocolsAndResolveOneOnOnes) + + private val classifiedDomainsConfigHandler + get() = ClassifiedDomainsConfigHandler(userConfigRepository) + + private val conferenceCallingConfigHandler + get() = ConferenceCallingConfigHandler(userConfigRepository) + + private val secondFactorPasswordChallengeConfigHandler + get() = SecondFactorPasswordChallengeConfigHandler(userConfigRepository) + + private val selfDeletingMessagesConfigHandler + get() = SelfDeletingMessagesConfigHandler(userConfigRepository, kaliumConfigs) + + private val e2eiConfigHandler + get() = E2EIConfigHandler(userConfigRepository) + + private val appLockConfigHandler + get() = AppLockConfigHandler(userConfigRepository) + private val featureConfigEventReceiver: FeatureConfigEventReceiver get() = FeatureConfigEventReceiverImpl( - GuestRoomConfigHandler(userConfigRepository, kaliumConfigs), - FileSharingConfigHandler(userConfigRepository), - MLSConfigHandler(userConfigRepository, userId), - ClassifiedDomainsConfigHandler(userConfigRepository), - ConferenceCallingConfigHandler(userConfigRepository), - SecondFactorPasswordChallengeConfigHandler(userConfigRepository), - SelfDeletingMessagesConfigHandler(userConfigRepository, kaliumConfigs), - E2EIConfigHandler(userConfigRepository), - AppLockConfigHandler(userConfigRepository) + guestRoomConfigHandler, + fileSharingConfigHandler, + mlsConfigHandler, + mlsMigrationConfigHandler, + classifiedDomainsConfigHandler, + conferenceCallingConfigHandler, + secondFactorPasswordChallengeConfigHandler, + selfDeletingMessagesConfigHandler, + e2eiConfigHandler, + appLockConfigHandler ) private val preKeyRepository: PreKeyRepository @@ -1390,8 +1433,7 @@ class UserSessionScope internal constructor( authenticationScope.secondFactorVerificationRepository, slowSyncRepository, cachedClientIdClearer, - users.updateSupportedProtocols, - oneOnOneResolver + updateSupportedProtocolsAndResolveOneOnOnes ) val conversations: ConversationScope by lazy { ConversationScope( @@ -1489,9 +1531,8 @@ class UserSessionScope internal constructor( messages.messageSender, clientIdProvider, e2eiRepository, - clientRepository, - featureConfigRepository, team.isSelfATeamMember, + updateSupportedProtocols ) private val clearUserData: ClearUserDataUseCase get() = ClearUserDataUseCaseImpl(userStorage) @@ -1561,15 +1602,16 @@ class UserSessionScope internal constructor( private val syncFeatureConfigsUseCase: SyncFeatureConfigsUseCase get() = SyncFeatureConfigsUseCaseImpl( featureConfigRepository, - GuestRoomConfigHandler(userConfigRepository, kaliumConfigs), - FileSharingConfigHandler(userConfigRepository), - MLSConfigHandler(userConfigRepository, userId), - ClassifiedDomainsConfigHandler(userConfigRepository), - ConferenceCallingConfigHandler(userConfigRepository), - SecondFactorPasswordChallengeConfigHandler(userConfigRepository), - SelfDeletingMessagesConfigHandler(userConfigRepository, kaliumConfigs), - E2EIConfigHandler(userConfigRepository), - AppLockConfigHandler(userConfigRepository) + guestRoomConfigHandler, + fileSharingConfigHandler, + mlsConfigHandler, + mlsMigrationConfigHandler, + classifiedDomainsConfigHandler, + conferenceCallingConfigHandler, + secondFactorPasswordChallengeConfigHandler, + selfDeletingMessagesConfigHandler, + e2eiConfigHandler, + appLockConfigHandler ) val team: TeamScope get() = TeamScope(userRepository, teamRepository, conversationRepository, selfTeamId) 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 ea19fb70f63..258c3da0dbb 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 @@ -35,7 +35,6 @@ import com.wire.kalium.logic.data.user.UserRepository import com.wire.kalium.logic.feature.CachedClientIdClearer import com.wire.kalium.logic.feature.CurrentClientIdProvider import com.wire.kalium.logic.feature.ProteusClientProvider -import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolver import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCase import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCaseImpl import com.wire.kalium.logic.feature.keypackage.RefillKeyPackagesUseCase @@ -43,7 +42,7 @@ import com.wire.kalium.logic.feature.keypackage.RefillKeyPackagesUseCaseImpl import com.wire.kalium.logic.feature.session.DeregisterTokenUseCase import com.wire.kalium.logic.feature.session.DeregisterTokenUseCaseImpl import com.wire.kalium.logic.feature.session.UpgradeCurrentSessionUseCase -import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCase import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCaseImpl import com.wire.kalium.util.DelicateKaliumApi @@ -69,8 +68,7 @@ class ClientScope @OptIn(DelicateKaliumApi::class) internal constructor( private val secondFactorVerificationRepository: SecondFactorVerificationRepository, private val slowSyncRepository: SlowSyncRepository, private val cachedClientIdClearer: CachedClientIdClearer, - private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase, - private val oneOnOneResolver: OneOnOneResolver + private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase ) { @OptIn(DelicateKaliumApi::class) val register: RegisterClientUseCase @@ -92,9 +90,7 @@ class ClientScope @OptIn(DelicateKaliumApi::class) internal constructor( val deleteClient: DeleteClientUseCase get() = DeleteClientUseCaseImpl( clientRepository, - updateSupportedProtocols, - userRepository, - oneOnOneResolver + updateSupportedProtocolsAndResolveOneOnOnes, ) val needsToRegisterClient: NeedsToRegisterClientUseCase get() = NeedsToRegisterClientUseCaseImpl(clientIdProvider, sessionRepository, proteusClientProvider, selfUserId) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCase.kt index 9379b5bce1e..fda4033b4e7 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCase.kt @@ -22,10 +22,7 @@ import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.client.DeleteClientParam -import com.wire.kalium.logic.data.user.UserRepository -import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolver -import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase -import com.wire.kalium.logic.functional.flatMap +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase import com.wire.kalium.logic.functional.fold import com.wire.kalium.logic.functional.onSuccess import com.wire.kalium.network.exceptions.KaliumException @@ -43,20 +40,14 @@ interface DeleteClientUseCase { internal class DeleteClientUseCaseImpl( private val clientRepository: ClientRepository, - private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase, - private val userRepository: UserRepository, - private val oneOnOneResolver: OneOnOneResolver + private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase, ) : DeleteClientUseCase { override suspend operator fun invoke(param: DeleteClientParam): DeleteClientResult = clientRepository.deleteClient(param) .onSuccess { - updateSupportedProtocols().onSuccess { updated -> - if (updated) { - userRepository.fetchAllOtherUsers().flatMap { - oneOnOneResolver.resolveAllOneOnOneConversations() - } - } - } + updateSupportedProtocolsAndResolveOneOnOnes( + synchroniseUsers = true + ) } .fold( { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/mls/OneOnOneResolver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/mls/OneOnOneResolver.kt index 1c8f7d43241..b83c41ba3ad 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/mls/OneOnOneResolver.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/conversation/mls/OneOnOneResolver.kt @@ -46,7 +46,7 @@ import kotlinx.coroutines.launch import kotlin.time.Duration interface OneOnOneResolver { - suspend fun resolveAllOneOnOneConversations(): Either + suspend fun resolveAllOneOnOneConversations(synchronizeUsers: Boolean = false): Either suspend fun scheduleResolveOneOnOneConversationWithUserId(userId: UserId, delay: Duration = Duration.ZERO): Job suspend fun resolveOneOnOneConversationWithUserId(userId: UserId): Either suspend fun resolveOneOnOneConversationWithUser(user: OtherUser): Either @@ -64,29 +64,34 @@ internal class OneOnOneResolverImpl( private val dispatcher = kaliumDispatcher.default.limitedParallelism(1) private val resolveActiveOneOnOneScope = CoroutineScope(dispatcher) - override suspend fun resolveAllOneOnOneConversations(): Either { - val usersWithOneOnOne = userRepository.getUsersWithOneOnOneConversation() - kaliumLogger.i("Resolving one-on-one protocol for ${usersWithOneOnOne.size} user(s)") - return usersWithOneOnOne.foldToEitherWhileRight(Unit) { item, _ -> - resolveOneOnOneConversationWithUser(item).flatMapLeft { - when (it) { - is CoreFailure.NoKeyPackagesAvailable, - is NetworkFailure.ServerMiscommunication, - is NetworkFailure.FederatedBackendFailure, - is CoreFailure.NoCommonProtocolFound - -> { - kaliumLogger.e("Resolving one-on-one failed $it, skipping") - Either.Right(Unit) - } + override suspend fun resolveAllOneOnOneConversations(synchronizeUsers: Boolean): Either = + if (synchronizeUsers) { + userRepository.fetchAllOtherUsers() + } else { + Either.Right(Unit) + }.flatMap { + val usersWithOneOnOne = userRepository.getUsersWithOneOnOneConversation() + kaliumLogger.i("Resolving one-on-one protocol for ${usersWithOneOnOne.size} user(s)") + usersWithOneOnOne.foldToEitherWhileRight(Unit) { item, _ -> + resolveOneOnOneConversationWithUser(item).flatMapLeft { + when (it) { + is CoreFailure.NoKeyPackagesAvailable, + is NetworkFailure.ServerMiscommunication, + is NetworkFailure.FederatedBackendFailure, + is CoreFailure.NoCommonProtocolFound + -> { + kaliumLogger.e("Resolving one-on-one failed $it, skipping") + Either.Right(Unit) + } - else -> { - kaliumLogger.e("Resolving one-on-one failed $it, retrying") - Either.Left(it) + else -> { + kaliumLogger.e("Resolving one-on-one failed $it, retrying") + Either.Left(it) + } } - } - }.map { } + }.map { } + } } - } override suspend fun scheduleResolveOneOnOneConversationWithUserId(userId: UserId, delay: Duration) = resolveActiveOneOnOneScope.launch { diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt index 76d1c5f173e..44732d85db6 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCase.kt @@ -28,6 +28,7 @@ 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.functional.Either @@ -51,6 +52,7 @@ internal class SyncFeatureConfigsUseCaseImpl( private val guestRoomConfigHandler: GuestRoomConfigHandler, private val fileSharingConfigHandler: FileSharingConfigHandler, private val mlsConfigHandler: MLSConfigHandler, + private val mlsMigrationConfigHandler: MLSMigrationConfigHandler, private val classifiedDomainsConfigHandler: ClassifiedDomainsConfigHandler, private val conferenceCallingConfigHandler: ConferenceCallingConfigHandler, private val passwordChallengeConfigHandler: SecondFactorPasswordChallengeConfigHandler, @@ -63,7 +65,8 @@ internal class SyncFeatureConfigsUseCaseImpl( // TODO handle other feature flags and after it bump version in [SlowSyncManager.CURRENT_VERSION] guestRoomConfigHandler.handle(it.guestRoomLinkModel) fileSharingConfigHandler.handle(it.fileSharingModel) - mlsConfigHandler.handle(it.mlsModel) + mlsConfigHandler.handle(it.mlsModel, duringSlowSync = true) + it.mlsMigrationModel?.let { mlsMigrationConfigHandler.handle(it, duringSlowSync = true) } classifiedDomainsConfigHandler.handle(it.classifiedDomainsModel) conferenceCallingConfigHandler.handle(it.conferenceCallingModel) passwordChallengeConfigHandler.handle(it.secondFactorPasswordChallengeModel) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandler.kt index 9420f004a16..c4987ceeca3 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandler.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandler.kt @@ -23,19 +23,36 @@ import com.wire.kalium.logic.data.featureConfig.MLSModel import com.wire.kalium.logic.data.featureConfig.Status import com.wire.kalium.logic.data.user.SupportedProtocol import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap +import com.wire.kalium.logic.functional.getOrElse class MLSConfigHandler( private val userConfigRepository: UserConfigRepository, + private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase, private val selfUserId: UserId ) { - fun handle(mlsConfig: MLSModel): Either { + suspend fun handle(mlsConfig: MLSModel, duringSlowSync: Boolean): Either { val mlsEnabled = mlsConfig.status == Status.ENABLED val selfUserIsWhitelisted = mlsConfig.allowedUsers.contains(selfUserId.toPlainID()) + val previousSupportedProtocols = userConfigRepository.getSupportedProtocols().getOrElse(setOf(SupportedProtocol.PROTEUS)) + val supportedProtocolsHasChanged = !previousSupportedProtocols.equals(mlsConfig.supportedProtocols) + return userConfigRepository.setMLSEnabled(mlsEnabled && selfUserIsWhitelisted) + .flatMap { + if (supportedProtocolsHasChanged) { + updateSupportedProtocolsAndResolveOneOnOnes( + synchroniseUsers = !duringSlowSync + ) + } else { + Either.Right(Unit) + } + } .flatMap { userConfigRepository.setDefaultProtocol(if (mlsEnabled) mlsConfig.defaultProtocol else SupportedProtocol.PROTEUS) + }.flatMap { + userConfigRepository.setSupportedProtocols(mlsConfig.supportedProtocols) } } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSMigrationConfigHandler.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSMigrationConfigHandler.kt new file mode 100644 index 00000000000..799b8036cd2 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSMigrationConfigHandler.kt @@ -0,0 +1,41 @@ +/* + * 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.featureConfig.handler + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel +import com.wire.kalium.logic.feature.mlsmigration.hasMigrationEnded +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase +import com.wire.kalium.logic.functional.Either + +class MLSMigrationConfigHandler( + private val userConfigRepository: UserConfigRepository, + private val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase +) { + + suspend fun handle(mlsMigrationConfig: MLSMigrationModel, duringSlowSync: Boolean): Either { + if (mlsMigrationConfig.hasMigrationEnded() && !duringSlowSync) { + updateSupportedProtocolsAndResolveOneOnOnes( + synchroniseUsers = true + ) + } + + return userConfigRepository.setMigrationConfiguration(mlsMigrationConfig) + } +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManager.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManager.kt index 28aad0fd6dc..2ff18128855 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManager.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManager.kt @@ -21,7 +21,6 @@ import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel import com.wire.kalium.logic.data.featureConfig.Status -import com.wire.kalium.logic.data.mlsmigration.MLSMigrationRepository import com.wire.kalium.logic.data.sync.IncrementalSyncRepository import com.wire.kalium.logic.data.sync.IncrementalSyncStatus import com.wire.kalium.logic.feature.TimestampKeyRepository @@ -31,6 +30,7 @@ import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap import com.wire.kalium.logic.functional.getOrElse +import com.wire.kalium.logic.functional.onFailure import com.wire.kalium.logic.functional.onSuccess import com.wire.kalium.logic.kaliumLogger import com.wire.kalium.util.KaliumDispatcher @@ -55,7 +55,6 @@ internal class MLSMigrationManagerImpl( private val clientRepository: Lazy, private val timestampKeyRepository: Lazy, private val mlsMigrationWorker: Lazy, - private val mlsMigrationRepository: Lazy, kaliumDispatcher: KaliumDispatcher = KaliumDispatcherImpl ) : MLSMigrationManager { /** @@ -90,12 +89,14 @@ internal class MLSMigrationManagerImpl( ).flatMap { lastMlsMigrationCheckHasPassed -> kaliumLogger.d("Migration needs to be updated: $lastMlsMigrationCheckHasPassed") if (lastMlsMigrationCheckHasPassed) { - kaliumLogger.d("Updating migration config") - mlsMigrationRepository.value.fetchMigrationConfiguration() - .onSuccess { timestampKeyRepository.value.reset(TimestampKeys.LAST_MLS_MIGRATION_CHECK) } - .flatMap { - mlsMigrationWorker.value.runMigration() - Either.Right(Unit) + kaliumLogger.d("Running mls migration") + mlsMigrationWorker.value.runMigration() + .onSuccess { + kaliumLogger.d("Successfully advanced the mls migration") + timestampKeyRepository.value.reset(TimestampKeys.LAST_MLS_MIGRATION_CHECK) + } + .onFailure { + kaliumLogger.d("Failure while advancing the mls migration: $it") } } Either.Right(Unit) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationWorker.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationWorker.kt index ef963629473..c481ba50771 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationWorker.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationWorker.kt @@ -17,35 +17,33 @@ */ package com.wire.kalium.logic.feature.mlsmigration -import com.wire.kalium.logic.data.mlsmigration.MLSMigrationRepository -import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.configuration.UserConfigRepository +import com.wire.kalium.logic.data.featureConfig.FeatureConfigRepository +import com.wire.kalium.logic.feature.featureConfig.handler.MLSConfigHandler +import com.wire.kalium.logic.feature.featureConfig.handler.MLSMigrationConfigHandler import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.functional.flatMap import com.wire.kalium.logic.functional.getOrNull -import com.wire.kalium.logic.functional.onFailure import com.wire.kalium.logic.kaliumLogger interface MLSMigrationWorker { - suspend fun runMigration() + suspend fun runMigration(): Either } -class MLSMigrationWorkerImpl( - private val mlsMigrationRepository: MLSMigrationRepository, +internal class MLSMigrationWorkerImpl( + private val userConfigRepository: UserConfigRepository, + private val featureConfigRepository: FeatureConfigRepository, + private val mlsConfigHandler: MLSConfigHandler, + private val mlsMigrationConfigHandler: MLSMigrationConfigHandler, private val mlsMigrator: MLSMigrator, - private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase ) : MLSMigrationWorker { - override suspend fun runMigration() { - advanceMigration().onFailure { - kaliumLogger.e("Failed to advance migration: $it") - } - } - - suspend fun advanceMigration() = - mlsMigrationRepository.getMigrationConfiguration().getOrNull()?.let { configuration -> - if (configuration.hasMigrationStarted()) { - kaliumLogger.i("Running proteus to MLS migration") - updateSupportedProtocols().flatMap { + override suspend fun runMigration() = + syncMigrationConfigurations().flatMap { + userConfigRepository.getMigrationConfiguration().getOrNull()?.let { configuration -> + if (configuration.hasMigrationStarted()) { + kaliumLogger.i("Running proteus to MLS migration") mlsMigrator.migrateProteusConversations().flatMap { if (configuration.hasMigrationEnded()) { mlsMigrator.finaliseAllProteusConversations() @@ -53,10 +51,18 @@ class MLSMigrationWorkerImpl( mlsMigrator.finaliseProteusConversations() } } + } else { + kaliumLogger.i("MLS migration is not enabled") + Either.Right(Unit) } - } else { - kaliumLogger.i("MLS migration is not enabled") - Either.Right(Unit) - } - } ?: Either.Right(Unit) + } ?: Either.Right(Unit) + } + + private suspend fun syncMigrationConfigurations(): Either = + featureConfigRepository.getFeatureConfigs().flatMap { configurations -> + mlsConfigHandler.handle(configurations.mlsModel, duringSlowSync = false) + .flatMap { configurations.mlsMigrationModel?.let { + mlsMigrationConfigHandler.handle(configurations.mlsMigrationModel, duringSlowSync = false) + } ?: Either.Right(Unit) } + } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsAndResolveOneOnOnesUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsAndResolveOneOnOnesUseCase.kt new file mode 100644 index 00000000000..b42ff81ecc3 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsAndResolveOneOnOnesUseCase.kt @@ -0,0 +1,52 @@ +/* + * 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.user + +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.feature.conversation.mls.OneOnOneResolver +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.flatMap + +/** + * Update self supported protocols, and if the supported protocols + * did change we also resolve the active protocol for all one-on-one + * conversations. + */ +interface UpdateSupportedProtocolsAndResolveOneOnOnesUseCase { + + /** + * @param synchroniseUsers if true we synchronize all known users from backend + * in order to have to up-to-date information about which protocols are supported. + */ + suspend operator fun invoke(synchroniseUsers: Boolean): Either +} + +class UpdateSupportedProtocolsAndResolveOneOnOnesUseCaseImpl( + private val updateSupportedProtocols: UpdateSupportedProtocolsUseCase, + private val oneOnOneResolver: OneOnOneResolver +) : UpdateSupportedProtocolsAndResolveOneOnOnesUseCase { + + override suspend operator fun invoke(synchroniseUsers: Boolean) = + updateSupportedProtocols().flatMap { updated -> + if (updated) { + oneOnOneResolver.resolveAllOneOnOneConversations(synchronizeUsers = synchroniseUsers) + } else { + Either.Right(Unit) + } + } +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt index 00f86da5cc3..b9aef7c048a 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCase.kt @@ -20,12 +20,11 @@ package com.wire.kalium.logic.feature.user import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.client.Client import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.client.isActive -import com.wire.kalium.logic.data.featureConfig.FeatureConfigRepository import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel -import com.wire.kalium.logic.data.featureConfig.MLSModel import com.wire.kalium.logic.data.featureConfig.Status import com.wire.kalium.logic.data.user.SupportedProtocol import com.wire.kalium.logic.data.user.UserRepository @@ -47,14 +46,14 @@ interface UpdateSupportedProtocolsUseCase { internal class UpdateSupportedProtocolsUseCaseImpl( private val clientsRepository: ClientRepository, private val userRepository: UserRepository, - private val featureConfigRepository: FeatureConfigRepository + private val userConfigRepository: UserConfigRepository ) : UpdateSupportedProtocolsUseCase { override suspend operator fun invoke(): Either { kaliumLogger.d("Updating supported protocols") return (userRepository.getSelfUser()?.let { selfUser -> - supportedProtocols().flatMap { newSupportedProtocols -> + selfSupportedProtocols().flatMap { newSupportedProtocols -> kaliumLogger.i( "Updating supported protocols = $newSupportedProtocols previously = ${selfUser.supportedProtocols}" ) @@ -76,29 +75,31 @@ internal class UpdateSupportedProtocolsUseCaseImpl( } ?: Either.Left(StorageFailure.DataNotFound)) } - private suspend fun supportedProtocols(): Either> = - featureConfigRepository.getFeatureConfigs().flatMap { featureConfigs -> - clientsRepository.selfListOfClients().map { selfClients -> - val mlsConfiguration = featureConfigs.mlsModel - val migrationConfiguration = featureConfigs.mlsMigrationModel ?: MIGRATION_CONFIGURATION_DISABLED - val supportedProtocols = mutableSetOf() - if (proteusIsSupported(mlsConfiguration, migrationConfiguration)) { - supportedProtocols.add(SupportedProtocol.PROTEUS) - } + private suspend fun selfSupportedProtocols(): Either> = + clientsRepository.selfListOfClients().flatMap { selfClients -> + userConfigRepository.getMigrationConfiguration() + .flatMapLeft { if (it is StorageFailure.DataNotFound) Either.Right(MIGRATION_CONFIGURATION_DISABLED) else Either.Left(it) } + .flatMap { migrationConfiguration -> + userConfigRepository.getSupportedProtocols().map { supportedProtocols -> + val selfSupportedProtocols = mutableSetOf() + if (proteusIsSupported(supportedProtocols, migrationConfiguration)) { + selfSupportedProtocols.add(SupportedProtocol.PROTEUS) + } - if (mlsIsSupported(mlsConfiguration, migrationConfiguration, selfClients)) { - supportedProtocols.add(SupportedProtocol.MLS) + if (mlsIsSupported(supportedProtocols, migrationConfiguration, selfClients)) { + selfSupportedProtocols.add(SupportedProtocol.MLS) + } + selfSupportedProtocols + } } - supportedProtocols - } } private fun mlsIsSupported( - mlsConfiguration: MLSModel, + supportedProtocols: Set, migrationConfiguration: MLSMigrationModel, selfClients: List ): Boolean { - val mlsIsSupported = mlsConfiguration.supportedProtocols.contains(SupportedProtocol.MLS) + val mlsIsSupported = supportedProtocols.contains(SupportedProtocol.MLS) val mlsMigrationHasEnded = migrationConfiguration.hasMigrationEnded() val allSelfClientsAreMLSCapable = selfClients.filter { it.isActive }.all { it.isMLSCapable } kaliumLogger.d( @@ -110,10 +111,10 @@ internal class UpdateSupportedProtocolsUseCaseImpl( } private fun proteusIsSupported( - mlsConfiguration: MLSModel, + supportedProtocols: Set, migrationConfiguration: MLSMigrationModel ): Boolean { - val proteusIsSupported = mlsConfiguration.supportedProtocols.contains(SupportedProtocol.PROTEUS) + val proteusIsSupported = supportedProtocols.contains(SupportedProtocol.PROTEUS) val mlsMigrationHasEnded = migrationConfiguration.hasMigrationEnded() kaliumLogger.d( "proteus is supported = $proteusIsSupported, " + diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UserScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UserScope.kt index f73eb4f2277..a0cad86fe92 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UserScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/UserScope.kt @@ -20,10 +20,8 @@ package com.wire.kalium.logic.feature.user import com.wire.kalium.logic.configuration.server.ServerConfigRepository import com.wire.kalium.logic.data.asset.AssetRepository -import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.connection.ConnectionRepository import com.wire.kalium.logic.data.e2ei.E2EIRepository -import com.wire.kalium.logic.data.featureConfig.FeatureConfigRepository import com.wire.kalium.logic.data.id.QualifiedIdMapper import com.wire.kalium.logic.data.properties.UserPropertyRepository import com.wire.kalium.logic.data.publicuser.SearchUserRepository @@ -85,9 +83,8 @@ class UserScope internal constructor( private val messageSender: MessageSender, private val clientIdProvider: CurrentClientIdProvider, private val e2EIRepository: E2EIRepository, - private val clientRepository: ClientRepository, - private val featureConfigRepository: FeatureConfigRepository, - private val isSelfATeamMember: IsSelfATeamMemberUseCase + private val isSelfATeamMember: IsSelfATeamMemberUseCase, + private val updateSupportedProtocolsUseCase: UpdateSupportedProtocolsUseCase, ) { private val validateUserHandleUseCase: ValidateUserHandleUseCase get() = ValidateUserHandleUseCaseImpl() val getSelfUser: GetSelfUserUseCase get() = GetSelfUserUseCaseImpl(userRepository) @@ -118,12 +115,6 @@ class UserScope internal constructor( get() = UpdateSelfAvailabilityStatusUseCase(accountRepository, messageSender, clientIdProvider, selfUserId) val getAllContactsNotInConversation: GetAllContactsNotInConversationUseCase get() = GetAllContactsNotInConversationUseCase(userRepository) - val updateSupportedProtocols: UpdateSupportedProtocolsUseCase - get() = UpdateSupportedProtocolsUseCaseImpl( - clientRepository, - userRepository, - featureConfigRepository - ) val isPasswordRequired get() = IsPasswordRequiredUseCase( @@ -165,4 +156,6 @@ class UserScope internal constructor( val getAssetSizeLimit: GetAssetSizeLimitUseCase get() = GetAssetSizeLimitUseCaseImpl(isSelfATeamMember) val deleteAccount: DeleteAccountUseCase get() = DeleteAccountUseCase(accountRepository) + + val updateSupportedProtocols: UpdateSupportedProtocolsUseCase get() = updateSupportedProtocolsUseCase } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiver.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiver.kt index e4690dc3b44..ac5c525e3ee 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiver.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiver.kt @@ -29,6 +29,7 @@ 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.functional.Either @@ -43,6 +44,7 @@ internal class FeatureConfigEventReceiverImpl internal constructor( private val guestRoomConfigHandler: GuestRoomConfigHandler, private val fileSharingConfigHandler: FileSharingConfigHandler, private val mlsConfigHandler: MLSConfigHandler, + private val mlsMigrationConfigHandler: MLSMigrationConfigHandler, private val classifiedDomainsConfigHandler: ClassifiedDomainsConfigHandler, private val conferenceCallingConfigHandler: ConferenceCallingConfigHandler, private val passwordChallengeConfigHandler: SecondFactorPasswordChallengeConfigHandler, @@ -84,7 +86,8 @@ internal class FeatureConfigEventReceiverImpl internal constructor( private suspend fun handleFeatureConfigEvent(event: Event.FeatureConfig): Either = when (event) { is Event.FeatureConfig.FileSharingUpdated -> fileSharingConfigHandler.handle(event.model) - is Event.FeatureConfig.MLSUpdated -> mlsConfigHandler.handle(event.model) + is Event.FeatureConfig.MLSUpdated -> mlsConfigHandler.handle(event.model, duringSlowSync = false) + is Event.FeatureConfig.MLSMigrationUpdated -> mlsMigrationConfigHandler.handle(event.model, duringSlowSync = false) is Event.FeatureConfig.ClassifiedDomainsUpdated -> classifiedDomainsConfigHandler.handle(event.model) is Event.FeatureConfig.ConferenceCallingUpdated -> conferenceCallingConfigHandler.handle(event.model) is Event.FeatureConfig.GuestRoomLinkUpdated -> guestRoomConfigHandler.handle(event.model) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt index fcac1670dfc..b41e37e166b 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/featureConfig/FeatureConfigTest.kt @@ -43,7 +43,7 @@ object FeatureConfigTest { secondFactorPasswordChallengeModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED), ssoModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED), validateSAMLEmailsModel: ConfigsStatusModel = ConfigsStatusModel(Status.ENABLED), - mlsModel: MLSModel = MLSModel(listOf(), SupportedProtocol.PROTEUS, setOf(), Status.ENABLED), + mlsModel: MLSModel = MLSModel(listOf(), SupportedProtocol.PROTEUS, setOf(SupportedProtocol.PROTEUS), Status.ENABLED), e2EIModel: E2EIModel = E2EIModel(E2EIConfigModel("url", 10000L), Status.ENABLED), mlsMigrationModel: MLSMigrationModel? = MLSMigrationModel( Instant.DISTANT_FUTURE, diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/mlsmigration/MLSMigrationRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/mlsmigration/MLSMigrationRepositoryTest.kt deleted file mode 100644 index f6c5981f4a5..00000000000 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/mlsmigration/MLSMigrationRepositoryTest.kt +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.mlsmigration - -import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel -import com.wire.kalium.logic.data.featureConfig.Status -import com.wire.kalium.logic.data.user.SupportedProtocol -import com.wire.kalium.logic.di.MapperProvider -import com.wire.kalium.logic.functional.getOrNull -import com.wire.kalium.logic.util.shouldSucceed -import com.wire.kalium.network.api.base.authenticated.conversation.ConvProtocol -import com.wire.kalium.network.api.base.authenticated.featureConfigs.AppLockConfigDTO -import com.wire.kalium.network.api.base.authenticated.featureConfigs.ClassifiedDomainsConfigDTO -import com.wire.kalium.network.api.base.authenticated.featureConfigs.E2EIConfigDTO -import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigApi -import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigData -import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureConfigResponse -import com.wire.kalium.network.api.base.authenticated.featureConfigs.FeatureFlagStatusDTO -import com.wire.kalium.network.api.base.authenticated.featureConfigs.MLSConfigDTO -import com.wire.kalium.network.api.base.authenticated.featureConfigs.MLSMigrationConfigDTO -import com.wire.kalium.network.api.base.authenticated.featureConfigs.SelfDeletingMessagesConfigDTO -import com.wire.kalium.network.api.base.model.SupportedProtocolDTO -import com.wire.kalium.network.utils.NetworkResponse -import com.wire.kalium.persistence.dao.MetadataDAO -import io.mockative.Mock -import io.mockative.anything -import io.mockative.classOf -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 kotlinx.datetime.Clock -import kotlinx.datetime.Instant -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import kotlin.test.Test -import kotlin.test.assertEquals - -class MLSMigrationRepositoryTest { - @Test - fun givenExistingConfiguration_whenFetchingMigrationConfiguration_thenConfigurationIsPersisted() = runTest { - val (arrangement, mlsMigrationRepository) = Arrangement() - .withFeatureConfigApiRequestSucceeds() - .withMetaDataDaoInsertValue() - .arrange() - - val result = mlsMigrationRepository.fetchMigrationConfiguration() - - result.shouldSucceed() - - verify(arrangement.metadataDAO) - .suspendFunction(arrangement.metadataDAO::insertValue) - .with(anything(), eq(MLSMigrationRepositoryImpl.MLS_MIGRATION_CONFIGURATION_KEY)) - .wasInvoked(exactly = once) - } - - @Test - fun givenMLSConfiguration_whenSettingMigrationConfiguration_thenValueIsEncodedCorrectly() = runTest { - val configuration = MLSMigrationModel( - Clock.System.now(), - Clock.System.now(), - Status.ENABLED - ) - val encodedValue = Json.encodeToString(MapperProvider.featureConfigMapper().fromModel(configuration)) - - val (arrangement, mlsMigrationRepository) = Arrangement() - .withMetaDataDaoInsertValue() - .arrange() - - val result = mlsMigrationRepository.setMigrationConfiguration(configuration) - result.shouldSucceed() - - verify(arrangement.metadataDAO) - .suspendFunction(arrangement.metadataDAO::insertValue) - .with(eq(encodedValue), eq(MLSMigrationRepositoryImpl.MLS_MIGRATION_CONFIGURATION_KEY)) - .wasInvoked(exactly = once) - } - - @Test - fun givenMLSConfiguration_whenGettingMigrationConfiguration_thenValueIsDecodedCorrectly() = runTest { - val configuration = MLSMigrationModel( - Clock.System.now(), - Clock.System.now(), - Status.ENABLED - ) - val encodedValue = Json.encodeToString(MapperProvider.featureConfigMapper().fromModel(configuration)) - - val (arrangement, mlsMigrationRepository) = Arrangement() - .withMetaDataDaoValueReturns(encodedValue) - .arrange() - - val result = mlsMigrationRepository.getMigrationConfiguration() - result.shouldSucceed() - - verify(arrangement.metadataDAO) - .suspendFunction(arrangement.metadataDAO::valueByKey) - .with(eq(MLSMigrationRepositoryImpl.MLS_MIGRATION_CONFIGURATION_KEY)) - .wasInvoked(exactly = once) - - assertEquals(configuration, result.getOrNull()) - } - - private class Arrangement { - - @Mock - val featureConfigApi = mock(classOf()) - - @Mock - val metadataDAO = mock(classOf()) - - fun withFeatureConfigApiRequestSucceeds() = apply { - given(featureConfigApi).suspendFunction(featureConfigApi::featureConfigs) - .whenInvoked() - .thenReturn(NetworkResponse.Success(featureConfigResponse, emptyMap(),200)) - } - - fun withMetaDataDaoValueReturns(value: String?) = apply { - given(metadataDAO).suspendFunction(metadataDAO::valueByKey) - .whenInvokedWith(anything()) - .thenReturn(value) - } - - fun withMetaDataDaoInsertValue() = apply { - given(metadataDAO).suspendFunction(metadataDAO::insertValue) - .whenInvokedWith(anything(), anything()) - .thenReturn(Unit) - } - - fun arrange() = this to MLSMigrationRepositoryImpl(featureConfigApi, metadataDAO) - - companion object { - val featureConfigResponse = FeatureConfigResponse( - FeatureConfigData.AppLock( - AppLockConfigDTO(true, 0), FeatureFlagStatusDTO.ENABLED - ), - FeatureConfigData.ClassifiedDomains(ClassifiedDomainsConfigDTO(listOf()), FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.ConferenceCalling(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.ConversationGuestLinks(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.DigitalSignatures(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.FileSharing(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.Legalhold(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.SearchVisibility(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.SelfDeletingMessages(SelfDeletingMessagesConfigDTO(0), FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.SecondFactorPasswordChallenge(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.SSO(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.ValidateSAMLEmails(FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.MLS( - MLSConfigDTO( - emptyList(), - SupportedProtocolDTO.MLS, - listOf(SupportedProtocolDTO.MLS), - emptyList(), - 1 - ), FeatureFlagStatusDTO.ENABLED), - FeatureConfigData.E2EI( - E2EIConfigDTO(null), - FeatureFlagStatusDTO.ENABLED - ), - FeatureConfigData.MLSMigration( - MLSMigrationConfigDTO(Instant.DISTANT_FUTURE, Instant.DISTANT_FUTURE), - FeatureFlagStatusDTO.ENABLED - ) - ) - } - } -} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCaseTest.kt index 4a96452d408..c90409ba8a9 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/DeleteClientUseCaseTest.kt @@ -22,7 +22,7 @@ import com.wire.kalium.logic.CoreFailure import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.client.DeleteClientParam -import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsUseCase +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase import com.wire.kalium.logic.framework.TestClient import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.test_util.TestNetworkException @@ -33,6 +33,7 @@ import com.wire.kalium.logic.util.arrangement.mls.OneOnOneResolverArrangementImp import com.wire.kalium.network.exceptions.KaliumException import io.ktor.utils.io.errors.IOException import io.mockative.Mock +import io.mockative.any import io.mockative.anything import io.mockative.classOf import io.mockative.eq @@ -116,31 +117,15 @@ class DeleteClientUseCaseTest { fun givenRepositoryDeleteClientSucceeds_whenDeleting_thenUpdateSupportedProtocols() = runTest { val (arrangement, deleteClient) = arrange { withDeleteClient(Either.Right(Unit)) - withUpdateSupportedProtocols(Either.Right(false)) + withUpdateSupportedProtocolsAndResolveOneOnOnes(Either.Right(Unit)) } val result = deleteClient(DELETE_CLIENT_PARAMETERS) assertIs(result) - verify(arrangement.updateSupportedProtocols) - .suspendFunction(arrangement.updateSupportedProtocols::invoke) - .wasInvoked(exactly = once) - } - - @Test - fun givenSupportedProtocolsAreUpdated_whenDeleting_thenResolveActiveOneOnOneConversations() = runTest { - val (arrangement, deleteClient) = arrange { - withDeleteClient(Either.Right(Unit)) - withUpdateSupportedProtocols(Either.Right(true)) - withFetchAllOtherUsersReturning(Either.Right(Unit)) - withResolveAllOneOnOneConversationsReturning(Either.Right(Unit)) - } - - val result = deleteClient(DELETE_CLIENT_PARAMETERS) - - assertIs(result) - verify(arrangement.updateSupportedProtocols) - .suspendFunction(arrangement.updateSupportedProtocols::invoke) + verify(arrangement.updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(arrangement.updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .with(eq(true)) .wasInvoked(exactly = once) } @@ -152,7 +137,7 @@ class DeleteClientUseCaseTest { val clientRepository = mock(classOf()) @Mock - val updateSupportedProtocols = mock(classOf()) + val updateSupportedProtocolsAndResolveOneOnOnes = mock(classOf()) fun withDeleteClient(result: Either) { given(clientRepository) @@ -161,10 +146,10 @@ class DeleteClientUseCaseTest { .then { result } } - fun withUpdateSupportedProtocols(result: Either) { - given(updateSupportedProtocols) - .suspendFunction(updateSupportedProtocols::invoke) - .whenInvoked() + fun withUpdateSupportedProtocolsAndResolveOneOnOnes(result: Either) { + given(updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .whenInvokedWith(any()) .thenReturn(result) } @@ -172,9 +157,7 @@ class DeleteClientUseCaseTest { block() this@Arrangement to DeleteClientUseCaseImpl( clientRepository = clientRepository, - updateSupportedProtocols = updateSupportedProtocols, - userRepository = userRepository, - oneOnOneResolver = oneOnOneResolver, + updateSupportedProtocolsAndResolveOneOnOnes = updateSupportedProtocolsAndResolveOneOnOnes, ) } } @@ -185,6 +168,5 @@ class DeleteClientUseCaseTest { val CLIENT = TestClient.CLIENT val DELETE_CLIENT_PARAMETERS = DeleteClientParam("pass", CLIENT.id) val TEST_FAILURE = NetworkFailure.ServerMiscommunication(KaliumException.GenericError(IOException("no internet"))) - } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt index ccb657f02e3..9e4e26b3e4f 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/SyncFeatureConfigsUseCaseTest.kt @@ -29,7 +29,6 @@ import com.wire.kalium.logic.data.featureConfig.FeatureConfigRepository import com.wire.kalium.logic.data.featureConfig.FeatureConfigTest import com.wire.kalium.logic.data.featureConfig.E2EIConfigModel import com.wire.kalium.logic.data.featureConfig.E2EIModel -import com.wire.kalium.logic.data.featureConfig.MLSModel import com.wire.kalium.logic.data.featureConfig.SelfDeletingMessagesConfigModel import com.wire.kalium.logic.data.featureConfig.SelfDeletingMessagesModel import com.wire.kalium.logic.data.featureConfig.Status @@ -40,11 +39,12 @@ 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.data.user.SupportedProtocol import com.wire.kalium.logic.feature.selfDeletingMessages.SelfDeletionMapper.toTeamSelfDeleteTimer import com.wire.kalium.logic.feature.selfDeletingMessages.TeamSelfDeleteTimer +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase import com.wire.kalium.logic.featureFlags.BuildFileRestrictionState import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.framework.TestUser @@ -52,6 +52,7 @@ import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.test_util.TestNetworkException import com.wire.kalium.logic.util.shouldSucceed import com.wire.kalium.persistence.config.inMemoryUserConfigStorage +import com.wire.kalium.persistence.dao.SupportedProtocolEntity import com.wire.kalium.persistence.dao.unread.UserConfigDAO import io.mockative.Mock import io.mockative.any @@ -83,6 +84,7 @@ class SyncFeatureConfigsUseCaseTest { ) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -102,6 +104,7 @@ class SyncFeatureConfigsUseCaseTest { ) ) ) + .withGetSupportedProtocolsReturning(null) .withGetTeamSettingsSelfDeletionStatusSuccessful() .arrange() @@ -112,135 +115,6 @@ class SyncFeatureConfigsUseCaseTest { } } - @Test - fun givenMlsIsEnabledAndMlsIsDefaultProtocol_whenSyncing_thenMlsShouldBeStoredAsDefault() = runTest { - val (arrangement, syncFeatureConfigsUseCase) = Arrangement() - .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel(mlsModel = MLSModel( - allowedUsers = emptyList(), - defaultProtocol = SupportedProtocol.MLS, - supportedProtocols = emptySet(), - status = Status.ENABLED)) - ) - .withGetTeamSettingsSelfDeletionStatusSuccessful() - .arrange() - - syncFeatureConfigsUseCase() - - arrangement.userConfigRepository.getDefaultProtocol().shouldSucceed { - assertEquals(SupportedProtocol.MLS, it) - } - } - - @Test - fun givenMlsIsEnabledAndProteusIsDefaultProtocol_whenSyncing_thenProteusShouldBeStoredAsDefault() = runTest { - val (arrangement, syncFeatureConfigsUseCase) = Arrangement() - .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel(mlsModel = MLSModel( - allowedUsers = emptyList(), - defaultProtocol = SupportedProtocol.PROTEUS, - supportedProtocols = emptySet(), - status = Status.ENABLED)) - ) - .withGetTeamSettingsSelfDeletionStatusSuccessful() - .arrange() - - syncFeatureConfigsUseCase() - - arrangement.userConfigRepository.getDefaultProtocol().shouldSucceed { - assertEquals(SupportedProtocol.PROTEUS, it) - } - } - - @Test - fun givenMlsIsDisabledAndMlsIsDefaultProtocol_whenSyncing_thenProteusShouldBeStoredAsDefault() = runTest { - val (arrangement, syncFeatureConfigsUseCase) = Arrangement() - .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel(mlsModel = MLSModel( - allowedUsers = emptyList(), - defaultProtocol = SupportedProtocol.MLS, - supportedProtocols = emptySet(), - status = Status.DISABLED)) - ) - .withGetTeamSettingsSelfDeletionStatusSuccessful() - .arrange() - - syncFeatureConfigsUseCase() - - arrangement.userConfigRepository.getDefaultProtocol().shouldSucceed { - assertEquals(SupportedProtocol.PROTEUS, it) - } - } - - @Test - fun givenMlsIsEnabledAndSelfUserIsWhitelisted_whenSyncing_thenItShouldBeStoredAsEnabled() = runTest { - val (arrangement, syncFeatureConfigsUseCase) = Arrangement() - .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel( - mlsModel = MLSModel( - allowedUsers = listOf(SELF_USER_ID.toPlainID()), - defaultProtocol = SupportedProtocol.MLS, - supportedProtocols = emptySet(), - status = Status.ENABLED - ) - ) - ) - .withGetTeamSettingsSelfDeletionStatusSuccessful() - .arrange() - - syncFeatureConfigsUseCase() - - arrangement.userConfigRepository.isMLSEnabled().shouldSucceed { - assertTrue(it) - } - } - - @Test - fun givenMlsIsEnabledAndSelfUserIsNotWhitelisted_whenSyncing_thenItShouldBeStoredAsDisabled() = runTest { - val (arrangement, syncFeatureConfigsUseCase) = Arrangement() - .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel( - mlsModel = MLSModel( - allowedUsers = emptyList(), - defaultProtocol = SupportedProtocol.MLS, - supportedProtocols = emptySet(), - status = Status.ENABLED - ) - ) - ) - .withGetTeamSettingsSelfDeletionStatusSuccessful() - .arrange() - - syncFeatureConfigsUseCase() - - arrangement.userConfigRepository.isMLSEnabled().shouldSucceed { - assertFalse(it) - } - } - - @Test - fun givenMlsIsDisabled_whenSyncing_thenItShouldBeStoredAsDisabled() = runTest { - val (arrangement, syncFeatureConfigsUseCase) = Arrangement() - .withRemoteFeatureConfigsSucceeding( - FeatureConfigTest.newModel( - mlsModel = MLSModel( - allowedUsers = emptyList(), - defaultProtocol = SupportedProtocol.MLS, - supportedProtocols = emptySet(), - status = Status.DISABLED - ) - ) - ) - .withGetTeamSettingsSelfDeletionStatusSuccessful() - .arrange() - - syncFeatureConfigsUseCase() - - arrangement.userConfigRepository.isMLSEnabled().shouldSucceed { - assertFalse(it) - } - } - @Test fun givenConferenceCallingIsEnabled_whenSyncing_thenItShouldBeStoredAsEnabled() = runTest { val (arrangement, syncFeatureConfigsUseCase) = Arrangement() @@ -248,6 +122,7 @@ class SyncFeatureConfigsUseCaseTest { FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.ENABLED)) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -264,6 +139,7 @@ class SyncFeatureConfigsUseCaseTest { FeatureConfigTest.newModel(conferenceCallingModel = ConferenceCallingModel(Status.DISABLED)) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() @@ -281,6 +157,7 @@ class SyncFeatureConfigsUseCaseTest { FeatureConfigTest.newModel(fileSharingModel = ConfigsStatusModel(Status.ENABLED)) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -301,6 +178,7 @@ class SyncFeatureConfigsUseCaseTest { isStatusChanged = false ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -317,6 +195,7 @@ class SyncFeatureConfigsUseCaseTest { FeatureConfigTest.newModel(fileSharingModel = ConfigsStatusModel(Status.DISABLED)) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() @@ -338,6 +217,7 @@ class SyncFeatureConfigsUseCaseTest { isStatusChanged = false ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -358,6 +238,7 @@ class SyncFeatureConfigsUseCaseTest { isStatusChanged = false ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -382,6 +263,7 @@ class SyncFeatureConfigsUseCaseTest { isStatusChanged = false ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -405,6 +287,7 @@ class SyncFeatureConfigsUseCaseTest { ) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -427,6 +310,7 @@ class SyncFeatureConfigsUseCaseTest { ) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -449,6 +333,7 @@ class SyncFeatureConfigsUseCaseTest { ) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -472,6 +357,7 @@ class SyncFeatureConfigsUseCaseTest { ) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -528,6 +414,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withBuildConfigFileSharing(BuildFileRestrictionState.NoRestriction) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -549,6 +436,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withBuildConfigFileSharing(BuildFileRestrictionState.AllowSome(listOf("png", "jpg"))) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -566,6 +454,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withBuildConfigFileSharing(BuildFileRestrictionState.AllowSome(listOf("png", "jpg"))) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -583,6 +472,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withBuildConfigFileSharing(BuildFileRestrictionState.NoRestriction) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -598,6 +488,7 @@ class SyncFeatureConfigsUseCaseTest { val (arrangement, getTeamSettingsSelfDeletionStatusUseCase) = Arrangement() .withKaliumConfigs { it.copy(selfDeletingMessages = false) } .withSetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() // When @@ -624,6 +515,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withSetTeamSettingsSelfDeletionStatusSuccessful() .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() // When @@ -650,6 +542,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withSetTeamSettingsSelfDeletionStatusSuccessful() .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() // When @@ -677,6 +570,7 @@ class SyncFeatureConfigsUseCaseTest { ) .withSetTeamSettingsSelfDeletionStatusSuccessful() .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() // When @@ -702,6 +596,7 @@ class SyncFeatureConfigsUseCaseTest { FeatureConfigTest.newModel(e2EIModel = e2EIModel) ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -720,6 +615,7 @@ class SyncFeatureConfigsUseCaseTest { FeatureConfigTest.newModel() ) .withGetTeamSettingsSelfDeletionStatusSuccessful() + .withGetSupportedProtocolsReturning(null) .arrange() syncFeatureConfigsUseCase() @@ -748,6 +644,8 @@ class SyncFeatureConfigsUseCaseTest { @Mock val featureConfigRepository = mock(classOf()) + @Mock + val updateSupportedProtocolsAndResolveOneOnOnes = mock(classOf()) private lateinit var syncFeatureConfigsUseCase: SyncFeatureConfigsUseCase @@ -816,6 +714,13 @@ class SyncFeatureConfigsUseCaseTest { .then { } } + fun withGetSupportedProtocolsReturning(result: Set?) = apply { + given(userConfigDAO) + .suspendFunction(userConfigDAO::getSupportedProtocols) + .whenInvoked() + .thenReturn(result) + } + fun withKaliumConfigs(changeConfigs: (KaliumConfigs) -> KaliumConfigs) = apply { this.kaliumConfigs = changeConfigs(this.kaliumConfigs) } @@ -825,7 +730,8 @@ class SyncFeatureConfigsUseCaseTest { featureConfigRepository, GuestRoomConfigHandler(userConfigRepository, kaliumConfigs), FileSharingConfigHandler(userConfigRepository), - MLSConfigHandler(userConfigRepository, TestUser.SELF.id), + MLSConfigHandler(userConfigRepository, updateSupportedProtocolsAndResolveOneOnOnes, TestUser.SELF.id), + MLSMigrationConfigHandler(userConfigRepository, updateSupportedProtocolsAndResolveOneOnOnes), ClassifiedDomainsConfigHandler(userConfigRepository), ConferenceCallingConfigHandler(userConfigRepository), SecondFactorPasswordChallengeConfigHandler(userConfigRepository), @@ -837,8 +743,4 @@ class SyncFeatureConfigsUseCaseTest { } } - - private companion object { - val SELF_USER_ID = TestUser.USER_ID - } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandlerTest.kt new file mode 100644 index 00000000000..51eebdebd76 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSConfigHandlerTest.kt @@ -0,0 +1,223 @@ +/* + * 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.featureConfig.handler + +import com.wire.kalium.logic.data.featureConfig.MLSModel +import com.wire.kalium.logic.data.featureConfig.Status +import com.wire.kalium.logic.data.user.SupportedProtocol +import com.wire.kalium.logic.framework.TestUser +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.util.arrangement.repository.UserConfigRepositoryArrangement +import com.wire.kalium.logic.util.arrangement.repository.UserConfigRepositoryArrangementImpl +import com.wire.kalium.logic.util.arrangement.usecase.UpdateSupportedProtocolsAndResolveOneOnOnesArrangement +import com.wire.kalium.logic.util.arrangement.usecase.UpdateSupportedProtocolsAndResolveOneOnOnesArrangementImpl +import io.mockative.eq +import io.mockative.once +import io.mockative.verify +import kotlinx.coroutines.test.runTest +import kotlin.test.Test + +class MLSConfigHandlerTest { + @Test + fun givenMlsIsEnabledAndMlsIsDefaultProtocol_whenSyncing_thenSetMlsAsDefault() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.ENABLED, + defaultProtocol = SupportedProtocol.MLS + ), duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setDefaultProtocol) + .with(eq(SupportedProtocol.MLS)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMlsIsEnabledAndProteusIsDefaultProtocol_whenSyncing_thenSetProteusAsDefault() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.ENABLED, + defaultProtocol = SupportedProtocol.PROTEUS + ), duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setDefaultProtocol) + .with(eq(SupportedProtocol.PROTEUS)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMlsIsDisabledAndMlsIsDefaultProtocol_whenSyncing_thenSetProteusAsDefault() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.DISABLED, + defaultProtocol = SupportedProtocol.MLS + ), duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setDefaultProtocol) + .with(eq(SupportedProtocol.PROTEUS)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMlsIsEnabledAndSelfUserIsWhitelisted_whenSyncing_thenSetMlsEnabled() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.ENABLED, + allowedUsers = listOf(SELF_USER_ID.toPlainID()) + ), duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setMLSEnabled) + .with(eq(true)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMlsIsEnabledAndSelfUserIsNotWhitelisted_whenSyncing_thenSetMlsDisabled() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.ENABLED, + allowedUsers = listOf(TestUser.OTHER_USER_ID.toPlainID()) + ), duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setMLSEnabled) + .with(eq(false)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMlsIsDisabled_whenSyncing_thenSetMlsDisabled() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.DISABLED + ), duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setMLSEnabled) + .with(eq(false)) + .wasInvoked(exactly = once) + } + + @Test + fun givenSupportedProtocolsHasChangedInEvent_whenSyncing_thenUpdateSelfSupportedProtocols() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withUpdateSupportedProtocolsAndResolveOneOnOnesSuccessful() + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.ENABLED, + supportedProtocols = setOf(SupportedProtocol.PROTEUS, SupportedProtocol.MLS) + ), duringSlowSync = false) + + verify(arrangement.updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(arrangement.updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .with(eq(true)) + .wasInvoked(exactly = once) + } + + @Test + fun givenSupportedProtocolsHasChangedDuringSlowSync_whenSyncing_thenUpdateSelfSupportedProtocols() = runTest { + val (arrangement, handler) = arrange { + withGetSupportedProtocolsReturning(Either.Right(setOf(SupportedProtocol.PROTEUS))) + withUpdateSupportedProtocolsAndResolveOneOnOnesSuccessful() + withSetSupportedProtocolsSuccessful() + withSetDefaultProtocolSuccessful() + withSetMLSEnabledSuccessful() + } + + handler.handle(MLS_CONFIG.copy( + status = Status.ENABLED, + supportedProtocols = setOf(SupportedProtocol.PROTEUS, SupportedProtocol.MLS) + ), duringSlowSync = true) + + verify(arrangement.updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(arrangement.updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .with(eq(false)) + .wasInvoked(exactly = once) + } + + private class Arrangement(private val block: Arrangement.() -> Unit) : + UserConfigRepositoryArrangement by UserConfigRepositoryArrangementImpl(), + UpdateSupportedProtocolsAndResolveOneOnOnesArrangement by UpdateSupportedProtocolsAndResolveOneOnOnesArrangementImpl() + { + fun arrange() = run { + block() + this@Arrangement to MLSConfigHandler( + userConfigRepository = userConfigRepository, + updateSupportedProtocolsAndResolveOneOnOnes = updateSupportedProtocolsAndResolveOneOnOnes, + SELF_USER_ID + ) + } + } + + private companion object { + fun arrange(configuration: Arrangement.() -> Unit) = Arrangement(configuration).arrange() + + val SELF_USER_ID = TestUser.USER_ID + val MLS_CONFIG = MLSModel( + allowedUsers = emptyList(), + defaultProtocol = SupportedProtocol.MLS, + supportedProtocols = setOf(SupportedProtocol.PROTEUS), + status = Status.ENABLED + ) + } + +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSMigrationConfigHandlerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSMigrationConfigHandlerTest.kt new file mode 100644 index 00000000000..e177a705e97 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/featureConfig/handler/MLSMigrationConfigHandlerTest.kt @@ -0,0 +1,109 @@ +/* + * 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.featureConfig.handler + +import com.wire.kalium.logic.data.featureConfig.MLSMigrationModel +import com.wire.kalium.logic.data.featureConfig.Status +import com.wire.kalium.logic.util.arrangement.repository.UserConfigRepositoryArrangement +import com.wire.kalium.logic.util.arrangement.repository.UserConfigRepositoryArrangementImpl +import com.wire.kalium.logic.util.arrangement.usecase.UpdateSupportedProtocolsAndResolveOneOnOnesArrangement +import com.wire.kalium.logic.util.arrangement.usecase.UpdateSupportedProtocolsAndResolveOneOnOnesArrangementImpl +import io.mockative.any +import io.mockative.eq +import io.mockative.once +import io.mockative.verify +import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant +import kotlin.test.Test + +class MLSMigrationConfigHandlerTest { + @Test + fun givenMlsConfiguration_whenHandling_thenSetMlsConfiguration() = runTest { + val (arrangement, handler) = arrange { + withSetMigrationConfigurationSuccessful() + } + + handler.handle(MIGRATION_CONFIG, duringSlowSync = false) + + verify(arrangement.userConfigRepository) + .suspendFunction(arrangement.userConfigRepository::setMigrationConfiguration) + .with(eq(MIGRATION_CONFIG)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMigrationHasEnded_whenHandling_thenUpdateSelfSupportedProtocols() = runTest { + val (arrangement, handler) = arrange { + withUpdateSupportedProtocolsAndResolveOneOnOnesSuccessful() + withSetMigrationConfigurationSuccessful() + } + + handler.handle(MIGRATION_CONFIG.copy( + startTime = Instant.DISTANT_PAST, + endTime = Instant.DISTANT_PAST + ), duringSlowSync = false) + + verify(arrangement.updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(arrangement.updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .with(eq(true)) + .wasInvoked(exactly = once) + } + + @Test + fun givenMigrationHasEndedDuringSlowSync_whenHandling_thenDontUpdateSelfSupportedProtocols() = runTest { + val (arrangement, handler) = arrange { + withUpdateSupportedProtocolsAndResolveOneOnOnesSuccessful() + withSetMigrationConfigurationSuccessful() + } + + handler.handle(MIGRATION_CONFIG.copy( + startTime = Instant.DISTANT_PAST, + endTime = Instant.DISTANT_PAST + ), duringSlowSync = true) + + verify(arrangement.updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(arrangement.updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .with(any()) + .wasNotInvoked() + } + + private class Arrangement(private val block: Arrangement.() -> Unit) : + UserConfigRepositoryArrangement by UserConfigRepositoryArrangementImpl(), + UpdateSupportedProtocolsAndResolveOneOnOnesArrangement by UpdateSupportedProtocolsAndResolveOneOnOnesArrangementImpl() + { + fun arrange() = run { + block() + this@Arrangement to MLSMigrationConfigHandler( + userConfigRepository = userConfigRepository, + updateSupportedProtocolsAndResolveOneOnOnes = updateSupportedProtocolsAndResolveOneOnOnes + ) + } + } + + private companion object { + fun arrange(configuration: Arrangement.() -> Unit) = Arrangement(configuration).arrange() + + val MIGRATION_CONFIG = MLSMigrationModel( + startTime = null, + endTime = null, + status = Status.ENABLED + ) + } + +} + diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManagerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManagerTest.kt index f6848678487..7ccb066128e 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManagerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/mlsmigration/MLSMigrationManagerTest.kt @@ -18,7 +18,6 @@ package com.wire.kalium.logic.feature.mlsmigration import com.wire.kalium.logic.data.client.ClientRepository -import com.wire.kalium.logic.data.mlsmigration.MLSMigrationRepository import com.wire.kalium.logic.data.sync.InMemoryIncrementalSyncRepository import com.wire.kalium.logic.data.sync.IncrementalSyncRepository import com.wire.kalium.logic.data.sync.IncrementalSyncStatus @@ -51,8 +50,8 @@ class MLSMigrationManagerTest { .withIsMLSSupported(true) .withHasRegisteredMLSClient(true) .withLastMLSMigrationCheck(true) - .withFetchMigrationConfigurationSucceeds() - .withLastMLSMigrationChecResetSucceeds() + .withRunMigrationSucceeds() + .withLastMLSMigrationCheckResetSucceeds() .arrange() arrangement.incrementalSyncRepository.updateIncrementalSyncState(IncrementalSyncStatus.Live) @@ -70,6 +69,7 @@ class MLSMigrationManagerTest { .withIsMLSSupported(true) .withHasRegisteredMLSClient(true) .withLastMLSMigrationCheck(false) + .withRunMigrationSucceeds() .arrange() arrangement.incrementalSyncRepository.updateIncrementalSyncState(IncrementalSyncStatus.Live) @@ -85,6 +85,7 @@ class MLSMigrationManagerTest { runTest(TestKaliumDispatcher.default) { val (arrangement, _) = Arrangement() .withIsMLSSupported(false) + .withRunMigrationSucceeds() .arrange() arrangement.incrementalSyncRepository.updateIncrementalSyncState(IncrementalSyncStatus.Live) @@ -101,6 +102,7 @@ class MLSMigrationManagerTest { val (arrangement, _) = Arrangement() .withIsMLSSupported(true) .withHasRegisteredMLSClient(false) + .withRunMigrationSucceeds() .arrange() arrangement.incrementalSyncRepository.updateIncrementalSyncState(IncrementalSyncStatus.Live) @@ -126,15 +128,12 @@ class MLSMigrationManagerTest { @Mock val timestampKeyRepository = mock(classOf()) - @Mock - val mlsMigrationRepository = mock(classOf()) - @Mock val mlsMigrationWorker = mock(classOf()) - fun withFetchMigrationConfigurationSucceeds() = apply { - given(mlsMigrationRepository) - .suspendFunction(mlsMigrationRepository::fetchMigrationConfiguration) + fun withRunMigrationSucceeds() = apply { + given(mlsMigrationWorker) + .suspendFunction(mlsMigrationWorker::runMigration) .whenInvoked() .thenReturn(Either.Right(Unit)) } @@ -146,7 +145,7 @@ class MLSMigrationManagerTest { .thenReturn(Either.Right(hasPassed)) } - fun withLastMLSMigrationChecResetSucceeds() = apply { + fun withLastMLSMigrationCheckResetSucceeds() = apply { given(timestampKeyRepository) .suspendFunction(timestampKeyRepository::reset) .whenInvokedWith(eq(TimestampKeys.LAST_MLS_MIGRATION_CHECK)) @@ -173,7 +172,6 @@ class MLSMigrationManagerTest { lazy { clientRepository }, lazy { timestampKeyRepository }, lazy { mlsMigrationWorker }, - lazy { mlsMigrationRepository }, TestKaliumDispatcher ) } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt index d66ee16a337..1608183e809 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/user/UpdateSupportedProtocolsUseCaseTest.kt @@ -17,6 +17,9 @@ */ package com.wire.kalium.logic.feature.user +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.client.Client import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.featureConfig.FeatureConfigRepository @@ -53,10 +56,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenSupportedProtocolsHasNotChanged_whenInvokingUseCase_thenSupportedProtocolsAreNotUpdated() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful(supportedProtocols = setOf(SupportedProtocol.PROTEUS)) - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.PROTEUS), - migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.PROTEUS)) + .withGetMigrationConfigurationSuccessful(ONGOING_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = emptyList()) .withUpdateSupportedProtocolsSuccessful() .arrange() @@ -73,10 +74,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenProteusAsSupportedProtocol_whenInvokingUseCase_thenProteusIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.PROTEUS), - migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.PROTEUS)) + .withGetMigrationConfigurationSuccessful(ONGOING_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = emptyList()) .withUpdateSupportedProtocolsSuccessful() .arrange() @@ -93,10 +92,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenProteusIsNotSupportedButMigrationHasNotEnded_whenInvokingUseCase_thenProteusIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.MLS), - migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.MLS)) + .withGetMigrationConfigurationSuccessful(ONGOING_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = emptyList()) .withUpdateSupportedProtocolsSuccessful() .arrange() @@ -113,10 +110,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenProteusIsNotSupported_whenInvokingUseCase_thenProteusIsNotIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.MLS), - migrationConfiguration = COMPLETED_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.MLS)) + .withGetMigrationConfigurationSuccessful(COMPLETED_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = emptyList()) .withUpdateSupportedProtocolsSuccessful() .arrange() @@ -133,10 +128,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenMlsIsSupportedAndAllActiveClientsAreCapable_whenInvokingUseCase_thenMlsIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.MLS), - migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.MLS)) + .withGetMigrationConfigurationSuccessful(ONGOING_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = listOf( TestClient.CLIENT.copy(isMLSCapable = true, lastActive = Clock.System.now()) )) @@ -155,10 +148,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenMlsIsSupportedAndAnInactiveClientIsNotMlsCapable_whenInvokingUseCase_thenMlsIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.MLS), - migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.MLS)) + .withGetMigrationConfigurationSuccessful(ONGOING_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = listOf( TestClient.CLIENT.copy(isMLSCapable = true, lastActive = Clock.System.now()), TestClient.CLIENT.copy(isMLSCapable = false, lastActive = Instant.DISTANT_PAST) @@ -178,10 +169,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenMlsIsSupportedAndAllActiveClientsAreNotCapable_whenInvokingUseCase_thenMlsIsNotIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.MLS), - migrationConfiguration = ONGOING_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.MLS)) + .withGetMigrationConfigurationSuccessful(ONGOING_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = listOf( TestClient.CLIENT.copy(isMLSCapable = true, lastActive = Clock.System.now()), TestClient.CLIENT.copy(isMLSCapable = false, lastActive = Clock.System.now()) @@ -201,10 +190,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenMlsIsSupportedAndMigrationHasEnded_whenInvokingUseCase_thenMlsIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.MLS), - migrationConfiguration = COMPLETED_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.MLS)) + .withGetMigrationConfigurationSuccessful(COMPLETED_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = listOf( TestClient.CLIENT.copy(isMLSCapable = true), TestClient.CLIENT.copy(isMLSCapable = false) @@ -224,10 +211,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenMigrationIsMissingAndAllClientsAreCapable_whenInvokingUseCase_thenMlsIsIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.PROTEUS, SupportedProtocol.MLS), - migrationConfiguration = null - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.PROTEUS, SupportedProtocol.MLS)) + .withGetMigrationConfigurationFailing(StorageFailure.DataNotFound) .withGetSelfClientsSuccessful(clients = listOf( TestClient.CLIENT.copy(isMLSCapable = true) )) @@ -246,10 +231,8 @@ class UpdateSupportedProtocolsUseCaseTest { fun givenMlsIsNotSupportedAndAllClientsAreCapable_whenInvokingUseCase_thenMlsIsNotIncluded() = runTest { val (arrangement, useCase) = Arrangement() .withGetSelfUserSuccessful() - .withGetFeatureConfigurationSuccessful( - supportedProtocols = setOf(SupportedProtocol.PROTEUS), - migrationConfiguration = DISABLED_MIGRATION_CONFIGURATION - ) + .withGetSupportedProtocolsSuccessful(setOf(SupportedProtocol.PROTEUS)) + .withGetMigrationConfigurationSuccessful(DISABLED_MIGRATION_CONFIGURATION) .withGetSelfClientsSuccessful(clients = listOf( TestClient.CLIENT.copy(isMLSCapable = true) )) @@ -270,7 +253,7 @@ class UpdateSupportedProtocolsUseCaseTest { @Mock val userRepository = mock(UserRepository::class) @Mock - val featureConfigRepository = mock(FeatureConfigRepository::class) + val userConfigRepository = mock(UserConfigRepository::class) fun withGetSelfUserSuccessful(supportedProtocols: Set? = null) = apply { given(userRepository) @@ -288,21 +271,25 @@ class UpdateSupportedProtocolsUseCaseTest { .thenReturn(Either.Right(Unit)) } - fun withGetFeatureConfigurationSuccessful( - supportedProtocols: Set, - migrationConfiguration: MLSMigrationModel?) = apply { - given(featureConfigRepository) - .suspendFunction(featureConfigRepository::getFeatureConfigs) + fun withGetMigrationConfigurationSuccessful(migrationConfiguration: MLSMigrationModel) = apply { + given(userConfigRepository) + .suspendFunction(userConfigRepository::getMigrationConfiguration) + .whenInvoked() + .thenReturn(Either.Right(migrationConfiguration)) + } + + fun withGetMigrationConfigurationFailing(failure: StorageFailure) = apply { + given(userConfigRepository) + .suspendFunction(userConfigRepository::getMigrationConfiguration) + .whenInvoked() + .thenReturn(Either.Left(failure)) + } + + fun withGetSupportedProtocolsSuccessful(supportedProtocols: Set) = apply { + given(userConfigRepository) + .suspendFunction(userConfigRepository::getSupportedProtocols) .whenInvoked() - .thenReturn(Either.Right(FeatureConfigTest.newModel( - mlsModel = MLSModel( - allowedUsers = emptyList(), - defaultProtocol = SupportedProtocol.PROTEUS, - supportedProtocols = supportedProtocols, - status = Status.ENABLED - ), - mlsMigrationModel = migrationConfiguration - ))) + .thenReturn(Either.Right(supportedProtocols)) } fun withGetSelfClientsSuccessful(clients: List) = apply { @@ -315,7 +302,7 @@ class UpdateSupportedProtocolsUseCaseTest { fun arrange() = this to UpdateSupportedProtocolsUseCaseImpl( clientRepository, userRepository, - featureConfigRepository + userConfigRepository ) companion object { diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt index e44def97543..e7836d14f16 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/receiver/FeatureConfigEventReceiverTest.kt @@ -24,7 +24,6 @@ import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.event.Event import com.wire.kalium.logic.data.featureConfig.ConferenceCallingModel import com.wire.kalium.logic.data.featureConfig.ConfigsStatusModel -import com.wire.kalium.logic.data.featureConfig.MLSModel import com.wire.kalium.logic.data.featureConfig.SelfDeletingMessagesConfigModel import com.wire.kalium.logic.data.featureConfig.SelfDeletingMessagesModel import com.wire.kalium.logic.data.featureConfig.Status @@ -35,11 +34,12 @@ 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.data.user.SupportedProtocol import com.wire.kalium.logic.feature.selfDeletingMessages.TeamSelfDeleteTimer import com.wire.kalium.logic.feature.selfDeletingMessages.TeamSettingsSelfDeletionStatus +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase import com.wire.kalium.logic.featureFlags.KaliumConfigs import com.wire.kalium.logic.framework.TestUser import com.wire.kalium.logic.functional.Either @@ -59,90 +59,6 @@ import kotlin.time.toDuration class FeatureConfigEventReceiverTest { - @Test - fun givenMLSUpdatedEventSettingMlsAsDefaultProtocol_whenProcessingEvent_ThenSetMLSAsDefaultProtocol() = runTest { - val (arrangement, featureConfigEventReceiver) = Arrangement() - .withSettingMLSEnabledSuccessful() - .withSettingDefaultProtocolSuccessful() - .arrange() - - featureConfigEventReceiver.onEvent( - arrangement.newMLSUpdatedEvent(MLSModel(listOf(TestUser.SELF.id.toPlainID()), SupportedProtocol.MLS, emptySet(), Status.ENABLED)) - ) - - verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setDefaultProtocol) - .with(eq(SupportedProtocol.MLS)) - .wasInvoked(once) - } - - @Test - fun givenMLSUpdatedEventSettingMlsAsDefaultProtocolButStatusIsDisabled_whenProcessingEvent_ThenSetProteusAsDefaultProtocol() = runTest { - val (arrangement, featureConfigEventReceiver) = Arrangement() - .withSettingMLSEnabledSuccessful() - .withSettingDefaultProtocolSuccessful() - .arrange() - - featureConfigEventReceiver.onEvent( - arrangement.newMLSUpdatedEvent(MLSModel(listOf(TestUser.SELF.id.toPlainID()), SupportedProtocol.MLS, emptySet(), Status.DISABLED)) - ) - - verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setDefaultProtocol) - .with(eq(SupportedProtocol.PROTEUS)) - .wasInvoked(once) - } - - @Test - fun givenMLSUpdatedEventGrantingAccessForSelfUser_whenProcessingEvent_ThenSetMLSEnabledToTrue() = runTest { - val (arrangement, featureConfigEventReceiver) = Arrangement() - .withSettingMLSEnabledSuccessful() - .withSettingDefaultProtocolSuccessful() - .arrange() - - featureConfigEventReceiver.onEvent( - arrangement.newMLSUpdatedEvent(MLSModel(listOf(TestUser.SELF.id.toPlainID()), SupportedProtocol.MLS, emptySet(), Status.ENABLED)) - ) - - verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setMLSEnabled) - .with(eq(true)) - .wasInvoked(once) - } - - @Test - fun givenMLSUpdatedEventRemovingAccessForSelfUser_whenProcessingEvent_ThenSetMLSEnabledToFalse() = runTest { - val (arrangement, featureConfigEventReceiver) = Arrangement() - .withSettingMLSEnabledSuccessful() - .withSettingDefaultProtocolSuccessful() - .arrange() - - featureConfigEventReceiver.onEvent(arrangement.newMLSUpdatedEvent(MLSModel(emptyList(), SupportedProtocol.MLS, emptySet(), Status.ENABLED))) - - verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setMLSEnabled) - .with(eq(false)) - .wasInvoked(once) - } - - @Suppress("MaxLineLength") - @Test - fun givenMLSUpdatedEventGrantingAccessForSelfUserButStatusIsDisabled_whenProcessingEvent_ThenSetMLSEnabledToFalse() = runTest { - val (arrangement, featureConfigEventReceiver) = Arrangement() - .withSettingMLSEnabledSuccessful() - .withSettingDefaultProtocolSuccessful() - .arrange() - - featureConfigEventReceiver.onEvent( - arrangement.newMLSUpdatedEvent(MLSModel(listOf(TestUser.SELF.id.toPlainID()), SupportedProtocol.PROTEUS, emptySet(), Status.DISABLED)) - ) - - verify(arrangement.userConfigRepository) - .function(arrangement.userConfigRepository::setMLSEnabled) - .with(eq(false)) - .wasInvoked(once) - } - @Test fun givenFileSharingUpdatedEventWithStatusEnabled_whenProcessingEvent_ThenSetFileSharingStatusToTrue() = runTest { val (arrangement, featureConfigEventReceiver) = Arrangement() @@ -370,11 +286,15 @@ class FeatureConfigEventReceiverTest { @Mock val userConfigRepository = mock(classOf()) + @Mock + val updateSupportedProtocolsAndResolveOneOnOnes = mock(classOf()) + private val featureConfigEventReceiver: FeatureConfigEventReceiver by lazy { FeatureConfigEventReceiverImpl( GuestRoomConfigHandler(userConfigRepository, kaliumConfigs), FileSharingConfigHandler(userConfigRepository), - MLSConfigHandler(userConfigRepository, TestUser.SELF.id), + MLSConfigHandler(userConfigRepository, updateSupportedProtocolsAndResolveOneOnOnes, TestUser.SELF.id), + MLSMigrationConfigHandler(userConfigRepository, updateSupportedProtocolsAndResolveOneOnOnes), ClassifiedDomainsConfigHandler(userConfigRepository), ConferenceCallingConfigHandler(userConfigRepository), SecondFactorPasswordChallengeConfigHandler(userConfigRepository), @@ -384,20 +304,6 @@ class FeatureConfigEventReceiverTest { ) } - fun withSettingDefaultProtocolSuccessful() = apply { - given(userConfigRepository) - .function(userConfigRepository::setDefaultProtocol) - .whenInvokedWith(any()) - .thenReturn(Either.Right(Unit)) - } - - fun withSettingMLSEnabledSuccessful() = apply { - given(userConfigRepository) - .function(userConfigRepository::setMLSEnabled) - .whenInvokedWith(any()) - .thenReturn(Either.Right(Unit)) - } - fun withSettingFileSharingEnabledSuccessful() = apply { given(userConfigRepository) .function(userConfigRepository::setFileSharingStatus) @@ -449,10 +355,6 @@ class FeatureConfigEventReceiverTest { .thenReturn(Either.Right(Unit)) } - fun newMLSUpdatedEvent( - model: MLSModel - ) = Event.FeatureConfig.MLSUpdated("eventId", false, false, model) - fun newFileSharingUpdatedEvent( model: ConfigsStatusModel ) = Event.FeatureConfig.FileSharingUpdated("eventId", false, false, model) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/slow/SlowSyncWorkerTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/slow/SlowSyncWorkerTest.kt index c3f96d13558..cde7df34b0c 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/slow/SlowSyncWorkerTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/sync/slow/SlowSyncWorkerTest.kt @@ -677,7 +677,7 @@ class SlowSyncWorkerTest { fun withResolveOneOnOneConversationsSuccess() = apply { given(oneOnOneResolver) .suspendFunction(oneOnOneResolver::resolveAllOneOnOneConversations) - .whenInvoked() + .whenInvokedWith(any()) .thenReturn(success) } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/mls/OneOnOneResolverArrangement.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/mls/OneOnOneResolverArrangement.kt index 21fdbe02e99..0775258e7f6 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/mls/OneOnOneResolverArrangement.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/mls/OneOnOneResolverArrangement.kt @@ -66,7 +66,7 @@ class OneOnOneResolverArrangementImpl : OneOnOneResolverArrangement { override fun withResolveAllOneOnOneConversationsReturning(result: Either) { given(oneOnOneResolver) .suspendFunction(oneOnOneResolver::resolveAllOneOnOneConversations) - .whenInvoked() + .whenInvokedWith(any()) .thenReturn(result) } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserConfigRepositoryArrangement.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserConfigRepositoryArrangement.kt new file mode 100644 index 00000000000..1ea16666fe4 --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserConfigRepositoryArrangement.kt @@ -0,0 +1,87 @@ +/* + * 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.util.arrangement.repository + +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.featureConfig.MLSMigrationModel +import com.wire.kalium.logic.data.user.SupportedProtocol +import com.wire.kalium.logic.functional.Either +import io.mockative.Mock +import io.mockative.any +import io.mockative.given +import io.mockative.mock + +internal interface UserConfigRepositoryArrangement { + val userConfigRepository: UserConfigRepository + + fun withGetSupportedProtocolsReturning(result: Either>) + fun withSetSupportedProtocolsSuccessful() + fun withSetDefaultProtocolSuccessful() + fun withSetMLSEnabledSuccessful() + fun withSetMigrationConfigurationSuccessful() + fun withGetMigrationConfigurationReturning(result: Either) +} + +internal class UserConfigRepositoryArrangementImpl : UserConfigRepositoryArrangement { + @Mock + override val userConfigRepository: UserConfigRepository = mock(UserConfigRepository::class) + + override fun withGetSupportedProtocolsReturning(result: Either>) { + given(userConfigRepository) + .suspendFunction(userConfigRepository::getSupportedProtocols) + .whenInvoked() + .thenReturn(result) + } + + override fun withSetSupportedProtocolsSuccessful() { + given(userConfigRepository) + .suspendFunction(userConfigRepository::setSupportedProtocols) + .whenInvokedWith(any()) + .thenReturn(Either.Right(Unit)) + } + + override fun withSetDefaultProtocolSuccessful() { + given(userConfigRepository) + .function(userConfigRepository::setDefaultProtocol) + .whenInvokedWith(any()) + .thenReturn(Either.Right(Unit)) + } + + override fun withSetMLSEnabledSuccessful() { + given(userConfigRepository) + .function(userConfigRepository::setMLSEnabled) + .whenInvokedWith(any()) + .thenReturn(Either.Right(Unit)) + } + + override fun withSetMigrationConfigurationSuccessful() { + given(userConfigRepository) + .suspendFunction(userConfigRepository::setMigrationConfiguration) + .whenInvokedWith(any()) + .thenReturn(Either.Right(Unit)) + } + + override fun withGetMigrationConfigurationReturning(result: Either) { + given(userConfigRepository) + .suspendFunction(userConfigRepository::getMigrationConfiguration) + .whenInvoked() + .thenReturn(result) + } +} diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/usecase/UpdateSupportedProtocolsAndResolveOneOnOnesArrangement.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/usecase/UpdateSupportedProtocolsAndResolveOneOnOnesArrangement.kt new file mode 100644 index 00000000000..4de524462de --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/usecase/UpdateSupportedProtocolsAndResolveOneOnOnesArrangement.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.util.arrangement.usecase + +import com.wire.kalium.logic.feature.user.UpdateSupportedProtocolsAndResolveOneOnOnesUseCase +import com.wire.kalium.logic.functional.Either +import io.mockative.Mock +import io.mockative.any +import io.mockative.given +import io.mockative.mock + +internal interface UpdateSupportedProtocolsAndResolveOneOnOnesArrangement { + val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase + + fun withUpdateSupportedProtocolsAndResolveOneOnOnesSuccessful() +} + +internal class UpdateSupportedProtocolsAndResolveOneOnOnesArrangementImpl + : UpdateSupportedProtocolsAndResolveOneOnOnesArrangement { + @Mock + override val updateSupportedProtocolsAndResolveOneOnOnes: UpdateSupportedProtocolsAndResolveOneOnOnesUseCase = + mock(UpdateSupportedProtocolsAndResolveOneOnOnesUseCase::class) + + override fun withUpdateSupportedProtocolsAndResolveOneOnOnesSuccessful() { + given(updateSupportedProtocolsAndResolveOneOnOnes) + .suspendFunction(updateSupportedProtocolsAndResolveOneOnOnes::invoke) + .whenInvokedWith(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 ea4503d460a..9d80f1d1b1e 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 @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlin.time.Duration @@ -224,6 +225,13 @@ sealed class SelfDeletionTimerEntity { data class Enforced(val enforcedDuration: Duration) : SelfDeletionTimerEntity() } +@Serializable +data class MLSMigrationEntity( + @Serializable val status: Boolean, + @Serializable val startTime: Instant?, + @Serializable val endTime: Instant?, +) + @Suppress("TooManyFunctions") class UserConfigStorageImpl( private val kaliumPreferences: KaliumPreferences diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAO.kt index 0c7a095cde9..51c6c190633 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAO.kt @@ -48,8 +48,10 @@ data class QualifiedIDEntity( typealias UserIDEntity = QualifiedIDEntity typealias ConversationIDEntity = QualifiedIDEntity +@Serializable enum class SupportedProtocolEntity { - PROTEUS, MLS + @SerialName("PROTEUS") PROTEUS, + @SerialName("MLS") MLS } enum class UserAvailabilityStatusEntity { 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 f6255dc67d2..e39e7149c83 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,9 +17,12 @@ */ package com.wire.kalium.persistence.dao.unread +import com.wire.kalium.persistence.config.MLSMigrationEntity import com.wire.kalium.persistence.config.TeamSettingsSelfDeletionStatusEntity import com.wire.kalium.persistence.dao.MetadataDAO +import com.wire.kalium.persistence.dao.SupportedProtocolEntity import kotlinx.coroutines.flow.Flow +import kotlinx.serialization.builtins.SetSerializer interface UserConfigDAO { @@ -30,6 +33,12 @@ interface UserConfigDAO { suspend fun markTeamSettingsSelfDeletingMessagesStatusAsNotified() suspend fun observeTeamSettingsSelfDeletingStatus(): Flow + + suspend fun getMigrationConfiguration(): MLSMigrationEntity? + suspend fun setMigrationConfiguration(configuration: MLSMigrationEntity) + + suspend fun getSupportedProtocols(): Set? + suspend fun setSupportedProtocols(protocols: Set) } internal class UserConfigDAOImpl internal constructor( @@ -37,23 +46,23 @@ internal class UserConfigDAOImpl internal constructor( ) : UserConfigDAO { override suspend fun getTeamSettingsSelfDeletionStatus(): TeamSettingsSelfDeletionStatusEntity? = - metadataDAO.getSerializable(SELF_DELETING_MESSAGES, TeamSettingsSelfDeletionStatusEntity.serializer()) + metadataDAO.getSerializable(SELF_DELETING_MESSAGES_KEY, TeamSettingsSelfDeletionStatusEntity.serializer()) override suspend fun setTeamSettingsSelfDeletionStatus( teamSettingsSelfDeletionStatusEntity: TeamSettingsSelfDeletionStatusEntity ) { metadataDAO.putSerializable( - key = SELF_DELETING_MESSAGES, + key = SELF_DELETING_MESSAGES_KEY, value = teamSettingsSelfDeletionStatusEntity, TeamSettingsSelfDeletionStatusEntity.serializer() ) } override suspend fun markTeamSettingsSelfDeletingMessagesStatusAsNotified() { - metadataDAO.getSerializable(SELF_DELETING_MESSAGES, TeamSettingsSelfDeletionStatusEntity.serializer()) + metadataDAO.getSerializable(SELF_DELETING_MESSAGES_KEY, TeamSettingsSelfDeletionStatusEntity.serializer()) ?.copy(isStatusChanged = false)?.let { newValue -> metadataDAO.putSerializable( - SELF_DELETING_MESSAGES, + SELF_DELETING_MESSAGES_KEY, newValue, TeamSettingsSelfDeletionStatusEntity.serializer() ) @@ -61,9 +70,23 @@ internal class UserConfigDAOImpl internal constructor( } override suspend fun observeTeamSettingsSelfDeletingStatus(): Flow = - metadataDAO.observeSerializable(SELF_DELETING_MESSAGES, TeamSettingsSelfDeletionStatusEntity.serializer()) + metadataDAO.observeSerializable(SELF_DELETING_MESSAGES_KEY, TeamSettingsSelfDeletionStatusEntity.serializer()) + + override suspend fun getMigrationConfiguration(): MLSMigrationEntity? = + metadataDAO.getSerializable(MLS_MIGRATION_KEY, MLSMigrationEntity.serializer()) + + override suspend fun setMigrationConfiguration(configuration: MLSMigrationEntity) = + metadataDAO.putSerializable(MLS_MIGRATION_KEY, configuration, MLSMigrationEntity.serializer()) + + override suspend fun getSupportedProtocols(): Set? = + metadataDAO.getSerializable(SUPPORTED_PROTOCOLS_KEY, SetSerializer(SupportedProtocolEntity.serializer())) + + override suspend fun setSupportedProtocols(protocols: Set) = + metadataDAO.putSerializable(SUPPORTED_PROTOCOLS_KEY, protocols, SetSerializer(SupportedProtocolEntity.serializer())) private companion object { - private const val SELF_DELETING_MESSAGES = "SELF_DELETING_MESSAGES" + 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" } }