From 3acfed05ca26bc52841ad9424692ebf225afd052 Mon Sep 17 00:00:00 2001 From: Mojtaba Chenani Date: Thu, 18 Jul 2024 09:21:27 +0200 Subject: [PATCH] chore(mls): unify MLSClientIdentity models (WPB-9774) (#2818) * chore: refactor identity models * fix tests * user correct clientId and Handle in MLSClientIdentity object * clean mapping object checker * fix formatting and remove one line un-used code --------- Co-authored-by: Vitor Hugo Schwaab (cherry picked from commit 8f000c04316f32576e5a95813053c265e1143fa2) --- .../MLSClientImpl.kt | 27 +++-- .../com/wire/kalium/cryptography/IDs.kt | 47 ++------ .../com/wire/kalium/logic/data/id/PlainId.kt | 5 +- .../wire/kalium/logic/data/id/QualifiedId.kt | 5 +- .../DecryptedMessageBundleMapper.kt | 14 +-- .../conversation/MLSConversationRepository.kt | 13 +-- .../wire/kalium/logic/di/MapperProvider.kt | 3 - .../logic/feature/e2ei/CertificateStatus.kt | 5 + .../feature/e2ei/CertificateStatusMapper.kt | 34 ------ .../logic/feature/e2ei/E2eiCertificate.kt | 97 ++++++++++++----- .../FetchMLSVerificationStatusUseCase.kt | 8 +- .../e2ei/usecase/GetE2EICertificateUseCase.kt | 36 +++---- ...etMembersE2EICertificateStatusesUseCase.kt | 30 ++---- .../GetUserE2EIAllCertificatesUseCase.kt | 16 ++- .../usecase/GetUserE2EICertificateUseCase.kt | 36 ++----- ...rtificateRevocationForSelfClientUseCase.kt | 8 +- .../user/ObserveE2EIRequiredUseCase.kt | 58 +++++----- .../kalium/logic/feature/user/UserScope.kt | 18 ++-- .../MLSConversationRepositoryTest.kt | 33 +++--- .../client/ObserveE2EIRequiredUseCaseTest.kt | 62 +++++------ .../e2ei/GetE2eiCertificateUseCaseTest.kt | 82 +++++++++----- ...mbersE2EICertificateStatusesUseCaseTest.kt | 37 ++++--- ...erE2eiAllCertificateStatusesUseCaseTest.kt | 36 ++++--- ...GetUserE2eiCertificateStatusUseCaseTest.kt | 65 ++++++----- .../FetchMLSVerificationStatusUseCaseTest.kt | 101 +++++++----------- .../logic/framework/TestMLSClientIdentity.kt | 60 +++++++++++ 26 files changed, 477 insertions(+), 459 deletions(-) delete mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatusMapper.kt create mode 100644 logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestMLSClientIdentity.kt diff --git a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt index 63ece6b5ee9..1eb86ec162c 100644 --- a/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt +++ b/cryptography/src/commonJvmAndroid/kotlin/com.wire.kalium.cryptography/MLSClientImpl.kt @@ -19,6 +19,7 @@ package com.wire.kalium.cryptography import com.wire.crypto.BufferedDecryptedMessage +import com.wire.crypto.Ciphersuite import com.wire.crypto.ConversationConfiguration import com.wire.crypto.CoreCrypto import com.wire.crypto.CustomConfiguration @@ -28,7 +29,6 @@ import com.wire.crypto.MlsCredentialType import com.wire.crypto.MlsGroupInfoEncryptionType import com.wire.crypto.MlsRatchetTreeType import com.wire.crypto.MlsWirePolicy -import com.wire.crypto.Ciphersuite import io.ktor.util.decodeBase64Bytes import io.ktor.util.encodeBase64 import kotlin.time.Duration @@ -332,18 +332,26 @@ class MLSClientImpl( return clientId?.let { WireIdentity( CryptoQualifiedClientId.fromEncodedString(value.clientId)!!, - value.x509Identity?.handle, - value.x509Identity?.displayName, - value.x509Identity?.domain, - value.x509Identity?.certificate, toDeviceStatus(value.status), value.thumbprint, - value.x509Identity?.serialNumber, - value.x509Identity?.notAfter?.toLong() + toCredentialType(value.credentialType), + value.x509Identity?.let { + toX509Identity(it) + } ) } } + fun toX509Identity(value: com.wire.crypto.X509Identity) = WireIdentity.X509Identity( + handle = WireIdentity.Handle.fromString(value.handle, value.domain), + displayName = value.displayName, + domain = value.domain, + certificate = value.certificate, + serialNumber = value.serialNumber, + notBefore = value.notBefore.toLong(), + notAfter = value.notAfter.toLong() + ) + fun toDeviceStatus(value: com.wire.crypto.DeviceStatus) = when (value) { com.wire.crypto.DeviceStatus.VALID -> CryptoCertificateStatus.VALID com.wire.crypto.DeviceStatus.EXPIRED -> CryptoCertificateStatus.EXPIRED @@ -403,6 +411,11 @@ class MLSClientImpl( CredentialType.X509 -> MlsCredentialType.X509 } + fun toCredentialType(value: MlsCredentialType) = when (value) { + MlsCredentialType.BASIC -> CredentialType.Basic + MlsCredentialType.X509 -> CredentialType.X509 + } + fun toCrlRegistration(value: com.wire.crypto.CrlRegistration) = CrlRegistration( value.dirty, value.expiration diff --git a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt index 4754bec9e84..1f45fde52ff 100644 --- a/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt +++ b/cryptography/src/commonMain/kotlin/com/wire/kalium/cryptography/IDs.kt @@ -78,54 +78,19 @@ data class CryptoQualifiedClientId( data class WireIdentity( val clientId: CryptoQualifiedClientId, - val certificate: Certificate?, val status: CryptoCertificateStatus, + val thumbprint: String, + val credentialType: CredentialType, + val x509Identity: X509Identity? ) { - companion object { - @Suppress("LongParameterList") - operator fun invoke( - clientId: CryptoQualifiedClientId, - handle: String?, - displayName: String?, - domain: String?, - certificate: String?, - status: CryptoCertificateStatus, - thumbprint: String?, - serialNumber: String?, - endTimestampSeconds: Long? - ): WireIdentity { - @Suppress("ComplexCondition") - val certificateData = if (handle == null || displayName == null || domain == null || certificate == null - || thumbprint == null || serialNumber == null || endTimestampSeconds == null - ) { - null - } else { - Certificate( - Handle.fromString(handle, domain), - displayName, - domain, - certificate, - thumbprint, - serialNumber, - endTimestampSeconds - ) - } - return WireIdentity( - clientId = clientId, - certificate = certificateData, - status = status - ) - } - } - - data class Certificate( + data class X509Identity( val handle: Handle, val displayName: String, val domain: String, val certificate: String, - val thumbprint: String, val serialNumber: String, - val endTimestampSeconds: Long + val notBefore: Long, + val notAfter: Long ) // WireIdentity handle format is "{scheme}%40{username}@{domain}" diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/PlainId.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/PlainId.kt index 3e061b14e2c..673c0d7983c 100644 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/PlainId.kt +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/PlainId.kt @@ -18,9 +18,12 @@ package com.wire.kalium.logic.data.id +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlin.jvm.JvmInline @JvmInline -value class PlainId(val value: String) +@Serializable +value class PlainId(@SerialName("value") val value: String) typealias TeamId = PlainId diff --git a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/QualifiedId.kt b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/QualifiedId.kt index 9695e79145e..a8f19d888f1 100644 --- a/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/QualifiedId.kt +++ b/data/src/commonMain/kotlin/com/wire/kalium/logic/data/id/QualifiedId.kt @@ -60,9 +60,10 @@ value class SubconversationId(val value: String) { fun toLogString() = value.obfuscateId() } +@Serializable data class QualifiedClientID( - val clientId: ClientId, - val userId: UserId + @SerialName("clientId") val clientId: ClientId, + @SerialName("userId") val userId: UserId ) typealias MessageId = String diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/DecryptedMessageBundleMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/DecryptedMessageBundleMapper.kt index 9d69be60775..fba4b54ed1a 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/DecryptedMessageBundleMapper.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/DecryptedMessageBundleMapper.kt @@ -33,17 +33,5 @@ fun com.wire.kalium.cryptography.DecryptedMessageBundle.toModel(groupID: GroupID ) }, commitDelay, - identity?.let { identity -> - identity.certificate?.let { certificate -> - E2EIdentity( - identity.clientId, - certificate.handle.handle, - certificate.displayName, - certificate.domain, - certificate.certificate, - identity.status, - certificate.thumbprint - ) - } - } + identity ) diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt index ac47939b137..83ed7cfbf9d 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepository.kt @@ -19,7 +19,6 @@ package com.wire.kalium.logic.data.conversation import com.wire.kalium.cryptography.CommitBundle -import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.E2EIClient import com.wire.kalium.cryptography.MLSClient @@ -99,17 +98,7 @@ data class DecryptedMessageBundle( val groupID: GroupID, val applicationMessage: ApplicationMessage?, val commitDelay: Long?, - val identity: E2EIdentity? -) - -data class E2EIdentity( - val clientId: CryptoQualifiedClientId, - val handle: String, - val displayName: String, - val domain: String, - val certificate: String, - val status: CryptoCertificateStatus, - val thumbprint: String + val identity: WireIdentity? ) @Suppress("TooManyFunctions", "LongParameterList") diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/MapperProvider.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/MapperProvider.kt index 4ac6b3a34f3..797f6209d1a 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/MapperProvider.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/di/MapperProvider.kt @@ -101,8 +101,6 @@ import com.wire.kalium.logic.data.user.type.DomainUserTypeMapper import com.wire.kalium.logic.data.user.type.DomainUserTypeMapperImpl import com.wire.kalium.logic.data.user.type.UserEntityTypeMapper import com.wire.kalium.logic.data.user.type.UserEntityTypeMapperImpl -import com.wire.kalium.logic.feature.e2ei.CertificateStatusMapper -import com.wire.kalium.logic.feature.e2ei.CertificateStatusMapperImpl @Suppress("TooManyFunctions") internal object MapperProvider { @@ -180,5 +178,4 @@ internal object MapperProvider { fun serviceMapper(): ServiceMapper = ServiceMapper() fun legalHoldStatusMapper(): LegalHoldStatusMapper = LegalHoldStatusMapperImpl fun acmeMapper(): AcmeMapper = AcmeMapperImpl() - fun certificateStatusMapper(): CertificateStatusMapper = CertificateStatusMapperImpl() } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatus.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatus.kt index cc976d5eeda..0aaa873478c 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatus.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatus.kt @@ -22,3 +22,8 @@ enum class CertificateStatus { EXPIRED, VALID } + +enum class UserVerificationStatus { + Verified, + NotVerified +} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatusMapper.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatusMapper.kt deleted file mode 100644 index 252810561bc..00000000000 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/CertificateStatusMapper.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Wire - * Copyright (C) 2024 Wire Swiss GmbH - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - */ - -package com.wire.kalium.logic.feature.e2ei - -import com.wire.kalium.cryptography.CryptoCertificateStatus - -interface CertificateStatusMapper { - fun toCertificateStatus(certificateStatus: CryptoCertificateStatus): CertificateStatus -} - -class CertificateStatusMapperImpl : CertificateStatusMapper { - override fun toCertificateStatus(certificateStatus: CryptoCertificateStatus): CertificateStatus = - when (certificateStatus) { - CryptoCertificateStatus.EXPIRED -> CertificateStatus.EXPIRED - CryptoCertificateStatus.REVOKED -> CertificateStatus.REVOKED - CryptoCertificateStatus.VALID -> CertificateStatus.VALID - } -} diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/E2eiCertificate.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/E2eiCertificate.kt index 50c85858c99..f56caab5dff 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/E2eiCertificate.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/E2eiCertificate.kt @@ -17,40 +17,89 @@ */ package com.wire.kalium.logic.feature.e2ei +import com.wire.kalium.cryptography.CredentialType +import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.WireIdentity -import com.wire.kalium.logic.di.MapperProvider +import com.wire.kalium.logic.data.id.QualifiedClientID +import com.wire.kalium.logic.data.id.toModel import kotlinx.datetime.Instant import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class E2eiCertificate( - @SerialName("userHandle") - var userHandle: String, - @SerialName("status") - val status: CertificateStatus, - @SerialName("serialNumber") - val serialNumber: String, - @SerialName("certificateDetail") - val certificateDetail: String, - @SerialName("thumbprint") - val thumbprint: String, - @SerialName("endAt") - val endAt: Instant +data class MLSClientIdentity( + @SerialName("clientId") val clientId: QualifiedClientID, + @SerialName("e2eiStatus") val e2eiStatus: MLSClientE2EIStatus, + @SerialName("thumbprint") val thumbprint: String, + @SerialName("credentialType") val credentialType: MLSCredentialsType, + @SerialName("x509Identity") val x509Identity: X509Identity? ) { companion object { - private val certificateStatusMapper = MapperProvider.certificateStatusMapper() - - fun fromWireIdentity(identity: WireIdentity): E2eiCertificate? = - identity.certificate?.let { - E2eiCertificate( - userHandle = it.handle.handle, - status = certificateStatusMapper.toCertificateStatus(identity.status), + fun fromWireIdentity(identity: WireIdentity): MLSClientIdentity = MLSClientIdentity( + clientId = identity.clientId.toModel(), + e2eiStatus = MLSClientE2EIStatus.fromCryptoStatus(identity), + thumbprint = identity.thumbprint, + credentialType = MLSCredentialsType.fromCrypto(identity.credentialType), + x509Identity = identity.x509Identity?.let { + X509Identity( + handle = Handle.fromWireIdentity(it.handle), + displayName = it.displayName, + domain = it.domain, serialNumber = it.serialNumber, - certificateDetail = it.certificate, - thumbprint = it.thumbprint, - endAt = Instant.fromEpochSeconds(it.endTimestampSeconds) + certificate = it.certificate, + notBefore = Instant.fromEpochSeconds(it.notBefore), + notAfter = Instant.fromEpochSeconds(it.notAfter) ) } + ) + } +} + +@Serializable +data class X509Identity( + @SerialName("handle") val handle: Handle, + @SerialName("displayName") val displayName: String, + @SerialName("domain") val domain: String, + @SerialName("serialNumber") val serialNumber: String, + @SerialName("certificateDetail") val certificate: String, + @SerialName("notBefore") val notBefore: Instant, + @SerialName("notAfter") val notAfter: Instant +) + +@Serializable +data class Handle( + @SerialName("scheme") val scheme: String, + @SerialName("handle") val handle: String, + @SerialName("domain") val domain: String +) { + companion object { + fun fromWireIdentity(handle: WireIdentity.Handle) = + Handle(handle.scheme, handle.handle, handle.domain) + } +} + +enum class MLSClientE2EIStatus { + REVOKED, EXPIRED, VALID, NOT_ACTIVATED; + + companion object { + fun fromCryptoStatus(identity: WireIdentity) = + if (identity.credentialType == CredentialType.Basic || identity.x509Identity == null) + NOT_ACTIVATED + else when (identity.status) { + CryptoCertificateStatus.REVOKED -> REVOKED + CryptoCertificateStatus.EXPIRED -> EXPIRED + CryptoCertificateStatus.VALID -> VALID + } + } +} + +enum class MLSCredentialsType { + X509, BASIC; + + companion object { + fun fromCrypto(value: CredentialType) = when (value) { + CredentialType.Basic -> BASIC + CredentialType.X509 -> X509 + } } } 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 9e605680c63..7250f73cb5f 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 @@ -18,6 +18,7 @@ package com.wire.kalium.logic.feature.e2ei.usecase import com.benasher44.uuid.uuid4 +import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.WireIdentity import com.wire.kalium.logger.KaliumLogger @@ -115,9 +116,10 @@ internal class FetchMLSVerificationStatusUseCaseImpl( val persistedMemberInfo = dbData.members[userId] val isUserVerified = wireIdentity.firstOrNull { it.status != CryptoCertificateStatus.VALID || - it.certificate == null || - it.certificate?.displayName != persistedMemberInfo?.name || - it.certificate?.handle?.handle != persistedMemberInfo?.handle + 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 diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetE2EICertificateUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetE2EICertificateUseCase.kt index da2ad2a676e..dd63f578d5c 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetE2EICertificateUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetE2EICertificateUseCase.kt @@ -17,36 +17,30 @@ */ package com.wire.kalium.logic.feature.e2ei.usecase +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.conversation.MLSConversationRepository -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate -import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.flatMap +import com.wire.kalium.logic.functional.left +import com.wire.kalium.logic.functional.right /** * This use case is used to get the e2ei certificate */ -interface GetE2eiCertificateUseCase { - suspend operator fun invoke(clientId: ClientId): GetE2EICertificateUseCaseResult +interface GetMLSClientIdentityUseCase { + suspend operator fun invoke(clientId: ClientId): Either } -class GetE2eiCertificateUseCaseImpl internal constructor( +class GetMLSClientIdentityUseCaseImpl internal constructor( private val mlsConversationRepository: MLSConversationRepository -) : GetE2eiCertificateUseCase { - override suspend operator fun invoke(clientId: ClientId): GetE2EICertificateUseCaseResult = - mlsConversationRepository.getClientIdentity(clientId).fold( - { GetE2EICertificateUseCaseResult.Failure }, - { +) : GetMLSClientIdentityUseCase { + override suspend operator fun invoke(clientId: ClientId): Either = + mlsConversationRepository.getClientIdentity(clientId).flatMap { it?.let { - E2eiCertificate.fromWireIdentity(it)?.let { certificate -> - GetE2EICertificateUseCaseResult.Success(certificate) - } - } ?: GetE2EICertificateUseCaseResult.NotActivated + MLSClientIdentity.fromWireIdentity(it).right() + } ?: StorageFailure.DataNotFound.left() } - ) -} - -sealed class GetE2EICertificateUseCaseResult { - class Success(val certificate: E2eiCertificate) : GetE2EICertificateUseCaseResult() - data object NotActivated : GetE2EICertificateUseCaseResult() - data object Failure : GetE2EICertificateUseCaseResult() } 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 cb36d1ca7f9..034d802a9bc 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 @@ -17,12 +17,13 @@ */ 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.MLSConversationRepository 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.feature.e2ei.E2eiCertificate import com.wire.kalium.logic.functional.fold /** @@ -30,40 +31,27 @@ import com.wire.kalium.logic.functional.fold * Return [Map] where keys are [UserId] and values - nullable [CertificateStatus] of corresponding user. */ interface GetMembersE2EICertificateStatusesUseCase { - suspend operator fun invoke(conversationId: ConversationId, userIds: List): Map + suspend operator fun invoke(conversationId: ConversationId, userIds: List): Map } class GetMembersE2EICertificateStatusesUseCaseImpl internal constructor( private val mlsConversationRepository: MLSConversationRepository ) : GetMembersE2EICertificateStatusesUseCase { - override suspend operator fun invoke(conversationId: ConversationId, userIds: List): Map = + override suspend operator fun invoke(conversationId: ConversationId, userIds: List): Map = mlsConversationRepository.getMembersIdentities(conversationId, userIds).fold( { mapOf() }, { it.mapValues { (_, identities) -> - identities.getUserCertificateStatus() + // todo: we need to check the user name and details! + identities.isUserMLSVerified() } } ) } /** - * @return null if list is empty; - * [CertificateStatus.REVOKED] if any certificate is revoked; - * [CertificateStatus.EXPIRED] if any certificate is expired; - * [CertificateStatus.VALID] otherwise. + * @return if given user is verified or not */ -fun List.getUserCertificateStatus(): CertificateStatus? { - val certificates = this.map { - E2eiCertificate.fromWireIdentity(it) - } - return if (certificates.isEmpty() || certificates.any { it == null }) { - null - } else if (certificates.any { it!!.status == CertificateStatus.REVOKED }) { - CertificateStatus.REVOKED - } else if (certificates.any { it!!.status == CertificateStatus.EXPIRED }) { - CertificateStatus.EXPIRED - } else { - CertificateStatus.VALID - } +fun List.isUserMLSVerified() = this.isNotEmpty() && this.all { + it.x509Identity != null && it.credentialType == CredentialType.X509 && it.status == CryptoCertificateStatus.VALID } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EIAllCertificatesUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EIAllCertificatesUseCase.kt index bf10ce41595..5304ee9c16e 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EIAllCertificatesUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/GetUserE2EIAllCertificatesUseCase.kt @@ -20,31 +20,29 @@ package com.wire.kalium.logic.feature.e2ei.usecase import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.conversation.MLSConversationRepository import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase import com.wire.kalium.logic.functional.getOrElse import com.wire.kalium.logic.functional.map /** - * This use case is used to get all e2ei certificates of the user. - * Returns Map where key is value of [ClientId] and [E2eiCertificate] is certificate itself + * This use case is used to get all MLSClientIdentities of the user. + * Returns Map where key is value of [ClientId] and [MLSClientIdentity] is certificate itself */ interface GetUserE2eiCertificatesUseCase { - suspend operator fun invoke(userId: UserId): Map + suspend operator fun invoke(userId: UserId): Map } class GetUserE2eiCertificatesUseCaseImpl internal constructor( private val mlsConversationRepository: MLSConversationRepository, private val isE2EIEnabledUseCase: IsE2EIEnabledUseCase ) : GetUserE2eiCertificatesUseCase { - override suspend operator fun invoke(userId: UserId): Map = + override suspend operator fun invoke(userId: UserId): Map = if (isE2EIEnabledUseCase()) { mlsConversationRepository.getUserIdentity(userId).map { identities -> - val result = mutableMapOf() + val result = mutableMapOf() identities.forEach { - E2eiCertificate.fromWireIdentity(it)?.let { certificate -> - result[it.clientId.value] = certificate - } + result[it.clientId.value] = MLSClientIdentity.fromWireIdentity(it) } result }.getOrElse(mapOf()) 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 f884f0c23d4..7b4d0313321 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,41 +19,23 @@ 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.feature.e2ei.CertificateStatus import com.wire.kalium.logic.feature.user.IsE2EIEnabledUseCase import com.wire.kalium.logic.functional.fold /** * This use case is used to get the e2ei certificate status of specific user */ -interface GetUserE2eiCertificateStatusUseCase { - suspend operator fun invoke(userId: UserId): GetUserE2eiCertificateStatusResult +interface IsOtherUserE2EIVerifiedUseCase { + suspend operator fun invoke(userId: UserId): Boolean } -class GetUserE2eiCertificateStatusUseCaseImpl internal constructor( +class IsOtherUserE2EIVerifiedUseCaseImpl internal constructor( private val mlsConversationRepository: MLSConversationRepository, private val isE2EIEnabledUseCase: IsE2EIEnabledUseCase -) : GetUserE2eiCertificateStatusUseCase { - override suspend operator fun invoke(userId: UserId): GetUserE2eiCertificateStatusResult = - if (isE2EIEnabledUseCase()) { - mlsConversationRepository.getUserIdentity(userId).fold( - { - GetUserE2eiCertificateStatusResult.Failure.NotActivated - }, - { identities -> - identities.getUserCertificateStatus()?.let { - GetUserE2eiCertificateStatusResult.Success(it) - } ?: GetUserE2eiCertificateStatusResult.Failure.NotActivated - } - ) - } else { - GetUserE2eiCertificateStatusResult.Failure.NotActivated - } -} - -sealed class GetUserE2eiCertificateStatusResult { - class Success(val status: CertificateStatus) : GetUserE2eiCertificateStatusResult() - sealed class Failure : GetUserE2eiCertificateStatusResult() { - data object NotActivated : Failure() - } +) : IsOtherUserE2EIVerifiedUseCase { + override suspend operator fun invoke(userId: UserId): Boolean = + isE2EIEnabledUseCase() && mlsConversationRepository.getUserIdentity(userId).fold( + { false }, + { it.isUserMLSVerified() } + ) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/ObserveCertificateRevocationForSelfClientUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/ObserveCertificateRevocationForSelfClientUseCase.kt index eb6505311b3..deac100c120 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/ObserveCertificateRevocationForSelfClientUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/ObserveCertificateRevocationForSelfClientUseCase.kt @@ -20,7 +20,9 @@ package com.wire.kalium.logic.feature.e2ei.usecase import com.wire.kalium.logger.KaliumLogger import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.id.CurrentClientIdProvider -import com.wire.kalium.logic.feature.e2ei.CertificateStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.functional.isRight import com.wire.kalium.logic.functional.map /** @@ -34,7 +36,7 @@ interface ObserveCertificateRevocationForSelfClientUseCase { internal class ObserveCertificateRevocationForSelfClientUseCaseImpl( private val userConfigRepository: UserConfigRepository, private val currentClientIdProvider: CurrentClientIdProvider, - private val getE2eiCertificate: GetE2eiCertificateUseCase, + private val getE2eiCertificate: GetMLSClientIdentityUseCase, kaliumLogger: KaliumLogger, ) : ObserveCertificateRevocationForSelfClientUseCase { @@ -44,7 +46,7 @@ internal class ObserveCertificateRevocationForSelfClientUseCaseImpl( logger.d("Checking if should notify certificate revocation") currentClientIdProvider().map { clientId -> getE2eiCertificate(clientId).run { - if (this is GetE2EICertificateUseCaseResult.Success && certificate.status == CertificateStatus.REVOKED) { + if (isRight() && (value as MLSClientIdentity).e2eiStatus == MLSClientE2EIStatus.REVOKED) { logger.i("Setting that should notify certificate revocation") userConfigRepository.setShouldNotifyForRevokedCertificate(true) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/ObserveE2EIRequiredUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/ObserveE2EIRequiredUseCase.kt index 01b1178a703..8c94bfeb1d4 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/ObserveE2EIRequiredUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/user/ObserveE2EIRequiredUseCase.kt @@ -20,13 +20,11 @@ package com.wire.kalium.logic.feature.user import com.wire.kalium.logic.configuration.E2EISettings import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.id.CurrentClientIdProvider -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2EICertificateUseCaseResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCase +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.X509Identity +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase import com.wire.kalium.logic.featureFlags.FeatureSupport -import com.wire.kalium.logic.functional.getOrElse -import com.wire.kalium.logic.functional.map +import com.wire.kalium.logic.functional.fold import com.wire.kalium.logic.functional.onlyRight import com.wire.kalium.util.DateTimeUtil import com.wire.kalium.util.KaliumDispatcherImpl @@ -58,7 +56,7 @@ interface ObserveE2EIRequiredUseCase { internal class ObserveE2EIRequiredUseCaseImpl( private val userConfigRepository: UserConfigRepository, private val featureSupport: FeatureSupport, - private val e2eiCertificate: GetE2eiCertificateUseCase, + private val mlsClientIdentity: GetMLSClientIdentityUseCase, private val currentClientIdProvider: CurrentClientIdProvider, private val dispatcher: CoroutineDispatcher = KaliumDispatcherImpl.io, private val renewCertificateRandomDelay: Duration = RENEW_RANDOM_DELAY @@ -76,18 +74,25 @@ internal class ObserveE2EIRequiredUseCaseImpl( observeE2EISettings().map { setting -> if (!setting.isRequired) return@map E2EIRequiredResult.NotRequired - return@map currentClientIdProvider().map { clientId -> - when (val certificateResult = e2eiCertificate(clientId)) { - is GetE2EICertificateUseCaseResult.Failure -> E2EIRequiredResult.NotRequired - - is GetE2EICertificateUseCaseResult.Success -> onUserHasCertificate(certificateResult.certificate) - - is GetE2EICertificateUseCaseResult.NotActivated -> onUserHasNoCertificate(setting) + return@map currentClientIdProvider().fold( + { + E2EIRequiredResult.NotRequired + }, + { clientId -> + return@map mlsClientIdentity(clientId).fold({ + E2EIRequiredResult.NotRequired + }, { clientIdentity -> + when (clientIdentity.e2eiStatus) { + MLSClientE2EIStatus.REVOKED -> E2EIRequiredResult.NotRequired + MLSClientE2EIStatus.NOT_ACTIVATED -> onUserHasNoCertificate(setting) + MLSClientE2EIStatus.EXPIRED -> E2EIRequiredResult.NoGracePeriod.Renew + MLSClientE2EIStatus.VALID -> onUserHasValidCertificate(clientIdentity.x509Identity!!) + } + }) } - }.getOrElse { E2EIRequiredResult.NotRequired } + ) } - } - .flowOn(dispatcher) + }.flowOn(dispatcher) } private fun onUserHasNoCertificate(setting: E2EISettings) = @@ -95,14 +100,11 @@ internal class ObserveE2EIRequiredUseCaseImpl( E2EIRequiredResult.WithGracePeriod.Create(timeLeft) } ?: E2EIRequiredResult.NoGracePeriod.Create - private fun onUserHasCertificate(certificate: E2eiCertificate) = - if (certificate.status == CertificateStatus.EXPIRED) { - E2EIRequiredResult.NoGracePeriod.Renew - } else if (certificate.status == CertificateStatus.VALID && certificate.shouldRenew()) { - E2EIRequiredResult.WithGracePeriod.Renew(certificate.renewGracePeriodLeft()) - } else { - E2EIRequiredResult.NotRequired - } + private fun onUserHasValidCertificate(identity: X509Identity) = if (identity.shouldRenew()) { + E2EIRequiredResult.WithGracePeriod.Renew(identity.renewGracePeriodLeft()) + } else { + E2EIRequiredResult.NotRequired + } private fun observeE2EISettings() = userConfigRepository.observeE2EISettings().onlyRight().flowOn(dispatcher) @@ -119,13 +121,13 @@ internal class ObserveE2EIRequiredUseCaseImpl( else gracePeriodEnd.minus(DateTimeUtil.currentInstant()) } - private fun E2eiCertificate.shouldRenew(): Boolean = - endAt.minus(DateTimeUtil.currentInstant()) + private fun X509Identity.shouldRenew(): Boolean = + notAfter.minus(DateTimeUtil.currentInstant()) .minus(RENEW_CONSTANT_DELAY) .minus(renewCertificateRandomDelay) .inWholeMilliseconds <= 0 - private fun E2eiCertificate.renewGracePeriodLeft(): Duration = endAt.minus(DateTimeUtil.currentInstant()) + private fun X509Identity.renewGracePeriodLeft(): Duration = notAfter.minus(DateTimeUtil.currentInstant()) companion object { private const val NO_DELAY_MS = 0L 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 bf55756d062..b81afca7eb1 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 @@ -27,8 +27,8 @@ import com.wire.kalium.logic.data.client.ClientRepository import com.wire.kalium.logic.data.conversation.JoinExistingMLSConversationsUseCase import com.wire.kalium.logic.data.conversation.MLSConversationRepository import com.wire.kalium.logic.data.e2ei.CertificateRevocationListRepository -import com.wire.kalium.logic.data.e2ei.RevocationListChecker import com.wire.kalium.logic.data.e2ei.E2EIRepository +import com.wire.kalium.logic.data.e2ei.RevocationListChecker import com.wire.kalium.logic.data.id.CurrentClientIdProvider import com.wire.kalium.logic.data.properties.UserPropertyRepository import com.wire.kalium.logic.data.session.SessionRepository @@ -53,14 +53,14 @@ import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorker import com.wire.kalium.logic.feature.e2ei.CertificateRevocationListCheckWorkerImpl import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIUseCase import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIUseCaseImpl -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCase -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCaseImpl +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCaseImpl import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatusesUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetMembersE2EICertificateStatusesUseCaseImpl -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusUseCase -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusUseCaseImpl import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCase import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificatesUseCaseImpl +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCaseImpl import com.wire.kalium.logic.feature.e2ei.usecase.ObserveCertificateRevocationForSelfClientUseCase import com.wire.kalium.logic.feature.e2ei.usecase.ObserveCertificateRevocationForSelfClientUseCaseImpl import com.wire.kalium.logic.feature.featureConfig.FeatureFlagSyncWorkerImpl @@ -126,12 +126,12 @@ class UserScope internal constructor( clientRepository, joinExistingMLSConversationsUseCase ) - val getE2EICertificate: GetE2eiCertificateUseCase - get() = GetE2eiCertificateUseCaseImpl( + val getE2EICertificate: GetMLSClientIdentityUseCase + get() = GetMLSClientIdentityUseCaseImpl( mlsConversationRepository = mlsConversationRepository ) - val getUserE2eiCertificateStatus: GetUserE2eiCertificateStatusUseCase - get() = GetUserE2eiCertificateStatusUseCaseImpl( + val getUserE2eiCertificateStatus: IsOtherUserE2EIVerifiedUseCase + get() = IsOtherUserE2EIVerifiedUseCaseImpl( mlsConversationRepository = mlsConversationRepository, isE2EIEnabledUseCase = isE2EIEnabledUseCase ) diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt index 2099eab9ce8..eb125017485 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/data/conversation/MLSConversationRepositoryTest.kt @@ -20,6 +20,7 @@ package com.wire.kalium.logic.data.conversation import com.benasher44.uuid.uuid4 import com.wire.kalium.cryptography.CommitBundle +import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.E2EIClient @@ -1460,7 +1461,7 @@ class MLSConversationRepositoryTest { val handleWithSchemeAndDomain = "$scheme://%40$handle@$domain" val groupId = Arrangement.GROUP_ID.value val wireIdentity = WIRE_IDENTITY.copy( - certificate = WIRE_IDENTITY.certificate!!.copy( + x509Identity = WIRE_IDENTITY.x509Identity!!.copy( handle = WireIdentity.Handle.fromString(handleWithSchemeAndDomain, domain) ) ) @@ -1474,9 +1475,9 @@ class MLSConversationRepositoryTest { // then result.shouldSucceed() { it.forEach { - assertEquals(scheme, it.certificate?.handle?.scheme) - assertEquals(handle, it.certificate?.handle?.handle) - assertEquals(domain, it.certificate?.handle?.domain) + assertEquals(scheme, it.x509Identity?.handle?.scheme) + assertEquals(handle, it.x509Identity?.handle?.handle) + assertEquals(domain, it.x509Identity?.handle?.domain) } } } @@ -1490,7 +1491,7 @@ class MLSConversationRepositoryTest { val handleWithSchemeAndDomain = "$scheme://%40$handle@$domain" val groupId = Arrangement.GROUP_ID.value val wireIdentity = WIRE_IDENTITY.copy( - certificate = WIRE_IDENTITY.certificate!!.copy( + x509Identity = WIRE_IDENTITY.x509Identity!!.copy( handle = WireIdentity.Handle.fromString(handleWithSchemeAndDomain, domain) ) ) @@ -1505,9 +1506,9 @@ class MLSConversationRepositoryTest { result.shouldSucceed() { it.values.forEach { it.forEach { - assertEquals(scheme, it.certificate?.handle?.scheme) - assertEquals(handle, it.certificate?.handle?.handle) - assertEquals(domain, it.certificate?.handle?.domain) + assertEquals(scheme, it.x509Identity?.handle?.scheme) + assertEquals(handle, it.x509Identity?.handle?.handle) + assertEquals(domain, it.x509Identity?.handle?.domain) } } } @@ -1823,14 +1824,22 @@ class MLSConversationRepositoryTest { val WIRE_IDENTITY = WireIdentity( CRYPTO_CLIENT_ID, - "user_handle", + CryptoCertificateStatus.VALID, + thumbprint = "thumbprint", + CredentialType.X509, + x509Identity = WireIdentity.X509Identity( + WireIdentity.Handle( + "wireapp", + "user_handle", + "wire.com" + ), "User Test", "domain.com", "certificate", - CryptoCertificateStatus.VALID, - thumbprint = "thumbprint", serialNumber = "serialNumber", - endTimestampSeconds = 1899105093 + notAfter = 1899105093, + notBefore = 1899205093 + ) ) val E2EI_CONVERSATION_CLIENT_INFO_ENTITY = E2EIConversationClientInfoEntity(UserIDEntity(uuid4().toString(), "domain.com"), "clientId", "groupId") diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/ObserveE2EIRequiredUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/ObserveE2EIRequiredUseCaseTest.kt index 760fa2da8f0..f97f6873542 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/ObserveE2EIRequiredUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/client/ObserveE2EIRequiredUseCaseTest.kt @@ -18,20 +18,27 @@ package com.wire.kalium.logic.feature.client import app.cash.turbine.test +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.StorageFailure import com.wire.kalium.logic.configuration.E2EISettings import com.wire.kalium.logic.configuration.UserConfigRepository import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.id.CurrentClientIdProvider -import com.wire.kalium.logic.feature.e2ei.CertificateStatus -import com.wire.kalium.logic.feature.e2ei.E2eiCertificate -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2EICertificateUseCaseResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCase +import com.wire.kalium.logic.data.id.QualifiedClientID +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase import com.wire.kalium.logic.feature.user.E2EIRequiredResult import com.wire.kalium.logic.feature.user.ObserveE2EIRequiredUseCase import com.wire.kalium.logic.feature.user.ObserveE2EIRequiredUseCaseImpl import com.wire.kalium.logic.featureFlags.FeatureSupport import com.wire.kalium.logic.framework.TestClient +import com.wire.kalium.logic.framework.TestMLSClientIdentity.getMLSClientIdentityWithE2EI +import com.wire.kalium.logic.framework.TestMLSClientIdentity.getMLSClientIdentityWithOutE2EI +import com.wire.kalium.logic.framework.TestUser import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.left +import com.wire.kalium.logic.functional.right import com.wire.kalium.logic.test_util.TestKaliumDispatcher import com.wire.kalium.util.DateTimeUtil import io.mockative.Mock @@ -68,7 +75,7 @@ class ObserveE2EIRequiredUseCaseTest { .withE2EINotificationTime(DateTimeUtil.currentInstant()) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.NotActivated) + .withGetE2EICertificateUseCaseResult(getMLSClientIdentityWithOutE2EI(CLIENT_ID_USER1).right()) .arrange() useCase().test { @@ -87,7 +94,7 @@ class ObserveE2EIRequiredUseCaseTest { .withE2EINotificationTime(DateTimeUtil.currentInstant()) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.NotActivated) + .withGetE2EICertificateUseCaseResult(getMLSClientIdentityWithOutE2EI(CLIENT_ID_USER1).right()) .arrange() useCase().test { @@ -107,7 +114,7 @@ class ObserveE2EIRequiredUseCaseTest { .withE2EINotificationTime(DateTimeUtil.currentInstant().plus(delayDuration)) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.NotActivated) + .withGetE2EICertificateUseCaseResult(getMLSClientIdentityWithOutE2EI(CLIENT_ID_USER1).right()) .arrange() useCase().test { @@ -130,7 +137,7 @@ class ObserveE2EIRequiredUseCaseTest { .withE2EINotificationTime(DateTimeUtil.currentInstant()) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.NotActivated) + .withGetE2EICertificateUseCaseResult(getMLSClientIdentityWithOutE2EI(CLIENT_ID_USER1).right()) .arrange() useCase().test { @@ -148,7 +155,7 @@ class ObserveE2EIRequiredUseCaseTest { .withE2EINotificationTime(DateTimeUtil.currentInstant()) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.NotActivated) + .withGetE2EICertificateUseCaseResult(getMLSClientIdentityWithOutE2EI(CLIENT_ID_USER1).right()) .arrange() useCase().test { @@ -186,12 +193,15 @@ class ObserveE2EIRequiredUseCaseTest { val setting = MLS_E2EI_SETTING.copy( gracePeriodEnd = DateTimeUtil.currentInstant() ) + val mlsClientIdentity = getMLSClientIdentityWithE2EI(CLIENT_ID_USER1) val (_, useCase) = Arrangement(TestKaliumDispatcher.io) .withMLSE2EISetting(setting) .withE2EINotificationTime(DateTimeUtil.currentInstant()) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.Success(VALID_CERTIFICATE)) + .withGetE2EICertificateUseCaseResult( + mlsClientIdentity.copy(x509Identity = mlsClientIdentity.x509Identity?.copy(notAfter = Instant.DISTANT_PAST)).right() + ) .arrange() useCase().test { @@ -212,7 +222,7 @@ class ObserveE2EIRequiredUseCaseTest { .withIsMLSSupported(true) .withCurrentClientProviderSuccess() .withGetE2EICertificateUseCaseResult( - GetE2EICertificateUseCaseResult.Success(VALID_CERTIFICATE.copy(status = CertificateStatus.EXPIRED)) + getMLSClientIdentityWithE2EI(CLIENT_ID_USER1, MLSClientE2EIStatus.EXPIRED).right() ) .arrange() @@ -233,7 +243,7 @@ class ObserveE2EIRequiredUseCaseTest { .withE2EINotificationTime(DateTimeUtil.currentInstant()) .withIsMLSSupported(true) .withCurrentClientProviderSuccess() - .withGetE2EICertificateUseCaseResult(GetE2EICertificateUseCaseResult.Failure) + .withGetE2EICertificateUseCaseResult(StorageFailure.DataNotFound.left()) .arrange() useCase().test { @@ -253,12 +263,7 @@ class ObserveE2EIRequiredUseCaseTest { .withIsMLSSupported(true) .withCurrentClientProviderSuccess() .withGetE2EICertificateUseCaseResult( - GetE2EICertificateUseCaseResult.Success( - VALID_CERTIFICATE.copy( - status = CertificateStatus.EXPIRED, - endAt = DateTimeUtil.currentInstant().minus(1.days) - ) - ) + getMLSClientIdentityWithE2EI(CLIENT_ID_USER1, MLSClientE2EIStatus.EXPIRED).right() ) .arrange() @@ -280,9 +285,7 @@ class ObserveE2EIRequiredUseCaseTest { .withIsMLSSupported(true) .withCurrentClientProviderSuccess() .withGetE2EICertificateUseCaseResult( - GetE2EICertificateUseCaseResult.Success( - VALID_CERTIFICATE.copy(endAt = DateTimeUtil.currentInstant().plus(40.days)) - ) + getMLSClientIdentityWithE2EI(CLIENT_ID_USER1).right() ) .arrange() @@ -304,9 +307,7 @@ class ObserveE2EIRequiredUseCaseTest { .withIsMLSSupported(true) .withCurrentClientProviderSuccess() .withGetE2EICertificateUseCaseResult( - GetE2EICertificateUseCaseResult.Success( - VALID_CERTIFICATE.copy(status = CertificateStatus.REVOKED) - ) + getMLSClientIdentityWithE2EI(CLIENT_ID_USER1, MLSClientE2EIStatus.REVOKED).right() ) .arrange() @@ -325,7 +326,7 @@ class ObserveE2EIRequiredUseCaseTest { val featureSupport = mock(FeatureSupport::class) @Mock - val e2eiCertificate = mock(GetE2eiCertificateUseCase::class) + val e2eiCertificate = mock(GetMLSClientIdentityUseCase::class) @Mock val currentClientIdProvider = mock(CurrentClientIdProvider::class) @@ -355,7 +356,7 @@ class ObserveE2EIRequiredUseCaseTest { }.returns(Either.Right(clientId)) } - suspend fun withGetE2EICertificateUseCaseResult(result: GetE2EICertificateUseCaseResult) = apply { + suspend fun withGetE2EICertificateUseCaseResult(result: Either) = apply { coEvery { e2eiCertificate.invoke(any()) }.returns(result) @@ -366,13 +367,6 @@ class ObserveE2EIRequiredUseCaseTest { companion object { private val MLS_E2EI_SETTING = E2EISettings(true, "some_url", null, false, null) - private val VALID_CERTIFICATE = E2eiCertificate( - userHandle = "userHandle", - serialNumber = "serialNumber", - certificateDetail = "certificateDetail", - status = CertificateStatus.VALID, - thumbprint = "thumbprint", - endAt = DateTimeUtil.currentInstant().plus(1.days) - ) + private val CLIENT_ID_USER1 = QualifiedClientID(ClientId("clientId1"), TestUser.USER_ID) } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetE2eiCertificateUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetE2eiCertificateUseCaseTest.kt index b904ed19e0f..5ff5f67540e 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetE2eiCertificateUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetE2eiCertificateUseCaseTest.kt @@ -17,6 +17,7 @@ */ package com.wire.kalium.logic.feature.e2ei +import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.WireIdentity @@ -27,9 +28,10 @@ import com.wire.kalium.logic.data.conversation.ClientId import com.wire.kalium.logic.data.conversation.MLSConversationRepository import com.wire.kalium.logic.data.id.toCrypto import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2EICertificateUseCaseResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetE2eiCertificateUseCaseImpl +import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCaseImpl import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.util.shouldFail +import com.wire.kalium.logic.util.shouldSucceed import io.mockative.Mock import io.mockative.any import io.mockative.coEvery @@ -42,6 +44,7 @@ import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertIs class GetE2eiCertificateUseCaseTest { @@ -57,7 +60,7 @@ class GetE2eiCertificateUseCaseTest { arrangement.mlsConversationRepository.getClientIdentity(any()) }.wasInvoked(once) - assertEquals(GetE2EICertificateUseCaseResult.Failure, result) + result.shouldFail() } @Test @@ -67,32 +70,51 @@ class GetE2eiCertificateUseCaseTest { .arrange() val result = getE2eiCertificateUseCase.invoke(CLIENT_ID) + result.shouldFail() coVerify { arrangement.mlsConversationRepository.getClientIdentity(any()) }.wasInvoked(once) - assertEquals(GetE2EICertificateUseCaseResult.Failure, result) } @Test fun givenRepositoryReturnsValidCertificateString_whenRunningUseCase_thenReturnCertificate() = runTest { val (arrangement, getE2eiCertificateUseCase) = Arrangement() - .withRepositoryValidCertificate(IDENTITY) + .withRepositoryValidCertificate(X509_VALID_IDENTITY) .arrange() val result = getE2eiCertificateUseCase.invoke(CLIENT_ID) + result.shouldSucceed() + assertIs>(result) + assertEquals(MLSClientE2EIStatus.VALID, (result.value as MLSClientIdentity).e2eiStatus) + coVerify { arrangement.mlsConversationRepository.getClientIdentity(any()) }.wasInvoked(once) + } + + @Test + fun givenRepositoryReturnsBasicClient_whenRunningUseCase_thenReturnNotActivatedStatus() = + runTest { + val (arrangement, getE2eiCertificateUseCase) = Arrangement() + .withRepositoryValidCertificate(BASIC_VALID_IDENTITY) + .arrange() - assertEquals(true, result is GetE2EICertificateUseCaseResult.Success) + val result = getE2eiCertificateUseCase.invoke(CLIENT_ID) + result.shouldSucceed() + assertIs>(result) + assertEquals(MLSClientE2EIStatus.NOT_ACTIVATED, (result.value as MLSClientIdentity).e2eiStatus) + + coVerify { + arrangement.mlsConversationRepository.getClientIdentity(any()) + }.wasInvoked(once) } @Test - fun givenRepositoryReturnsNullCertificate_whenRunningUseCase_thenReturnNotActivated() = + fun givenRepositoryReturnsNullCertificate_whenRunningUseCase_thenReturnFailure() = runTest { val (arrangement, getE2eiCertificateUseCase) = Arrangement() .withRepositoryValidCertificate(null) @@ -100,11 +122,11 @@ class GetE2eiCertificateUseCaseTest { val result = getE2eiCertificateUseCase.invoke(CLIENT_ID) + result.shouldFail() + coVerify { arrangement.mlsConversationRepository.getClientIdentity(any()) }.wasInvoked(once) - - assertEquals(true, result is GetE2EICertificateUseCaseResult.NotActivated) } class Arrangement { @@ -112,7 +134,7 @@ class GetE2eiCertificateUseCaseTest { @Mock val mlsConversationRepository = mock(MLSConversationRepository::class) - fun arrange() = this to GetE2eiCertificateUseCaseImpl( + fun arrange() = this to GetMLSClientIdentityUseCaseImpl( mlsConversationRepository = mlsConversationRepository ) @@ -135,25 +157,31 @@ class GetE2eiCertificateUseCaseTest { private val CRYPTO_QUALIFIED_CLIENT_ID = CryptoQualifiedClientId("clientId", USER_ID.toCrypto()) - val e2eiCertificate = - E2eiCertificate( - userHandle = "handle", - status = CertificateStatus.EXPIRED, - serialNumber = "serialNumber", - certificateDetail = "certificateDetail", - endAt = Instant.DISTANT_FUTURE, - thumbprint = "thumbprint" - ) - val IDENTITY = WireIdentity( + val BASIC_VALID_IDENTITY = WireIdentity( CRYPTO_QUALIFIED_CLIENT_ID, - handle = "alic_test", - displayName = "Alice Test", - domain = "test.com", - certificate = "certificate", - status = CryptoCertificateStatus.EXPIRED, + status = CryptoCertificateStatus.VALID, thumbprint = "thumbprint", - serialNumber = "serialNumber", - endTimestampSeconds = 1899105093 + credentialType = CredentialType.Basic, + x509Identity = null + ) + val X509_VALID_IDENTITY = WireIdentity( + CRYPTO_QUALIFIED_CLIENT_ID, + status = CryptoCertificateStatus.VALID, + thumbprint = "thumbprint", + credentialType = CredentialType.X509, + x509Identity = WireIdentity.X509Identity( + WireIdentity.Handle( + scheme = "wireapp", + handle = "userHandle", + domain = "domain1" + ), + displayName = "user displayName", + domain = "domain.com", + certificate = "cert1", + serialNumber = "serial1", + notBefore = Instant.DISTANT_PAST.epochSeconds, + notAfter = Instant.DISTANT_FUTURE.epochSeconds + ) ) } } 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 fb389ae54c5..aa76385da59 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 @@ -17,6 +17,7 @@ */ package com.wire.kalium.logic.feature.e2ei +import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.WireIdentity @@ -31,6 +32,7 @@ import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArran import io.mockative.matchers.EqualsMatcher import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant import kotlin.test.Test import kotlin.test.assertEquals @@ -77,7 +79,7 @@ class GetMembersE2EICertificateStatusesUseCaseTest { val result = getMembersE2EICertificateStatuses(CONVERSATION_ID, listOf(USER_ID)) - assertEquals(CertificateStatus.EXPIRED, result[USER_ID]) + assertEquals(false, result[USER_ID]) } @Test @@ -101,8 +103,8 @@ class GetMembersE2EICertificateStatusesUseCaseTest { val result = getMembersE2EICertificateStatuses(CONVERSATION_ID, listOf(USER_ID, userId2)) - assertEquals(CertificateStatus.REVOKED, result[USER_ID]) - assertEquals(CertificateStatus.VALID, result[userId2]) + assertEquals(false, result[USER_ID]) + assertEquals(true, result[userId2]) } private class Arrangement(private val block: suspend Arrangement.() -> Unit) : @@ -124,17 +126,24 @@ class GetMembersE2EICertificateStatusesUseCaseTest { CryptoQualifiedClientId("clientId", USER_ID.toCrypto()) private val CONVERSATION_ID = ConversationId("conversation_value", "domain") - private val WIRE_IDENTITY = - WireIdentity( - CRYPTO_QUALIFIED_CLIENT_ID, - "user_handle", - "User Test", - "domain.com", - "certificate", - CryptoCertificateStatus.VALID, - "thumbprint", - "serialNumber", - endTimestampSeconds = 1899105093 + private val WIRE_IDENTITY = WireIdentity( + CRYPTO_QUALIFIED_CLIENT_ID, + status = CryptoCertificateStatus.VALID, + thumbprint = "thumbprint", + credentialType = CredentialType.X509, + x509Identity = WireIdentity.X509Identity( + WireIdentity.Handle( + scheme = "wireapp", + handle = "userHandle", + domain = "domain1" + ), + displayName = "user displayName", + domain = "domain.com", + certificate = "cert1", + serialNumber = "serial1", + notBefore = Instant.DISTANT_PAST.epochSeconds, + notAfter = Instant.DISTANT_FUTURE.epochSeconds ) + ) } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiAllCertificateStatusesUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiAllCertificateStatusesUseCaseTest.kt index b3289e3430d..1d5a2934bff 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiAllCertificateStatusesUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/GetUserE2eiAllCertificateStatusesUseCaseTest.kt @@ -17,6 +17,7 @@ */ package com.wire.kalium.logic.feature.e2ei +import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.WireIdentity @@ -30,9 +31,11 @@ import com.wire.kalium.logic.util.arrangement.mls.IsE2EIEnabledUseCaseArrangemen import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArrangement import com.wire.kalium.logic.util.arrangement.mls.MLSConversationRepositoryArrangementImpl import io.mockative.any +import io.mockative.verify 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.assertTrue @@ -85,9 +88,9 @@ class GetUserE2eiAllCertificateStatusesUseCaseTest { val result = getUserE2eiAllCertificateStatuses(USER_ID) assertEquals(3, result.size) - assertEquals(CertificateStatus.VALID, result[identity1.clientId.value]?.status) - assertEquals(CertificateStatus.EXPIRED, result[identity2.clientId.value]?.status) - assertEquals(CertificateStatus.REVOKED, result[identity3.clientId.value]?.status) + assertEquals(MLSClientE2EIStatus.VALID, result[identity1.clientId.value]?.e2eiStatus) + assertEquals(MLSClientE2EIStatus.EXPIRED, result[identity2.clientId.value]?.e2eiStatus) + assertEquals(MLSClientE2EIStatus.REVOKED, result[identity3.clientId.value]?.e2eiStatus) } @Test @@ -130,17 +133,22 @@ class GetUserE2eiAllCertificateStatusesUseCaseTest { private val USER_ID = UserId("value", "domain") private val CRYPTO_QUALIFIED_CLIENT_ID = CryptoQualifiedClientId("clientId", USER_ID.toCrypto()) - private val WIRE_IDENTITY = - WireIdentity( - CRYPTO_QUALIFIED_CLIENT_ID, - "user_handle", - "User Test", - "domain.com", - "certificate", - CryptoCertificateStatus.VALID, - "thumbprint", - "serialNumber", - 1899105093 + private val WIRE_IDENTITY = WireIdentity( + CRYPTO_QUALIFIED_CLIENT_ID, + status = CryptoCertificateStatus.VALID, + thumbprint = "thumbprint", + credentialType = CredentialType.X509, + x509Identity = WireIdentity.X509Identity( + WireIdentity.Handle( + scheme = "wireapp", handle = "userHandle", domain = "domain1" + ), + displayName = "user displayName", + domain = "domain.com", + certificate = "cert1", + serialNumber = "serial1", + notBefore = Instant.DISTANT_PAST.epochSeconds, + notAfter = Instant.DISTANT_FUTURE.epochSeconds ) + ) } } 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 a430555560b..451d60e8224 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 @@ -17,14 +17,14 @@ */ package com.wire.kalium.logic.feature.e2ei +import com.wire.kalium.cryptography.CredentialType 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.id.toCrypto import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusResult -import com.wire.kalium.logic.feature.e2ei.usecase.GetUserE2eiCertificateStatusUseCaseImpl +import com.wire.kalium.logic.feature.e2ei.usecase.IsOtherUserE2EIVerifiedUseCaseImpl import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.util.arrangement.mls.IsE2EIEnabledUseCaseArrangement import com.wire.kalium.logic.util.arrangement.mls.IsE2EIEnabledUseCaseArrangementImpl @@ -34,8 +34,10 @@ 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 class GetUserE2eiCertificateStatusUseCaseTest { @@ -50,7 +52,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val result = getUserE2eiCertificateStatus(USER_ID) - assertEquals(GetUserE2eiCertificateStatusResult.Failure.NotActivated, result) + assertFalse(result) } @Test @@ -63,7 +65,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val result = getUserE2eiCertificateStatus(USER_ID) - assertEquals(GetUserE2eiCertificateStatusResult.Failure.NotActivated, result) + assertFalse(result) } @Test @@ -83,11 +85,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val result = getUserE2eiCertificateStatus(USER_ID) - assertTrue { result is GetUserE2eiCertificateStatusResult.Success } - assertEquals( - CertificateStatus.EXPIRED, - (result as GetUserE2eiCertificateStatusResult.Success).status - ) + assertFalse(result) } @Test @@ -107,11 +105,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val result = getUserE2eiCertificateStatus(USER_ID) - assertTrue { result is GetUserE2eiCertificateStatusResult.Success } - assertEquals( - CertificateStatus.REVOKED, - (result as GetUserE2eiCertificateStatusResult.Success).status - ) + assertFalse(result) } @Test @@ -131,11 +125,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { val result = getUserE2eiCertificateStatus(USER_ID) - assertTrue { result is GetUserE2eiCertificateStatusResult.Success } - assertEquals( - CertificateStatus.REVOKED, - (result as GetUserE2eiCertificateStatusResult.Success).status - ) + assertFalse(result) } @Test @@ -150,10 +140,8 @@ class GetUserE2eiCertificateStatusUseCaseTest { val result = getUserE2eiCertificateStatus(USER_ID) // then - assertEquals( - GetUserE2eiCertificateStatusResult.Failure.NotActivated, - result - ) + assertFalse(result) + coVerify { arrangement.mlsConversationRepository.getUserIdentity(any()) }.wasNotInvoked() @@ -165,7 +153,7 @@ class GetUserE2eiCertificateStatusUseCaseTest { fun arrange() = run { runBlocking { block() } - this@Arrangement to GetUserE2eiCertificateStatusUseCaseImpl( + this@Arrangement to IsOtherUserE2EIVerifiedUseCaseImpl( mlsConversationRepository = mlsConversationRepository, isE2EIEnabledUseCase = isE2EIEnabledUseCase ) @@ -178,17 +166,24 @@ class GetUserE2eiCertificateStatusUseCaseTest { private val USER_ID = UserId("value", "domain") private val CRYPTO_QUALIFIED_CLIENT_ID = CryptoQualifiedClientId("clientId", USER_ID.toCrypto()) - private val WIRE_IDENTITY = - WireIdentity( - CRYPTO_QUALIFIED_CLIENT_ID, - "user_handle", - "User Test", - "domain.com", - "certificate", - CryptoCertificateStatus.VALID, - "thumbprint", - "serialNumber", - 1899105093 + private val WIRE_IDENTITY = WireIdentity( + CRYPTO_QUALIFIED_CLIENT_ID, + status = CryptoCertificateStatus.VALID, + thumbprint = "thumbprint", + credentialType = CredentialType.X509, + x509Identity = WireIdentity.X509Identity( + WireIdentity.Handle( + scheme = "wireapp", + handle = "userHandle", + domain = "domain1" + ), + displayName = "user displayName", + domain = "domain.com", + certificate = "cert1", + serialNumber = "serial1", + notBefore = Instant.DISTANT_PAST.epochSeconds, + notAfter = Instant.DISTANT_FUTURE.epochSeconds ) + ) } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCaseTest.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCaseTest.kt index 9e73cd53469..b815c0e558d 100644 --- a/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCaseTest.kt +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/feature/e2ei/usecase/FetchMLSVerificationStatusUseCaseTest.kt @@ -17,6 +17,7 @@ */ package com.wire.kalium.logic.feature.conversation +import com.wire.kalium.cryptography.CredentialType import com.wire.kalium.cryptography.CryptoCertificateStatus import com.wire.kalium.cryptography.CryptoQualifiedClientId import com.wire.kalium.cryptography.E2EIConversationState @@ -32,6 +33,7 @@ import com.wire.kalium.logic.data.id.QualifiedID import com.wire.kalium.logic.data.id.toCrypto import com.wire.kalium.logic.data.message.Message import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.conversation.FetchMLSVerificationStatusUseCaseTest.Arrangement.Companion.getMockedIdentity import com.wire.kalium.logic.feature.e2ei.usecase.FetchMLSVerificationStatusUseCaseImpl import com.wire.kalium.logic.framework.TestConversation import com.wire.kalium.logic.framework.TestConversationDetails @@ -40,16 +42,10 @@ import com.wire.kalium.logic.functional.Either import com.wire.kalium.logic.kaliumLogger import com.wire.kalium.logic.util.arrangement.repository.ConversationRepositoryArrangement import com.wire.kalium.logic.util.arrangement.repository.ConversationRepositoryArrangementImpl -import com.wire.kalium.logic.util.arrangement.repository.MLSConversationRepositoryArrangement -import com.wire.kalium.logic.util.arrangement.repository.MLSConversationRepositoryArrangementImpl import com.wire.kalium.logic.util.arrangement.repository.UserRepositoryArrangement import com.wire.kalium.logic.util.arrangement.repository.UserRepositoryArrangementImpl import com.wire.kalium.logic.util.arrangement.usecase.PersistMessageUseCaseArrangement import com.wire.kalium.logic.util.arrangement.usecase.PersistMessageUseCaseArrangementImpl -import com.wire.kalium.persistence.dao.UserIDEntity -import com.wire.kalium.persistence.dao.conversation.ConversationEntity -import com.wire.kalium.persistence.dao.conversation.EpochChangesDataEntity -import com.wire.kalium.persistence.dao.conversation.NameAndHandleEntity import io.mockative.Mock import io.mockative.any import io.mockative.coEvery @@ -57,11 +53,9 @@ import io.mockative.coVerify import io.mockative.eq import io.mockative.mock import io.mockative.once -import io.mockative.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Instant import kotlin.test.Test class FetchMLSVerificationStatusUseCaseTest { @@ -162,36 +156,10 @@ class FetchMLSVerificationStatusUseCaseTest { val ccMembersIdentity: Map> = mapOf( user1.first to listOf( - WireIdentity( - CryptoQualifiedClientId( - value = "client_user_1", - userId = user1.first.toCrypto() - ), - handle = user1.second.handle!!, - displayName = user1.second.name!!, - certificate = "cert1", - domain = "domain1", - serialNumber = "serial1", - status = CryptoCertificateStatus.VALID, - thumbprint = "thumbprint1", - endTimestampSeconds = 1899105093 - ) + getMockedIdentity(user1.first, user1.second) ), user2.first to listOf( - WireIdentity( - CryptoQualifiedClientId( - value = "client_user_2", - userId = user2.first.toCrypto() - ), - handle = user2.second.handle!!, - displayName = user2.second.name!!, - certificate = "cert2", - domain = "domain2", - serialNumber = "serial2", - status = CryptoCertificateStatus.VALID, - thumbprint = "thumbprint2", - endTimestampSeconds = 1899105093 - ) + getMockedIdentity(user2.first, user2.second) ) ) val (arrangement, handler) = arrange { @@ -233,36 +201,10 @@ class FetchMLSVerificationStatusUseCaseTest { val ccMembersIdentity: Map> = mapOf( user1.first to listOf( - WireIdentity( - CryptoQualifiedClientId( - value = "client_user_1", - userId = user1.first.toCrypto() - ), - handle = user1.second.handle!! + "1", // this user name changed - displayName = user1.second.name!! + "1", - certificate = "cert1", - domain = "domain1", - serialNumber = "serial1", - status = CryptoCertificateStatus.VALID, - thumbprint = "thumbprint1", - endTimestampSeconds = 1899105093 - ) + getMockedIdentity(user1.first, user1.second, CryptoCertificateStatus.REVOKED) ), user2.first to listOf( - WireIdentity( - CryptoQualifiedClientId( - value = "client_user_2", - userId = user2.first.toCrypto() - ), - handle = user2.second.handle!!, - displayName = user2.second.name!!, - certificate = "cert2", - domain = "domain2", - serialNumber = "serial2", - status = CryptoCertificateStatus.VALID, - thumbprint = "thumbprint2", - endTimestampSeconds = 1899105093 - ) + getMockedIdentity(user2.first, user2.second) ) ) val (arrangement, handler) = arrange { @@ -337,5 +279,34 @@ class FetchMLSVerificationStatusUseCaseTest { mlsConversationRepository = mlsConversationRepository ) } + + companion object { + fun getMockedIdentity( + userId: QualifiedID, + nameAndHandle: NameAndHandle, + status: CryptoCertificateStatus = CryptoCertificateStatus.VALID + ) = WireIdentity( + CryptoQualifiedClientId( + value = userId.value, + userId = userId.toCrypto() + ), + status, + thumbprint = "thumbprint", + credentialType = CredentialType.X509, + x509Identity = WireIdentity.X509Identity( + WireIdentity.Handle( + scheme = "wireapp", + handle = nameAndHandle.handle!!, + domain = userId.domain + ), + displayName = nameAndHandle.name!!, + domain = userId.domain, + certificate = "cert1", + serialNumber = "serial1", + notBefore = Instant.DISTANT_PAST.epochSeconds, + notAfter = Instant.DISTANT_FUTURE.epochSeconds + ) + ) + } } } diff --git a/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestMLSClientIdentity.kt b/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestMLSClientIdentity.kt new file mode 100644 index 00000000000..40f9875e7eb --- /dev/null +++ b/logic/src/commonTest/kotlin/com/wire/kalium/logic/framework/TestMLSClientIdentity.kt @@ -0,0 +1,60 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.framework + +import com.wire.kalium.logic.data.id.QualifiedClientID +import com.wire.kalium.logic.feature.e2ei.Handle +import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus +import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity +import com.wire.kalium.logic.feature.e2ei.MLSCredentialsType +import com.wire.kalium.logic.feature.e2ei.X509Identity +import kotlinx.datetime.Instant + +object TestMLSClientIdentity { + fun getMLSClientIdentityWithE2EI(clientId: QualifiedClientID, status: MLSClientE2EIStatus = MLSClientE2EIStatus.VALID) = + MLSClientIdentity( + clientId = clientId, + status, + thumbprint = "thumbprint", + credentialType = MLSCredentialsType.X509, + x509Identity = getValidX509Identity(clientId, status) + ) + + fun getMLSClientIdentityWithOutE2EI(clientId: QualifiedClientID) = + MLSClientIdentity( + clientId = clientId, + MLSClientE2EIStatus.NOT_ACTIVATED, + thumbprint = "thumbprint", + credentialType = MLSCredentialsType.BASIC, + x509Identity = null + ) + + fun getValidX509Identity(clientId: QualifiedClientID, status: MLSClientE2EIStatus) = X509Identity( + Handle( + scheme = "wireapp", + handle = "user_handle", + domain = clientId.userId.domain + ), + displayName = "User Test", + domain = clientId.userId.domain, + certificate = "certificate", + serialNumber = "serialNumber", + notBefore = Instant.DISTANT_PAST, + notAfter = if (status == MLSClientE2EIStatus.EXPIRED) Instant.DISTANT_PAST else Instant.DISTANT_FUTURE + ) +}