diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt index 9bbf027020b..1b1cfc93911 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/ConversationRepository.kt @@ -25,6 +25,7 @@ import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.client.MLSClientProvider import com.wire.kalium.logic.data.conversation.Conversation.ProtocolInfo.MLSCapable.GroupState import com.wire.kalium.logic.data.conversation.mls.EpochChangesData +import com.wire.kalium.logic.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.GroupID import com.wire.kalium.logic.data.id.IdMapper @@ -57,8 +58,6 @@ import com.wire.kalium.logic.kaliumLogger import com.wire.kalium.logic.wrapApiRequest import com.wire.kalium.logic.wrapMLSRequest import com.wire.kalium.logic.wrapStorageRequest -import com.wire.kalium.network.api.base.authenticated.client.ClientApi -import com.wire.kalium.network.api.base.authenticated.conversation.ConversationApi import com.wire.kalium.network.api.authenticated.conversation.ConversationMemberDTO import com.wire.kalium.network.api.authenticated.conversation.ConversationRenameResponse import com.wire.kalium.network.api.authenticated.conversation.ConversationResponse @@ -67,6 +66,8 @@ import com.wire.kalium.network.api.authenticated.conversation.UpdateConversation import com.wire.kalium.network.api.authenticated.conversation.UpdateConversationReceiptModeResponse import com.wire.kalium.network.api.authenticated.conversation.model.ConversationMemberRoleDTO import com.wire.kalium.network.api.authenticated.conversation.model.ConversationReceiptModeDTO +import com.wire.kalium.network.api.base.authenticated.client.ClientApi +import com.wire.kalium.network.api.base.authenticated.conversation.ConversationApi import com.wire.kalium.persistence.dao.QualifiedIDEntity import com.wire.kalium.persistence.dao.client.ClientDAO import com.wire.kalium.persistence.dao.conversation.ConversationDAO @@ -303,6 +304,7 @@ interface ConversationRepository { suspend fun observeLegalHoldStatusChangeNotified(conversationId: ConversationId): Flow> suspend fun getGroupStatusMembersNamesAndHandles(groupID: GroupID): Either + suspend fun selectMembersNameAndHandle(conversationId: ConversationId): Either> } @Suppress("LongParameterList", "TooManyFunctions", "LargeClass") @@ -1102,6 +1104,13 @@ internal class ConversationDataSource internal constructor( conversationDAO.selectGroupStatusMembersNamesAndHandles(groupID.value) }.map { EpochChangesData.fromEntity(it) } + override suspend fun selectMembersNameAndHandle(conversationId: ConversationId): Either> = + wrapStorageRequest { + memberDAO.selectMembersNameAndHandle(conversationId.toDao()) + .mapValues { NameAndHandle.fromEntity(it.value) } + .mapKeys { it.key.toModel() } + } + companion object { const val DEFAULT_MEMBER_ROLE = "wire_member" } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt index 03944f0a9b9..9609079b094 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/user/UserRepository.kt @@ -25,6 +25,7 @@ import com.wire.kalium.logic.NetworkFailure import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.conversation.MemberMapper import com.wire.kalium.logic.data.conversation.Recipient +import com.wire.kalium.logic.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.event.Event import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.IdMapper @@ -162,6 +163,7 @@ interface UserRepository { suspend fun getOneOnOnConversationId(userId: QualifiedID): Either suspend fun getUsersMinimizedByQualifiedIDs(userIds: List): Either> + suspend fun getNameAndHandle(userId: UserId): Either } @Suppress("LongParameterList", "TooManyFunctions") @@ -641,6 +643,10 @@ internal class UserDataSource internal constructor( userDAO.getOneOnOnConversationId(userId.toDao())?.toModel() } + override suspend fun getNameAndHandle(userId: UserId): Either = wrapStorageRequest { + userDAO.getNameAndHandle(userId.toDao()) + }.map { NameAndHandle.fromEntity(it) } + companion object { internal const val SELF_USER_ID_KEY = "selfUserID" 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 68a353dc754..175dfdb3576 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 @@ -1842,6 +1842,7 @@ class UserSessionScope internal constructor( clientIdProvider, e2eiRepository, mlsConversationRepository, + conversationRepository, team.isSelfATeamMember, updateSupportedProtocols, clientRepository, diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCase.kt index 7250f73cb5f..85d8c5254e1 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCase.kt @@ -114,13 +114,13 @@ internal class FetchMLSVerificationStatusUseCaseImpl( // check that all identities are valid and name and handle are matching for ((userId, wireIdentity) in ccIdentity) { val persistedMemberInfo = dbData.members[userId] - val isUserVerified = wireIdentity.firstOrNull { + val isUserVerified = wireIdentity.none { it.status != CryptoCertificateStatus.VALID || it.credentialType != CredentialType.X509 || it.x509Identity == null || it.x509Identity?.displayName != persistedMemberInfo?.name || it.x509Identity?.handle?.handle != persistedMemberInfo?.handle - } == null + } if (!isUserVerified) { newStatus = VerificationStatus.NOT_VERIFIED break @@ -141,7 +141,7 @@ internal class FetchMLSVerificationStatusUseCaseImpl( var dbData = epochChangesData val missingUsers = missingUsers( - usersFromDB = epochChangesData.members.keys.map { it }.toSet(), + usersFromDB = epochChangesData.members.keys, usersFromCC = ccIdentities.keys ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetMembersE2EICertificateStatusesUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetMembersE2EICertificateStatusesUseCase.kt index 034d802a9bc..2ce15cbbefc 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetMembersE2EICertificateStatusesUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetMembersE2EICertificateStatusesUseCase.kt @@ -20,11 +20,14 @@ package com.wire.kalium.logic.feature.e2ei.usecase import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.WireIdentity +import com.wire.kalium.logic.data.conversation.ConversationRepository import com.wire.kalium.logic.data.conversation.MLSConversationRepository +import com.wire.kalium.logic.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.functional.getOrElse +import com.wire.kalium.logic.functional.map /** * This use case is used to get the e2ei certificates of all the users in Conversation. @@ -35,23 +38,27 @@ interface GetMembersE2EICertificateStatusesUseCase { } class GetMembersE2EICertificateStatusesUseCaseImpl internal constructor( - private val mlsConversationRepository: MLSConversationRepository + private val mlsConversationRepository: MLSConversationRepository, + private val conversationRepository: ConversationRepository ) : GetMembersE2EICertificateStatusesUseCase { override suspend operator fun invoke(conversationId: ConversationId, userIds: List): Map = - mlsConversationRepository.getMembersIdentities(conversationId, userIds).fold( - { mapOf() }, - { - it.mapValues { (_, identities) -> - // todo: we need to check the user name and details! - identities.isUserMLSVerified() + mlsConversationRepository.getMembersIdentities(conversationId, userIds) + .map { identities -> + val usersNameAndHandle = conversationRepository.selectMembersNameAndHandle(conversationId).getOrElse(mapOf()) + + identities.mapValues { (userId, identities) -> + identities.isUserMLSVerified(usersNameAndHandle[userId]) } - } - ) + }.getOrElse(mapOf()) } /** * @return if given user is verified or not */ -fun List.isUserMLSVerified() = this.isNotEmpty() && this.all { - it.x509Identity != null && it.credentialType == CredentialType.X509 && it.status == CryptoCertificateStatus.VALID +fun List.isUserMLSVerified(nameAndHandle: NameAndHandle?) = this.isNotEmpty() && this.all { + it.x509Identity != null + && it.credentialType == CredentialType.X509 + && it.status == CryptoCertificateStatus.VALID + && it.x509Identity?.handle?.handle == nameAndHandle?.handle + && it.x509Identity?.displayName == nameAndHandle?.name } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EICertificateUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EICertificateUseCase.kt index 7b4d0313321..a63e7705fe9 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EICertificateUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EICertificateUseCase.kt @@ -19,8 +19,10 @@ package com.wire.kalium.logic.feature.e2ei.usecase import com.wire.kalium.logic.data.conversation.MLSConversationRepository import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.data.user.UserRepository import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.functional.getOrNull /** * This use case is used to get the e2ei certificate status of specific user @@ -31,11 +33,14 @@ interface IsOtherUserE2EIVerifiedUseCase { class IsOtherUserE2EIVerifiedUseCaseImpl internal constructor( private val mlsConversationRepository: MLSConversationRepository, - private val isE2EIEnabledUseCase: IsE2EIEnabledUseCase + private val isE2EIEnabledUseCase: IsE2EIEnabledUseCase, + private val userRepository: UserRepository ) : IsOtherUserE2EIVerifiedUseCase { override suspend operator fun invoke(userId: UserId): Boolean = - isE2EIEnabledUseCase() && mlsConversationRepository.getUserIdentity(userId).fold( - { false }, - { it.isUserMLSVerified() } - ) + if (isE2EIEnabledUseCase()) { + val nameHandle = userRepository.getNameAndHandle(userId).getOrNull() + mlsConversationRepository.getUserIdentity(userId).fold({ false }, { it.isUserMLSVerified(nameHandle) }) + } else { + false + } } 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 b81afca7eb1..5ee8bbeaabd 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 @@ -24,6 +24,7 @@ import com.wire.kalium.logic.configuration.UserConfigRepository 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.conversation.ConversationRepository import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationsUseCase import com.wire.kalium.logic.data.conversation.MLSConversationRepository import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository @@ -100,6 +101,7 @@ class UserScope internal constructor( private val clientIdProvider: CurrentClientIdProvider, private val e2EIRepository: E2EIRepository, private val mlsConversationRepository: MLSConversationRepository, + private val conversationRepository: ConversationRepository, private val isSelfATeamMember: IsSelfATeamMemberUseCase, private val updateSelfUserSupportedProtocolsUseCase: UpdateSelfUserSupportedProtocolsUseCase, private val clientRepository: ClientRepository, @@ -133,7 +135,8 @@ class UserScope internal constructor( val getUserE2eiCertificateStatus: IsOtherUserE2EIVerifiedUseCase get() = IsOtherUserE2EIVerifiedUseCaseImpl( mlsConversationRepository = mlsConversationRepository, - isE2EIEnabledUseCase = isE2EIEnabledUseCase + isE2EIEnabledUseCase = isE2EIEnabledUseCase, + userRepository = userRepository ) val getUserE2eiCertificates: GetUserE2eiCertificatesUseCase get() = GetUserE2eiCertificatesUseCaseImpl( @@ -142,7 +145,8 @@ class UserScope internal constructor( ) val getMembersE2EICertificateStatuses: GetMembersE2EICertificateStatusesUseCase get() = GetMembersE2EICertificateStatusesUseCaseImpl( - mlsConversationRepository = mlsConversationRepository + mlsConversationRepository = mlsConversationRepository, + conversationRepository = conversationRepository ) val deleteAsset: DeleteAssetUseCase get() = DeleteAssetUseCaseImpl(assetRepository) val setUserHandle: SetUserHandleUseCase get() = SetUserHandleUseCase(accountRepository, validateUserHandleUseCase, syncManager) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetMembersE2EICertificateStatusesUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetMembersE2EICertificateStatusesUseCaseTest.kt index aa76385da59..49682c539f2 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetMembersE2EICertificateStatusesUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetMembersE2EICertificateStatusesUseCaseTest.kt @@ -22,6 +22,7 @@ import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.WireIdentity import com.wire.kalium.logic.MLSFailure +import com.wire.kalium.logic.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.toCrypto import com.wire.kalium.logic.data.user.UserId @@ -29,7 +30,8 @@ import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatu import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArrangement import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArrangementImpl -import io.mockative.matchers.EqualsMatcher +import com.wire.kalium.logic.util.arrangement.repository.ConversationRepositoryArrangement +import com.wire.kalium.logic.util.arrangement.repository.ConversationRepositoryArrangementImpl import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant @@ -54,6 +56,7 @@ class GetMembersE2EICertificateStatusesUseCaseTest { fun givenEmptyWireIdentityMap_whenRequestMembersStatuses_thenNotActivatedResult() = runTest { val (_, getMembersE2EICertificateStatuses) = arrange { withMembersIdentities(Either.Right(mapOf())) + withMembersNameAndHandle(Either.Right(mapOf())) } val result = getMembersE2EICertificateStatuses(CONVERSATION_ID, listOf()) @@ -65,6 +68,7 @@ class GetMembersE2EICertificateStatusesUseCaseTest { fun givenOneWireIdentityExpiredForSomeUser_whenRequestMembersStatuses_thenResultUsersStatusIsExpired() = runTest { val (_, getMembersE2EICertificateStatuses) = arrange { + withMembersNameAndHandle(Either.Right(mapOf(USER_ID to NAME_AND_HANDLE))) withMembersIdentities( Either.Right( mapOf( @@ -87,6 +91,7 @@ class GetMembersE2EICertificateStatusesUseCaseTest { runTest { val userId2 = USER_ID.copy(value = "value_2") val (_, getMembersE2EICertificateStatuses) = arrange { + withMembersNameAndHandle(Either.Right(mapOf(userId2 to NAME_AND_HANDLE))) withMembersIdentities( Either.Right( mapOf( @@ -108,12 +113,14 @@ class GetMembersE2EICertificateStatusesUseCaseTest { } private class Arrangement(private val block: suspend Arrangement.() -> Unit) : - MLSConversationRepositoryArrangement by MLSConversationRepositoryArrangementImpl() { + MLSConversationRepositoryArrangement by MLSConversationRepositoryArrangementImpl(), + ConversationRepositoryArrangement by ConversationRepositoryArrangementImpl() { fun arrange() = run { runBlocking { block() } this@Arrangement to GetMembersE2EICertificateStatusesUseCaseImpl( - mlsConversationRepository = mlsConversationRepository + mlsConversationRepository = mlsConversationRepository, + conversationRepository = conversationRepository ) } } @@ -145,5 +152,7 @@ class GetMembersE2EICertificateStatusesUseCaseTest { notAfter = Instant.DISTANT_FUTURE.epochSeconds ) ) + + private val NAME_AND_HANDLE = NameAndHandle(name = "user displayName", handle = "userHandle") } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiCertificateStatusUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiCertificateStatusUseCaseTest.kt index 451d60e8224..2a0dc5dab3f 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiCertificateStatusUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiCertificateStatusUseCaseTest.kt @@ -22,6 +22,8 @@ import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.WireIdentity import com.wire.kalium.logic.MLSFailure +import com.wire.kalium.logic.StorageFailure +import com.wire.kalium.logic.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.id.toCrypto import com.wire.kalium.logic.data.user.UserId import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCaseImpl @@ -30,13 +32,14 @@ import com.wire.kalium.logic.util.arrangement.mls.IsE2EIEnabledUseCaseArrangemen import com.wire.kalium.logic.util.arrangement.mls.IsE2EIEnabledUseCaseArrangementImpl import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArrangement import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArrangementImpl +import com.wire.kalium.logic.util.arrangement.repository.UserRepositoryArrangement +import com.wire.kalium.logic.util.arrangement.repository.UserRepositoryArrangementImpl import io.mockative.any import io.mockative.coVerify import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import kotlin.test.Test -import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -48,6 +51,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val (_, getUserE2eiCertificateStatus) = arrange { withE2EIEnabledAndMLSEnabled(true) withUserIdentity(Either.Left(MLSFailure.WrongEpoch)) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "userHandle"))) } val result = getUserE2eiCertificateStatus(USER_ID) @@ -61,6 +65,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val (_, getUserE2eiCertificateStatus) = arrange { withE2EIEnabledAndMLSEnabled(true) withUserIdentity(Either.Right(listOf())) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "userHandle"))) } val result = getUserE2eiCertificateStatus(USER_ID) @@ -73,6 +78,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { runTest { val (_, getUserE2eiCertificateStatus) = arrange { withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "userHandle"))) withUserIdentity( Either.Right( listOf( @@ -93,6 +99,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { runTest { val (_, getUserE2eiCertificateStatus) = arrange { withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "userHandle"))) withUserIdentity( Either.Right( listOf( @@ -113,6 +120,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { runTest { val (_, getUserE2eiCertificateStatus) = arrange { withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "userHandle"))) withUserIdentity( Either.Right( listOf( @@ -147,15 +155,73 @@ class GetUserE2eiCertificateStatusUseCaseTest { }.wasNotInvoked() } + @Test + fun givenOneWireIdentityIsOk_whenUserNameDiffFromIdentityName_thenResultIsExpired() = + runTest { + val (_, getUserE2eiCertificateStatus) = arrange { + withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Right(NameAndHandle("another user displayName", "userHandle"))) + withUserIdentity(Either.Right(listOf(WIRE_IDENTITY))) + } + + val result = getUserE2eiCertificateStatus(USER_ID) + + assertFalse(result) + } + + @Test + fun givenOneWireIdentityIsOk_whenUserHandleDiffFromIdentityName_thenResultIsExpired() = + runTest { + val (_, getUserE2eiCertificateStatus) = arrange { + withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "anotherUserHandle"))) + withUserIdentity(Either.Right(listOf(WIRE_IDENTITY))) + } + + val result = getUserE2eiCertificateStatus(USER_ID) + + assertFalse(result) + } + + @Test + fun givenOneWireIdentityIsOk_whenUserNameAndHandleAbsent_thenResultIsExpired() = + runTest { + val (_, getUserE2eiCertificateStatus) = arrange { + withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Left(StorageFailure.DataNotFound)) + withUserIdentity(Either.Right(listOf(WIRE_IDENTITY))) + } + + val result = getUserE2eiCertificateStatus(USER_ID) + + assertFalse(result) + } + + @Test + fun givenOneWireIdentityIsOk_whenUserNameAndHandleSameAsInIdentity_thenResultIsTrue() = + runTest { + val (_, getUserE2eiCertificateStatus) = arrange { + withE2EIEnabledAndMLSEnabled(true) + withNameAndHandle(Either.Right(NameAndHandle("user displayName", "userHandle"))) + withUserIdentity(Either.Right(listOf(WIRE_IDENTITY))) + } + + val result = getUserE2eiCertificateStatus(USER_ID) + + assertTrue(result) + } + private class Arrangement(private val block: suspend Arrangement.() -> Unit) : MLSConversationRepositoryArrangement by MLSConversationRepositoryArrangementImpl(), - IsE2EIEnabledUseCaseArrangement by IsE2EIEnabledUseCaseArrangementImpl() { + IsE2EIEnabledUseCaseArrangement by IsE2EIEnabledUseCaseArrangementImpl(), + UserRepositoryArrangement by UserRepositoryArrangementImpl() { fun arrange() = run { runBlocking { block() } this@Arrangement to IsOtherUserE2EIVerifiedUseCaseImpl( mlsConversationRepository = mlsConversationRepository, - isE2EIEnabledUseCase = isE2EIEnabledUseCase + isE2EIEnabledUseCase = isE2EIEnabledUseCase, + userRepository = userRepository ) } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/ConversationRepositoryArrangement.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/ConversationRepositoryArrangement.kt index ece6d0c10c9..1a223b651d9 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/ConversationRepositoryArrangement.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/ConversationRepositoryArrangement.kt @@ -23,6 +23,7 @@ import com.wire.kalium.logic.data.conversation.Conversation import com.wire.kalium.logic.data.conversation.ConversationDetails import com.wire.kalium.logic.data.conversation.ConversationRepository import com.wire.kalium.logic.data.conversation.mls.EpochChangesData +import com.wire.kalium.logic.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.user.UserId @@ -109,6 +110,7 @@ internal interface ConversationRepositoryArrangement { suspend fun withSelectGroupStatusMembersNamesAndHandles(result: Either) suspend fun withConversationDetailsByIdReturning(result: Either) suspend fun withPersistMembers(result: Either) + suspend fun withMembersNameAndHandle(result: Either>) } internal open class ConversationRepositoryArrangementImpl : ConversationRepositoryArrangement { @@ -266,4 +268,8 @@ internal open class ConversationRepositoryArrangementImpl : ConversationReposito override suspend fun withPersistMembers(result: Either) { coEvery { conversationRepository.persistMembers(any(), any()) }.returns(result) } + + override suspend fun withMembersNameAndHandle(result: Either>) { + coEvery { conversationRepository.selectMembersNameAndHandle(any()) }.returns(result) + } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserRepositoryArrangement.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserRepositoryArrangement.kt index e0bbb8c0511..25fecc6da7c 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserRepositoryArrangement.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/util/arrangement/repository/UserRepositoryArrangement.kt @@ -19,6 +19,7 @@ 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.data.conversation.mls.NameAndHandle import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.user.OtherUser @@ -31,7 +32,6 @@ import com.wire.kalium.logic.functional.Either import io.mockative.Mock import io.mockative.any import io.mockative.coEvery -import io.mockative.eq import io.mockative.fake.valueOf import io.mockative.matchers.AnyMatcher import io.mockative.matchers.Matcher @@ -93,6 +93,8 @@ internal interface UserRepositoryArrangement { userId: Matcher = AnyMatcher(valueOf()), conversationId: Matcher = AnyMatcher(valueOf()) ) + + suspend fun withNameAndHandle(result: Either, userId: Matcher = AnyMatcher(valueOf())) } @Suppress("INAPPLICABLE_JVM_NAME") @@ -227,4 +229,8 @@ internal open class UserRepositoryArrangementImpl : UserRepositoryArrangement { } .returns(result) } + + override suspend fun withNameAndHandle(result: Either, userId: Matcher) { + coEvery { userRepository.getNameAndHandle(matches { userId.matches(it) }) }.returns(result) + } } diff --git a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Users.sq b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Users.sq index cf6e7b3aba5..b5db7a32d9d 100644 --- a/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Users.sq +++ b/persistence/src/commonMain/db_user/com/wire/kalium/persistence/Users.sq @@ -270,3 +270,6 @@ WHERE m.user = :userId selectOneOnOnConversationId: SELECT active_one_on_one_conversation_id FROM User WHERE qualified_id = :userId; + +selectNamesAndHandle: +SELECT name, handle FROM User WHERE qualified_id = :userId; 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 2fa8258cce8..c89b8a9c47e 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 @@ -21,6 +21,7 @@ package com.wire.kalium.persistence.dao import com.wire.kalium.logger.obfuscateDomain import com.wire.kalium.logger.obfuscateId import com.wire.kalium.persistence.dao.ManagedByEntity.WIRE +import com.wire.kalium.persistence.dao.conversation.NameAndHandleEntity import kotlinx.coroutines.flow.Flow import kotlinx.datetime.Instant import kotlinx.serialization.SerialName @@ -313,4 +314,5 @@ interface UserDAO { suspend fun isAtLeastOneUserATeamMember(userId: List, teamId: String): Boolean suspend fun getOneOnOnConversationId(userId: UserIDEntity): QualifiedIDEntity? suspend fun getUsersMinimizedByQualifiedIDs(qualifiedIDs: List): List + suspend fun getNameAndHandle(userId: UserIDEntity): NameAndHandleEntity? } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAOImpl.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAOImpl.kt index 74f9c332f46..b79fe3c67dd 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAOImpl.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/UserDAOImpl.kt @@ -21,6 +21,7 @@ package com.wire.kalium.persistence.dao import app.cash.sqldelight.coroutines.asFlow import com.wire.kalium.persistence.UsersQueries import com.wire.kalium.persistence.cache.FlowCache +import com.wire.kalium.persistence.dao.conversation.NameAndHandleEntity import com.wire.kalium.persistence.util.mapToList import com.wire.kalium.persistence.util.mapToOneOrNull import kotlinx.coroutines.flow.Flow @@ -466,4 +467,8 @@ class UserDAOImpl internal constructor( override suspend fun getOneOnOnConversationId(userId: UserIDEntity): QualifiedIDEntity? = withContext(queriesContext) { userQueries.selectOneOnOnConversationId(userId).executeAsOneOrNull()?.active_one_on_one_conversation_id } + + override suspend fun getNameAndHandle(userId: UserIDEntity): NameAndHandleEntity? = withContext(queriesContext) { + userQueries.selectNamesAndHandle(userId, ::NameAndHandleEntity).executeAsOneOrNull() + } } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/member/MemberDAO.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/member/MemberDAO.kt index 47c34917679..fa1268acb40 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/member/MemberDAO.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/dao/member/MemberDAO.kt @@ -26,6 +26,7 @@ import com.wire.kalium.persistence.dao.ConversationIDEntity import com.wire.kalium.persistence.dao.QualifiedIDEntity import com.wire.kalium.persistence.dao.UserIDEntity import com.wire.kalium.persistence.dao.conversation.ConversationEntity +import com.wire.kalium.persistence.dao.conversation.NameAndHandleEntity import com.wire.kalium.persistence.kaliumLogger import com.wire.kalium.persistence.util.mapToList import com.wire.kalium.persistence.util.mapToOneOrNull @@ -65,6 +66,7 @@ interface MemberDAO { ): Map> suspend fun getOneOneConversationWithFederatedMembers(domain: String): Map + suspend fun selectMembersNameAndHandle(conversationId: QualifiedIDEntity): Map } @Suppress("TooManyFunctions") @@ -221,4 +223,9 @@ internal class MemberDAOImpl internal constructor( .executeAsList() .associateBy({ it.conversation }, { it.user }) } + + override suspend fun selectMembersNameAndHandle(conversationId: QualifiedIDEntity) = withContext(coroutineContext) { + memberQueries.selectMembersNamesAndHandle(conversationId).executeAsList() + .let { members -> members.associate { it.user to NameAndHandleEntity(it.name, it.handle) } } + } }