mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
fix broken test, userId confustion
cleaning cleaning add tests cleaning
This commit is contained in:
parent
c8f0792997
commit
ac0e5e9dec
7 changed files with 442 additions and 59 deletions
|
@ -365,7 +365,7 @@ class CryptoTestHelper(val testHelper: CommonTestHelper) {
|
|||
}
|
||||
|
||||
testHelper.retryPeriodically {
|
||||
alice.cryptoService().crossSigningService().isUserTrusted(bob.myUserId)
|
||||
bob.cryptoService().crossSigningService().isUserTrusted(alice.myUserId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.junit.Assert.assertNull
|
|||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.MethodSorters
|
||||
|
@ -34,6 +33,7 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
|
|||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.UserTrustResult
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.isCrossSignedVerified
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
|
@ -48,7 +48,6 @@ import kotlin.coroutines.resume
|
|||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
@LargeTest
|
||||
@Ignore
|
||||
class XSigningTest : InstrumentedTest {
|
||||
|
||||
@Test
|
||||
|
@ -214,4 +213,101 @@ class XSigningTest : InstrumentedTest {
|
|||
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
|
||||
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWarnOnCrossSigningReset() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
|
||||
val aliceSession = cryptoTestData.firstSession
|
||||
val bobSession = cryptoTestData.secondSession
|
||||
|
||||
val aliceAuthParams = UserPasswordAuth(
|
||||
user = aliceSession.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
val bobAuthParams = UserPasswordAuth(
|
||||
user = bobSession!!.myUserId,
|
||||
password = TestConstants.PASSWORD
|
||||
)
|
||||
|
||||
testHelper.doSync<Unit> {
|
||||
aliceSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(aliceAuthParams)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
testHelper.doSync<Unit> {
|
||||
bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(bobAuthParams)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
cryptoTestHelper.verifySASCrossSign(aliceSession, bobSession, cryptoTestData.roomId)
|
||||
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
aliceSession.cryptoService().crossSigningService().isUserTrusted(bobSession.myUserId)
|
||||
}
|
||||
}
|
||||
|
||||
aliceSession.cryptoService().crossSigningService().checkUserTrust(bobSession.myUserId).let {
|
||||
assertTrue(it is UserTrustResult.Success)
|
||||
}
|
||||
|
||||
val currentBobMSK = aliceSession.cryptoService().crossSigningService()
|
||||
.getUserCrossSigningKeys(bobSession.myUserId)!!
|
||||
.masterKey()!!.unpaddedBase64PublicKey!!
|
||||
|
||||
testHelper.doSync<Unit> {
|
||||
bobSession.cryptoService().crossSigningService().initializeCrossSigning(object : UserInteractiveAuthInterceptor {
|
||||
override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation<UIABaseAuth>) {
|
||||
promise.resume(bobAuthParams)
|
||||
}
|
||||
}, it)
|
||||
}
|
||||
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
val newBobMsk = aliceSession.cryptoService().crossSigningService()
|
||||
.getUserCrossSigningKeys(bobSession.myUserId)
|
||||
?.masterKey()?.unpaddedBase64PublicKey
|
||||
newBobMsk != null && newBobMsk != currentBobMSK
|
||||
}
|
||||
}
|
||||
|
||||
// trick to force event to sync
|
||||
bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userIsTyping()
|
||||
|
||||
// assert that bob is not trusted anymore from alice s
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
val trust = aliceSession.cryptoService().crossSigningService().checkUserTrust(bobSession.myUserId)
|
||||
!trust.isVerified()
|
||||
}
|
||||
}
|
||||
|
||||
// trick to force event to sync
|
||||
bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userStopsTyping()
|
||||
bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userIsTyping()
|
||||
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
val info = aliceSession.cryptoService().crossSigningService().getUserCrossSigningKeys(bobSession.myUserId)
|
||||
info?.wasTrustedOnce == true
|
||||
}
|
||||
}
|
||||
|
||||
// trick to force event to sync
|
||||
bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userStopsTyping()
|
||||
bobSession.roomService().getRoom(cryptoTestData.roomId)!!.typingService().userIsTyping()
|
||||
|
||||
testHelper.waitWithLatch {
|
||||
testHelper.retryPeriodicallyWithLatch(it) {
|
||||
!aliceSession.cryptoService().crossSigningService().isUserTrusted(bobSession.myUserId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ import javax.inject.Inject
|
|||
|
||||
@SessionScope
|
||||
internal class DefaultCrossSigningService @Inject constructor(
|
||||
@UserId private val userId: String,
|
||||
@UserId private val myUserId: String,
|
||||
@SessionId private val sessionId: String,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
|
@ -127,7 +127,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}
|
||||
|
||||
// Recover local trust in case private key are there?
|
||||
setUserKeysAsTrusted(userId, checkUserTrust(userId).isVerified())
|
||||
setUserKeysAsTrusted(myUserId, checkUserTrust(myUserId).isVerified())
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// Mmm this kind of a big issue
|
||||
|
@ -168,12 +168,12 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
|
||||
override fun onSuccess(data: InitializeCrossSigningTask.Result) {
|
||||
val crossSigningInfo = MXCrossSigningInfo(
|
||||
userId,
|
||||
myUserId,
|
||||
listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo),
|
||||
true
|
||||
)
|
||||
cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
|
||||
setUserKeysAsTrusted(userId, true)
|
||||
setUserKeysAsTrusted(myUserId, true)
|
||||
cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK)
|
||||
crossSigningOlm.masterPkSigning = OlmPkSigning().apply { initWithSeed(data.masterKeyPK.fromBase64()) }
|
||||
crossSigningOlm.userPkSigning = OlmPkSigning().apply { initWithSeed(data.userKeyPK.fromBase64()) }
|
||||
|
@ -270,7 +270,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
uskKeyPrivateKey: String?,
|
||||
sskPrivateKey: String?
|
||||
): UserTrustResult {
|
||||
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(userId)
|
||||
val mxCrossSigningInfo = getMyCrossSigningKeys() ?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
var masterKeyIsTrusted = false
|
||||
var userKeyIsTrusted = false
|
||||
|
@ -334,7 +334,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
val checkSelfTrust = checkSelfTrust()
|
||||
if (checkSelfTrust.isVerified()) {
|
||||
cryptoStore.storePrivateKeysInfo(masterKeyPrivateKey, uskKeyPrivateKey, sskPrivateKey)
|
||||
setUserKeysAsTrusted(userId, true)
|
||||
setUserKeysAsTrusted(myUserId, true)
|
||||
}
|
||||
return checkSelfTrust
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
* .
|
||||
*/
|
||||
override fun isUserTrusted(otherUserId: String): Boolean {
|
||||
return cryptoStore.getCrossSigningInfo(userId)?.isTrusted() == true
|
||||
return cryptoStore.getCrossSigningInfo(otherUserId)?.isTrusted() == true
|
||||
}
|
||||
|
||||
override fun isCrossSigningVerified(): Boolean {
|
||||
|
@ -367,7 +367,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
*/
|
||||
override fun checkUserTrust(otherUserId: String): UserTrustResult {
|
||||
Timber.v("## CrossSigning checkUserTrust for $otherUserId")
|
||||
if (otherUserId == userId) {
|
||||
if (otherUserId == myUserId) {
|
||||
return checkSelfTrust()
|
||||
}
|
||||
// I trust a user if I trust his master key
|
||||
|
@ -375,16 +375,14 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
// TODO what if the master key is signed by a device key that i have verified
|
||||
|
||||
// First let's get my user key
|
||||
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
|
||||
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(myUserId)
|
||||
|
||||
checkOtherMSKTrusted(myCrossSigningInfo, cryptoStore.getCrossSigningInfo(otherUserId))
|
||||
|
||||
return UserTrustResult.Success
|
||||
return checkOtherMSKTrusted(myCrossSigningInfo, cryptoStore.getCrossSigningInfo(otherUserId))
|
||||
}
|
||||
|
||||
fun checkOtherMSKTrusted(myCrossSigningInfo: MXCrossSigningInfo?, otherInfo: MXCrossSigningInfo?): UserTrustResult {
|
||||
val myUserKey = myCrossSigningInfo?.userKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(userId)
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
if (!myCrossSigningInfo.isTrusted()) {
|
||||
return UserTrustResult.KeysNotTrusted(myCrossSigningInfo)
|
||||
|
@ -395,7 +393,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
?: return UserTrustResult.UnknownCrossSignatureInfo(otherInfo?.userId ?: "")
|
||||
|
||||
val masterKeySignaturesMadeByMyUserKey = otherMasterKey.signatures
|
||||
?.get(userId) // Signatures made by me
|
||||
?.get(myUserId) // Signatures made by me
|
||||
?.get("ed25519:${myUserKey.unpaddedBase64PublicKey}")
|
||||
|
||||
if (masterKeySignaturesMadeByMyUserKey.isNullOrBlank()) {
|
||||
|
@ -421,9 +419,9 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
// Special case when it's me,
|
||||
// I have to check that MSK -> USK -> SSK
|
||||
// and that MSK is trusted (i know the private key, or is signed by a trusted device)
|
||||
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
|
||||
val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(myUserId)
|
||||
|
||||
return checkSelfTrust(myCrossSigningInfo, cryptoStore.getUserDeviceList(userId))
|
||||
return checkSelfTrust(myCrossSigningInfo, cryptoStore.getUserDeviceList(myUserId))
|
||||
}
|
||||
|
||||
fun checkSelfTrust(myCrossSigningInfo: MXCrossSigningInfo?, myDevices: List<CryptoDeviceInfo>?): UserTrustResult {
|
||||
|
@ -433,7 +431,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
// val myCrossSigningInfo = cryptoStore.getCrossSigningInfo(userId)
|
||||
|
||||
val myMasterKey = myCrossSigningInfo?.masterKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(userId)
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
// Is the master key trusted
|
||||
// 1) check if I know the private key
|
||||
|
@ -457,7 +455,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
olmPkSigning?.releaseSigning()
|
||||
} else {
|
||||
// Maybe it's signed by a locally trusted device?
|
||||
myMasterKey.signatures?.get(userId)?.forEach { (key, value) ->
|
||||
myMasterKey.signatures?.get(myUserId)?.forEach { (key, value) ->
|
||||
val potentialDeviceId = key.removePrefix("ed25519:")
|
||||
val potentialDevice = myDevices?.firstOrNull { it.deviceId == potentialDeviceId } // cryptoStore.getUserDevice(userId, potentialDeviceId)
|
||||
if (potentialDevice != null && potentialDevice.isVerified) {
|
||||
|
@ -479,14 +477,14 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}
|
||||
|
||||
val myUserKey = myCrossSigningInfo.userKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(userId)
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
val userKeySignaturesMadeByMyMasterKey = myUserKey.signatures
|
||||
?.get(userId) // Signatures made by me
|
||||
?.get(myUserId) // Signatures made by me
|
||||
?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
|
||||
|
||||
if (userKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
|
||||
Timber.d("## CrossSigning checkUserTrust false for $userId, USK not signed by MSK")
|
||||
Timber.d("## CrossSigning checkUserTrust false for $myUserId, USK not signed by MSK")
|
||||
return UserTrustResult.KeyNotSigned(myUserKey)
|
||||
}
|
||||
|
||||
|
@ -502,14 +500,14 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}
|
||||
|
||||
val mySSKey = myCrossSigningInfo.selfSigningKey()
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(userId)
|
||||
?: return UserTrustResult.CrossSigningNotConfigured(myUserId)
|
||||
|
||||
val ssKeySignaturesMadeByMyMasterKey = mySSKey.signatures
|
||||
?.get(userId) // Signatures made by me
|
||||
?.get(myUserId) // Signatures made by me
|
||||
?.get("ed25519:${myMasterKey.unpaddedBase64PublicKey}")
|
||||
|
||||
if (ssKeySignaturesMadeByMyMasterKey.isNullOrBlank()) {
|
||||
Timber.d("## CrossSigning checkUserTrust false for $userId, SSK not signed by MSK")
|
||||
Timber.d("## CrossSigning checkUserTrust false for $myUserId, SSK not signed by MSK")
|
||||
return UserTrustResult.KeyNotSigned(mySSKey)
|
||||
}
|
||||
|
||||
|
@ -559,14 +557,14 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
|
||||
override fun trustUser(otherUserId: String, callback: MatrixCallback<Unit>) {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
Timber.d("## CrossSigning - Mark user $userId as trusted ")
|
||||
Timber.d("## CrossSigning - Mark user $otherUserId as trusted ")
|
||||
// We should have this user keys
|
||||
val otherMasterKeys = getUserCrossSigningKeys(otherUserId)?.masterKey()
|
||||
if (otherMasterKeys == null) {
|
||||
callback.onFailure(Throwable("## CrossSigning - Other master signing key is not known"))
|
||||
return@launch
|
||||
}
|
||||
val myKeys = getUserCrossSigningKeys(userId)
|
||||
val myKeys = getUserCrossSigningKeys(myUserId)
|
||||
if (myKeys == null) {
|
||||
callback.onFailure(Throwable("## CrossSigning - CrossSigning is not setup for this account"))
|
||||
return@launch
|
||||
|
@ -592,9 +590,9 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
cryptoStore.setUserKeysAsTrusted(otherUserId, true)
|
||||
// TODO update local copy with new signature directly here? kind of local echo of trust?
|
||||
|
||||
Timber.d("## CrossSigning - Upload signature of $userId MSK signed by USK")
|
||||
Timber.d("## CrossSigning - Upload signature of $otherUserId MSK signed by USK")
|
||||
val uploadQuery = UploadSignatureQueryBuilder()
|
||||
.withSigningKeyInfo(otherMasterKeys.copyForSignature(userId, userPubKey, newSignature))
|
||||
.withSigningKeyInfo(otherMasterKeys.copyForSignature(myUserId, userPubKey, newSignature))
|
||||
.build()
|
||||
uploadSignaturesTask.configureWith(UploadSignaturesTask.Params(uploadQuery)) {
|
||||
this.executionThread = TaskThread.CRYPTO
|
||||
|
@ -608,20 +606,20 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
cryptoStore.markMyMasterKeyAsLocallyTrusted(true)
|
||||
checkSelfTrust()
|
||||
// re-verify all trusts
|
||||
onUsersDeviceUpdate(listOf(userId))
|
||||
onUsersDeviceUpdate(listOf(myUserId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun trustDevice(deviceId: String, callback: MatrixCallback<Unit>) {
|
||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||
// This device should be yours
|
||||
val device = cryptoStore.getUserDevice(userId, deviceId)
|
||||
val device = cryptoStore.getUserDevice(myUserId, deviceId)
|
||||
if (device == null) {
|
||||
callback.onFailure(IllegalArgumentException("This device [$deviceId] is not known, or not yours"))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val myKeys = getUserCrossSigningKeys(userId)
|
||||
val myKeys = getUserCrossSigningKeys(myUserId)
|
||||
if (myKeys == null) {
|
||||
callback.onFailure(Throwable("CrossSigning is not setup for this account"))
|
||||
return@launch
|
||||
|
@ -643,7 +641,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
}
|
||||
val toUpload = device.copy(
|
||||
signatures = mapOf(
|
||||
userId
|
||||
myUserId
|
||||
to
|
||||
mapOf(
|
||||
"ed25519:$ssPubKey" to newSignature
|
||||
|
@ -665,8 +663,8 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
val otherDevice = cryptoStore.getUserDevice(otherUserId, otherDeviceId)
|
||||
?: return DeviceTrustResult.UnknownDevice(otherDeviceId)
|
||||
|
||||
val myKeys = getUserCrossSigningKeys(userId)
|
||||
?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
|
||||
val myKeys = getUserCrossSigningKeys(myUserId)
|
||||
?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(myUserId))
|
||||
|
||||
if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
|
||||
|
||||
|
@ -721,7 +719,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
|
||||
fun checkDeviceTrust(myKeys: MXCrossSigningInfo?, otherKeys: MXCrossSigningInfo?, otherDevice: CryptoDeviceInfo): DeviceTrustResult {
|
||||
val locallyTrusted = otherDevice.trustLevel?.isLocallyVerified()
|
||||
myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(userId))
|
||||
myKeys ?: return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.CrossSigningNotConfigured(myUserId))
|
||||
|
||||
if (!myKeys.isTrusted()) return legacyFallbackTrust(locallyTrusted, DeviceTrustResult.KeysNotTrusted(myKeys))
|
||||
|
||||
|
@ -809,7 +807,7 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
cryptoStore.setUserKeysAsTrusted(otherUserId, trusted)
|
||||
// If it's me, recheck trust of all users and devices?
|
||||
val users = ArrayList<String>()
|
||||
if (otherUserId == userId && currentTrust != trusted) {
|
||||
if (otherUserId == myUserId && currentTrust != trusted) {
|
||||
// notify key requester
|
||||
outgoingKeyRequestManager.onSelfCrossSigningTrustChanged(trusted)
|
||||
cryptoStore.updateUsersTrust {
|
||||
|
|
|
@ -161,6 +161,7 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
|
|||
// i have all the new trusts, update DB
|
||||
trusts.forEach {
|
||||
val verified = it.value?.isVerified() == true
|
||||
Timber.v("[$myUserId] ## CrossSigning - Updating user trust: ${it.key} to $verified")
|
||||
updateCrossSigningKeysTrust(cryptoRealm, it.key, verified)
|
||||
}
|
||||
|
||||
|
|
|
@ -289,6 +289,7 @@ dependencies {
|
|||
testImplementation libs.tests.junit
|
||||
testImplementation libs.tests.kluent
|
||||
testImplementation libs.mockk.mockk
|
||||
testImplementation libs.androidx.coreTesting
|
||||
// Plant Timber tree for test
|
||||
testImplementation libs.tests.timberJunitRule
|
||||
testImplementation libs.airbnb.mavericksTesting
|
||||
|
|
|
@ -37,6 +37,7 @@ import kotlinx.coroutines.launch
|
|||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
|
@ -116,26 +117,7 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
|||
.map { deviceList ->
|
||||
// If any key change, emit the userIds list
|
||||
deviceList.groupBy { it.userId }.mapValues {
|
||||
val allDeviceTrusted = it.value.fold(it.value.isNotEmpty()) { prev, next ->
|
||||
prev && next.trustLevel?.isCrossSigningVerified().orFalse()
|
||||
}
|
||||
val mxCrossSigningInfo = session.cryptoService().crossSigningService().getUserCrossSigningKeys(it.key)
|
||||
when {
|
||||
mxCrossSigningInfo == null -> {
|
||||
UserVerificationLevel.WAS_NEVER_VERIFIED
|
||||
}
|
||||
mxCrossSigningInfo.isTrusted() -> {
|
||||
if (allDeviceTrusted) UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
|
||||
else UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
|
||||
}
|
||||
else -> {
|
||||
if (mxCrossSigningInfo.wasTrustedOnce) {
|
||||
UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
|
||||
} else {
|
||||
UserVerificationLevel.WAS_NEVER_VERIFIED
|
||||
}
|
||||
}
|
||||
}
|
||||
getUserTrustLevel(it.key, it.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +127,29 @@ class RoomMemberListViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getUserTrustLevel(userId: String, devices: List<CryptoDeviceInfo>): UserVerificationLevel {
|
||||
val allDeviceTrusted = devices.fold(devices.isNotEmpty()) { prev, next ->
|
||||
prev && next.trustLevel?.isCrossSigningVerified().orFalse()
|
||||
}
|
||||
val mxCrossSigningInfo = session.cryptoService().crossSigningService().getUserCrossSigningKeys(userId)
|
||||
return when {
|
||||
mxCrossSigningInfo == null -> {
|
||||
UserVerificationLevel.WAS_NEVER_VERIFIED
|
||||
}
|
||||
mxCrossSigningInfo.isTrusted() -> {
|
||||
if (allDeviceTrusted) UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
|
||||
else UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
|
||||
}
|
||||
else -> {
|
||||
if (mxCrossSigningInfo.wasTrustedOnce) {
|
||||
UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
|
||||
} else {
|
||||
UserVerificationLevel.WAS_NEVER_VERIFIED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observePowerLevel() {
|
||||
PowerLevelsFlowFactory(room).createFlow()
|
||||
.onEach {
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.airbnb.mvrx.test.MvRxTestRule
|
||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
|
||||
import im.vector.app.features.roomprofile.members.RoomMemberListViewState
|
||||
import im.vector.app.features.roomprofile.members.RoomMemberSummaryComparator
|
||||
import im.vector.app.test.test
|
||||
import im.vector.app.test.testCoroutineDispatchers
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.CryptoService
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||
import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.RoomService
|
||||
import org.matrix.android.sdk.api.session.room.crypto.RoomCryptoService
|
||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.state.StateService
|
||||
import org.matrix.android.sdk.api.session.user.UserService
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
|
||||
class MemberListViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mvrxTestRule = MvRxTestRule()
|
||||
|
||||
@get:Rule
|
||||
val instantExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
private val fakeRoomId = "!roomId"
|
||||
private val args = RoomProfileArgs(fakeRoomId)
|
||||
|
||||
private val aliceMxid = "@alice:example.com"
|
||||
private val bobMxid = "@bob:example.com"
|
||||
private val marcMxid = "@marc:example.com"
|
||||
|
||||
private val aliceDevice1 = CryptoDeviceInfo(
|
||||
deviceId = "ALICE_1",
|
||||
userId = aliceMxid,
|
||||
trustLevel = DeviceTrustLevel(true, true)
|
||||
)
|
||||
|
||||
private val aliceDevice2 = CryptoDeviceInfo(
|
||||
deviceId = "ALICE_2",
|
||||
userId = aliceMxid,
|
||||
trustLevel = DeviceTrustLevel(false, false)
|
||||
)
|
||||
|
||||
private val bobDevice1 = CryptoDeviceInfo(
|
||||
deviceId = "BOB_1",
|
||||
userId = bobMxid,
|
||||
trustLevel = DeviceTrustLevel(true, true)
|
||||
)
|
||||
|
||||
private val bobDevice2 = CryptoDeviceInfo(
|
||||
deviceId = "BOB_2",
|
||||
userId = bobMxid,
|
||||
trustLevel = DeviceTrustLevel(true, true)
|
||||
)
|
||||
|
||||
private val markDevice = CryptoDeviceInfo(
|
||||
deviceId = "MARK_1",
|
||||
userId = marcMxid,
|
||||
trustLevel = DeviceTrustLevel(false, true)
|
||||
)
|
||||
|
||||
private val fakeMembershipservice: MembershipService = mockk {
|
||||
|
||||
val memberList = mutableListOf<RoomMemberSummary>(
|
||||
RoomMemberSummary(Membership.JOIN, aliceMxid, displayName = "Alice"),
|
||||
RoomMemberSummary(Membership.JOIN, bobMxid, displayName = "Bob"),
|
||||
RoomMemberSummary(Membership.JOIN, marcMxid, displayName = "marc")
|
||||
)
|
||||
|
||||
every { getRoomMembers(any()) } returns memberList
|
||||
|
||||
every { getRoomMembersLive(any()) } returns MutableLiveData(memberList)
|
||||
|
||||
every { areAllMembersLoadedLive() } returns MutableLiveData(true)
|
||||
|
||||
coEvery { areAllMembersLoaded() } returns true
|
||||
}
|
||||
|
||||
private val fakeRoomCryptoService: RoomCryptoService = mockk {
|
||||
every { isEncrypted() } returns true
|
||||
}
|
||||
private val fakeRoom: Room = mockk {
|
||||
|
||||
val fakeStateService: StateService = mockk {
|
||||
every { getStateEventLive(any(), any()) } returns MutableLiveData()
|
||||
every { getStateEventsLive(any(), any()) } returns MutableLiveData()
|
||||
every { getStateEvent(any(), any()) } returns null
|
||||
}
|
||||
|
||||
every { stateService() } returns fakeStateService
|
||||
|
||||
every { coroutineDispatchers } returns testCoroutineDispatchers
|
||||
|
||||
every { getRoomSummaryLive() } returns MutableLiveData<Optional<RoomSummary>>(Optional(fakeRoomSummary))
|
||||
|
||||
every { membershipService() } returns fakeMembershipservice
|
||||
|
||||
every { roomCryptoService() } returns fakeRoomCryptoService
|
||||
|
||||
every { roomSummary() } returns fakeRoomSummary
|
||||
}
|
||||
|
||||
private val fakeUserService: UserService = mockk {
|
||||
every { getIgnoredUsersLive() } returns MutableLiveData()
|
||||
}
|
||||
|
||||
val fakeSession: Session = mockk {
|
||||
|
||||
val fakeCrossSigningService: CrossSigningService = mockk {
|
||||
every { isUserTrusted(aliceMxid) } returns true
|
||||
every { isUserTrusted(bobMxid) } returns true
|
||||
every { isUserTrusted(marcMxid) } returns false
|
||||
|
||||
every { getUserCrossSigningKeys(aliceMxid) } returns MXCrossSigningInfo(
|
||||
aliceMxid,
|
||||
crossSigningKeys = listOf(
|
||||
CryptoCrossSigningKey(
|
||||
aliceMxid,
|
||||
usages = listOf("master"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(true, true),
|
||||
signatures = emptyMap()
|
||||
),
|
||||
CryptoCrossSigningKey(
|
||||
aliceMxid,
|
||||
usages = listOf("self_signing"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(true, true),
|
||||
signatures = emptyMap()
|
||||
),
|
||||
CryptoCrossSigningKey(
|
||||
aliceMxid,
|
||||
usages = listOf("user_signing"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(true, true),
|
||||
signatures = emptyMap()
|
||||
)
|
||||
),
|
||||
true
|
||||
)
|
||||
every { getUserCrossSigningKeys(bobMxid) } returns MXCrossSigningInfo(
|
||||
aliceMxid,
|
||||
crossSigningKeys = listOf(
|
||||
CryptoCrossSigningKey(
|
||||
bobMxid,
|
||||
usages = listOf("master"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(true, true),
|
||||
signatures = emptyMap()
|
||||
),
|
||||
CryptoCrossSigningKey(
|
||||
bobMxid,
|
||||
usages = listOf("self_signing"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(true, true),
|
||||
signatures = emptyMap()
|
||||
),
|
||||
CryptoCrossSigningKey(
|
||||
bobMxid,
|
||||
usages = listOf("user_signing"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(true, true),
|
||||
signatures = emptyMap()
|
||||
)
|
||||
),
|
||||
true
|
||||
)
|
||||
every { getUserCrossSigningKeys(marcMxid) } returns MXCrossSigningInfo(
|
||||
aliceMxid,
|
||||
crossSigningKeys = listOf(
|
||||
CryptoCrossSigningKey(
|
||||
marcMxid,
|
||||
usages = listOf("master"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(false, false),
|
||||
signatures = emptyMap()
|
||||
),
|
||||
CryptoCrossSigningKey(
|
||||
marcMxid,
|
||||
usages = listOf("self_signing"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(false, false),
|
||||
signatures = emptyMap()
|
||||
),
|
||||
CryptoCrossSigningKey(
|
||||
marcMxid,
|
||||
usages = listOf("user_signing"),
|
||||
keys = emptyMap(),
|
||||
trustLevel = DeviceTrustLevel(false, false),
|
||||
signatures = emptyMap()
|
||||
)
|
||||
),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
val fakeCryptoService: CryptoService = mockk {
|
||||
every { crossSigningService() } returns fakeCrossSigningService
|
||||
|
||||
every {
|
||||
getLiveCryptoDeviceInfo(listOf(aliceMxid, bobMxid, marcMxid))
|
||||
} returns MutableLiveData(
|
||||
listOf(
|
||||
aliceDevice1, aliceDevice2, bobDevice1, bobDevice2, markDevice
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val fakeRoomService: RoomService = mockk {
|
||||
every { getRoom(any()) } returns fakeRoom
|
||||
}
|
||||
every { roomService() } returns fakeRoomService
|
||||
every { userService() } returns fakeUserService
|
||||
every { cryptoService() } returns fakeCryptoService
|
||||
}
|
||||
|
||||
private val fakeRoomSummary = RoomSummary(
|
||||
roomId = fakeRoomId,
|
||||
displayName = "Fake Room",
|
||||
topic = "A topic",
|
||||
isEncrypted = true,
|
||||
encryptionEventTs = 0,
|
||||
typingUsers = emptyList(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testBasicUserVerificationLevels() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel
|
||||
.test()
|
||||
.assertPredicateLatestState {
|
||||
val trustMap = it.trustLevelMap.invoke() ?: return@assertPredicateLatestState false
|
||||
trustMap[aliceMxid] == UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED &&
|
||||
trustMap[bobMxid] == UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED &&
|
||||
trustMap[marcMxid] == UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
|
||||
}
|
||||
.finish()
|
||||
}
|
||||
|
||||
private fun createViewModel(): RoomMemberListViewModel {
|
||||
return RoomMemberListViewModel(
|
||||
RoomMemberListViewState(args),
|
||||
RoomMemberSummaryComparator(),
|
||||
fakeSession,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue