mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 09:25:49 +03:00
Merge branch 'develop' of github.com:vector-im/element-android into michaelk/sonarqube_fixes
This commit is contained in:
commit
d18e7ad001
65 changed files with 1382 additions and 729 deletions
1
changelog.d/5283.wip
Normal file
1
changelog.d/5283.wip
Normal file
|
@ -0,0 +1 @@
|
||||||
|
FTUE - Adds the redesigned Sign In screen
|
1
changelog.d/6073.feature
Normal file
1
changelog.d/6073.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Adds up navigation in spaces
|
1
changelog.d/6077.sdk
Normal file
1
changelog.d/6077.sdk
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Improve replay attacks and reduce duplicate message index errors
|
1
changelog.d/6148.bugfix
Normal file
1
changelog.d/6148.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fix decrypting redacted event from sending errors
|
|
@ -199,7 +199,7 @@ dependencies {
|
||||||
implementation libs.apache.commonsImaging
|
implementation libs.apache.commonsImaging
|
||||||
|
|
||||||
// Phone number https://github.com/google/libphonenumber
|
// Phone number https://github.com/google/libphonenumber
|
||||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.48'
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.49'
|
||||||
|
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||||
|
|
|
@ -24,8 +24,8 @@ import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
|
|
||||||
|
@ -34,32 +34,22 @@ import org.matrix.android.sdk.common.TestConstants
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class AccountCreationTest : InstrumentedTest {
|
class AccountCreationTest : InstrumentedTest {
|
||||||
|
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
|
||||||
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createAccountTest() {
|
fun createAccountTest() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(session)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
fun createAccountAndLoginAgainTest() {
|
fun createAccountAndLoginAgainTest() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||||
|
|
||||||
// Log again to the same account
|
// Log again to the same account
|
||||||
val session2 = commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
|
commonTestHelper.logIntoAccount(session.myUserId, SessionTestParams(withInitialSync = true))
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(session)
|
|
||||||
commonTestHelper.signOutAndClose(session2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun simpleE2eTest() {
|
fun simpleE2eTest() = runCryptoTest(context()) { cryptoTestHelper, _ ->
|
||||||
val res = cryptoTestHelper.doE2ETestWithAliceInARoom()
|
cryptoTestHelper.doE2ETestWithAliceInARoom()
|
||||||
|
|
||||||
res.cleanUp(commonTestHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.junit.runners.JUnit4
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
|
|
||||||
|
@ -34,14 +34,12 @@ import org.matrix.android.sdk.common.TestConstants
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
class ChangePasswordTest : InstrumentedTest {
|
class ChangePasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NEW_PASSWORD = "this is a new password"
|
private const val NEW_PASSWORD = "this is a new password"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun changePasswordTest() {
|
fun changePasswordTest() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false))
|
||||||
|
|
||||||
// Change password
|
// Change password
|
||||||
|
@ -54,9 +52,6 @@ class ChangePasswordTest : InstrumentedTest {
|
||||||
throwable.isInvalidPassword().shouldBeTrue()
|
throwable.isInvalidPassword().shouldBeTrue()
|
||||||
|
|
||||||
// Try to login with the new password, should work
|
// Try to login with the new password, should work
|
||||||
val session2 = commonTestHelper.logIntoAccount(session.myUserId, NEW_PASSWORD, SessionTestParams(withInitialSync = false))
|
commonTestHelper.logIntoAccount(session.myUserId, NEW_PASSWORD, SessionTestParams(withInitialSync = false))
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(session)
|
|
||||||
commonTestHelper.signOutAndClose(session2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.MatrixError
|
import org.matrix.android.sdk.api.failure.MatrixError
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
|
@ -39,10 +39,8 @@ import kotlin.coroutines.resume
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class DeactivateAccountTest : InstrumentedTest {
|
class DeactivateAccountTest : InstrumentedTest {
|
||||||
|
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun deactivateAccountTest() {
|
fun deactivateAccountTest() = runSessionTest(context(), false /* session will be deactivated */) { commonTestHelper ->
|
||||||
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
val session = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true))
|
||||||
|
|
||||||
// Deactivate the account
|
// Deactivate the account
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.junit.runner.RunWith
|
||||||
import org.junit.runners.JUnit4
|
import org.junit.runners.JUnit4
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -32,10 +32,8 @@ import timber.log.Timber
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class ApiInterceptorTest : InstrumentedTest {
|
class ApiInterceptorTest : InstrumentedTest {
|
||||||
|
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun apiInterceptorTest() {
|
fun apiInterceptorTest() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val responses = mutableListOf<String>()
|
val responses = mutableListOf<String>()
|
||||||
|
|
||||||
val listener = object : ApiInterceptorListener {
|
val listener = object : ApiInterceptorListener {
|
||||||
|
|
|
@ -54,12 +54,39 @@ import java.util.concurrent.TimeUnit
|
||||||
* This class exposes methods to be used in common cases
|
* This class exposes methods to be used in common cases
|
||||||
* Registration, login, Sync, Sending messages...
|
* Registration, login, Sync, Sending messages...
|
||||||
*/
|
*/
|
||||||
class CommonTestHelper(context: Context) {
|
class CommonTestHelper private constructor(context: Context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal fun runSessionTest(context: Context, autoSignoutOnClose: Boolean = true, block: (CommonTestHelper) -> Unit) {
|
||||||
|
val testHelper = CommonTestHelper(context)
|
||||||
|
return try {
|
||||||
|
block(testHelper)
|
||||||
|
} finally {
|
||||||
|
if (autoSignoutOnClose) {
|
||||||
|
testHelper.cleanUpOpenedSessions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun runCryptoTest(context: Context, autoSignoutOnClose: Boolean = true, block: (CryptoTestHelper, CommonTestHelper) -> Unit) {
|
||||||
|
val testHelper = CommonTestHelper(context)
|
||||||
|
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
||||||
|
return try {
|
||||||
|
block(cryptoTestHelper, testHelper)
|
||||||
|
} finally {
|
||||||
|
if (autoSignoutOnClose) {
|
||||||
|
testHelper.cleanUpOpenedSessions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal val matrix: TestMatrix
|
internal val matrix: TestMatrix
|
||||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||||
private var accountNumber = 0
|
private var accountNumber = 0
|
||||||
|
|
||||||
|
private val trackedSessions = mutableListOf<Session>()
|
||||||
|
|
||||||
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -84,6 +111,15 @@ class CommonTestHelper(context: Context) {
|
||||||
return logIntoAccount(userId, TestConstants.PASSWORD, testParams)
|
return logIntoAccount(userId, TestConstants.PASSWORD, testParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cleanUpOpenedSessions() {
|
||||||
|
trackedSessions.forEach {
|
||||||
|
runBlockingTest {
|
||||||
|
it.signOutService().signOut(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackedSessions.clear()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a homeserver configuration, with Http connection allowed for test
|
* Create a homeserver configuration, with Http connection allowed for test
|
||||||
*/
|
*/
|
||||||
|
@ -245,7 +281,9 @@ class CommonTestHelper(context: Context) {
|
||||||
testParams
|
testParams
|
||||||
)
|
)
|
||||||
assertNotNull(session)
|
assertNotNull(session)
|
||||||
return session
|
return session.also {
|
||||||
|
trackedSessions.add(session)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -261,7 +299,9 @@ class CommonTestHelper(context: Context) {
|
||||||
testParams: SessionTestParams): Session {
|
testParams: SessionTestParams): Session {
|
||||||
val session = logAccountAndSync(userId, password, testParams)
|
val session = logAccountAndSync(userId, password, testParams)
|
||||||
assertNotNull(session)
|
assertNotNull(session)
|
||||||
return session
|
return session.also {
|
||||||
|
trackedSessions.add(session)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -436,6 +476,7 @@ class CommonTestHelper(context: Context) {
|
||||||
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
|
fun Iterable<Session>.signOutAndClose() = forEach { signOutAndClose(it) }
|
||||||
|
|
||||||
fun signOutAndClose(session: Session) {
|
fun signOutAndClose(session: Session) {
|
||||||
|
trackedSessions.remove(session)
|
||||||
runBlockingTest(timeout = 60_000) {
|
runBlockingTest(timeout = 60_000) {
|
||||||
session.signOutService().signOut(true)
|
session.signOutService().signOut(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ import java.util.UUID
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
class CryptoTestHelper(private val testHelper: CommonTestHelper) {
|
class CryptoTestHelper(val testHelper: CommonTestHelper) {
|
||||||
|
|
||||||
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
|
private val messagesFromAlice: List<String> = listOf("0 - Hello I'm Alice!", "4 - Go!")
|
||||||
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
|
private val messagesFromBob: List<String> = listOf("1 - Hello I'm Bob!", "2 - Isn't life grand?", "3 - Let's go to the opera.")
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.crypto
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
|
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
class DecryptRedactedEventTest : InstrumentedTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun doNotFailToDecryptRedactedEvent() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
|
val e2eRoomID = testData.roomId
|
||||||
|
val aliceSession = testData.firstSession
|
||||||
|
val bobSession = testData.secondSession!!
|
||||||
|
|
||||||
|
val roomALicePOV = aliceSession.getRoom(e2eRoomID)!!
|
||||||
|
val timelineEvent = testHelper.sendTextMessage(roomALicePOV, "Hello", 1).first()
|
||||||
|
val redactionReason = "Wrong Room"
|
||||||
|
roomALicePOV.sendService().redactEvent(timelineEvent.root, redactionReason)
|
||||||
|
|
||||||
|
// get the event from bob
|
||||||
|
testHelper.waitWithLatch {
|
||||||
|
testHelper.retryPeriodicallyWithLatch(it) {
|
||||||
|
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)?.root?.isRedacted() == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val eventBobPov = bobSession.getRoom(e2eRoomID)?.getTimelineEvent(timelineEvent.eventId)!!
|
||||||
|
|
||||||
|
testHelper.runBlockingTest {
|
||||||
|
try {
|
||||||
|
val result = bobSession.cryptoService().decryptEvent(eventBobPov.root, "")
|
||||||
|
Assert.assertEquals(
|
||||||
|
"Unexpected redacted reason",
|
||||||
|
redactionReason,
|
||||||
|
result.clearEvent.toModel<Event>()?.unsignedData?.redactedEvent?.content?.get("reason")
|
||||||
|
)
|
||||||
|
Assert.assertEquals(
|
||||||
|
"Unexpected Redacted event id",
|
||||||
|
timelineEvent.eventId,
|
||||||
|
result.clearEvent.toModel<Event>()?.unsignedData?.redactedEvent?.redacts
|
||||||
|
)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Assert.fail("Should not throw when decrypting a redacted event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,7 +58,8 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.send.SendState
|
import org.matrix.android.sdk.api.session.room.send.SendState
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.RetryTestRule
|
import org.matrix.android.sdk.common.RetryTestRule
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
|
@ -84,9 +85,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
* Alice sends a new message, then check that the new one can be decrypted
|
* Alice sends a new message, then check that the new one can be decrypted
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testSendingE2EEMessages() {
|
fun testSendingE2EEMessages() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -200,21 +199,12 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
otherAccounts.forEach {
|
|
||||||
testHelper.signOutAndClose(it)
|
|
||||||
}
|
|
||||||
newAccount.forEach { testHelper.signOutAndClose(it) }
|
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testKeyGossipingIsEnabledByDefault() {
|
fun testKeyGossipingIsEnabledByDefault() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val session = testHelper.createAccount("alice", SessionTestParams(true))
|
val session = testHelper.createAccount("alice", SessionTestParams(true))
|
||||||
Assert.assertTrue("Key gossiping should be enabled by default", session.cryptoService().isKeyGossipingEnabled())
|
Assert.assertTrue("Key gossiping should be enabled by default", session.cryptoService().isKeyGossipingEnabled())
|
||||||
testHelper.signOutAndClose(session)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -232,9 +222,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
* 9. Check that new session can decrypt
|
* 9. Check that new session can decrypt
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBasicBackupImport() {
|
fun testBasicBackupImport() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -346,8 +334,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
|
|
||||||
// ensure bob can now decrypt
|
// ensure bob can now decrypt
|
||||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||||
|
|
||||||
testHelper.signOutAndClose(newBobSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -355,9 +341,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
* get them from an older one.
|
* get them from an older one.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testSimpleGossip() {
|
fun testSimpleGossip() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -451,18 +435,13 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
testHelper.signOutAndClose(newBobSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that if a better key is forwarded (lower index, it is then used)
|
* Test that if a better key is forwarded (lower index, it is then used)
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testForwardBetterKey() {
|
fun testForwardBetterKey() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -578,10 +557,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
canDecryptFirst && canDecryptSecond
|
canDecryptFirst && canDecryptSecond
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
testHelper.signOutAndClose(bobSessionWithBetterKey)
|
|
||||||
testHelper.signOutAndClose(newBobSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendMessageInRoom(testHelper: CommonTestHelper, aliceRoomPOV: Room, text: String): String? {
|
private fun sendMessageInRoom(testHelper: CommonTestHelper, aliceRoomPOV: Room, text: String): String? {
|
||||||
|
@ -612,9 +587,7 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
* Test that if a better key is forwared (lower index, it is then used)
|
* Test that if a better key is forwared (lower index, it is then used)
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testSelfInteractiveVerificationAndGossip() {
|
fun testASelfInteractiveVerificationAndGossip() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val aliceSession = testHelper.createAccount("alice", SessionTestParams(true))
|
val aliceSession = testHelper.createAccount("alice", SessionTestParams(true))
|
||||||
cryptoTestHelper.bootstrapSecurity(aliceSession)
|
cryptoTestHelper.bootstrapSecurity(aliceSession)
|
||||||
|
@ -753,9 +726,6 @@ class E2eeSanityTests : InstrumentedTest {
|
||||||
aliceSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()!!.version,
|
aliceSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()!!.version,
|
||||||
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()!!.version
|
aliceNewSession.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo()!!.version
|
||||||
)
|
)
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
testHelper.signOutAndClose(aliceNewSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ensureMembersHaveJoined(testHelper: CommonTestHelper, aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String) {
|
private fun ensureMembersHaveJoined(testHelper: CommonTestHelper, aliceSession: Session, otherAccounts: List<Session>, e2eRoomID: String) {
|
||||||
|
|
|
@ -30,18 +30,14 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class PreShareKeysTest : InstrumentedTest {
|
class PreShareKeysTest : InstrumentedTest {
|
||||||
|
|
||||||
private val testHelper = CommonTestHelper(context())
|
|
||||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun ensure_outbound_session_happy_path() {
|
fun ensure_outbound_session_happy_path() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val e2eRoomID = testData.roomId
|
val e2eRoomID = testData.roomId
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
|
@ -94,7 +90,5 @@ class PreShareKeysTest : InstrumentedTest {
|
||||||
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,7 @@ import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
||||||
|
@ -65,8 +64,6 @@ import kotlin.coroutines.resume
|
||||||
class UnwedgingTest : InstrumentedTest {
|
class UnwedgingTest : InstrumentedTest {
|
||||||
|
|
||||||
private lateinit var messagesReceivedByBob: List<TimelineEvent>
|
private lateinit var messagesReceivedByBob: List<TimelineEvent>
|
||||||
private val testHelper = CommonTestHelper(context())
|
|
||||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun init() {
|
fun init() {
|
||||||
|
@ -87,7 +84,7 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
* -> This is automatically fixed after SDKs restarted the olm session
|
* -> This is automatically fixed after SDKs restarted the olm session
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUnwedging() {
|
fun testUnwedging() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -242,8 +239,6 @@ class UnwedgingTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.dispose()
|
bobTimeline.dispose()
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
|
private fun createEventListener(latch: CountDownLatch, expectedNumberOfMessages: Int): Timeline.Listener {
|
||||||
|
|
|
@ -38,8 +38,8 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.isCrossSignedVerif
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.isVerified
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
|
@ -51,11 +51,8 @@ import kotlin.coroutines.resume
|
||||||
@Ignore
|
@Ignore
|
||||||
class XSigningTest : InstrumentedTest {
|
class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
private val testHelper = CommonTestHelper(context())
|
|
||||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_InitializeAndStoreKeys() {
|
fun test_InitializeAndStoreKeys() = runSessionTest(context()) { testHelper ->
|
||||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
testHelper.doSync<Unit> {
|
testHelper.doSync<Unit> {
|
||||||
|
@ -89,7 +86,7 @@ class XSigningTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_CrossSigningCheckBobSeesTheKeys() {
|
fun test_CrossSigningCheckBobSeesTheKeys() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -139,12 +136,10 @@ class XSigningTest : InstrumentedTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
|
assertFalse("Bob keys from alice pov should not be trusted", bobKeysFromAlicePOV.isTrusted())
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_CrossSigningTestAliceTrustBobNewDevice() {
|
fun test_CrossSigningTestAliceTrustBobNewDevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -218,9 +213,5 @@ class XSigningTest : InstrumentedTest {
|
||||||
|
|
||||||
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
|
val result = aliceSession.cryptoService().crossSigningService().checkDeviceTrust(bobUserId, bobSecondDeviceId, null)
|
||||||
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
|
assertTrue("Bob second device should be trusted from alice POV", result.isCrossSignedVerified())
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
testHelper.signOutAndClose(bobSession)
|
|
||||||
testHelper.signOutAndClose(bobSession2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
@ -42,12 +43,10 @@ import java.util.concurrent.CountDownLatch
|
||||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
class EncryptionTest : InstrumentedTest {
|
class EncryptionTest : InstrumentedTest {
|
||||||
|
|
||||||
private val testHelper = CommonTestHelper(context())
|
|
||||||
private val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_EncryptionEvent() {
|
fun test_EncryptionEvent() {
|
||||||
performTest(roomShouldBeEncrypted = false) { room ->
|
runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
|
performTest(cryptoTestHelper, testHelper, roomShouldBeEncrypted = false) { room ->
|
||||||
// Send an encryption Event as an Event (and not as a state event)
|
// Send an encryption Event as an Event (and not as a state event)
|
||||||
room.sendService().sendEvent(
|
room.sendService().sendEvent(
|
||||||
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
eventType = EventType.STATE_ROOM_ENCRYPTION,
|
||||||
|
@ -55,10 +54,12 @@ class EncryptionTest : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_EncryptionStateEvent() {
|
fun test_EncryptionStateEvent() {
|
||||||
performTest(roomShouldBeEncrypted = true) { room ->
|
runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
|
performTest(cryptoTestHelper, testHelper, roomShouldBeEncrypted = true) { room ->
|
||||||
runBlocking {
|
runBlocking {
|
||||||
// Send an encryption Event as a State Event
|
// Send an encryption Event as a State Event
|
||||||
room.stateService().sendStateEvent(
|
room.stateService().sendStateEvent(
|
||||||
|
@ -69,8 +70,9 @@ class EncryptionTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun performTest(roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
private fun performTest(cryptoTestHelper: CryptoTestHelper, testHelper: CommonTestHelper, roomShouldBeEncrypted: Boolean, action: (Room) -> Unit) {
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(encryptedRoom = false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -109,6 +111,5 @@ class EncryptionTest : InstrumentedTest {
|
||||||
room.roomCryptoService().isEncrypted() shouldBe roomShouldBeEncrypted
|
room.roomCryptoService().isEncrypted() shouldBe roomShouldBeEncrypted
|
||||||
it.countDown()
|
it.countDown()
|
||||||
}
|
}
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,7 @@ import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.RetryTestRule
|
import org.matrix.android.sdk.common.RetryTestRule
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
|
@ -58,9 +57,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
@get:Rule val rule = RetryTestRule(3)
|
@get:Rule val rule = RetryTestRule(3)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_DoNotSelfShareIfNotTrusted() {
|
fun test_DoNotSelfShareIfNotTrusted() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
Log.v("TEST", "=======> AliceSession 1 is ${aliceSession.sessionParams.deviceId}")
|
Log.v("TEST", "=======> AliceSession 1 is ${aliceSession.sessionParams.deviceId}")
|
||||||
|
@ -196,9 +193,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
* if the key was originally shared with him
|
* if the key was originally shared with him
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun test_reShareIfWasIntendedToBeShared() {
|
fun test_reShareIfWasIntendedToBeShared() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
|
@ -229,9 +224,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
* if the key was originally shared with him
|
* if the key was originally shared with him
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun test_reShareToUnverifiedIfWasIntendedToBeShared() {
|
fun test_reShareToUnverifiedIfWasIntendedToBeShared() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceInARoom(true)
|
val testData = cryptoTestHelper.doE2ETestWithAliceInARoom(true)
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
|
@ -268,9 +261,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
* Tests that keys reshared with own verified session are done from the earliest known index
|
* Tests that keys reshared with own verified session are done from the earliest known index
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun test_reShareFromTheEarliestKnownIndexWithOwnVerifiedSession() {
|
fun test_reShareFromTheEarliestKnownIndexWithOwnVerifiedSession() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
|
@ -390,10 +381,7 @@ class KeyShareTests : InstrumentedTest {
|
||||||
* Tests that we don't cancel a request to early on first forward if the index is not good enough
|
* Tests that we don't cancel a request to early on first forward if the index is not good enough
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun test_dontCancelToEarly() {
|
fun test_dontCancelToEarly() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
val bobSession = testData.secondSession!!
|
val bobSession = testData.secondSession!!
|
||||||
|
|
|
@ -37,8 +37,7 @@ import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.MockOkHttpInterceptor
|
import org.matrix.android.sdk.common.MockOkHttpInterceptor
|
||||||
import org.matrix.android.sdk.common.RetryTestRule
|
import org.matrix.android.sdk.common.RetryTestRule
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
|
@ -54,9 +53,7 @@ class WithHeldTests : InstrumentedTest {
|
||||||
@get:Rule val rule = RetryTestRule(3)
|
@get:Rule val rule = RetryTestRule(3)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_WithHeldUnverifiedReason() {
|
fun test_WithHeldUnverifiedReason() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
|
@ -155,16 +152,10 @@ class WithHeldTests : InstrumentedTest {
|
||||||
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
|
bobUnverifiedSession.cryptoService().decryptEvent(eventBobPOV.root, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
testHelper.signOutAndClose(bobSession)
|
|
||||||
testHelper.signOutAndClose(bobUnverifiedSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_WithHeldNoOlm() {
|
fun test_WithHeldNoOlm() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
|
@ -241,14 +232,10 @@ class WithHeldTests : InstrumentedTest {
|
||||||
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
|
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
|
||||||
|
|
||||||
aliceInterceptor.clearRules()
|
aliceInterceptor.clearRules()
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
testHelper.signOutAndClose(bobSecondSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_WithHeldKeyRequest() {
|
fun test_WithHeldKeyRequest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
val aliceSession = testData.firstSession
|
val aliceSession = testData.firstSession
|
||||||
|
@ -295,8 +282,5 @@ class WithHeldTests : InstrumentedTest {
|
||||||
wc?.code == WithHeldCode.UNAUTHORISED
|
wc?.code == WithHeldCode.UNAUTHORISED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
testHelper.signOutAndClose(bobSecondSession)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@ import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreation
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
|
import org.matrix.android.sdk.api.session.crypto.keysbackup.toKeysVersionResult
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
import org.matrix.android.sdk.api.session.crypto.model.ImportRoomKeysResult
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.RetryTestRule
|
import org.matrix.android.sdk.common.RetryTestRule
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.common.TestMatrixCallback
|
import org.matrix.android.sdk.common.TestMatrixCallback
|
||||||
|
@ -67,9 +67,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Reset keys backup markers
|
* - Reset keys backup markers
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun roomKeysTest_testBackupStore_ok() {
|
fun roomKeysTest_testBackupStore_ok() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
|
||||||
|
@ -108,8 +106,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* Check that prepareKeysBackupVersionWithPassword returns valid data
|
* Check that prepareKeysBackupVersionWithPassword returns valid data
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun prepareKeysBackupVersionTest() {
|
fun prepareKeysBackupVersionTest() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||||
|
|
||||||
|
@ -131,16 +128,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertNotNull(megolmBackupCreationInfo.recoveryKey)
|
assertNotNull(megolmBackupCreationInfo.recoveryKey)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testHelper.signOutAndClose(bobSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test creating a keys backup version and check that createKeysBackupVersion() returns valid data
|
* Test creating a keys backup version and check that createKeysBackupVersion() returns valid data
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun createKeysBackupVersionTest() {
|
fun createKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
|
|
||||||
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
val bobSession = testHelper.createAccount(TestConstants.USER_BOB, KeysBackupTestConstants.defaultSessionParams)
|
||||||
cryptoTestHelper.initializeCrossSigning(bobSession)
|
cryptoTestHelper.initializeCrossSigning(bobSession)
|
||||||
|
@ -199,7 +193,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testHelper.signOutAndClose(bobSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -207,9 +200,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Check the backup completes
|
* - Check the backup completes
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun backupAfterCreateKeysBackupVersionTest() {
|
fun backupAfterCreateKeysBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
@ -244,16 +235,13 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
KeysBackupState.ReadyToBackUp
|
KeysBackupState.ReadyToBackUp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that backupAllGroupSessions() returns valid data
|
* Check that backupAllGroupSessions() returns valid data
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun backupAllGroupSessionsTest() {
|
fun backupAllGroupSessionsTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
@ -287,7 +275,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
|
assertEquals("All keys must have been marked as backed up", nbOfKeys, backedUpKeys)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -299,9 +286,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Compare the decrypted megolm key with the original one
|
* - Compare the decrypted megolm key with the original one
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testEncryptAndDecryptKeysBackupData() {
|
fun testEncryptAndDecryptKeysBackupData() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoomWithEncryptedMessages()
|
||||||
|
@ -336,7 +321,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
|
keysBackupTestHelper.assertKeysEquals(session.exportKeys(), sessionData)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,9 +330,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Restore must be successful
|
* - Restore must be successful
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupTest() {
|
fun restoreKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
@ -434,9 +416,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - It must be trusted and must have with 2 signatures now
|
* - It must be trusted and must have with 2 signatures now
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionTest() {
|
fun trustKeyBackupVersionTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Do an e2e backup to the homeserver with a recovery key
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
|
@ -483,7 +463,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -497,9 +476,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - It must be trusted and must have with 2 signatures now
|
* - It must be trusted and must have with 2 signatures now
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithRecoveryKeyTest() {
|
fun trustKeyBackupVersionWithRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Do an e2e backup to the homeserver with a recovery key
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
|
@ -546,7 +523,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -558,9 +534,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - The backup must still be untrusted and disabled
|
* - The backup must still be untrusted and disabled
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() {
|
fun trustKeyBackupVersionWithWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Do an e2e backup to the homeserver with a recovery key
|
// - Do an e2e backup to the homeserver with a recovery key
|
||||||
|
@ -589,7 +563,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -603,9 +576,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - It must be trusted and must have with 2 signatures now
|
* - It must be trusted and must have with 2 signatures now
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithPasswordTest() {
|
fun trustKeyBackupVersionWithPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "Password"
|
val password = "Password"
|
||||||
|
@ -654,7 +625,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
assertEquals(2, keysBackupVersionTrust.signatures.size)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -666,9 +636,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - The backup must still be untrusted and disabled
|
* - The backup must still be untrusted and disabled
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun trustKeyBackupVersionWithWrongPasswordTest() {
|
fun trustKeyBackupVersionWithWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "Password"
|
val password = "Password"
|
||||||
|
@ -700,7 +668,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
assertEquals(KeysBackupState.NotTrusted, testData.aliceSession2.cryptoService().keysBackupService().state)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -710,9 +677,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - It must fail
|
* - It must fail
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupWithAWrongRecoveryKeyTest() {
|
fun restoreKeysBackupWithAWrongRecoveryKeyTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
@ -736,8 +701,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
// onSuccess may not have been called
|
// onSuccess may not have been called
|
||||||
assertNull(importRoomKeysResult)
|
assertNull(importRoomKeysResult)
|
||||||
|
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -747,9 +710,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Restore must be successful
|
* - Restore must be successful
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBackupWithPassword() {
|
fun testBackupWithPassword() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
@ -796,8 +757,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
|
assertEquals(100, (steps[104] as StepProgressListener.Step.ImportingKey).progress)
|
||||||
|
|
||||||
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||||
|
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -807,9 +766,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - It must fail
|
* - It must fail
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun restoreKeysBackupWithAWrongPasswordTest() {
|
fun restoreKeysBackupWithAWrongPasswordTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
@ -836,8 +793,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
// onSuccess may not have been called
|
// onSuccess may not have been called
|
||||||
assertNull(importRoomKeysResult)
|
assertNull(importRoomKeysResult)
|
||||||
|
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -847,9 +802,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Restore must be successful
|
* - Restore must be successful
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() {
|
fun testUseRecoveryKeyToRestoreAPasswordBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val password = "password"
|
val password = "password"
|
||||||
|
@ -869,8 +822,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
keysBackupTestHelper.checkRestoreSuccess(testData, importRoomKeysResult.totalNumberOfKeys, importRoomKeysResult.successfullyNumberOfImportedKeys)
|
||||||
|
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -880,9 +831,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - It must fail
|
* - It must fail
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() {
|
fun testUsePasswordToRestoreARecoveryKeyBasedKeysBackup() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
val testData = keysBackupTestHelper.createKeysBackupScenarioWithPassword(null)
|
||||||
|
@ -906,8 +855,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
// onSuccess may not have been called
|
// onSuccess may not have been called
|
||||||
assertNull(importRoomKeysResult)
|
assertNull(importRoomKeysResult)
|
||||||
|
|
||||||
testData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -915,9 +862,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Check the returned KeysVersionResult is trusted
|
* - Check the returned KeysVersionResult is trusted
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testIsKeysBackupTrusted() {
|
fun testIsKeysBackupTrusted() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
|
@ -951,7 +896,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
|
assertEquals(signature.device!!.deviceId, cryptoTestData.firstSession.sessionParams.deviceId)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -963,9 +907,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* -> That must fail and her backup state must be WrongBackUpVersion
|
* -> That must fail and her backup state must be WrongBackUpVersion
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBackupWhenAnotherBackupWasCreated() {
|
fun testBackupWhenAnotherBackupWasCreated() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
|
@ -1022,7 +964,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1038,9 +979,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* -> It must success
|
* -> It must success
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun testBackupAfterVerifyingADevice() {
|
fun testBackupAfterVerifyingADevice() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
|
@ -1131,8 +1070,6 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
stateObserver2.stopAndCheckStates(null)
|
stateObserver2.stopAndCheckStates(null)
|
||||||
testHelper.signOutAndClose(aliceSession2)
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1140,9 +1077,7 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
* - Delete the backup
|
* - Delete the backup
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun deleteKeysBackupTest() {
|
fun deleteKeysBackupTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
val keysBackupTestHelper = KeysBackupTestHelper(testHelper, cryptoTestHelper)
|
||||||
|
|
||||||
// - Create a backup version
|
// - Create a backup version
|
||||||
|
@ -1165,6 +1100,5 @@ class KeysBackupTest : InstrumentedTest {
|
||||||
assertFalse(keysBackup.isEnabled)
|
assertFalse(keysBackup.isEnabled)
|
||||||
|
|
||||||
stateObserver.stopAndCheckStates(null)
|
stateObserver.stopAndCheckStates(null)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 org.matrix.android.sdk.internal.crypto.replayattack
|
||||||
|
|
||||||
|
import androidx.test.filters.LargeTest
|
||||||
|
import org.amshove.kluent.internal.assertFailsWith
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.fail
|
||||||
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.junit.runners.MethodSorters
|
||||||
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
|
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
@LargeTest
|
||||||
|
class ReplayAttackTest : InstrumentedTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replayAttackAlreadyDecryptedEventTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
|
|
||||||
|
val e2eRoomID = cryptoTestData.roomId
|
||||||
|
|
||||||
|
// Alice
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!!
|
||||||
|
|
||||||
|
// Bob
|
||||||
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!!
|
||||||
|
assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2)
|
||||||
|
|
||||||
|
// Alice will send a message
|
||||||
|
val sentEvents = testHelper.sendTextMessage(aliceRoomPOV, "Hello I will be decrypted twice", 1)
|
||||||
|
assertEquals(1, sentEvents.size)
|
||||||
|
|
||||||
|
val fakeEventId = sentEvents[0].eventId + "_fake"
|
||||||
|
val fakeEventWithTheSameIndex =
|
||||||
|
sentEvents[0].copy(eventId = fakeEventId, root = sentEvents[0].root.copy(eventId = fakeEventId))
|
||||||
|
|
||||||
|
testHelper.runBlockingTest {
|
||||||
|
// Lets assume we are from the main timelineId
|
||||||
|
val timelineId = "timelineId"
|
||||||
|
// Lets decrypt the original event
|
||||||
|
aliceSession.cryptoService().decryptEvent(sentEvents[0].root, timelineId)
|
||||||
|
// Lets decrypt the fake event that will have the same message index
|
||||||
|
val exception = assertFailsWith<MXCryptoError.Base> {
|
||||||
|
// An exception should be thrown while the same index would have been used for the previous decryption
|
||||||
|
aliceSession.cryptoService().decryptEvent(fakeEventWithTheSameIndex.root, timelineId)
|
||||||
|
}
|
||||||
|
assertEquals(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, exception.errorType)
|
||||||
|
}
|
||||||
|
cryptoTestData.cleanUp(testHelper)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replayAttackSameEventTest() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true)
|
||||||
|
|
||||||
|
val e2eRoomID = cryptoTestData.roomId
|
||||||
|
|
||||||
|
// Alice
|
||||||
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!!
|
||||||
|
|
||||||
|
// Bob
|
||||||
|
val bobSession = cryptoTestData.secondSession
|
||||||
|
val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!!
|
||||||
|
assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2)
|
||||||
|
|
||||||
|
// Alice will send a message
|
||||||
|
val sentEvents = testHelper.sendTextMessage(aliceRoomPOV, "Hello I will be decrypted twice", 1)
|
||||||
|
Assert.assertTrue("Message should be sent", sentEvents.size == 1)
|
||||||
|
assertEquals(sentEvents.size, 1)
|
||||||
|
|
||||||
|
testHelper.runBlockingTest {
|
||||||
|
// Lets assume we are from the main timelineId
|
||||||
|
val timelineId = "timelineId"
|
||||||
|
// Lets decrypt the original event
|
||||||
|
aliceSession.cryptoService().decryptEvent(sentEvents[0].root, timelineId)
|
||||||
|
try {
|
||||||
|
// Lets try to decrypt the same event
|
||||||
|
aliceSession.cryptoService().decryptEvent(sentEvents[0].root, timelineId)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
fail("Shouldn't throw a decryption error for same event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService
|
||||||
|
@ -55,8 +56,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_Generate4SKey() {
|
fun test_Generate4SKey() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -108,12 +108,11 @@ class QuadSTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_StoreSecret() {
|
fun test_StoreSecret() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val keyId = "My.Key"
|
val keyId = "My.Key"
|
||||||
val info = generatedSecret(aliceSession, keyId, true)
|
val info = generatedSecret(testHelper, aliceSession, keyId, true)
|
||||||
|
|
||||||
val keySpec = RawBytesKeySpec.fromRecoveryKey(info.recoveryKey)
|
val keySpec = RawBytesKeySpec.fromRecoveryKey(info.recoveryKey)
|
||||||
|
|
||||||
|
@ -127,7 +126,7 @@ class QuadSTests : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val secretAccountData = assertAccountData(aliceSession, "secret.of.life")
|
val secretAccountData = assertAccountData(testHelper, aliceSession, "secret.of.life")
|
||||||
|
|
||||||
val encryptedContent = secretAccountData.content["encrypted"] as? Map<*, *>
|
val encryptedContent = secretAccountData.content["encrypted"] as? Map<*, *>
|
||||||
assertNotNull("Element should be encrypted", encryptedContent)
|
assertNotNull("Element should be encrypted", encryptedContent)
|
||||||
|
@ -149,12 +148,10 @@ class QuadSTests : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
|
assertEquals("Secret mismatch", clearSecret, decryptedSecret)
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_SetDefaultLocalEcho() {
|
fun test_SetDefaultLocalEcho() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -170,19 +167,16 @@ class QuadSTests : InstrumentedTest {
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(TEST_KEY_ID)
|
quadS.setDefaultKey(TEST_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_StoreSecretWithMultipleKey() {
|
fun test_StoreSecretWithMultipleKey() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val keyId1 = "Key.1"
|
val keyId1 = "Key.1"
|
||||||
val key1Info = generatedSecret(aliceSession, keyId1, true)
|
val key1Info = generatedSecret(testHelper, aliceSession, keyId1, true)
|
||||||
val keyId2 = "Key2"
|
val keyId2 = "Key2"
|
||||||
val key2Info = generatedSecret(aliceSession, keyId2, true)
|
val key2Info = generatedSecret(testHelper, aliceSession, keyId2, true)
|
||||||
|
|
||||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||||
|
|
||||||
|
@ -221,19 +215,16 @@ class QuadSTests : InstrumentedTest {
|
||||||
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
|
RawBytesKeySpec.fromRecoveryKey(key2Info.recoveryKey)!!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Test is working locally, not in GitHub actions")
|
@Ignore("Test is working locally, not in GitHub actions")
|
||||||
fun test_GetSecretWithBadPassphrase() {
|
fun test_GetSecretWithBadPassphrase() = runSessionTest(context()) { testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
val keyId1 = "Key.1"
|
val keyId1 = "Key.1"
|
||||||
val passphrase = "The good pass phrase"
|
val passphrase = "The good pass phrase"
|
||||||
val key1Info = generatedSecretFromPassphrase(aliceSession, passphrase, keyId1, true)
|
val key1Info = generatedSecretFromPassphrase(testHelper, aliceSession, passphrase, keyId1, true)
|
||||||
|
|
||||||
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
val mySecretText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||||
|
|
||||||
|
@ -275,13 +266,9 @@ class QuadSTests : InstrumentedTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
testHelper.signOutAndClose(aliceSession)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertAccountData(session: Session, type: String): UserAccountDataEvent {
|
private fun assertAccountData(testHelper: CommonTestHelper, session: Session, type: String): UserAccountDataEvent {
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
var accountData: UserAccountDataEvent? = null
|
var accountData: UserAccountDataEvent? = null
|
||||||
testHelper.waitWithLatch {
|
testHelper.waitWithLatch {
|
||||||
val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
|
val liveAccountData = session.accountDataService().getLiveUserAccountDataEvent(type)
|
||||||
|
@ -297,29 +284,27 @@ class QuadSTests : InstrumentedTest {
|
||||||
return accountData!!
|
return accountData!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generatedSecret(session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
private fun generatedSecret(testHelper: CommonTestHelper, session: Session, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||||
val quadS = session.sharedSecretStorageService()
|
val quadS = session.sharedSecretStorageService()
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val creationInfo = testHelper.runBlockingTest {
|
val creationInfo = testHelper.runBlockingTest {
|
||||||
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
|
quadS.generateKey(keyId, null, keyId, emptyKeySigner)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
assertAccountData(testHelper, session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||||
|
|
||||||
if (asDefault) {
|
if (asDefault) {
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(keyId)
|
quadS.setDefaultKey(keyId)
|
||||||
}
|
}
|
||||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
assertAccountData(testHelper, session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return creationInfo
|
return creationInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generatedSecretFromPassphrase(session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
private fun generatedSecretFromPassphrase(testHelper: CommonTestHelper, session: Session, passphrase: String, keyId: String, asDefault: Boolean = true): SsssKeyCreationInfo {
|
||||||
val quadS = session.sharedSecretStorageService()
|
val quadS = session.sharedSecretStorageService()
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val creationInfo = testHelper.runBlockingTest {
|
val creationInfo = testHelper.runBlockingTest {
|
||||||
quadS.generateKeyWithPassphrase(
|
quadS.generateKeyWithPassphrase(
|
||||||
|
@ -331,12 +316,12 @@ class QuadSTests : InstrumentedTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertAccountData(session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
assertAccountData(testHelper, session, "${DefaultSharedSecretStorageService.KEY_ID_BASE}.$keyId")
|
||||||
if (asDefault) {
|
if (asDefault) {
|
||||||
testHelper.runBlockingTest {
|
testHelper.runBlockingTest {
|
||||||
quadS.setDefaultKey(keyId)
|
quadS.setDefaultKey(keyId)
|
||||||
}
|
}
|
||||||
assertAccountData(session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
assertAccountData(testHelper, session, DefaultSharedSecretStorageService.DEFAULT_KEY_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return creationInfo
|
return creationInfo
|
||||||
|
|
|
@ -44,8 +44,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel
|
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationStart
|
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationStart
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
import org.matrix.android.sdk.internal.crypto.model.rest.toValue
|
||||||
|
@ -57,9 +56,7 @@ import java.util.concurrent.CountDownLatch
|
||||||
class SASTest : InstrumentedTest {
|
class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceStartThenAliceCancel() {
|
fun test_aliceStartThenAliceCancel() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -136,15 +133,11 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
|
assertNull(bobVerificationService.getExistingTransaction(aliceSession.myUserId, txID))
|
||||||
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
|
assertNull(aliceVerificationService.getExistingTransaction(bobSession.myUserId, txID))
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
fun test_key_agreement_protocols_must_include_curve25519() {
|
fun test_key_agreement_protocols_must_include_curve25519() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
fail("Not passing for the moment")
|
fail("Not passing for the moment")
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
|
@ -196,15 +189,11 @@ class SASTest : InstrumentedTest {
|
||||||
testHelper.await(cancelLatch)
|
testHelper.await(cancelLatch)
|
||||||
|
|
||||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
|
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod, cancelReason)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
fun test_key_agreement_macs_Must_include_hmac_sha256() {
|
fun test_key_agreement_macs_Must_include_hmac_sha256() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
fail("Not passing for the moment")
|
fail("Not passing for the moment")
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
|
@ -237,15 +226,11 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
||||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
fun test_key_agreement_short_code_include_decimal() {
|
fun test_key_agreement_short_code_include_decimal() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
fail("Not passing for the moment")
|
fail("Not passing for the moment")
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
|
@ -278,8 +263,6 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
val cancelReq = canceledToDeviceEvent!!.content.toModel<KeyVerificationCancel>()!!
|
||||||
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
assertEquals("Request should be cancelled with m.unknown_method", CancelCode.UnknownMethod.value, cancelReq.code)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fakeBobStart(bobSession: Session,
|
private fun fakeBobStart(bobSession: Session,
|
||||||
|
@ -315,9 +298,7 @@ class SASTest : InstrumentedTest {
|
||||||
// any two devices may only have at most one key verification in flight at a time.
|
// any two devices may only have at most one key verification in flight at a time.
|
||||||
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
|
// If a device has two verifications in progress with the same device, then it should cancel both verifications.
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceStartTwoRequests() {
|
fun test_aliceStartTwoRequests() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -358,9 +339,7 @@ class SASTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
fun test_aliceAndBobAgreement() {
|
fun test_aliceAndBobAgreement() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -414,14 +393,10 @@ class SASTest : InstrumentedTest {
|
||||||
accepted!!.shortAuthenticationStrings.forEach {
|
accepted!!.shortAuthenticationStrings.forEach {
|
||||||
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
|
assertTrue("all agreed Short Code should be known by alice", startReq!!.shortAuthenticationStrings.contains(it))
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_aliceAndBobSASCode() {
|
fun test_aliceAndBobSASCode() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -474,14 +449,10 @@ class SASTest : InstrumentedTest {
|
||||||
"Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
|
"Should have same SAS", aliceTx.getShortCodeRepresentation(SasMode.DECIMAL),
|
||||||
bobTx.getShortCodeRepresentation(SasMode.DECIMAL)
|
bobTx.getShortCodeRepresentation(SasMode.DECIMAL)
|
||||||
)
|
)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_happyPath() {
|
fun test_happyPath() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -554,13 +525,10 @@ class SASTest : InstrumentedTest {
|
||||||
|
|
||||||
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
|
assertTrue("alice device should be verified from bob point of view", aliceDeviceInfoFromBobPOV!!.isVerified)
|
||||||
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
|
assertTrue("bob device should be verified from alice point of view", bobDeviceInfoFromAlicePOV!!.isVerified)
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_ConcurrentStart() {
|
fun test_ConcurrentStart() = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -647,7 +615,5 @@ class SASTest : InstrumentedTest {
|
||||||
bobPovTx?.state == VerificationTxState.ShortCodeReady
|
bobPovTx?.state == VerificationTxState.ShortCodeReady
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
@ -156,9 +156,7 @@ class VerificationTest : InstrumentedTest {
|
||||||
private fun doTest(aliceSupportedMethods: List<VerificationMethod>,
|
private fun doTest(aliceSupportedMethods: List<VerificationMethod>,
|
||||||
bobSupportedMethods: List<VerificationMethod>,
|
bobSupportedMethods: List<VerificationMethod>,
|
||||||
expectedResultForAlice: ExpectedResult,
|
expectedResultForAlice: ExpectedResult,
|
||||||
expectedResultForBob: ExpectedResult) {
|
expectedResultForBob: ExpectedResult) = runCryptoTest(context()) { cryptoTestHelper, testHelper ->
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(testHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -253,14 +251,11 @@ class VerificationTest : InstrumentedTest {
|
||||||
pr.otherCanShowQrCode() shouldBe expectedResultForBob.otherCanShowQrCode
|
pr.otherCanShowQrCode() shouldBe expectedResultForBob.otherCanShowQrCode
|
||||||
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
|
pr.otherCanScanQrCode() shouldBe expectedResultForBob.otherCanScanQrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_selfVerificationAcceptedCancelsItForOtherSessions() {
|
fun test_selfVerificationAcceptedCancelsItForOtherSessions() = runSessionTest(context()) { testHelper ->
|
||||||
val defaultSessionParams = SessionTestParams(true)
|
val defaultSessionParams = SessionTestParams(true)
|
||||||
val testHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val aliceSessionToVerify = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
val aliceSessionToVerify = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||||
val aliceSessionThatVerifies = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
|
val aliceSessionThatVerifies = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
|
||||||
|
|
|
@ -34,8 +34,7 @@ import org.matrix.android.sdk.api.session.events.model.isThread
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
|
@ -44,9 +43,7 @@ import java.util.concurrent.CountDownLatch
|
||||||
class ThreadMessagingTest : InstrumentedTest {
|
class ThreadMessagingTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun reply_in_thread_should_create_a_thread() {
|
fun reply_in_thread_should_create_a_thread() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -104,9 +101,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun reply_in_thread_should_create_a_thread_from_other_user() {
|
fun reply_in_thread_should_create_a_thread_from_other_user() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -179,9 +174,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun reply_in_thread_to_timeline_message_multiple_times() {
|
fun reply_in_thread_to_timeline_message_multiple_times() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -244,9 +237,7 @@ class ThreadMessagingTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun thread_summary_advanced_validation_after_multiple_messages_in_multiple_threads() {
|
fun thread_summary_advanced_validation_after_multiple_messages_in_multiple_threads() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
|
|
@ -38,8 +38,7 @@ import org.matrix.android.sdk.api.session.room.model.message.PollType
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
|
@ -47,9 +46,7 @@ import java.util.concurrent.CountDownLatch
|
||||||
class PollAggregationTest : InstrumentedTest {
|
class PollAggregationTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAllPollUseCases() {
|
fun testAllPollUseCases() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -138,7 +135,6 @@ class PollAggregationTest : InstrumentedTest {
|
||||||
|
|
||||||
aliceSession.stopSync()
|
aliceSession.stopSync()
|
||||||
aliceTimeline.dispose()
|
aliceTimeline.dispose()
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun testInitialPollConditions(pollContent: MessagePollContent, pollSummary: PollResponseAggregatedSummary?) {
|
private fun testInitialPollConditions(pollContent: MessagePollContent, pollSummary: PollResponseAggregatedSummary?) {
|
||||||
|
|
|
@ -34,8 +34,7 @@ import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.checkSendOrder
|
import org.matrix.android.sdk.common.checkSendOrder
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
@ -53,9 +52,7 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
||||||
* This test ensure that if we click to permalink, we will be able to go back to the live
|
* This test ensure that if we click to permalink, we will be able to go back to the live
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun forwardPaginationTest() {
|
fun forwardPaginationTest() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val numberOfMessagesToSend = 90
|
val numberOfMessagesToSend = 90
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||||
|
|
||||||
|
@ -177,7 +174,5 @@ class TimelineForwardPaginationTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
aliceTimeline.dispose()
|
aliceTimeline.dispose()
|
||||||
|
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.checkSendOrder
|
import org.matrix.android.sdk.common.checkSendOrder
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
@ -48,9 +47,7 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun previousLastForwardTest() {
|
fun previousLastForwardTest() = CommonTestHelper.runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
|
@ -242,7 +239,5 @@ class TimelinePreviousLastForwardTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
bobTimeline.dispose()
|
bobTimeline.dispose()
|
||||||
|
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,7 @@ import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
|
@ -44,9 +43,7 @@ import org.matrix.android.sdk.common.TestConstants
|
||||||
class TimelineSimpleBackPaginationTest : InstrumentedTest {
|
class TimelineSimpleBackPaginationTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun timeline_backPaginate_shouldReachEndOfTimeline() {
|
fun timeline_backPaginate_shouldReachEndOfTimeline() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val numberOfMessagesToSent = 200
|
val numberOfMessagesToSent = 200
|
||||||
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(false)
|
||||||
|
@ -104,6 +101,5 @@ class TimelineSimpleBackPaginationTest : InstrumentedTest {
|
||||||
assertEquals(numberOfMessagesToSent, onlySentEvents.size)
|
assertEquals(numberOfMessagesToSent, onlySentEvents.size)
|
||||||
|
|
||||||
bobTimeline.dispose()
|
bobTimeline.dispose()
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
/** !! Not working with the new timeline
|
/** !! Not working with the new timeline
|
||||||
|
@ -47,15 +46,12 @@ class TimelineWithManyMembersTest : InstrumentedTest {
|
||||||
private const val NUMBER_OF_MEMBERS = 6
|
private const val NUMBER_OF_MEMBERS = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
private val commonTestHelper = CommonTestHelper(context())
|
|
||||||
private val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
|
* Ensures when someone sends a message to a crowded room, everyone can decrypt the message.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun everyone_should_decrypt_message_in_a_crowded_room() {
|
fun everyone_should_decrypt_message_in_a_crowded_room() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithManyMembers(NUMBER_OF_MEMBERS)
|
||||||
|
|
||||||
val sessionForFirstMember = cryptoTestData.firstSession
|
val sessionForFirstMember = cryptoTestData.firstSession
|
||||||
|
|
|
@ -26,9 +26,8 @@ import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.search.SearchResult
|
import org.matrix.android.sdk.api.session.search.SearchResult
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest
|
||||||
import org.matrix.android.sdk.common.CryptoTestData
|
import org.matrix.android.sdk.common.CryptoTestData
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
|
@ -74,9 +73,7 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doTest(block: suspend (CryptoTestData) -> SearchResult) {
|
private fun doTest(block: suspend (CryptoTestData) -> SearchResult) = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val cryptoTestHelper = CryptoTestHelper(commonTestHelper)
|
|
||||||
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceInARoom(false)
|
||||||
val aliceSession = cryptoTestData.firstSession
|
val aliceSession = cryptoTestData.firstSession
|
||||||
val aliceRoomId = cryptoTestData.roomId
|
val aliceRoomId = cryptoTestData.roomId
|
||||||
|
@ -99,7 +96,5 @@ class SearchMessagesTest : InstrumentedTest {
|
||||||
(it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse()
|
(it.event.content?.get("body") as? String)?.startsWith(MESSAGE).orFalse()
|
||||||
}.orFalse()
|
}.orFalse()
|
||||||
)
|
)
|
||||||
|
|
||||||
cryptoTestData.cleanUp(commonTestHelper)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||||
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
import org.matrix.android.sdk.api.session.space.JoinSpaceResult
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
|
@ -50,8 +50,7 @@ import org.matrix.android.sdk.common.SessionTestParams
|
||||||
class SpaceCreationTest : InstrumentedTest {
|
class SpaceCreationTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createSimplePublicSpace() {
|
fun createSimplePublicSpace() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("Hubble", SessionTestParams(true))
|
||||||
val roomName = "My Space"
|
val roomName = "My Space"
|
||||||
val topic = "A public space for test"
|
val topic = "A public space for test"
|
||||||
|
@ -97,13 +96,11 @@ class SpaceCreationTest : InstrumentedTest {
|
||||||
?.toModel<RoomHistoryVisibilityContent>()?.historyVisibility
|
?.toModel<RoomHistoryVisibilityContent>()?.historyVisibility
|
||||||
|
|
||||||
assertEquals("Public space room should be world readable", RoomHistoryVisibility.WORLD_READABLE, historyVisibility)
|
assertEquals("Public space room should be world readable", RoomHistoryVisibility.WORLD_READABLE, historyVisibility)
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(session)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testJoinSimplePublicSpace() {
|
@Ignore
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
fun testJoinSimplePublicSpace() = runSessionTest(context()) { commonTestHelper ->
|
||||||
|
|
||||||
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
||||||
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
||||||
|
@ -135,9 +132,7 @@ class SpaceCreationTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
fun testSimplePublicSpaceWithChildren() = runSessionTest(context()) { commonTestHelper ->
|
||||||
fun testSimplePublicSpaceWithChildren() {
|
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount("alice", SessionTestParams(true))
|
||||||
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
val bobSession = commonTestHelper.createAccount("bob", SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -206,8 +201,5 @@ class SpaceCreationTest : InstrumentedTest {
|
||||||
// ).size
|
// ).size
|
||||||
//
|
//
|
||||||
// assertEquals("Unexpected number of joined children", 1, childCount)
|
// assertEquals("Unexpected number of joined children", 1, childCount)
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(aliceSession)
|
|
||||||
commonTestHelper.signOutAndClose(bobSession)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||||
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
|
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runSessionTest
|
||||||
import org.matrix.android.sdk.common.SessionTestParams
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
|
@ -55,8 +56,7 @@ import org.matrix.android.sdk.common.SessionTestParams
|
||||||
class SpaceHierarchyTest : InstrumentedTest {
|
class SpaceHierarchyTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun createCanonicalChildRelation() {
|
fun createCanonicalChildRelation() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
|
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
val spaceName = "My Space"
|
val spaceName = "My Space"
|
||||||
|
@ -173,8 +173,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFilteringBySpace() {
|
fun testFilteringBySpace() = CommonTestHelper.runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
val spaceAInfo = createPublicSpace(
|
val spaceAInfo = createPublicSpace(
|
||||||
|
@ -256,8 +255,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("This test will be ignored until it is fixed")
|
@Ignore("This test will be ignored until it is fixed")
|
||||||
fun testBreakCycle() {
|
fun testBreakCycle() = CommonTestHelper.runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
val spaceAInfo = createPublicSpace(
|
val spaceAInfo = createPublicSpace(
|
||||||
|
@ -302,8 +300,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLiveFlatChildren() {
|
fun testLiveFlatChildren() = CommonTestHelper.runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
val spaceAInfo = createPublicSpace(
|
val spaceAInfo = createPublicSpace(
|
||||||
|
@ -395,9 +392,9 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||||
/** Name, auto-join, canonical*/
|
/** Name, auto-join, canonical*/
|
||||||
): TestSpaceCreationResult {
|
): TestSpaceCreationResult {
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
var spaceId = ""
|
var spaceId = ""
|
||||||
var roomIds: List<String> = emptyList()
|
var roomIds: List<String> = emptyList()
|
||||||
|
runSessionTest(context()) { commonTestHelper ->
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
spaceId = session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
||||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||||
|
@ -415,6 +412,7 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return TestSpaceCreationResult(spaceId, roomIds)
|
return TestSpaceCreationResult(spaceId, roomIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,9 +421,9 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||||
/** Name, auto-join, canonical*/
|
/** Name, auto-join, canonical*/
|
||||||
): TestSpaceCreationResult {
|
): TestSpaceCreationResult {
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
var spaceId = ""
|
var spaceId = ""
|
||||||
var roomIds: List<String> = emptyList()
|
var roomIds: List<String> = emptyList()
|
||||||
|
runSessionTest(context()) { commonTestHelper ->
|
||||||
commonTestHelper.waitWithLatch { latch ->
|
commonTestHelper.waitWithLatch { latch ->
|
||||||
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
|
spaceId = session.spaceService().createSpace(spaceName, "My Private Space", null, false)
|
||||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||||
|
@ -454,12 +452,12 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
latch.countDown()
|
latch.countDown()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return TestSpaceCreationResult(spaceId, roomIds)
|
return TestSpaceCreationResult(spaceId, roomIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRootSpaces() {
|
fun testRootSpaces() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||||
|
|
||||||
/* val spaceAInfo = */ createPublicSpace(
|
/* val spaceAInfo = */ createPublicSpace(
|
||||||
|
@ -506,13 +504,10 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size)
|
assertEquals("Unexpected number of root spaces ${rootSpaces.map { it.name }}", 2, rootSpaces.size)
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(session)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testParentRelation() {
|
fun testParentRelation() = runSessionTest(context()) { commonTestHelper ->
|
||||||
val commonTestHelper = CommonTestHelper(context())
|
|
||||||
val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true))
|
val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true))
|
||||||
val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true))
|
val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true))
|
||||||
|
|
||||||
|
@ -604,8 +599,5 @@ class SpaceHierarchyTest : InstrumentedTest {
|
||||||
bobSession.getRoomSummary(bobRoomId)?.flattenParentIds?.contains(spaceAInfo.spaceId) == true
|
bobSession.getRoomSummary(bobRoomId)?.flattenParentIds?.contains(spaceAInfo.spaceId) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
commonTestHelper.signOutAndClose(aliceSession)
|
|
||||||
commonTestHelper.signOutAndClose(bobSession)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.OlmEventContent
|
import org.matrix.android.sdk.api.session.events.model.content.OlmEventContent
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
|
||||||
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
|
||||||
|
@ -42,7 +43,7 @@ import javax.inject.Inject
|
||||||
|
|
||||||
private const val SEND_TO_DEVICE_RETRY_COUNT = 3
|
private const val SEND_TO_DEVICE_RETRY_COUNT = 3
|
||||||
|
|
||||||
private val loggerTag = LoggerTag("CryptoSyncHandler", LoggerTag.CRYPTO)
|
private val loggerTag = LoggerTag("EventDecryptor", LoggerTag.CRYPTO)
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class EventDecryptor @Inject constructor(
|
internal class EventDecryptor @Inject constructor(
|
||||||
|
@ -110,6 +111,16 @@ internal class EventDecryptor @Inject constructor(
|
||||||
if (eventContent == null) {
|
if (eventContent == null) {
|
||||||
Timber.tag(loggerTag.value).e("decryptEvent : empty event content")
|
Timber.tag(loggerTag.value).e("decryptEvent : empty event content")
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
|
||||||
|
} else if (event.isRedacted()) {
|
||||||
|
// we shouldn't attempt to decrypt a redacted event because the content is cleared and decryption will fail because of null algorithm
|
||||||
|
return MXEventDecryptionResult(
|
||||||
|
clearEvent = mapOf(
|
||||||
|
"room_id" to event.roomId.orEmpty(),
|
||||||
|
"type" to EventType.MESSAGE,
|
||||||
|
"content" to emptyMap<String, Any>(),
|
||||||
|
"unsigned" to event.unsignedData.toContent()
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val algorithm = eventContent["algorithm"]?.toString()
|
val algorithm = eventContent["algorithm"]?.toString()
|
||||||
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
|
val alg = roomDecryptorProvider.getOrCreateRoomDecryptor(event.roomId, algorithm)
|
||||||
|
|
|
@ -96,8 +96,9 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
// So, store these message indexes per timeline id.
|
// So, store these message indexes per timeline id.
|
||||||
//
|
//
|
||||||
// The first level keys are timeline ids.
|
// The first level keys are timeline ids.
|
||||||
// The second level keys are strings of form "<senderKey>|<session_id>|<message_index>"
|
// The second level values is a Map that represents:
|
||||||
private val inboundGroupSessionMessageIndexes: MutableMap<String, MutableSet<String>> = HashMap()
|
// "<senderKey>|<session_id>|<roomId>|<message_index>" --> eventId
|
||||||
|
private val inboundGroupSessionMessageIndexes: MutableMap<String, MutableMap<String, String>> = HashMap()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Retrieve the account from the store
|
// Retrieve the account from the store
|
||||||
|
@ -755,23 +756,29 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
* @param body the base64-encoded body of the encrypted message.
|
* @param body the base64-encoded body of the encrypted message.
|
||||||
* @param roomId the room in which the message was received.
|
* @param roomId the room in which the message was received.
|
||||||
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
||||||
|
* @param eventId the eventId of the message that will be decrypted
|
||||||
* @param sessionId the session identifier.
|
* @param sessionId the session identifier.
|
||||||
* @param senderKey the base64-encoded curve25519 key of the sender.
|
* @param senderKey the base64-encoded curve25519 key of the sender.
|
||||||
* @return the decrypting result. Nil if the sessionId is unknown.
|
* @return the decrypting result. Null if the sessionId is unknown.
|
||||||
*/
|
*/
|
||||||
@Throws(MXCryptoError::class)
|
@Throws(MXCryptoError::class)
|
||||||
suspend fun decryptGroupMessage(body: String,
|
suspend fun decryptGroupMessage(body: String,
|
||||||
roomId: String,
|
roomId: String,
|
||||||
timeline: String?,
|
timeline: String?,
|
||||||
|
eventId: String,
|
||||||
sessionId: String,
|
sessionId: String,
|
||||||
senderKey: String): OlmDecryptionResult {
|
senderKey: String): OlmDecryptionResult {
|
||||||
val sessionHolder = getInboundGroupSession(sessionId, senderKey, roomId)
|
val sessionHolder = getInboundGroupSession(sessionId, senderKey, roomId)
|
||||||
val wrapper = sessionHolder.wrapper
|
val wrapper = sessionHolder.wrapper
|
||||||
val inboundGroupSession = wrapper.olmInboundGroupSession
|
val inboundGroupSession = wrapper.olmInboundGroupSession
|
||||||
?: throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, "Session is null")
|
?: throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, "Session is null")
|
||||||
|
if (roomId != wrapper.roomId) {
|
||||||
// Check that the room id matches the original one for the session. This stops
|
// Check that the room id matches the original one for the session. This stops
|
||||||
// the HS pretending a message was targeting a different room.
|
// the HS pretending a message was targeting a different room.
|
||||||
if (roomId == wrapper.roomId) {
|
val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, wrapper.roomId)
|
||||||
|
Timber.tag(loggerTag.value).e("## decryptGroupMessage() : $reason")
|
||||||
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)
|
||||||
|
}
|
||||||
val decryptResult = try {
|
val decryptResult = try {
|
||||||
sessionHolder.mutex.withLock {
|
sessionHolder.mutex.withLock {
|
||||||
inboundGroupSession.decryptMessage(body)
|
inboundGroupSession.decryptMessage(body)
|
||||||
|
@ -781,20 +788,24 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
throw MXCryptoError.OlmError(e)
|
throw MXCryptoError.OlmError(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val messageIndexKey = senderKey + "|" + sessionId + "|" + roomId + "|" + decryptResult.mIndex
|
||||||
|
Timber.tag(loggerTag.value).v("##########################################################")
|
||||||
|
Timber.tag(loggerTag.value).v("## decryptGroupMessage() timeline: $timeline")
|
||||||
|
Timber.tag(loggerTag.value).v("## decryptGroupMessage() senderKey: $senderKey")
|
||||||
|
Timber.tag(loggerTag.value).v("## decryptGroupMessage() sessionId: $sessionId")
|
||||||
|
Timber.tag(loggerTag.value).v("## decryptGroupMessage() roomId: $roomId")
|
||||||
|
Timber.tag(loggerTag.value).v("## decryptGroupMessage() eventId: $eventId")
|
||||||
|
Timber.tag(loggerTag.value).v("## decryptGroupMessage() mIndex: ${decryptResult.mIndex}")
|
||||||
|
|
||||||
if (timeline?.isNotBlank() == true) {
|
if (timeline?.isNotBlank() == true) {
|
||||||
val timelineSet = inboundGroupSessionMessageIndexes.getOrPut(timeline) { mutableSetOf() }
|
val replayAttackMap = inboundGroupSessionMessageIndexes.getOrPut(timeline) { mutableMapOf() }
|
||||||
|
if (replayAttackMap.contains(messageIndexKey) && replayAttackMap[messageIndexKey] != eventId) {
|
||||||
val messageIndexKey = senderKey + "|" + sessionId + "|" + decryptResult.mIndex
|
|
||||||
|
|
||||||
if (timelineSet.contains(messageIndexKey)) {
|
|
||||||
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
val reason = String.format(MXCryptoError.DUPLICATE_MESSAGE_INDEX_REASON, decryptResult.mIndex)
|
||||||
Timber.tag(loggerTag.value).e("## decryptGroupMessage() timelineId=$timeline: $reason")
|
Timber.tag(loggerTag.value).e("## decryptGroupMessage() timelineId=$timeline: $reason")
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.DUPLICATED_MESSAGE_INDEX, reason)
|
||||||
}
|
}
|
||||||
|
replayAttackMap[messageIndexKey] = eventId
|
||||||
timelineSet.add(messageIndexKey)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inboundGroupSessionStore.storeInBoundGroupSession(sessionHolder, sessionId, senderKey)
|
inboundGroupSessionStore.storeInBoundGroupSession(sessionHolder, sessionId, senderKey)
|
||||||
val payload = try {
|
val payload = try {
|
||||||
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
val adapter = MoshiProvider.providesMoshi().adapter<JsonDict>(JSON_DICT_PARAMETERIZED_TYPE)
|
||||||
|
@ -811,11 +822,6 @@ internal class MXOlmDevice @Inject constructor(
|
||||||
senderKey,
|
senderKey,
|
||||||
wrapper.forwardingCurve25519KeyChain
|
wrapper.forwardingCurve25519KeyChain
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
val reason = String.format(MXCryptoError.INBOUND_SESSION_MISMATCH_ROOM_ID_REASON, roomId, wrapper.roomId)
|
|
||||||
Timber.tag(loggerTag.value).e("## decryptGroupMessage() : $reason")
|
|
||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.INBOUND_SESSION_MISMATCH_ROOM_ID, reason)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -78,6 +78,7 @@ internal class MXMegolmDecryption(
|
||||||
encryptedEventContent.ciphertext,
|
encryptedEventContent.ciphertext,
|
||||||
event.roomId,
|
event.roomId,
|
||||||
timeline,
|
timeline,
|
||||||
|
eventId = event.eventId.orEmpty(),
|
||||||
encryptedEventContent.sessionId,
|
encryptedEventContent.sessionId,
|
||||||
encryptedEventContent.senderKey
|
encryptedEventContent.senderKey
|
||||||
)
|
)
|
||||||
|
|
|
@ -520,9 +520,10 @@ internal class RoomSyncHandler @Inject constructor(
|
||||||
|
|
||||||
private fun decryptIfNeeded(event: Event, roomId: String) {
|
private fun decryptIfNeeded(event: Event, roomId: String) {
|
||||||
try {
|
try {
|
||||||
|
val timelineId = generateTimelineId(roomId)
|
||||||
// Event from sync does not have roomId, so add it to the event first
|
// Event from sync does not have roomId, so add it to the event first
|
||||||
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
|
// note: runBlocking should be used here while we are in realm single thread executor, to avoid thread switching
|
||||||
val result = runBlocking { cryptoService.decryptEvent(event.copy(roomId = roomId), "") }
|
val result = runBlocking { cryptoService.decryptEvent(event.copy(roomId = roomId), timelineId) }
|
||||||
event.mxDecryptionResult = OlmDecryptionResult(
|
event.mxDecryptionResult = OlmDecryptionResult(
|
||||||
payload = result.clearEvent,
|
payload = result.clearEvent,
|
||||||
senderKey = result.senderCurve25519Key,
|
senderKey = result.senderCurve25519Key,
|
||||||
|
@ -537,6 +538,10 @@ internal class RoomSyncHandler @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateTimelineId(roomId: String): String {
|
||||||
|
return "RoomSyncHandler$roomId"
|
||||||
|
}
|
||||||
|
|
||||||
data class EphemeralResult(
|
data class EphemeralResult(
|
||||||
val typingUserIds: List<String> = emptyList()
|
val typingUserIds: List<String> = emptyList()
|
||||||
)
|
)
|
||||||
|
|
|
@ -382,7 +382,7 @@ dependencies {
|
||||||
implementation 'com.facebook.stetho:stetho:1.6.0'
|
implementation 'com.facebook.stetho:stetho:1.6.0'
|
||||||
|
|
||||||
// Phone number https://github.com/google/libphonenumber
|
// Phone number https://github.com/google/libphonenumber
|
||||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.48'
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.49'
|
||||||
|
|
||||||
// FlowBinding
|
// FlowBinding
|
||||||
implementation libs.github.flowBinding
|
implementation libs.github.flowBinding
|
||||||
|
|
|
@ -60,6 +60,11 @@ class DebugFeaturesStateFactory @Inject constructor(
|
||||||
key = DebugFeatureKeys.onboardingCombinedRegister,
|
key = DebugFeatureKeys.onboardingCombinedRegister,
|
||||||
factory = VectorFeatures::isOnboardingCombinedRegisterEnabled
|
factory = VectorFeatures::isOnboardingCombinedRegisterEnabled
|
||||||
),
|
),
|
||||||
|
createBooleanFeature(
|
||||||
|
label = "FTUE Combined login",
|
||||||
|
key = DebugFeatureKeys.onboardingCombinedLogin,
|
||||||
|
factory = VectorFeatures::isOnboardingCombinedLoginEnabled
|
||||||
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,9 @@ class DebugVectorFeatures(
|
||||||
override fun isOnboardingCombinedRegisterEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedRegister)
|
override fun isOnboardingCombinedRegisterEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedRegister)
|
||||||
?: vectorFeatures.isOnboardingCombinedRegisterEnabled()
|
?: vectorFeatures.isOnboardingCombinedRegisterEnabled()
|
||||||
|
|
||||||
|
override fun isOnboardingCombinedLoginEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedLogin)
|
||||||
|
?: vectorFeatures.isOnboardingCombinedLoginEnabled()
|
||||||
|
|
||||||
override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
|
override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
|
||||||
?: vectorFeatures.isScreenSharingEnabled()
|
?: vectorFeatures.isScreenSharingEnabled()
|
||||||
|
|
||||||
|
@ -113,6 +116,7 @@ object DebugFeatureKeys {
|
||||||
val onboardingUseCase = booleanPreferencesKey("onboarding-splash-carousel")
|
val onboardingUseCase = booleanPreferencesKey("onboarding-splash-carousel")
|
||||||
val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
|
val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
|
||||||
val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
|
val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
|
||||||
|
val onboardingCombinedLogin = booleanPreferencesKey("onboarding-combined-login")
|
||||||
val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
|
val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
|
||||||
val screenSharing = booleanPreferencesKey("screen-sharing")
|
val screenSharing = booleanPreferencesKey("screen-sharing")
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,8 @@ class AppStateHandler @Inject constructor(
|
||||||
|
|
||||||
val selectedRoomGroupingFlow = selectedSpaceDataSource.stream()
|
val selectedRoomGroupingFlow = selectedSpaceDataSource.stream()
|
||||||
|
|
||||||
|
private val spaceBackstack = ArrayDeque<String?>()
|
||||||
|
|
||||||
fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? {
|
fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? {
|
||||||
// XXX we should somehow make it live :/ just a work around
|
// XXX we should somehow make it live :/ just a work around
|
||||||
// For example just after creating a space and switching to it the
|
// For example just after creating a space and switching to it the
|
||||||
|
@ -87,12 +89,16 @@ class AppStateHandler @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCurrentSpace(spaceId: String?, session: Session? = null, persistNow: Boolean = false) {
|
fun setCurrentSpace(spaceId: String?, session: Session? = null, persistNow: Boolean = false, isForwardNavigation: Boolean = true) {
|
||||||
|
val currentSpace = (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.space()
|
||||||
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return
|
||||||
if (selectedSpaceDataSource.currentValue?.orNull() is RoomGroupingMethod.BySpace &&
|
if (currentSpace != null && spaceId == currentSpace.roomId) return
|
||||||
spaceId == selectedSpaceDataSource.currentValue?.orNull()?.space()?.roomId) return
|
|
||||||
val spaceSum = spaceId?.let { uSession.getRoomSummary(spaceId) }
|
val spaceSum = spaceId?.let { uSession.getRoomSummary(spaceId) }
|
||||||
|
|
||||||
|
if (isForwardNavigation) {
|
||||||
|
spaceBackstack.addLast(currentSpace?.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
if (persistNow) {
|
if (persistNow) {
|
||||||
uiStateRepository.storeGroupingMethod(true, uSession.sessionId)
|
uiStateRepository.storeGroupingMethod(true, uSession.sessionId)
|
||||||
uiStateRepository.storeSelectedSpace(spaceSum?.roomId, uSession.sessionId)
|
uiStateRepository.storeSelectedSpace(spaceSum?.roomId, uSession.sessionId)
|
||||||
|
@ -151,6 +157,8 @@ class AppStateHandler @Inject constructor(
|
||||||
}.launchIn(session.coroutineScope)
|
}.launchIn(session.coroutineScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSpaceBackstack() = spaceBackstack
|
||||||
|
|
||||||
fun safeActiveSpaceId(): String? {
|
fun safeActiveSpaceId(): String? {
|
||||||
return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId
|
return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,9 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseProfilePictureFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseProfilePictureFragment
|
||||||
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedLoginFragment
|
||||||
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedRegisterFragment
|
||||||
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthCombinedServerSelectionFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthEmailEntryFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthEmailEntryFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFragment
|
||||||
|
@ -521,6 +524,21 @@ interface FragmentModule {
|
||||||
@FragmentKey(FtueAuthPersonalizationCompleteFragment::class)
|
@FragmentKey(FtueAuthPersonalizationCompleteFragment::class)
|
||||||
fun bindFtueAuthPersonalizationCompleteFragment(fragment: FtueAuthPersonalizationCompleteFragment): Fragment
|
fun bindFtueAuthPersonalizationCompleteFragment(fragment: FtueAuthPersonalizationCompleteFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(FtueAuthCombinedLoginFragment::class)
|
||||||
|
fun bindFtueAuthCombinedLoginFragment(fragment: FtueAuthCombinedLoginFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(FtueAuthCombinedRegisterFragment::class)
|
||||||
|
fun bindFtueAuthCombinedRegisterFragment(fragment: FtueAuthCombinedRegisterFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(FtueAuthCombinedServerSelectionFragment::class)
|
||||||
|
fun bindFtueAuthCombinedServerSelectionFragment(fragment: FtueAuthCombinedServerSelectionFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(UserListFragment::class)
|
@FragmentKey(UserListFragment::class)
|
||||||
|
|
|
@ -26,6 +26,7 @@ interface VectorFeatures {
|
||||||
fun isOnboardingUseCaseEnabled(): Boolean
|
fun isOnboardingUseCaseEnabled(): Boolean
|
||||||
fun isOnboardingPersonalizeEnabled(): Boolean
|
fun isOnboardingPersonalizeEnabled(): Boolean
|
||||||
fun isOnboardingCombinedRegisterEnabled(): Boolean
|
fun isOnboardingCombinedRegisterEnabled(): Boolean
|
||||||
|
fun isOnboardingCombinedLoginEnabled(): Boolean
|
||||||
fun isScreenSharingEnabled(): Boolean
|
fun isScreenSharingEnabled(): Boolean
|
||||||
|
|
||||||
enum class OnboardingVariant {
|
enum class OnboardingVariant {
|
||||||
|
@ -42,5 +43,6 @@ class DefaultVectorFeatures : VectorFeatures {
|
||||||
override fun isOnboardingUseCaseEnabled() = true
|
override fun isOnboardingUseCaseEnabled() = true
|
||||||
override fun isOnboardingPersonalizeEnabled() = false
|
override fun isOnboardingPersonalizeEnabled() = false
|
||||||
override fun isOnboardingCombinedRegisterEnabled() = false
|
override fun isOnboardingCombinedRegisterEnabled() = false
|
||||||
|
override fun isOnboardingCombinedLoginEnabled() = false
|
||||||
override fun isScreenSharingEnabled(): Boolean = true
|
override fun isScreenSharingEnabled(): Boolean = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,43 +199,13 @@ class HomeActivity :
|
||||||
when (sharedAction) {
|
when (sharedAction) {
|
||||||
is HomeActivitySharedAction.OpenDrawer -> views.drawerLayout.openDrawer(GravityCompat.START)
|
is HomeActivitySharedAction.OpenDrawer -> views.drawerLayout.openDrawer(GravityCompat.START)
|
||||||
is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START)
|
is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START)
|
||||||
is HomeActivitySharedAction.OpenGroup -> {
|
is HomeActivitySharedAction.OpenGroup -> openGroup(sharedAction.shouldClearFragment)
|
||||||
views.drawerLayout.closeDrawer(GravityCompat.START)
|
is HomeActivitySharedAction.OpenSpacePreview -> startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId))
|
||||||
|
is HomeActivitySharedAction.AddSpace -> createSpaceResultLauncher.launch(SpaceCreationActivity.newIntent(this))
|
||||||
// Temporary
|
is HomeActivitySharedAction.ShowSpaceSettings -> showSpaceSettings(sharedAction.spaceId)
|
||||||
// When switching from space to group or group to space, we need to reload the fragment
|
is HomeActivitySharedAction.OpenSpaceInvite -> openSpaceInvite(sharedAction.spaceId)
|
||||||
// To be removed when dropping legacy groups
|
HomeActivitySharedAction.SendSpaceFeedBack -> bugReporter.openBugReportScreen(this, ReportType.SPACE_BETA_FEEDBACK)
|
||||||
if (sharedAction.clearFragment) {
|
HomeActivitySharedAction.CloseGroup -> closeGroup()
|
||||||
replaceFragment(views.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
|
|
||||||
} else {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
// we might want to delay that to avoid having the drawer animation lagging
|
|
||||||
// would be probably better to let the drawer do that? in the on closed callback?
|
|
||||||
}
|
|
||||||
is HomeActivitySharedAction.OpenSpacePreview -> {
|
|
||||||
startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId))
|
|
||||||
}
|
|
||||||
is HomeActivitySharedAction.AddSpace -> {
|
|
||||||
createSpaceResultLauncher.launch(SpaceCreationActivity.newIntent(this))
|
|
||||||
}
|
|
||||||
is HomeActivitySharedAction.ShowSpaceSettings -> {
|
|
||||||
// open bottom sheet
|
|
||||||
SpaceSettingsMenuBottomSheet
|
|
||||||
.newInstance(sharedAction.spaceId, object : SpaceSettingsMenuBottomSheet.InteractionListener {
|
|
||||||
override fun onShareSpaceSelected(spaceId: String) {
|
|
||||||
ShareSpaceBottomSheet.show(supportFragmentManager, spaceId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show(supportFragmentManager, "SPACE_SETTINGS")
|
|
||||||
}
|
|
||||||
is HomeActivitySharedAction.OpenSpaceInvite -> {
|
|
||||||
SpaceInviteBottomSheet.newInstance(sharedAction.spaceId)
|
|
||||||
.show(supportFragmentManager, "SPACE_INVITE")
|
|
||||||
}
|
|
||||||
HomeActivitySharedAction.SendSpaceFeedBack -> {
|
|
||||||
bugReporter.openBugReportScreen(this, ReportType.SPACE_BETA_FEEDBACK)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(lifecycleScope)
|
.launchIn(lifecycleScope)
|
||||||
|
@ -272,6 +242,37 @@ class HomeActivity :
|
||||||
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
|
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openGroup(shouldClearFragment: Boolean) {
|
||||||
|
views.drawerLayout.closeDrawer(GravityCompat.START)
|
||||||
|
|
||||||
|
// When switching from space to group or group to space, we need to reload the fragment
|
||||||
|
if (shouldClearFragment) {
|
||||||
|
replaceFragment(views.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSpaceSettings(spaceId: String) {
|
||||||
|
// open bottom sheet
|
||||||
|
SpaceSettingsMenuBottomSheet
|
||||||
|
.newInstance(spaceId, object : SpaceSettingsMenuBottomSheet.InteractionListener {
|
||||||
|
override fun onShareSpaceSelected(spaceId: String) {
|
||||||
|
ShareSpaceBottomSheet.show(supportFragmentManager, spaceId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show(supportFragmentManager, "SPACE_SETTINGS")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openSpaceInvite(spaceId: String) {
|
||||||
|
SpaceInviteBottomSheet.newInstance(spaceId)
|
||||||
|
.show(supportFragmentManager, "SPACE_INVITE")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun closeGroup() {
|
||||||
|
views.drawerLayout.openDrawer(GravityCompat.START)
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleShowAnalyticsOptIn() {
|
private fun handleShowAnalyticsOptIn() {
|
||||||
navigator.openAnalyticsOptIn(this)
|
navigator.openAnalyticsOptIn(this)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ import im.vector.app.core.platform.VectorSharedAction
|
||||||
sealed class HomeActivitySharedAction : VectorSharedAction {
|
sealed class HomeActivitySharedAction : VectorSharedAction {
|
||||||
object OpenDrawer : HomeActivitySharedAction()
|
object OpenDrawer : HomeActivitySharedAction()
|
||||||
object CloseDrawer : HomeActivitySharedAction()
|
object CloseDrawer : HomeActivitySharedAction()
|
||||||
data class OpenGroup(val clearFragment: Boolean) : HomeActivitySharedAction()
|
data class OpenGroup(val shouldClearFragment: Boolean) : HomeActivitySharedAction()
|
||||||
|
object CloseGroup : HomeActivitySharedAction()
|
||||||
object AddSpace : HomeActivitySharedAction()
|
object AddSpace : HomeActivitySharedAction()
|
||||||
data class OpenSpacePreview(val spaceId: String) : HomeActivitySharedAction()
|
data class OpenSpacePreview(val spaceId: String) : HomeActivitySharedAction()
|
||||||
data class OpenSpaceInvite(val spaceId: String) : HomeActivitySharedAction()
|
data class OpenSpaceInvite(val spaceId: String) : HomeActivitySharedAction()
|
||||||
|
|
|
@ -33,6 +33,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.RoomGroupingMethod
|
import im.vector.app.RoomGroupingMethod
|
||||||
import im.vector.app.core.extensions.commitTransaction
|
import im.vector.app.core.extensions.commitTransaction
|
||||||
import im.vector.app.core.extensions.toMvRxBundle
|
import im.vector.app.core.extensions.toMvRxBundle
|
||||||
|
import im.vector.app.core.platform.OnBackPressed
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
@ -69,7 +70,8 @@ class HomeDetailFragment @Inject constructor(
|
||||||
private val appStateHandler: AppStateHandler
|
private val appStateHandler: AppStateHandler
|
||||||
) : VectorBaseFragment<FragmentHomeDetailBinding>(),
|
) : VectorBaseFragment<FragmentHomeDetailBinding>(),
|
||||||
KeysBackupBanner.Delegate,
|
KeysBackupBanner.Delegate,
|
||||||
CurrentCallsView.Callback {
|
CurrentCallsView.Callback,
|
||||||
|
OnBackPressed {
|
||||||
|
|
||||||
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
||||||
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
||||||
|
@ -130,12 +132,8 @@ class HomeDetailFragment @Inject constructor(
|
||||||
|
|
||||||
viewModel.onEach(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod ->
|
viewModel.onEach(HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod ->
|
||||||
when (roomGroupingMethod) {
|
when (roomGroupingMethod) {
|
||||||
is RoomGroupingMethod.ByLegacyGroup -> {
|
is RoomGroupingMethod.ByLegacyGroup -> onGroupChange(roomGroupingMethod.groupSummary)
|
||||||
onGroupChange(roomGroupingMethod.groupSummary)
|
is RoomGroupingMethod.BySpace -> onSpaceChange(roomGroupingMethod.spaceSummary)
|
||||||
}
|
|
||||||
is RoomGroupingMethod.BySpace -> {
|
|
||||||
onSpaceChange(roomGroupingMethod.spaceSummary)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +155,6 @@ class HomeDetailFragment @Inject constructor(
|
||||||
|
|
||||||
unknownDeviceDetectorSharedViewModel.onEach { state ->
|
unknownDeviceDetectorSharedViewModel.onEach { state ->
|
||||||
state.unknownSessions.invoke()?.let { unknownDevices ->
|
state.unknownSessions.invoke()?.let { unknownDevices ->
|
||||||
// Timber.v("## Detector Triggerred in fragment - ${unknownDevices.firstOrNull()}")
|
|
||||||
if (unknownDevices.firstOrNull()?.currentSessionTrust == true) {
|
if (unknownDevices.firstOrNull()?.currentSessionTrust == true) {
|
||||||
val uid = "review_login"
|
val uid = "review_login"
|
||||||
alertManager.cancelAlert(uid)
|
alertManager.cancelAlert(uid)
|
||||||
|
@ -190,6 +187,27 @@ class HomeDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun navigateBack() {
|
||||||
|
try {
|
||||||
|
val lastSpace = appStateHandler.getSpaceBackstack().removeLast()
|
||||||
|
setCurrentSpace(lastSpace)
|
||||||
|
} catch (e: NoSuchElementException) {
|
||||||
|
navigateUpOneSpace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setCurrentSpace(spaceId: String?) {
|
||||||
|
appStateHandler.setCurrentSpace(spaceId, isForwardNavigation = false)
|
||||||
|
sharedActionViewModel.post(HomeActivitySharedAction.CloseGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateUpOneSpace() {
|
||||||
|
val parentId = getCurrentSpace()?.flattenParentIds?.lastOrNull()
|
||||||
|
setCurrentSpace(parentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCurrentSpace() = (appStateHandler.getCurrentRoomGroupingMethod() as? RoomGroupingMethod.BySpace)?.spaceSummary
|
||||||
|
|
||||||
private fun handleCallStarted() {
|
private fun handleCallStarted() {
|
||||||
dismissLoadingDialog()
|
dismissLoadingDialog()
|
||||||
val fragmentTag = HomeTab.DialPad.toFragmentTag()
|
val fragmentTag = HomeTab.DialPad.toFragmentTag()
|
||||||
|
@ -203,20 +221,16 @@ class HomeDetailFragment @Inject constructor(
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
// update notification tab if needed
|
|
||||||
updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab())
|
updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab())
|
||||||
callManager.checkForProtocolsSupportIfNeeded()
|
callManager.checkForProtocolsSupportIfNeeded()
|
||||||
|
refreshSpaceState()
|
||||||
|
}
|
||||||
|
|
||||||
// Current space/group is not live so at least refresh toolbar on resume
|
private fun refreshSpaceState() {
|
||||||
appStateHandler.getCurrentRoomGroupingMethod()?.let { roomGroupingMethod ->
|
when (val roomGroupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) {
|
||||||
when (roomGroupingMethod) {
|
is RoomGroupingMethod.ByLegacyGroup -> onGroupChange(roomGroupingMethod.groupSummary)
|
||||||
is RoomGroupingMethod.ByLegacyGroup -> {
|
is RoomGroupingMethod.BySpace -> onSpaceChange(roomGroupingMethod.spaceSummary)
|
||||||
onGroupChange(roomGroupingMethod.groupSummary)
|
else -> Unit
|
||||||
}
|
|
||||||
is RoomGroupingMethod.BySpace -> {
|
|
||||||
onSpaceChange(roomGroupingMethod.spaceSummary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,12 +274,12 @@ class HomeDetailFragment @Inject constructor(
|
||||||
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
|
viewBinder = VerificationVectorAlert.ViewBinder(user, avatarRenderer)
|
||||||
colorInt = colorProvider.getColorFromAttribute(R.attr.colorPrimary)
|
colorInt = colorProvider.getColorFromAttribute(R.attr.colorPrimary)
|
||||||
contentAction = Runnable {
|
contentAction = Runnable {
|
||||||
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
|
(weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let { activity ->
|
||||||
// mark as ignored to avoid showing it again
|
// mark as ignored to avoid showing it again
|
||||||
unknownDeviceDetectorSharedViewModel.handle(
|
unknownDeviceDetectorSharedViewModel.handle(
|
||||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(oldUnverified.mapNotNull { it.deviceId })
|
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(oldUnverified.mapNotNull { it.deviceId })
|
||||||
)
|
)
|
||||||
it.navigator.openSettings(it, EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS)
|
activity.navigator.openSettings(activity, EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dismissedAction = Runnable {
|
dismissedAction = Runnable {
|
||||||
|
@ -324,11 +338,11 @@ class HomeDetailFragment @Inject constructor(
|
||||||
withState(viewModel) {
|
withState(viewModel) {
|
||||||
when (it.roomGroupingMethod) {
|
when (it.roomGroupingMethod) {
|
||||||
is RoomGroupingMethod.ByLegacyGroup -> {
|
is RoomGroupingMethod.ByLegacyGroup -> {
|
||||||
// nothing do far
|
// do nothing
|
||||||
}
|
}
|
||||||
is RoomGroupingMethod.BySpace -> {
|
is RoomGroupingMethod.BySpace -> {
|
||||||
it.roomGroupingMethod.spaceSummary?.let {
|
it.roomGroupingMethod.spaceSummary?.let { spaceSummary ->
|
||||||
sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(it.roomId))
|
sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(spaceSummary.roomId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -348,17 +362,6 @@ class HomeDetailFragment @Inject constructor(
|
||||||
viewModel.handle(HomeDetailAction.SwitchTab(tab))
|
viewModel.handle(HomeDetailAction.SwitchTab(tab))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// val menuView = bottomNavigationView.getChildAt(0) as BottomNavigationMenuView
|
|
||||||
|
|
||||||
// bottomNavigationView.getOrCreateBadge()
|
|
||||||
// menuView.forEachIndexed { index, view ->
|
|
||||||
// val itemView = view as BottomNavigationItemView
|
|
||||||
// val badgeLayout = LayoutInflater.from(requireContext()).inflate(R.layout.vector_home_badge_unread_layout, menuView, false)
|
|
||||||
// val unreadCounterBadgeView: UnreadCounterBadgeView = badgeLayout.findViewById(R.id.actionUnreadCounterBadgeView)
|
|
||||||
// itemView.addView(badgeLayout)
|
|
||||||
// unreadCounterBadgeViews.add(index, unreadCounterBadgeView)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateUIForTab(tab: HomeTab) {
|
private fun updateUIForTab(tab: HomeTab) {
|
||||||
|
@ -436,7 +439,6 @@ class HomeDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
// Timber.v(it.toString())
|
|
||||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople)
|
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople)
|
||||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms)
|
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms)
|
||||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup)
|
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup)
|
||||||
|
@ -496,4 +498,11 @@ class HomeDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed(toolbarButton: Boolean) = if (getCurrentSpace() != null) {
|
||||||
|
navigateBack()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,3 +159,9 @@ class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs:
|
||||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), resources.displayMetrics).toInt()
|
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), resources.displayMetrics).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun SocialLoginButtonsView.render(ssoProviders: List<SsoIdentityProvider>?, mode: SocialLoginButtonsView.Mode, listener: (String?) -> Unit) {
|
||||||
|
this.mode = mode
|
||||||
|
this.ssoIdentityProviders = ssoProviders?.sorted()
|
||||||
|
this.listener = SocialLoginButtonsView.InteractionListener { listener(it) }
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ package im.vector.app.features.onboarding
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.andThen
|
import im.vector.app.core.extensions.andThen
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.onboarding.OnboardingAction.LoginOrRegister
|
import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction.LoginDirect
|
||||||
import org.matrix.android.sdk.api.MatrixPatterns.getServerName
|
import org.matrix.android.sdk.api.MatrixPatterns.getServerName
|
||||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
|
@ -33,8 +33,8 @@ class DirectLoginUseCase @Inject constructor(
|
||||||
private val uriFactory: UriFactory
|
private val uriFactory: UriFactory
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun execute(action: LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?): Result<Session> {
|
suspend fun execute(action: LoginDirect, homeServerConnectionConfig: HomeServerConnectionConfig?): Result<Session> {
|
||||||
return fetchWellKnown(action.username, homeServerConnectionConfig)
|
return fetchWellKnown(action.matrixId, homeServerConnectionConfig)
|
||||||
.andThen { wellKnown -> createSessionFor(wellKnown, action, homeServerConnectionConfig) }
|
.andThen { wellKnown -> createSessionFor(wellKnown, action, homeServerConnectionConfig) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,13 +42,13 @@ class DirectLoginUseCase @Inject constructor(
|
||||||
authenticationService.getWellKnownData(matrixId, config)
|
authenticationService.getWellKnownData(matrixId, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createSessionFor(data: WellknownResult, action: LoginOrRegister, config: HomeServerConnectionConfig?) = when (data) {
|
private suspend fun createSessionFor(data: WellknownResult, action: LoginDirect, config: HomeServerConnectionConfig?) = when (data) {
|
||||||
is WellknownResult.Prompt -> loginDirect(action, data, config)
|
is WellknownResult.Prompt -> loginDirect(action, data, config)
|
||||||
is WellknownResult.FailPrompt -> handleFailPrompt(data, action, config)
|
is WellknownResult.FailPrompt -> handleFailPrompt(data, action, config)
|
||||||
else -> onWellKnownError()
|
else -> onWellKnownError()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleFailPrompt(data: WellknownResult.FailPrompt, action: LoginOrRegister, config: HomeServerConnectionConfig?): Result<Session> {
|
private suspend fun handleFailPrompt(data: WellknownResult.FailPrompt, action: LoginDirect, config: HomeServerConnectionConfig?): Result<Session> {
|
||||||
// Relax on IS discovery if homeserver is valid
|
// Relax on IS discovery if homeserver is valid
|
||||||
val isMissingInformationToLogin = data.homeServerUrl == null || data.wellKnown == null
|
val isMissingInformationToLogin = data.homeServerUrl == null || data.wellKnown == null
|
||||||
return when {
|
return when {
|
||||||
|
@ -57,12 +57,12 @@ class DirectLoginUseCase @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loginDirect(action: LoginOrRegister, wellKnownPrompt: WellknownResult.Prompt, config: HomeServerConnectionConfig?): Result<Session> {
|
private suspend fun loginDirect(action: LoginDirect, wellKnownPrompt: WellknownResult.Prompt, config: HomeServerConnectionConfig?): Result<Session> {
|
||||||
val alteredHomeServerConnectionConfig = config?.updateWith(wellKnownPrompt) ?: fallbackConfig(action, wellKnownPrompt)
|
val alteredHomeServerConnectionConfig = config?.updateWith(wellKnownPrompt) ?: fallbackConfig(action, wellKnownPrompt)
|
||||||
return runCatching {
|
return runCatching {
|
||||||
authenticationService.directAuthentication(
|
authenticationService.directAuthentication(
|
||||||
alteredHomeServerConnectionConfig,
|
alteredHomeServerConnectionConfig,
|
||||||
action.username,
|
action.matrixId,
|
||||||
action.password,
|
action.password,
|
||||||
action.initialDeviceName
|
action.initialDeviceName
|
||||||
)
|
)
|
||||||
|
@ -74,8 +74,8 @@ class DirectLoginUseCase @Inject constructor(
|
||||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
|
identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun fallbackConfig(action: LoginOrRegister, wellKnownPrompt: WellknownResult.Prompt) = HomeServerConnectionConfig(
|
private fun fallbackConfig(action: LoginDirect, wellKnownPrompt: WellknownResult.Prompt) = HomeServerConnectionConfig(
|
||||||
homeServerUri = uriFactory.parse("https://${action.username.getServerName()}"),
|
homeServerUri = uriFactory.parse("https://${action.matrixId.getServerName()}"),
|
||||||
homeServerUriBase = uriFactory.parse(wellKnownPrompt.homeServerUrl),
|
homeServerUriBase = uriFactory.parse(wellKnownPrompt.homeServerUrl),
|
||||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
|
identityServerUri = wellKnownPrompt.identityServerUrl?.let { uriFactory.parse(it) }
|
||||||
)
|
)
|
||||||
|
|
|
@ -46,9 +46,12 @@ sealed interface OnboardingAction : VectorViewModelAction {
|
||||||
data class ResetPassword(val email: String, val newPassword: String) : OnboardingAction
|
data class ResetPassword(val email: String, val newPassword: String) : OnboardingAction
|
||||||
object ResetPasswordMailConfirmed : OnboardingAction
|
object ResetPasswordMailConfirmed : OnboardingAction
|
||||||
|
|
||||||
// Login or Register, depending on the signMode
|
sealed interface AuthenticateAction : OnboardingAction {
|
||||||
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
data class Register(val username: String, val password: String, val initialDeviceName: String) : AuthenticateAction
|
||||||
data class Register(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
|
data class Login(val username: String, val password: String, val initialDeviceName: String) : AuthenticateAction
|
||||||
|
data class LoginDirect(val matrixId: String, val password: String, val initialDeviceName: String) : AuthenticateAction
|
||||||
|
}
|
||||||
|
|
||||||
object StopEmailValidationCheck : OnboardingAction
|
object StopEmailValidationCheck : OnboardingAction
|
||||||
|
|
||||||
data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
|
data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
|
||||||
|
|
|
@ -37,6 +37,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {
|
||||||
object OpenUseCaseSelection : OnboardingViewEvents()
|
object OpenUseCaseSelection : OnboardingViewEvents()
|
||||||
object OpenServerSelection : OnboardingViewEvents()
|
object OpenServerSelection : OnboardingViewEvents()
|
||||||
object OpenCombinedRegister : OnboardingViewEvents()
|
object OpenCombinedRegister : OnboardingViewEvents()
|
||||||
|
object OpenCombinedLogin : OnboardingViewEvents()
|
||||||
object EditServerSelection : OnboardingViewEvents()
|
object EditServerSelection : OnboardingViewEvents()
|
||||||
data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
|
data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
|
||||||
object OnLoginFlowRetrieved : OnboardingViewEvents()
|
object OnLoginFlowRetrieved : OnboardingViewEvents()
|
||||||
|
|
|
@ -42,6 +42,7 @@ import im.vector.app.features.login.LoginMode
|
||||||
import im.vector.app.features.login.ReAuthHelper
|
import im.vector.app.features.login.ReAuthHelper
|
||||||
import im.vector.app.features.login.ServerType
|
import im.vector.app.features.login.ServerType
|
||||||
import im.vector.app.features.login.SignMode
|
import im.vector.app.features.login.SignMode
|
||||||
|
import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
|
||||||
import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
|
import im.vector.app.features.onboarding.StartAuthenticationFlowUseCase.StartAuthenticationResult
|
||||||
import im.vector.app.features.onboarding.ftueauth.MatrixOrgRegistrationStagesComparator
|
import im.vector.app.features.onboarding.ftueauth.MatrixOrgRegistrationStagesComparator
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -139,8 +140,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
|
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
|
||||||
is OnboardingAction.InitWith -> handleInitWith(action)
|
is OnboardingAction.InitWith -> handleInitWith(action)
|
||||||
is OnboardingAction.HomeServerChange -> withAction(action) { handleHomeserverChange(action) }
|
is OnboardingAction.HomeServerChange -> withAction(action) { handleHomeserverChange(action) }
|
||||||
is OnboardingAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
|
is AuthenticateAction -> withAction(action) { handleAuthenticateAction(action) }
|
||||||
is OnboardingAction.Register -> handleRegisterWith(action).also { lastAction = action }
|
|
||||||
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
||||||
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||||
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
||||||
|
@ -165,6 +165,14 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
block(action)
|
block(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleAuthenticateAction(action: AuthenticateAction) {
|
||||||
|
when (action) {
|
||||||
|
is AuthenticateAction.Register -> handleRegisterWith(action)
|
||||||
|
is AuthenticateAction.Login -> handleLogin(action)
|
||||||
|
is AuthenticateAction.LoginDirect -> handleDirectLogin(action, homeServerConnectionConfig = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleSplashAction(resetConfig: Boolean, onboardingFlow: OnboardingFlow) {
|
private fun handleSplashAction(resetConfig: Boolean, onboardingFlow: OnboardingFlow) {
|
||||||
if (resetConfig) {
|
if (resetConfig) {
|
||||||
loginConfig = null
|
loginConfig = null
|
||||||
|
@ -188,16 +196,21 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
|
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
|
||||||
val nextOnboardingStep = when (onboardingFlow) {
|
when (onboardingFlow) {
|
||||||
OnboardingFlow.SignUp -> if (vectorFeatures.isOnboardingUseCaseEnabled()) {
|
OnboardingFlow.SignUp -> {
|
||||||
|
_viewEvents.post(
|
||||||
|
if (vectorFeatures.isOnboardingUseCaseEnabled()) {
|
||||||
OnboardingViewEvents.OpenUseCaseSelection
|
OnboardingViewEvents.OpenUseCaseSelection
|
||||||
} else {
|
} else {
|
||||||
OnboardingViewEvents.OpenServerSelection
|
OnboardingViewEvents.OpenServerSelection
|
||||||
}
|
}
|
||||||
OnboardingFlow.SignIn,
|
)
|
||||||
OnboardingFlow.SignInSignUp -> OnboardingViewEvents.OpenServerSelection
|
}
|
||||||
|
OnboardingFlow.SignIn -> if (vectorFeatures.isOnboardingCombinedLoginEnabled()) {
|
||||||
|
handle(OnboardingAction.HomeServerChange.SelectHomeServer(defaultHomeserverUrl))
|
||||||
|
} else _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
|
||||||
|
OnboardingFlow.SignInSignUp -> _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
|
||||||
}
|
}
|
||||||
_viewEvents.post(nextOnboardingStep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
||||||
|
@ -209,7 +222,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
||||||
?.let { startAuthenticationFlow(finalLastAction, it) }
|
?.let { startAuthenticationFlow(finalLastAction, it) }
|
||||||
}
|
}
|
||||||
is OnboardingAction.LoginOrRegister ->
|
is AuthenticateAction.LoginDirect ->
|
||||||
handleDirectLogin(
|
handleDirectLogin(
|
||||||
finalLastAction,
|
finalLastAction,
|
||||||
HomeServerConnectionConfig.Builder()
|
HomeServerConnectionConfig.Builder()
|
||||||
|
@ -307,7 +320,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
private fun OnboardingViewState.hasSelectedMatrixOrg() = selectedHomeserver.userFacingUrl == matrixOrgUrl
|
private fun OnboardingViewState.hasSelectedMatrixOrg() = selectedHomeserver.userFacingUrl == matrixOrgUrl
|
||||||
|
|
||||||
private fun handleRegisterWith(action: OnboardingAction.Register) {
|
private fun handleRegisterWith(action: AuthenticateAction.Register) {
|
||||||
reAuthHelper.data = action.password
|
reAuthHelper.data = action.password
|
||||||
handleRegisterAction(
|
handleRegisterAction(
|
||||||
RegisterAction.CreateAccount(
|
RegisterAction.CreateAccount(
|
||||||
|
@ -482,16 +495,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLoginOrRegister(action: OnboardingAction.LoginOrRegister) = withState { state ->
|
private fun handleDirectLogin(action: AuthenticateAction.LoginDirect, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
||||||
when (state.signMode) {
|
|
||||||
SignMode.Unknown -> error("Developer error, invalid sign mode")
|
|
||||||
SignMode.SignIn -> handleLogin(action)
|
|
||||||
SignMode.SignUp -> handleRegisterWith(OnboardingAction.Register(action.username, action.password, action.initialDeviceName))
|
|
||||||
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDirectLogin(action: OnboardingAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
|
||||||
setState { copy(isLoading = true) }
|
setState { copy(isLoading = true) }
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
directLoginUseCase.execute(action, homeServerConnectionConfig).fold(
|
directLoginUseCase.execute(action, homeServerConnectionConfig).fold(
|
||||||
|
@ -504,7 +508,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleLogin(action: OnboardingAction.LoginOrRegister) {
|
private fun handleLogin(action: AuthenticateAction.Login) {
|
||||||
val safeLoginWizard = loginWizard
|
val safeLoginWizard = loginWizard
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
if (safeLoginWizard == null) {
|
||||||
|
@ -648,7 +652,11 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
when (trigger) {
|
when (trigger) {
|
||||||
is OnboardingAction.HomeServerChange.EditHomeServer -> {
|
is OnboardingAction.HomeServerChange.EditHomeServer -> {
|
||||||
when (awaitState().onboardingFlow) {
|
when (awaitState().onboardingFlow) {
|
||||||
OnboardingFlow.SignUp -> internalRegisterAction(RegisterAction.StartRegistration) { _ ->
|
OnboardingFlow.SignUp -> internalRegisterAction(RegisterAction.StartRegistration) {
|
||||||
|
updateServerSelection(config, serverTypeOverride, authResult)
|
||||||
|
_viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
|
||||||
|
}
|
||||||
|
OnboardingFlow.SignIn -> {
|
||||||
updateServerSelection(config, serverTypeOverride, authResult)
|
updateServerSelection(config, serverTypeOverride, authResult)
|
||||||
_viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
|
_viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
|
||||||
}
|
}
|
||||||
|
@ -661,7 +669,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
when (awaitState().onboardingFlow) {
|
when (awaitState().onboardingFlow) {
|
||||||
OnboardingFlow.SignIn -> {
|
OnboardingFlow.SignIn -> {
|
||||||
updateSignMode(SignMode.SignIn)
|
updateSignMode(SignMode.SignIn)
|
||||||
_viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignIn))
|
when (vectorFeatures.isOnboardingCombinedLoginEnabled()) {
|
||||||
|
true -> _viewEvents.post(OnboardingViewEvents.OpenCombinedLogin)
|
||||||
|
false -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignIn))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OnboardingFlow.SignUp -> {
|
OnboardingFlow.SignUp -> {
|
||||||
updateSignMode(SignMode.SignUp)
|
updateSignMode(SignMode.SignUp)
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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.onboarding.ftueauth
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.autofill.HintConstants
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.content
|
||||||
|
import im.vector.app.core.extensions.editText
|
||||||
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
|
import im.vector.app.core.extensions.hidePassword
|
||||||
|
import im.vector.app.core.extensions.realignPercentagesToParent
|
||||||
|
import im.vector.app.core.extensions.setOnImeDoneListener
|
||||||
|
import im.vector.app.core.extensions.toReducedUrl
|
||||||
|
import im.vector.app.databinding.FragmentFtueCombinedLoginBinding
|
||||||
|
import im.vector.app.features.login.LoginMode
|
||||||
|
import im.vector.app.features.login.SSORedirectRouterActivity
|
||||||
|
import im.vector.app.features.login.SocialLoginButtonsView
|
||||||
|
import im.vector.app.features.login.render
|
||||||
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class FtueAuthCombinedLoginFragment @Inject constructor(
|
||||||
|
private val loginFieldsValidation: LoginFieldsValidation,
|
||||||
|
private val loginErrorParser: LoginErrorParser
|
||||||
|
) : AbstractSSOFtueAuthFragment<FragmentFtueCombinedLoginBinding>() {
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueCombinedLoginBinding {
|
||||||
|
return FragmentFtueCombinedLoginBinding.inflate(inflater, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupSubmitButton()
|
||||||
|
views.loginRoot.realignPercentagesToParent()
|
||||||
|
views.editServerButton.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection)) }
|
||||||
|
views.loginPasswordInput.setOnImeDoneListener { submit() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSubmitButton() {
|
||||||
|
views.loginSubmit.setOnClickListener { submit() }
|
||||||
|
observeContentChangesAndResetErrors(views.loginInput, views.loginPasswordInput, views.loginSubmit)
|
||||||
|
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun submit() {
|
||||||
|
cleanupUi()
|
||||||
|
loginFieldsValidation.validate(views.loginInput.content(), views.loginPasswordInput.content())
|
||||||
|
.onUsernameOrIdError { views.loginInput.error = it }
|
||||||
|
.onPasswordError { views.loginPasswordInput.error = it }
|
||||||
|
.onValid { usernameOrId, password ->
|
||||||
|
val initialDeviceName = getString(R.string.login_default_session_public_name)
|
||||||
|
viewModel.handle(OnboardingAction.AuthenticateAction.Login(usernameOrId, password, initialDeviceName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanupUi() {
|
||||||
|
views.loginSubmit.hideKeyboard()
|
||||||
|
views.loginInput.error = null
|
||||||
|
views.loginPasswordInput.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetViewModel() {
|
||||||
|
viewModel.handle(OnboardingAction.ResetAuthenticationAttempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(throwable: Throwable) {
|
||||||
|
// Trick to display the error without text.
|
||||||
|
views.loginInput.error = " "
|
||||||
|
loginErrorParser.parse(throwable, views.loginPasswordInput.content())
|
||||||
|
.onUnknown { super.onError(it) }
|
||||||
|
.onUsernameOrIdError { views.loginInput.error = it }
|
||||||
|
.onPasswordError { views.loginPasswordInput.error = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateWithState(state: OnboardingViewState) {
|
||||||
|
setupUi(state)
|
||||||
|
setupAutoFill()
|
||||||
|
|
||||||
|
views.selectedServerName.text = state.selectedHomeserver.userFacingUrl.toReducedUrl()
|
||||||
|
views.selectedServerDescription.text = state.selectedHomeserver.description
|
||||||
|
|
||||||
|
if (state.isLoading) {
|
||||||
|
// Ensure password is hidden
|
||||||
|
views.loginPasswordInput.editText().hidePassword()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupUi(state: OnboardingViewState) {
|
||||||
|
when (state.selectedHomeserver.preferredLoginMode) {
|
||||||
|
is LoginMode.SsoAndPassword -> {
|
||||||
|
showUsernamePassword()
|
||||||
|
renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
|
||||||
|
}
|
||||||
|
is LoginMode.Sso -> {
|
||||||
|
hideUsernamePassword()
|
||||||
|
renderSsoProviders(state.deviceId, state.selectedHomeserver.preferredLoginMode.ssoIdentityProviders)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
showUsernamePassword()
|
||||||
|
hideSsoProviders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
|
||||||
|
views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
|
||||||
|
views.ssoButtonsHeader.isVisible = views.ssoGroup.isVisible && views.loginEntryGroup.isVisible
|
||||||
|
views.ssoButtons.render(ssoProviders, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
|
||||||
|
viewModel.getSsoUrl(
|
||||||
|
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||||
|
deviceId = deviceId,
|
||||||
|
providerId = id
|
||||||
|
)?.let { openInCustomTab(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideSsoProviders() {
|
||||||
|
views.ssoGroup.isVisible = false
|
||||||
|
views.ssoButtons.ssoIdentityProviders = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideUsernamePassword() {
|
||||||
|
views.loginEntryGroup.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showUsernamePassword() {
|
||||||
|
views.loginEntryGroup.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupAutoFill() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
views.loginInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
|
||||||
|
views.loginPasswordInput.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
|
||||||
import androidx.autofill.HintConstants
|
import androidx.autofill.HintConstants
|
||||||
import androidx.core.text.isDigitsOnly
|
import androidx.core.text.isDigitsOnly
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -31,22 +30,22 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.content
|
import im.vector.app.core.extensions.content
|
||||||
import im.vector.app.core.extensions.editText
|
import im.vector.app.core.extensions.editText
|
||||||
import im.vector.app.core.extensions.hasContentFlow
|
|
||||||
import im.vector.app.core.extensions.hasSurroundingSpaces
|
import im.vector.app.core.extensions.hasSurroundingSpaces
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
import im.vector.app.core.extensions.hidePassword
|
import im.vector.app.core.extensions.hidePassword
|
||||||
import im.vector.app.core.extensions.realignPercentagesToParent
|
import im.vector.app.core.extensions.realignPercentagesToParent
|
||||||
|
import im.vector.app.core.extensions.setOnImeDoneListener
|
||||||
import im.vector.app.core.extensions.toReducedUrl
|
import im.vector.app.core.extensions.toReducedUrl
|
||||||
import im.vector.app.databinding.FragmentFtueCombinedRegisterBinding
|
import im.vector.app.databinding.FragmentFtueCombinedRegisterBinding
|
||||||
import im.vector.app.features.login.LoginMode
|
import im.vector.app.features.login.LoginMode
|
||||||
import im.vector.app.features.login.SSORedirectRouterActivity
|
import im.vector.app.features.login.SSORedirectRouterActivity
|
||||||
import im.vector.app.features.login.SocialLoginButtonsView
|
import im.vector.app.features.login.SocialLoginButtonsView
|
||||||
|
import im.vector.app.features.login.render
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
|
import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
|
||||||
import im.vector.app.features.onboarding.OnboardingViewEvents
|
import im.vector.app.features.onboarding.OnboardingViewEvents
|
||||||
import im.vector.app.features.onboarding.OnboardingViewState
|
import im.vector.app.features.onboarding.OnboardingViewState
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||||
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||||
import org.matrix.android.sdk.api.failure.isInvalidUsername
|
import org.matrix.android.sdk.api.failure.isInvalidUsername
|
||||||
|
@ -66,36 +65,16 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setupSubmitButton()
|
setupSubmitButton()
|
||||||
views.createAccountRoot.realignPercentagesToParent()
|
views.createAccountRoot.realignPercentagesToParent()
|
||||||
views.editServerButton.debouncedClicks {
|
views.editServerButton.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection)) }
|
||||||
viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.EditServerSelection))
|
views.createAccountPasswordInput.setOnImeDoneListener { submit() }
|
||||||
}
|
|
||||||
|
|
||||||
views.createAccountPasswordInput.editText().setOnEditorActionListener { _, actionId, _ ->
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
|
||||||
submit()
|
|
||||||
return@setOnEditorActionListener true
|
|
||||||
}
|
|
||||||
return@setOnEditorActionListener false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSubmitButton() {
|
private fun setupSubmitButton() {
|
||||||
views.createAccountSubmit.setOnClickListener { submit() }
|
views.createAccountSubmit.setOnClickListener { submit() }
|
||||||
observeInputFields()
|
observeContentChangesAndResetErrors(views.createAccountInput, views.createAccountPasswordInput, views.createAccountSubmit)
|
||||||
.onEach {
|
|
||||||
views.createAccountPasswordInput.error = null
|
|
||||||
views.createAccountInput.error = null
|
|
||||||
views.createAccountSubmit.isEnabled = it
|
|
||||||
}
|
|
||||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeInputFields() = combine(
|
|
||||||
views.createAccountInput.hasContentFlow { it.trim() },
|
|
||||||
views.createAccountPasswordInput.hasContentFlow(),
|
|
||||||
transform = { isLoginNotEmpty, isPasswordNotEmpty -> isLoginNotEmpty && isPasswordNotEmpty }
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun submit() {
|
private fun submit() {
|
||||||
withState(viewModel) { state ->
|
withState(viewModel) { state ->
|
||||||
cleanupUi()
|
cleanupUi()
|
||||||
|
@ -119,7 +98,7 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
viewModel.handle(OnboardingAction.Register(login, password, getString(R.string.login_default_session_public_name)))
|
viewModel.handle(AuthenticateAction.Register(login, password, getString(R.string.login_default_session_public_name)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,9 +164,7 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu
|
||||||
|
|
||||||
private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
|
private fun renderSsoProviders(deviceId: String?, ssoProviders: List<SsoIdentityProvider>?) {
|
||||||
views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
|
views.ssoGroup.isVisible = ssoProviders?.isNotEmpty() == true
|
||||||
views.ssoButtons.mode = SocialLoginButtonsView.Mode.MODE_CONTINUE
|
views.ssoButtons.render(ssoProviders, SocialLoginButtonsView.Mode.MODE_CONTINUE) { id ->
|
||||||
views.ssoButtons.ssoIdentityProviders = ssoProviders?.sorted()
|
|
||||||
views.ssoButtons.listener = SocialLoginButtonsView.InteractionListener { id ->
|
|
||||||
viewModel.getSsoUrl(
|
viewModel.getSsoUrl(
|
||||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||||
deviceId = deviceId,
|
deviceId = deviceId,
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.autofill.HintConstants
|
||||||
import androidx.core.text.isDigitsOnly
|
import androidx.core.text.isDigitsOnly
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
|
@ -119,6 +120,7 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun submit() {
|
private fun submit() {
|
||||||
|
withState(viewModel) { state ->
|
||||||
cleanupUi()
|
cleanupUi()
|
||||||
|
|
||||||
val login = views.loginField.text.toString()
|
val login = views.loginField.text.toString()
|
||||||
|
@ -152,7 +154,9 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
viewModel.handle(OnboardingAction.LoginOrRegister(login, password, getString(R.string.login_default_session_public_name)))
|
val initialDeviceName = getString(R.string.login_default_session_public_name)
|
||||||
|
viewModel.handle(state.signMode.toAuthenticateAction(login, password, initialDeviceName))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,9 +228,14 @@ class FtueAuthVariant(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
OnboardingViewEvents.OnHomeserverEdited -> activity.popBackstack()
|
OnboardingViewEvents.OnHomeserverEdited -> activity.popBackstack()
|
||||||
|
OnboardingViewEvents.OpenCombinedLogin -> onStartCombinedLogin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onStartCombinedLogin() {
|
||||||
|
addRegistrationStageFragmentToBackstack(FtueAuthCombinedLoginFragment::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
private fun onRegistrationFlow(viewEvents: OnboardingViewEvents.RegistrationFlowResult) {
|
private fun onRegistrationFlow(viewEvents: OnboardingViewEvents.RegistrationFlowResult) {
|
||||||
when {
|
when {
|
||||||
registrationShouldFallback(viewEvents) -> displayFallbackWebDialog()
|
registrationShouldFallback(viewEvents) -> displayFallbackWebDialog()
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.onboarding.ftueauth
|
||||||
|
|
||||||
|
import android.widget.Button
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import im.vector.app.core.extensions.hasContentFlow
|
||||||
|
import im.vector.app.features.login.SignMode
|
||||||
|
import im.vector.app.features.onboarding.OnboardingAction
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
fun SignMode.toAuthenticateAction(login: String, password: String, initialDeviceName: String): OnboardingAction.AuthenticateAction {
|
||||||
|
return when (this) {
|
||||||
|
SignMode.Unknown -> error("developer error")
|
||||||
|
SignMode.SignUp -> OnboardingAction.AuthenticateAction.Register(username = login, password, initialDeviceName)
|
||||||
|
SignMode.SignIn -> OnboardingAction.AuthenticateAction.Login(username = login, password, initialDeviceName)
|
||||||
|
SignMode.SignInWithMatrixId -> OnboardingAction.AuthenticateAction.LoginDirect(matrixId = login, password, initialDeviceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flow to monitor content changes from both username/id and password fields,
|
||||||
|
* clearing errors and enabling/disabling the submission button on non empty content changes.
|
||||||
|
*/
|
||||||
|
fun observeContentChangesAndResetErrors(username: TextInputLayout, password: TextInputLayout, submit: Button): Flow<*> {
|
||||||
|
return combine(
|
||||||
|
username.hasContentFlow { it.trim() },
|
||||||
|
password.hasContentFlow(),
|
||||||
|
transform = { usernameHasContent, passwordHasContent -> usernameHasContent && passwordHasContent }
|
||||||
|
).onEach {
|
||||||
|
username.error = null
|
||||||
|
password.error = null
|
||||||
|
submit.isEnabled = it
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* 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.onboarding.ftueauth
|
||||||
|
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.onboarding.ftueauth.LoginErrorParser.LoginErrorResult
|
||||||
|
import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||||
|
import org.matrix.android.sdk.api.failure.isInvalidUsername
|
||||||
|
import org.matrix.android.sdk.api.failure.isLoginEmailUnknown
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LoginErrorParser @Inject constructor(
|
||||||
|
private val errorFormatter: ErrorFormatter,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
) {
|
||||||
|
fun parse(throwable: Throwable, password: String): LoginErrorResult {
|
||||||
|
return when {
|
||||||
|
throwable.isInvalidUsername() -> {
|
||||||
|
LoginErrorResult(throwable, usernameOrIdError = errorFormatter.toHumanReadable(throwable))
|
||||||
|
}
|
||||||
|
throwable.isLoginEmailUnknown() -> {
|
||||||
|
LoginErrorResult(throwable, usernameOrIdError = stringProvider.getString(R.string.login_login_with_email_error))
|
||||||
|
}
|
||||||
|
throwable.isInvalidPassword() && password.hasSurroundingSpaces() -> {
|
||||||
|
LoginErrorResult(throwable, passwordError = stringProvider.getString(R.string.auth_invalid_login_param_space_in_password))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
LoginErrorResult(throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.hasSurroundingSpaces() = trim() != this
|
||||||
|
|
||||||
|
data class LoginErrorResult(val cause: Throwable, val usernameOrIdError: String? = null, val passwordError: String? = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoginErrorResult.onUnknown(action: (Throwable) -> Unit): LoginErrorResult {
|
||||||
|
when {
|
||||||
|
usernameOrIdError == null && passwordError == null -> action(cause)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoginErrorResult.onUsernameOrIdError(action: (String) -> Unit): LoginErrorResult {
|
||||||
|
usernameOrIdError?.let(action)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoginErrorResult.onPasswordError(action: (String) -> Unit): LoginErrorResult {
|
||||||
|
passwordError?.let(action)
|
||||||
|
return this
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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.onboarding.ftueauth
|
||||||
|
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LoginFieldsValidation @Inject constructor(
|
||||||
|
private val stringProvider: StringProvider
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun validate(usernameOrId: String, password: String): LoginValidationResult {
|
||||||
|
return LoginValidationResult(usernameOrId, password, validateUsernameOrId(usernameOrId), validatePassword(password))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateUsernameOrId(usernameOrId: String): String? {
|
||||||
|
val accountError = when {
|
||||||
|
usernameOrId.isEmpty() -> stringProvider.getString(R.string.error_empty_field_enter_user_name)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return accountError
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validatePassword(password: String): String? {
|
||||||
|
val passwordError = when {
|
||||||
|
password.isEmpty() -> stringProvider.getString(R.string.error_empty_field_your_password)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
return passwordError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoginValidationResult.onValid(action: (String, String) -> Unit): LoginValidationResult {
|
||||||
|
when {
|
||||||
|
usernameOrIdError == null && passwordError == null -> action(usernameOrId, password)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoginValidationResult.onUsernameOrIdError(action: (String) -> Unit): LoginValidationResult {
|
||||||
|
usernameOrIdError?.let(action)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun LoginValidationResult.onPasswordError(action: (String) -> Unit): LoginValidationResult {
|
||||||
|
passwordError?.let(action)
|
||||||
|
return this
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.onboarding.ftueauth
|
||||||
|
|
||||||
|
data class LoginValidationResult(
|
||||||
|
val usernameOrId: String,
|
||||||
|
val password: String,
|
||||||
|
val usernameOrIdError: String?,
|
||||||
|
val passwordError: String?
|
||||||
|
)
|
244
vector/src/main/res/layout/fragment_ftue_combined_login.xml
Normal file
244
vector/src/main/res/layout/fragment_ftue_combined_login.xml
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/LoginFormScrollView"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingBottom="0dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/loginRoot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/loginGutterStart"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_start_percent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/loginGutterEnd"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintGuide_percent="@dimen/ftue_auth_gutter_end_percent" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/headerSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/loginHeaderTitle"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_bias="0"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/loginHeaderTitle"
|
||||||
|
style="@style/Widget.Vector.TextView.Title.Medium"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/ftue_auth_welcome_back_title"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/titleContentSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/headerSpacing" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/titleContentSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/chooseYourServerHeader"
|
||||||
|
app:layout_constraintHeight_percent="0.03"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/loginHeaderTitle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/chooseYourServerHeader"
|
||||||
|
style="@style/Widget.Vector.TextView.Caption"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_choose_server_header"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/selectedServerName"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/editServerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/titleContentSpacing" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/selectedServerName"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/selectedServerDescription"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/editServerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/chooseYourServerHeader" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/selectedServerDescription"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:textColor="?vctr_content_tertiary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/serverSelectionSpacing"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/editServerButton"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/selectedServerName" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/editServerButton"
|
||||||
|
style="@style/Widget.Vector.Button.Outlined"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_edit_server_selection"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/selectedServerDescription"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/chooseYourServerHeader" />
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/serverSelectionSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/loginInput"
|
||||||
|
app:layout_constraintHeight_percent="0.05"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/selectedServerDescription" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?vctr_content_quaternary"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/serverSelectionSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/serverSelectionSpacing" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/loginEntryGroup"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:constraint_referenced_ids="loginInput,loginPasswordInput,entrySpacing,actionSpacing,loginSubmit" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/loginInput"
|
||||||
|
style="@style/Widget.Vector.TextInputLayout.Username"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/username"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/entrySpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/serverSelectionSpacing">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:imeOptions="actionNext"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:nextFocusForward="@id/loginPasswordInput" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/entrySpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/loginPasswordInput"
|
||||||
|
app:layout_constraintHeight_percent="0.03"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/loginInput" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/loginPasswordInput"
|
||||||
|
style="@style/Widget.Vector.TextInputLayout.Password"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/login_signup_password_hint"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/actionSpacing"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/entrySpacing">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:imeOptions="actionDone"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:id="@+id/actionSpacing"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/loginSubmit"
|
||||||
|
app:layout_constraintHeight_percent="0.02"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/loginPasswordInput" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/loginSubmit"
|
||||||
|
style="@style/Widget.Vector.Button.Login"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/login_signup_submit"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/actionSpacing" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Group
|
||||||
|
android:id="@+id/ssoGroup"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:constraint_referenced_ids="ssoButtonsHeader,ssoButtons"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/ssoButtonsHeader"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/loginSubmit"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ssoButtonsHeader"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle.Medium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="@string/ftue_auth_create_account_sso_section_header"
|
||||||
|
android:textColor="?vctr_content_secondary"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/ssoButtons"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/loginSubmit" />
|
||||||
|
|
||||||
|
<im.vector.app.features.login.SocialLoginButtonsView
|
||||||
|
android:id="@+id/ssoButtons"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/loginGutterEnd"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/loginGutterStart"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/ssoButtonsHeader"
|
||||||
|
tools:signMode="signup" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
|
@ -19,6 +19,8 @@
|
||||||
<string name="ftue_auth_create_account_matrix_dot_org_server_description">Join millions for free on the largest public server</string>
|
<string name="ftue_auth_create_account_matrix_dot_org_server_description">Join millions for free on the largest public server</string>
|
||||||
<string name="ftue_auth_create_account_edit_server_selection">Edit</string>
|
<string name="ftue_auth_create_account_edit_server_selection">Edit</string>
|
||||||
|
|
||||||
|
<string name="ftue_auth_welcome_back_title">Welcome back!</string>
|
||||||
|
|
||||||
<string name="ftue_auth_choose_server_title">Choose your server</string>
|
<string name="ftue_auth_choose_server_title">Choose your server</string>
|
||||||
<string name="ftue_auth_choose_server_subtitle">What is the address of your server? Server is like a home for all your data.</string>
|
<string name="ftue_auth_choose_server_subtitle">What is the address of your server? Server is like a home for all your data.</string>
|
||||||
<string name="ftue_auth_choose_server_entry_hint">Server URL</string>
|
<string name="ftue_auth_choose_server_entry_hint">Server URL</string>
|
||||||
|
|
|
@ -32,13 +32,13 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
import org.matrix.android.sdk.api.auth.data.WellKnown
|
import org.matrix.android.sdk.api.auth.data.WellKnown
|
||||||
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
|
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
|
||||||
|
|
||||||
private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
|
private val A_DIRECT_LOGIN_ACTION = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
|
||||||
private val A_WELLKNOWN_SUCCESS_RESULT = WellknownResult.Prompt("https://homeserverurl.com", identityServerUrl = null, WellKnown())
|
private val A_WELLKNOWN_SUCCESS_RESULT = WellknownResult.Prompt("https://homeserverurl.com", identityServerUrl = null, WellKnown())
|
||||||
private val A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT = WellknownResult.FailPrompt("https://homeserverurl.com", WellKnown())
|
private val A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT = WellknownResult.FailPrompt("https://homeserverurl.com", WellKnown())
|
||||||
private val A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT = WellknownResult.FailPrompt(null, null)
|
private val A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT = WellknownResult.FailPrompt(null, null)
|
||||||
private val NO_HOMESERVER_CONFIG: HomeServerConnectionConfig? = null
|
private val NO_HOMESERVER_CONFIG: HomeServerConnectionConfig? = null
|
||||||
private val A_FALLBACK_CONFIG: HomeServerConnectionConfig = HomeServerConnectionConfig(
|
private val A_FALLBACK_CONFIG: HomeServerConnectionConfig = HomeServerConnectionConfig(
|
||||||
homeServerUri = FakeUri("https://${A_LOGIN_OR_REGISTER_ACTION.username.getServerName()}").instance,
|
homeServerUri = FakeUri("https://${A_DIRECT_LOGIN_ACTION.matrixId.getServerName()}").instance,
|
||||||
homeServerUriBase = FakeUri(A_WELLKNOWN_SUCCESS_RESULT.homeServerUrl).instance,
|
homeServerUriBase = FakeUri(A_WELLKNOWN_SUCCESS_RESULT.homeServerUrl).instance,
|
||||||
identityServerUri = null
|
identityServerUri = null
|
||||||
)
|
)
|
||||||
|
@ -54,11 +54,11 @@ class DirectLoginUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when logging in directly, then returns success with direct session result`() = runTest {
|
fun `when logging in directly, then returns success with direct session result`() = runTest {
|
||||||
fakeAuthenticationService.givenWellKnown(A_LOGIN_OR_REGISTER_ACTION.username, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
|
fakeAuthenticationService.givenWellKnown(A_DIRECT_LOGIN_ACTION.matrixId, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
|
||||||
val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
|
val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
|
||||||
fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
|
fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
|
||||||
|
|
||||||
val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
||||||
|
|
||||||
result shouldBeEqualTo Result.success(fakeSession)
|
result shouldBeEqualTo Result.success(fakeSession)
|
||||||
}
|
}
|
||||||
|
@ -66,14 +66,14 @@ class DirectLoginUseCaseTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given wellknown fails with content, when logging in directly, then returns success with direct session result`() = runTest {
|
fun `given wellknown fails with content, when logging in directly, then returns success with direct session result`() = runTest {
|
||||||
fakeAuthenticationService.givenWellKnown(
|
fakeAuthenticationService.givenWellKnown(
|
||||||
A_LOGIN_OR_REGISTER_ACTION.username,
|
A_DIRECT_LOGIN_ACTION.matrixId,
|
||||||
config = NO_HOMESERVER_CONFIG,
|
config = NO_HOMESERVER_CONFIG,
|
||||||
result = A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT
|
result = A_WELLKNOWN_FAILED_WITH_CONTENT_RESULT
|
||||||
)
|
)
|
||||||
val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
|
val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
|
||||||
fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
|
fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
|
||||||
|
|
||||||
val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
||||||
|
|
||||||
result shouldBeEqualTo Result.success(fakeSession)
|
result shouldBeEqualTo Result.success(fakeSession)
|
||||||
}
|
}
|
||||||
|
@ -81,14 +81,14 @@ class DirectLoginUseCaseTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given wellknown fails without content, when logging in directly, then returns well known error`() = runTest {
|
fun `given wellknown fails without content, when logging in directly, then returns well known error`() = runTest {
|
||||||
fakeAuthenticationService.givenWellKnown(
|
fakeAuthenticationService.givenWellKnown(
|
||||||
A_LOGIN_OR_REGISTER_ACTION.username,
|
A_DIRECT_LOGIN_ACTION.matrixId,
|
||||||
config = NO_HOMESERVER_CONFIG,
|
config = NO_HOMESERVER_CONFIG,
|
||||||
result = A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT
|
result = A_WELLKNOWN_FAILED_WITHOUT_CONTENT_RESULT
|
||||||
)
|
)
|
||||||
val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
|
val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
|
||||||
fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
|
fakeAuthenticationService.givenDirectAuthentication(A_FALLBACK_CONFIG, username, password, initialDeviceName, result = fakeSession)
|
||||||
|
|
||||||
val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
||||||
|
|
||||||
result should { this.isFailure }
|
result should { this.isFailure }
|
||||||
result should { this.exceptionOrNull() is Exception }
|
result should { this.exceptionOrNull() is Exception }
|
||||||
|
@ -97,20 +97,20 @@ class DirectLoginUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given wellknown throws, when logging in directly, then returns failure result with original cause`() = runTest {
|
fun `given wellknown throws, when logging in directly, then returns failure result with original cause`() = runTest {
|
||||||
fakeAuthenticationService.givenWellKnownThrows(A_LOGIN_OR_REGISTER_ACTION.username, config = NO_HOMESERVER_CONFIG, cause = AN_ERROR)
|
fakeAuthenticationService.givenWellKnownThrows(A_DIRECT_LOGIN_ACTION.matrixId, config = NO_HOMESERVER_CONFIG, cause = AN_ERROR)
|
||||||
|
|
||||||
val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
||||||
|
|
||||||
result shouldBeEqualTo Result.failure(AN_ERROR)
|
result shouldBeEqualTo Result.failure(AN_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given direct authentication throws, when logging in directly, then returns failure result with original cause`() = runTest {
|
fun `given direct authentication throws, when logging in directly, then returns failure result with original cause`() = runTest {
|
||||||
fakeAuthenticationService.givenWellKnown(A_LOGIN_OR_REGISTER_ACTION.username, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
|
fakeAuthenticationService.givenWellKnown(A_DIRECT_LOGIN_ACTION.matrixId, config = NO_HOMESERVER_CONFIG, result = A_WELLKNOWN_SUCCESS_RESULT)
|
||||||
val (username, password, initialDeviceName) = A_LOGIN_OR_REGISTER_ACTION
|
val (username, password, initialDeviceName) = A_DIRECT_LOGIN_ACTION
|
||||||
fakeAuthenticationService.givenDirectAuthenticationThrows(A_FALLBACK_CONFIG, username, password, initialDeviceName, cause = AN_ERROR)
|
fakeAuthenticationService.givenDirectAuthenticationThrows(A_FALLBACK_CONFIG, username, password, initialDeviceName, cause = AN_ERROR)
|
||||||
|
|
||||||
val result = useCase.execute(A_LOGIN_OR_REGISTER_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
val result = useCase.execute(A_DIRECT_LOGIN_ACTION, homeServerConnectionConfig = NO_HOMESERVER_CONFIG)
|
||||||
|
|
||||||
result shouldBeEqualTo Result.failure(AN_ERROR)
|
result shouldBeEqualTo Result.failure(AN_ERROR)
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ private val A_RESULT_IGNORED_REGISTER_ACTION = RegisterAction.SendAgainThreePid
|
||||||
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
private val A_HOMESERVER_CAPABILITIES = aHomeServerCapabilities(canChangeDisplayName = true, canChangeAvatar = true)
|
||||||
private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
|
private val AN_IGNORED_FLOW_RESULT = FlowResult(missingStages = emptyList(), completedStages = emptyList())
|
||||||
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.NextStep(AN_IGNORED_FLOW_RESULT)
|
private val ANY_CONTINUING_REGISTRATION_RESULT = RegistrationResult.NextStep(AN_IGNORED_FLOW_RESULT)
|
||||||
private val A_LOGIN_OR_REGISTER_ACTION = OnboardingAction.LoginOrRegister("@a-user:id.org", "a-password", "a-device-name")
|
private val A_DIRECT_LOGIN = OnboardingAction.AuthenticateAction.LoginDirect("@a-user:id.org", "a-password", "a-device-name")
|
||||||
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
private const val A_HOMESERVER_URL = "https://edited-homeserver.org"
|
||||||
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
|
private val A_HOMESERVER_CONFIG = HomeServerConnectionConfig(FakeUri().instance)
|
||||||
private val SELECTED_HOMESERVER_STATE = SelectedHomeserverState(preferredLoginMode = LoginMode.Password)
|
private val SELECTED_HOMESERVER_STATE = SelectedHomeserverState(preferredLoginMode = LoginMode.Password)
|
||||||
|
@ -142,11 +142,11 @@ class OnboardingViewModelTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given has sign in with matrix id sign mode, when handling login or register action, then logs in directly`() = runTest {
|
fun `given has sign in with matrix id sign mode, when handling login or register action, then logs in directly`() = runTest {
|
||||||
viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
|
viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
|
||||||
fakeDirectLoginUseCase.givenSuccessResult(A_LOGIN_OR_REGISTER_ACTION, config = null, result = fakeSession)
|
fakeDirectLoginUseCase.givenSuccessResult(A_DIRECT_LOGIN, config = null, result = fakeSession)
|
||||||
givenInitialisesSession(fakeSession)
|
givenInitialisesSession(fakeSession)
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
|
|
||||||
viewModel.handle(A_LOGIN_OR_REGISTER_ACTION)
|
viewModel.handle(A_DIRECT_LOGIN)
|
||||||
|
|
||||||
test
|
test
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
|
@ -161,11 +161,11 @@ class OnboardingViewModelTest {
|
||||||
@Test
|
@Test
|
||||||
fun `given has sign in with matrix id sign mode, when handling login or register action fails, then emits error`() = runTest {
|
fun `given has sign in with matrix id sign mode, when handling login or register action fails, then emits error`() = runTest {
|
||||||
viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
|
viewModelWith(initialState.copy(signMode = SignMode.SignInWithMatrixId))
|
||||||
fakeDirectLoginUseCase.givenFailureResult(A_LOGIN_OR_REGISTER_ACTION, config = null, cause = AN_ERROR)
|
fakeDirectLoginUseCase.givenFailureResult(A_DIRECT_LOGIN, config = null, cause = AN_ERROR)
|
||||||
givenInitialisesSession(fakeSession)
|
givenInitialisesSession(fakeSession)
|
||||||
val test = viewModel.test()
|
val test = viewModel.test()
|
||||||
|
|
||||||
viewModel.handle(A_LOGIN_OR_REGISTER_ACTION)
|
viewModel.handle(A_DIRECT_LOGIN)
|
||||||
|
|
||||||
test
|
test
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
import im.vector.app.features.onboarding.DirectLoginUseCase
|
import im.vector.app.features.onboarding.DirectLoginUseCase
|
||||||
import im.vector.app.features.onboarding.OnboardingAction
|
import im.vector.app.features.onboarding.OnboardingAction.AuthenticateAction
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
|
@ -25,11 +25,11 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||||
class FakeDirectLoginUseCase {
|
class FakeDirectLoginUseCase {
|
||||||
val instance = mockk<DirectLoginUseCase>()
|
val instance = mockk<DirectLoginUseCase>()
|
||||||
|
|
||||||
fun givenSuccessResult(action: OnboardingAction.LoginOrRegister, config: HomeServerConnectionConfig?, result: FakeSession) {
|
fun givenSuccessResult(action: AuthenticateAction.LoginDirect, config: HomeServerConnectionConfig?, result: FakeSession) {
|
||||||
coEvery { instance.execute(action, config) } returns Result.success(result)
|
coEvery { instance.execute(action, config) } returns Result.success(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun givenFailureResult(action: OnboardingAction.LoginOrRegister, config: HomeServerConnectionConfig?, cause: Throwable) {
|
fun givenFailureResult(action: AuthenticateAction.LoginDirect, config: HomeServerConnectionConfig?, cause: Throwable) {
|
||||||
coEvery { instance.execute(action, config) } returns Result.failure(cause)
|
coEvery { instance.execute(action, config) } returns Result.failure(cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue