Skip to content

Commit 6fe0002

Browse files
committed
Clean room shield update logic
1 parent f576437 commit 6fe0002

File tree

12 files changed

+379
-99
lines changed

12 files changed

+379
-99
lines changed

matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,23 @@ class CommonTestHelper internal constructor(context: Context, val cryptoConfig:
9999
}
100100
}
101101
}
102+
103+
@OptIn(ExperimentalCoroutinesApi::class)
104+
internal fun runLongCryptoTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CryptoTestHelper, CommonTestHelper) -> Unit) {
105+
val testHelper = CommonTestHelper(context, cryptoConfig)
106+
val cryptoTestHelper = CryptoTestHelper(testHelper)
107+
return runTest(dispatchTimeoutMs = TestConstants.timeOutMillis * 4) {
108+
try {
109+
withContext(Dispatchers.Default) {
110+
block(cryptoTestHelper, testHelper)
111+
}
112+
} finally {
113+
if (autoSignoutOnClose) {
114+
testHelper.cleanUpOpenedSessions()
115+
}
116+
}
117+
}
118+
}
102119
}
103120

104121
internal val matrix: TestMatrix
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2023 The Matrix.org Foundation C.I.C.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.matrix.android.sdk.internal.crypto
18+
19+
import android.util.Log
20+
import androidx.lifecycle.Observer
21+
import androidx.test.filters.LargeTest
22+
import kotlinx.coroutines.DelicateCoroutinesApi
23+
import kotlinx.coroutines.Dispatchers
24+
import kotlinx.coroutines.GlobalScope
25+
import kotlinx.coroutines.launch
26+
import kotlinx.coroutines.withContext
27+
import org.junit.FixMethodOrder
28+
import org.junit.Test
29+
import org.junit.runner.RunWith
30+
import org.junit.runners.JUnit4
31+
import org.junit.runners.MethodSorters
32+
import org.matrix.android.sdk.InstrumentedTest
33+
import org.matrix.android.sdk.api.session.Session
34+
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
35+
import org.matrix.android.sdk.api.session.getRoom
36+
import org.matrix.android.sdk.api.session.room.model.RoomSummary
37+
import org.matrix.android.sdk.api.util.Optional
38+
import org.matrix.android.sdk.common.CommonTestHelper
39+
import org.matrix.android.sdk.common.SessionTestParams
40+
import org.matrix.android.sdk.common.TestConstants
41+
import java.util.concurrent.CountDownLatch
42+
import java.util.concurrent.TimeUnit
43+
44+
@RunWith(JUnit4::class)
45+
@FixMethodOrder(MethodSorters.JVM)
46+
@LargeTest
47+
class RoomShieldTest : InstrumentedTest {
48+
49+
@Test
50+
fun testShieldNoVerification() = CommonTestHelper.runCryptoTest(context()) { cryptoTestHelper, _ ->
51+
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
52+
53+
val roomId = testData.roomId
54+
55+
cryptoTestHelper.initializeCrossSigning(testData.firstSession)
56+
cryptoTestHelper.initializeCrossSigning(testData.secondSession!!)
57+
58+
// Test are flaky unless I use liveData observer on main thread
59+
// Just calling getRoomSummary() with retryWithBackOff keeps an outdated version of the value
60+
testData.firstSession.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Default)
61+
testData.secondSession!!.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Default)
62+
}
63+
64+
@Test
65+
fun testShieldInOneOne() = CommonTestHelper.runLongCryptoTest(context()) { cryptoTestHelper, testHelper ->
66+
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
67+
68+
val roomId = testData.roomId
69+
70+
Log.v("#E2E TEST", "Initialize cross signing...")
71+
cryptoTestHelper.initializeCrossSigning(testData.firstSession)
72+
cryptoTestHelper.initializeCrossSigning(testData.secondSession!!)
73+
Log.v("#E2E TEST", "... Initialized.")
74+
75+
// let alive and bob verify
76+
Log.v("#E2E TEST", "Alice and Bob verify each others...")
77+
cryptoTestHelper.verifySASCrossSign(testData.firstSession, testData.secondSession!!, testData.roomId)
78+
79+
// Add a new session for bob
80+
// This session will be unverified for now
81+
82+
Log.v("#E2E TEST", "Log in a new bob device...")
83+
val bobSecondSession = testHelper.logIntoAccount(testData.secondSession!!.myUserId, SessionTestParams(true))
84+
85+
Log.v("#E2E TEST", "Bob session logged in ${bobSecondSession.myUserId.take(6)}")
86+
87+
Log.v("#E2E TEST", "Assert room shields...")
88+
testData.firstSession.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Warning)
89+
// in 1:1 we ignore our own status
90+
testData.secondSession!!.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Trusted)
91+
92+
// Adding another user should make bob consider his devices now and see same shield as alice
93+
Log.v("#E2E TEST", "Create Sam account")
94+
val samSession = testHelper.createAccount(TestConstants.USER_SAM, SessionTestParams(withInitialSync = true))
95+
96+
// Let alice invite sam
97+
Log.v("#E2E TEST", "Let alice invite sam")
98+
testData.firstSession.getRoom(roomId)!!.membershipService().invite(samSession.myUserId)
99+
testHelper.waitForAndAcceptInviteInRoom(samSession, roomId)
100+
101+
Log.v("#E2E TEST", "Assert room shields...")
102+
testData.firstSession.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Warning)
103+
testData.secondSession!!.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Warning)
104+
105+
// Now let's bob verify his session
106+
107+
Log.v("#E2E TEST", "Bob verifies his new session")
108+
cryptoTestHelper.verifyNewSession(testData.secondSession!!, bobSecondSession)
109+
110+
testData.firstSession.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Trusted)
111+
testData.secondSession!!.assertRoomShieldIs(roomId, RoomEncryptionTrustLevel.Trusted)
112+
}
113+
114+
@OptIn(DelicateCoroutinesApi::class)
115+
private suspend fun Session.assertRoomShieldIs(roomId: String, state: RoomEncryptionTrustLevel?) {
116+
val lock = CountDownLatch(1)
117+
val roomLiveData = withContext(Dispatchers.Main) {
118+
roomService().getRoomSummaryLive(roomId)
119+
}
120+
val observer = object : Observer<Optional<RoomSummary>> {
121+
override fun onChanged(value: Optional<RoomSummary>) {
122+
Log.v("#E2E TEST ${this@assertRoomShieldIs.myUserId.take(6)}", "Shield Update ${value.getOrNull()?.roomEncryptionTrustLevel}")
123+
if (value.getOrNull()?.roomEncryptionTrustLevel == state) {
124+
lock.countDown()
125+
roomLiveData.removeObserver(this)
126+
}
127+
}
128+
}
129+
GlobalScope.launch(Dispatchers.Main) { roomLiveData.observeForever(observer) }
130+
131+
lock.await(40_000, TimeUnit.MILLISECONDS)
132+
}
133+
}

matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ import org.matrix.android.sdk.internal.di.SessionId
4949
import org.matrix.android.sdk.internal.di.UserId
5050
import org.matrix.android.sdk.internal.di.WorkManagerProvider
5151
import org.matrix.android.sdk.internal.session.SessionScope
52-
import org.matrix.android.sdk.internal.task.TaskExecutor
5352
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
5453
import org.matrix.android.sdk.internal.util.logLimit
5554
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
@@ -66,7 +65,6 @@ internal class DefaultCrossSigningService @Inject constructor(
6665
private val deviceListManager: DeviceListManager,
6766
private val initializeCrossSigningTask: InitializeCrossSigningTask,
6867
private val uploadSignaturesTask: UploadSignaturesTask,
69-
private val taskExecutor: TaskExecutor,
7068
private val coroutineDispatchers: MatrixCoroutineDispatchers,
7169
private val cryptoCoroutineScope: CoroutineScope,
7270
private val workManagerProvider: WorkManagerProvider,
@@ -612,9 +610,7 @@ internal class DefaultCrossSigningService @Inject constructor(
612610
withContext(coroutineDispatchers.crypto) {
613611
// This device should be yours
614612
val device = cryptoStore.getUserDevice(myUserId, deviceId)
615-
if (device == null) {
616-
throw IllegalArgumentException("This device [$deviceId] is not known, or not yours")
617-
}
613+
?: throw IllegalArgumentException("This device [$deviceId] is not known, or not yours")
618614

619615
val myKeys = getUserCrossSigningKeys(myUserId)
620616
?: throw Throwable("CrossSigning is not setup for this account")

0 commit comments

Comments
 (0)