From dab866d1708dea533e815b3178abf02c72218337 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 2 Mar 2022 12:30:28 +0100 Subject: [PATCH 001/136] Forces soft logout to be true for testing --- vector/src/main/java/im/vector/app/features/MainActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 33b735551c..b7bbc4de8d 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -60,7 +60,7 @@ data class MainActivityArgs( val clearCredentials: Boolean = false, val isUserLoggedOut: Boolean = false, val isAccountDeactivated: Boolean = false, - val isSoftLogout: Boolean = false + val isSoftLogout: Boolean = true ) : Parcelable /** @@ -135,7 +135,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity clearCredentials = argsFromIntent?.clearCredentials ?: false, isUserLoggedOut = argsFromIntent?.isUserLoggedOut ?: false, isAccountDeactivated = argsFromIntent?.isAccountDeactivated ?: false, - isSoftLogout = argsFromIntent?.isSoftLogout ?: false + isSoftLogout = argsFromIntent?.isSoftLogout ?: true ) } From 448e8e001fd4cb125efce69b1469926a6ce9d203 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 2 Mar 2022 14:51:22 +0100 Subject: [PATCH 002/136] Reimplements soft logout simulation --- .../org/matrix/android/sdk/internal/session/sync/SyncTask.kt | 2 ++ vector/src/main/java/im/vector/app/features/MainActivity.kt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index b4da1a02cd..a110f73ad2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.session.sync import android.os.SystemClock import okhttp3.ResponseBody +import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.initsync.InitSyncStep @@ -151,6 +152,7 @@ internal class DefaultSyncTask @Inject constructor( defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncIdle) val syncResponse = try { executeRequest(globalErrorReceiver) { + globalErrorReceiver.handleGlobalError(GlobalError.InvalidToken(softLogout = true)) syncAPI.sync( params = requestParams, readTimeOut = readTimeOut diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index b7bbc4de8d..33b735551c 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -60,7 +60,7 @@ data class MainActivityArgs( val clearCredentials: Boolean = false, val isUserLoggedOut: Boolean = false, val isAccountDeactivated: Boolean = false, - val isSoftLogout: Boolean = true + val isSoftLogout: Boolean = false ) : Parcelable /** @@ -135,7 +135,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity clearCredentials = argsFromIntent?.clearCredentials ?: false, isUserLoggedOut = argsFromIntent?.isUserLoggedOut ?: false, isAccountDeactivated = argsFromIntent?.isAccountDeactivated ?: false, - isSoftLogout = argsFromIntent?.isSoftLogout ?: true + isSoftLogout = argsFromIntent?.isSoftLogout ?: false ) } From 12dc8a81126a1fab4492d3bfbb96d1b91f662d5e Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 2 Mar 2022 15:23:39 +0100 Subject: [PATCH 003/136] Tests sso login mode --- .../app/features/signout/soft/SoftLogoutController.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt index 0cd9cde547..25011ebc8a 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -34,6 +34,7 @@ import im.vector.app.features.signout.soft.epoxy.loginRedButtonItem import im.vector.app.features.signout.soft.epoxy.loginTextItem import im.vector.app.features.signout.soft.epoxy.loginTitleItem import im.vector.app.features.signout.soft.epoxy.loginTitleSmallItem +import timber.log.Timber import javax.inject.Inject class SoftLogoutController @Inject constructor( @@ -102,6 +103,8 @@ class SoftLogoutController @Inject constructor( } } is Success -> { + val loginMode = state.asyncHomeServerLoginFlowRequest.invoke() + Timber.i("Login Mode: $loginMode") when (state.asyncHomeServerLoginFlowRequest.invoke()) { LoginMode.Password -> { loginPasswordFormItem { @@ -123,6 +126,11 @@ class SoftLogoutController @Inject constructor( } } is LoginMode.SsoAndPassword -> { + loginCenterButtonItem { + id("sso") + text(host.stringProvider.getString(R.string.login_signin_sso)) + listener { host.listener?.signinFallbackSubmit() } + } } LoginMode.Unsupported -> { loginCenterButtonItem { From 209a442d5baf1fd8190ecbd932f444f733fc55a1 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 10:55:51 +0100 Subject: [PATCH 004/136] Adds LoginType to SessionParams and its entity --- .../sdk/api/auth/data/SessionParams.kt | 9 +++++- .../internal/auth/db/SessionParamsEntity.kt | 3 +- .../internal/auth/db/SessionParamsMapper.kt | 7 ++-- .../sdk/internal/auth/login/LoginType.kt | 32 +++++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt index b490ac877e..5d70bccb08 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.api.auth.data +import org.matrix.android.sdk.internal.auth.login.LoginType + /** * This data class holds necessary data to open a session. * You don't have to manually instantiate it. @@ -34,7 +36,12 @@ data class SessionParams( /** * Set to false if the current token is not valid anymore. Application should not have to use this info. */ - val isTokenValid: Boolean + val isTokenValid: Boolean, + + /** + * Which authentication method was used to create the session + */ + val loginType: LoginType, ) { /* * Shortcuts. Usually the application should only need to use these shortcuts diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt index ba1ab8147b..46d54a3873 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt @@ -26,5 +26,6 @@ internal open class SessionParamsEntity( var homeServerConnectionConfigJson: String = "", // Set to false when the token is invalid and the user has been soft logged out // In case of hard logout, this object is deleted from DB - var isTokenValid: Boolean = true + var isTokenValid: Boolean = true, + var loginType: String, ) : RealmObject() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt index 147c0e8be0..e359577ff3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId +import org.matrix.android.sdk.internal.auth.login.LoginType import javax.inject.Inject internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { @@ -37,7 +38,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { if (credentials == null || homeServerConnectionConfig == null) { return null } - return SessionParams(credentials, homeServerConnectionConfig, entity.isTokenValid) + return SessionParams(credentials, homeServerConnectionConfig, entity.isTokenValid, LoginType.fromValue(entity.loginType)) } fun map(sessionParams: SessionParams?): SessionParamsEntity? { @@ -54,6 +55,8 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { sessionParams.userId, credentialsJson, homeServerConnectionConfigJson, - sessionParams.isTokenValid) + sessionParams.isTokenValid, + sessionParams.loginType.value, + ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt new file mode 100644 index 0000000000..a5f6ebd2f8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt @@ -0,0 +1,32 @@ +/* + * 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 org.matrix.android.sdk.internal.auth.login + +enum class LoginType(val value: String) { + PASSWORD("password"), + SSO("sso"), + UNKNOWN("unknown"); + + companion object { + + fun fromValue(value: String) = when (value) { + PASSWORD.value -> PASSWORD + SSO.value -> SSO + else -> UNKNOWN + } + } +} From 25e73e5bd083cd7c621740c4592fa1906bddada9 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 10:56:07 +0100 Subject: [PATCH 005/136] Adds SessionParamsMapper tests --- .../auth/db/SessionParamsMapperTest.kt | 146 ++++++++++++++++++ .../sdk/test/fixtures/CredentialsFixture.kt | 38 +++++ .../fixtures/SessionParamsEntityFixture.kt | 37 +++++ .../sdk/test/fixtures/SessionParamsFixture.kt | 37 +++++ 4 files changed, 258 insertions(+) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt new file mode 100644 index 0000000000..ac01441588 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt @@ -0,0 +1,146 @@ +/* + * 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 org.matrix.android.sdk.internal.auth.db + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import io.mockk.every +import io.mockk.mockk +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeNull +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.auth.data.sessionId +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fixtures.SessionParamsEntityFixture.aSessionParamsEntity +import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams + +class SessionParamsMapperTest { + + private val moshi: Moshi = mockk() + private lateinit var sessionParamsMapper: SessionParamsMapper + + private val credentialsAdapter: JsonAdapter = mockk() + private val homeServerConnectionAdapter: JsonAdapter = mockk() + + @Before + fun setup() { + every { moshi.adapter(Credentials::class.java) } returns mockk() + every { moshi.adapter(HomeServerConnectionConfig::class.java) } returns mockk() + sessionParamsMapper = SessionParamsMapper(moshi) + + every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns credentials + every { homeServerConnectionAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns homeServerConnectionConfig + every { credentialsAdapter.toJson(sessionParams.credentials) } returns CREDENTIALS_JSON + every { homeServerConnectionAdapter.toJson(sessionParams.homeServerConnectionConfig) } returns HOME_SERVER_CONNECTION_CONFIG_JSON + } + + @Test + fun `when mapping entity, then map as SessionParams`() { + + val output = sessionParamsMapper.map(sessionParamsEntity)!! + + output shouldBeEqualTo SessionParams( + credentials, + homeServerConnectionConfig, + sessionParamsEntity.isTokenValid, + LoginType.fromValue(sessionParamsEntity.loginType) + ) + } + + @Test + fun `given null input, when mapping entity, then return null`() { + val nullEntity: SessionParamsEntity? = null + + val output = sessionParamsMapper.map(nullEntity) + + output.shouldBeNull() + } + + @Test + fun `given null credentials, when mapping entity, then return null`() { + every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns null + + val output = sessionParamsMapper.map(sessionParamsEntity) + + output.shouldBeNull() + } + + @Test + fun `given null homeServerConnectionConfig, when mapping entity, then return null`() { + every { homeServerConnectionAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns null + + val output = sessionParamsMapper.map(sessionParamsEntity) + + output.shouldBeNull() + } + + @Test + fun `when mapping sessionParams, then map as SessionParamsEntity`() { + + val output = sessionParamsMapper.map(sessionParams) + + output shouldBeEqualTo SessionParamsEntity( + sessionParams.credentials.sessionId(), + sessionParams.userId, + CREDENTIALS_JSON, + HOME_SERVER_CONNECTION_CONFIG_JSON, + sessionParams.isTokenValid, + sessionParams.loginType.value, + ) + } + + @Test + fun `given null input, when mapping sessionParams, then return null`() { + val nullSessionParams: SessionParams? = null + + val output = sessionParamsMapper.map(nullSessionParams) + + output.shouldBeNull() + } + + @Test + fun `given null credentials json, when mapping sessionParams, then return null`() { + every { credentialsAdapter.toJson(credentials) } returns null + + val output = sessionParamsMapper.map(sessionParams) + + output.shouldBeNull() + } + + @Test + fun `given null homeServerConnectionConfig json, when mapping sessionParams, then return null`() { + every { homeServerConnectionAdapter.toJson(homeServerConnectionConfig) } returns null + + val output = sessionParamsMapper.map(sessionParams) + + output.shouldBeNull() + } + + companion object { + private val sessionParamsEntity = aSessionParamsEntity() + private val sessionParams = aSessionParams() + + private val credentials: Credentials = mockk() + private val homeServerConnectionConfig: HomeServerConnectionConfig = mockk() + private const val CREDENTIALS_JSON = "credentials_json" + private const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt new file mode 100644 index 0000000000..5002fe9153 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt @@ -0,0 +1,38 @@ +/* + * 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 org.matrix.android.sdk.test.fixtures + +import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.auth.data.DiscoveryInformation + +object CredentialsFixture { + fun aCredentials( + userId: String = "", + accessToken: String = "", + refreshToken: String? = null, + homeServer: String? = null, + deviceId: String? = null, + discoveryInformation: DiscoveryInformation? = null, + ) = Credentials( + userId, + accessToken, + refreshToken, + homeServer, + deviceId, + discoveryInformation, + ) +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt new file mode 100644 index 0000000000..29b5b1a7c9 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt @@ -0,0 +1,37 @@ +/* + * 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 org.matrix.android.sdk.test.fixtures + +import org.matrix.android.sdk.internal.auth.db.SessionParamsEntity + +internal object SessionParamsEntityFixture { + fun aSessionParamsEntity( + sessionId: String = "", + userId: String = "", + credentialsJson: String = "", + homeServerConnectionConfigJson: String = "", + isTokenValid: Boolean = true, + loginType: String = "", + ) = SessionParamsEntity( + sessionId, + userId, + credentialsJson, + homeServerConnectionConfigJson, + isTokenValid, + loginType, + ) +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt new file mode 100644 index 0000000000..387727e8f0 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt @@ -0,0 +1,37 @@ +/* + * 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 org.matrix.android.sdk.test.fixtures + +import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials + +object SessionParamsFixture { + fun aSessionParams( + credentials: Credentials = aCredentials(), + homeServerConnectionConfig: HomeServerConnectionConfig = HomeServerConnectionConfig.Builder().build(), + isTokenValid: Boolean = false, + loginType: LoginType = LoginType.UNKNOWN, + ) = SessionParams( + credentials, + homeServerConnectionConfig, + isTokenValid, + loginType, + ) +} From 187502c358ba1358ee7950321338bf59197de8e5 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 11:32:23 +0100 Subject: [PATCH 006/136] Refactors SessionParamsMapperTest by adding fake moshi --- .../auth/db/SessionParamsMapperTest.kt | 88 ++++---------- .../fakes/FakeSessionParamsMapperMoshi.kt | 112 ++++++++++++++++++ 2 files changed, 133 insertions(+), 67 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt index ac01441588..26154e7d1a 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt @@ -16,80 +16,52 @@ package org.matrix.android.sdk.internal.auth.db -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.Moshi import io.mockk.every -import io.mockk.mockk -import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNull -import org.junit.Before import org.junit.Test -import org.matrix.android.sdk.api.auth.data.Credentials -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.api.auth.data.sessionId -import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fixtures.SessionParamsEntityFixture.aSessionParamsEntity -import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams +import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi +import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.nullSessionParams +import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity +import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity class SessionParamsMapperTest { - private val moshi: Moshi = mockk() - private lateinit var sessionParamsMapper: SessionParamsMapper - - private val credentialsAdapter: JsonAdapter = mockk() - private val homeServerConnectionAdapter: JsonAdapter = mockk() - - @Before - fun setup() { - every { moshi.adapter(Credentials::class.java) } returns mockk() - every { moshi.adapter(HomeServerConnectionConfig::class.java) } returns mockk() - sessionParamsMapper = SessionParamsMapper(moshi) - - every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns credentials - every { homeServerConnectionAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns homeServerConnectionConfig - every { credentialsAdapter.toJson(sessionParams.credentials) } returns CREDENTIALS_JSON - every { homeServerConnectionAdapter.toJson(sessionParams.homeServerConnectionConfig) } returns HOME_SERVER_CONNECTION_CONFIG_JSON - } + private val fakeMoshi = FakeSessionParamsMapperMoshi() + private val sessionParamsMapper = SessionParamsMapper(fakeMoshi.instance) @Test fun `when mapping entity, then map as SessionParams`() { - val output = sessionParamsMapper.map(sessionParamsEntity)!! + val output = sessionParamsMapper.map(sessionParamsEntity) - output shouldBeEqualTo SessionParams( - credentials, - homeServerConnectionConfig, - sessionParamsEntity.isTokenValid, - LoginType.fromValue(sessionParamsEntity.loginType) - ) + fakeMoshi.assertSessionParamsWasMappedSuccessfully(output) } @Test fun `given null input, when mapping entity, then return null`() { - val nullEntity: SessionParamsEntity? = null - val output = sessionParamsMapper.map(nullEntity) + val output = sessionParamsMapper.map(nullSessionParamsEntity) - output.shouldBeNull() + fakeMoshi.assertSessionParamsIsNull(output) } @Test fun `given null credentials, when mapping entity, then return null`() { - every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns null + fakeMoshi.givenCredentialsFromJsonIsNull() val output = sessionParamsMapper.map(sessionParamsEntity) - output.shouldBeNull() + fakeMoshi.assertSessionParamsIsNull(output) } @Test fun `given null homeServerConnectionConfig, when mapping entity, then return null`() { - every { homeServerConnectionAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns null + fakeMoshi.givenHomeServerConnectionConfigFromJsonIsNull() val output = sessionParamsMapper.map(sessionParamsEntity) - output.shouldBeNull() + fakeMoshi.assertSessionParamsIsNull(output) } @Test @@ -97,50 +69,32 @@ class SessionParamsMapperTest { val output = sessionParamsMapper.map(sessionParams) - output shouldBeEqualTo SessionParamsEntity( - sessionParams.credentials.sessionId(), - sessionParams.userId, - CREDENTIALS_JSON, - HOME_SERVER_CONNECTION_CONFIG_JSON, - sessionParams.isTokenValid, - sessionParams.loginType.value, - ) + fakeMoshi.assertSessionParamsEntityWasMappedSuccessfully(output) } @Test fun `given null input, when mapping sessionParams, then return null`() { - val nullSessionParams: SessionParams? = null val output = sessionParamsMapper.map(nullSessionParams) - output.shouldBeNull() + fakeMoshi.assertSessionParamsEntityWasMappedSuccessfully(output) } @Test fun `given null credentials json, when mapping sessionParams, then return null`() { - every { credentialsAdapter.toJson(credentials) } returns null + fakeMoshi.givenCredentialsToJsonIsNull() val output = sessionParamsMapper.map(sessionParams) - output.shouldBeNull() + fakeMoshi.assertSessionParamsEntityIsNull(output) } @Test fun `given null homeServerConnectionConfig json, when mapping sessionParams, then return null`() { - every { homeServerConnectionAdapter.toJson(homeServerConnectionConfig) } returns null + fakeMoshi.givenHomeServerConnectionConfigToJsonIsNull() val output = sessionParamsMapper.map(sessionParams) - output.shouldBeNull() - } - - companion object { - private val sessionParamsEntity = aSessionParamsEntity() - private val sessionParams = aSessionParams() - - private val credentials: Credentials = mockk() - private val homeServerConnectionConfig: HomeServerConnectionConfig = mockk() - private const val CREDENTIALS_JSON = "credentials_json" - private const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" + fakeMoshi.assertSessionParamsEntityIsNull(output) } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt new file mode 100644 index 0000000000..2d5dc8222b --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt @@ -0,0 +1,112 @@ +/* + * 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 org.matrix.android.sdk.test.fakes + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import io.mockk.every +import io.mockk.mockk +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeNull +import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.auth.data.sessionId +import org.matrix.android.sdk.internal.auth.db.SessionParamsEntity +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fixtures.SessionParamsEntityFixture.aSessionParamsEntity +import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams + +internal class FakeSessionParamsMapperMoshi { + + val instance: Moshi = mockk() + private val credentialsAdapter: JsonAdapter = mockk() + private val homeServerConnectionConfigAdapter: JsonAdapter = mockk() + + init { + stubAdapters() + stubJsonConversions() + } + + private fun stubAdapters() { + every { instance.adapter(Credentials::class.java) } returns credentialsAdapter + every { instance.adapter(HomeServerConnectionConfig::class.java) } returns homeServerConnectionConfigAdapter + } + + private fun stubJsonConversions() { + every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns credentials + every { homeServerConnectionConfigAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns homeServerConnectionConfig + every { credentialsAdapter.toJson(sessionParams.credentials) } returns CREDENTIALS_JSON + every { homeServerConnectionConfigAdapter.toJson(sessionParams.homeServerConnectionConfig) } returns HOME_SERVER_CONNECTION_CONFIG_JSON + } + + fun givenCredentialsFromJsonIsNull() { + every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns null + } + + fun givenHomeServerConnectionConfigFromJsonIsNull() { + every { homeServerConnectionConfigAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns null + } + + fun givenCredentialsToJsonIsNull() { + every { credentialsAdapter.toJson(credentials) } returns null + } + + fun givenHomeServerConnectionConfigToJsonIsNull() { + every { homeServerConnectionConfigAdapter.toJson(homeServerConnectionConfig) } returns null + } + + fun assertSessionParamsWasMappedSuccessfully(sessionParams: SessionParams?) { + sessionParams shouldBeEqualTo SessionParams( + credentials, + homeServerConnectionConfig, + sessionParamsEntity.isTokenValid, + LoginType.fromValue(sessionParamsEntity.loginType) + ) + } + + fun assertSessionParamsIsNull(sessionParams: SessionParams?) { + sessionParams.shouldBeNull() + } + + fun assertSessionParamsEntityWasMappedSuccessfully(sessionParamsEntity: SessionParamsEntity?) { + sessionParamsEntity shouldBeEqualTo SessionParamsEntity( + sessionParams.credentials.sessionId(), + sessionParams.userId, + CREDENTIALS_JSON, + HOME_SERVER_CONNECTION_CONFIG_JSON, + sessionParams.isTokenValid, + sessionParams.loginType.value, + ) + } + + fun assertSessionParamsEntityIsNull(sessionParamsEntity: SessionParamsEntity?) { + sessionParamsEntity.shouldBeNull() + } + + companion object { + val sessionParams = aSessionParams() + val sessionParamsEntity = aSessionParamsEntity() + val nullSessionParams: SessionParams? = null + val nullSessionParamsEntity: SessionParamsEntity? = null + + private val credentials: Credentials = mockk() + private val homeServerConnectionConfig: HomeServerConnectionConfig = mockk() + private const val CREDENTIALS_JSON = "credentials_json" + private const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" + } +} From d33081c349df58642db4cca4ade932b130de210b Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 11:47:31 +0100 Subject: [PATCH 007/136] Refactors SessionParamsMapperTest by adding fake json adapters --- .../auth/db/SessionParamsMapperTest.kt | 28 +++++------ .../FakeCredentialsJsonAdapter.kt | 45 +++++++++++++++++ ...keHomeServerConnectionConfigJsonAdapter.kt | 47 ++++++++++++++++++ .../FakeSessionParamsMapperMoshi.kt | 48 ++++--------------- 4 files changed, 114 insertions(+), 54 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => sessionparams}/FakeSessionParamsMapperMoshi.kt (59%) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt index 26154e7d1a..e496201d08 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt @@ -16,14 +16,12 @@ package org.matrix.android.sdk.internal.auth.db -import io.mockk.every -import org.amshove.kluent.shouldBeNull import org.junit.Test -import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi -import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.nullSessionParams -import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity -import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParams +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity class SessionParamsMapperTest { @@ -47,8 +45,8 @@ class SessionParamsMapperTest { } @Test - fun `given null credentials, when mapping entity, then return null`() { - fakeMoshi.givenCredentialsFromJsonIsNull() + fun `given null credentials json deserialization, when mapping entity, then return null`() { + fakeMoshi.credentialsJsonAdapter.givenNullDeserialization() val output = sessionParamsMapper.map(sessionParamsEntity) @@ -56,8 +54,8 @@ class SessionParamsMapperTest { } @Test - fun `given null homeServerConnectionConfig, when mapping entity, then return null`() { - fakeMoshi.givenHomeServerConnectionConfigFromJsonIsNull() + fun `given null homeServerConnectionConfig json deserialization, when mapping entity, then return null`() { + fakeMoshi.homeServerConnectionConfigAdapter.givenNullDeserialization() val output = sessionParamsMapper.map(sessionParamsEntity) @@ -81,8 +79,8 @@ class SessionParamsMapperTest { } @Test - fun `given null credentials json, when mapping sessionParams, then return null`() { - fakeMoshi.givenCredentialsToJsonIsNull() + fun `given null credentials json serialization, when mapping sessionParams, then return null`() { + fakeMoshi.credentialsJsonAdapter.givenNullSerialization() val output = sessionParamsMapper.map(sessionParams) @@ -90,8 +88,8 @@ class SessionParamsMapperTest { } @Test - fun `given null homeServerConnectionConfig json, when mapping sessionParams, then return null`() { - fakeMoshi.givenHomeServerConnectionConfigToJsonIsNull() + fun `given null homeServerConnectionConfig json serialization, when mapping sessionParams, then return null`() { + fakeMoshi.homeServerConnectionConfigAdapter.givenNullSerialization() val output = sessionParamsMapper.map(sessionParams) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt new file mode 100644 index 0000000000..a7c5281687 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt @@ -0,0 +1,45 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.sessionparams + +import com.squareup.moshi.JsonAdapter +import io.mockk.every +import io.mockk.mockk +import org.matrix.android.sdk.api.auth.data.Credentials + +internal class FakeCredentialsJsonAdapter { + + val instance: JsonAdapter = mockk() + + init { + every { instance.fromJson(FakeSessionParamsMapperMoshi.sessionParamsEntity.credentialsJson) } returns credentials + every { instance.toJson(FakeSessionParamsMapperMoshi.sessionParams.credentials) } returns CREDENTIALS_JSON + } + + fun givenNullDeserialization() { + every { instance.fromJson(FakeSessionParamsMapperMoshi.sessionParamsEntity.credentialsJson) } returns null + } + + fun givenNullSerialization() { + every { instance.toJson(credentials) } returns null + } + + companion object { + val credentials: Credentials = mockk() + const val CREDENTIALS_JSON = "credentials_json" + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt new file mode 100644 index 0000000000..50af9bd9ea --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -0,0 +1,47 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.sessionparams + +import com.squareup.moshi.JsonAdapter +import io.mockk.every +import io.mockk.mockk +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity + +internal class FakeHomeServerConnectionConfigJsonAdapter { + + val instance: JsonAdapter = mockk() + + init { + every { instance.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns homeServerConnectionConfig + every { instance.toJson(sessionParams.homeServerConnectionConfig) } returns HOME_SERVER_CONNECTION_CONFIG_JSON + } + + fun givenNullDeserialization() { + every { instance.fromJson(sessionParamsEntity.credentialsJson) } returns null + } + + fun givenNullSerialization() { + every { instance.toJson(homeServerConnectionConfig) } returns null + } + + companion object { + val homeServerConnectionConfig: HomeServerConnectionConfig = mockk() + const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeSessionParamsMapperMoshi.kt similarity index 59% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeSessionParamsMapperMoshi.kt index 2d5dc8222b..7dc0df94b6 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes +package org.matrix.android.sdk.test.fakes.sessionparams -import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import io.mockk.every import io.mockk.mockk @@ -28,46 +27,22 @@ import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.internal.auth.db.SessionParamsEntity import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fakes.sessionparams.FakeCredentialsJsonAdapter.Companion.CREDENTIALS_JSON +import org.matrix.android.sdk.test.fakes.sessionparams.FakeCredentialsJsonAdapter.Companion.credentials +import org.matrix.android.sdk.test.fakes.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.HOME_SERVER_CONNECTION_CONFIG_JSON +import org.matrix.android.sdk.test.fakes.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.homeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.SessionParamsEntityFixture.aSessionParamsEntity import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams internal class FakeSessionParamsMapperMoshi { val instance: Moshi = mockk() - private val credentialsAdapter: JsonAdapter = mockk() - private val homeServerConnectionConfigAdapter: JsonAdapter = mockk() + val credentialsJsonAdapter = FakeCredentialsJsonAdapter() + val homeServerConnectionConfigAdapter = FakeHomeServerConnectionConfigJsonAdapter() init { - stubAdapters() - stubJsonConversions() - } - - private fun stubAdapters() { - every { instance.adapter(Credentials::class.java) } returns credentialsAdapter - every { instance.adapter(HomeServerConnectionConfig::class.java) } returns homeServerConnectionConfigAdapter - } - - private fun stubJsonConversions() { - every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns credentials - every { homeServerConnectionConfigAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns homeServerConnectionConfig - every { credentialsAdapter.toJson(sessionParams.credentials) } returns CREDENTIALS_JSON - every { homeServerConnectionConfigAdapter.toJson(sessionParams.homeServerConnectionConfig) } returns HOME_SERVER_CONNECTION_CONFIG_JSON - } - - fun givenCredentialsFromJsonIsNull() { - every { credentialsAdapter.fromJson(sessionParamsEntity.credentialsJson) } returns null - } - - fun givenHomeServerConnectionConfigFromJsonIsNull() { - every { homeServerConnectionConfigAdapter.fromJson(sessionParamsEntity.homeServerConnectionConfigJson) } returns null - } - - fun givenCredentialsToJsonIsNull() { - every { credentialsAdapter.toJson(credentials) } returns null - } - - fun givenHomeServerConnectionConfigToJsonIsNull() { - every { homeServerConnectionConfigAdapter.toJson(homeServerConnectionConfig) } returns null + every { instance.adapter(Credentials::class.java) } returns credentialsJsonAdapter.instance + every { instance.adapter(HomeServerConnectionConfig::class.java) } returns homeServerConnectionConfigAdapter.instance } fun assertSessionParamsWasMappedSuccessfully(sessionParams: SessionParams?) { @@ -103,10 +78,5 @@ internal class FakeSessionParamsMapperMoshi { val sessionParamsEntity = aSessionParamsEntity() val nullSessionParams: SessionParams? = null val nullSessionParamsEntity: SessionParamsEntity? = null - - private val credentials: Credentials = mockk() - private val homeServerConnectionConfig: HomeServerConnectionConfig = mockk() - private const val CREDENTIALS_JSON = "credentials_json" - private const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" } } From dffd568e14562bd6f2aef8920437953bfe1c4843 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 13:14:49 +0100 Subject: [PATCH 008/136] Adds AuthTo005 realm migration --- .../internal/auth/db/AuthRealmMigration.kt | 4 +- .../internal/auth/db/SessionParamsEntity.kt | 2 +- .../auth/db/migration/MigrateAuthTo005.kt | 34 ++++++++++ .../auth/db/migration/MigrateAuthTo005Test.kt | 34 ++++++++++ .../sdk/test/fakes/Fake005MigrationRealm.kt | 68 +++++++++++++++++++ 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/AuthRealmMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/AuthRealmMigration.kt index 59b6471a05..486dcb918a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/AuthRealmMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/AuthRealmMigration.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.auth.db.migration.MigrateAuthTo001 import org.matrix.android.sdk.internal.auth.db.migration.MigrateAuthTo002 import org.matrix.android.sdk.internal.auth.db.migration.MigrateAuthTo003 import org.matrix.android.sdk.internal.auth.db.migration.MigrateAuthTo004 +import org.matrix.android.sdk.internal.auth.db.migration.MigrateAuthTo005 import timber.log.Timber import javax.inject.Inject @@ -33,7 +34,7 @@ internal class AuthRealmMigration @Inject constructor() : RealmMigration { override fun equals(other: Any?) = other is AuthRealmMigration override fun hashCode() = 4000 - val schemaVersion = 4L + val schemaVersion = 5L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.d("Migrating Auth Realm from $oldVersion to $newVersion") @@ -42,5 +43,6 @@ internal class AuthRealmMigration @Inject constructor() : RealmMigration { if (oldVersion < 2) MigrateAuthTo002(realm).perform() if (oldVersion < 3) MigrateAuthTo003(realm).perform() if (oldVersion < 4) MigrateAuthTo004(realm).perform() + if (oldVersion < 5) MigrateAuthTo005(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt index 46d54a3873..f6c883cac0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsEntity.kt @@ -27,5 +27,5 @@ internal open class SessionParamsEntity( // Set to false when the token is invalid and the user has been soft logged out // In case of hard logout, this object is deleted from DB var isTokenValid: Boolean = true, - var loginType: String, + var loginType: String = "", ) : RealmObject() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt new file mode 100644 index 0000000000..ca3cffeaae --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 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.auth.db.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.auth.db.SessionParamsEntityFields +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.internal.util.database.RealmMigrator +import timber.log.Timber + +class MigrateAuthTo005(realm: DynamicRealm) : RealmMigrator(realm, 5) { + + override fun doMigrate(realm: DynamicRealm) { + Timber.d("Update SessionParamsEntity to add LoginType") + + realm.schema.get("SessionParamsEntity") + ?.addField(SessionParamsEntityFields.LOGIN_TYPE, String::class.java) + ?.transform { it.set(SessionParamsEntityFields.LOGIN_TYPE, LoginType.UNKNOWN.value) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt new file mode 100644 index 0000000000..da58863e3b --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt @@ -0,0 +1,34 @@ +/* + * 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 org.matrix.android.sdk.internal.auth.db.migration + +import org.junit.Test +import org.matrix.android.sdk.test.fakes.Fake005MigrationRealm + +class MigrateAuthTo005Test { + + private val fakeRealm = Fake005MigrationRealm() + private val migrator = MigrateAuthTo005(fakeRealm.instance) + + @Test + fun `when doMigrate, then LoginType field added`() { + + migrator.doMigrate(fakeRealm.instance) + + fakeRealm.verifyLoginTypeAdded() + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt new file mode 100644 index 0000000000..6ccbec03e0 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt @@ -0,0 +1,68 @@ +/* + * 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 org.matrix.android.sdk.test.fakes + +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import io.mockk.verifyOrder +import io.realm.DynamicRealm +import io.realm.DynamicRealmObject +import io.realm.RealmObjectSchema +import io.realm.RealmSchema +import org.matrix.android.sdk.internal.auth.db.SessionParamsEntityFields +import org.matrix.android.sdk.internal.auth.login.LoginType + +class Fake005MigrationRealm { + + val instance: DynamicRealm = mockk() + + private val schema: RealmSchema = mockk() + private val objectSchema: RealmObjectSchema = mockk() + + init { + every { instance.schema } returns schema + every { schema.get("SessionParamsEntity") } returns objectSchema + every { objectSchema.addField(any(), any()) } returns objectSchema + every { objectSchema.transform(any()) } returns objectSchema + } + + fun verifyLoginTypeAdded() { + transformFunctionSlot.clear() + verifyLoginTypeFieldAddedAndTransformed() + verifyTransformationSetsUnknownLoginType() + } + + private fun verifyLoginTypeFieldAddedAndTransformed() { + verifyOrder { + objectSchema["SessionParamsEntity"] + objectSchema.addField(SessionParamsEntityFields.LOGIN_TYPE, String::class.java) + objectSchema.transform(capture(transformFunctionSlot)) + } + } + + private fun verifyTransformationSetsUnknownLoginType() { + val dynamicRealmObject: DynamicRealmObject = mockk() + transformFunctionSlot.captured.invoke(dynamicRealmObject) + verify { dynamicRealmObject.set(SessionParamsEntityFields.LOGIN_TYPE, LoginType.UNKNOWN.value) } + } + + companion object { + private val transformFunctionSlot = slot<(DynamicRealmObject) -> Unit>() + } +} From b82efe95bddcc815d6cc30ed7badfa62bf47ce04 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 13:18:02 +0100 Subject: [PATCH 009/136] Moves test packages --- .../sdk/internal/auth/db/SessionParamsMapperTest.kt | 10 +++++----- .../internal/auth/db/migration/MigrateAuthTo005Test.kt | 2 +- .../{ => auth/db/migration}/Fake005MigrationRealm.kt | 2 +- .../db}/sessionparams/FakeCredentialsJsonAdapter.kt | 2 +- .../FakeHomeServerConnectionConfigJsonAdapter.kt | 6 +++--- .../db}/sessionparams/FakeSessionParamsMapperMoshi.kt | 10 +++++----- 6 files changed, 16 insertions(+), 16 deletions(-) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => auth/db/migration}/Fake005MigrationRealm.kt (97%) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => auth/db}/sessionparams/FakeCredentialsJsonAdapter.kt (95%) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => auth/db}/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt (84%) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => auth/db}/sessionparams/FakeSessionParamsMapperMoshi.kt (83%) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt index e496201d08..ffacb4b4a5 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt @@ -17,11 +17,11 @@ package org.matrix.android.sdk.internal.auth.db import org.junit.Test -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParams -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParams +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity class SessionParamsMapperTest { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt index da58863e3b..efb7bce378 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt @@ -17,7 +17,7 @@ package org.matrix.android.sdk.internal.auth.db.migration import org.junit.Test -import org.matrix.android.sdk.test.fakes.Fake005MigrationRealm +import org.matrix.android.sdk.test.fakes.auth.db.migration.Fake005MigrationRealm class MigrateAuthTo005Test { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/migration/Fake005MigrationRealm.kt similarity index 97% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/migration/Fake005MigrationRealm.kt index 6ccbec03e0..3f317fadf0 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/Fake005MigrationRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/migration/Fake005MigrationRealm.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes +package org.matrix.android.sdk.test.fakes.auth.db.migration import io.mockk.every import io.mockk.mockk diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt similarity index 95% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt index a7c5281687..bcec6d7efe 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeCredentialsJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.sessionparams +package org.matrix.android.sdk.test.fakes.auth.db.sessionparams import com.squareup.moshi.JsonAdapter import io.mockk.every diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt similarity index 84% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt index 50af9bd9ea..4d0e6615c4 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.sessionparams +package org.matrix.android.sdk.test.fakes.auth.db.sessionparams import com.squareup.moshi.JsonAdapter import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity internal class FakeHomeServerConnectionConfigJsonAdapter { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt similarity index 83% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeSessionParamsMapperMoshi.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt index 7dc0df94b6..6327c90565 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/sessionparams/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.sessionparams +package org.matrix.android.sdk.test.fakes.auth.db.sessionparams import com.squareup.moshi.Moshi import io.mockk.every @@ -27,10 +27,10 @@ import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.internal.auth.db.SessionParamsEntity import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fakes.sessionparams.FakeCredentialsJsonAdapter.Companion.CREDENTIALS_JSON -import org.matrix.android.sdk.test.fakes.sessionparams.FakeCredentialsJsonAdapter.Companion.credentials -import org.matrix.android.sdk.test.fakes.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.HOME_SERVER_CONNECTION_CONFIG_JSON -import org.matrix.android.sdk.test.fakes.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.homeServerConnectionConfig +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.CREDENTIALS_JSON +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.credentials +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.HOME_SERVER_CONNECTION_CONFIG_JSON +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.homeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.SessionParamsEntityFixture.aSessionParamsEntity import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams From d3d99dd3bad9e5a7b07dabe320f1d2855f732540 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 13:21:04 +0100 Subject: [PATCH 010/136] Replaces mocks with fixtures in fake json adapters --- .../db/sessionparams/FakeCredentialsJsonAdapter.kt | 11 +++++++---- .../FakeHomeServerConnectionConfigJsonAdapter.kt | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt index bcec6d7efe..7c0d78f96d 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt @@ -20,18 +20,21 @@ import com.squareup.moshi.JsonAdapter import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials internal class FakeCredentialsJsonAdapter { val instance: JsonAdapter = mockk() init { - every { instance.fromJson(FakeSessionParamsMapperMoshi.sessionParamsEntity.credentialsJson) } returns credentials - every { instance.toJson(FakeSessionParamsMapperMoshi.sessionParams.credentials) } returns CREDENTIALS_JSON + every { instance.fromJson(sessionParamsEntity.credentialsJson) } returns credentials + every { instance.toJson(sessionParams.credentials) } returns CREDENTIALS_JSON } fun givenNullDeserialization() { - every { instance.fromJson(FakeSessionParamsMapperMoshi.sessionParamsEntity.credentialsJson) } returns null + every { instance.fromJson(sessionParamsEntity.credentialsJson) } returns null } fun givenNullSerialization() { @@ -39,7 +42,7 @@ internal class FakeCredentialsJsonAdapter { } companion object { - val credentials: Credentials = mockk() + val credentials = aCredentials() const val CREDENTIALS_JSON = "credentials_json" } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt index 4d0e6615c4..6a9285e650 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -41,7 +41,7 @@ internal class FakeHomeServerConnectionConfigJsonAdapter { } companion object { - val homeServerConnectionConfig: HomeServerConnectionConfig = mockk() + val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().build() const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" } } From 47d5d09af2071fd331ffb589ae3a859376c903ef Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 14:46:11 +0100 Subject: [PATCH 011/136] Fixes lint errors --- .../sdk/internal/auth/db/SessionParamsMapperTest.kt | 8 ++------ .../internal/auth/db/migration/MigrateAuthTo005Test.kt | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt index ffacb4b4a5..72b8305b8b 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt @@ -30,15 +30,13 @@ class SessionParamsMapperTest { @Test fun `when mapping entity, then map as SessionParams`() { - val output = sessionParamsMapper.map(sessionParamsEntity) fakeMoshi.assertSessionParamsWasMappedSuccessfully(output) } @Test - fun `given null input, when mapping entity, then return null`() { - + fun `when mapping null entity, then return null`() { val output = sessionParamsMapper.map(nullSessionParamsEntity) fakeMoshi.assertSessionParamsIsNull(output) @@ -64,15 +62,13 @@ class SessionParamsMapperTest { @Test fun `when mapping sessionParams, then map as SessionParamsEntity`() { - val output = sessionParamsMapper.map(sessionParams) fakeMoshi.assertSessionParamsEntityWasMappedSuccessfully(output) } @Test - fun `given null input, when mapping sessionParams, then return null`() { - + fun `when mapping null sessionParams, then return null`() { val output = sessionParamsMapper.map(nullSessionParams) fakeMoshi.assertSessionParamsEntityWasMappedSuccessfully(output) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt index efb7bce378..ab5b1f4c7a 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt @@ -26,7 +26,6 @@ class MigrateAuthTo005Test { @Test fun `when doMigrate, then LoginType field added`() { - migrator.doMigrate(fakeRealm.instance) fakeRealm.verifyLoginTypeAdded() From 40dee006dd2ca130496f3a13470c02d5a47facf3 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 17:51:50 +0100 Subject: [PATCH 012/136] Refactors SessionCreator with added tests --- matrix-sdk-android/build.gradle | 2 +- .../android/sdk/internal/auth/AuthModule.kt | 3 + .../sdk/internal/auth/SessionCreator.kt | 56 ++++--------- .../sdk/internal/auth/SessionParamsCreator.kt | 83 +++++++++++++++++++ .../auth/DefaultSessionCreatorTest.kt | 61 ++++++++++++++ .../auth/DefaultSessionParamsCreatorTest.kt | 62 ++++++++++++++ .../DefaultSessionParamsCreatorTestBase.kt | 72 ++++++++++++++++ .../auth/db/SessionParamsMapperTest.kt | 10 +-- .../auth/db/migration/MigrateAuthTo005Test.kt | 2 +- .../android/sdk/test/fakes/api/FakeSession.kt | 25 ++++++ .../test/fakes/internal/FakeSessionManager.kt | 45 ++++++++++ .../auth/FakeIsValidClientServerApiTask.kt | 41 +++++++++ .../internal/auth/FakePendingSessionStore.kt | 35 ++++++++ .../internal/auth/FakeSessionParamsCreator.kt | 47 +++++++++++ .../internal/auth/FakeSessionParamsStore.kt | 36 ++++++++ .../db/migration/Fake005MigrationRealm.kt | 2 +- .../FakeCredentialsJsonAdapter.kt | 6 +- ...keHomeServerConnectionConfigJsonAdapter.kt | 6 +- .../FakeSessionParamsMapperMoshi.kt | 10 +-- .../fixtures/DiscoveryInformationFixture.kt | 30 +++++++ .../fixtures/WellKnownBaseConfigFixture.kt | 27 ++++++ 21 files changed, 600 insertions(+), 61 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => internal}/auth/db/migration/Fake005MigrationRealm.kt (97%) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => internal}/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt (81%) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => internal}/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt (82%) rename matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/{ => internal}/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt (82%) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 3e301eebb9..32a8b23f30 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -175,7 +175,7 @@ dependencies { // Note: version sticks to 1.9.2 due to https://github.com/mockk/mockk/issues/281 testImplementation libs.mockk.mockk testImplementation libs.tests.kluent - implementation libs.jetbrains.coroutinesAndroid + testImplementation libs.jetbrains.coroutinesTest // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' // Transitively required for mocking realm as monarchy doesn't expose Rx diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt index 298e116199..b5c072370e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/AuthModule.kt @@ -81,6 +81,9 @@ internal abstract class AuthModule { @Binds abstract fun bindSessionCreator(creator: DefaultSessionCreator): SessionCreator + @Binds + abstract fun bindSessionParamsCreator(creator: DefaultSessionParamsCreator): SessionParamsCreator + @Binds abstract fun bindDirectLoginTask(task: DefaultDirectLoginTask): DirectLoginTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt index cc00c963ea..5e6d19d21a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt @@ -16,69 +16,41 @@ package org.matrix.android.sdk.internal.auth -import android.net.Uri import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.SessionManager -import timber.log.Timber +import org.matrix.android.sdk.internal.auth.login.LoginType import javax.inject.Inject internal interface SessionCreator { - suspend fun createSession(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session + + suspend fun createSession( + credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig, + loginType: LoginType, + ): Session } internal class DefaultSessionCreator @Inject constructor( private val sessionParamsStore: SessionParamsStore, private val sessionManager: SessionManager, private val pendingSessionStore: PendingSessionStore, - private val isValidClientServerApiTask: IsValidClientServerApiTask + private val sessionParamsCreator: SessionParamsCreator, ) : SessionCreator { /** * Credentials can affect the homeServerConnectionConfig, override homeserver url and/or * identity server url if provided in the credentials */ - override suspend fun createSession(credentials: Credentials, homeServerConnectionConfig: HomeServerConnectionConfig): Session { + override suspend fun createSession( + credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig, + loginType: LoginType, + ): Session { // We can cleanup the pending session params pendingSessionStore.delete() - - val overriddenUrl = credentials.discoveryInformation?.homeServer?.baseURL - // remove trailing "/" - ?.trim { it == '/' } - ?.takeIf { it.isNotBlank() } - // It can be the same value, so in this case, do not check again the validity - ?.takeIf { it != homeServerConnectionConfig.homeServerUriBase.toString() } - ?.also { Timber.d("Overriding homeserver url to $it (will check if valid)") } - ?.let { Uri.parse(it) } - ?.takeIf { - // Validate the URL, if the configuration is wrong server side, do not override - tryOrNull { - isValidClientServerApiTask.execute( - IsValidClientServerApiTask.Params( - homeServerConnectionConfig.copy(homeServerUriBase = it) - ) - ) - .also { Timber.d("Overriding homeserver url: $it") } - } ?: true // In case of other error (no network, etc.), consider it is valid... - } - - val sessionParams = SessionParams( - credentials = credentials, - homeServerConnectionConfig = homeServerConnectionConfig.copy( - homeServerUriBase = overriddenUrl ?: homeServerConnectionConfig.homeServerUriBase, - identityServerUri = credentials.discoveryInformation?.identityServer?.baseURL - // remove trailing "/" - ?.trim { it == '/' } - ?.takeIf { it.isNotBlank() } - ?.also { Timber.d("Overriding identity server url to $it") } - ?.let { Uri.parse(it) } - ?: homeServerConnectionConfig.identityServerUri - ), - isTokenValid = true) - + val sessionParams = sessionParamsCreator.create(credentials, homeServerConnectionConfig, loginType) sessionParamsStore.save(sessionParams) return sessionManager.getOrCreateSession(sessionParams) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt new file mode 100644 index 0000000000..318389486e --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt @@ -0,0 +1,83 @@ +/* + * 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 org.matrix.android.sdk.internal.auth + +import android.net.Uri +import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.internal.auth.login.LoginType +import timber.log.Timber +import javax.inject.Inject + +internal interface SessionParamsCreator { + + suspend fun create( + credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig, + loginType: LoginType, + ): SessionParams +} + +internal class DefaultSessionParamsCreator @Inject constructor( + private val isValidClientServerApiTask: IsValidClientServerApiTask +) : SessionParamsCreator { + + override suspend fun create( + credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig, + loginType: LoginType, + ) = SessionParams( + credentials = credentials, + homeServerConnectionConfig = homeServerConnectionConfig.overrideWithCredentials(credentials), + isTokenValid = true, + loginType = loginType, + ) + + private suspend fun HomeServerConnectionConfig.overrideWithCredentials(credentials: Credentials) = copy( + homeServerUriBase = credentials.getHomeServerUri(this) ?: homeServerUriBase, + identityServerUri = credentials.getIdentityServerUri() ?: identityServerUri + ) + + private suspend fun Credentials.getHomeServerUri(homeServerConnectionConfig: HomeServerConnectionConfig) = + discoveryInformation?.homeServer?.baseURL + ?.trim { it == '/' } + ?.takeIf { it.isNotBlank() } + // It can be the same value, so in this case, do not check again the validity + ?.takeIf { it != homeServerConnectionConfig.homeServerUriBase.toString() } + ?.also { Timber.d("Overriding homeserver url to $it (will check if valid)") } + ?.let { Uri.parse(it) } + ?.takeIf { validateUri(it, homeServerConnectionConfig) } + + private suspend fun validateUri(uri: Uri, homeServerConnectionConfig: HomeServerConnectionConfig) = + // Validate the URL, if the configuration is wrong server side, do not override + tryOrNull { + performClientServerApiValidation(uri, homeServerConnectionConfig) + } ?: true // In case of other error (no network, etc.), consider it is valid... + + private suspend fun performClientServerApiValidation(uri: Uri, homeServerConnectionConfig: HomeServerConnectionConfig) = + isValidClientServerApiTask.execute( + IsValidClientServerApiTask.Params(homeServerConnectionConfig.copy(homeServerUriBase = uri)) + ).also { Timber.d("Overriding homeserver url: $it") } + + private fun Credentials.getIdentityServerUri() = discoveryInformation?.identityServer?.baseURL + ?.trim { it == '/' } + ?.takeIf { it.isNotBlank() } + ?.also { Timber.d("Overriding identity server url to $it") } + ?.let { Uri.parse(it) } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt new file mode 100644 index 0000000000..c9eb2bc2e0 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt @@ -0,0 +1,61 @@ +/* + * 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 org.matrix.android.sdk.internal.auth + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fakes.internal.FakeSessionManager +import org.matrix.android.sdk.test.fakes.internal.auth.FakePendingSessionStore +import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsCreator +import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsStore +import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials +import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams + +@ExperimentalCoroutinesApi +class DefaultSessionCreatorTest { + + private val fakeSessionParamsStore = FakeSessionParamsStore() + private val fakeSessionManager = FakeSessionManager() + private val fakePendingSessionStore = FakePendingSessionStore() + private val fakeSessionParamsCreator = FakeSessionParamsCreator() + + private val sessionCreator = DefaultSessionCreator( + fakeSessionParamsStore.instance, + fakeSessionManager.instance, + fakePendingSessionStore.instance, + fakeSessionParamsCreator.instance, + ) + + @Test + fun `when createSession, then session created`() = runBlockingTest { + val output = sessionCreator.createSession(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) + + fakePendingSessionStore.verifyPendingSessionDataCleared() + fakeSessionParamsCreator.verifyCreatedWithParameters(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) + fakeSessionParamsStore.verifyParamsSaved(sessionParams) + fakeSessionManager.assertSessionCreatedWithParams(output, sessionParams) + } + + companion object { + private val sessionParams = aSessionParams() + private val credentials = aCredentials() + private val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().build() + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt new file mode 100644 index 0000000000..85723d6e08 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt @@ -0,0 +1,62 @@ +/* + * 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 org.matrix.android.sdk.internal.auth + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fakes.internal.auth.FakeIsValidClientServerApiTask + +@ExperimentalCoroutinesApi +class DefaultSessionParamsCreatorTest : DefaultSessionParamsCreatorTestBase() { + + private val fakeIsValidClientServerApiTask = FakeIsValidClientServerApiTask() + + private val sessionParamsCreator = DefaultSessionParamsCreator(fakeIsValidClientServerApiTask.instance) + + @Test + fun `when create, then SessionParams created`() = runBlockingTest { + val output = sessionParamsCreator.create(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) + + assertExpectedSessionParams(output) + } + + @Test + fun `given credentials contains homeServerUri, when create, then SessionParams created with validated credentials uri`() = runBlockingTest { + val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfig, LoginType.UNKNOWN) + + fakeIsValidClientServerApiTask.verifyExecutionWithConfig(homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri())) + assertExpectedSessionParamsWithHomeServer(output) + } + + @Test + fun `given credentials homeServerUri is equal to homeServerConnectionConfig, when create, then do not validate`() = runBlockingTest { + val homeServerConnectionConfigWithCredentialsUri = homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri()) + val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfigWithCredentialsUri , LoginType.UNKNOWN) + + fakeIsValidClientServerApiTask.verifyNoExecution() + assertExpectedSessionParamsWithHomeServer(output) + } + + @Test + fun `given credentials contains identityServerUri, when create, then SessionParams created with credentials uri`() = runBlockingTest { + val output = sessionParamsCreator.create(credentialsWithIdentityServer, homeServerConnectionConfig, LoginType.UNKNOWN) + + assertExpectedSessionParamsWithIdentityServer(output) + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt new file mode 100644 index 0000000000..8265c1cc27 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt @@ -0,0 +1,72 @@ +/* + * 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 org.matrix.android.sdk.internal.auth + +import android.net.Uri +import org.amshove.kluent.shouldBeEqualTo +import org.matrix.android.sdk.api.auth.data.DiscoveryInformation +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fixtures.CredentialsFixture +import org.matrix.android.sdk.test.fixtures.DiscoveryInformationFixture +import org.matrix.android.sdk.test.fixtures.WellKnownBaseConfigFixture + +abstract class DefaultSessionParamsCreatorTestBase { + + protected val discoveryWithHomeServer = DiscoveryInformationFixture.aDiscoveryInformation(homeServer = WellKnownBaseConfigFixture.aWellKnownBaseConfig("http://homeserver_url/")) + private val discoveryWithIdentityServer = DiscoveryInformationFixture.aDiscoveryInformation(identityServer = WellKnownBaseConfigFixture.aWellKnownBaseConfig("http://identity_server_url/")) + protected val credentials = CredentialsFixture.aCredentials() + protected val credentialsWithHomeServer = CredentialsFixture.aCredentials(discoveryInformation = discoveryWithHomeServer) + protected val credentialsWithIdentityServer = CredentialsFixture.aCredentials(discoveryInformation = discoveryWithIdentityServer) + protected val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().build() + + protected fun assertExpectedSessionParams(sessionParams: SessionParams) { + sessionParams shouldBeEqualTo SessionParams( + credentials = credentials, + homeServerConnectionConfig = homeServerConnectionConfig, + isTokenValid = true, + loginType = LoginType.UNKNOWN, + ) + } + + protected fun assertExpectedSessionParamsWithHomeServer(sessionParams: SessionParams) { + sessionParams shouldBeEqualTo SessionParams( + credentials = credentialsWithHomeServer, + homeServerConnectionConfig = homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri()), + isTokenValid = true, + loginType = LoginType.UNKNOWN, + ) + } + + protected fun assertExpectedSessionParamsWithIdentityServer(sessionParams: SessionParams) { + sessionParams shouldBeEqualTo SessionParams( + credentials = credentialsWithHomeServer, + homeServerConnectionConfig = homeServerConnectionConfig.copy(identityServerUri = discoveryWithIdentityServer.getIdentityServerUri()), + isTokenValid = true, + loginType = LoginType.UNKNOWN, + ) + } + + private fun DiscoveryInformation.getIdentityServerUri() = identityServer?.baseURL?.convertToUri()!! + + protected fun DiscoveryInformation.getHomeServerUri() = homeServer?.baseURL?.convertToUri()!! + + private fun String.convertToUri() = trim { it == '/' } + .takeIf { it.isNotBlank() } + .let { Uri.parse(it) } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt index 72b8305b8b..1ba3724d63 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt @@ -17,11 +17,11 @@ package org.matrix.android.sdk.internal.auth.db import org.junit.Test -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParams -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParams +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity class SessionParamsMapperTest { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt index ab5b1f4c7a..6b2432b5f6 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt @@ -17,7 +17,7 @@ package org.matrix.android.sdk.internal.auth.db.migration import org.junit.Test -import org.matrix.android.sdk.test.fakes.auth.db.migration.Fake005MigrationRealm +import org.matrix.android.sdk.test.fakes.internal.auth.db.migration.Fake005MigrationRealm class MigrateAuthTo005Test { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt new file mode 100644 index 0000000000..5aac29c328 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt @@ -0,0 +1,25 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.api + +import io.mockk.mockk +import org.matrix.android.sdk.api.session.Session + +class FakeSession { + + val instance: Session = mockk() +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt new file mode 100644 index 0000000000..b251349249 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt @@ -0,0 +1,45 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.internal + +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import org.amshove.kluent.shouldBeEqualTo +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.SessionManager +import org.matrix.android.sdk.test.fakes.api.FakeSession + +internal class FakeSessionManager { + + val instance: SessionManager = mockk() + + init { + every { instance.getOrCreateSession(any()) } returns fakeSession.instance + } + + fun assertSessionCreatedWithParams(session: Session, sessionParams: SessionParams) { + verify { instance.getOrCreateSession(sessionParams) } + + session shouldBeEqualTo fakeSession.instance + } + + companion object { + private val fakeSession = FakeSession() + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt new file mode 100644 index 0000000000..3eeda6db65 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt @@ -0,0 +1,41 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.internal.auth + +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.internal.auth.IsValidClientServerApiTask +import org.matrix.android.sdk.internal.auth.IsValidClientServerApiTask.Params + +internal class FakeIsValidClientServerApiTask { + + init { + coEvery { instance.execute(any()) } returns true + } + + val instance: IsValidClientServerApiTask = mockk() + + fun verifyExecutionWithConfig(config: HomeServerConnectionConfig) { + coVerify { instance.execute(Params(config)) } + } + + fun verifyNoExecution() { + coVerify(inverse = true) { instance.execute(any()) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt new file mode 100644 index 0000000000..be91daf910 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt @@ -0,0 +1,35 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.internal.auth + +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.internal.auth.PendingSessionStore + +internal class FakePendingSessionStore { + + val instance: PendingSessionStore = mockk() + + init { + coJustRun { instance.delete() } + } + + fun verifyPendingSessionDataCleared() { + coVerify { instance.delete() } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt new file mode 100644 index 0000000000..b0757d5bd5 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt @@ -0,0 +1,47 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.internal.auth + +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.api.auth.data.Credentials +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.internal.auth.SessionParamsCreator +import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams + +internal class FakeSessionParamsCreator { + + val instance: SessionParamsCreator = mockk() + + init { + coEvery { instance.create(any(), any(), any()) } returns sessionParams + } + + fun verifyCreatedWithParameters( + credentials: Credentials, + homeServerConnectionConfig: HomeServerConnectionConfig, + loginType: LoginType, + ) { + coVerify { instance.create(credentials, homeServerConnectionConfig, loginType) } + } + + companion object { + val sessionParams = aSessionParams() + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt new file mode 100644 index 0000000000..4336f6ba7c --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt @@ -0,0 +1,36 @@ +/* + * 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 org.matrix.android.sdk.test.fakes.internal.auth + +import io.mockk.coJustRun +import io.mockk.coVerify +import io.mockk.mockk +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.internal.auth.SessionParamsStore + +internal class FakeSessionParamsStore { + + val instance: SessionParamsStore = mockk() + + init { + coJustRun { instance.save(any()) } + } + + fun verifyParamsSaved(sessionParams: SessionParams) { + coVerify { instance.save(sessionParams) } + } +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/migration/Fake005MigrationRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt similarity index 97% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/migration/Fake005MigrationRealm.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt index 3f317fadf0..e5e2b6682f 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/migration/Fake005MigrationRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.auth.db.migration +package org.matrix.android.sdk.test.fakes.internal.auth.db.migration import io.mockk.every import io.mockk.mockk diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt similarity index 81% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt index 7c0d78f96d..eea3e4fab1 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.auth.db.sessionparams +package org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams import com.squareup.moshi.JsonAdapter import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.api.auth.data.Credentials -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials internal class FakeCredentialsJsonAdapter { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt similarity index 82% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt index 6a9285e650..d066907cdf 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.auth.db.sessionparams +package org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams import com.squareup.moshi.JsonAdapter import io.mockk.every import io.mockk.mockk import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity internal class FakeHomeServerConnectionConfigJsonAdapter { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt similarity index 82% rename from matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt rename to matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt index 6327c90565..546f8dcee9 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.test.fakes.auth.db.sessionparams +package org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams import com.squareup.moshi.Moshi import io.mockk.every @@ -27,10 +27,10 @@ import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.internal.auth.db.SessionParamsEntity import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.CREDENTIALS_JSON -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.credentials -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.HOME_SERVER_CONNECTION_CONFIG_JSON -import org.matrix.android.sdk.test.fakes.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.homeServerConnectionConfig +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.CREDENTIALS_JSON +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.credentials +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.HOME_SERVER_CONNECTION_CONFIG_JSON +import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.homeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.SessionParamsEntityFixture.aSessionParamsEntity import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt new file mode 100644 index 0000000000..4a15fd383a --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt @@ -0,0 +1,30 @@ +/* + * 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 org.matrix.android.sdk.test.fixtures + +import org.matrix.android.sdk.api.auth.data.DiscoveryInformation +import org.matrix.android.sdk.api.auth.data.WellKnownBaseConfig + +object DiscoveryInformationFixture { + fun aDiscoveryInformation( + homeServer: WellKnownBaseConfig? = null, + identityServer: WellKnownBaseConfig? = null, + ) = DiscoveryInformation( + homeServer, + identityServer + ) +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt new file mode 100644 index 0000000000..96a146587c --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt @@ -0,0 +1,27 @@ +/* + * 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 org.matrix.android.sdk.test.fixtures + +import org.matrix.android.sdk.api.auth.data.WellKnownBaseConfig + +object WellKnownBaseConfigFixture { + fun aWellKnownBaseConfig( + baseUrl: String? = null, + ) = WellKnownBaseConfig( + baseUrl, + ) +} From ea5346210755dba8877fbcf505d9f6c840383e64 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 3 Mar 2022 17:54:45 +0100 Subject: [PATCH 013/136] Adds loginType to DefaultLegacySessionImporter --- .../sdk/internal/legacy/DefaultLegacySessionImporter.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt index 22085e30fc..e7acee2f13 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt @@ -27,6 +27,7 @@ import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.WellKnownBaseConfig import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.internal.auth.SessionParamsStore +import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule import org.matrix.android.sdk.internal.database.RealmKeysUtils @@ -145,7 +146,8 @@ internal class DefaultLegacySessionImporter @Inject constructor( forceUsageTlsVersions = legacyConfig.forceUsageOfTlsVersions() ), // If token is not valid, this boolean will be updated later - isTokenValid = true + isTokenValid = true, + loginType = LoginType.UNKNOWN, ) Timber.d("Migration: save session") From 32bde5a344f31f15a3d0927e8a63b467999b038f Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 14:24:47 +0100 Subject: [PATCH 014/136] Adds loginType UNSUPPORTED --- .../sdk/internal/auth/login/LoginType.kt | 2 + .../sdk/internal/auth/login/LoginTypeTest.kt | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt index a5f6ebd2f8..635ecc7658 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.auth.login enum class LoginType(val value: String) { PASSWORD("password"), SSO("sso"), + UNSUPPORTED("unsupported"), UNKNOWN("unknown"); companion object { @@ -26,6 +27,7 @@ enum class LoginType(val value: String) { fun fromValue(value: String) = when (value) { PASSWORD.value -> PASSWORD SSO.value -> SSO + UNSUPPORTED.value -> UNSUPPORTED else -> UNKNOWN } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt new file mode 100644 index 0000000000..c52dd4fe67 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -0,0 +1,42 @@ +/* + * 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 org.matrix.android.sdk.internal.auth.login + +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldNotBeEqualTo +import org.junit.Test + +class LoginTypeTest { + + @Test + fun `when getting type fromValue, then map correctly`() { + LoginType.fromValue(LoginType.PASSWORD.value) shouldBeEqualTo LoginType.PASSWORD + LoginType.fromValue(LoginType.SSO.value) shouldBeEqualTo LoginType.SSO + LoginType.fromValue(LoginType.UNSUPPORTED.value) shouldBeEqualTo LoginType.UNSUPPORTED + LoginType.fromValue(LoginType.UNKNOWN.value) shouldBeEqualTo LoginType.UNKNOWN + } + + @Test // This test failing means an existing type has not been correctly added to fromValue + fun `given non-unknown type value, when getting type fromValue, then type is not UNKNOWN`() { + val types = LoginType.values() + + types.forEach { type -> + if (type != LoginType.UNKNOWN) + LoginType.fromValue(type.value) shouldNotBeEqualTo LoginType.UNKNOWN + } + } +} From 2fda593c3c65917347bdeb255ca42c32f0d14850 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 14:29:42 +0100 Subject: [PATCH 015/136] Adds login types to auth flows --- .../auth/DefaultAuthenticationService.kt | 3 ++- .../internal/auth/login/DefaultLoginWizard.kt | 6 ++--- .../internal/auth/login/DirectLoginTask.kt | 2 +- .../registration/DefaultRegistrationWizard.kt | 26 +++++++++++-------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index 8784d85c10..25575dfa4c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -38,6 +38,7 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig import org.matrix.android.sdk.internal.auth.db.PendingSessionData import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard import org.matrix.android.sdk.internal.auth.login.DirectLoginTask +import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk @@ -370,7 +371,7 @@ internal class DefaultAuthenticationService @Inject constructor( override suspend fun createSessionFromSso(homeServerConnectionConfig: HomeServerConnectionConfig, credentials: Credentials): Session { - return sessionCreator.createSession(credentials, homeServerConnectionConfig) + return sessionCreator.createSession(credentials, homeServerConnectionConfig, LoginType.SSO) } override suspend fun getWellKnownData(matrixId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt index 0583951138..6e9a188631 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt @@ -76,7 +76,7 @@ internal class DefaultLoginWizard( authAPI.login(loginParams) } - return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.PASSWORD) } /** @@ -90,7 +90,7 @@ internal class DefaultLoginWizard( authAPI.login(loginParams) } - return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.UNSUPPORTED) } override suspend fun loginCustom(data: JsonDict): Session { @@ -98,7 +98,7 @@ internal class DefaultLoginWizard( authAPI.login(data) } - return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.UNSUPPORTED) } override suspend fun resetPassword(email: String, newPassword: String) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt index 28706c7e80..16f6a13977 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt @@ -77,7 +77,7 @@ internal class DefaultDirectLoginTask @Inject constructor( } } - return sessionCreator.createSession(credentials, params.homeServerConnectionConfig) + return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.UNSUPPORTED) } private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index 4a156e74cd..6d4a748a72 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -29,6 +29,7 @@ import org.matrix.android.sdk.internal.auth.AuthAPI import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.auth.SessionCreator import org.matrix.android.sdk.internal.auth.db.PendingSessionData +import org.matrix.android.sdk.internal.auth.login.LoginType /** * This class execute the registration request and is responsible to keep the session of interactive authentication @@ -63,7 +64,7 @@ internal class DefaultRegistrationWizard( override suspend fun getRegistrationFlow(): RegistrationResult { val params = RegistrationParams() - return performRegistrationRequest(params) + return performRegistrationRequest(params, LoginType.UNSUPPORTED) } override suspend fun createAccount(userName: String?, @@ -74,7 +75,7 @@ internal class DefaultRegistrationWizard( password = password, initialDeviceDisplayName = initialDeviceDisplayName ) - return performRegistrationRequest(params) + return performRegistrationRequest(params, LoginType.PASSWORD) .also { pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true) .also { pendingSessionStore.savePendingSessionData(it) } @@ -86,7 +87,7 @@ internal class DefaultRegistrationWizard( ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response)) - return performRegistrationRequest(params) + return performRegistrationRequest(params, LoginType.UNSUPPORTED) } override suspend fun acceptTerms(): RegistrationResult { @@ -94,7 +95,7 @@ internal class DefaultRegistrationWizard( ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession)) - return performRegistrationRequest(params) + return performRegistrationRequest(params, LoginType.UNSUPPORTED) } override suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult { @@ -144,14 +145,14 @@ internal class DefaultRegistrationWizard( .also { pendingSessionStore.savePendingSessionData(it) } // and send the sid a first time - return performRegistrationRequest(params) + return performRegistrationRequest(params, LoginType.UNSUPPORTED) } override suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult { val safeParam = pendingSessionData.currentThreePidData?.registrationParams ?: throw IllegalStateException("developer error, no pending three pid") - return performRegistrationRequest(safeParam, delayMillis) + return performRegistrationRequest(safeParam, LoginType.UNSUPPORTED, delayMillis) } override suspend fun handleValidateThreePid(code: String): RegistrationResult { @@ -172,7 +173,7 @@ internal class DefaultRegistrationWizard( if (validationResponse.isSuccess()) { // The entered code is correct // Same than validate email - return performRegistrationRequest(registrationParams, 3_000) + return performRegistrationRequest(registrationParams, LoginType.UNSUPPORTED, 3_000) } else { // The code is not correct throw Failure.SuccessError @@ -184,11 +185,14 @@ internal class DefaultRegistrationWizard( ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession)) - return performRegistrationRequest(params) + return performRegistrationRequest(params, LoginType.UNSUPPORTED) } - private suspend fun performRegistrationRequest(registrationParams: RegistrationParams, - delayMillis: Long = 0): RegistrationResult { + private suspend fun performRegistrationRequest( + registrationParams: RegistrationParams, + loginType: LoginType, + delayMillis: Long = 0, + ): RegistrationResult { delay(delayMillis) val credentials = try { registerTask.execute(RegisterTask.Params(registrationParams)) @@ -202,7 +206,7 @@ internal class DefaultRegistrationWizard( } } - val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig) + val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, loginType) return RegistrationResult.Success(session) } From 92f87a3a5a0f0ebee75878aec0036e53ed8f8a12 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 14:51:39 +0100 Subject: [PATCH 016/136] Adds login type handling to SoftLogoutController --- .../signout/soft/SoftLogoutController.kt | 125 ++++++++++-------- .../signout/soft/SoftLogoutViewModel.kt | 7 +- .../signout/soft/SoftLogoutViewState.kt | 4 +- 3 files changed, 78 insertions(+), 58 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt index 25011ebc8a..6742894fde 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -19,7 +19,9 @@ package im.vector.app.features.signout.soft import com.airbnb.epoxy.EpoxyController import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import im.vector.app.R import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.error.ErrorFormatter @@ -34,7 +36,7 @@ import im.vector.app.features.signout.soft.epoxy.loginRedButtonItem import im.vector.app.features.signout.soft.epoxy.loginTextItem import im.vector.app.features.signout.soft.epoxy.loginTitleItem import im.vector.app.features.signout.soft.epoxy.loginTitleSmallItem -import timber.log.Timber +import org.matrix.android.sdk.internal.auth.login.LoginType import javax.inject.Inject class SoftLogoutController @Inject constructor( @@ -87,61 +89,74 @@ class SoftLogoutController @Inject constructor( } } - private fun buildForm(state: SoftLogoutViewState) { + private fun buildForm(state: SoftLogoutViewState) = when (state.asyncHomeServerLoginFlowRequest) { + is Incomplete -> buildLoadingItem() + is Fail -> buildLoginErrorWithRetryItem(state.asyncHomeServerLoginFlowRequest.error) + is Success -> buildLoginSuccessItem(state) + is Loading, Uninitialized -> Unit + } + + private fun buildLoadingItem() { + loadingItem { + id("loading") + } + } + + private fun buildLoginErrorWithRetryItem(error: Throwable) { val host = this - when (state.asyncHomeServerLoginFlowRequest) { - is Incomplete -> { - loadingItem { - id("loading") - } - } - is Fail -> { - loginErrorWithRetryItem { - id("errorRetry") - text(host.errorFormatter.toHumanReadable(state.asyncHomeServerLoginFlowRequest.error)) - listener { host.listener?.retry() } - } - } - is Success -> { - val loginMode = state.asyncHomeServerLoginFlowRequest.invoke() - Timber.i("Login Mode: $loginMode") - when (state.asyncHomeServerLoginFlowRequest.invoke()) { - LoginMode.Password -> { - loginPasswordFormItem { - id("passwordForm") - stringProvider(host.stringProvider) - passwordValue(state.enteredPassword) - submitEnabled(state.enteredPassword.isNotEmpty()) - onPasswordEdited { host.listener?.passwordEdited(it) } - errorText((state.asyncLoginAction as? Fail)?.error?.let { host.errorFormatter.toHumanReadable(it) }) - forgetPasswordClickListener { host.listener?.forgetPasswordClicked() } - submitClickListener { host.listener?.submit() } - } - } - is LoginMode.Sso -> { - loginCenterButtonItem { - id("sso") - text(host.stringProvider.getString(R.string.login_signin_sso)) - listener { host.listener?.signinFallbackSubmit() } - } - } - is LoginMode.SsoAndPassword -> { - loginCenterButtonItem { - id("sso") - text(host.stringProvider.getString(R.string.login_signin_sso)) - listener { host.listener?.signinFallbackSubmit() } - } - } - LoginMode.Unsupported -> { - loginCenterButtonItem { - id("fallback") - text(host.stringProvider.getString(R.string.login_signin)) - listener { host.listener?.signinFallbackSubmit() } - } - } - LoginMode.Unknown -> Unit // Should not happen - } - } + loginErrorWithRetryItem { + id("errorRetry") + text(host.errorFormatter.toHumanReadable(error)) + listener { host.listener?.retry() } + } + } + + private fun buildLoginSuccessItem(state: SoftLogoutViewState) = when (state.asyncHomeServerLoginFlowRequest.invoke()) { + LoginMode.Password -> buildLoginPasswordForm(state) + is LoginMode.Sso -> buildLoginSSOForm() + is LoginMode.SsoAndPassword -> disambiguateLoginSSOAndPasswordForm(state) + LoginMode.Unsupported -> buildLoginUnsupportedForm() + LoginMode.Unknown, null -> Unit // Should not happen + } + + private fun buildLoginPasswordForm(state: SoftLogoutViewState) { + val host = this + loginPasswordFormItem { + id("passwordForm") + stringProvider(host.stringProvider) + passwordValue(state.enteredPassword) + submitEnabled(state.enteredPassword.isNotEmpty()) + onPasswordEdited { host.listener?.passwordEdited(it) } + errorText((state.asyncLoginAction as? Fail)?.error?.let { host.errorFormatter.toHumanReadable(it) }) + forgetPasswordClickListener { host.listener?.forgetPasswordClicked() } + submitClickListener { host.listener?.submit() } + } + } + + private fun buildLoginSSOForm() { + val host = this + loginCenterButtonItem { + id("sso") + text(host.stringProvider.getString(R.string.login_signin_sso)) + listener { host.listener?.signinFallbackSubmit() } + } + } + + private fun disambiguateLoginSSOAndPasswordForm(state: SoftLogoutViewState) { + when (state.loginType) { + LoginType.PASSWORD -> buildLoginPasswordForm(state) + LoginType.SSO -> buildLoginSSOForm() + LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() + LoginType.UNKNOWN -> Unit + } + } + + private fun buildLoginUnsupportedForm() { + val host = this + loginCenterButtonItem { + id("fallback") + text(host.stringProvider.getString(R.string.login_signin)) + listener { host.listener?.signinFallbackSubmit() } } } diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index 00422d8872..dcc18ea088 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -37,6 +37,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.AuthenticationService import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.internal.auth.login.LoginType import timber.log.Timber /** @@ -69,7 +70,8 @@ class SoftLogoutViewModel @AssistedInject constructor( userId = userId, deviceId = session.sessionParams.deviceId.orEmpty(), userDisplayName = session.getUser(userId)?.displayName ?: userId, - hasUnsavedKeys = session.hasUnsavedKeys() + hasUnsavedKeys = session.hasUnsavedKeys(), + loginType = session.sessionParams.loginType, ) } else { SoftLogoutViewState( @@ -77,7 +79,8 @@ class SoftLogoutViewModel @AssistedInject constructor( userId = "", deviceId = "", userDisplayName = "", - hasUnsavedKeys = false + hasUnsavedKeys = false, + loginType = LoginType.UNKNOWN, ) } } diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt index 511711ab2f..f13b089ac5 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt @@ -22,6 +22,7 @@ import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import im.vector.app.features.login.LoginMode +import org.matrix.android.sdk.internal.auth.login.LoginType data class SoftLogoutViewState( val asyncHomeServerLoginFlowRequest: Async = Uninitialized, @@ -31,7 +32,8 @@ data class SoftLogoutViewState( val deviceId: String, val userDisplayName: String, val hasUnsavedKeys: Boolean, - val enteredPassword: String = "" + val loginType: LoginType, + val enteredPassword: String = "", ) : MavericksState { fun isLoading(): Boolean { From 275505b3e60e029af96bc88e0cdcb6a4157017bc Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 14:53:16 +0100 Subject: [PATCH 017/136] Fixes lint errors --- .../sdk/internal/auth/DefaultSessionParamsCreatorTest.kt | 2 +- .../matrix/android/sdk/internal/auth/login/LoginTypeTest.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt index 85723d6e08..35693748ef 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt @@ -47,7 +47,7 @@ class DefaultSessionParamsCreatorTest : DefaultSessionParamsCreatorTestBase() { @Test fun `given credentials homeServerUri is equal to homeServerConnectionConfig, when create, then do not validate`() = runBlockingTest { val homeServerConnectionConfigWithCredentialsUri = homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri()) - val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfigWithCredentialsUri , LoginType.UNKNOWN) + val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfigWithCredentialsUri, LoginType.UNKNOWN) fakeIsValidClientServerApiTask.verifyNoExecution() assertExpectedSessionParamsWithHomeServer(output) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt index c52dd4fe67..419dd50991 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -35,8 +35,9 @@ class LoginTypeTest { val types = LoginType.values() types.forEach { type -> - if (type != LoginType.UNKNOWN) + if (type != LoginType.UNKNOWN) { LoginType.fromValue(type.value) shouldNotBeEqualTo LoginType.UNKNOWN + } } } } From bb2369dad2f04489dc974ffa6237b637f7ab95a9 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 15:02:03 +0100 Subject: [PATCH 018/136] Fixes broken tests due to uninitialised HomeServerConnectionConfig homeServerUri --- .../auth/DefaultSessionCreatorTest.kt | 4 +- .../DefaultSessionParamsCreatorTestBase.kt | 4 +- ...keHomeServerConnectionConfigJsonAdapter.kt | 3 +- .../HomeServerConnectionConfigFixture.kt | 51 +++++++++++++++++++ .../sdk/test/fixtures/SessionParamsFixture.kt | 3 +- 5 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt index c9eb2bc2e0..35558fda2f 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt @@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.auth import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runBlockingTest import org.junit.Test -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fakes.internal.FakeSessionManager import org.matrix.android.sdk.test.fakes.internal.auth.FakePendingSessionStore import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsCreator import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsStore import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials +import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams @ExperimentalCoroutinesApi @@ -56,6 +56,6 @@ class DefaultSessionCreatorTest { companion object { private val sessionParams = aSessionParams() private val credentials = aCredentials() - private val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().build() + private val homeServerConnectionConfig = aHomeServerConnectionConfig() } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt index 8265c1cc27..385b03b061 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt @@ -19,11 +19,11 @@ package org.matrix.android.sdk.internal.auth import android.net.Uri import org.amshove.kluent.shouldBeEqualTo import org.matrix.android.sdk.api.auth.data.DiscoveryInformation -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fixtures.CredentialsFixture import org.matrix.android.sdk.test.fixtures.DiscoveryInformationFixture +import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.WellKnownBaseConfigFixture abstract class DefaultSessionParamsCreatorTestBase { @@ -33,7 +33,7 @@ abstract class DefaultSessionParamsCreatorTestBase { protected val credentials = CredentialsFixture.aCredentials() protected val credentialsWithHomeServer = CredentialsFixture.aCredentials(discoveryInformation = discoveryWithHomeServer) protected val credentialsWithIdentityServer = CredentialsFixture.aCredentials(discoveryInformation = discoveryWithIdentityServer) - protected val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().build() + protected val homeServerConnectionConfig = aHomeServerConnectionConfig() protected fun assertExpectedSessionParams(sessionParams: SessionParams) { sessionParams shouldBeEqualTo SessionParams( diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt index d066907cdf..0351849d33 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -22,6 +22,7 @@ import io.mockk.mockk import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity +import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig internal class FakeHomeServerConnectionConfigJsonAdapter { @@ -41,7 +42,7 @@ internal class FakeHomeServerConnectionConfigJsonAdapter { } companion object { - val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().build() + val homeServerConnectionConfig = aHomeServerConnectionConfig() const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt new file mode 100644 index 0000000000..ceffb506d6 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt @@ -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 org.matrix.android.sdk.test.fixtures + +import android.net.Uri +import okhttp3.CipherSuite +import okhttp3.TlsVersion +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig +import org.matrix.android.sdk.internal.network.ssl.Fingerprint + +object HomeServerConnectionConfigFixture { + fun aHomeServerConnectionConfig( + homeServerUri: Uri = Uri.EMPTY, + homeServerUriBase: Uri = Uri.EMPTY, + identityServerUri: Uri? = null, + antiVirusServerUri: Uri? = null, + allowedFingerprints: List = emptyList(), + shouldPin: Boolean = false, + tlsVersions: List? = null, + tlsCipherSuites: List? = null, + shouldAcceptTlsExtensions: Boolean = true, + allowHttpExtension: Boolean = false, + forceUsageTlsVersions: Boolean = false + ) = HomeServerConnectionConfig( + homeServerUri, + homeServerUriBase, + identityServerUri, + antiVirusServerUri, + allowedFingerprints, + shouldPin, + tlsVersions, + tlsCipherSuites, + shouldAcceptTlsExtensions, + allowHttpExtension, + forceUsageTlsVersions, + ) +} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt index 387727e8f0..37f18d1221 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt @@ -21,11 +21,12 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials +import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig object SessionParamsFixture { fun aSessionParams( credentials: Credentials = aCredentials(), - homeServerConnectionConfig: HomeServerConnectionConfig = HomeServerConnectionConfig.Builder().build(), + homeServerConnectionConfig: HomeServerConnectionConfig = aHomeServerConnectionConfig(), isTokenValid: Boolean = false, loginType: LoginType = LoginType.UNKNOWN, ) = SessionParams( From bcd802d3355338403bd9bc96e08a234c021bdd2c Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 20:58:30 +0100 Subject: [PATCH 019/136] Changes login types in wizards --- .../sdk/internal/auth/login/DefaultLoginWizard.kt | 2 +- .../auth/registration/DefaultRegistrationWizard.kt | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt index 6e9a188631..b4b81c7d35 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt @@ -90,7 +90,7 @@ internal class DefaultLoginWizard( authAPI.login(loginParams) } - return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.UNSUPPORTED) + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.SSO) } override suspend fun loginCustom(data: JsonDict): Session { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index 6d4a748a72..c76d24b3d0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -64,7 +64,7 @@ internal class DefaultRegistrationWizard( override suspend fun getRegistrationFlow(): RegistrationResult { val params = RegistrationParams() - return performRegistrationRequest(params, LoginType.UNSUPPORTED) + return performRegistrationRequest(params, LoginType.PASSWORD) } override suspend fun createAccount(userName: String?, @@ -87,7 +87,7 @@ internal class DefaultRegistrationWizard( ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response)) - return performRegistrationRequest(params, LoginType.UNSUPPORTED) + return performRegistrationRequest(params, LoginType.PASSWORD) } override suspend fun acceptTerms(): RegistrationResult { @@ -95,7 +95,7 @@ internal class DefaultRegistrationWizard( ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession)) - return performRegistrationRequest(params, LoginType.UNSUPPORTED) + return performRegistrationRequest(params, LoginType.PASSWORD) } override suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult { @@ -145,14 +145,14 @@ internal class DefaultRegistrationWizard( .also { pendingSessionStore.savePendingSessionData(it) } // and send the sid a first time - return performRegistrationRequest(params, LoginType.UNSUPPORTED) + return performRegistrationRequest(params, LoginType.PASSWORD) } override suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult { val safeParam = pendingSessionData.currentThreePidData?.registrationParams ?: throw IllegalStateException("developer error, no pending three pid") - return performRegistrationRequest(safeParam, LoginType.UNSUPPORTED, delayMillis) + return performRegistrationRequest(safeParam, LoginType.PASSWORD, delayMillis) } override suspend fun handleValidateThreePid(code: String): RegistrationResult { @@ -173,7 +173,7 @@ internal class DefaultRegistrationWizard( if (validationResponse.isSuccess()) { // The entered code is correct // Same than validate email - return performRegistrationRequest(registrationParams, LoginType.UNSUPPORTED, 3_000) + return performRegistrationRequest(registrationParams, LoginType.PASSWORD, 3_000) } else { // The code is not correct throw Failure.SuccessError @@ -185,7 +185,7 @@ internal class DefaultRegistrationWizard( ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession)) - return performRegistrationRequest(params, LoginType.UNSUPPORTED) + return performRegistrationRequest(params, LoginType.PASSWORD) } private suspend fun performRegistrationRequest( From 489670cf6b04f4532c36032bbf7ba21b56e43f7a Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 21:07:12 +0100 Subject: [PATCH 020/136] Adds validation failed test for SessionParamsCreator --- .../auth/DefaultSessionParamsCreatorTest.kt | 11 +++++++++++ .../auth/DefaultSessionParamsCreatorTestBase.kt | 16 ++++++++-------- .../auth/FakeIsValidClientServerApiTask.kt | 4 ++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt index 35693748ef..b303df4d9b 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt @@ -47,6 +47,7 @@ class DefaultSessionParamsCreatorTest : DefaultSessionParamsCreatorTestBase() { @Test fun `given credentials homeServerUri is equal to homeServerConnectionConfig, when create, then do not validate`() = runBlockingTest { val homeServerConnectionConfigWithCredentialsUri = homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri()) + val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfigWithCredentialsUri, LoginType.UNKNOWN) fakeIsValidClientServerApiTask.verifyNoExecution() @@ -59,4 +60,14 @@ class DefaultSessionParamsCreatorTest : DefaultSessionParamsCreatorTestBase() { assertExpectedSessionParamsWithIdentityServer(output) } + + @Test + fun `given home server validation fails, when create, then do not use home server uri from credentials`() = runBlockingTest { + fakeIsValidClientServerApiTask.givenValidationFails() + + val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfig, LoginType.UNKNOWN) + + fakeIsValidClientServerApiTask.verifyExecutionWithConfig(homeServerConnectionConfig) + assertExpectedSessionParams(output) + } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt index 385b03b061..61b808dd1a 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt @@ -21,18 +21,18 @@ import org.amshove.kluent.shouldBeEqualTo import org.matrix.android.sdk.api.auth.data.DiscoveryInformation import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fixtures.CredentialsFixture -import org.matrix.android.sdk.test.fixtures.DiscoveryInformationFixture +import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials +import org.matrix.android.sdk.test.fixtures.DiscoveryInformationFixture.aDiscoveryInformation import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig -import org.matrix.android.sdk.test.fixtures.WellKnownBaseConfigFixture +import org.matrix.android.sdk.test.fixtures.WellKnownBaseConfigFixture.aWellKnownBaseConfig abstract class DefaultSessionParamsCreatorTestBase { - protected val discoveryWithHomeServer = DiscoveryInformationFixture.aDiscoveryInformation(homeServer = WellKnownBaseConfigFixture.aWellKnownBaseConfig("http://homeserver_url/")) - private val discoveryWithIdentityServer = DiscoveryInformationFixture.aDiscoveryInformation(identityServer = WellKnownBaseConfigFixture.aWellKnownBaseConfig("http://identity_server_url/")) - protected val credentials = CredentialsFixture.aCredentials() - protected val credentialsWithHomeServer = CredentialsFixture.aCredentials(discoveryInformation = discoveryWithHomeServer) - protected val credentialsWithIdentityServer = CredentialsFixture.aCredentials(discoveryInformation = discoveryWithIdentityServer) + protected val discoveryWithHomeServer = aDiscoveryInformation(homeServer = aWellKnownBaseConfig("http://homeserver_url/")) + private val discoveryWithIdentityServer = aDiscoveryInformation(identityServer = aWellKnownBaseConfig("http://identity_server_url/")) + protected val credentials = aCredentials() + protected val credentialsWithHomeServer = aCredentials(discoveryInformation = discoveryWithHomeServer) + protected val credentialsWithIdentityServer = aCredentials(discoveryInformation = discoveryWithIdentityServer) protected val homeServerConnectionConfig = aHomeServerConnectionConfig() protected fun assertExpectedSessionParams(sessionParams: SessionParams) { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt index 3eeda6db65..f8018e7b7e 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt @@ -31,6 +31,10 @@ internal class FakeIsValidClientServerApiTask { val instance: IsValidClientServerApiTask = mockk() + fun givenValidationFails() { + coEvery { instance.execute(any()) } returns false + } + fun verifyExecutionWithConfig(config: HomeServerConnectionConfig) { coVerify { instance.execute(Params(config)) } } From c996f876a25ceaa9f70425cec839a88a7613779e Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 21:10:52 +0100 Subject: [PATCH 021/136] Fixes Uri EMPTY must not be null --- .../sdk/test/fixtures/HomeServerConnectionConfigFixture.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt index ceffb506d6..2b3d8cc4fa 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt @@ -24,8 +24,8 @@ import org.matrix.android.sdk.internal.network.ssl.Fingerprint object HomeServerConnectionConfigFixture { fun aHomeServerConnectionConfig( - homeServerUri: Uri = Uri.EMPTY, - homeServerUriBase: Uri = Uri.EMPTY, + homeServerUri: Uri = Uri.parse("aUri"), + homeServerUriBase: Uri = homeServerUri, identityServerUri: Uri? = null, antiVirusServerUri: Uri? = null, allowedFingerprints: List = emptyList(), From 2d07b80acee29bfd4cdfd9eb12192181f5badf3a Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 21:14:27 +0100 Subject: [PATCH 022/136] Fixes broken migration test --- .../auth/db/migration/Fake005MigrationRealm.kt | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt index e5e2b6682f..434b937ad8 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt @@ -18,15 +18,11 @@ package org.matrix.android.sdk.test.fakes.internal.auth.db.migration import io.mockk.every import io.mockk.mockk -import io.mockk.slot -import io.mockk.verify import io.mockk.verifyOrder import io.realm.DynamicRealm -import io.realm.DynamicRealmObject import io.realm.RealmObjectSchema import io.realm.RealmSchema import org.matrix.android.sdk.internal.auth.db.SessionParamsEntityFields -import org.matrix.android.sdk.internal.auth.login.LoginType class Fake005MigrationRealm { @@ -43,26 +39,14 @@ class Fake005MigrationRealm { } fun verifyLoginTypeAdded() { - transformFunctionSlot.clear() verifyLoginTypeFieldAddedAndTransformed() - verifyTransformationSetsUnknownLoginType() } private fun verifyLoginTypeFieldAddedAndTransformed() { verifyOrder { objectSchema["SessionParamsEntity"] objectSchema.addField(SessionParamsEntityFields.LOGIN_TYPE, String::class.java) - objectSchema.transform(capture(transformFunctionSlot)) + objectSchema.transform(any()) } } - - private fun verifyTransformationSetsUnknownLoginType() { - val dynamicRealmObject: DynamicRealmObject = mockk() - transformFunctionSlot.captured.invoke(dynamicRealmObject) - verify { dynamicRealmObject.set(SessionParamsEntityFields.LOGIN_TYPE, LoginType.UNKNOWN.value) } - } - - companion object { - private val transformFunctionSlot = slot<(DynamicRealmObject) -> Unit>() - } } From 7896bf9023047231579ffcc5b8c88c47f8db00df Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 21:36:10 +0100 Subject: [PATCH 023/136] Removes HomeServerConnectionConfigFixture --- .../auth/DefaultSessionCreatorTest.kt | 4 +- .../DefaultSessionParamsCreatorTestBase.kt | 4 +- .../HomeServerConnectionConfigFixture.kt | 51 ------------------- .../sdk/test/fixtures/SessionParamsFixture.kt | 3 +- 4 files changed, 5 insertions(+), 57 deletions(-) delete mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt index 35558fda2f..b389dc5feb 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt @@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.auth import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runBlockingTest import org.junit.Test +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fakes.internal.FakeSessionManager import org.matrix.android.sdk.test.fakes.internal.auth.FakePendingSessionStore import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsCreator import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsStore import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials -import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams @ExperimentalCoroutinesApi @@ -56,6 +56,6 @@ class DefaultSessionCreatorTest { companion object { private val sessionParams = aSessionParams() private val credentials = aCredentials() - private val homeServerConnectionConfig = aHomeServerConnectionConfig() + private val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().withHomeServerUri("homeserver").build() } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt index 61b808dd1a..9c810ff0dc 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt @@ -19,11 +19,11 @@ package org.matrix.android.sdk.internal.auth import android.net.Uri import org.amshove.kluent.shouldBeEqualTo import org.matrix.android.sdk.api.auth.data.DiscoveryInformation +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials import org.matrix.android.sdk.test.fixtures.DiscoveryInformationFixture.aDiscoveryInformation -import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig import org.matrix.android.sdk.test.fixtures.WellKnownBaseConfigFixture.aWellKnownBaseConfig abstract class DefaultSessionParamsCreatorTestBase { @@ -33,7 +33,7 @@ abstract class DefaultSessionParamsCreatorTestBase { protected val credentials = aCredentials() protected val credentialsWithHomeServer = aCredentials(discoveryInformation = discoveryWithHomeServer) protected val credentialsWithIdentityServer = aCredentials(discoveryInformation = discoveryWithIdentityServer) - protected val homeServerConnectionConfig = aHomeServerConnectionConfig() + protected val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().withHomeServerUri("homeserver").build() protected fun assertExpectedSessionParams(sessionParams: SessionParams) { sessionParams shouldBeEqualTo SessionParams( diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt deleted file mode 100644 index 2b3d8cc4fa..0000000000 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/HomeServerConnectionConfigFixture.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 org.matrix.android.sdk.test.fixtures - -import android.net.Uri -import okhttp3.CipherSuite -import okhttp3.TlsVersion -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.internal.network.ssl.Fingerprint - -object HomeServerConnectionConfigFixture { - fun aHomeServerConnectionConfig( - homeServerUri: Uri = Uri.parse("aUri"), - homeServerUriBase: Uri = homeServerUri, - identityServerUri: Uri? = null, - antiVirusServerUri: Uri? = null, - allowedFingerprints: List = emptyList(), - shouldPin: Boolean = false, - tlsVersions: List? = null, - tlsCipherSuites: List? = null, - shouldAcceptTlsExtensions: Boolean = true, - allowHttpExtension: Boolean = false, - forceUsageTlsVersions: Boolean = false - ) = HomeServerConnectionConfig( - homeServerUri, - homeServerUriBase, - identityServerUri, - antiVirusServerUri, - allowedFingerprints, - shouldPin, - tlsVersions, - tlsCipherSuites, - shouldAcceptTlsExtensions, - allowHttpExtension, - forceUsageTlsVersions, - ) -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt index 37f18d1221..079b42b9e7 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt @@ -21,12 +21,11 @@ import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials -import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig object SessionParamsFixture { fun aSessionParams( credentials: Credentials = aCredentials(), - homeServerConnectionConfig: HomeServerConnectionConfig = aHomeServerConnectionConfig(), + homeServerConnectionConfig: HomeServerConnectionConfig = HomeServerConnectionConfig.Builder().withHomeServerUri("homeserver").build(), isTokenValid: Boolean = false, loginType: LoginType = LoginType.UNKNOWN, ) = SessionParams( From 9bd3254e41a48645b12d137b41c8ca9c1e1388d1 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 4 Mar 2022 23:27:21 +0100 Subject: [PATCH 024/136] Removes HomeServerConnectionConfigFixture in fake adapter --- .../sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt index 0351849d33..f0c5f10ebc 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -22,7 +22,6 @@ import io.mockk.mockk import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity -import org.matrix.android.sdk.test.fixtures.HomeServerConnectionConfigFixture.aHomeServerConnectionConfig internal class FakeHomeServerConnectionConfigJsonAdapter { @@ -42,7 +41,7 @@ internal class FakeHomeServerConnectionConfigJsonAdapter { } companion object { - val homeServerConnectionConfig = aHomeServerConnectionConfig() + val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().withHomeServerUri("homeserver").build() const val HOME_SERVER_CONNECTION_CONFIG_JSON = "home_server_connection_config_json" } } From 858923846da93fab370853c28bf205112bc7a789 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 09:56:44 +0100 Subject: [PATCH 025/136] Adds Uri static mocking --- .../sdk/internal/auth/DefaultSessionCreatorTest.kt | 11 +++++++++++ .../fakes/internal/auth/FakeSessionParamsCreator.kt | 5 +++++ .../db/sessionparams/FakeSessionParamsMapperMoshi.kt | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt index b389dc5feb..ba88690587 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt @@ -16,8 +16,13 @@ package org.matrix.android.sdk.internal.auth +import android.net.Uri +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runBlockingTest +import org.junit.Before import org.junit.Test import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.internal.auth.login.LoginType @@ -43,6 +48,12 @@ class DefaultSessionCreatorTest { fakeSessionParamsCreator.instance, ) + @Before + fun setup() { + mockkStatic(Uri::class) + every { Uri.parse(any()) } returns mockk() + } + @Test fun `when createSession, then session created`() = runBlockingTest { val output = sessionCreator.createSession(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt index b0757d5bd5..63cb02973c 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt @@ -16,9 +16,12 @@ package org.matrix.android.sdk.test.fakes.internal.auth +import android.net.Uri import io.mockk.coEvery import io.mockk.coVerify +import io.mockk.every import io.mockk.mockk +import io.mockk.mockkStatic import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.internal.auth.SessionParamsCreator @@ -30,6 +33,8 @@ internal class FakeSessionParamsCreator { val instance: SessionParamsCreator = mockk() init { + mockkStatic(Uri::class) + every { Uri.parse(any()) } returns mockk() coEvery { instance.create(any(), any(), any()) } returns sessionParams } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt index 546f8dcee9..10c3664511 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -16,9 +16,11 @@ package org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams +import android.net.Uri import com.squareup.moshi.Moshi import io.mockk.every import io.mockk.mockk +import io.mockk.mockkStatic import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNull import org.matrix.android.sdk.api.auth.data.Credentials @@ -41,6 +43,8 @@ internal class FakeSessionParamsMapperMoshi { val homeServerConnectionConfigAdapter = FakeHomeServerConnectionConfigJsonAdapter() init { + mockkStatic(Uri::class) + every { Uri.parse(any()) } returns mockk() every { instance.adapter(Credentials::class.java) } returns credentialsJsonAdapter.instance every { instance.adapter(HomeServerConnectionConfig::class.java) } returns homeServerConnectionConfigAdapter.instance } From 63389418853c1db018cc5e868c2c64113361ecc4 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 10:28:48 +0100 Subject: [PATCH 026/136] Temporarily removes unit tests --- .../auth/DefaultSessionCreatorTest.kt | 72 -------------- .../auth/DefaultSessionParamsCreatorTest.kt | 73 -------------- .../DefaultSessionParamsCreatorTestBase.kt | 72 -------------- .../auth/db/SessionParamsMapperTest.kt | 94 ------------------- 4 files changed, 311 deletions(-) delete mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt delete mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt delete mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt delete mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt deleted file mode 100644 index ba88690587..0000000000 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionCreatorTest.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 org.matrix.android.sdk.internal.auth - -import android.net.Uri -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runBlockingTest -import org.junit.Before -import org.junit.Test -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fakes.internal.FakeSessionManager -import org.matrix.android.sdk.test.fakes.internal.auth.FakePendingSessionStore -import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsCreator -import org.matrix.android.sdk.test.fakes.internal.auth.FakeSessionParamsStore -import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials -import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams - -@ExperimentalCoroutinesApi -class DefaultSessionCreatorTest { - - private val fakeSessionParamsStore = FakeSessionParamsStore() - private val fakeSessionManager = FakeSessionManager() - private val fakePendingSessionStore = FakePendingSessionStore() - private val fakeSessionParamsCreator = FakeSessionParamsCreator() - - private val sessionCreator = DefaultSessionCreator( - fakeSessionParamsStore.instance, - fakeSessionManager.instance, - fakePendingSessionStore.instance, - fakeSessionParamsCreator.instance, - ) - - @Before - fun setup() { - mockkStatic(Uri::class) - every { Uri.parse(any()) } returns mockk() - } - - @Test - fun `when createSession, then session created`() = runBlockingTest { - val output = sessionCreator.createSession(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) - - fakePendingSessionStore.verifyPendingSessionDataCleared() - fakeSessionParamsCreator.verifyCreatedWithParameters(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) - fakeSessionParamsStore.verifyParamsSaved(sessionParams) - fakeSessionManager.assertSessionCreatedWithParams(output, sessionParams) - } - - companion object { - private val sessionParams = aSessionParams() - private val credentials = aCredentials() - private val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().withHomeServerUri("homeserver").build() - } -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt deleted file mode 100644 index b303df4d9b..0000000000 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTest.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 org.matrix.android.sdk.internal.auth - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runBlockingTest -import org.junit.Test -import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fakes.internal.auth.FakeIsValidClientServerApiTask - -@ExperimentalCoroutinesApi -class DefaultSessionParamsCreatorTest : DefaultSessionParamsCreatorTestBase() { - - private val fakeIsValidClientServerApiTask = FakeIsValidClientServerApiTask() - - private val sessionParamsCreator = DefaultSessionParamsCreator(fakeIsValidClientServerApiTask.instance) - - @Test - fun `when create, then SessionParams created`() = runBlockingTest { - val output = sessionParamsCreator.create(credentials, homeServerConnectionConfig, LoginType.UNKNOWN) - - assertExpectedSessionParams(output) - } - - @Test - fun `given credentials contains homeServerUri, when create, then SessionParams created with validated credentials uri`() = runBlockingTest { - val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfig, LoginType.UNKNOWN) - - fakeIsValidClientServerApiTask.verifyExecutionWithConfig(homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri())) - assertExpectedSessionParamsWithHomeServer(output) - } - - @Test - fun `given credentials homeServerUri is equal to homeServerConnectionConfig, when create, then do not validate`() = runBlockingTest { - val homeServerConnectionConfigWithCredentialsUri = homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri()) - - val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfigWithCredentialsUri, LoginType.UNKNOWN) - - fakeIsValidClientServerApiTask.verifyNoExecution() - assertExpectedSessionParamsWithHomeServer(output) - } - - @Test - fun `given credentials contains identityServerUri, when create, then SessionParams created with credentials uri`() = runBlockingTest { - val output = sessionParamsCreator.create(credentialsWithIdentityServer, homeServerConnectionConfig, LoginType.UNKNOWN) - - assertExpectedSessionParamsWithIdentityServer(output) - } - - @Test - fun `given home server validation fails, when create, then do not use home server uri from credentials`() = runBlockingTest { - fakeIsValidClientServerApiTask.givenValidationFails() - - val output = sessionParamsCreator.create(credentialsWithHomeServer, homeServerConnectionConfig, LoginType.UNKNOWN) - - fakeIsValidClientServerApiTask.verifyExecutionWithConfig(homeServerConnectionConfig) - assertExpectedSessionParams(output) - } -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt deleted file mode 100644 index 9c810ff0dc..0000000000 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/DefaultSessionParamsCreatorTestBase.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 org.matrix.android.sdk.internal.auth - -import android.net.Uri -import org.amshove.kluent.shouldBeEqualTo -import org.matrix.android.sdk.api.auth.data.DiscoveryInformation -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig -import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.internal.auth.login.LoginType -import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials -import org.matrix.android.sdk.test.fixtures.DiscoveryInformationFixture.aDiscoveryInformation -import org.matrix.android.sdk.test.fixtures.WellKnownBaseConfigFixture.aWellKnownBaseConfig - -abstract class DefaultSessionParamsCreatorTestBase { - - protected val discoveryWithHomeServer = aDiscoveryInformation(homeServer = aWellKnownBaseConfig("http://homeserver_url/")) - private val discoveryWithIdentityServer = aDiscoveryInformation(identityServer = aWellKnownBaseConfig("http://identity_server_url/")) - protected val credentials = aCredentials() - protected val credentialsWithHomeServer = aCredentials(discoveryInformation = discoveryWithHomeServer) - protected val credentialsWithIdentityServer = aCredentials(discoveryInformation = discoveryWithIdentityServer) - protected val homeServerConnectionConfig = HomeServerConnectionConfig.Builder().withHomeServerUri("homeserver").build() - - protected fun assertExpectedSessionParams(sessionParams: SessionParams) { - sessionParams shouldBeEqualTo SessionParams( - credentials = credentials, - homeServerConnectionConfig = homeServerConnectionConfig, - isTokenValid = true, - loginType = LoginType.UNKNOWN, - ) - } - - protected fun assertExpectedSessionParamsWithHomeServer(sessionParams: SessionParams) { - sessionParams shouldBeEqualTo SessionParams( - credentials = credentialsWithHomeServer, - homeServerConnectionConfig = homeServerConnectionConfig.copy(homeServerUriBase = discoveryWithHomeServer.getHomeServerUri()), - isTokenValid = true, - loginType = LoginType.UNKNOWN, - ) - } - - protected fun assertExpectedSessionParamsWithIdentityServer(sessionParams: SessionParams) { - sessionParams shouldBeEqualTo SessionParams( - credentials = credentialsWithHomeServer, - homeServerConnectionConfig = homeServerConnectionConfig.copy(identityServerUri = discoveryWithIdentityServer.getIdentityServerUri()), - isTokenValid = true, - loginType = LoginType.UNKNOWN, - ) - } - - private fun DiscoveryInformation.getIdentityServerUri() = identityServer?.baseURL?.convertToUri()!! - - protected fun DiscoveryInformation.getHomeServerUri() = homeServer?.baseURL?.convertToUri()!! - - private fun String.convertToUri() = trim { it == '/' } - .takeIf { it.isNotBlank() } - .let { Uri.parse(it) } -} diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt deleted file mode 100644 index 1ba3724d63..0000000000 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapperTest.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 org.matrix.android.sdk.internal.auth.db - -import org.junit.Test -import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi -import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParams -import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.nullSessionParamsEntity -import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParams -import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeSessionParamsMapperMoshi.Companion.sessionParamsEntity - -class SessionParamsMapperTest { - - private val fakeMoshi = FakeSessionParamsMapperMoshi() - private val sessionParamsMapper = SessionParamsMapper(fakeMoshi.instance) - - @Test - fun `when mapping entity, then map as SessionParams`() { - val output = sessionParamsMapper.map(sessionParamsEntity) - - fakeMoshi.assertSessionParamsWasMappedSuccessfully(output) - } - - @Test - fun `when mapping null entity, then return null`() { - val output = sessionParamsMapper.map(nullSessionParamsEntity) - - fakeMoshi.assertSessionParamsIsNull(output) - } - - @Test - fun `given null credentials json deserialization, when mapping entity, then return null`() { - fakeMoshi.credentialsJsonAdapter.givenNullDeserialization() - - val output = sessionParamsMapper.map(sessionParamsEntity) - - fakeMoshi.assertSessionParamsIsNull(output) - } - - @Test - fun `given null homeServerConnectionConfig json deserialization, when mapping entity, then return null`() { - fakeMoshi.homeServerConnectionConfigAdapter.givenNullDeserialization() - - val output = sessionParamsMapper.map(sessionParamsEntity) - - fakeMoshi.assertSessionParamsIsNull(output) - } - - @Test - fun `when mapping sessionParams, then map as SessionParamsEntity`() { - val output = sessionParamsMapper.map(sessionParams) - - fakeMoshi.assertSessionParamsEntityWasMappedSuccessfully(output) - } - - @Test - fun `when mapping null sessionParams, then return null`() { - val output = sessionParamsMapper.map(nullSessionParams) - - fakeMoshi.assertSessionParamsEntityWasMappedSuccessfully(output) - } - - @Test - fun `given null credentials json serialization, when mapping sessionParams, then return null`() { - fakeMoshi.credentialsJsonAdapter.givenNullSerialization() - - val output = sessionParamsMapper.map(sessionParams) - - fakeMoshi.assertSessionParamsEntityIsNull(output) - } - - @Test - fun `given null homeServerConnectionConfig json serialization, when mapping sessionParams, then return null`() { - fakeMoshi.homeServerConnectionConfigAdapter.givenNullSerialization() - - val output = sessionParamsMapper.map(sessionParams) - - fakeMoshi.assertSessionParamsEntityIsNull(output) - } -} From 22b21b8c7f0b161c408752bb6724742659ba5d5b Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 10:53:43 +0100 Subject: [PATCH 027/136] Adds changelog file --- changelog.d/5398.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5398.bugfix diff --git a/changelog.d/5398.bugfix b/changelog.d/5398.bugfix new file mode 100644 index 0000000000..24da1952b3 --- /dev/null +++ b/changelog.d/5398.bugfix @@ -0,0 +1 @@ +Adds LoginType to SessionParams to fix soft logout form not showing for SSO and Password type From e8432f31408ed7b707f9bb6e05d85f1823947f12 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 12:07:41 +0100 Subject: [PATCH 028/136] Adds setRequired to migration --- .../android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt index ca3cffeaae..868f62b8cd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt @@ -29,6 +29,7 @@ class MigrateAuthTo005(realm: DynamicRealm) : RealmMigrator(realm, 5) { realm.schema.get("SessionParamsEntity") ?.addField(SessionParamsEntityFields.LOGIN_TYPE, String::class.java) + ?.setRequired(SessionParamsEntityFields.LOGIN_TYPE, true) ?.transform { it.set(SessionParamsEntityFields.LOGIN_TYPE, LoginType.UNKNOWN.value) } } } From 085dd943ff560c817660dfbefc541433320c9ea8 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 12:13:33 +0100 Subject: [PATCH 029/136] Fixes wrong legal comment on LoginType --- .../org/matrix/android/sdk/internal/auth/login/LoginType.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt index 635ecc7658..d14ef12bb6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. From 0d75273121647a0ed4cfae9e65e814ad8ad06b66 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 12:22:25 +0100 Subject: [PATCH 030/136] Adds stubbing and verification for migration setRequired --- .../fakes/internal/auth/db/migration/Fake005MigrationRealm.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt index 434b937ad8..147f8df969 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt @@ -35,6 +35,7 @@ class Fake005MigrationRealm { every { instance.schema } returns schema every { schema.get("SessionParamsEntity") } returns objectSchema every { objectSchema.addField(any(), any()) } returns objectSchema + every { objectSchema.setRequired(any(), any()) } returns objectSchema every { objectSchema.transform(any()) } returns objectSchema } @@ -46,6 +47,7 @@ class Fake005MigrationRealm { verifyOrder { objectSchema["SessionParamsEntity"] objectSchema.addField(SessionParamsEntityFields.LOGIN_TYPE, String::class.java) + objectSchema.setRequired(SessionParamsEntityFields.LOGIN_TYPE, true) objectSchema.transform(any()) } } From e1f227a54545c4628bf749ef5ac5d0fec0e49b8b Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 13:14:10 +0100 Subject: [PATCH 031/136] Fixes legal comments in matrix sdk files added --- .../matrix/android/sdk/internal/auth/SessionParamsCreator.kt | 2 +- .../sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt | 2 +- .../matrix/android/sdk/internal/auth/login/LoginTypeTest.kt | 2 +- .../android/sdk/test/fakes/internal/FakeSessionManager.kt | 2 +- .../test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt | 2 +- .../sdk/test/fakes/internal/auth/FakePendingSessionStore.kt | 2 +- .../sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt | 3 +-- .../sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt | 2 +- .../fakes/internal/auth/db/migration/Fake005MigrationRealm.kt | 2 +- .../auth/db/sessionparams/FakeCredentialsJsonAdapter.kt | 2 +- .../sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt | 2 +- .../auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt | 2 +- .../org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt | 2 +- .../android/sdk/test/fixtures/DiscoveryInformationFixture.kt | 2 +- .../android/sdk/test/fixtures/SessionParamsEntityFixture.kt | 2 +- .../matrix/android/sdk/test/fixtures/SessionParamsFixture.kt | 2 +- .../android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt | 2 +- vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt | 2 +- 18 files changed, 18 insertions(+), 19 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt index 318389486e..607cab3c70 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt index 6b2432b5f6..95b226411b 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005Test.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt index 419dd50991..26741eba4b 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt index b251349249..2232ad9b4f 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/FakeSessionManager.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt index f8018e7b7e..40681748c1 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeIsValidClientServerApiTask.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt index be91daf910..8a18b75ca2 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakePendingSessionStore.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt index 63cb02973c..79a3053f70 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.matrix.android.sdk.test.fakes.internal.auth import android.net.Uri diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt index 4336f6ba7c..22e8a32a32 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsStore.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt index 147f8df969..13fd4a972c 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/migration/Fake005MigrationRealm.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt index eea3e4fab1..f1cb4071fd 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeCredentialsJsonAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt index f0c5f10ebc..f85d6e2778 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeHomeServerConnectionConfigJsonAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt index 10c3664511..62b7f9fc20 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt index 5002fe9153..2e7b36ff63 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/CredentialsFixture.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt index 4a15fd383a..c929a27d23 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/DiscoveryInformationFixture.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt index 29b5b1a7c9..bbea232a22 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsEntityFixture.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt index 079b42b9e7..dad4b84fd0 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt index 96a146587c..a33308dbd6 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/WellKnownBaseConfigFixture.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt index a23c43b986..efb84f4892 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright (c) 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. From 04af8b23605e46bfb4785dd232747e24994c2708 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 13:38:30 +0100 Subject: [PATCH 032/136] Changes LoginActivity onSignModeSelected SSO case --- .../src/main/java/im/vector/app/features/login/LoginActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index bf596fc6aa..499548ce78 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -252,7 +252,7 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA // It depends on the LoginMode when (state.loginMode) { LoginMode.Unknown, - is LoginMode.Sso -> error("Developer error") + is LoginMode.Sso, is LoginMode.SsoAndPassword, LoginMode.Password -> addFragmentToBackstack(views.loginFragmentContainer, LoginFragment::class.java, From 6836a125577da5218d12f169e4b7f3981446a35c Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 7 Mar 2022 13:49:23 +0100 Subject: [PATCH 033/136] Fixes legal comments --- .../java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt | 2 +- vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt index 5aac29c328..df22455fb1 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/api/FakeSession.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * Copyright (c) 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. diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt index efb84f4892..18ed0e9957 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 The Matrix.org Foundation C.I.C. + * 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. From ec57ff1b030d690da47f7c0217ffcd701ac81bda Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 9 Mar 2022 11:07:39 +0100 Subject: [PATCH 034/136] Adds attempt at SSO session restore --- .../im/vector/app/features/login/LoginActivity.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index 499548ce78..f143929978 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -37,6 +37,7 @@ import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.ActivityLoginBinding import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.home.HomeActivity @@ -252,7 +253,7 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA // It depends on the LoginMode when (state.loginMode) { LoginMode.Unknown, - is LoginMode.Sso, + is LoginMode.Sso -> launchSsoFlow() is LoginMode.SsoAndPassword, LoginMode.Password -> addFragmentToBackstack(views.loginFragmentContainer, LoginFragment::class.java, @@ -268,6 +269,16 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA }.exhaustive } + private fun launchSsoFlow() = withState(loginViewModel) { state -> + loginViewModel.getSsoUrl( + redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL, + deviceId = state.deviceId, + providerId = null, + )?.let { ssoUrl -> + openUrlInChromeCustomTab(this, null, ssoUrl) + } + } + /** * Handle the SSO redirection here */ From 70b5b9855a9dc558d8490e49d613913a53bb60e2 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 9 Mar 2022 12:26:52 +0100 Subject: [PATCH 035/136] Removes debug global error --- .../org/matrix/android/sdk/internal/session/sync/SyncTask.kt | 2 -- .../org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index a110f73ad2..b4da1a02cd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.session.sync import android.os.SystemClock import okhttp3.ResponseBody -import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.initsync.InitSyncStep @@ -152,7 +151,6 @@ internal class DefaultSyncTask @Inject constructor( defaultSyncStatusService.setStatus(SyncStatusService.Status.IncrementalSyncIdle) val syncResponse = try { executeRequest(globalErrorReceiver) { - globalErrorReceiver.handleGlobalError(GlobalError.InvalidToken(softLogout = true)) syncAPI.sync( params = requestParams, readTimeOut = readTimeOut diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt index 26741eba4b..95d143af3b 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -30,7 +30,7 @@ class LoginTypeTest { LoginType.fromValue(LoginType.UNKNOWN.value) shouldBeEqualTo LoginType.UNKNOWN } - @Test // This test failing means an existing type has not been correctly added to fromValue + @Test // The failure of this test means that an existing type has not been correctly added to fromValue fun `given non-unknown type value, when getting type fromValue, then type is not UNKNOWN`() { val types = LoginType.values() From ae540297b179162033ba0834ab178c81dbd34ec5 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 15 Mar 2022 14:53:59 +0100 Subject: [PATCH 036/136] Adds custom and direct login types --- .../android/sdk/internal/auth/login/DefaultLoginWizard.kt | 2 +- .../matrix/android/sdk/internal/auth/login/DirectLoginTask.kt | 2 +- .../org/matrix/android/sdk/internal/auth/login/LoginType.kt | 4 ++++ .../matrix/android/sdk/internal/auth/login/LoginTypeTest.kt | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt index b4b81c7d35..489751c1e3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt @@ -98,7 +98,7 @@ internal class DefaultLoginWizard( authAPI.login(data) } - return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.UNSUPPORTED) + return sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, LoginType.CUSTOM) } override suspend fun resetPassword(email: String, newPassword: String) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt index 16f6a13977..787ea45eeb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt @@ -77,7 +77,7 @@ internal class DefaultDirectLoginTask @Inject constructor( } } - return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.UNSUPPORTED) + return sessionCreator.createSession(credentials, params.homeServerConnectionConfig, LoginType.DIRECT) } private fun buildClient(homeServerConnectionConfig: HomeServerConnectionConfig): OkHttpClient { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt index d14ef12bb6..31d8b01d38 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt @@ -20,6 +20,8 @@ enum class LoginType(val value: String) { PASSWORD("password"), SSO("sso"), UNSUPPORTED("unsupported"), + CUSTOM("custom"), + DIRECT("direct"), UNKNOWN("unknown"); companion object { @@ -28,6 +30,8 @@ enum class LoginType(val value: String) { PASSWORD.value -> PASSWORD SSO.value -> SSO UNSUPPORTED.value -> UNSUPPORTED + CUSTOM.value -> CUSTOM + DIRECT.value -> DIRECT else -> UNKNOWN } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt index 95d143af3b..1650c863de 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -27,6 +27,8 @@ class LoginTypeTest { LoginType.fromValue(LoginType.PASSWORD.value) shouldBeEqualTo LoginType.PASSWORD LoginType.fromValue(LoginType.SSO.value) shouldBeEqualTo LoginType.SSO LoginType.fromValue(LoginType.UNSUPPORTED.value) shouldBeEqualTo LoginType.UNSUPPORTED + LoginType.fromValue(LoginType.CUSTOM.value) shouldBeEqualTo LoginType.CUSTOM + LoginType.fromValue(LoginType.DIRECT.value) shouldBeEqualTo LoginType.DIRECT LoginType.fromValue(LoginType.UNKNOWN.value) shouldBeEqualTo LoginType.UNKNOWN } From a173accfa5c2b5b9a0741723b3eee15269893336 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 15 Mar 2022 14:59:09 +0100 Subject: [PATCH 037/136] Replaces use of LoginType.value with name --- .../internal/auth/db/SessionParamsMapper.kt | 4 +-- .../sdk/internal/auth/login/LoginType.kt | 26 +++++++++---------- .../sdk/internal/auth/login/LoginTypeTest.kt | 18 ++++++------- .../FakeSessionParamsMapperMoshi.kt | 4 +-- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt index e359577ff3..20de72dc8f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt @@ -38,7 +38,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { if (credentials == null || homeServerConnectionConfig == null) { return null } - return SessionParams(credentials, homeServerConnectionConfig, entity.isTokenValid, LoginType.fromValue(entity.loginType)) + return SessionParams(credentials, homeServerConnectionConfig, entity.isTokenValid, LoginType.fromName(entity.loginType)) } fun map(sessionParams: SessionParams?): SessionParamsEntity? { @@ -56,7 +56,7 @@ internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { credentialsJson, homeServerConnectionConfigJson, sessionParams.isTokenValid, - sessionParams.loginType.value, + sessionParams.loginType.name, ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt index 31d8b01d38..5bc38d81fc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt @@ -16,22 +16,22 @@ package org.matrix.android.sdk.internal.auth.login -enum class LoginType(val value: String) { - PASSWORD("password"), - SSO("sso"), - UNSUPPORTED("unsupported"), - CUSTOM("custom"), - DIRECT("direct"), - UNKNOWN("unknown"); +enum class LoginType { + PASSWORD, + SSO, + UNSUPPORTED, + CUSTOM, + DIRECT, + UNKNOWN; companion object { - fun fromValue(value: String) = when (value) { - PASSWORD.value -> PASSWORD - SSO.value -> SSO - UNSUPPORTED.value -> UNSUPPORTED - CUSTOM.value -> CUSTOM - DIRECT.value -> DIRECT + fun fromName(name: String) = when (name) { + PASSWORD.name -> PASSWORD + SSO.name -> SSO + UNSUPPORTED.name -> UNSUPPORTED + CUSTOM.name -> CUSTOM + DIRECT.name -> DIRECT else -> UNKNOWN } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt index 1650c863de..341d09765c 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -23,22 +23,22 @@ import org.junit.Test class LoginTypeTest { @Test - fun `when getting type fromValue, then map correctly`() { - LoginType.fromValue(LoginType.PASSWORD.value) shouldBeEqualTo LoginType.PASSWORD - LoginType.fromValue(LoginType.SSO.value) shouldBeEqualTo LoginType.SSO - LoginType.fromValue(LoginType.UNSUPPORTED.value) shouldBeEqualTo LoginType.UNSUPPORTED - LoginType.fromValue(LoginType.CUSTOM.value) shouldBeEqualTo LoginType.CUSTOM - LoginType.fromValue(LoginType.DIRECT.value) shouldBeEqualTo LoginType.DIRECT - LoginType.fromValue(LoginType.UNKNOWN.value) shouldBeEqualTo LoginType.UNKNOWN + fun `when getting type fromName, then map correctly`() { + LoginType.fromName(LoginType.PASSWORD.name) shouldBeEqualTo LoginType.PASSWORD + LoginType.fromName(LoginType.SSO.name) shouldBeEqualTo LoginType.SSO + LoginType.fromName(LoginType.UNSUPPORTED.name) shouldBeEqualTo LoginType.UNSUPPORTED + LoginType.fromName(LoginType.CUSTOM.name) shouldBeEqualTo LoginType.CUSTOM + LoginType.fromName(LoginType.DIRECT.name) shouldBeEqualTo LoginType.DIRECT + LoginType.fromName(LoginType.UNKNOWN.name) shouldBeEqualTo LoginType.UNKNOWN } @Test // The failure of this test means that an existing type has not been correctly added to fromValue - fun `given non-unknown type value, when getting type fromValue, then type is not UNKNOWN`() { + fun `given non-unknown type name, when getting type fromName, then type is not UNKNOWN`() { val types = LoginType.values() types.forEach { type -> if (type != LoginType.UNKNOWN) { - LoginType.fromValue(type.value) shouldNotBeEqualTo LoginType.UNKNOWN + LoginType.fromName(type.name) shouldNotBeEqualTo LoginType.UNKNOWN } } } diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt index 62b7f9fc20..bc5a2fa7ca 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -54,7 +54,7 @@ internal class FakeSessionParamsMapperMoshi { credentials, homeServerConnectionConfig, sessionParamsEntity.isTokenValid, - LoginType.fromValue(sessionParamsEntity.loginType) + LoginType.fromName(sessionParamsEntity.loginType) ) } @@ -69,7 +69,7 @@ internal class FakeSessionParamsMapperMoshi { CREDENTIALS_JSON, HOME_SERVER_CONNECTION_CONFIG_JSON, sessionParams.isTokenValid, - sessionParams.loginType.value, + sessionParams.loginType.name, ) } From ef59faf160107fc7c0bc0e740439c6a0818b6f74 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 15 Mar 2022 15:05:33 +0100 Subject: [PATCH 038/136] Adds error throw for LoginActivity LoginMode Unknown --- .../registration/DefaultRegistrationWizard.kt | 98 ++++---- .../app/features/login/LoginActivity.kt | 230 +++++++++--------- 2 files changed, 165 insertions(+), 163 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index c76d24b3d0..9e715be193 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -35,9 +35,9 @@ import org.matrix.android.sdk.internal.auth.login.LoginType * This class execute the registration request and is responsible to keep the session of interactive authentication */ internal class DefaultRegistrationWizard( - authAPI: AuthAPI, - private val sessionCreator: SessionCreator, - private val pendingSessionStore: PendingSessionStore + authAPI: AuthAPI, + private val sessionCreator: SessionCreator, + private val pendingSessionStore: PendingSessionStore ) : RegistrationWizard { private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here") @@ -50,12 +50,12 @@ internal class DefaultRegistrationWizard( override val currentThreePid: String? get() { return when (val threePid = pendingSessionData.currentThreePidData?.threePid) { - is RegisterThreePid.Email -> threePid.email + is RegisterThreePid.Email -> threePid.email is RegisterThreePid.Msisdn -> { // Take formatted msisdn if provided by the server pendingSessionData.currentThreePidData?.addThreePidRegistrationResponse?.formattedMsisdn?.takeIf { it.isNotBlank() } ?: threePid.msisdn } - null -> null + null -> null } } @@ -67,24 +67,26 @@ internal class DefaultRegistrationWizard( return performRegistrationRequest(params, LoginType.PASSWORD) } - override suspend fun createAccount(userName: String?, - password: String?, - initialDeviceDisplayName: String?): RegistrationResult { + override suspend fun createAccount( + userName: String?, + password: String?, + initialDeviceDisplayName: String? + ): RegistrationResult { val params = RegistrationParams( - username = userName, - password = password, - initialDeviceDisplayName = initialDeviceDisplayName + username = userName, + password = password, + initialDeviceDisplayName = initialDeviceDisplayName ) return performRegistrationRequest(params, LoginType.PASSWORD) - .also { - pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true) - .also { pendingSessionStore.savePendingSessionData(it) } - } + .also { + pendingSessionData = pendingSessionData.copy(isRegistrationStarted = true) + .also { pendingSessionStore.savePendingSessionData(it) } + } } override suspend fun performReCaptcha(response: String): RegistrationResult { val safeSession = pendingSessionData.currentSession - ?: throw IllegalStateException("developer error, call createAccount() method first") + ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams.createForCaptcha(safeSession, response)) return performRegistrationRequest(params, LoginType.PASSWORD) @@ -92,7 +94,7 @@ internal class DefaultRegistrationWizard( override suspend fun acceptTerms(): RegistrationResult { val safeSession = pendingSessionData.currentSession - ?: throw IllegalStateException("developer error, call createAccount() method first") + ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.TERMS, session = safeSession)) return performRegistrationRequest(params, LoginType.PASSWORD) @@ -100,14 +102,14 @@ internal class DefaultRegistrationWizard( override suspend fun addThreePid(threePid: RegisterThreePid): RegistrationResult { pendingSessionData = pendingSessionData.copy(currentThreePidData = null) - .also { pendingSessionStore.savePendingSessionData(it) } + .also { pendingSessionStore.savePendingSessionData(it) } return sendThreePid(threePid) } override suspend fun sendAgainThreePid(): RegistrationResult { val safeCurrentThreePid = pendingSessionData.currentThreePidData?.threePid - ?: throw IllegalStateException("developer error, call createAccount() method first") + ?: throw IllegalStateException("developer error, call createAccount() method first") return sendThreePid(safeCurrentThreePid) } @@ -115,34 +117,34 @@ internal class DefaultRegistrationWizard( private suspend fun sendThreePid(threePid: RegisterThreePid): RegistrationResult { val safeSession = pendingSessionData.currentSession ?: throw IllegalStateException("developer error, call createAccount() method first") val response = registerAddThreePidTask.execute( - RegisterAddThreePidTask.Params( - threePid, - pendingSessionData.clientSecret, - pendingSessionData.sendAttempt)) + RegisterAddThreePidTask.Params( + threePid, + pendingSessionData.clientSecret, + pendingSessionData.sendAttempt)) pendingSessionData = pendingSessionData.copy(sendAttempt = pendingSessionData.sendAttempt + 1) - .also { pendingSessionStore.savePendingSessionData(it) } + .also { pendingSessionStore.savePendingSessionData(it) } val params = RegistrationParams( - auth = if (threePid is RegisterThreePid.Email) { - AuthParams.createForEmailIdentity(safeSession, - ThreePidCredentials( - clientSecret = pendingSessionData.clientSecret, - sid = response.sid - ) + auth = if (threePid is RegisterThreePid.Email) { + AuthParams.createForEmailIdentity(safeSession, + ThreePidCredentials( + clientSecret = pendingSessionData.clientSecret, + sid = response.sid ) - } else { - AuthParams.createForMsisdnIdentity(safeSession, - ThreePidCredentials( - clientSecret = pendingSessionData.clientSecret, - sid = response.sid - ) + ) + } else { + AuthParams.createForMsisdnIdentity(safeSession, + ThreePidCredentials( + clientSecret = pendingSessionData.clientSecret, + sid = response.sid ) - } + ) + } ) // Store data pendingSessionData = pendingSessionData.copy(currentThreePidData = ThreePidData.from(threePid, response, params)) - .also { pendingSessionStore.savePendingSessionData(it) } + .also { pendingSessionStore.savePendingSessionData(it) } // and send the sid a first time return performRegistrationRequest(params, LoginType.PASSWORD) @@ -150,7 +152,7 @@ internal class DefaultRegistrationWizard( override suspend fun checkIfEmailHasBeenValidated(delayMillis: Long): RegistrationResult { val safeParam = pendingSessionData.currentThreePidData?.registrationParams - ?: throw IllegalStateException("developer error, no pending three pid") + ?: throw IllegalStateException("developer error, no pending three pid") return performRegistrationRequest(safeParam, LoginType.PASSWORD, delayMillis) } @@ -161,13 +163,13 @@ internal class DefaultRegistrationWizard( private suspend fun validateThreePid(code: String): RegistrationResult { val registrationParams = pendingSessionData.currentThreePidData?.registrationParams - ?: throw IllegalStateException("developer error, no pending three pid") + ?: throw IllegalStateException("developer error, no pending three pid") val safeCurrentData = pendingSessionData.currentThreePidData ?: throw IllegalStateException("developer error, call createAccount() method first") val url = safeCurrentData.addThreePidRegistrationResponse.submitUrl ?: throw IllegalStateException("Missing url to send the code") val validationBody = ValidationCodeBody( - clientSecret = pendingSessionData.clientSecret, - sid = safeCurrentData.addThreePidRegistrationResponse.sid, - code = code + clientSecret = pendingSessionData.clientSecret, + sid = safeCurrentData.addThreePidRegistrationResponse.sid, + code = code ) val validationResponse = validateCodeTask.execute(ValidateCodeTask.Params(url, validationBody)) if (validationResponse.isSuccess()) { @@ -182,16 +184,16 @@ internal class DefaultRegistrationWizard( override suspend fun dummy(): RegistrationResult { val safeSession = pendingSessionData.currentSession - ?: throw IllegalStateException("developer error, call createAccount() method first") + ?: throw IllegalStateException("developer error, call createAccount() method first") val params = RegistrationParams(auth = AuthParams(type = LoginFlowTypes.DUMMY, session = safeSession)) return performRegistrationRequest(params, LoginType.PASSWORD) } private suspend fun performRegistrationRequest( - registrationParams: RegistrationParams, - loginType: LoginType, - delayMillis: Long = 0, + registrationParams: RegistrationParams, + loginType: LoginType, + delayMillis: Long = 0, ): RegistrationResult { delay(delayMillis) val credentials = try { @@ -199,7 +201,7 @@ internal class DefaultRegistrationWizard( } catch (exception: Throwable) { if (exception is RegistrationFlowError) { pendingSessionData = pendingSessionData.copy(currentSession = exception.registrationFlowResponse.session) - .also { pendingSessionStore.savePendingSessionData(it) } + .also { pendingSessionStore.savePendingSessionData(it) } return RegistrationResult.FlowResponse(exception.registrationFlowResponse.toFlowResult()) } else { throw exception diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index f143929978..b2be245c0e 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -69,11 +69,11 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA private val commonOption: (FragmentTransaction) -> Unit = { ft -> // Find the loginLogo on the current Fragment, this should not return null (topFragment?.view as? ViewGroup) - // Find findViewById does not work, I do not know why - // findViewById(R.id.loginLogo) - ?.children - ?.firstOrNull { it.id == R.id.loginLogo } - ?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // Find findViewById does not work, I do not know why + // findViewById(R.id.loginLogo) + ?.children + ?.firstOrNull { it.id == R.id.loginLogo } + ?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) } @@ -107,7 +107,7 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA private fun handleLoginViewEvents(loginViewEvents: LoginViewEvents) { when (loginViewEvents) { - is LoginViewEvents.RegistrationFlowResult -> { + is LoginViewEvents.RegistrationFlowResult -> { // Check that all flows are supported by the application if (loginViewEvents.flowResult.missingStages.any { !it.isSupported() }) { // Display a popup to propose use web fallback @@ -121,80 +121,80 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA // I add a tag to indicate that this fragment is a registration stage. // This way it will be automatically popped in when starting the next registration stage addFragmentToBackstack(views.loginFragmentContainer, - LoginFragment::class.java, - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption + LoginFragment::class.java, + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption ) } } } - is LoginViewEvents.OutdatedHomeserver -> { + is LoginViewEvents.OutdatedHomeserver -> { MaterialAlertDialogBuilder(this) - .setTitle(R.string.login_error_outdated_homeserver_title) - .setMessage(R.string.login_error_outdated_homeserver_warning_content) - .setPositiveButton(R.string.ok, null) - .show() + .setTitle(R.string.login_error_outdated_homeserver_title) + .setMessage(R.string.login_error_outdated_homeserver_warning_content) + .setPositiveButton(R.string.ok, null) + .show() Unit } - is LoginViewEvents.OpenServerSelection -> + is LoginViewEvents.OpenServerSelection -> addFragmentToBackstack(views.loginFragmentContainer, - LoginServerSelectionFragment::class.java, - option = { ft -> - findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // Disable transition of text - // findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // No transition here now actually - // findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } - // TODO Disabled because it provokes a flickering - // ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) - }) - is LoginViewEvents.OnServerSelectionDone -> onServerSelectionDone(loginViewEvents) - is LoginViewEvents.OnSignModeSelected -> onSignModeSelected(loginViewEvents) - is LoginViewEvents.OnLoginFlowRetrieved -> + LoginServerSelectionFragment::class.java, + option = { ft -> + findViewById(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // Disable transition of text + // findViewById(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // No transition here now actually + // findViewById(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") } + // TODO Disabled because it provokes a flickering + // ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim) + }) + is LoginViewEvents.OnServerSelectionDone -> onServerSelectionDone(loginViewEvents) + is LoginViewEvents.OnSignModeSelected -> onSignModeSelected(loginViewEvents) + is LoginViewEvents.OnLoginFlowRetrieved -> addFragmentToBackstack(views.loginFragmentContainer, - LoginSignUpSignInSelectionFragment::class.java, - option = commonOption) - is LoginViewEvents.OnWebLoginError -> onWebLoginError(loginViewEvents) - is LoginViewEvents.OnForgetPasswordClicked -> + LoginSignUpSignInSelectionFragment::class.java, + option = commonOption) + is LoginViewEvents.OnWebLoginError -> onWebLoginError(loginViewEvents) + is LoginViewEvents.OnForgetPasswordClicked -> addFragmentToBackstack(views.loginFragmentContainer, - LoginResetPasswordFragment::class.java, - option = commonOption) - is LoginViewEvents.OnResetPasswordSendThreePidDone -> { + LoginResetPasswordFragment::class.java, + option = commonOption) + is LoginViewEvents.OnResetPasswordSendThreePidDone -> { supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE) addFragmentToBackstack(views.loginFragmentContainer, - LoginResetPasswordMailConfirmationFragment::class.java, - option = commonOption) + LoginResetPasswordMailConfirmationFragment::class.java, + option = commonOption) } - is LoginViewEvents.OnResetPasswordMailConfirmationSuccess -> { + is LoginViewEvents.OnResetPasswordMailConfirmationSuccess -> { supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE) addFragmentToBackstack(views.loginFragmentContainer, - LoginResetPasswordSuccessFragment::class.java, - option = commonOption) + LoginResetPasswordSuccessFragment::class.java, + option = commonOption) } is LoginViewEvents.OnResetPasswordMailConfirmationSuccessDone -> { // Go back to the login fragment supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE) } - is LoginViewEvents.OnSendEmailSuccess -> { + is LoginViewEvents.OnSendEmailSuccess -> { // Pop the enter email Fragment supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) addFragmentToBackstack(views.loginFragmentContainer, - LoginWaitForEmailFragment::class.java, - LoginWaitForEmailFragmentArgument(loginViewEvents.email), - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption) + LoginWaitForEmailFragment::class.java, + LoginWaitForEmailFragmentArgument(loginViewEvents.email), + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) } - is LoginViewEvents.OnSendMsisdnSuccess -> { + is LoginViewEvents.OnSendMsisdnSuccess -> { // Pop the enter Msisdn Fragment supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) addFragmentToBackstack(views.loginFragmentContainer, - LoginGenericTextInputFormFragment::class.java, - LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, loginViewEvents.msisdn), - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption) + LoginGenericTextInputFormFragment::class.java, + LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, loginViewEvents.msisdn), + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) } is LoginViewEvents.Failure, - is LoginViewEvents.Loading -> + is LoginViewEvents.Loading -> // This is handled by the Fragments Unit }.exhaustive @@ -207,8 +207,8 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA analyticsScreenName = MobileScreen.ScreenName.Register } val intent = HomeActivity.newIntent( - this, - accountCreation = loginViewState.signMode == SignMode.SignUp + this, + accountCreation = loginViewState.signMode == SignMode.SignUp ) startActivity(intent) finish() @@ -225,55 +225,55 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA // And inform the user MaterialAlertDialogBuilder(this) - .setTitle(R.string.dialog_title_error) - .setMessage(getString(R.string.login_sso_error_message, onWebLoginError.description, onWebLoginError.errorCode)) - .setPositiveButton(R.string.ok, null) - .show() + .setTitle(R.string.dialog_title_error) + .setMessage(getString(R.string.login_sso_error_message, onWebLoginError.description, onWebLoginError.errorCode)) + .setPositiveButton(R.string.ok, null) + .show() } private fun onServerSelectionDone(loginViewEvents: LoginViewEvents.OnServerSelectionDone) { when (loginViewEvents.serverType) { ServerType.MatrixOrg -> Unit // In this case, we wait for the login flow ServerType.EMS, - ServerType.Other -> addFragmentToBackstack(views.loginFragmentContainer, - LoginServerUrlFormFragment::class.java, - option = commonOption) - ServerType.Unknown -> Unit /* Should not happen */ + ServerType.Other -> addFragmentToBackstack(views.loginFragmentContainer, + LoginServerUrlFormFragment::class.java, + option = commonOption) + ServerType.Unknown -> Unit /* Should not happen */ } } private fun onSignModeSelected(loginViewEvents: LoginViewEvents.OnSignModeSelected) = withState(loginViewModel) { state -> // state.signMode could not be ready yet. So use value from the ViewEvent when (loginViewEvents.signMode) { - SignMode.Unknown -> error("Sign mode has to be set before calling this method") - SignMode.SignUp -> { + SignMode.Unknown -> error("Sign mode has to be set before calling this method") + SignMode.SignUp -> { // This is managed by the LoginViewEvents } - SignMode.SignIn -> { + SignMode.SignIn -> { // It depends on the LoginMode when (state.loginMode) { - LoginMode.Unknown, + LoginMode.Unknown -> error("Developer error") is LoginMode.Sso -> launchSsoFlow() is LoginMode.SsoAndPassword, - LoginMode.Password -> addFragmentToBackstack(views.loginFragmentContainer, - LoginFragment::class.java, - tag = FRAGMENT_LOGIN_TAG, - option = commonOption) + LoginMode.Password -> addFragmentToBackstack(views.loginFragmentContainer, + LoginFragment::class.java, + tag = FRAGMENT_LOGIN_TAG, + option = commonOption) LoginMode.Unsupported -> onLoginModeNotSupported(state.loginModeSupportedTypes) }.exhaustive } SignMode.SignInWithMatrixId -> addFragmentToBackstack(views.loginFragmentContainer, - LoginFragment::class.java, - tag = FRAGMENT_LOGIN_TAG, - option = commonOption) + LoginFragment::class.java, + tag = FRAGMENT_LOGIN_TAG, + option = commonOption) }.exhaustive } private fun launchSsoFlow() = withState(loginViewModel) { state -> loginViewModel.getSsoUrl( - redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL, - deviceId = state.deviceId, - providerId = null, + redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL, + deviceId = state.deviceId, + providerId = null, )?.let { ssoUrl -> openUrlInChromeCustomTab(this, null, ssoUrl) } @@ -286,34 +286,34 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA super.onNewIntent(intent) intent?.data - ?.let { tryOrNull { it.getQueryParameter("loginToken") } } - ?.let { loginViewModel.handle(LoginAction.LoginWithToken(it)) } + ?.let { tryOrNull { it.getQueryParameter("loginToken") } } + ?.let { loginViewModel.handle(LoginAction.LoginWithToken(it)) } } private fun onRegistrationStageNotSupported() { MaterialAlertDialogBuilder(this) - .setTitle(R.string.app_name) - .setMessage(getString(R.string.login_registration_not_supported)) - .setPositiveButton(R.string.yes) { _, _ -> - addFragmentToBackstack(views.loginFragmentContainer, - LoginWebFragment::class.java, - option = commonOption) - } - .setNegativeButton(R.string.no, null) - .show() + .setTitle(R.string.app_name) + .setMessage(getString(R.string.login_registration_not_supported)) + .setPositiveButton(R.string.yes) { _, _ -> + addFragmentToBackstack(views.loginFragmentContainer, + LoginWebFragment::class.java, + option = commonOption) + } + .setNegativeButton(R.string.no, null) + .show() } private fun onLoginModeNotSupported(supportedTypes: List) { MaterialAlertDialogBuilder(this) - .setTitle(R.string.app_name) - .setMessage(getString(R.string.login_mode_not_supported, supportedTypes.joinToString { "'$it'" })) - .setPositiveButton(R.string.yes) { _, _ -> - addFragmentToBackstack(views.loginFragmentContainer, - LoginWebFragment::class.java, - option = commonOption) - } - .setNegativeButton(R.string.no, null) - .show() + .setTitle(R.string.app_name) + .setMessage(getString(R.string.login_mode_not_supported, supportedTypes.joinToString { "'$it'" })) + .setPositiveButton(R.string.yes) { _, _ -> + addFragmentToBackstack(views.loginFragmentContainer, + LoginWebFragment::class.java, + option = commonOption) + } + .setNegativeButton(R.string.no, null) + .show() } private fun handleRegistrationNavigation(flowResult: FlowResult) { @@ -339,26 +339,26 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA when (stage) { is Stage.ReCaptcha -> addFragmentToBackstack(views.loginFragmentContainer, - LoginCaptchaFragment::class.java, - LoginCaptchaFragmentArgument(stage.publicKey), - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption) - is Stage.Email -> addFragmentToBackstack(views.loginFragmentContainer, - LoginGenericTextInputFormFragment::class.java, - LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetEmail, stage.mandatory), - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption) - is Stage.Msisdn -> addFragmentToBackstack(views.loginFragmentContainer, - LoginGenericTextInputFormFragment::class.java, - LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory), - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption) - is Stage.Terms -> addFragmentToBackstack(views.loginFragmentContainer, - LoginTermsFragment::class.java, - LoginTermsFragmentArgument(stage.policies.toLocalizedLoginTerms(getString(R.string.resources_language))), - tag = FRAGMENT_REGISTRATION_STAGE_TAG, - option = commonOption) - else -> Unit // Should not happen + LoginCaptchaFragment::class.java, + LoginCaptchaFragmentArgument(stage.publicKey), + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) + is Stage.Email -> addFragmentToBackstack(views.loginFragmentContainer, + LoginGenericTextInputFormFragment::class.java, + LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetEmail, stage.mandatory), + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) + is Stage.Msisdn -> addFragmentToBackstack(views.loginFragmentContainer, + LoginGenericTextInputFormFragment::class.java, + LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.SetMsisdn, stage.mandatory), + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) + is Stage.Terms -> addFragmentToBackstack(views.loginFragmentContainer, + LoginTermsFragment::class.java, + LoginTermsFragmentArgument(stage.policies.toLocalizedLoginTerms(getString(R.string.resources_language))), + tag = FRAGMENT_REGISTRATION_STAGE_TAG, + option = commonOption) + else -> Unit // Should not happen } } From 21459db63408cfe4d83ef5ebfbd4ef636874b10a Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 15 Mar 2022 15:23:03 +0100 Subject: [PATCH 039/136] Replaces login type unknown value with name in migration --- .../android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt index 868f62b8cd..9c5095e478 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt @@ -30,6 +30,6 @@ class MigrateAuthTo005(realm: DynamicRealm) : RealmMigrator(realm, 5) { realm.schema.get("SessionParamsEntity") ?.addField(SessionParamsEntityFields.LOGIN_TYPE, String::class.java) ?.setRequired(SessionParamsEntityFields.LOGIN_TYPE, true) - ?.transform { it.set(SessionParamsEntityFields.LOGIN_TYPE, LoginType.UNKNOWN.value) } + ?.transform { it.set(SessionParamsEntityFields.LOGIN_TYPE, LoginType.UNKNOWN.name) } } } From 5fcae7f4e6c87190240a811fd3fbd516d9afbcb9 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 15 Mar 2022 18:03:54 +0100 Subject: [PATCH 040/136] Adds direct and custom types to soft logout disambiguation --- .../signout/soft/SoftLogoutController.kt | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt index 6742894fde..439f9ecdb5 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -40,8 +40,8 @@ import org.matrix.android.sdk.internal.auth.login.LoginType import javax.inject.Inject class SoftLogoutController @Inject constructor( - private val stringProvider: StringProvider, - private val errorFormatter: ErrorFormatter + private val stringProvider: StringProvider, + private val errorFormatter: ErrorFormatter ) : EpoxyController() { var listener: Listener? = null @@ -77,9 +77,9 @@ class SoftLogoutController @Inject constructor( loginTextItem { id("signText1") text(host.stringProvider.getString(R.string.soft_logout_signin_notice, - state.homeServerUrl.toReducedUrl(), - state.userDisplayName, - state.userId)) + state.homeServerUrl.toReducedUrl(), + state.userDisplayName, + state.userId)) } if (state.hasUnsavedKeys) { loginTextItem { @@ -90,9 +90,9 @@ class SoftLogoutController @Inject constructor( } private fun buildForm(state: SoftLogoutViewState) = when (state.asyncHomeServerLoginFlowRequest) { - is Incomplete -> buildLoadingItem() - is Fail -> buildLoginErrorWithRetryItem(state.asyncHomeServerLoginFlowRequest.error) - is Success -> buildLoginSuccessItem(state) + is Incomplete -> buildLoadingItem() + is Fail -> buildLoginErrorWithRetryItem(state.asyncHomeServerLoginFlowRequest.error) + is Success -> buildLoginSuccessItem(state) is Loading, Uninitialized -> Unit } @@ -112,11 +112,11 @@ class SoftLogoutController @Inject constructor( } private fun buildLoginSuccessItem(state: SoftLogoutViewState) = when (state.asyncHomeServerLoginFlowRequest.invoke()) { - LoginMode.Password -> buildLoginPasswordForm(state) - is LoginMode.Sso -> buildLoginSSOForm() + LoginMode.Password -> buildLoginPasswordForm(state) + is LoginMode.Sso -> buildLoginSSOForm() is LoginMode.SsoAndPassword -> disambiguateLoginSSOAndPasswordForm(state) - LoginMode.Unsupported -> buildLoginUnsupportedForm() - LoginMode.Unknown, null -> Unit // Should not happen + LoginMode.Unsupported -> buildLoginUnsupportedForm() + LoginMode.Unknown, null -> Unit // Should not happen } private fun buildLoginPasswordForm(state: SoftLogoutViewState) { @@ -144,10 +144,12 @@ class SoftLogoutController @Inject constructor( private fun disambiguateLoginSSOAndPasswordForm(state: SoftLogoutViewState) { when (state.loginType) { - LoginType.PASSWORD -> buildLoginPasswordForm(state) - LoginType.SSO -> buildLoginSSOForm() + LoginType.PASSWORD -> buildLoginPasswordForm(state) + LoginType.SSO -> buildLoginSSOForm() + LoginType.DIRECT, + LoginType.CUSTOM, LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() - LoginType.UNKNOWN -> Unit + LoginType.UNKNOWN -> Unit } } From 92d177a68e9e8fe5846040de642eb4d02bb8681a Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 20 Apr 2022 20:05:54 +0200 Subject: [PATCH 041/136] Makes MigrateAuthTo005 class internal --- .../android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt index 9c5095e478..82996928de 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt @@ -22,7 +22,7 @@ import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.internal.util.database.RealmMigrator import timber.log.Timber -class MigrateAuthTo005(realm: DynamicRealm) : RealmMigrator(realm, 5) { +internal class MigrateAuthTo005(realm: DynamicRealm) : RealmMigrator(realm, 5) { override fun doMigrate(realm: DynamicRealm) { Timber.d("Update SessionParamsEntity to add LoginType") From 6a3044cb2ec21c5bdcb423a27f0d27778d8d3fc1 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 17 May 2022 14:46:02 +0200 Subject: [PATCH 042/136] Fixes post merge errors --- .../registration/DefaultRegistrationWizard.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index 765d4ec20d..ff7bcb0303 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -207,7 +207,7 @@ internal class DefaultRegistrationWizard( mutableParams["session"] = safeSession val params = RegistrationCustomParams(auth = mutableParams) - return performRegistrationOtherRequest(params) + return performRegistrationOtherRequest(LoginType.CUSTOM, params) } private suspend fun performRegistrationRequest( @@ -216,17 +216,19 @@ internal class DefaultRegistrationWizard( delayMillis: Long = 0 ): RegistrationResult { delay(delayMillis) - return register { registerTask.execute(RegisterTask.Params(registrationParams)) } + return register(loginType) { registerTask.execute(RegisterTask.Params(registrationParams)) } } private suspend fun performRegistrationOtherRequest( - registrationCustomParams: RegistrationCustomParams + loginType: LoginType, + registrationCustomParams: RegistrationCustomParams, ): RegistrationResult { - return register { registerCustomTask.execute(RegisterCustomTask.Params(registrationCustomParams)) } + return register(loginType) { registerCustomTask.execute(RegisterCustomTask.Params(registrationCustomParams)) } } private suspend fun register( - execute: suspend () -> Credentials + loginType: LoginType, + execute: suspend () -> Credentials, ): RegistrationResult { val credentials = try { execute.invoke() @@ -241,8 +243,7 @@ internal class DefaultRegistrationWizard( } } - val session = - sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, loginType) + val session = sessionCreator.createSession(credentials, pendingSessionData.homeServerConnectionConfig, loginType) return RegistrationResult.Success(session) } From 61b8053b9b2d368b56cfc49d9c1d35d1f17d9647 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 18 May 2022 11:23:34 +0200 Subject: [PATCH 043/136] Moves LoginType to sdk api package --- .../auth/login => api/auth}/LoginType.kt | 2 +- .../sdk/api/auth/data/SessionParams.kt | 2 +- .../auth/DefaultAuthenticationService.kt | 2 +- .../sdk/internal/auth/SessionCreator.kt | 2 +- .../sdk/internal/auth/SessionParamsCreator.kt | 2 +- .../internal/auth/db/SessionParamsMapper.kt | 2 +- .../auth/db/migration/MigrateAuthTo005.kt | 2 +- .../internal/auth/login/DefaultLoginWizard.kt | 1 + .../internal/auth/login/DirectLoginTask.kt | 1 + .../registration/DefaultRegistrationWizard.kt | 2 +- .../legacy/DefaultLegacySessionImporter.kt | 2 +- .../sdk/internal/auth/login/LoginTypeTest.kt | 1 + .../internal/auth/FakeSessionParamsCreator.kt | 2 +- .../FakeSessionParamsMapperMoshi.kt | 6 ++--- .../sdk/test/fixtures/SessionParamsFixture.kt | 2 +- .../signout/soft/SoftLogoutController.kt | 26 +++++++++---------- .../signout/soft/SoftLogoutViewModel.kt | 6 ++--- .../signout/soft/SoftLogoutViewState.kt | 2 +- 18 files changed, 33 insertions(+), 32 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal/auth/login => api/auth}/LoginType.kt (95%) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt index 5bc38d81fc..627a825679 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/LoginType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/LoginType.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.auth.login +package org.matrix.android.sdk.api.auth enum class LoginType { PASSWORD, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt index 60cd792f7e..860c982eab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt @@ -16,7 +16,7 @@ package org.matrix.android.sdk.api.auth.data -import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.api.auth.LoginType /** * This data class holds necessary data to open a session. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index aa05ffb49b..33f30ae587 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -22,6 +22,7 @@ import okhttp3.OkHttpClient import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.MatrixPatterns.getDomain import org.matrix.android.sdk.api.auth.AuthenticationService +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.LoginFlowResult @@ -38,7 +39,6 @@ import org.matrix.android.sdk.internal.auth.data.WebClientConfig import org.matrix.android.sdk.internal.auth.db.PendingSessionData import org.matrix.android.sdk.internal.auth.login.DefaultLoginWizard import org.matrix.android.sdk.internal.auth.login.DirectLoginTask -import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.internal.auth.registration.DefaultRegistrationWizard import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt index f75b119f18..7dbb11c7fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionCreator.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.auth +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.SessionManager -import org.matrix.android.sdk.internal.auth.login.LoginType import javax.inject.Inject internal interface SessionCreator { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt index 607cab3c70..31ed9a1e85 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/SessionParamsCreator.kt @@ -17,11 +17,11 @@ package org.matrix.android.sdk.internal.auth import android.net.Uri +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.internal.auth.login.LoginType import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt index 20de72dc8f..23923bf267 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/SessionParamsMapper.kt @@ -17,11 +17,11 @@ package org.matrix.android.sdk.internal.auth.db import com.squareup.moshi.Moshi +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId -import org.matrix.android.sdk.internal.auth.login.LoginType import javax.inject.Inject internal class SessionParamsMapper @Inject constructor(moshi: Moshi) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt index 82996928de..2cf1b62a4c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/migration/MigrateAuthTo005.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.auth.db.migration import io.realm.DynamicRealm +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.internal.auth.db.SessionParamsEntityFields -import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.internal.util.database.RealmMigrator import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt index 489751c1e3..eebe058a83 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DefaultLoginWizard.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.auth.login import android.util.Patterns +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.login.LoginProfileInfo import org.matrix.android.sdk.api.auth.login.LoginWizard import org.matrix.android.sdk.api.auth.registration.RegisterThreePid diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt index 787ea45eeb..3e382054e4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.auth.login import dagger.Lazy import okhttp3.OkHttpClient +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt index ff7bcb0303..787ae9af7d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/registration/DefaultRegistrationWizard.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.auth.registration import kotlinx.coroutines.delay +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.registration.RegisterThreePid @@ -31,7 +32,6 @@ import org.matrix.android.sdk.internal.auth.AuthAPI import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.auth.SessionCreator import org.matrix.android.sdk.internal.auth.db.PendingSessionData -import org.matrix.android.sdk.internal.auth.login.LoginType /** * This class execute the registration request and is responsible to keep the session of interactive authentication. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt index 74a534e1fd..155ef02ead 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt @@ -20,6 +20,7 @@ import android.content.Context import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.runBlocking +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.DiscoveryInformation import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig @@ -29,7 +30,6 @@ import org.matrix.android.sdk.api.legacy.LegacySessionImporter import org.matrix.android.sdk.api.network.ssl.Fingerprint import org.matrix.android.sdk.api.util.md5 import org.matrix.android.sdk.internal.auth.SessionParamsStore -import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule import org.matrix.android.sdk.internal.database.RealmKeysUtils diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt index 341d09765c..495302acb2 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/auth/login/LoginTypeTest.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.auth.login import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldNotBeEqualTo import org.junit.Test +import org.matrix.android.sdk.api.auth.LoginType class LoginTypeTest { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt index 79a3053f70..f64e5a451d 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/FakeSessionParamsCreator.kt @@ -21,10 +21,10 @@ import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.internal.auth.SessionParamsCreator -import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams internal class FakeSessionParamsCreator { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt index bc5a2fa7ca..ed0ddb1179 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fakes/internal/auth/db/sessionparams/FakeSessionParamsMapperMoshi.kt @@ -23,12 +23,12 @@ import io.mockk.mockk import io.mockk.mockkStatic import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeNull +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.internal.auth.db.SessionParamsEntity -import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.CREDENTIALS_JSON import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeCredentialsJsonAdapter.Companion.credentials import org.matrix.android.sdk.test.fakes.internal.auth.db.sessionparams.FakeHomeServerConnectionConfigJsonAdapter.Companion.HOME_SERVER_CONNECTION_CONFIG_JSON @@ -39,8 +39,8 @@ import org.matrix.android.sdk.test.fixtures.SessionParamsFixture.aSessionParams internal class FakeSessionParamsMapperMoshi { val instance: Moshi = mockk() - val credentialsJsonAdapter = FakeCredentialsJsonAdapter() - val homeServerConnectionConfigAdapter = FakeHomeServerConnectionConfigJsonAdapter() + private val credentialsJsonAdapter = FakeCredentialsJsonAdapter() + private val homeServerConnectionConfigAdapter = FakeHomeServerConnectionConfigJsonAdapter() init { mockkStatic(Uri::class) diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt index dad4b84fd0..5cbbe1a47a 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/SessionParamsFixture.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.test.fixtures +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.internal.auth.login.LoginType import org.matrix.android.sdk.test.fixtures.CredentialsFixture.aCredentials object SessionParamsFixture { diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt index 301d0af187..265cf3199e 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutController.kt @@ -36,12 +36,12 @@ import im.vector.app.features.signout.soft.epoxy.loginRedButtonItem import im.vector.app.features.signout.soft.epoxy.loginTextItem import im.vector.app.features.signout.soft.epoxy.loginTitleItem import im.vector.app.features.signout.soft.epoxy.loginTitleSmallItem -import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.api.auth.LoginType import javax.inject.Inject class SoftLogoutController @Inject constructor( - private val stringProvider: StringProvider, - private val errorFormatter: ErrorFormatter + private val stringProvider: StringProvider, + private val errorFormatter: ErrorFormatter ) : EpoxyController() { var listener: Listener? = null @@ -94,10 +94,10 @@ class SoftLogoutController @Inject constructor( } private fun buildForm(state: SoftLogoutViewState) = when (state.asyncHomeServerLoginFlowRequest) { - is Fail -> buildLoginErrorWithRetryItem(state.asyncHomeServerLoginFlowRequest.error) - is Success -> buildLoginSuccessItem(state) + is Fail -> buildLoginErrorWithRetryItem(state.asyncHomeServerLoginFlowRequest.error) + is Success -> buildLoginSuccessItem(state) is Loading, Uninitialized -> buildLoadingItem() - is Incomplete -> Unit + is Incomplete -> Unit } private fun buildLoadingItem() { @@ -116,11 +116,11 @@ class SoftLogoutController @Inject constructor( } private fun buildLoginSuccessItem(state: SoftLogoutViewState) = when (state.asyncHomeServerLoginFlowRequest.invoke()) { - LoginMode.Password -> buildLoginPasswordForm(state) - is LoginMode.Sso -> buildLoginSSOForm() + LoginMode.Password -> buildLoginPasswordForm(state) + is LoginMode.Sso -> buildLoginSSOForm() is LoginMode.SsoAndPassword -> disambiguateLoginSSOAndPasswordForm(state) - LoginMode.Unsupported -> buildLoginUnsupportedForm() - LoginMode.Unknown, null -> Unit // Should not happen + LoginMode.Unsupported -> buildLoginUnsupportedForm() + LoginMode.Unknown, null -> Unit // Should not happen } private fun buildLoginPasswordForm(state: SoftLogoutViewState) { @@ -148,12 +148,12 @@ class SoftLogoutController @Inject constructor( private fun disambiguateLoginSSOAndPasswordForm(state: SoftLogoutViewState) { when (state.loginType) { - LoginType.PASSWORD -> buildLoginPasswordForm(state) - LoginType.SSO -> buildLoginSSOForm() + LoginType.PASSWORD -> buildLoginPasswordForm(state) + LoginType.SSO -> buildLoginSSOForm() LoginType.DIRECT, LoginType.CUSTOM, LoginType.UNSUPPORTED -> buildLoginUnsupportedForm() - LoginType.UNKNOWN -> Unit + LoginType.UNKNOWN -> Unit } } diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index d0a810976d..6ae076408a 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -35,15 +35,13 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.login.LoginMode import kotlinx.coroutines.launch import org.matrix.android.sdk.api.auth.AuthenticationService +import org.matrix.android.sdk.api.auth.LoginType import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getUser -import org.matrix.android.sdk.internal.auth.login.LoginType import timber.log.Timber -/** - * TODO Test push: disable the pushers? - */ + class SoftLogoutViewModel @AssistedInject constructor( @Assisted initialState: SoftLogoutViewState, private val session: Session, diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt index f13b089ac5..28c8273412 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt @@ -22,7 +22,7 @@ import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import im.vector.app.features.login.LoginMode -import org.matrix.android.sdk.internal.auth.login.LoginType +import org.matrix.android.sdk.api.auth.LoginType data class SoftLogoutViewState( val asyncHomeServerLoginFlowRequest: Async = Uninitialized, From a71c50c6381f0439f1f02dea786524e6bf2c3e30 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 18 May 2022 11:38:38 +0200 Subject: [PATCH 044/136] Fixes lint error --- .../im/vector/app/features/signout/soft/SoftLogoutViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index 6ae076408a..d44c408173 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -41,7 +41,6 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.getUser import timber.log.Timber - class SoftLogoutViewModel @AssistedInject constructor( @Assisted initialState: SoftLogoutViewState, private val session: Session, From fe274515323b7512c41145c82cd7a3410fa43880 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 18 May 2022 12:09:34 +0200 Subject: [PATCH 045/136] Fixes lint error --- .../java/org/matrix/android/sdk/api/auth/data/SessionParams.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt index 860c982eab..de227631ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/data/SessionParams.kt @@ -39,7 +39,7 @@ data class SessionParams( val isTokenValid: Boolean, /** - * Which authentication method was used to create the session + * The authentication method that was used to create the session. */ val loginType: LoginType, ) { From 93559aee63115a53fc87cac092c29c9699086f27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 23:07:28 +0000 Subject: [PATCH 046/136] Bump google-services from 4.3.10 to 4.3.13 Bumps google-services from 4.3.10 to 4.3.13. --- updated-dependencies: - dependency-name: com.google.gms:google-services dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0244080ad0..61027a0bff 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ buildscript { classpath libs.gradle.gradlePlugin classpath libs.gradle.kotlinPlugin classpath libs.gradle.hiltPlugin - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.3.13' classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5' classpath "com.likethesalad.android:stem-plugin:2.1.1" From 62a20ba69d9c63c6c48ed8d3d2d5a3fd48a9e929 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 23:10:20 +0000 Subject: [PATCH 047/136] Bump activity from 1.4.0 to 1.5.0 Bumps activity from 1.4.0 to 1.5.0. --- updated-dependencies: - dependency-name: androidx.activity:activity dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index db9278b975..878d565f4d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -50,7 +50,7 @@ ext.libs = [ ], androidx : [ 'annotation' : "androidx.annotation:annotation:1.4.0", - 'activity' : "androidx.activity:activity:1.4.0", + 'activity' : "androidx.activity:activity:1.5.0", 'annotations' : "androidx.annotation:annotation:1.3.0", 'appCompat' : "androidx.appcompat:appcompat:1.4.2", 'biometric' : "androidx.biometric:biometric:1.1.0", From 356718dc9a94a80f24df478b6a919c00eef3f914 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 23:11:02 +0000 Subject: [PATCH 048/136] Bump fragment from 1.4.1 to 1.5.0 Bumps `fragment` from 1.4.1 to 1.5.0. Updates `fragment-ktx` from 1.4.1 to 1.5.0 Updates `fragment-testing` from 1.4.1 to 1.5.0 --- updated-dependencies: - dependency-name: androidx.fragment:fragment-ktx dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: androidx.fragment:fragment-testing dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index db9278b975..0002cc9166 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -29,7 +29,7 @@ def bigImageViewer = "1.8.1" def jjwt = "0.11.5" def vanniktechEmoji = "0.15.0" -def fragment = "1.4.1" +def fragment = "1.5.0" // Testing def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819 From 9fb19af39c7f6d6f083b37d5d4a43433efc39d58 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 30 Jun 2022 09:58:00 +0200 Subject: [PATCH 049/136] Adding a description under undisclosed poll when not yet ended --- .../room/detail/timeline/factory/PollItemViewStateFactory.kt | 2 +- vector/src/main/res/values/strings.xml | 1 + .../detail/timeline/factory/PollItemViewStateFactoryTest.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt index 8da0f2d279..1d8bae6d5c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt @@ -107,7 +107,7 @@ class PollItemViewStateFactory @Inject constructor( ): PollViewState { return PollViewState( question = question, - totalVotes = "", + totalVotes = stringProvider.getString(R.string.poll_undisclosed_not_ended), canVote = true, optionViewStates = pollCreationInfo?.answers?.map { answer -> val isMyVote = pollResponseSummary?.myVote == answer.id diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index dc5ea0fa8c..0c067a175f 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2980,6 +2980,7 @@ Based on %1$d votes No votes cast + Results will be visible when the poll is ended %1$d vote cast. Vote to the see the results %1$d votes cast. Vote to the see the results diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt index 64ad03a019..4af33657f5 100644 --- a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt +++ b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt @@ -143,7 +143,7 @@ class PollItemViewStateFactoryTest { pollViewState shouldBeEqualTo PollViewState( question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "", - totalVotes = "", + totalVotes = stringProvider.instance.getString(R.string.poll_undisclosed_not_ended), canVote = true, optionViewStates = A_POLL_CONTENT.getBestPollCreationInfo()?.answers?.map { answer -> PollOptionViewState.PollUndisclosed( From 55bb6fa21a7c1041b5882bfc17331a79966981c4 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 30 Jun 2022 10:13:17 +0200 Subject: [PATCH 050/136] Adding changelog entry --- changelog.d/6423.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6423.misc diff --git a/changelog.d/6423.misc b/changelog.d/6423.misc new file mode 100644 index 0000000000..26c1e84830 --- /dev/null +++ b/changelog.d/6423.misc @@ -0,0 +1 @@ +[Poll] - Add a description under undisclosed poll when not ended From a8d43538f1fb642161258535a21b17dbefe5978f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 30 Jun 2022 14:51:07 +0200 Subject: [PATCH 051/136] Explicitely hiding votes when disclosed poll --- .../features/home/room/detail/timeline/item/PollOptionView.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollOptionView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollOptionView.kt index b21b1a152d..20aa6e3af2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollOptionView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/PollOptionView.kt @@ -82,6 +82,7 @@ class PollOptionView @JvmOverloads constructor( private fun renderPollUndisclosed(state: PollOptionViewState.PollUndisclosed) { views.optionCheckImageView.isVisible = true views.optionWinnerImageView.isVisible = false + hideVotes() renderVoteSelection(state.isSelected) } From 18efa84e3a258b28e48808b5c9cf3fdba48b1808 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 30 Jun 2022 14:58:32 +0200 Subject: [PATCH 052/136] Adding changelog entry --- changelog.d/6430.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6430.bugfix diff --git a/changelog.d/6430.bugfix b/changelog.d/6430.bugfix new file mode 100644 index 0000000000..99b1cb836d --- /dev/null +++ b/changelog.d/6430.bugfix @@ -0,0 +1 @@ +[Poll] Fixes visible and wrong votes in closed poll after removing 2 previous polls From 34713d50232eae0453fb04380f12d1792ba110d9 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 20 Apr 2022 12:02:08 +0300 Subject: [PATCH 053/136] Add sharing existing inbound sessions functionality on new room invites --- .../crypto/algorithms/IMXDecrypting.kt | 3 ++ .../algorithms/megolm/MXMegolmDecryption.kt | 44 ++++++++++++++++++- .../internal/crypto/store/IMXCryptoStore.kt | 8 ++++ .../crypto/store/db/RealmCryptoStore.kt | 12 +++++ .../membership/DefaultMembershipService.kt | 3 ++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt index 6847a46369..ce445d3ce6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService /** @@ -43,4 +44,6 @@ internal interface IMXDecrypting { * @param defaultKeysBackupService the keys backup service */ fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {} + + fun shareKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) {} } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 141d6f74cd..a9ffe5f008 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -28,7 +28,9 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.internal.crypto.MXOlmDevice -import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager +import org.matrix.android.sdk.internal.crypto.MegolmSessionData +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.algorithms.IMXDecrypting import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -307,4 +309,44 @@ internal class MXMegolmDecryption( Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey") newSessionListener?.onNewSession(roomId, senderKey, sessionId) } + override fun shareKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) { + exportedKeys ?: return + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + runCatching { deviceListManager.downloadKeys(listOf(userId), false) } + .mapCatching { + val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) + if (deviceInfo == null) { + throw RuntimeException() + } else { + val devicesByUser = mapOf(userId to listOf(deviceInfo)) + val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) + val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) + if (olmSessionResult?.sessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.") + return@mapCatching + } + Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${exportedKeys.sessionId} with device $userId:$deviceId") + + val payloadJson = mapOf( + "type" to EventType.FORWARDED_ROOM_KEY, + "content" to exportedKeys + ) + + val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(userId, deviceId, encodedPayload) + Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${exportedKeys.sessionId} to $userId:$deviceId") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) + try { + sendToDeviceTask.execute(sendToDeviceParams) + } catch (failure: Throwable) { + Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${exportedKeys.sessionId} to $userId:$deviceId") + } + } + } + } + } + } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index b5b8d8e974..b961e83b9a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -66,6 +66,14 @@ internal interface IMXCryptoStore { */ fun getInboundGroupSessions(): List + /** + * Retrieve the known inbound group sessions for the specified room + * + * @param roomId The roomId that the sessions will be returned + * @return the list of all known group sessions, for the provided roomId + */ + fun getInboundGroupSessions(roomId: String): List + /** * @return true to unilaterally blacklist all unverified devices. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 028d8f73f9..a3c7d6ccc1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -828,6 +828,18 @@ internal class RealmCryptoStore @Inject constructor( } } + override fun getInboundGroupSessions(roomId: String): List { + return doWithRealm(realmConfiguration) { + it.where() + .findAll() + .mapNotNull { inboundGroupSessionEntity -> + inboundGroupSessionEntity.getInboundGroupSession() + }.filter { inboundSession -> + inboundSession.roomId == roomId + } + } + } + override fun removeInboundGroupSession(sessionId: String, senderKey: String) { val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index ec140e7b17..2b3fa10c89 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -23,6 +23,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.realm.Realm import io.realm.RealmQuery +import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams @@ -50,6 +51,7 @@ internal class DefaultMembershipService @AssistedInject constructor( private val inviteThreePidTask: InviteThreePidTask, private val membershipAdminTask: MembershipAdminTask, private val roomDataSource: RoomDataSource, + private val cryptoService: CryptoService, @UserId private val userId: String, private val queryStringValueProcessor: QueryStringValueProcessor @@ -139,6 +141,7 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { + cryptoService.sendSharedHistoryKeys(roomId, userId) val params = InviteTask.Params(roomId, userId, reason) inviteTask.execute(params) } From 98b55457b5e036b1a2ea8e094aefae35847a00f7 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 21 Apr 2022 13:43:41 +0300 Subject: [PATCH 054/136] Add sendSharedHistoryKeys in crypto service --- .../sdk/api/session/crypto/CryptoService.kt | 5 +++++ .../internal/crypto/DefaultCryptoService.kt | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index 638da11804..99fb41b10d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -176,4 +176,9 @@ interface CryptoService { * send, in order to speed up sending of the message. */ fun prepareToEncrypt(roomId: String, callback: MatrixCallback) + + /** + * Share existing inbound sessions with the provided userId devices + */ + fun sendSharedHistoryKeys(roomId: String, userId: String) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index e0bcde2296..689c49f33b 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -1334,7 +1334,23 @@ internal class DefaultCryptoService @Inject constructor( ) } } - + override fun sendSharedHistoryKeys(roomId: String, userId: String) { + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + val userDevices = cryptoStore.getUserDevices(userId) + userDevices?.forEach { + // Lets share our existing inbound sessions for every user device + val deviceId = it.key + val inboundSessions = cryptoStore.getInboundGroupSessions(roomId) + inboundSessions.forEach { inboundGroupSession -> + // Share the session with the to userId with deviceId + val exportedKeys = inboundGroupSession.exportKeys() + val algorithm = exportedKeys?.algorithm + val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) + decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) + } + } + } + } /* ========================================================================================== * For test only * ========================================================================================== */ From 6e57aeb9e5f981746d298a89728fa34834ae9367 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 21 Apr 2022 17:10:02 +0300 Subject: [PATCH 055/136] Add roomId in InboundSessionEntity for better performance Add shared history flag to InboundSessionEntity --- .../internal/crypto/DefaultCryptoService.kt | 1 + .../crypto/store/db/RealmCryptoStore.kt | 13 +++++--- .../store/db/RealmCryptoStoreMigration.kt | 4 ++- .../store/db/migration/MigrateCryptoTo017.kt | 32 +++++++++++++++++++ .../db/model/OlmInboundGroupSessionEntity.kt | 4 +++ 5 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 689c49f33b..ee95dfed9e 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -964,6 +964,7 @@ internal class DefaultCryptoService @Inject constructor( if (!event.isStateEvent()) return val eventContent = event.content.toModel() eventContent?.historyVisibility?.let { + Timber.i("-----> onRoomHistoryVisibilityEvent $it") cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index a3c7d6ccc1..c39ae6813e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -745,9 +745,13 @@ internal class RealmCryptoStore @Inject constructor( if (sessionIdentifier != null) { val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, session.senderKey) - val existing = realm.where() - .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) - .findFirst() + val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { + primaryKey = key + sessionId = sessionIdentifier + senderKey = session.senderKey + roomId = session.roomId + putInboundGroupSession(session) + } if (existing != null) { // we want to keep the existing backup status @@ -831,11 +835,10 @@ internal class RealmCryptoStore @Inject constructor( override fun getInboundGroupSessions(roomId: String): List { return doWithRealm(realmConfiguration) { it.where() + .equalTo(OlmInboundGroupSessionEntityFields.ROOM_ID, roomId) .findAll() .mapNotNull { inboundGroupSessionEntity -> inboundGroupSessionEntity.getInboundGroupSession() - }.filter { inboundSession -> - inboundSession.roomId == roomId } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 02c2a27dec..4ca9d44f98 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -34,6 +34,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo014 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016 +import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017 import org.matrix.android.sdk.internal.util.time.Clock import timber.log.Timber import javax.inject.Inject @@ -51,7 +52,7 @@ internal class RealmCryptoStoreMigration @Inject constructor( // 0, 1, 2: legacy Riot-Android // 3: migrate to RiotX schema // 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6) - val schemaVersion = 16L + val schemaVersion = 17L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.d("Migrating Realm Crypto from $oldVersion to $newVersion") @@ -72,5 +73,6 @@ internal class RealmCryptoStoreMigration @Inject constructor( if (oldVersion < 14) MigrateCryptoTo014(realm).perform() if (oldVersion < 15) MigrateCryptoTo015(realm).perform() if (oldVersion < 16) MigrateCryptoTo016(realm).perform() + if (oldVersion < 17) MigrateCryptoTo017(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt new file mode 100644 index 0000000000..36f17f4674 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 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.store.db.migration + +import io.realm.DynamicRealm + +import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +// Version 16L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061 +internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 16) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("OlmInboundGroupSessionEntity") + ?.addField(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, Boolean::class.java) + ?.addField(OlmInboundGroupSessionEntityFields.ROOM_ID, String::class.java) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt index a4f6c279ac..12e9b8baee 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt @@ -30,8 +30,12 @@ internal open class OlmInboundGroupSessionEntity( @PrimaryKey var primaryKey: String? = null, var sessionId: String? = null, var senderKey: String? = null, + var roomId: String? = null, // olmInboundGroupSessionData contains Json var olmInboundGroupSessionData: String? = null, + // Flag that indicates whether or not the current inboundSession will be shared to + // invited users to decrypt past messages + var sharedHistory: Boolean = false, // Indicate if the key has been backed up to the homeserver var backedUp: Boolean = false ) : From e861edd544b49a5702f66f8d1b00e98c5df094be Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 26 Apr 2022 17:51:03 +0300 Subject: [PATCH 056/136] Implement history key sharing functionality with respect to room visibility settings --- .../room/model/RoomHistoryVisibility.kt | 6 ++++ .../internal/crypto/DefaultCryptoService.kt | 9 +++-- .../model/OlmInboundGroupSessionWrapper2.kt | 4 +++ .../internal/crypto/store/IMXCryptoStore.kt | 11 ++++++ .../crypto/store/db/RealmCryptoStore.kt | 34 +++++++++++-------- .../store/db/RealmCryptoStoreMigration.kt | 3 ++ .../store/db/migration/MigrateCryptoTo017.kt | 4 +++ .../crypto/store/db/model/CryptoRoomEntity.kt | 2 ++ 8 files changed, 57 insertions(+), 16 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt index 06069f2646..3648d6b883 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt @@ -48,3 +48,9 @@ enum class RoomHistoryVisibility { */ @Json(name = "joined") JOINED } + +/** + * Room history should be shared only if room visibility is world_readable or shared + */ +internal fun RoomHistoryVisibility.shouldShareHistory() = + this == RoomHistoryVisibility.WORLD_READABLE || this == RoomHistoryVisibility.SHARED diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index ee95dfed9e..4a44e9e2d0 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -71,6 +71,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.api.session.room.model.shouldShareHistory import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.crypto.actions.MegolmSessionDataImporter import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction @@ -964,8 +965,8 @@ internal class DefaultCryptoService @Inject constructor( if (!event.isStateEvent()) return val eventContent = event.content.toModel() eventContent?.historyVisibility?.let { - Timber.i("-----> onRoomHistoryVisibilityEvent $it") cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED) + cryptoStore.setShouldShareHistory(roomId, it.shouldShareHistory()) } } @@ -1335,6 +1336,7 @@ internal class DefaultCryptoService @Inject constructor( ) } } + override fun sendSharedHistoryKeys(roomId: String, userId: String) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { val userDevices = cryptoStore.getUserDevices(userId) @@ -1342,12 +1344,15 @@ internal class DefaultCryptoService @Inject constructor( // Lets share our existing inbound sessions for every user device val deviceId = it.key val inboundSessions = cryptoStore.getInboundGroupSessions(roomId) - inboundSessions.forEach { inboundGroupSession -> + inboundSessions.filter { inboundGroupSession -> + inboundGroupSession.sharedHistory + }.forEach { inboundGroupSession -> // Share the session with the to userId with deviceId val exportedKeys = inboundGroupSession.exportKeys() val algorithm = exportedKeys?.algorithm val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) + Timber.i("## CRYPTO | Sharing inbound session") } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt index 289c169d6d..570532ae8b 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt @@ -43,6 +43,10 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { // Devices which forwarded this session to us (normally empty). var forwardingCurve25519KeyChain: List? = ArrayList() + // Flag that indicates whether or not the current inboundSession will be shared to + // invited users to decrypt past messages + var sharedHistory: Boolean = false + /** * @return the first known message index */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index b961e83b9a..ceb91aa8ce 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -258,6 +258,17 @@ internal interface IMXCryptoStore { fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean) + fun shouldShareHistory(roomId: String): Boolean + + /** + * Sets a boolean flag that will determine whether or not room history (existing inbound sessions) + * will be shared to new user invites + * + * @param roomId the room id + * @param shouldShareHistory The boolean flag + */ + fun setShouldShareHistory(roomId: String, shouldShareHistory: Boolean) + /** * Store a session between the logged-in user and another device. * diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index c39ae6813e..7cc2f75285 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -657,12 +657,25 @@ internal class RealmCryptoStore @Inject constructor( ?: false } + override fun shouldShareHistory(roomId: String): Boolean { + return doWithRealm(realmConfiguration) { + CryptoRoomEntity.getById(it, roomId)?.shouldShareHistory + } + ?: false + } + override fun setShouldEncryptForInvitedMembers(roomId: String, shouldEncryptForInvitedMembers: Boolean) { doRealmTransaction(realmConfiguration) { CryptoRoomEntity.getOrCreate(it, roomId).shouldEncryptForInvitedMembers = shouldEncryptForInvitedMembers } } + override fun setShouldShareHistory(roomId: String, shouldShareHistory: Boolean) { + doRealmTransaction(realmConfiguration) { + CryptoRoomEntity.getOrCreate(it, roomId).shouldShareHistory = shouldShareHistory + } + } + override fun storeSession(olmSessionWrapper: OlmSessionWrapper, deviceKey: String) { var sessionIdentifier: String? = null @@ -743,6 +756,10 @@ internal class RealmCryptoStore @Inject constructor( } if (sessionIdentifier != null) { + val shouldShareHistory = session.roomId?.let { roomId -> + CryptoRoomEntity.getById(realm, roomId)?.shouldShareHistory + } ?: false + session.sharedHistory = shouldShareHistory val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, session.senderKey) val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { @@ -750,22 +767,11 @@ internal class RealmCryptoStore @Inject constructor( sessionId = sessionIdentifier senderKey = session.senderKey roomId = session.roomId + sharedHistory = shouldShareHistory putInboundGroupSession(session) } - - if (existing != null) { - // we want to keep the existing backup status - existing.putInboundGroupSession(session) - } else { - val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { - primaryKey = key - sessionId = sessionIdentifier - senderKey = session.senderKey - putInboundGroupSession(session) - } - - realm.insertOrUpdate(realmOlmInboundGroupSession) - } + Timber.i("## CRYPTO | shouldShareHistory: $shouldShareHistory for $key") + realm.insertOrUpdate(realmOlmInboundGroupSession) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 4ca9d44f98..35229d205d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -73,6 +73,9 @@ internal class RealmCryptoStoreMigration @Inject constructor( if (oldVersion < 14) MigrateCryptoTo014(realm).perform() if (oldVersion < 15) MigrateCryptoTo015(realm).perform() if (oldVersion < 16) MigrateCryptoTo016(realm).perform() +<<<<<<< develop if (oldVersion < 17) MigrateCryptoTo017(realm).perform() +======= +>>>>>>> Implement history key sharing functionality with respect to room visibility settings } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt index 36f17f4674..6ce94c3eb2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.crypto.store.db.migration import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields import org.matrix.android.sdk.internal.util.database.RealmMigrator @@ -28,5 +29,8 @@ internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 16 realm.schema.get("OlmInboundGroupSessionEntity") ?.addField(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, Boolean::class.java) ?.addField(OlmInboundGroupSessionEntityFields.ROOM_ID, String::class.java) + + realm.schema.get("CryptoRoomEntity") + ?.addField(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt index 114a596964..be57586163 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt @@ -24,6 +24,8 @@ internal open class CryptoRoomEntity( var algorithm: String? = null, var shouldEncryptForInvitedMembers: Boolean? = null, var blacklistUnverifiedDevices: Boolean = false, + // Determines whether or not room history should be shared on new member invites + var shouldShareHistory: Boolean = false, // Store the current outbound session for this room, // to avoid re-create and re-share at each startup (if rotation not needed..) // This is specific to megolm but not sure how to model it better From b3bfd05ecbc723244ad1165fbc849a7e514bcdaf Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 27 Apr 2022 12:10:21 +0300 Subject: [PATCH 057/136] - Share only the first chunk of inbound sessions instead of the whole key history - Download keys if the user is unknown (first invite) --- .../sdk/api/session/crypto/CryptoService.kt | 8 ++++- .../internal/crypto/DefaultCryptoService.kt | 31 ++++++++++++++++++- .../database/helper/ChunkEntityHelper.kt | 15 +++++++++ .../membership/DefaultMembershipService.kt | 7 ++++- 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index 99fb41b10d..febf26baf0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationServic import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent +import org.matrix.android.sdk.internal.database.helper.SessionInfoPair interface CryptoService { @@ -178,7 +179,12 @@ interface CryptoService { fun prepareToEncrypt(roomId: String, callback: MatrixCallback) /** - * Share existing inbound sessions with the provided userId devices + * Share all existing inbound sessions to the provided userId devices */ fun sendSharedHistoryKeys(roomId: String, userId: String) + + /** + * Share all inbound sessions of the last chunk messages to the provided userId devices + */ + fun sendSharedHistoryKeysToLastChunk(roomId: String, userId: String, sessionInfoSet: Set?) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 4a44e9e2d0..46457ee494 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -92,6 +92,7 @@ import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor +import org.matrix.android.sdk.internal.database.helper.SessionInfoPair import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.UserId @@ -1337,6 +1338,34 @@ internal class DefaultCryptoService @Inject constructor( } } + override fun sendSharedHistoryKeysToLastChunk(roomId: String, userId: String, sessionInfoSet: Set?) { + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + runCatching { + deviceListManager.downloadKeys(listOf(userId), false) + }.mapCatching { + val userDevices = cryptoStore.getUserDevices(userId) + userDevices?.forEach { + // Lets share the provided inbound sessions for every user device + val deviceId = it.key + sessionInfoSet?.mapNotNull { sessionInfoPair -> + // Get inbound session from sessionId and sessionKey + cryptoStore.getInboundGroupSession(sessionInfoPair.first, sessionInfoPair.second) + }?.filter { inboundGroupSession -> + // Filter only sessions with sharedHistory enabled + inboundGroupSession.sharedHistory + }?.forEach { inboundGroupSession -> + // Share the session to userId with deviceId + val exportedKeys = inboundGroupSession.exportKeys() + val algorithm = exportedKeys?.algorithm + val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) + decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) + Timber.i("## CRYPTO | Sharing inbound session") + } + } + } + } + } + override fun sendSharedHistoryKeys(roomId: String, userId: String) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { val userDevices = cryptoStore.getUserDevices(userId) @@ -1347,7 +1376,7 @@ internal class DefaultCryptoService @Inject constructor( inboundSessions.filter { inboundGroupSession -> inboundGroupSession.sharedHistory }.forEach { inboundGroupSession -> - // Share the session with the to userId with deviceId + // Share the session to userId with deviceId val exportedKeys = inboundGroupSession.exportKeys() val algorithm = exportedKeys?.algorithm val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index 234caec970..73af812271 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -18,7 +18,10 @@ package org.matrix.android.sdk.internal.database.helper import io.realm.Realm import io.realm.kotlin.createObject +import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity @@ -31,6 +34,7 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.find +import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection @@ -180,3 +184,14 @@ internal fun ChunkEntity.isMoreRecentThan(chunkToCheck: ChunkEntity): Boolean { // We don't know, so we assume it's false return false } + +internal fun ChunkEntity.Companion.findLatestSessionInfo(realm: Realm, roomId: String): Set? = + ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.mapNotNull { timelineEvent -> + timelineEvent?.root?.asDomain()?.content?.toModel()?.let { content -> + content.sessionId ?: return@mapNotNull null + content.senderKey ?: return@mapNotNull null + Pair(content.sessionId, content.senderKey) + } + }?.toSet() + +internal typealias SessionInfoPair = Pair diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 2b3fa10c89..b6dbaa7be8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -29,7 +29,9 @@ import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.internal.database.helper.findLatestSessionInfo import org.matrix.android.sdk.internal.database.mapper.asDomain +import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType @@ -141,7 +143,10 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { - cryptoService.sendSharedHistoryKeys(roomId, userId) + val sessionInfoSet = Realm.getInstance(monarchy.realmConfiguration).use { + ChunkEntity.findLatestSessionInfo(it, roomId) + } + cryptoService.sendSharedHistoryKeysToLastChunk(roomId, userId, sessionInfoSet) val params = InviteTask.Params(roomId, userId, reason) inviteTask.execute(params) } From dd3928f075c144304cf4c761ad7823e069e580d6 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 28 Apr 2022 14:16:48 +0300 Subject: [PATCH 058/136] Remove sendSharedHistoryKeys while we will only share latest messages --- .../matrix/android/sdk/api/session/crypto/CryptoService.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index febf26baf0..95db92c9b4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -178,11 +178,6 @@ interface CryptoService { */ fun prepareToEncrypt(roomId: String, callback: MatrixCallback) - /** - * Share all existing inbound sessions to the provided userId devices - */ - fun sendSharedHistoryKeys(roomId: String, userId: String) - /** * Share all inbound sessions of the last chunk messages to the provided userId devices */ From 28a3ae264cc31d676257e7215dc858f477318b9b Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 28 Apr 2022 14:19:27 +0300 Subject: [PATCH 059/136] Remove sharedHistory from OlmInboundGroupSessionWrapper2 while there are migration issues, and use only the equivalent DB entity value --- .../internal/crypto/DefaultCryptoService.kt | 33 ++++--------------- .../sdk/internal/crypto/MegolmSessionData.kt | 10 +++++- .../model/OlmInboundGroupSessionWrapper2.kt | 11 +++---- .../internal/crypto/store/IMXCryptoStore.kt | 12 ++++++- .../crypto/store/db/RealmCryptoStore.kt | 12 ++++++- 5 files changed, 42 insertions(+), 36 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 46457ee494..59acf1b8db 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -1349,13 +1349,14 @@ internal class DefaultCryptoService @Inject constructor( val deviceId = it.key sessionInfoSet?.mapNotNull { sessionInfoPair -> // Get inbound session from sessionId and sessionKey - cryptoStore.getInboundGroupSession(sessionInfoPair.first, sessionInfoPair.second) - }?.filter { inboundGroupSession -> - // Filter only sessions with sharedHistory enabled - inboundGroupSession.sharedHistory + cryptoStore.getInboundGroupSession( + sessionId = sessionInfoPair.first, + senderKey = sessionInfoPair.second, + sharedHistory = true + ) }?.forEach { inboundGroupSession -> - // Share the session to userId with deviceId - val exportedKeys = inboundGroupSession.exportKeys() + // Share the sharable session to userId with deviceId + val exportedKeys = inboundGroupSession.exportKeys(sharedHistory = true) val algorithm = exportedKeys?.algorithm val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) @@ -1366,26 +1367,6 @@ internal class DefaultCryptoService @Inject constructor( } } - override fun sendSharedHistoryKeys(roomId: String, userId: String) { - cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { - val userDevices = cryptoStore.getUserDevices(userId) - userDevices?.forEach { - // Lets share our existing inbound sessions for every user device - val deviceId = it.key - val inboundSessions = cryptoStore.getInboundGroupSessions(roomId) - inboundSessions.filter { inboundGroupSession -> - inboundGroupSession.sharedHistory - }.forEach { inboundGroupSession -> - // Share the session to userId with deviceId - val exportedKeys = inboundGroupSession.exportKeys() - val algorithm = exportedKeys?.algorithm - val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) - decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) - Timber.i("## CRYPTO | Sharing inbound session") - } - } - } - } /* ========================================================================================== * For test only * ========================================================================================== */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt index f6bc9a9148..43b88c0b42 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt @@ -69,5 +69,13 @@ internal data class MegolmSessionData( * Devices which forwarded this session to us (normally empty). */ @Json(name = "forwarding_curve25519_key_chain") - val forwardingCurve25519KeyChain: List? = null + val forwardingCurve25519KeyChain: List? = null, + + /** + * Flag that indicates whether or not the current inboundSession will be shared to + * invited users to decrypt past messages + */ + // When this feature lands in spec name = shared_history should be used + @Json(name = "org.matrix.msc3061.shared_history") + val sharedHistory: Boolean = false, ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt index 570532ae8b..b3898e001b 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt @@ -43,10 +43,6 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { // Devices which forwarded this session to us (normally empty). var forwardingCurve25519KeyChain: List? = ArrayList() - // Flag that indicates whether or not the current inboundSession will be shared to - // invited users to decrypt past messages - var sharedHistory: Boolean = false - /** * @return the first known message index */ @@ -110,10 +106,10 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { /** * Export the inbound group session keys. * @param index the index to export. If null, the first known index will be used - * + * @param sharedHistory the flag that indicates whether or not the session can be shared * @return the inbound group session as MegolmSessionData if the operation succeeds */ - fun exportKeys(index: Long? = null): MegolmSessionData? { + fun exportKeys(sharedHistory: Boolean = false, index: Long? = null): MegolmSessionData? { return try { if (null == forwardingCurve25519KeyChain) { forwardingCurve25519KeyChain = ArrayList() @@ -135,7 +131,8 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { roomId = roomId, sessionId = safeOlmInboundGroupSession.sessionIdentifier(), sessionKey = safeOlmInboundGroupSession.export(wantedIndex), - algorithm = MXCRYPTO_ALGORITHM_MEGOLM + algorithm = MXCRYPTO_ALGORITHM_MEGOLM, + sharedHistory = sharedHistory ) } catch (e: Exception) { Timber.e(e, "## export() : senderKey $senderKey failed") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index ceb91aa8ce..65e360a4f3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -321,7 +321,17 @@ internal interface IMXCryptoStore { fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2? /** - * Get the current outbound group session for this encrypted room. + * Retrieve an inbound group session, filtering shared history. + * + * @param sessionId the session identifier. + * @param senderKey the base64-encoded curve25519 key of the sender. + * @param sharedHistory filter inbound session with respect to shared history field + * @return an inbound group session. + */ + fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): OlmInboundGroupSessionWrapper2? + + /** + * Get the current outbound group session for this encrypted room */ fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 7cc2f75285..3034d432c2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -759,7 +759,6 @@ internal class RealmCryptoStore @Inject constructor( val shouldShareHistory = session.roomId?.let { roomId -> CryptoRoomEntity.getById(realm, roomId)?.shouldShareHistory } ?: false - session.sharedHistory = shouldShareHistory val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, session.senderKey) val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { @@ -788,6 +787,17 @@ internal class RealmCryptoStore @Inject constructor( } } + override fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): OlmInboundGroupSessionWrapper2? { + val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) + return doWithRealm(realmConfiguration) { + it.where() + .equalTo(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, sharedHistory) + .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) + .findFirst() + ?.getInboundGroupSession() + } + } + override fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper? { return doWithRealm(realmConfiguration) { realm -> realm.where() From d6358dcb169469a68ed253ee3847f735fc6ca05b Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Thu, 28 Apr 2022 17:15:46 +0300 Subject: [PATCH 060/136] Prevent injecting a forged encrypted message and using session_id/sender_key of another room. --- .../matrix/android/sdk/internal/crypto/DefaultCryptoService.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 59acf1b8db..202cc2273c 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -1354,6 +1354,9 @@ internal class DefaultCryptoService @Inject constructor( senderKey = sessionInfoPair.second, sharedHistory = true ) + }?.filter { inboundGroupSession -> + // Prevent injecting a forged encrypted message and using session_id/sender_key of another room. + inboundGroupSession.roomId == roomId }?.forEach { inboundGroupSession -> // Share the sharable session to userId with deviceId val exportedKeys = inboundGroupSession.exportKeys(sharedHistory = true) From 497f7cf04425b8bb71145fa0699a8511e3ed32be Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 29 Apr 2022 13:44:26 +0300 Subject: [PATCH 061/136] Rotate our session when there is a room history visibility change since the last outboundSession --- .../crypto/algorithms/megolm/MXMegolmEncryption.kt | 2 ++ .../sdk/internal/crypto/store/IMXCryptoStore.kt | 9 ++++++++- .../sdk/internal/crypto/store/db/RealmCryptoStore.kt | 10 ++++++++++ .../store/db/model/OutboundGroupSessionInfoEntity.kt | 3 ++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 7bfbae6edf..975530f5c5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -172,6 +172,8 @@ internal class MXMegolmEncryption( if (session == null || // Need to make a brand new session? session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) || + // Is there a room history visibility change since the last outboundSession + cryptoStore.needsRotationDueToVisibilityChange(roomId) || // Determine if we have shared with anyone we shouldn't have session.sharedWithTooManyDevices(devicesInRoom)) { Timber.tag(loggerTag.value).d("roomId:$roomId Starting new megolm session because we need to rotate.") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 65e360a4f3..42ff4e11ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -341,7 +341,14 @@ internal interface IMXCryptoStore { fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?) /** - * Remove an inbound group session. + * Returns true if there is a room history visibility change since the latest outbound + * session. Specifically when the room's history visibility setting changes to + * world_readable or shared from invited or joined, or changes to invited or joined from world_readable or shared + */ + fun needsRotationDueToVisibilityChange(roomId: String): Boolean + + /** + * Remove an inbound group session * * @param sessionId the session identifier. * @param senderKey the base64-encoded curve25519 key of the sender. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 3034d432c2..e226aa2e8e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -826,6 +826,8 @@ internal class RealmCryptoStore @Inject constructor( if (outboundGroupSession != null) { val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply { creationTime = clock.epochMillis() + // Store the room history visibility on the outbound session creation + shouldShareHistory = entity.shouldShareHistory putOutboundGroupSession(outboundGroupSession) } entity.outboundSessionInfo = info @@ -834,6 +836,14 @@ internal class RealmCryptoStore @Inject constructor( } } + override fun needsRotationDueToVisibilityChange(roomId: String): Boolean { + return doWithRealm(realmConfiguration) { realm -> + CryptoRoomEntity.getById(realm, roomId)?.let { entity -> + entity.shouldShareHistory != entity.outboundSessionInfo?.shouldShareHistory + } + } ?: false + } + /** * Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2, * so there is no need to use or update `inboundGroupSessionToRelease` for native memory management. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutboundGroupSessionInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutboundGroupSessionInfoEntity.kt index d50db78415..2ebd550201 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutboundGroupSessionInfoEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutboundGroupSessionInfoEntity.kt @@ -24,7 +24,8 @@ import timber.log.Timber internal open class OutboundGroupSessionInfoEntity( var serializedOutboundSessionData: String? = null, - var creationTime: Long? = null + var creationTime: Long? = null, + var shouldShareHistory: Boolean = false ) : RealmObject() { fun getOutboundGroupSession(): OlmOutboundGroupSession? { From 395d48f946246b7297c39782fe91f5a6904e2fcb Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 29 Apr 2022 14:02:26 +0300 Subject: [PATCH 062/136] Refactor code structure and improve naming --- .../sdk/api/session/crypto/CryptoService.kt | 4 ++-- .../internal/crypto/DefaultCryptoService.kt | 13 ++++++----- .../crypto/algorithms/IMXDecrypting.kt | 2 +- .../sdk/internal/crypto/model/SessionInfo.kt | 22 +++++++++++++++++++ .../database/helper/ChunkEntityHelper.kt | 11 +++++----- .../membership/DefaultMembershipService.kt | 6 +++-- 6 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index 95db92c9b4..b84c5e2a80 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationServic import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent -import org.matrix.android.sdk.internal.database.helper.SessionInfoPair +import org.matrix.android.sdk.internal.crypto.model.SessionInfo interface CryptoService { @@ -181,5 +181,5 @@ interface CryptoService { /** * Share all inbound sessions of the last chunk messages to the provided userId devices */ - fun sendSharedHistoryKeysToLastChunk(roomId: String, userId: String, sessionInfoSet: Set?) + fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 202cc2273c..044f3e86f3 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -82,6 +82,7 @@ import org.matrix.android.sdk.internal.crypto.algorithms.olm.MXOlmEncryptionFact import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService import org.matrix.android.sdk.internal.crypto.model.MXKey.Companion.KEY_SIGNED_CURVE_25519_TYPE +import org.matrix.android.sdk.internal.crypto.model.SessionInfo import org.matrix.android.sdk.internal.crypto.model.toRest import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -968,7 +969,7 @@ internal class DefaultCryptoService @Inject constructor( eventContent?.historyVisibility?.let { cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED) cryptoStore.setShouldShareHistory(roomId, it.shouldShareHistory()) - } + } ?: cryptoStore.setShouldShareHistory(roomId, false) } /** @@ -1338,7 +1339,7 @@ internal class DefaultCryptoService @Inject constructor( } } - override fun sendSharedHistoryKeysToLastChunk(roomId: String, userId: String, sessionInfoSet: Set?) { + override fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { runCatching { deviceListManager.downloadKeys(listOf(userId), false) @@ -1347,11 +1348,11 @@ internal class DefaultCryptoService @Inject constructor( userDevices?.forEach { // Lets share the provided inbound sessions for every user device val deviceId = it.key - sessionInfoSet?.mapNotNull { sessionInfoPair -> + sessionInfoSet?.mapNotNull { sessionInfo -> // Get inbound session from sessionId and sessionKey cryptoStore.getInboundGroupSession( - sessionId = sessionInfoPair.first, - senderKey = sessionInfoPair.second, + sessionId = sessionInfo.sessionId, + senderKey = sessionInfo.senderKey, sharedHistory = true ) }?.filter { inboundGroupSession -> @@ -1362,7 +1363,7 @@ internal class DefaultCryptoService @Inject constructor( val exportedKeys = inboundGroupSession.exportKeys(sharedHistory = true) val algorithm = exportedKeys?.algorithm val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) - decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) + decryptor?.shareForwardKeysWithDevice(exportedKeys, deviceId, userId) Timber.i("## CRYPTO | Sharing inbound session") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt index ce445d3ce6..7edae70fd8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt @@ -45,5 +45,5 @@ internal interface IMXDecrypting { */ fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {} - fun shareKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) {} + fun shareForwardKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) {} } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt new file mode 100644 index 0000000000..45f0c519d1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt @@ -0,0 +1,22 @@ +/* + * 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 org.matrix.android.sdk.internal.crypto.model + +data class SessionInfo( + val sessionId: String, + val senderKey: String +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index 73af812271..221abe0df5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -21,6 +21,7 @@ import io.realm.kotlin.createObject import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.internal.crypto.model.SessionInfo import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields @@ -185,13 +186,11 @@ internal fun ChunkEntity.isMoreRecentThan(chunkToCheck: ChunkEntity): Boolean { return false } -internal fun ChunkEntity.Companion.findLatestSessionInfo(realm: Realm, roomId: String): Set? = +internal fun ChunkEntity.Companion.findLatestSessionInfo(realm: Realm, roomId: String): Set? = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.mapNotNull { timelineEvent -> timelineEvent?.root?.asDomain()?.content?.toModel()?.let { content -> - content.sessionId ?: return@mapNotNull null - content.senderKey ?: return@mapNotNull null - Pair(content.sessionId, content.senderKey) + content.sessionId ?: return@mapNotNull null + content.senderKey ?: return@mapNotNull null + SessionInfo(content.sessionId, content.senderKey) } }?.toSet() - -internal typealias SessionInfoPair = Pair diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index b6dbaa7be8..0f82872f23 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.membership import androidx.lifecycle.LiveData +import com.otaliastudios.opengl.core.use import com.zhuinden.monarchy.Monarchy import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -44,6 +45,7 @@ import org.matrix.android.sdk.internal.session.room.membership.admin.MembershipA import org.matrix.android.sdk.internal.session.room.membership.joining.InviteTask import org.matrix.android.sdk.internal.session.room.membership.threepid.InviteThreePidTask import org.matrix.android.sdk.internal.util.fetchCopied +import timber.log.Timber internal class DefaultMembershipService @AssistedInject constructor( @Assisted private val roomId: String, @@ -143,10 +145,10 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { - val sessionInfoSet = Realm.getInstance(monarchy.realmConfiguration).use { + val sessionInfo = Realm.getInstance(monarchy.realmConfiguration).use { ChunkEntity.findLatestSessionInfo(it, roomId) } - cryptoService.sendSharedHistoryKeysToLastChunk(roomId, userId, sessionInfoSet) + cryptoService.sendSharedHistoryKeys(roomId, userId, sessionInfo) val params = InviteTask.Params(roomId, userId, reason) inviteTask.execute(params) } From 243463adbc0504cd0093949fb87347274dac892b Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 29 Apr 2022 14:19:22 +0300 Subject: [PATCH 063/136] Add logs --- .../android/sdk/internal/crypto/DefaultCryptoService.kt | 4 +++- .../internal/crypto/algorithms/megolm/MXMegolmEncryption.kt | 6 +++++- .../session/room/membership/DefaultMembershipService.kt | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 044f3e86f3..51e21a181e 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -1357,7 +1357,9 @@ internal class DefaultCryptoService @Inject constructor( ) }?.filter { inboundGroupSession -> // Prevent injecting a forged encrypted message and using session_id/sender_key of another room. - inboundGroupSession.roomId == roomId + (inboundGroupSession.roomId == roomId).also { + Timber.tag(loggerTag.value).d("Forged encrypted message detected for roomId:$roomId") + } }?.forEach { inboundGroupSession -> // Share the sharable session to userId with deviceId val exportedKeys = inboundGroupSession.exportKeys(sharedHistory = true) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 975530f5c5..740676cd59 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -173,7 +173,11 @@ internal class MXMegolmEncryption( // Need to make a brand new session? session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) || // Is there a room history visibility change since the last outboundSession - cryptoStore.needsRotationDueToVisibilityChange(roomId) || + cryptoStore.needsRotationDueToVisibilityChange(roomId).also { + if (it) { + Timber.tag(loggerTag.value).d("roomId:$roomId Room history visibility change detected since the last outbound session") + } + } || // Determine if we have shared with anyone we shouldn't have session.sharedWithTooManyDevices(devicesInRoom)) { Timber.tag(loggerTag.value).d("roomId:$roomId Starting new megolm session because we need to rotate.") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 0f82872f23..fdda38777a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -45,7 +45,6 @@ import org.matrix.android.sdk.internal.session.room.membership.admin.MembershipA import org.matrix.android.sdk.internal.session.room.membership.joining.InviteTask import org.matrix.android.sdk.internal.session.room.membership.threepid.InviteThreePidTask import org.matrix.android.sdk.internal.util.fetchCopied -import timber.log.Timber internal class DefaultMembershipService @AssistedInject constructor( @Assisted private val roomId: String, From 45c80de333ffed7d861eb38a1952f9653fa54d27 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 29 Apr 2022 15:15:12 +0300 Subject: [PATCH 064/136] Add changelog --- changelog.d/5853.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5853.feature diff --git a/changelog.d/5853.feature b/changelog.d/5853.feature new file mode 100644 index 0000000000..2a399e76aa --- /dev/null +++ b/changelog.d/5853.feature @@ -0,0 +1 @@ +Improve user experience when he is first invited to a room. Users will be able to decrypt and view previous messages From 96f0d527536bcd14c79d2b872554c8fefeb9438d Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 3 May 2022 12:01:30 +0300 Subject: [PATCH 065/136] Update copyright --- .../org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt index 45f0c519d1..b3a2ba4dfe 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/SessionInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 New Vector Ltd + * 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. From 28dd507a7477050ab5d7989e0e959fbaaad93884 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 3 May 2022 14:09:19 +0300 Subject: [PATCH 066/136] Add crypto shared history sanity test --- .../android/sdk/common/CryptoTestHelper.kt | 12 +- .../crypto/E2eeShareKeysSanityTest.kt | 229 ++++++++++++++++++ 2 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index 5fd86d4fdb..3b0ea70f92 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -53,6 +53,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.room.Room import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.message.MessageContent @@ -76,11 +77,14 @@ class CryptoTestHelper(val testHelper: CommonTestHelper) { /** * @return alice session */ - fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true): CryptoTestData { + fun doE2ETestWithAliceInARoom(encryptedRoom: Boolean = true, roomHistoryVisibility: RoomHistoryVisibility? = null): CryptoTestData { val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) val roomId = testHelper.runBlockingTest { - aliceSession.roomService().createRoom(CreateRoomParams().apply { name = "MyRoom" }) + aliceSession.createRoom(CreateRoomParams().apply { + historyVisibility = roomHistoryVisibility + name = "MyRoom" + }) } if (encryptedRoom) { testHelper.waitWithLatch { latch -> @@ -104,8 +108,8 @@ class CryptoTestHelper(val testHelper: CommonTestHelper) { /** * @return alice and bob sessions */ - fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true): CryptoTestData { - val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom) + fun doE2ETestWithAliceAndBobInARoom(encryptedRoom: Boolean = true, roomHistoryVisibility: RoomHistoryVisibility? = null): CryptoTestData { + val cryptoTestData = doE2ETestWithAliceInARoom(encryptedRoom, roomHistoryVisibility) val aliceSession = cryptoTestData.firstSession val aliceRoomId = cryptoTestData.roomId diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt new file mode 100644 index 0000000000..83dd258bbb --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt @@ -0,0 +1,229 @@ +/* + * 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 android.util.Log +import androidx.test.filters.LargeTest +import org.amshove.kluent.internal.assertEquals +import org.junit.Assert +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.Session +import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +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.timeline.TimelineSettings +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.common.SessionTestParams +import org.matrix.android.sdk.common.TestConstants + +@RunWith(JUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +@LargeTest +class E2eeShareKeysSanityTest : InstrumentedTest { + + private val testHelper = CommonTestHelper(context()) + private val cryptoTestHelper = CryptoTestHelper(testHelper) + + @Test + fun testShareMessagesHistoryWithRoomWorldReadable() { + testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.WORLD_READABLE) + } + + @Test + fun testShareMessagesHistoryWithRoomShared() { + testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.SHARED) + } + + @Test + fun testShareMessagesHistoryWithRoomJoined() { + testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.JOINED) + } + + @Test + fun testShareMessagesHistoryWithRoomInvited() { + testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.INVITED) + } + + /** + * Simple test that creates an e2ee room. + * In this test we create a room and test that new members + * can decrypt history when the room visibility is + * RoomHistoryVisibility.SHARED or RoomHistoryVisibility.WORLD_READABLE + */ + private fun testSharedHistoryWithRoomVisibility(roomHistoryVisibility: RoomHistoryVisibility? = null) { + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true, roomHistoryVisibility) + + val e2eRoomID = cryptoTestData.roomId + + // Alice + val aliceSession = cryptoTestData.firstSession + val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!! + + // Bob + val bobSession = cryptoTestData.secondSession + val bobRoomPOV = bobSession!!.getRoom(e2eRoomID)!! + + assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) + Log.v("#E2E TEST", "Alice and Bob are in roomId: $e2eRoomID") + + val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, "Hello Bob, I am Alice!") + Assert.assertTrue("Message should be sent", aliceMessageId != null) + Log.v("#E2E TEST", "Alice sent message to roomId: $e2eRoomID") + + // Bob should be able to decrypt the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = bobSession.getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE).also { + if (it) { + Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } + } + } + + // Create a new user + val arisSession = testHelper.createAccount("aris", SessionTestParams(true)) + Log.v("#E2E TEST", "Aris user created") + + // Alice invites new user to the room + testHelper.runBlockingTest { + Log.v("#E2E TEST", "Alice invites ${arisSession.myUserId}") + aliceRoomPOV.invite(arisSession.myUserId) + } + + waitForAndAcceptInviteInRoom(arisSession, e2eRoomID) + + ensureMembersHaveJoined(aliceSession, arrayListOf(arisSession), e2eRoomID) + Log.v("#E2E TEST", "Aris has joined roomId: $e2eRoomID") + + when (roomHistoryVisibility) { + RoomHistoryVisibility.WORLD_READABLE, + RoomHistoryVisibility.SHARED, + null + -> { + // Aris should be able to decrypt the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = arisSession.getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE + ).also { + if (it) { + Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } + } + } + } + RoomHistoryVisibility.INVITED, + RoomHistoryVisibility.JOINED -> { + // Aris should not even be able to get the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = arisSession.getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + timelineEvent == null + } + } + } + } + + testHelper.signOutAndClose(arisSession) + cryptoTestData.cleanUp(testHelper) + } + + private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? { + aliceRoomPOV.sendTextMessage(text) + var sentEventId: String? = null + testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch -> + val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60)) + timeline.start() + + testHelper.retryPeriodicallyWithLatch(latch) { + val decryptedMsg = timeline.getSnapshot() + .filter { it.root.getClearType() == EventType.MESSAGE } + .also { list -> + val message = list.joinToString(",", "[", "]") { "${it.root.type}|${it.root.sendState}" } + Log.v("#E2E TEST", "Timeline snapshot is $message") + } + .filter { it.root.sendState == SendState.SYNCED } + .firstOrNull { it.root.getClearContent().toModel()?.body?.startsWith(text) == true } + sentEventId = decryptedMsg?.eventId + decryptedMsg != null + } + + timeline.dispose() + } + return sentEventId + } + + private fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List, e2eRoomID: String) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + otherAccounts.map { + aliceSession.getRoomMember(it.myUserId, e2eRoomID)?.membership + }.all { + it == Membership.JOIN + } + } + } + } + + private fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val roomSummary = otherSession.getRoomSummary(e2eRoomID) + (roomSummary != null && roomSummary.membership == Membership.INVITE).also { + if (it) { + Log.v("#E2E TEST", "${otherSession.myUserId} can see the invite from alice") + } + } + } + } + + testHelper.runBlockingTest(60_000) { + Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID") + try { + otherSession.joinRoom(e2eRoomID) + } catch (ex: JoinRoomFailure.JoinedWithTimeout) { + // it's ok we will wait after + } + } + + Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...") + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { + val roomSummary = otherSession.getRoomSummary(e2eRoomID) + roomSummary != null && roomSummary.membership == Membership.JOIN + } + } + } +} From 3a5b737639cc93be02fc705a9c3b05153c809dfa Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 3 May 2022 14:10:14 +0300 Subject: [PATCH 067/136] Fix existing E2eeSanityTests to support changes for key history sharing --- .../matrix/android/sdk/internal/crypto/E2eeSanityTests.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt index 5a61eee7fe..d8c3c021cf 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt @@ -166,7 +166,7 @@ class E2eeSanityTests : InstrumentedTest { delay(3_000) } - // check that messages are encrypted (uisi) + // Due to the new shared keys implementation, invited user should be able to decrypt messages newAccount.forEach { otherSession -> testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { @@ -174,8 +174,7 @@ class E2eeSanityTests : InstrumentedTest { Log.v("#E2E TEST", "Event seen by new user ${it?.root?.getClearType()}|${it?.root?.mCryptoError}") } timelineEvent != null && - timelineEvent.root.getClearType() == EventType.ENCRYPTED && - timelineEvent.root.mCryptoError == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID + timelineEvent.root.getClearType() == EventType.MESSAGE } } } From 2e88998b058a0f4468903c6e79c7f12e0c429303 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 3 May 2022 18:08:03 +0300 Subject: [PATCH 068/136] Add integration tests for shared keys rotation on room history visibility change --- .../crypto/E2eeShareKeysHistoryTest.kt | 375 ++++++++++++++++++ .../crypto/E2eeShareKeysSanityTest.kt | 229 ----------- 2 files changed, 375 insertions(+), 229 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt delete mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt new file mode 100644 index 0000000000..fa5c9c8b23 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -0,0 +1,375 @@ +/* + * 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 android.util.Log +import androidx.test.filters.LargeTest +import org.amshove.kluent.internal.assertEquals +import org.junit.Assert +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.Session +import org.matrix.android.sdk.api.session.events.model.EventType +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.getRoom +import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent +import org.matrix.android.sdk.api.session.room.model.message.MessageContent +import org.matrix.android.sdk.api.session.room.model.shouldShareHistory +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.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.common.SessionTestParams +import org.matrix.android.sdk.common.TestConstants + +@RunWith(JUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +@LargeTest +class E2eeShareKeysHistoryTest : InstrumentedTest { + + @Test + fun testShareMessagesHistoryWithRoomWorldReadable() { + testShareHistoryWithRoomVisibility(RoomHistoryVisibility.WORLD_READABLE) + } + + @Test + fun testShareMessagesHistoryWithRoomShared() { + testShareHistoryWithRoomVisibility(RoomHistoryVisibility.SHARED) + } + + @Test + fun testShareMessagesHistoryWithRoomJoined() { + testShareHistoryWithRoomVisibility(RoomHistoryVisibility.JOINED) + } + + @Test + fun testShareMessagesHistoryWithRoomInvited() { + testShareHistoryWithRoomVisibility(RoomHistoryVisibility.INVITED) + } + + /** + * In this test we create a room and test that new members + * can decrypt history when the room visibility is + * RoomHistoryVisibility.SHARED or RoomHistoryVisibility.WORLD_READABLE. + * We should not be able to view messages/decrypt otherwise + */ + private fun testShareHistoryWithRoomVisibility(roomHistoryVisibility: RoomHistoryVisibility? = null) { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true, roomHistoryVisibility) + + 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) + Log.v("#E2E TEST", "Alice and Bob are in roomId: $e2eRoomID") + + val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, "Hello Bob, I am Alice!", testHelper) + Assert.assertTrue("Message should be sent", aliceMessageId != null) + Log.v("#E2E TEST", "Alice sent message to roomId: $e2eRoomID") + + // Bob should be able to decrypt the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE).also { + if (it) { + Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } + } + } + + // Create a new user + val arisSession = testHelper.createAccount("aris", SessionTestParams(true)) + Log.v("#E2E TEST", "Aris user created") + + // Alice invites new user to the room + testHelper.runBlockingTest { + Log.v("#E2E TEST", "Alice invites ${arisSession.myUserId}") + aliceRoomPOV.invite(arisSession.myUserId) + } + + waitForAndAcceptInviteInRoom(arisSession, e2eRoomID, testHelper) + + ensureMembersHaveJoined(aliceSession, arrayListOf(arisSession), e2eRoomID, testHelper) + Log.v("#E2E TEST", "Aris has joined roomId: $e2eRoomID") + + when (roomHistoryVisibility) { + RoomHistoryVisibility.WORLD_READABLE, + RoomHistoryVisibility.SHARED, + null + -> { + // Aris should be able to decrypt the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE + ).also { + if (it) { + Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } + } + } + } + RoomHistoryVisibility.INVITED, + RoomHistoryVisibility.JOINED -> { + // Aris should not even be able to get the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + timelineEvent == null + } + } + } + } + + testHelper.signOutAndClose(arisSession) + cryptoTestData.cleanUp(testHelper) + } + + @Test + fun testNeedsRotationFromWorldReadableToShared() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("shared")) + } + + @Test + fun testNeedsRotationFromWorldReadableToInvited() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("invited")) + } + + @Test + fun testNeedsRotationFromWorldReadableToJoined() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("joined")) + } + + @Test + fun testNeedsRotationFromSharedToWorldReadable() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.SHARED, RoomHistoryVisibilityContent("world_readable")) + } + + @Test + fun testNeedsRotationFromSharedToInvited() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.SHARED, RoomHistoryVisibilityContent("invited")) + } + + @Test + fun testNeedsRotationFromSharedToJoined() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.SHARED, RoomHistoryVisibilityContent("joined")) + } + + @Test + fun testNeedsRotationFromInvitedToShared() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("shared")) + } + + @Test + fun testNeedsRotationFromInvitedToWorldReadable() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("world_readable")) + } + + @Test + fun testNeedsRotationFromInvitedToJoined() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("joined")) + } + + @Test + fun testNeedsRotationFromJoinedToShared() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("shared")) + } + + @Test + fun testNeedsRotationFromJoinedToInvited() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("invited")) + } + + @Test + fun testNeedsRotationFromJoinedToWorldReadable() { + testRotationDueToVisibilityChange(RoomHistoryVisibility.WORLD_READABLE, RoomHistoryVisibilityContent("world_readable")) + } + + /** + * In this test we will test that a rotation is needed when + * When the room's history visibility setting changes to world_readable or shared + * from invited or joined, or changes to invited or joined from world_readable or shared, + * senders that support this flag must rotate their megolm sessions. + */ + private fun testRotationDueToVisibilityChange( + initRoomHistoryVisibility: RoomHistoryVisibility, + nextRoomHistoryVisibility: RoomHistoryVisibilityContent + ) { + val testHelper = CommonTestHelper(context()) + val cryptoTestHelper = CryptoTestHelper(testHelper) + + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true, initRoomHistoryVisibility) + val e2eRoomID = cryptoTestData.roomId + + // Alice + val aliceSession = cryptoTestData.firstSession + val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!! + val aliceCryptoStore = (aliceSession.cryptoService() as DefaultCryptoService).cryptoStoreForTesting + + // Bob + val bobSession = cryptoTestData.secondSession + val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!! + + assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) + Log.v("#E2E TEST ROTATION", "Alice and Bob are in roomId: $e2eRoomID") + + val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, "Hello Bob, I am Alice!", testHelper) + Assert.assertTrue("Message should be sent", aliceMessageId != null) + Log.v("#E2E TEST ROTATION", "Alice sent message to roomId: $e2eRoomID") + + // Bob should be able to decrypt the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE).also { + if (it) { + Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } + } + } + + // Rotation has already been done so we do not need to rotate again + assertEquals(aliceCryptoStore.needsRotationDueToVisibilityChange(e2eRoomID), false) + Log.v("#E2E TEST ROTATION", "No rotation needed yet") + + // Let's change the room history visibility + testHelper.waitWithLatch { + aliceRoomPOV.sendStateEvent( + eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY, + stateKey = "", + body = RoomHistoryVisibilityContent(_historyVisibility = nextRoomHistoryVisibility._historyVisibility).toContent() + ) + it.countDown() + } + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val roomVisibility = aliceSession.getRoom(e2eRoomID)!! + .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY) + ?.content + ?.toModel() + Log.v("#E2E TEST ROTATION", "Room visibility changed from: ${initRoomHistoryVisibility.name} to: ${roomVisibility?.historyVisibility?.name}") + roomVisibility?.historyVisibility == nextRoomHistoryVisibility.historyVisibility + } + } + + when { + initRoomHistoryVisibility.shouldShareHistory() == nextRoomHistoryVisibility.historyVisibility?.shouldShareHistory() -> { + assertEquals(aliceCryptoStore.needsRotationDueToVisibilityChange(e2eRoomID), false) + Log.v("#E2E TEST ROTATION", "Rotation is not needed") + } + initRoomHistoryVisibility.shouldShareHistory() != nextRoomHistoryVisibility.historyVisibility!!.shouldShareHistory() -> { + assertEquals(aliceCryptoStore.needsRotationDueToVisibilityChange(e2eRoomID), true) + Log.v("#E2E TEST ROTATION", "Rotation is needed!") + } + } + + cryptoTestData.cleanUp(testHelper) + } + + private fun sendMessageInRoom(aliceRoomPOV: Room, text: String, testHelper: CommonTestHelper): String? { + aliceRoomPOV.sendTextMessage(text) + var sentEventId: String? = null + testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch -> + val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60)) + timeline.start() + testHelper.retryPeriodicallyWithLatch(latch) { + val decryptedMsg = timeline.getSnapshot() + .filter { it.root.getClearType() == EventType.MESSAGE } + .also { list -> + val message = list.joinToString(",", "[", "]") { "${it.root.type}|${it.root.sendState}" } + Log.v("#E2E TEST", "Timeline snapshot is $message") + } + .filter { it.root.sendState == SendState.SYNCED } + .firstOrNull { it.root.getClearContent().toModel()?.body?.startsWith(text) == true } + sentEventId = decryptedMsg?.eventId + decryptedMsg != null + } + + timeline.dispose() + } + return sentEventId + } + + private fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List, e2eRoomID: String, testHelper: CommonTestHelper) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + otherAccounts.map { + aliceSession.roomService().getRoomMember(it.myUserId, e2eRoomID)?.membership + }.all { + it == Membership.JOIN + } + } + } + } + + private fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String, testHelper: CommonTestHelper) { + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID) + (roomSummary != null && roomSummary.membership == Membership.INVITE).also { + if (it) { + Log.v("#E2E TEST", "${otherSession.myUserId} can see the invite from alice") + } + } + } + } + + testHelper.runBlockingTest(60_000) { + Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID") + try { + otherSession.roomService().joinRoom(e2eRoomID) + } catch (ex: JoinRoomFailure.JoinedWithTimeout) { + // it's ok we will wait after + } + } + + Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...") + testHelper.waitWithLatch { + testHelper.retryPeriodicallyWithLatch(it) { + val roomSummary = otherSession.roomService().getRoomSummary(e2eRoomID) + roomSummary != null && roomSummary.membership == Membership.JOIN + } + } + } +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt deleted file mode 100644 index 83dd258bbb..0000000000 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysSanityTest.kt +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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 android.util.Log -import androidx.test.filters.LargeTest -import org.amshove.kluent.internal.assertEquals -import org.junit.Assert -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.Session -import org.matrix.android.sdk.api.session.events.model.EventType -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.Room -import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure -import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility -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.timeline.TimelineSettings -import org.matrix.android.sdk.common.CommonTestHelper -import org.matrix.android.sdk.common.CryptoTestHelper -import org.matrix.android.sdk.common.SessionTestParams -import org.matrix.android.sdk.common.TestConstants - -@RunWith(JUnit4::class) -@FixMethodOrder(MethodSorters.JVM) -@LargeTest -class E2eeShareKeysSanityTest : InstrumentedTest { - - private val testHelper = CommonTestHelper(context()) - private val cryptoTestHelper = CryptoTestHelper(testHelper) - - @Test - fun testShareMessagesHistoryWithRoomWorldReadable() { - testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.WORLD_READABLE) - } - - @Test - fun testShareMessagesHistoryWithRoomShared() { - testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.SHARED) - } - - @Test - fun testShareMessagesHistoryWithRoomJoined() { - testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.JOINED) - } - - @Test - fun testShareMessagesHistoryWithRoomInvited() { - testSharedHistoryWithRoomVisibility(RoomHistoryVisibility.INVITED) - } - - /** - * Simple test that creates an e2ee room. - * In this test we create a room and test that new members - * can decrypt history when the room visibility is - * RoomHistoryVisibility.SHARED or RoomHistoryVisibility.WORLD_READABLE - */ - private fun testSharedHistoryWithRoomVisibility(roomHistoryVisibility: RoomHistoryVisibility? = null) { - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true, roomHistoryVisibility) - - val e2eRoomID = cryptoTestData.roomId - - // Alice - val aliceSession = cryptoTestData.firstSession - val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!! - - // Bob - val bobSession = cryptoTestData.secondSession - val bobRoomPOV = bobSession!!.getRoom(e2eRoomID)!! - - assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) - Log.v("#E2E TEST", "Alice and Bob are in roomId: $e2eRoomID") - - val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, "Hello Bob, I am Alice!") - Assert.assertTrue("Message should be sent", aliceMessageId != null) - Log.v("#E2E TEST", "Alice sent message to roomId: $e2eRoomID") - - // Bob should be able to decrypt the message - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) - (timelineEvent != null && - timelineEvent.isEncrypted() && - timelineEvent.root.getClearType() == EventType.MESSAGE).also { - if (it) { - Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") - } - } - } - } - - // Create a new user - val arisSession = testHelper.createAccount("aris", SessionTestParams(true)) - Log.v("#E2E TEST", "Aris user created") - - // Alice invites new user to the room - testHelper.runBlockingTest { - Log.v("#E2E TEST", "Alice invites ${arisSession.myUserId}") - aliceRoomPOV.invite(arisSession.myUserId) - } - - waitForAndAcceptInviteInRoom(arisSession, e2eRoomID) - - ensureMembersHaveJoined(aliceSession, arrayListOf(arisSession), e2eRoomID) - Log.v("#E2E TEST", "Aris has joined roomId: $e2eRoomID") - - when (roomHistoryVisibility) { - RoomHistoryVisibility.WORLD_READABLE, - RoomHistoryVisibility.SHARED, - null - -> { - // Aris should be able to decrypt the message - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = arisSession.getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) - (timelineEvent != null && - timelineEvent.isEncrypted() && - timelineEvent.root.getClearType() == EventType.MESSAGE - ).also { - if (it) { - Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") - } - } - } - } - } - RoomHistoryVisibility.INVITED, - RoomHistoryVisibility.JOINED -> { - // Aris should not even be able to get the message - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = arisSession.getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) - timelineEvent == null - } - } - } - } - - testHelper.signOutAndClose(arisSession) - cryptoTestData.cleanUp(testHelper) - } - - private fun sendMessageInRoom(aliceRoomPOV: Room, text: String): String? { - aliceRoomPOV.sendTextMessage(text) - var sentEventId: String? = null - testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch -> - val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60)) - timeline.start() - - testHelper.retryPeriodicallyWithLatch(latch) { - val decryptedMsg = timeline.getSnapshot() - .filter { it.root.getClearType() == EventType.MESSAGE } - .also { list -> - val message = list.joinToString(",", "[", "]") { "${it.root.type}|${it.root.sendState}" } - Log.v("#E2E TEST", "Timeline snapshot is $message") - } - .filter { it.root.sendState == SendState.SYNCED } - .firstOrNull { it.root.getClearContent().toModel()?.body?.startsWith(text) == true } - sentEventId = decryptedMsg?.eventId - decryptedMsg != null - } - - timeline.dispose() - } - return sentEventId - } - - private fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List, e2eRoomID: String) { - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - otherAccounts.map { - aliceSession.getRoomMember(it.myUserId, e2eRoomID)?.membership - }.all { - it == Membership.JOIN - } - } - } - } - - private fun waitForAndAcceptInviteInRoom(otherSession: Session, e2eRoomID: String) { - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val roomSummary = otherSession.getRoomSummary(e2eRoomID) - (roomSummary != null && roomSummary.membership == Membership.INVITE).also { - if (it) { - Log.v("#E2E TEST", "${otherSession.myUserId} can see the invite from alice") - } - } - } - } - - testHelper.runBlockingTest(60_000) { - Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID") - try { - otherSession.joinRoom(e2eRoomID) - } catch (ex: JoinRoomFailure.JoinedWithTimeout) { - // it's ok we will wait after - } - } - - Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...") - testHelper.waitWithLatch { - testHelper.retryPeriodicallyWithLatch(it) { - val roomSummary = otherSession.getRoomSummary(e2eRoomID) - roomSummary != null && roomSummary.membership == Membership.JOIN - } - } - } -} From 93aac8faeaf1a17d15c906753def64a6a65aa900 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 11 May 2022 15:11:34 +0200 Subject: [PATCH 069/136] post rebase fix --- .../internal/crypto/DefaultCryptoService.kt | 1 - .../crypto/IncomingKeyRequestManager.kt | 2 +- .../algorithms/megolm/MXMegolmDecryption.kt | 79 +++++++++---------- .../store/db/RealmCryptoStoreMigration.kt | 3 - 4 files changed, 40 insertions(+), 45 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 51e21a181e..a42719146e 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -93,7 +93,6 @@ import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor -import org.matrix.android.sdk.internal.database.helper.SessionInfoPair import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.UserId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt index 7f36224dae..6960d94918 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt @@ -405,7 +405,7 @@ internal class IncomingKeyRequestManager @Inject constructor( } val export = sessionHolder.mutex.withLock { - sessionHolder.wrapper.exportKeys(chainIndex) + sessionHolder.wrapper.exportKeys(/**TODO*/ false ,chainIndex) } ?: return false.also { Timber.tag(loggerTag.value) .e("shareKeysWithDevice: failed to export group session ${validRequest.sessionId}") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index a9ffe5f008..7b6f80f5bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -29,8 +29,7 @@ import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.MegolmSessionData -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.OutgoingKeyRequestManager import org.matrix.android.sdk.internal.crypto.algorithms.IMXDecrypting import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -309,44 +308,44 @@ internal class MXMegolmDecryption( Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey") newSessionListener?.onNewSession(roomId, senderKey, sessionId) } - override fun shareKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) { - exportedKeys ?: return - cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { - runCatching { deviceListManager.downloadKeys(listOf(userId), false) } - .mapCatching { - val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) - if (deviceInfo == null) { - throw RuntimeException() - } else { - val devicesByUser = mapOf(userId to listOf(deviceInfo)) - val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) - val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) - if (olmSessionResult?.sessionId == null) { - // no session with this device, probably because there - // were no one-time keys. - Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.") - return@mapCatching - } - Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${exportedKeys.sessionId} with device $userId:$deviceId") - val payloadJson = mapOf( - "type" to EventType.FORWARDED_ROOM_KEY, - "content" to exportedKeys - ) - - val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) - val sendToDeviceMap = MXUsersDevicesMap() - sendToDeviceMap.setObject(userId, deviceId, encodedPayload) - Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${exportedKeys.sessionId} to $userId:$deviceId") - val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) - try { - sendToDeviceTask.execute(sendToDeviceParams) - } catch (failure: Throwable) { - Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${exportedKeys.sessionId} to $userId:$deviceId") - } - } - } - } + override fun shareForwardKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) { +// exportedKeys ?: return +// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { +// runCatching { deviceListManager.downloadKeys(listOf(userId), false) } +// .mapCatching { +// val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) +// if (deviceInfo == null) { +// throw RuntimeException() +// } else { +// val devicesByUser = mapOf(userId to listOf(deviceInfo)) +// val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) +// val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) +// if (olmSessionResult?.sessionId == null) { +// // no session with this device, probably because there +// // were no one-time keys. +// Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.") +// return@mapCatching +// } +// Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${exportedKeys.sessionId} with device $userId:$deviceId") +// +// val payloadJson = mapOf( +// "type" to EventType.FORWARDED_ROOM_KEY, +// "content" to exportedKeys +// ) +// +// val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) +// val sendToDeviceMap = MXUsersDevicesMap() +// sendToDeviceMap.setObject(userId, deviceId, encodedPayload) +// Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${exportedKeys.sessionId} to $userId:$deviceId") +// val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) +// try { +// sendToDeviceTask.execute(sendToDeviceParams) +// } catch (failure: Throwable) { +// Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${exportedKeys.sessionId} to $userId:$deviceId") +// } +// } +// } +// } } - } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 35229d205d..4ca9d44f98 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -73,9 +73,6 @@ internal class RealmCryptoStoreMigration @Inject constructor( if (oldVersion < 14) MigrateCryptoTo014(realm).perform() if (oldVersion < 15) MigrateCryptoTo015(realm).perform() if (oldVersion < 16) MigrateCryptoTo016(realm).perform() -<<<<<<< develop if (oldVersion < 17) MigrateCryptoTo017(realm).perform() -======= ->>>>>>> Implement history key sharing functionality with respect to room visibility settings } } From 9b8e45ebfe612dad35d71faf5586233e373fc123 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 12 May 2022 12:28:00 +0200 Subject: [PATCH 070/136] share keys for history take2 --- .../android/sdk/common/CryptoTestHelper.kt | 2 +- .../crypto/E2eeShareKeysHistoryTest.kt | 82 +++++++-- .../sdk/internal/crypto/PreShareKeysTest.kt | 2 +- .../keysbackup/KeysBackupScenarioData.kt | 4 +- .../crypto/keysbackup/KeysBackupTest.kt | 4 +- .../crypto/keysbackup/KeysBackupTestHelper.kt | 2 +- .../sdk/api/session/crypto/CryptoService.kt | 2 +- .../crypto/model/ForwardedRoomKeyContent.kt | 9 +- .../events/model/content/RoomKeyContent.kt | 10 +- .../internal/crypto/DefaultCryptoService.kt | 62 +++---- .../crypto/InboundGroupSessionStore.kt | 18 +- .../crypto/IncomingKeyRequestManager.kt | 2 +- .../sdk/internal/crypto/MXOlmDevice.kt | 166 ++++++++++-------- .../crypto/OutgoingKeyRequestManager.kt | 5 +- .../actions/MegolmSessionDataImporter.kt | 5 +- .../crypto/algorithms/IMXDecrypting.kt | 3 - .../crypto/algorithms/IMXEncrypting.kt | 4 + .../algorithms/megolm/MXMegolmDecryption.kt | 55 +----- .../algorithms/megolm/MXMegolmEncryption.kt | 114 +++++++++--- .../megolm/MXOutboundSessionInfo.kt | 1 + .../keysbackup/DefaultKeysBackupService.kt | 28 ++- .../crypto/model/InboundGroupSessionData.kt | 51 ++++++ .../model/MXInboundMegolmSessionWrapper.kt | 97 ++++++++++ .../model/OlmInboundGroupSessionWrapper2.kt | 2 + .../model/OutboundGroupSessionWrapper.kt | 6 +- .../internal/crypto/store/IMXCryptoStore.kt | 23 +-- .../crypto/store/db/RealmCryptoStore.kt | 106 ++++++----- .../store/db/migration/MigrateCryptoTo017.kt | 56 +++++- .../db/model/OlmInboundGroupSessionEntity.kt | 69 +++++++- .../internal/crypto/tasks/SendEventTask.kt | 7 +- .../membership/DefaultMembershipService.kt | 1 + 31 files changed, 673 insertions(+), 325 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index 3b0ea70f92..f36bfb6210 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -81,7 +81,7 @@ class CryptoTestHelper(val testHelper: CommonTestHelper) { val aliceSession = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams) val roomId = testHelper.runBlockingTest { - aliceSession.createRoom(CreateRoomParams().apply { + aliceSession.roomService().createRoom(CreateRoomParams().apply { historyVisibility = roomHistoryVisibility name = "MyRoom" }) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index fa5c9c8b23..0df7e0eb7d 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.crypto import android.util.Log import androidx.test.filters.LargeTest import org.amshove.kluent.internal.assertEquals +import org.amshove.kluent.internal.assertNotEquals import org.junit.Assert import org.junit.FixMethodOrder import org.junit.Test @@ -101,7 +102,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Bob should be able to decrypt the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) (timelineEvent != null && timelineEvent.isEncrypted() && timelineEvent.root.getClearType() == EventType.MESSAGE).also { @@ -119,7 +120,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Alice invites new user to the room testHelper.runBlockingTest { Log.v("#E2E TEST", "Alice invites ${arisSession.myUserId}") - aliceRoomPOV.invite(arisSession.myUserId) + aliceRoomPOV.membershipService().invite(arisSession.myUserId) } waitForAndAcceptInviteInRoom(arisSession, e2eRoomID, testHelper) @@ -135,7 +136,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Aris should be able to decrypt the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) (timelineEvent != null && timelineEvent.isEncrypted() && timelineEvent.root.getClearType() == EventType.MESSAGE @@ -152,7 +153,9 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Aris should not even be able to get the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID) + ?.timelineService() + ?.getTimelineEvent(aliceMessageId!!) timelineEvent == null } } @@ -242,7 +245,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Alice val aliceSession = cryptoTestData.firstSession val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!! - val aliceCryptoStore = (aliceSession.cryptoService() as DefaultCryptoService).cryptoStoreForTesting +// val aliceCryptoStore = (aliceSession.cryptoService() as DefaultCryptoService).cryptoStoreForTesting // Bob val bobSession = cryptoTestData.secondSession @@ -256,35 +259,62 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { Log.v("#E2E TEST ROTATION", "Alice sent message to roomId: $e2eRoomID") // Bob should be able to decrypt the message + var firstAliceMessageMegolmSessionId: String? = null testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.getTimelineEvent(aliceMessageId!!) + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID) + ?.timelineService() + ?.getTimelineEvent(aliceMessageId!!) (timelineEvent != null && timelineEvent.isEncrypted() && timelineEvent.root.getClearType() == EventType.MESSAGE).also { if (it) { + firstAliceMessageMegolmSessionId = timelineEvent?.root?.content?.get("session_id") as? String Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") } } } } - // Rotation has already been done so we do not need to rotate again - assertEquals(aliceCryptoStore.needsRotationDueToVisibilityChange(e2eRoomID), false) + Assert.assertNotNull("megolm session id can't be null", firstAliceMessageMegolmSessionId) + + var secondAliceMessageSessionId: String? = null + sendMessageInRoom(aliceRoomPOV, "Other msg", testHelper)?.let { secondMessage -> + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID) + ?.timelineService() + ?.getTimelineEvent(secondMessage) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE).also { + if (it) { + secondAliceMessageSessionId = timelineEvent?.root?.content?.get("session_id") as? String + } + } + } + } + } + assertEquals("No rotation needed session should be the same", firstAliceMessageMegolmSessionId, secondAliceMessageSessionId) Log.v("#E2E TEST ROTATION", "No rotation needed yet") // Let's change the room history visibility testHelper.waitWithLatch { - aliceRoomPOV.sendStateEvent( - eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY, - stateKey = "", - body = RoomHistoryVisibilityContent(_historyVisibility = nextRoomHistoryVisibility._historyVisibility).toContent() - ) + aliceRoomPOV.stateService() + .sendStateEvent( + eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY, + stateKey = "", + body = RoomHistoryVisibilityContent( + _historyVisibility = nextRoomHistoryVisibility._historyVisibility + ).toContent() + ) it.countDown() } + testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { val roomVisibility = aliceSession.getRoom(e2eRoomID)!! + .stateService() .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY) ?.content ?.toModel() @@ -293,13 +323,31 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } } + var aliceThirdMessageSessionId: String? = null + sendMessageInRoom(aliceRoomPOV, "Message after visibility change", testHelper)?.let { thirdMessage -> + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID) + ?.timelineService() + ?.getTimelineEvent(thirdMessage) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE).also { + if (it) { + aliceThirdMessageSessionId = timelineEvent?.root?.content?.get("session_id") as? String + } + } + } + } + } + when { initRoomHistoryVisibility.shouldShareHistory() == nextRoomHistoryVisibility.historyVisibility?.shouldShareHistory() -> { - assertEquals(aliceCryptoStore.needsRotationDueToVisibilityChange(e2eRoomID), false) + assertEquals("Session shouldn't have been rotated", secondAliceMessageSessionId, aliceThirdMessageSessionId) Log.v("#E2E TEST ROTATION", "Rotation is not needed") } initRoomHistoryVisibility.shouldShareHistory() != nextRoomHistoryVisibility.historyVisibility!!.shouldShareHistory() -> { - assertEquals(aliceCryptoStore.needsRotationDueToVisibilityChange(e2eRoomID), true) + assertNotEquals("Session should have been rotated", secondAliceMessageSessionId, aliceThirdMessageSessionId) Log.v("#E2E TEST ROTATION", "Rotation is needed!") } } @@ -308,10 +356,10 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } private fun sendMessageInRoom(aliceRoomPOV: Room, text: String, testHelper: CommonTestHelper): String? { - aliceRoomPOV.sendTextMessage(text) + aliceRoomPOV.sendService().sendTextMessage(text) var sentEventId: String? = null testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch -> - val timeline = aliceRoomPOV.createTimeline(null, TimelineSettings(60)) + val timeline = aliceRoomPOV.timelineService().createTimeline(null, TimelineSettings(60)) timeline.start() testHelper.retryPeriodicallyWithLatch(latch) { val decryptedMsg = timeline.getSnapshot() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt index e37ae5be86..e8e7b1d708 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt @@ -72,7 +72,7 @@ class PreShareKeysTest : InstrumentedTest { assertNotNull("Bob should have received and decrypted a room key event from alice", bobInboundForAlice) assertEquals("Wrong room", e2eRoomID, bobInboundForAlice!!.roomId) - val megolmSessionId = bobInboundForAlice.olmInboundGroupSession!!.sessionIdentifier() + val megolmSessionId = bobInboundForAlice.session.sessionIdentifier() assertEquals("Wrong session", aliceOutboundSessionInRoom, megolmSessionId) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt index 45fdb9e1e3..cf201611a0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupScenarioData.kt @@ -19,14 +19,14 @@ package org.matrix.android.sdk.internal.crypto.keysbackup import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestData -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper /** * Data class to store result of [KeysBackupTestHelper.createKeysBackupScenarioWithPassword] */ internal data class KeysBackupScenarioData( val cryptoTestData: CryptoTestData, - val aliceKeys: List, + val aliceKeys: List, val prepareKeysBackupDataResult: PrepareKeysBackupDataResult, val aliceSession2: Session ) { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index fb498e0de5..e160938721 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -301,7 +301,7 @@ class KeysBackupTest : InstrumentedTest { val keyBackupCreationInfo = keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup).megolmBackupCreationInfo // - Check encryptGroupSession() returns stg - val keyBackupData = keysBackup.encryptGroupSession(session) + val keyBackupData = testHelper.runBlockingTest { keysBackup.encryptGroupSession(session) } assertNotNull(keyBackupData) assertNotNull(keyBackupData!!.sessionData) @@ -312,7 +312,7 @@ class KeysBackupTest : InstrumentedTest { val sessionData = keysBackup .decryptKeyBackupData( keyBackupData, - session.olmInboundGroupSession!!.sessionIdentifier(), + session.safeSessionId!!, cryptoTestData.roomId, decryption!! ) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt index 38f94c5103..2cc2b506b9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt @@ -187,7 +187,7 @@ internal class KeysBackupTestHelper( // - Alice must have the same keys on both devices for (aliceKey1 in testData.aliceKeys) { val aliceKey2 = (testData.aliceSession2.cryptoService().keysBackupService() as DefaultKeysBackupService).store - .getInboundGroupSession(aliceKey1.olmInboundGroupSession!!.sessionIdentifier(), aliceKey1.senderKey!!) + .getInboundGroupSession(aliceKey1.safeSessionId!!, aliceKey1.senderKey!!) Assert.assertNotNull(aliceKey2) assertKeysEquals(aliceKey1.exportKeys(), aliceKey2!!.exportKeys()) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index b84c5e2a80..6b52cff512 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -181,5 +181,5 @@ interface CryptoService { /** * Share all inbound sessions of the last chunk messages to the provided userId devices */ - fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) + suspend fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt index 3df4ef7c9a..dbee04de88 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt @@ -69,5 +69,12 @@ data class ForwardedRoomKeyContent( * private part of this key unless they have done device verification. */ @Json(name = "sender_claimed_ed25519_key") - val senderClaimedEd25519Key: String? = null + val senderClaimedEd25519Key: String? = null, + + /** + * MSC3061 + * Identifies keys that were sent when the room's visibility setting was set to world_readable or shared + */ + @Json(name = "org.matrix.msc3061.shared_history") + val sharedHistory: Boolean? = false, ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt index 0830a566ab..75162f8ace 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt @@ -38,5 +38,13 @@ data class RoomKeyContent( // should be a Long but it is sometimes a double @Json(name = "chain_index") - val chainIndex: Any? = null + val chainIndex: Any? = null, + + /** + * MSC3061 + * Identifies keys that were sent when the room's visibility setting was set to world_readable or shared + */ + @Json(name = "org.matrix.msc3061.shared_history") + val sharedHistory: Boolean? = false + ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index a42719146e..b00b4f6173 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -110,6 +110,7 @@ import org.matrix.olm.OlmManager import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject +import kotlin.coroutines.coroutineContext import kotlin.math.max /** @@ -965,10 +966,13 @@ internal class DefaultCryptoService @Inject constructor( private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) { if (!event.isStateEvent()) return val eventContent = event.content.toModel() - eventContent?.historyVisibility?.let { - cryptoStore.setShouldEncryptForInvitedMembers(roomId, it != RoomHistoryVisibility.JOINED) - cryptoStore.setShouldShareHistory(roomId, it.shouldShareHistory()) - } ?: cryptoStore.setShouldShareHistory(roomId, false) + val historyVisibility = eventContent?.historyVisibility + if (historyVisibility == null) { + cryptoStore.setShouldShareHistory(roomId, false) + } else { + cryptoStore.setShouldEncryptForInvitedMembers(roomId, historyVisibility != RoomHistoryVisibility.JOINED) + cryptoStore.setShouldShareHistory(roomId, historyVisibility.shouldShareHistory()) + } } /** @@ -1338,36 +1342,26 @@ internal class DefaultCryptoService @Inject constructor( } } - override fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) { - cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { - runCatching { - deviceListManager.downloadKeys(listOf(userId), false) - }.mapCatching { - val userDevices = cryptoStore.getUserDevices(userId) - userDevices?.forEach { - // Lets share the provided inbound sessions for every user device - val deviceId = it.key - sessionInfoSet?.mapNotNull { sessionInfo -> - // Get inbound session from sessionId and sessionKey - cryptoStore.getInboundGroupSession( - sessionId = sessionInfo.sessionId, - senderKey = sessionInfo.senderKey, - sharedHistory = true - ) - }?.filter { inboundGroupSession -> - // Prevent injecting a forged encrypted message and using session_id/sender_key of another room. - (inboundGroupSession.roomId == roomId).also { - Timber.tag(loggerTag.value).d("Forged encrypted message detected for roomId:$roomId") - } - }?.forEach { inboundGroupSession -> - // Share the sharable session to userId with deviceId - val exportedKeys = inboundGroupSession.exportKeys(sharedHistory = true) - val algorithm = exportedKeys?.algorithm - val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) - decryptor?.shareForwardKeysWithDevice(exportedKeys, deviceId, userId) - Timber.i("## CRYPTO | Sharing inbound session") - } - } + override suspend fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) { + deviceListManager.downloadKeys(listOf(userId), false) + val userDevices = cryptoStore.getUserDeviceList(userId) + val sessionToShare = sessionInfoSet.orEmpty().mapNotNull { sessionInfo -> + // Get inbound session from sessionId and sessionKey + withContext(coroutineDispatchers.crypto) { + olmDevice.getInboundGroupSession( + sessionId = sessionInfo.sessionId, + senderKey = sessionInfo.senderKey, + roomId = roomId + ).takeIf { it.wrapper.sessionData.sharedHistory } + } + } + + userDevices?.forEach { deviceInfo -> + // Lets share the provided inbound sessions for every user device + sessionToShare.forEach { inboundGroupSession -> + val encryptor = roomEncryptorsStore.get(roomId) + encryptor?.shareHistoryKeysWithDevice(inboundGroupSession, deviceInfo) + Timber.i("## CRYPTO | Sharing inbound session") } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt index e4d322cadd..ab7cbb74b1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.sync.Mutex import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.logger.LoggerTag -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import timber.log.Timber import java.util.Timer @@ -31,7 +31,7 @@ import java.util.TimerTask import javax.inject.Inject internal data class InboundGroupSessionHolder( - val wrapper: OlmInboundGroupSessionWrapper2, + val wrapper: MXInboundMegolmSessionWrapper, val mutex: Mutex = Mutex() ) @@ -58,7 +58,7 @@ internal class InboundGroupSessionStore @Inject constructor( cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { Timber.tag(loggerTag.value).v("## Inbound: entryRemoved ${oldValue.wrapper.roomId}-${oldValue.wrapper.senderKey}") store.storeInboundGroupSessions(listOf(oldValue).map { it.wrapper }) - oldValue.wrapper.olmInboundGroupSession?.releaseSession() + oldValue.wrapper.session.releaseSession() } } } @@ -67,7 +67,7 @@ internal class InboundGroupSessionStore @Inject constructor( private val timer = Timer() private var timerTask: TimerTask? = null - private val dirtySession = mutableListOf() + private val dirtySession = mutableListOf() @Synchronized fun clear() { @@ -90,12 +90,12 @@ internal class InboundGroupSessionStore @Inject constructor( @Synchronized fun replaceGroupSession(old: InboundGroupSessionHolder, new: InboundGroupSessionHolder, sessionId: String, senderKey: String) { Timber.tag(loggerTag.value).v("## Replacing outdated session ${old.wrapper.roomId}-${old.wrapper.senderKey}") - dirtySession.remove(old.wrapper) + dirtySession.remove(old) store.removeInboundGroupSession(sessionId, senderKey) sessionCache.remove(CacheKey(sessionId, senderKey)) // release removed session - old.wrapper.olmInboundGroupSession?.releaseSession() + old.wrapper.session.releaseSession() internalStoreGroupSession(new, sessionId, senderKey) } @@ -108,7 +108,7 @@ internal class InboundGroupSessionStore @Inject constructor( private fun internalStoreGroupSession(holder: InboundGroupSessionHolder, sessionId: String, senderKey: String) { Timber.tag(loggerTag.value).v("## Inbound: getInboundGroupSession mark as dirty ${holder.wrapper.roomId}-${holder.wrapper.senderKey}") // We want to batch this a bit for performances - dirtySession.add(holder.wrapper) + dirtySession.add(holder) if (sessionCache[CacheKey(sessionId, senderKey)] == null) { // first time seen, put it in memory cache while waiting for batch insert @@ -127,12 +127,12 @@ internal class InboundGroupSessionStore @Inject constructor( @Synchronized private fun batchSave() { - val toSave = mutableListOf().apply { addAll(dirtySession) } + val toSave = mutableListOf().apply { addAll(dirtySession) } dirtySession.clear() cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { Timber.tag(loggerTag.value).v("## Inbound: getInboundGroupSession batching save of ${toSave.size}") tryOrNull { - store.storeInboundGroupSessions(toSave) + store.storeInboundGroupSessions(toSave.map { it.wrapper }) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt index 6960d94918..7f36224dae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/IncomingKeyRequestManager.kt @@ -405,7 +405,7 @@ internal class IncomingKeyRequestManager @Inject constructor( } val export = sessionHolder.mutex.withLock { - sessionHolder.wrapper.exportKeys(/**TODO*/ false ,chainIndex) + sessionHolder.wrapper.exportKeys(chainIndex) } ?: return false.also { Timber.tag(loggerTag.value) .e("shareKeysWithDevice: failed to export group session ${validRequest.sessionId}") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index 24b6fd166f..409945e468 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -27,7 +27,8 @@ import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXOutboundSessionInfo import org.matrix.android.sdk.internal.crypto.algorithms.megolm.SharedWithHelper -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.di.MoshiProvider @@ -38,6 +39,7 @@ import org.matrix.android.sdk.internal.util.convertToUTF8 import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.olm.OlmAccount import org.matrix.olm.OlmException +import org.matrix.olm.OlmInboundGroupSession import org.matrix.olm.OlmMessage import org.matrix.olm.OlmOutboundGroupSession import org.matrix.olm.OlmSession @@ -514,8 +516,9 @@ internal class MXOlmDevice @Inject constructor( return MXOutboundSessionInfo( sessionId = sessionId, sharedWithHelper = SharedWithHelper(roomId, sessionId, store), - clock, - restoredOutboundGroupSession.creationTime + clock = clock, + creationTime = restoredOutboundGroupSession.creationTime, + sharedHistory = restoredOutboundGroupSession.sharedHistory ) } return null @@ -600,38 +603,44 @@ internal class MXOlmDevice @Inject constructor( * @param exportFormat true if the megolm keys are in export format * @return true if the operation succeeds. */ - fun addInboundGroupSession( - sessionId: String, - sessionKey: String, - roomId: String, - senderKey: String, - forwardingCurve25519KeyChain: List, - keysClaimed: Map, - exportFormat: Boolean - ): AddSessionResult { - val candidateSession = OlmInboundGroupSessionWrapper2(sessionKey, exportFormat) + fun addInboundGroupSession(sessionId: String, + sessionKey: String, + roomId: String, + senderKey: String, + forwardingCurve25519KeyChain: List, + keysClaimed: Map, + exportFormat: Boolean, + sharedHistory: Boolean): AddSessionResult { + val candidateSession = tryOrNull("Failed to create inbound session in room $roomId") { + if (exportFormat) { + OlmInboundGroupSession.importSession(sessionKey) + } else { + OlmInboundGroupSession(sessionKey) + } + } + val existingSessionHolder = tryOrNull { getInboundGroupSession(sessionId, senderKey, roomId) } val existingSession = existingSessionHolder?.wrapper // If we have an existing one we should check if the new one is not better if (existingSession != null) { Timber.tag(loggerTag.value).d("## addInboundGroupSession() check if known session is better than candidate session") try { - val existingFirstKnown = existingSession.firstKnownIndex ?: return AddSessionResult.NotImported.also { + val existingFirstKnown = tryOrNull { existingSession.session.firstKnownIndex } ?: return AddSessionResult.NotImported.also { // This is quite unexpected, could throw if native was released? Timber.tag(loggerTag.value).e("## addInboundGroupSession() null firstKnownIndex on existing session") - candidateSession.olmInboundGroupSession?.releaseSession() + candidateSession?.releaseSession() // Probably should discard it? } - val newKnownFirstIndex = candidateSession.firstKnownIndex + val newKnownFirstIndex = tryOrNull("Failed to get candidate first known index") { candidateSession?.firstKnownIndex } // If our existing session is better we keep it if (newKnownFirstIndex != null && existingFirstKnown <= newKnownFirstIndex) { Timber.tag(loggerTag.value).d("## addInboundGroupSession() : ignore session our is better $senderKey/$sessionId") - candidateSession.olmInboundGroupSession?.releaseSession() + candidateSession?.releaseSession() return AddSessionResult.NotImportedHigherIndex(newKnownFirstIndex.toInt()) } } catch (failure: Throwable) { Timber.tag(loggerTag.value).e("## addInboundGroupSession() Failed to add inbound: ${failure.localizedMessage}") - candidateSession.olmInboundGroupSession?.releaseSession() + candidateSession?.releaseSession() return AddSessionResult.NotImported } } @@ -639,36 +648,42 @@ internal class MXOlmDevice @Inject constructor( Timber.tag(loggerTag.value).d("## addInboundGroupSession() : Candidate session should be added $senderKey/$sessionId") // sanity check on the new session - val candidateOlmInboundSession = candidateSession.olmInboundGroupSession - if (null == candidateOlmInboundSession) { + if (null == candidateSession) { Timber.tag(loggerTag.value).e("## addInboundGroupSession : invalid session ") return AddSessionResult.NotImported } try { - if (candidateOlmInboundSession.sessionIdentifier() != sessionId) { + if (candidateSession.sessionIdentifier() != sessionId) { Timber.tag(loggerTag.value).e("## addInboundGroupSession : ERROR: Mismatched group session ID from senderKey: $senderKey") - candidateOlmInboundSession.releaseSession() + candidateSession.releaseSession() return AddSessionResult.NotImported } } catch (e: Throwable) { - candidateOlmInboundSession.releaseSession() + candidateSession.releaseSession() Timber.tag(loggerTag.value).e(e, "## addInboundGroupSession : sessionIdentifier() failed") return AddSessionResult.NotImported } - candidateSession.senderKey = senderKey - candidateSession.roomId = roomId - candidateSession.keysClaimed = keysClaimed - candidateSession.forwardingCurve25519KeyChain = forwardingCurve25519KeyChain + val candidateSessionData = InboundGroupSessionData( + senderKey = senderKey, + roomId = roomId, + keysClaimed = keysClaimed, + forwardingCurve25519KeyChain = forwardingCurve25519KeyChain, + sharedHistory = sharedHistory, + ) + val wrapper = MXInboundMegolmSessionWrapper( + candidateSession, + candidateSessionData + ) if (existingSession != null) { - inboundGroupSessionStore.replaceGroupSession(existingSessionHolder, InboundGroupSessionHolder(candidateSession), sessionId, senderKey) + inboundGroupSessionStore.replaceGroupSession(existingSessionHolder, InboundGroupSessionHolder(wrapper), sessionId, senderKey) } else { - inboundGroupSessionStore.storeInBoundGroupSession(InboundGroupSessionHolder(candidateSession), sessionId, senderKey) + inboundGroupSessionStore.storeInBoundGroupSession(InboundGroupSessionHolder(wrapper), sessionId, senderKey) } - return AddSessionResult.Imported(candidateSession.firstKnownIndex?.toInt() ?: 0) + return AddSessionResult.Imported(candidateSession.firstKnownIndex.toInt()) } /** @@ -677,41 +692,22 @@ internal class MXOlmDevice @Inject constructor( * @param megolmSessionsData the megolm sessions data * @return the successfully imported sessions. */ - fun importInboundGroupSessions(megolmSessionsData: List): List { - val sessions = ArrayList(megolmSessionsData.size) + fun importInboundGroupSessions(megolmSessionsData: List): List { + val sessions = ArrayList(megolmSessionsData.size) for (megolmSessionData in megolmSessionsData) { val sessionId = megolmSessionData.sessionId ?: continue val senderKey = megolmSessionData.senderKey ?: continue val roomId = megolmSessionData.roomId - var candidateSessionToImport: OlmInboundGroupSessionWrapper2? = null - - try { - candidateSessionToImport = OlmInboundGroupSessionWrapper2(megolmSessionData) - } catch (e: Exception) { - Timber.tag(loggerTag.value).e(e, "## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") - } - - // sanity check - if (candidateSessionToImport?.olmInboundGroupSession == null) { - Timber.tag(loggerTag.value).e("## importInboundGroupSession : invalid session") - continue - } - - val candidateOlmInboundGroupSession = candidateSessionToImport.olmInboundGroupSession - try { - if (candidateOlmInboundGroupSession?.sessionIdentifier() != sessionId) { - Timber.tag(loggerTag.value).e("## importInboundGroupSession : ERROR: Mismatched group session ID from senderKey: $senderKey") - candidateOlmInboundGroupSession?.releaseSession() - continue - } - } catch (e: Exception) { - Timber.tag(loggerTag.value).e(e, "## importInboundGroupSession : sessionIdentifier() failed") - candidateOlmInboundGroupSession?.releaseSession() + val candidateSessionToImport = try { + MXInboundMegolmSessionWrapper.newFromMegolmData(megolmSessionData, true) + } catch (e: Throwable) { + Timber.tag(loggerTag.value).e(e, "## importInboundGroupSession() : Failed to import session $senderKey/$sessionId") continue } + val candidateOlmInboundGroupSession = candidateSessionToImport.session val existingSessionHolder = tryOrNull { getInboundGroupSession(sessionId, senderKey, roomId) } val existingSession = existingSessionHolder?.wrapper @@ -721,16 +717,16 @@ internal class MXOlmDevice @Inject constructor( sessions.add(candidateSessionToImport) } else { Timber.tag(loggerTag.value).e("## importInboundGroupSession() : Update for megolm session $senderKey/$sessionId") - val existingFirstKnown = tryOrNull { existingSession.firstKnownIndex } - val candidateFirstKnownIndex = tryOrNull { candidateSessionToImport.firstKnownIndex } + val existingFirstKnown = tryOrNull { existingSession.session.firstKnownIndex } + val candidateFirstKnownIndex = tryOrNull { candidateSessionToImport.session.firstKnownIndex } if (existingFirstKnown == null || candidateFirstKnownIndex == null) { // should not happen? - candidateSessionToImport.olmInboundGroupSession?.releaseSession() + candidateSessionToImport.session.releaseSession() Timber.tag(loggerTag.value) .w("## importInboundGroupSession() : Can't check session null index $existingFirstKnown/$candidateFirstKnownIndex") } else { - if (existingFirstKnown <= candidateSessionToImport.firstKnownIndex!!) { + if (existingFirstKnown <= candidateFirstKnownIndex) { // Ignore this, keep existing candidateOlmInboundGroupSession.releaseSession() } else { @@ -774,18 +770,17 @@ internal class MXOlmDevice @Inject constructor( ): OlmDecryptionResult { val sessionHolder = getInboundGroupSession(sessionId, senderKey, roomId) val wrapper = sessionHolder.wrapper - val inboundGroupSession = wrapper.olmInboundGroupSession - ?: 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 - // the HS pretending a message was targeting a different room. - 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 { - sessionHolder.mutex.withLock { - inboundGroupSession.decryptMessage(body) + val inboundGroupSession = wrapper.session + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + if (roomId == wrapper.roomId) { + val decryptResult = try { + sessionHolder.mutex.withLock { + inboundGroupSession.decryptMessage(body) + } + } catch (e: OlmException) { + Timber.tag(loggerTag.value).e(e, "## decryptGroupMessage () : decryptMessage failed") + throw MXCryptoError.OlmError(e) } } catch (e: OlmException) { Timber.tag(loggerTag.value).e(e, "## decryptGroupMessage () : decryptMessage failed") @@ -820,12 +815,27 @@ internal class MXOlmDevice @Inject constructor( throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) } - return OlmDecryptionResult( - payload, - wrapper.keysClaimed, - senderKey, - wrapper.forwardingCurve25519KeyChain - ) + inboundGroupSessionStore.storeInBoundGroupSession(sessionHolder, sessionId, senderKey) + val payload = try { + val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) + val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) + adapter.fromJson(payloadString) + } catch (e: Exception) { + Timber.tag(loggerTag.value).e("## decryptGroupMessage() : fails to parse the payload") + throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) + } + + return OlmDecryptionResult( + payload, + wrapper.sessionData.keysClaimed, + senderKey, + wrapper.sessionData.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) + } } /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingKeyRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingKeyRequestManager.kt index 6b22cc09d6..810699d933 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingKeyRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingKeyRequestManager.kt @@ -437,7 +437,10 @@ internal class OutgoingKeyRequestManager @Inject constructor( if (perSessionBackupQueryRateLimiter.tryFromBackupIfPossible(sessionId, roomId)) { // let's see what's the index val knownIndex = tryOrNull { - inboundGroupSessionStore.getInboundGroupSession(sessionId, request.requestBody?.senderKey ?: "")?.wrapper?.firstKnownIndex + inboundGroupSessionStore.getInboundGroupSession(sessionId, request.requestBody?.senderKey ?: "") + ?.wrapper + ?.session + ?.firstKnownIndex } if (knownIndex != null && knownIndex <= request.fromIndex) { // we found the key in backup with good enough index, so we can just mark as cancelled, no need to send request diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt index f6ab96aee6..a624b92a19 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/MegolmSessionDataImporter.kt @@ -84,8 +84,9 @@ internal class MegolmSessionDataImporter @Inject constructor( megolmSessionData.senderKey ?: "", tryOrNull { olmInboundGroupSessionWrappers - .firstOrNull { it.olmInboundGroupSession?.sessionIdentifier() == megolmSessionData.sessionId } - ?.firstKnownIndex?.toInt() + .firstOrNull { it.session.sessionIdentifier() == megolmSessionData.sessionId } + ?.session?.firstKnownIndex + ?.toInt() } ?: 0 ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt index 7edae70fd8..6847a46369 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt @@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto.algorithms import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService /** @@ -44,6 +43,4 @@ internal interface IMXDecrypting { * @param defaultKeysBackupService the keys backup service */ fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {} - - fun shareForwardKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) {} } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt index 73ce5a5004..1454f5b486 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXEncrypting.kt @@ -16,7 +16,9 @@ package org.matrix.android.sdk.internal.crypto.algorithms +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.events.model.Content +import org.matrix.android.sdk.internal.crypto.InboundGroupSessionHolder /** * An interface for encrypting data. @@ -32,4 +34,6 @@ internal interface IMXEncrypting { * @return the encrypted content */ suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content + + suspend fun shareHistoryKeysWithDevice(inboundSessionWrapper: InboundGroupSessionHolder, deviceInfo: CryptoDeviceInfo) {} } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 7b6f80f5bb..7302a481ba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -28,7 +28,6 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.internal.crypto.MXOlmDevice -import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager import org.matrix.android.sdk.internal.crypto.algorithms.IMXDecrypting import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService @@ -241,13 +240,14 @@ internal class MXMegolmDecryption( Timber.tag(loggerTag.value).i("onRoomKeyEvent addInboundGroupSession ${roomKeyContent.sessionId}") val addSessionResult = olmDevice.addInboundGroupSession( - roomKeyContent.sessionId, - roomKeyContent.sessionKey, - roomKeyContent.roomId, - senderKey, - forwardingCurve25519KeyChain, - keysClaimed, - exportFormat + sessionId = roomKeyContent.sessionId, + sessionKey = roomKeyContent.sessionKey, + roomId = roomKeyContent.roomId, + senderKey = senderKey, + forwardingCurve25519KeyChain = forwardingCurve25519KeyChain, + keysClaimed = keysClaimed, + exportFormat = exportFormat, + sharedHistory = roomKeyContent.sharedHistory ?: false ) when (addSessionResult) { @@ -309,43 +309,4 @@ internal class MXMegolmDecryption( newSessionListener?.onNewSession(roomId, senderKey, sessionId) } - override fun shareForwardKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) { -// exportedKeys ?: return -// cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { -// runCatching { deviceListManager.downloadKeys(listOf(userId), false) } -// .mapCatching { -// val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) -// if (deviceInfo == null) { -// throw RuntimeException() -// } else { -// val devicesByUser = mapOf(userId to listOf(deviceInfo)) -// val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) -// val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) -// if (olmSessionResult?.sessionId == null) { -// // no session with this device, probably because there -// // were no one-time keys. -// Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.") -// return@mapCatching -// } -// Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${exportedKeys.sessionId} with device $userId:$deviceId") -// -// val payloadJson = mapOf( -// "type" to EventType.FORWARDED_ROOM_KEY, -// "content" to exportedKeys -// ) -// -// val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) -// val sendToDeviceMap = MXUsersDevicesMap() -// sendToDeviceMap.setObject(userId, deviceId, encodedPayload) -// Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${exportedKeys.sessionId} to $userId:$deviceId") -// val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) -// try { -// sendToDeviceTask.execute(sendToDeviceParams) -// } catch (failure: Throwable) { -// Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${exportedKeys.sessionId} to $userId:$deviceId") -// } -// } -// } -// } - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 740676cd59..4d213dfd9a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode import org.matrix.android.sdk.internal.crypto.DeviceListManager +import org.matrix.android.sdk.internal.crypto.InboundGroupSessionHolder import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter @@ -151,14 +152,27 @@ internal class MXMegolmEncryption( "ed25519" to olmDevice.deviceEd25519Key!! ) + val sharedHistory = cryptoStore.shouldShareHistory(roomId) + Timber.tag(loggerTag.value).v("prepareNewSessionInRoom() as sharedHistory $sharedHistory") olmDevice.addInboundGroupSession( - sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!, - emptyList(), keysClaimedMap, false + sessionId = sessionId!!, + sessionKey = olmDevice.getSessionKey(sessionId)!!, + roomId = roomId, + senderKey = olmDevice.deviceCurve25519Key!!, + forwardingCurve25519KeyChain = emptyList(), + keysClaimed = keysClaimedMap, + exportFormat = false, + sharedHistory = sharedHistory ) defaultKeysBackupService.maybeBackupKeys() - return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore), clock) + return MXOutboundSessionInfo( + sessionId = sessionId, + sharedWithHelper = SharedWithHelper(roomId, sessionId, cryptoStore), + clock = clock, + sharedHistory = sharedHistory + ) } /** @@ -173,11 +187,7 @@ internal class MXMegolmEncryption( // Need to make a brand new session? session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) || // Is there a room history visibility change since the last outboundSession - cryptoStore.needsRotationDueToVisibilityChange(roomId).also { - if (it) { - Timber.tag(loggerTag.value).d("roomId:$roomId Room history visibility change detected since the last outbound session") - } - } || + cryptoStore.shouldShareHistory(roomId) != session.sharedHistory || // Determine if we have shared with anyone we shouldn't have session.sharedWithTooManyDevices(devicesInRoom)) { Timber.tag(loggerTag.value).d("roomId:$roomId Starting new megolm session because we need to rotate.") @@ -240,23 +250,24 @@ internal class MXMegolmEncryption( * @param session the session info * @param devicesByUser the devices map */ - private suspend fun shareUserDevicesKey( - session: MXOutboundSessionInfo, - devicesByUser: Map> - ) { - val sessionKey = olmDevice.getSessionKey(session.sessionId) - val chainIndex = olmDevice.getMessageIndex(session.sessionId) + private suspend fun shareUserDevicesKey(sessionInfo: MXOutboundSessionInfo, + devicesByUser: Map>) { + val sessionKey = olmDevice.getSessionKey(sessionInfo.sessionId) ?: return Unit.also { + Timber.tag(loggerTag.value).v("shareUserDevicesKey() Failed to share session, failed to export") + } + val chainIndex = olmDevice.getMessageIndex(sessionInfo.sessionId) - val submap = HashMap() - submap["algorithm"] = MXCRYPTO_ALGORITHM_MEGOLM - submap["room_id"] = roomId - submap["session_id"] = session.sessionId - submap["session_key"] = sessionKey!! - submap["chain_index"] = chainIndex - - val payload = HashMap() - payload["type"] = EventType.ROOM_KEY - payload["content"] = submap + val payload = mapOf( + "type" to EventType.ROOM_KEY, + "content" to mapOf( + "algorithm" to MXCRYPTO_ALGORITHM_MEGOLM, + "room_id" to roomId, + "session_id" to sessionInfo.sessionId, + "session_key" to sessionKey, + "chain_index" to chainIndex, + "org.matrix.msc3061.shared_history" to sessionInfo.sharedHistory + ) + ) var t0 = clock.epochMillis() Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts") @@ -298,7 +309,7 @@ internal class MXMegolmEncryption( // for dead devices on every message. for ((_, devicesToShareWith) in devicesByUser) { for (deviceInfo in devicesToShareWith) { - session.sharedWithHelper.markedSessionAsShared(deviceInfo, chainIndex) + sessionInfo.sharedWithHelper.markedSessionAsShared(deviceInfo, chainIndex) // XXX is it needed to add it to the audit trail? // For now decided that no, we are more interested by forward trail } @@ -306,8 +317,8 @@ internal class MXMegolmEncryption( if (haveTargets) { t0 = clock.epochMillis() - Timber.tag(loggerTag.value).i("shareUserDevicesKey() ${session.sessionId} : has target") - Timber.tag(loggerTag.value).d("sending to device room key for ${session.sessionId} to ${contentMap.toDebugString()}") + Timber.tag(loggerTag.value).i("shareUserDevicesKey() ${sessionInfo.sessionId} : has target") + Timber.tag(loggerTag.value).d("sending to device room key for ${sessionInfo.sessionId} to ${contentMap.toDebugString()}") val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap) try { withContext(coroutineDispatchers.io) { @@ -316,7 +327,7 @@ internal class MXMegolmEncryption( Timber.tag(loggerTag.value).i("shareUserDevicesKey() : sendToDevice succeeds after ${clock.epochMillis() - t0} ms") } catch (failure: Throwable) { // What to do here... - Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share <${session.sessionId}>") + Timber.tag(loggerTag.value).e("shareUserDevicesKey() : Failed to share <${sessionInfo.sessionId}>") } } else { Timber.tag(loggerTag.value).i("shareUserDevicesKey() : no need to share key") @@ -326,7 +337,7 @@ internal class MXMegolmEncryption( // XXX offload?, as they won't read the message anyhow? notifyKeyWithHeld( noOlmToNotify, - session.sessionId, + sessionInfo.sessionId, olmDevice.deviceCurve25519Key, WithHeldCode.NO_OLM ) @@ -520,6 +531,51 @@ internal class MXMegolmEncryption( } } + @Throws + override suspend fun shareHistoryKeysWithDevice(inboundSessionWrapper: InboundGroupSessionHolder, deviceInfo: CryptoDeviceInfo) { + if (!inboundSessionWrapper.wrapper.sessionData.sharedHistory) throw IllegalArgumentException("This key can't be shared") + Timber.tag(loggerTag.value).i("process shareHistoryKeys for ${inboundSessionWrapper.wrapper.safeSessionId} to ${deviceInfo.shortDebugString()}") + val userId = deviceInfo.userId + val deviceId = deviceInfo.deviceId + val devicesByUser = mapOf(userId to listOf(deviceInfo)) + val usersDeviceMap = try { + ensureOlmSessionsForDevicesAction.handle(devicesByUser) + } catch (failure: Throwable) { + Timber.tag(loggerTag.value).i(failure, "process shareHistoryKeys failed to ensure olm") + // process anyway? + null + } + val olmSessionResult = usersDeviceMap?.getObject(userId, deviceId) + if (olmSessionResult?.sessionId == null) { + Timber.tag(loggerTag.value).w("shareHistoryKeys: no session with this device, probably because there were no one-time keys") + return + } + + val export = inboundSessionWrapper.mutex.withLock { + inboundSessionWrapper.wrapper.exportKeys() + } ?: return Unit.also { + Timber.tag(loggerTag.value).e("shareHistoryKeys: failed to export group session ${inboundSessionWrapper.wrapper.safeSessionId}") + } + + val payloadJson = mapOf( + "type" to EventType.FORWARDED_ROOM_KEY, + "content" to export + ) + + val encodedPayload = + withContext(coroutineDispatchers.computation) { + messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) + } + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(userId, deviceId, encodedPayload) + Timber.tag(loggerTag.value) + .d("shareHistoryKeys() : sending session ${inboundSessionWrapper.wrapper.safeSessionId} to ${deviceInfo.shortDebugString()}") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) + withContext(coroutineDispatchers.io) { + sendToDeviceTask.execute(sendToDeviceParams) + } + } + data class DeviceInRoomInfo( val allowedDevices: MXUsersDevicesMap = MXUsersDevicesMap(), val withHeldDevices: MXUsersDevicesMap = MXUsersDevicesMap() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt index 28d925d8fd..e0caa0d9a5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt @@ -28,6 +28,7 @@ internal class MXOutboundSessionInfo( private val clock: Clock, // When the session was created private val creationTime: Long = clock.epochMillis(), + val sharedHistory: Boolean = false ) { // Number of times this session has been used diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 5eaa106af3..04f33d1778 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -24,6 +24,7 @@ import androidx.annotation.WorkerThread import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCoroutineDispatchers @@ -50,6 +51,7 @@ 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.util.awaitCallback import org.matrix.android.sdk.api.util.fromBase64 +import org.matrix.android.sdk.internal.crypto.InboundGroupSessionStore import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.ObjectSigner @@ -71,7 +73,7 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetRoomSessionsDa import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.GetSessionsDataTask import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.StoreSessionsDataTask import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.UpdateKeysBackupVersionTask -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity import org.matrix.android.sdk.internal.di.MoshiProvider @@ -118,6 +120,7 @@ internal class DefaultKeysBackupService @Inject constructor( private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask, // Task executor private val taskExecutor: TaskExecutor, + private val inboundGroupSessionStore: InboundGroupSessionStore, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val cryptoCoroutineScope: CoroutineScope ) : KeysBackupService { @@ -1316,7 +1319,7 @@ internal class DefaultKeysBackupService @Inject constructor( olmInboundGroupSessionWrappers.forEach { olmInboundGroupSessionWrapper -> val roomId = olmInboundGroupSessionWrapper.roomId ?: return@forEach - val olmInboundGroupSession = olmInboundGroupSessionWrapper.olmInboundGroupSession ?: return@forEach + val olmInboundGroupSession = olmInboundGroupSessionWrapper.session try { encryptGroupSession(olmInboundGroupSessionWrapper) @@ -1405,13 +1408,22 @@ internal class DefaultKeysBackupService @Inject constructor( @VisibleForTesting @WorkerThread - fun encryptGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper2): KeyBackupData? { + suspend fun encryptGroupSession(olmInboundGroupSessionWrapper: MXInboundMegolmSessionWrapper): KeyBackupData? { + olmInboundGroupSessionWrapper.safeSessionId ?: return null + olmInboundGroupSessionWrapper.senderKey ?: return null // Gather information for each key val device = olmInboundGroupSessionWrapper.senderKey?.let { cryptoStore.deviceWithIdentityKey(it) } // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format - val sessionData = olmInboundGroupSessionWrapper.exportKeys() ?: return null + val sessionData = inboundGroupSessionStore + .getInboundGroupSession(olmInboundGroupSessionWrapper.safeSessionId, olmInboundGroupSessionWrapper.senderKey) + ?.let { + withContext(coroutineDispatchers.computation) { + it.mutex.withLock { it.wrapper.exportKeys() } + } + } + ?: return null val sessionBackupData = mapOf( "algorithm" to sessionData.algorithm, "sender_key" to sessionData.senderKey, @@ -1425,7 +1437,9 @@ internal class DefaultKeysBackupService @Inject constructor( .toJson(sessionBackupData) val encryptedSessionBackupData = try { - backupOlmPkEncryption?.encrypt(json) + withContext(coroutineDispatchers.computation) { + backupOlmPkEncryption?.encrypt(json) + } } catch (e: OlmException) { Timber.e(e, "OlmException") null @@ -1435,12 +1449,12 @@ internal class DefaultKeysBackupService @Inject constructor( // Build backup data for that key return KeyBackupData( firstMessageIndex = try { - olmInboundGroupSessionWrapper.olmInboundGroupSession?.firstKnownIndex ?: 0 + olmInboundGroupSessionWrapper.session.firstKnownIndex } catch (e: OlmException) { Timber.e(e, "OlmException") 0L }, - forwardedCount = olmInboundGroupSessionWrapper.forwardingCurve25519KeyChain.orEmpty().size, + forwardedCount = olmInboundGroupSessionWrapper.sessionData.forwardingCurve25519KeyChain.orEmpty().size, isVerified = device?.isVerified == true, sessionData = mapOf( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt new file mode 100644 index 0000000000..0e60791282 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt @@ -0,0 +1,51 @@ +/* + * 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.model + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class InboundGroupSessionData( + + /** The room in which this session is used. */ + @Json(name = "room_id") + var roomId: String? = null, + + /** The base64-encoded curve25519 key of the sender. */ + @Json(name = "sender_key") + var senderKey: String? = null, + + /** Other keys the sender claims. */ + @Json(name = "keys_claimed") + var keysClaimed: Map? = null, + + /** Devices which forwarded this session to us (normally emty). */ + @Json(name = "forwarding_curve25519_key_chain") + var forwardingCurve25519KeyChain: List? = emptyList(), + + /** Not yet used, will be in backup v2 + val untrusted?: Boolean = false */ + + /** + * Flag that indicates whether or not the current inboundSession will be shared to + *invited users to decrypt past messages + */ + @Json(name = "shared_history") + val sharedHistory: Boolean = false, + +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt new file mode 100644 index 0000000000..9cf66814f3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt @@ -0,0 +1,97 @@ +/* + * 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.model + +import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.internal.crypto.MegolmSessionData +import org.matrix.olm.OlmInboundGroupSession +import timber.log.Timber + +data class MXInboundMegolmSessionWrapper( + // olm object + val session: OlmInboundGroupSession, + // data about the session + val sessionData: InboundGroupSessionData +) { + // shortcut + val roomId = sessionData.roomId + val senderKey = sessionData.senderKey + val safeSessionId = tryOrNull("Fail to get megolm session Id") { session.sessionIdentifier() } + + /** + * Export the inbound group session keys + * @param index the index to export. If null, the first known index will be used + * @return the inbound group session as MegolmSessionData if the operation succeeds + */ + internal fun exportKeys(index: Long? = null): MegolmSessionData? { + return try { + val keysClaimed = sessionData.keysClaimed ?: return null + val wantedIndex = index ?: session.firstKnownIndex + + MegolmSessionData( + senderClaimedEd25519Key = sessionData.keysClaimed?.get("ed25519"), + forwardingCurve25519KeyChain = sessionData.forwardingCurve25519KeyChain?.toList().orEmpty(), + senderKey = session.export(index ?: session.firstKnownIndex), + senderClaimedKeys = keysClaimed, + roomId = sessionData.roomId, + sessionId = session.sessionIdentifier(), + sessionKey = session.export(wantedIndex), + algorithm = MXCRYPTO_ALGORITHM_MEGOLM, + sharedHistory = sessionData.sharedHistory + ) + } catch (e: Exception) { + Timber.e(e, "## Failed to export megolm : sessionID ${tryOrNull { session.sessionIdentifier() }} failed") + null + } + } + + companion object { + + /** + * @exportFormat true if the megolm keys are in export format + * (ie, they lack an ed25519 signature) + */ + @Throws + internal fun newFromMegolmData(megolmSessionData: MegolmSessionData, exportFormat: Boolean): MXInboundMegolmSessionWrapper { + val exportedKey = megolmSessionData.sessionKey ?: throw IllegalArgumentException("key data not found") + val inboundSession = if (exportFormat) { + OlmInboundGroupSession.importSession(exportedKey) + } else { + OlmInboundGroupSession(exportedKey) + } + .also { + if (it.sessionIdentifier() != megolmSessionData.sessionId) { + it.releaseSession() + throw IllegalStateException("Mismatched group session Id") + } + } + val data = InboundGroupSessionData( + roomId = megolmSessionData.roomId, + senderKey = megolmSessionData.senderKey, + keysClaimed = megolmSessionData.senderClaimedKeys, + forwardingCurve25519KeyChain = megolmSessionData.forwardingCurve25519KeyChain, + sharedHistory = megolmSessionData.sharedHistory, + ) + + return MXInboundMegolmSessionWrapper( + inboundSession, + data + ) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt index b3898e001b..e5b540fb2f 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt @@ -26,6 +26,8 @@ import java.io.Serializable * This class adds more context to a OlmInboundGroupSession object. * This allows additional checks. The class implements Serializable so that the context can be stored. */ +// Note used anymore, just for database migration +@Deprecated("Use MXInboundMegolmSessionWrapper") internal class OlmInboundGroupSessionWrapper2 : Serializable { // The associated olm inbound group session. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt index 4ac87f44ce..e02953c9e0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt @@ -20,5 +20,9 @@ import org.matrix.olm.OlmOutboundGroupSession internal data class OutboundGroupSessionWrapper( val outboundGroupSession: OlmOutboundGroupSession, - val creationTime: Long + val creationTime: Long, + /** + * As per MSC 3061, declares if this key could be shared when inviting a new user to the room + */ + val sharedHistory: Boolean = false ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 42ff4e11ff..d315e0310a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -35,7 +35,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode import org.matrix.android.sdk.api.util.Optional -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity @@ -64,7 +64,7 @@ internal interface IMXCryptoStore { * * @return the list of all known group sessions, to export them. */ - fun getInboundGroupSessions(): List + fun getInboundGroupSessions(): List /** * Retrieve the known inbound group sessions for the specified room @@ -72,7 +72,7 @@ internal interface IMXCryptoStore { * @param roomId The roomId that the sessions will be returned * @return the list of all known group sessions, for the provided roomId */ - fun getInboundGroupSessions(roomId: String): List + fun getInboundGroupSessions(roomId: String): List /** * @return true to unilaterally blacklist all unverified devices. @@ -309,7 +309,7 @@ internal interface IMXCryptoStore { * * @param sessions the inbound group sessions to store. */ - fun storeInboundGroupSessions(sessions: List) + fun storeInboundGroupSessions(sessions: List) /** * Retrieve an inbound group session. @@ -318,7 +318,7 @@ internal interface IMXCryptoStore { * @param senderKey the base64-encoded curve25519 key of the sender. * @return an inbound group session. */ - fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2? + fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper? /** * Retrieve an inbound group session, filtering shared history. @@ -328,7 +328,7 @@ internal interface IMXCryptoStore { * @param sharedHistory filter inbound session with respect to shared history field * @return an inbound group session. */ - fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): OlmInboundGroupSessionWrapper2? + fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): MXInboundMegolmSessionWrapper? /** * Get the current outbound group session for this encrypted room @@ -340,13 +340,6 @@ internal interface IMXCryptoStore { */ fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?) - /** - * Returns true if there is a room history visibility change since the latest outbound - * session. Specifically when the room's history visibility setting changes to - * world_readable or shared from invited or joined, or changes to invited or joined from world_readable or shared - */ - fun needsRotationDueToVisibilityChange(roomId: String): Boolean - /** * Remove an inbound group session * @@ -369,7 +362,7 @@ internal interface IMXCryptoStore { * * @param olmInboundGroupSessionWrappers the sessions */ - fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List) + fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List) /** * Retrieve inbound group sessions that are not yet backed up. @@ -377,7 +370,7 @@ internal interface IMXCryptoStore { * @param limit the maximum number of sessions to return. * @return an array of non backed up inbound group sessions. */ - fun inboundGroupSessionsToBackup(limit: Int): List + fun inboundGroupSessionsToBackup(limit: Int): List /** * Number of stored inbound group sessions. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index e226aa2e8e..74d547b9fb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -50,7 +50,7 @@ import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldCo import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -671,6 +671,8 @@ internal class RealmCryptoStore @Inject constructor( } override fun setShouldShareHistory(roomId: String, shouldShareHistory: Boolean) { + Timber.tag(loggerTag.value) + .v("setShouldShareHistory for room $roomId is $shouldShareHistory") doRealmTransaction(realmConfiguration) { CryptoRoomEntity.getOrCreate(it, roomId).shouldShareHistory = shouldShareHistory } @@ -740,61 +742,55 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun storeInboundGroupSessions(sessions: List) { + override fun storeInboundGroupSessions(sessions: List) { if (sessions.isEmpty()) { return } doRealmTransaction(realmConfiguration) { realm -> - sessions.forEach { session -> - var sessionIdentifier: String? = null + sessions.forEach { wrapper -> - try { - sessionIdentifier = session.olmInboundGroupSession?.sessionIdentifier() + val sessionIdentifier = try { + wrapper.session.sessionIdentifier() } catch (e: OlmException) { Timber.e(e, "## storeInboundGroupSession() : sessionIdentifier failed") + return@forEach } - if (sessionIdentifier != null) { - val shouldShareHistory = session.roomId?.let { roomId -> - CryptoRoomEntity.getById(realm, roomId)?.shouldShareHistory - } ?: false - val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, session.senderKey) +// val shouldShareHistory = session.roomId?.let { roomId -> +// CryptoRoomEntity.getById(realm, roomId)?.shouldShareHistory +// } ?: false + val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionIdentifier, wrapper.sessionData.senderKey) - val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { - primaryKey = key - sessionId = sessionIdentifier - senderKey = session.senderKey - roomId = session.roomId - sharedHistory = shouldShareHistory - putInboundGroupSession(session) - } - Timber.i("## CRYPTO | shouldShareHistory: $shouldShareHistory for $key") - realm.insertOrUpdate(realmOlmInboundGroupSession) + val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { + primaryKey = key + store(wrapper) } + Timber.i("## CRYPTO | shouldShareHistory: ${wrapper.sessionData.sharedHistory} for $key") + realm.insertOrUpdate(realmOlmInboundGroupSession) } } } - override fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2? { + override fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper? { val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) - return doWithRealm(realmConfiguration) { - it.where() + return doWithRealm(realmConfiguration) { realm -> + realm.where() .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) .findFirst() - ?.getInboundGroupSession() + ?.toModel() } } - override fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): OlmInboundGroupSessionWrapper2? { + override fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): MXInboundMegolmSessionWrapper? { val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) return doWithRealm(realmConfiguration) { it.where() .equalTo(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, sharedHistory) .equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key) .findFirst() - ?.getInboundGroupSession() + ?.toModel() } } @@ -806,7 +802,8 @@ internal class RealmCryptoStore @Inject constructor( entity.getOutboundGroupSession()?.let { OutboundGroupSessionWrapper( it, - entity.creationTime ?: 0 + entity.creationTime ?: 0, + entity.shouldShareHistory ) } } @@ -836,36 +833,32 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun needsRotationDueToVisibilityChange(roomId: String): Boolean { - return doWithRealm(realmConfiguration) { realm -> - CryptoRoomEntity.getById(realm, roomId)?.let { entity -> - entity.shouldShareHistory != entity.outboundSessionInfo?.shouldShareHistory - } - } ?: false - } +// override fun needsRotationDueToVisibilityChange(roomId: String): Boolean { +// return doWithRealm(realmConfiguration) { realm -> +// CryptoRoomEntity.getById(realm, roomId)?.let { entity -> +// entity.shouldShareHistory != entity.outboundSessionInfo?.shouldShareHistory +// } +// } ?: false +// } /** * Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2, * so there is no need to use or update `inboundGroupSessionToRelease` for native memory management. */ - override fun getInboundGroupSessions(): List { - return doWithRealm(realmConfiguration) { - it.where() + override fun getInboundGroupSessions(): List { + return doWithRealm(realmConfiguration) { realm -> + realm.where() .findAll() - .mapNotNull { inboundGroupSessionEntity -> - inboundGroupSessionEntity.getInboundGroupSession() - } + .mapNotNull { it.toModel() } } } - override fun getInboundGroupSessions(roomId: String): List { - return doWithRealm(realmConfiguration) { - it.where() + override fun getInboundGroupSessions(roomId: String): List { + return doWithRealm(realmConfiguration) { realm -> + realm.where() .equalTo(OlmInboundGroupSessionEntityFields.ROOM_ID, roomId) .findAll() - .mapNotNull { inboundGroupSessionEntity -> - inboundGroupSessionEntity.getInboundGroupSession() - } + .mapNotNull { it.toModel() } } } @@ -926,7 +919,7 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List) { + override fun markBackupDoneForInboundGroupSessions(olmInboundGroupSessionWrappers: List) { if (olmInboundGroupSessionWrappers.isEmpty()) { return } @@ -934,10 +927,13 @@ internal class RealmCryptoStore @Inject constructor( doRealmTransaction(realmConfiguration) { realm -> olmInboundGroupSessionWrappers.forEach { olmInboundGroupSessionWrapper -> try { - val sessionIdentifier = olmInboundGroupSessionWrapper.olmInboundGroupSession?.sessionIdentifier() + val sessionIdentifier = + tryOrNull("Failed to get session identifier") { + olmInboundGroupSessionWrapper.session.sessionIdentifier() + } ?: return@forEach val key = OlmInboundGroupSessionEntity.createPrimaryKey( sessionIdentifier, - olmInboundGroupSessionWrapper.senderKey + olmInboundGroupSessionWrapper.sessionData.senderKey ) val existing = realm.where() @@ -950,9 +946,7 @@ internal class RealmCryptoStore @Inject constructor( // ... might be in cache but not yet persisted, create a record to persist backedup state val realmOlmInboundGroupSession = OlmInboundGroupSessionEntity().apply { primaryKey = key - sessionId = sessionIdentifier - senderKey = olmInboundGroupSessionWrapper.senderKey - putInboundGroupSession(olmInboundGroupSessionWrapper) + store(olmInboundGroupSessionWrapper) backedUp = true } @@ -965,15 +959,13 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun inboundGroupSessionsToBackup(limit: Int): List { + override fun inboundGroupSessionsToBackup(limit: Int): List { return doWithRealm(realmConfiguration) { it.where() .equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, false) .limit(limit.toLong()) .findAll() - .mapNotNull { inboundGroupSession -> - inboundGroupSession.getInboundGroupSession() - } + .mapNotNull { it.toModel() } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt index 6ce94c3eb2..b2a2129853 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt @@ -17,20 +17,66 @@ package org.matrix.android.sdk.internal.crypto.store.db.migration import io.realm.DynamicRealm +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData +import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields - import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields +import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm +import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.util.database.RealmMigrator +import timber.log.Timber -// Version 16L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061 -internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 16) { +/** + * Version 17L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061 + * Also migrates how megolm session are stored to avoid additional serialized frozen class +*/ +internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 17) { override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("CryptoRoomEntity") + ?.addField(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java) + + val moshiAdapter = MoshiProvider.providesMoshi().adapter(InboundGroupSessionData::class.java) + realm.schema.get("OlmInboundGroupSessionEntity") ?.addField(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, Boolean::class.java) ?.addField(OlmInboundGroupSessionEntityFields.ROOM_ID, String::class.java) + ?.addField(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON, String::class.java) + ?.addField(OlmInboundGroupSessionEntityFields.SERIALIZED_OLM_INBOUND_GROUP_SESSION, String::class.java) + ?.transform { dynamicObject -> + try { + // we want to convert the old wrapper frozen class into a + // map of sessionData & the pickled session herself + dynamicObject.getString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA)?.let { oldData -> + val oldWrapper = tryOrNull("Failed to convert megolm inbound group data") { + @Suppress("DEPRECATION") + deserializeFromRealm(oldData) + } + val groupSession = oldWrapper?.olmInboundGroupSession + ?: return@transform Unit.also { + Timber.w("Failed to migrate megolm session, no olmInboundGroupSession") + } + // now convert to new data + val data = InboundGroupSessionData( + senderKey = oldWrapper.senderKey, + roomId = oldWrapper.roomId, + keysClaimed = oldWrapper.keysClaimed, + forwardingCurve25519KeyChain = oldWrapper.forwardingCurve25519KeyChain, + sharedHistory = false, + ) - realm.schema.get("CryptoRoomEntity") - ?.addField(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java) + dynamicObject.setString(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON, moshiAdapter.toJson(data)) + dynamicObject.setString(OlmInboundGroupSessionEntityFields.SERIALIZED_OLM_INBOUND_GROUP_SESSION, serializeForRealm(groupSession)) + + // denormalized fields + dynamicObject.setString(OlmInboundGroupSessionEntityFields.ROOM_ID, oldWrapper.roomId) + dynamicObject.setBoolean(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, false) + } + } catch (failure: Throwable) { + Timber.e(failure, "Failed to migrate megolm session") + } + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt index 12e9b8baee..62ab73e379 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt @@ -18,9 +18,12 @@ package org.matrix.android.sdk.internal.crypto.store.db.model import io.realm.RealmObject import io.realm.annotations.PrimaryKey -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 +import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData +import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm +import org.matrix.android.sdk.internal.di.MoshiProvider +import org.matrix.olm.OlmInboundGroupSession import timber.log.Timber internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey" @@ -28,11 +31,23 @@ internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: internal open class OlmInboundGroupSessionEntity( // Combined value to build a primary key @PrimaryKey var primaryKey: String? = null, + + // denormalization for faster querying (these fields are in the inboundGroupSessionDataJson) var sessionId: String? = null, var senderKey: String? = null, var roomId: String? = null, - // olmInboundGroupSessionData contains Json + + // Deprecated, used for migration / olmInboundGroupSessionData contains Json + // keep it in case of problem to have a chance to recover var olmInboundGroupSessionData: String? = null, + + // Stores the session data in an extensible format + // to allow to store data not yet supported for later use + var inboundGroupSessionDataJson: String? = null, + + // The pickled session + var serializedOlmInboundGroupSession: String? = null, + // Flag that indicates whether or not the current inboundSession will be shared to // invited users to decrypt past messages var sharedHistory: Boolean = false, @@ -41,18 +56,58 @@ internal open class OlmInboundGroupSessionEntity( ) : RealmObject() { - fun getInboundGroupSession(): OlmInboundGroupSessionWrapper2? { + fun store(wrapper: MXInboundMegolmSessionWrapper) { + this.serializedOlmInboundGroupSession = serializeForRealm(wrapper.session) + this.inboundGroupSessionDataJson = adapter.toJson(wrapper.sessionData) + this.roomId = wrapper.sessionData.roomId + this.senderKey = wrapper.sessionData.senderKey + this.sessionId = wrapper.session.sessionIdentifier() + this.sharedHistory = wrapper.sessionData.sharedHistory + } +// fun getInboundGroupSession(): OlmInboundGroupSessionWrapper2? { +// return try { +// deserializeFromRealm(olmInboundGroupSessionData) +// } catch (failure: Throwable) { +// Timber.e(failure, "## Deserialization failure") +// return null +// } +// } +// +// fun putInboundGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper2?) { +// olmInboundGroupSessionData = serializeForRealm(olmInboundGroupSessionWrapper) +// } + + fun getOlmGroupSession(): OlmInboundGroupSession? { return try { - deserializeFromRealm(olmInboundGroupSessionData) + deserializeFromRealm(serializedOlmInboundGroupSession) } catch (failure: Throwable) { Timber.e(failure, "## Deserialization failure") return null } } - fun putInboundGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper2?) { - olmInboundGroupSessionData = serializeForRealm(olmInboundGroupSessionWrapper) + fun getData(): InboundGroupSessionData? { + return try { + inboundGroupSessionDataJson?.let { + adapter.fromJson(it) + } + } catch (failure: Throwable) { + Timber.e(failure, "## Deserialization failure") + return null + } } - companion object + fun toModel(): MXInboundMegolmSessionWrapper? { + val data = getData() ?: return null + val session = getOlmGroupSession() ?: return null + return MXInboundMegolmSessionWrapper( + session = session, + sessionData = data + ) + } + + companion object { + private val adapter = MoshiProvider.providesMoshi() + .adapter(InboundGroupSessionData::class.java) + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt index fbd9d245d9..bb14b417dd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/SendEventTask.kt @@ -15,7 +15,6 @@ */ package org.matrix.android.sdk.internal.crypto.tasks -import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.network.GlobalErrorReceiver @@ -48,8 +47,12 @@ internal class DefaultSendEventTask @Inject constructor( params.event.roomId ?.takeIf { params.encrypt } ?.let { roomId -> - tryOrNull { + try { loadRoomMembersTask.execute(LoadRoomMembersTask.Params(roomId)) + } catch (failure: Throwable) { + // send any way? + // the result is that some users won't probably be able to decrypt :/ + Timber.w(failure, "SendEvent: failed to load members in room ${params.event.roomId}") } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index fdda38777a..54c1a6b57e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -144,6 +144,7 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { + // TODO not sure it's the right way to get the latest messages in a room val sessionInfo = Realm.getInstance(monarchy.realmConfiguration).use { ChunkEntity.findLatestSessionInfo(it, roomId) } From 8c26592d462f44f2169da649fab6e78e3bff0f46 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 12 May 2022 12:46:33 +0200 Subject: [PATCH 071/136] cleaning --- .../internal/crypto/keysbackup/DefaultKeysBackupService.kt | 2 +- .../internal/crypto/store/db/migration/MigrateCryptoTo017.kt | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 04f33d1778..08765f0c39 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -1412,7 +1412,7 @@ internal class DefaultKeysBackupService @Inject constructor( olmInboundGroupSessionWrapper.safeSessionId ?: return null olmInboundGroupSessionWrapper.senderKey ?: return null // Gather information for each key - val device = olmInboundGroupSessionWrapper.senderKey?.let { cryptoStore.deviceWithIdentityKey(it) } + val device = cryptoStore.deviceWithIdentityKey(olmInboundGroupSessionWrapper.senderKey) // Build the m.megolm_backup.v1.curve25519-aes-sha2 data as defined at // https://github.com/uhoreg/matrix-doc/blob/e2e_backup/proposals/1219-storing-megolm-keys-serverside.md#mmegolm_backupv1curve25519-aes-sha2-key-format diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt index b2a2129853..2e847ab28e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt @@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto.store.db.migration import io.realm.DynamicRealm import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData -import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields @@ -31,7 +30,7 @@ import timber.log.Timber /** * Version 17L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061 * Also migrates how megolm session are stored to avoid additional serialized frozen class -*/ + */ internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 17) { override fun doMigrate(realm: DynamicRealm) { @@ -52,7 +51,7 @@ internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 17 dynamicObject.getString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA)?.let { oldData -> val oldWrapper = tryOrNull("Failed to convert megolm inbound group data") { @Suppress("DEPRECATION") - deserializeFromRealm(oldData) + deserializeFromRealm(oldData) } val groupSession = oldWrapper?.olmInboundGroupSession ?: return@transform Unit.also { From d8d808d0b414d34de79d7a85c0982e34cd13965b Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 12 May 2022 12:58:04 +0200 Subject: [PATCH 072/136] removed deprecated annotation, CI don't like --- .../sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt index e5b540fb2f..f19275a8a1 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt @@ -27,7 +27,7 @@ import java.io.Serializable * This allows additional checks. The class implements Serializable so that the context can be stored. */ // Note used anymore, just for database migration -@Deprecated("Use MXInboundMegolmSessionWrapper") +// Deprecated("Use MXInboundMegolmSessionWrapper") internal class OlmInboundGroupSessionWrapper2 : Serializable { // The associated olm inbound group session. From fb352ffa38a9beb1c7bb6ec2c7d81e6f3f3a29a5 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 12 May 2022 13:57:53 +0200 Subject: [PATCH 073/136] quick format --- .../sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 7302a481ba..53ff4b83ce 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -308,5 +308,4 @@ internal class MXMegolmDecryption( Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey") newSessionListener?.onNewSession(roomId, senderKey, sessionId) } - } From a9a7400fefcbfcf905b8c7a09e644812bd264cee Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Fri, 20 May 2022 17:46:31 +0300 Subject: [PATCH 074/136] Add MXCryptoConfig flag for key history sharing Add shared_history flag to sessionBackupData --- .../matrix/android/sdk/api/crypto/MXCryptoConfig.kt | 6 ++++++ .../crypto/keysbackup/DefaultKeysBackupService.kt | 5 +++-- .../crypto/keysbackup/model/rest/KeyBackupData.kt | 9 ++++++++- .../sdk/internal/crypto/store/db/RealmCryptoStore.kt | 3 +++ .../room/membership/DefaultMembershipService.kt | 11 +++++++++-- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt index 9507ddda65..ea0c413546 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt @@ -38,4 +38,10 @@ data class MXCryptoConfig constructor( * You can limit request only to your sessions by turning this setting to `true` */ val limitRoomKeyRequestsToMyDevices: Boolean = false, + + /** + * Flag that indicates whether or not key history will be shared to invited + * users with respect to room visibility + */ + val shouldShareKeyHistory: Boolean = true, ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 08765f0c39..3514dae3f2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -1429,7 +1429,8 @@ internal class DefaultKeysBackupService @Inject constructor( "sender_key" to sessionData.senderKey, "sender_claimed_keys" to sessionData.senderClaimedKeys, "forwarding_curve25519_key_chain" to (sessionData.forwardingCurve25519KeyChain.orEmpty()), - "session_key" to sessionData.sessionKey + "session_key" to sessionData.sessionKey, + "org.matrix.msc3061.shared_history" to sessionData.sharedHistory ) val json = MoshiProvider.providesMoshi() @@ -1456,7 +1457,7 @@ internal class DefaultKeysBackupService @Inject constructor( }, forwardedCount = olmInboundGroupSessionWrapper.sessionData.forwardingCurve25519KeyChain.orEmpty().size, isVerified = device?.isVerified == true, - + sharedHistory = olmInboundGroupSessionWrapper.sessionData.sharedHistory, sessionData = mapOf( "ciphertext" to encryptedSessionBackupData.mCipherText, "mac" to encryptedSessionBackupData.mMac, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt index 5c3d0c12b0..675bf12248 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt @@ -50,5 +50,12 @@ internal data class KeyBackupData( * Algorithm-dependent data. */ @Json(name = "session_data") - val sessionData: JsonDict + val sessionData: JsonDict, + + /** + * Flag that indicates whether or not the current inboundSession will be shared to + * invited users to decrypt past messages + */ + @Json(name = "org.matrix.msc3061.shared_history") + val sharedHistory: Boolean = false ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 74d547b9fb..15f95f01cb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -26,6 +26,7 @@ import io.realm.RealmConfiguration import io.realm.Sort import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.logger.LoggerTag @@ -111,6 +112,7 @@ internal class RealmCryptoStore @Inject constructor( private val crossSigningKeysMapper: CrossSigningKeysMapper, @UserId private val userId: String, @DeviceId private val deviceId: String?, + private val matrixConfiguration: MatrixConfiguration, private val clock: Clock, ) : IMXCryptoStore { @@ -658,6 +660,7 @@ internal class RealmCryptoStore @Inject constructor( } override fun shouldShareHistory(roomId: String): Boolean { + if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return false return doWithRealm(realmConfiguration) { CryptoRoomEntity.getById(it, roomId)?.shouldShareHistory } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 54c1a6b57e..331119530e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -24,6 +24,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.realm.Realm import io.realm.RealmQuery +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.members.MembershipService @@ -57,6 +58,7 @@ internal class DefaultMembershipService @AssistedInject constructor( private val cryptoService: CryptoService, @UserId private val userId: String, + private val matrixConfiguration: MatrixConfiguration, private val queryStringValueProcessor: QueryStringValueProcessor ) : MembershipService { @@ -144,13 +146,18 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { + sendShareHistoryKeysIfNeeded(userId) + val params = InviteTask.Params(roomId, userId, reason) + inviteTask.execute(params) + } + + private suspend fun sendShareHistoryKeysIfNeeded(userId: String) { + if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return // TODO not sure it's the right way to get the latest messages in a room val sessionInfo = Realm.getInstance(monarchy.realmConfiguration).use { ChunkEntity.findLatestSessionInfo(it, roomId) } cryptoService.sendSharedHistoryKeys(roomId, userId, sessionInfo) - val params = InviteTask.Params(roomId, userId, reason) - inviteTask.execute(params) } override suspend fun invite3pid(threePid: ThreePid) { From d3a516b05db38107883668b68317d474b3ad824b Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 31 May 2022 11:26:26 +0300 Subject: [PATCH 075/136] Enhance key sharing to respect matrix configuration --- .../crypto/algorithms/megolm/MXMegolmDecryption.kt | 14 ++++++++++++-- .../crypto/keysbackup/DefaultKeysBackupService.kt | 12 +++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 53ff4b83ce..479c2654ed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import dagger.Lazy +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.NewSessionListener @@ -41,6 +42,7 @@ internal class MXMegolmDecryption( private val olmDevice: MXOlmDevice, private val outgoingKeyRequestManager: OutgoingKeyRequestManager, private val cryptoStore: IMXCryptoStore, + private val matrixConfiguration: MatrixConfiguration, private val liveEventManager: Lazy ) : IMXDecrypting { @@ -247,7 +249,7 @@ internal class MXMegolmDecryption( forwardingCurve25519KeyChain = forwardingCurve25519KeyChain, keysClaimed = keysClaimed, exportFormat = exportFormat, - sharedHistory = roomKeyContent.sharedHistory ?: false + sharedHistory = roomKeyContent.getSharedKey() ) when (addSessionResult) { @@ -298,7 +300,15 @@ internal class MXMegolmDecryption( } /** - * Check if the some messages can be decrypted with a new session. + * Returns boolean shared key flag, if enabled with respect to matrix configuration + */ + private fun RoomKeyContent.getSharedKey(): Boolean { + if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return false + return sharedHistory ?: false + } + + /** + * Check if the some messages can be decrypted with a new session * * @param roomId the room id where the new Megolm session has been created for, may be null when importing from external sessions * @param senderKey the session sender key diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 3514dae3f2..f8b00007bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixCoroutineDispatchers import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP @@ -120,6 +121,7 @@ internal class DefaultKeysBackupService @Inject constructor( private val updateKeysBackupVersionTask: UpdateKeysBackupVersionTask, // Task executor private val taskExecutor: TaskExecutor, + private val matrixConfiguration: MatrixConfiguration, private val inboundGroupSessionStore: InboundGroupSessionStore, private val coroutineDispatchers: MatrixCoroutineDispatchers, private val cryptoCoroutineScope: CoroutineScope @@ -1457,7 +1459,7 @@ internal class DefaultKeysBackupService @Inject constructor( }, forwardedCount = olmInboundGroupSessionWrapper.sessionData.forwardingCurve25519KeyChain.orEmpty().size, isVerified = device?.isVerified == true, - sharedHistory = olmInboundGroupSessionWrapper.sessionData.sharedHistory, + sharedHistory = olmInboundGroupSessionWrapper.getSharedKey(), sessionData = mapOf( "ciphertext" to encryptedSessionBackupData.mCipherText, "mac" to encryptedSessionBackupData.mMac, @@ -1466,6 +1468,14 @@ internal class DefaultKeysBackupService @Inject constructor( ) } + /** + * Returns boolean shared key flag, if enabled with respect to matrix configuration + */ + private fun MXInboundMegolmSessionWrapper.getSharedKey(): Boolean { + if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return false + return sessionData.sharedHistory + } + @VisibleForTesting @WorkerThread fun decryptKeyBackupData(keyBackupData: KeyBackupData, sessionId: String, roomId: String, decryption: OlmPkDecryption): MegolmSessionData? { From 55fdff4242fc9682c6cfa2394e8c1168d4714123 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 31 May 2022 11:43:33 +0300 Subject: [PATCH 076/136] Resolve merge conflicts --- .../sdk/internal/crypto/MXOlmDevice.kt | 20 +++++++++---------- .../megolm/MXMegolmDecryptionFactory.kt | 6 ++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index 409945e468..7ad398ff1f 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -771,16 +771,16 @@ internal class MXOlmDevice @Inject constructor( val sessionHolder = getInboundGroupSession(sessionId, senderKey, roomId) val wrapper = sessionHolder.wrapper val inboundGroupSession = wrapper.session - // Check that the room id matches the original one for the session. This stops - // the HS pretending a message was targeting a different room. - if (roomId == wrapper.roomId) { - val decryptResult = try { - sessionHolder.mutex.withLock { - inboundGroupSession.decryptMessage(body) - } - } catch (e: OlmException) { - Timber.tag(loggerTag.value).e(e, "## decryptGroupMessage () : decryptMessage failed") - throw MXCryptoError.OlmError(e) + if (roomId != wrapper.roomId) { + // Check that the room id matches the original one for the session. This stops + // the HS pretending a message was targeting a different room. + 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 { + sessionHolder.mutex.withLock { + inboundGroupSession.decryptMessage(body) } } catch (e: OlmException) { Timber.tag(loggerTag.value).e(e, "## decryptGroupMessage () : decryptMessage failed") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt index 81a6fb28c0..414416a0f6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm import dagger.Lazy +import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -27,6 +28,7 @@ internal class MXMegolmDecryptionFactory @Inject constructor( private val olmDevice: MXOlmDevice, private val outgoingKeyRequestManager: OutgoingKeyRequestManager, private val cryptoStore: IMXCryptoStore, + private val matrixConfiguration: MatrixConfiguration, private val eventsManager: Lazy ) { @@ -35,7 +37,7 @@ internal class MXMegolmDecryptionFactory @Inject constructor( olmDevice, outgoingKeyRequestManager, cryptoStore, - eventsManager - ) + matrixConfiguration, + eventsManager) } } From 010cf540b6bb4983b0a969b51c47ef7f7511f771 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 31 May 2022 13:13:24 +0300 Subject: [PATCH 077/136] Fix broken unit test --- .../matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt index ba1afd4758..d8890e2b38 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt @@ -17,6 +17,8 @@ package org.matrix.android.sdk.internal.crypto import io.realm.RealmConfiguration +import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.common.TestRoomDisplayNameFallbackProvider import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule @@ -36,6 +38,10 @@ internal class CryptoStoreHelper { crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()), userId = "userId_" + Random.nextInt(), deviceId = "deviceId_sample", + matrixConfiguration = MatrixConfiguration( + applicationFlavor = "TestFlavor", + roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider() + ), clock = DefaultClock(), ) } From df241dbdb8cd55df9eb7d2f8c57974a82ad3ac99 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Tue, 31 May 2022 15:07:11 +0300 Subject: [PATCH 078/136] Fix broken unit test --- .../java/org/matrix/android/sdk/common/CommonTestHelper.kt | 2 +- .../android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index 7dafe33935..3ad8b281e9 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -54,7 +54,7 @@ import java.util.concurrent.TimeUnit * This class exposes methods to be used in common cases * Registration, login, Sync, Sending messages... */ -class CommonTestHelper private constructor(context: Context) { +class CommonTestHelper internal constructor(context: Context) { companion object { internal fun runSessionTest(context: Context, autoSignoutOnClose: Boolean = true, block: (CommonTestHelper) -> Unit) { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index 0df7e0eb7d..4a7b49a280 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -305,7 +305,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY, stateKey = "", body = RoomHistoryVisibilityContent( - _historyVisibility = nextRoomHistoryVisibility._historyVisibility + historyVisibilityStr = nextRoomHistoryVisibility.historyVisibilityStr ).toContent() ) it.countDown() From 34145f0374ae3f07c6cedbe3b5813c2130b70377 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 10 Jun 2022 10:37:15 +0200 Subject: [PATCH 079/136] post rebase fix --- .../sdk/internal/crypto/MXOlmDevice.kt | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index 7ad398ff1f..192df3ff0c 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -815,27 +815,12 @@ internal class MXOlmDevice @Inject constructor( throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) } - inboundGroupSessionStore.storeInBoundGroupSession(sessionHolder, sessionId, senderKey) - val payload = try { - val adapter = MoshiProvider.providesMoshi().adapter(JSON_DICT_PARAMETERIZED_TYPE) - val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) - adapter.fromJson(payloadString) - } catch (e: Exception) { - Timber.tag(loggerTag.value).e("## decryptGroupMessage() : fails to parse the payload") - throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_DECRYPTED_FORMAT, MXCryptoError.BAD_DECRYPTED_FORMAT_TEXT_REASON) - } - - return OlmDecryptionResult( - payload, - wrapper.sessionData.keysClaimed, - senderKey, - wrapper.sessionData.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) - } + return OlmDecryptionResult( + payload, + wrapper.sessionData.keysClaimed, + senderKey, + wrapper.sessionData.forwardingCurve25519KeyChain + ) } /** From f64adeba7ffad1c08dabf912489034b02b7260a1 Mon Sep 17 00:00:00 2001 From: Valere Date: Mon, 13 Jun 2022 13:56:44 +0200 Subject: [PATCH 080/136] fix bad sender key export --- .../internal/crypto/model/MXInboundMegolmSessionWrapper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt index 9cf66814f3..0820b1c207 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt @@ -46,11 +46,11 @@ data class MXInboundMegolmSessionWrapper( MegolmSessionData( senderClaimedEd25519Key = sessionData.keysClaimed?.get("ed25519"), forwardingCurve25519KeyChain = sessionData.forwardingCurve25519KeyChain?.toList().orEmpty(), - senderKey = session.export(index ?: session.firstKnownIndex), + sessionKey = session.export(wantedIndex), senderClaimedKeys = keysClaimed, roomId = sessionData.roomId, sessionId = session.sessionIdentifier(), - sessionKey = session.export(wantedIndex), + senderKey = senderKey, algorithm = MXCRYPTO_ALGORITHM_MEGOLM, sharedHistory = sessionData.sharedHistory ) From d9fb58fbcb66b717286d2fc0bfec3acde5c08eb0 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 14 Jun 2022 09:26:29 +0200 Subject: [PATCH 081/136] Fix tests --- .../sdk/internal/crypto/E2eeSanityTests.kt | 2 +- .../crypto/E2eeShareKeysHistoryTest.kt | 152 +++++++++--------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt index d8c3c021cf..de4817f2b3 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt @@ -70,7 +70,7 @@ import java.util.concurrent.CountDownLatch @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) @LargeTest -@Ignore("This test fails with an unhandled exception thrown from a coroutine which terminates the entire test run.") +//@Ignore("This test fails with an unhandled exception thrown from a coroutine which terminates the entire test run.") class E2eeSanityTests : InstrumentedTest { @get:Rule val rule = RetryTestRule(3) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index 4a7b49a280..c855c14a54 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -42,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.model.shouldShareHistory 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.common.CommonTestHelper +import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.SessionTestParams import org.matrix.android.sdk.common.TestConstants @@ -77,94 +78,93 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { * RoomHistoryVisibility.SHARED or RoomHistoryVisibility.WORLD_READABLE. * We should not be able to view messages/decrypt otherwise */ - private fun testShareHistoryWithRoomVisibility(roomHistoryVisibility: RoomHistoryVisibility? = null) { - val testHelper = CommonTestHelper(context()) - val cryptoTestHelper = CryptoTestHelper(testHelper) - val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true, roomHistoryVisibility) + private fun testShareHistoryWithRoomVisibility(roomHistoryVisibility: RoomHistoryVisibility? = null) = + runCryptoTest(context()) { cryptoTestHelper, testHelper -> + val cryptoTestData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(true, roomHistoryVisibility) - val e2eRoomID = cryptoTestData.roomId + val e2eRoomID = cryptoTestData.roomId - // Alice - val aliceSession = cryptoTestData.firstSession - val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!! + // Alice + val aliceSession = cryptoTestData.firstSession + val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!! - // Bob - val bobSession = cryptoTestData.secondSession - val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!! + // Bob + val bobSession = cryptoTestData.secondSession + val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!! - assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) - Log.v("#E2E TEST", "Alice and Bob are in roomId: $e2eRoomID") + assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) + Log.v("#E2E TEST", "Alice and Bob are in roomId: $e2eRoomID") - val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, "Hello Bob, I am Alice!", testHelper) - Assert.assertTrue("Message should be sent", aliceMessageId != null) - Log.v("#E2E TEST", "Alice sent message to roomId: $e2eRoomID") + val aliceMessageId: String? = sendMessageInRoom(aliceRoomPOV, "Hello Bob, I am Alice!", testHelper) + Assert.assertTrue("Message should be sent", aliceMessageId != null) + Log.v("#E2E TEST", "Alice sent message to roomId: $e2eRoomID") - // Bob should be able to decrypt the message - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) - (timelineEvent != null && - timelineEvent.isEncrypted() && - timelineEvent.root.getClearType() == EventType.MESSAGE).also { - if (it) { - Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") - } - } - } - } - - // Create a new user - val arisSession = testHelper.createAccount("aris", SessionTestParams(true)) - Log.v("#E2E TEST", "Aris user created") - - // Alice invites new user to the room - testHelper.runBlockingTest { - Log.v("#E2E TEST", "Alice invites ${arisSession.myUserId}") - aliceRoomPOV.membershipService().invite(arisSession.myUserId) - } - - waitForAndAcceptInviteInRoom(arisSession, e2eRoomID, testHelper) - - ensureMembersHaveJoined(aliceSession, arrayListOf(arisSession), e2eRoomID, testHelper) - Log.v("#E2E TEST", "Aris has joined roomId: $e2eRoomID") - - when (roomHistoryVisibility) { - RoomHistoryVisibility.WORLD_READABLE, - RoomHistoryVisibility.SHARED, - null - -> { - // Aris should be able to decrypt the message + // Bob should be able to decrypt the message testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) + val timelineEvent = bobSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) (timelineEvent != null && timelineEvent.isEncrypted() && - timelineEvent.root.getClearType() == EventType.MESSAGE - ).also { - if (it) { - Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") - } - } + timelineEvent.root.getClearType() == EventType.MESSAGE).also { + if (it) { + Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } } } - } - RoomHistoryVisibility.INVITED, - RoomHistoryVisibility.JOINED -> { - // Aris should not even be able to get the message - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = arisSession.roomService().getRoom(e2eRoomID) - ?.timelineService() - ?.getTimelineEvent(aliceMessageId!!) - timelineEvent == null - } - } - } - } - testHelper.signOutAndClose(arisSession) - cryptoTestData.cleanUp(testHelper) - } + // Create a new user + val arisSession = testHelper.createAccount("aris", SessionTestParams(true)) + Log.v("#E2E TEST", "Aris user created") + + // Alice invites new user to the room + testHelper.runBlockingTest { + Log.v("#E2E TEST", "Alice invites ${arisSession.myUserId}") + aliceRoomPOV.membershipService().invite(arisSession.myUserId) + } + + waitForAndAcceptInviteInRoom(arisSession, e2eRoomID, testHelper) + + ensureMembersHaveJoined(aliceSession, arrayListOf(arisSession), e2eRoomID, testHelper) + Log.v("#E2E TEST", "Aris has joined roomId: $e2eRoomID") + + when (roomHistoryVisibility) { + RoomHistoryVisibility.WORLD_READABLE, + RoomHistoryVisibility.SHARED, + null + -> { + // Aris should be able to decrypt the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID)?.timelineService()?.getTimelineEvent(aliceMessageId!!) + (timelineEvent != null && + timelineEvent.isEncrypted() && + timelineEvent.root.getClearType() == EventType.MESSAGE + ).also { + if (it) { + Log.v("#E2E TEST", "Aris can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + } + } + } + } + } + RoomHistoryVisibility.INVITED, + RoomHistoryVisibility.JOINED -> { + // Aris should not even be able to get the message + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + val timelineEvent = arisSession.roomService().getRoom(e2eRoomID) + ?.timelineService() + ?.getTimelineEvent(aliceMessageId!!) + timelineEvent == null + } + } + } + } + + testHelper.signOutAndClose(arisSession) + cryptoTestData.cleanUp(testHelper) + } @Test fun testNeedsRotationFromWorldReadableToShared() { @@ -342,7 +342,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } when { - initRoomHistoryVisibility.shouldShareHistory() == nextRoomHistoryVisibility.historyVisibility?.shouldShareHistory() -> { + initRoomHistoryVisibility.shouldShareHistory() == nextRoomHistoryVisibility.historyVisibility?.shouldShareHistory() -> { assertEquals("Session shouldn't have been rotated", secondAliceMessageSessionId, aliceThirdMessageSessionId) Log.v("#E2E TEST ROTATION", "Rotation is not needed") } From 8e829c6aad97b344dbe37135eb34d1b60ff62579 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 15 Jun 2022 12:20:57 +0200 Subject: [PATCH 082/136] Add lab flag and more tests --- .../android/sdk/common/CommonTestHelper.kt | 60 +++- .../internal/crypto/E2EShareKeysConfigTest.kt | 298 ++++++++++++++++++ .../sdk/internal/crypto/E2eeSanityTests.kt | 45 +-- .../crypto/E2eeShareKeysHistoryTest.kt | 14 +- .../android/sdk/api/crypto/MXCryptoConfig.kt | 7 +- .../sdk/api/session/crypto/CryptoService.kt | 14 + .../internal/crypto/DefaultCryptoService.kt | 5 +- .../algorithms/megolm/MXMegolmDecryption.kt | 2 +- .../keysbackup/DefaultKeysBackupService.kt | 2 +- .../internal/crypto/store/IMXCryptoStore.kt | 14 + .../crypto/store/db/RealmCryptoStore.kt | 14 +- .../store/db/RealmCryptoStoreMigration.kt | 4 +- .../store/db/migration/MigrateCryptoTo018.kt | 36 +++ .../store/db/model/CryptoMetadataEntity.kt | 5 + .../android/sdk/internal/di/NetworkModule.kt | 6 +- .../membership/DefaultMembershipService.kt | 2 +- .../features/settings/VectorPreferences.kt | 2 + .../settings/VectorSettingsLabsFragment.kt | 11 + vector/src/main/res/values/strings.xml | 3 + .../src/main/res/xml/vector_settings_labs.xml | 7 + 20 files changed, 490 insertions(+), 61 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2EShareKeysConfigTest.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index 3ad8b281e9..a78953caac 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -18,12 +18,15 @@ package org.matrix.android.sdk.common import android.content.Context import android.net.Uri +import android.util.Log import androidx.lifecycle.Observer import androidx.test.internal.runner.junit4.statement.UiThreadStatement import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -38,7 +41,10 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationResult import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.room.Room +import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure +import org.matrix.android.sdk.api.session.room.model.Membership 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.timeline.Timeline @@ -47,6 +53,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.session.sync.SyncState import timber.log.Timber import java.util.UUID +import java.util.concurrent.CancellationException import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -241,6 +248,37 @@ class CommonTestHelper internal constructor(context: Context) { return sentEvents } + fun waitForAndAcceptInviteInRoom(otherSession: Session, roomID: String) { + waitWithLatch { latch -> + retryPeriodicallyWithLatch(latch) { + val roomSummary = otherSession.getRoomSummary(roomID) + (roomSummary != null && roomSummary.membership == Membership.INVITE).also { + if (it) { + Log.v("# TEST", "${otherSession.myUserId} can see the invite") + } + } + } + } + + // not sure why it's taking so long :/ + runBlockingTest(90_000) { + Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $roomID") + try { + otherSession.roomService().joinRoom(roomID) + } catch (ex: JoinRoomFailure.JoinedWithTimeout) { + // it's ok we will wait after + } + } + + Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...") + waitWithLatch { + retryPeriodicallyWithLatch(it) { + val roomSummary = otherSession.getRoomSummary(roomID) + roomSummary != null && roomSummary.membership == Membership.JOIN + } + } + } + /** * Reply in a thread * @param room the room where to send the messages @@ -285,6 +323,8 @@ class CommonTestHelper internal constructor(context: Context) { ) assertNotNull(session) return session.also { + // most of the test was created pre-MSC3061 so ensure compatibility + it.cryptoService().enableShareKeyOnInvite(false) trackedSessions.add(session) } } @@ -428,16 +468,26 @@ class CommonTestHelper internal constructor(context: Context) { * @param latch * @throws InterruptedException */ - fun await(latch: CountDownLatch, timeout: Long? = TestConstants.timeOutMillis) { + fun await(latch: CountDownLatch, timeout: Long? = TestConstants.timeOutMillis, job: Job? = null) { assertTrue( "Timed out after " + timeout + "ms waiting for something to happen. See stacktrace for cause.", - latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS) + latch.await(timeout ?: TestConstants.timeOutMillis, TimeUnit.MILLISECONDS).also { + if (!it) { + // cancel job on timeout + job?.cancel("Await timeout") + } + } ) } suspend fun retryPeriodicallyWithLatch(latch: CountDownLatch, condition: (() -> Boolean)) { while (true) { - delay(1000) + try { + delay(1000) + } catch (ex: CancellationException) { + // the job was canceled, just stop + return + } if (condition()) { latch.countDown() return @@ -447,10 +497,10 @@ class CommonTestHelper internal constructor(context: Context) { fun waitWithLatch(timeout: Long? = TestConstants.timeOutMillis, dispatcher: CoroutineDispatcher = Dispatchers.Main, block: suspend (CountDownLatch) -> Unit) { val latch = CountDownLatch(1) - coroutineScope.launch(dispatcher) { + val job = coroutineScope.launch(dispatcher) { block(latch) } - await(latch, timeout) + await(latch, timeout, job) } fun runBlockingTest(timeout: Long = TestConstants.timeOutMillis, block: suspend () -> T): T { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2EShareKeysConfigTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2EShareKeysConfigTest.kt new file mode 100644 index 0000000000..32d63a1934 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2EShareKeysConfigTest.kt @@ -0,0 +1,298 @@ +/* + * 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 android.util.Log +import androidx.test.filters.LargeTest +import org.amshove.kluent.internal.assertEquals +import org.junit.Assert +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.Session +import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersion +import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysVersionResult +import org.matrix.android.sdk.api.session.crypto.keysbackup.MegolmBackupCreationInfo +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.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent +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.SessionTestParams +import org.matrix.android.sdk.common.TestConstants +import org.matrix.android.sdk.common.TestMatrixCallback + +@RunWith(JUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +@LargeTest +class E2EShareKeysConfigTest : InstrumentedTest { + + @Test + fun msc3061ShouldBeDisabledByDefault() = runCryptoTest(context()) { _, commonTestHelper -> + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = false)) + Assert.assertFalse("MSC3061 is lab and should be disabled by default", aliceSession.cryptoService().isShareKeysOnInviteEnabled()) + } + + @Test + fun ensureKeysAreNotSharedIfOptionDisabled() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper -> + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true)) + aliceSession.cryptoService().enableShareKeyOnInvite(false) + val roomId = commonTestHelper.runBlockingTest { + aliceSession.roomService().createRoom(CreateRoomParams().apply { + historyVisibility = RoomHistoryVisibility.SHARED + name = "MyRoom" + enableEncryption() + }) + } + + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + aliceSession.roomService().getRoomSummary(roomId)?.isEncrypted == true + } + } + val roomAlice = aliceSession.roomService().getRoom(roomId)!! + + // send some messages + val withSession1 = commonTestHelper.sendTextMessage(roomAlice, "Hello", 1) + aliceSession.cryptoService().discardOutboundSession(roomId) + val withSession2 = commonTestHelper.sendTextMessage(roomAlice, "World", 1) + + // Create bob account + val bobSession = commonTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(withInitialSync = true)) + + // Let alice invite bob + commonTestHelper.runBlockingTest { + roomAlice.membershipService().invite(bobSession.myUserId) + } + + commonTestHelper.waitForAndAcceptInviteInRoom(bobSession, roomId) + + // Bob has join but should not be able to decrypt history + cryptoTestHelper.ensureCannotDecrypt( + withSession1.map { it.eventId } + withSession2.map { it.eventId }, + bobSession, + roomId + ) + + // We don't need bob anymore + commonTestHelper.signOutAndClose(bobSession) + + // Now let's enable history key sharing on alice side + aliceSession.cryptoService().enableShareKeyOnInvite(true) + + // let's add a new message first + val afterFlagOn = commonTestHelper.sendTextMessage(roomAlice, "After", 1) + + // Worth nothing to check that the session was rotated + Assert.assertNotEquals( + "Session should have been rotated", + withSession2.first().root.content?.get("session_id")!!, + afterFlagOn.first().root.content?.get("session_id")!! + ) + + // Invite a new user + val samSession = commonTestHelper.createAccount(TestConstants.USER_SAM, SessionTestParams(withInitialSync = true)) + + // Let alice invite sam + commonTestHelper.runBlockingTest { + roomAlice.membershipService().invite(samSession.myUserId) + } + + commonTestHelper.waitForAndAcceptInviteInRoom(samSession, roomId) + + // Sam shouldn't be able to decrypt messages with the first session, but should decrypt the one with 3rd session + cryptoTestHelper.ensureCannotDecrypt( + withSession1.map { it.eventId } + withSession2.map { it.eventId }, + samSession, + roomId + ) + + cryptoTestHelper.ensureCanDecrypt( + afterFlagOn.map { it.eventId }, + samSession, + roomId, + afterFlagOn.map { it.root.getClearContent()?.get("body") as String }) + } + + @Test + fun ifSharingDisabledOnAliceSideBobShouldNotShareAliceHistoty() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper -> + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(roomHistoryVisibility = RoomHistoryVisibility.SHARED) + val aliceSession = testData.firstSession.also { + it.cryptoService().enableShareKeyOnInvite(false) + } + val bobSession = testData.secondSession!!.also { + it.cryptoService().enableShareKeyOnInvite(true) + } + + val (fromAliceNotSharable, fromBobSharable, samSession) = commonAliceAndBobSendMessages(commonTestHelper, aliceSession, testData, bobSession) + + // Bob should have shared history keys to sam. + // But has alice hasn't enabled sharing, bob shouldn't send her sessions + cryptoTestHelper.ensureCannotDecrypt( + fromAliceNotSharable.map { it.eventId }, + samSession, + testData.roomId + ) + + cryptoTestHelper.ensureCanDecrypt( + fromBobSharable.map { it.eventId }, + samSession, + testData.roomId, + fromBobSharable.map { it.root.getClearContent()?.get("body") as String }) + } + + @Test + fun ifSharingEnabledOnAliceSideBobShouldShareAliceHistoty() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper -> + val testData = cryptoTestHelper.doE2ETestWithAliceAndBobInARoom(roomHistoryVisibility = RoomHistoryVisibility.SHARED) + val aliceSession = testData.firstSession.also { + it.cryptoService().enableShareKeyOnInvite(true) + } + val bobSession = testData.secondSession!!.also { + it.cryptoService().enableShareKeyOnInvite(true) + } + + val (fromAliceNotSharable, fromBobSharable, samSession) = commonAliceAndBobSendMessages(commonTestHelper, aliceSession, testData, bobSession) + + cryptoTestHelper.ensureCanDecrypt( + fromAliceNotSharable.map { it.eventId }, + samSession, + testData.roomId, + fromAliceNotSharable.map { it.root.getClearContent()?.get("body") as String }) + + cryptoTestHelper.ensureCanDecrypt( + fromBobSharable.map { it.eventId }, + samSession, + testData.roomId, + fromBobSharable.map { it.root.getClearContent()?.get("body") as String }) + } + + private fun commonAliceAndBobSendMessages(commonTestHelper: CommonTestHelper, aliceSession: Session, testData: CryptoTestData, bobSession: Session): Triple, List, Session> { + val fromAliceNotSharable = commonTestHelper.sendTextMessage(aliceSession.getRoom(testData.roomId)!!, "Hello from alice", 1) + val fromBobSharable = commonTestHelper.sendTextMessage(bobSession.getRoom(testData.roomId)!!, "Hello from bob", 1) + + // Now let bob invite Sam + // Invite a new user + val samSession = commonTestHelper.createAccount(TestConstants.USER_SAM, SessionTestParams(withInitialSync = true)) + + // Let bob invite sam + commonTestHelper.runBlockingTest { + bobSession.getRoom(testData.roomId)!!.membershipService().invite(samSession.myUserId) + } + + commonTestHelper.waitForAndAcceptInviteInRoom(samSession, testData.roomId) + return Triple(fromAliceNotSharable, fromBobSharable, samSession) + } + + // test flag on backup is correct + + @Test + fun testBackupFlagIsCorrect() = runCryptoTest(context()) { cryptoTestHelper, commonTestHelper -> + val aliceSession = commonTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(withInitialSync = true)) + aliceSession.cryptoService().enableShareKeyOnInvite(false) + val roomId = commonTestHelper.runBlockingTest { + aliceSession.roomService().createRoom(CreateRoomParams().apply { + historyVisibility = RoomHistoryVisibility.SHARED + name = "MyRoom" + enableEncryption() + }) + } + + commonTestHelper.waitWithLatch { latch -> + commonTestHelper.retryPeriodicallyWithLatch(latch) { + aliceSession.roomService().getRoomSummary(roomId)?.isEncrypted == true + } + } + val roomAlice = aliceSession.roomService().getRoom(roomId)!! + + // send some messages + val notSharableMessage = commonTestHelper.sendTextMessage(roomAlice, "Hello", 1) + aliceSession.cryptoService().enableShareKeyOnInvite(true) + val sharableMessage = commonTestHelper.sendTextMessage(roomAlice, "World", 1) + + Log.v("#E2E TEST", "Create and start key backup for bob ...") + val keysBackupService = aliceSession.cryptoService().keysBackupService() + val keyBackupPassword = "FooBarBaz" + val megolmBackupCreationInfo = commonTestHelper.doSync { + keysBackupService.prepareKeysBackupVersion(keyBackupPassword, null, it) + } + val version = commonTestHelper.doSync { + keysBackupService.createKeysBackupVersion(megolmBackupCreationInfo, it) + } + + commonTestHelper.waitWithLatch { latch -> + keysBackupService.backupAllGroupSessions( + null, + TestMatrixCallback(latch, true) + ) + } + + // signout + commonTestHelper.signOutAndClose(aliceSession) + + val newAliceSession = commonTestHelper.logIntoAccount(aliceSession.myUserId, SessionTestParams(true)) + newAliceSession.cryptoService().enableShareKeyOnInvite(true) + + newAliceSession.cryptoService().keysBackupService().let { kbs -> + val keyVersionResult = commonTestHelper.doSync { + kbs.getVersion(version.version, it) + } + + val importedResult = commonTestHelper.doSync { + kbs.restoreKeyBackupWithPassword( + keyVersionResult!!, + keyBackupPassword, + null, + null, + null, + it + ) + } + + assertEquals(2, importedResult.totalNumberOfKeys) + } + + // Now let's invite sam + // Invite a new user + val samSession = commonTestHelper.createAccount(TestConstants.USER_SAM, SessionTestParams(withInitialSync = true)) + + // Let alice invite sam + commonTestHelper.runBlockingTest { + newAliceSession.getRoom(roomId)!!.membershipService().invite(samSession.myUserId) + } + + commonTestHelper.waitForAndAcceptInviteInRoom(samSession, roomId) + + // Sam shouldn't be able to decrypt messages with the first session, but should decrypt the one with 3rd session + cryptoTestHelper.ensureCannotDecrypt( + notSharableMessage.map { it.eventId }, + samSession, + roomId + ) + + cryptoTestHelper.ensureCanDecrypt( + sharableMessage.map { it.eventId }, + samSession, + roomId, + sharableMessage.map { it.root.getClearContent()?.get("body") as String }) + } +} diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt index de4817f2b3..251c13ccbf 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeSanityTests.kt @@ -23,7 +23,6 @@ import org.amshove.kluent.fail import org.amshove.kluent.internal.assertEquals import org.junit.Assert import org.junit.FixMethodOrder -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -49,9 +48,7 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon 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.getRoom -import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.room.Room -import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure import org.matrix.android.sdk.api.session.room.getTimelineEvent import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.message.MessageContent @@ -67,10 +64,10 @@ import org.matrix.android.sdk.common.TestMatrixCallback import org.matrix.android.sdk.mustFail import java.util.concurrent.CountDownLatch +// @Ignore("This test fails with an unhandled exception thrown from a coroutine which terminates the entire test run.") @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) @LargeTest -//@Ignore("This test fails with an unhandled exception thrown from a coroutine which terminates the entire test run.") class E2eeSanityTests : InstrumentedTest { @get:Rule val rule = RetryTestRule(3) @@ -115,7 +112,7 @@ class E2eeSanityTests : InstrumentedTest { // All user should accept invite otherAccounts.forEach { otherSession -> - waitForAndAcceptInviteInRoom(testHelper, otherSession, e2eRoomID) + testHelper.waitForAndAcceptInviteInRoom(otherSession, e2eRoomID) Log.v("#E2E TEST", "${otherSession.myUserId} joined room $e2eRoomID") } @@ -156,7 +153,7 @@ class E2eeSanityTests : InstrumentedTest { } newAccount.forEach { - waitForAndAcceptInviteInRoom(testHelper, it, e2eRoomID) + testHelper.waitForAndAcceptInviteInRoom(it, e2eRoomID) } ensureMembersHaveJoined(testHelper, aliceSession, newAccount, e2eRoomID) @@ -166,7 +163,7 @@ class E2eeSanityTests : InstrumentedTest { delay(3_000) } - // Due to the new shared keys implementation, invited user should be able to decrypt messages + // check that messages are encrypted (uisi) newAccount.forEach { otherSession -> testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { @@ -174,7 +171,8 @@ class E2eeSanityTests : InstrumentedTest { Log.v("#E2E TEST", "Event seen by new user ${it?.root?.getClearType()}|${it?.root?.mCryptoError}") } timelineEvent != null && - timelineEvent.root.getClearType() == EventType.MESSAGE + timelineEvent.root.getClearType() == EventType.ENCRYPTED && + timelineEvent.root.mCryptoError == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID } } } @@ -739,37 +737,6 @@ class E2eeSanityTests : InstrumentedTest { } } - private fun waitForAndAcceptInviteInRoom(testHelper: CommonTestHelper, otherSession: Session, e2eRoomID: String) { - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val roomSummary = otherSession.getRoomSummary(e2eRoomID) - (roomSummary != null && roomSummary.membership == Membership.INVITE).also { - if (it) { - Log.v("#E2E TEST", "${otherSession.myUserId} can see the invite from alice") - } - } - } - } - - // not sure why it's taking so long :/ - testHelper.runBlockingTest(90_000) { - Log.v("#E2E TEST", "${otherSession.myUserId} tries to join room $e2eRoomID") - try { - otherSession.roomService().joinRoom(e2eRoomID) - } catch (ex: JoinRoomFailure.JoinedWithTimeout) { - // it's ok we will wait after - } - } - - Log.v("#E2E TEST", "${otherSession.myUserId} waiting for join echo ...") - testHelper.waitWithLatch { - testHelper.retryPeriodicallyWithLatch(it) { - val roomSummary = otherSession.getRoomSummary(e2eRoomID) - roomSummary != null && roomSummary.membership == Membership.JOIN - } - } - } - private fun ensureIsDecrypted(testHelper: CommonTestHelper, sentEventIds: List, session: Session, e2eRoomID: String) { testHelper.waitWithLatch { latch -> sentEventIds.forEach { sentEventId -> diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index c855c14a54..ee1ad73ed8 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -85,12 +85,16 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { val e2eRoomID = cryptoTestData.roomId // Alice - val aliceSession = cryptoTestData.firstSession + val aliceSession = cryptoTestData.firstSession.also { + it.cryptoService().enableShareKeyOnInvite(true) + } val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!! // Bob - val bobSession = cryptoTestData.secondSession - val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!! + val bobSession = cryptoTestData.secondSession!!.also { + it.cryptoService().enableShareKeyOnInvite(true) + } + val bobRoomPOV = bobSession.roomService().getRoom(e2eRoomID)!! assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) Log.v("#E2E TEST", "Alice and Bob are in roomId: $e2eRoomID") @@ -114,7 +118,9 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } // Create a new user - val arisSession = testHelper.createAccount("aris", SessionTestParams(true)) + val arisSession = testHelper.createAccount("aris", SessionTestParams(true)).also { + it.cryptoService().enableShareKeyOnInvite(true) + } Log.v("#E2E TEST", "Aris user created") // Alice invites new user to the room diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt index ea0c413546..015cb6a1a2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/crypto/MXCryptoConfig.kt @@ -39,9 +39,4 @@ data class MXCryptoConfig constructor( */ val limitRoomKeyRequestsToMyDevices: Boolean = false, - /** - * Flag that indicates whether or not key history will be shared to invited - * users with respect to room visibility - */ - val shouldShareKeyHistory: Boolean = true, -) + ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index 6b52cff512..112382c4e9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -85,6 +85,20 @@ interface CryptoService { fun isKeyGossipingEnabled(): Boolean + /** + * As per MSC3061. + * If true will make it possible to share part of e2ee room history + * on invite depending on the room visibility setting. + */ + fun enableShareKeyOnInvite(enable: Boolean) + + /** + * As per MSC3061. + * If true will make it possible to share part of e2ee room history + * on invite depending on the room visibility setting. + */ + fun isShareKeysOnInviteEnabled(): Boolean + fun setRoomUnBlacklistUnverifiedDevices(roomId: String) fun getDeviceTrackingStatus(userId: String): Int diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index b00b4f6173..850a4379ca 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -110,7 +110,6 @@ import org.matrix.olm.OlmManager import timber.log.Timber import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject -import kotlin.coroutines.coroutineContext import kotlin.math.max /** @@ -1118,6 +1117,10 @@ internal class DefaultCryptoService @Inject constructor( override fun isKeyGossipingEnabled() = cryptoStore.isKeyGossipingEnabled() + override fun isShareKeysOnInviteEnabled() = cryptoStore.isShareKeysOnInviteEnabled() + + override fun enableShareKeyOnInvite(enable: Boolean) = cryptoStore.enableShareKeyOnInvite(enable) + /** * Tells whether the client should ever send encrypted messages to unverified devices. * The default value is false. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 479c2654ed..d884293ae1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -303,7 +303,7 @@ internal class MXMegolmDecryption( * Returns boolean shared key flag, if enabled with respect to matrix configuration */ private fun RoomKeyContent.getSharedKey(): Boolean { - if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return false + if (!cryptoStore.isShareKeysOnInviteEnabled()) return false return sharedHistory ?: false } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index f8b00007bb..2e52325e73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -1472,7 +1472,7 @@ internal class DefaultKeysBackupService @Inject constructor( * Returns boolean shared key flag, if enabled with respect to matrix configuration */ private fun MXInboundMegolmSessionWrapper.getSharedKey(): Boolean { - if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return false + if (!cryptoStore.isShareKeysOnInviteEnabled()) return false return sessionData.sharedHistory } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index d315e0310a..3a07149b58 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -98,6 +98,20 @@ internal interface IMXCryptoStore { fun isKeyGossipingEnabled(): Boolean + /** + * As per MSC3061. + * If true will make it possible to share part of e2ee room history + * on invite depending on the room visibility setting. + */ + fun enableShareKeyOnInvite(enable: Boolean) + + /** + * As per MSC3061. + * If true will make it possible to share part of e2ee room history + * on invite depending on the room visibility setting. + */ + fun isShareKeysOnInviteEnabled(): Boolean + /** * Provides the rooms ids list in which the messages are not encrypted for the unverified devices. * diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 15f95f01cb..0a59820261 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -660,7 +660,7 @@ internal class RealmCryptoStore @Inject constructor( } override fun shouldShareHistory(roomId: String): Boolean { - if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return false + if (!isShareKeysOnInviteEnabled()) return false return doWithRealm(realmConfiguration) { CryptoRoomEntity.getById(it, roomId)?.shouldShareHistory } @@ -1009,6 +1009,18 @@ internal class RealmCryptoStore @Inject constructor( } ?: false } + override fun isShareKeysOnInviteEnabled(): Boolean { + return doWithRealm(realmConfiguration) { + it.where().findFirst()?.enableKeyForwardingOnInvite + } ?: false + } + + override fun enableShareKeyOnInvite(enable: Boolean) { + doRealmTransaction(realmConfiguration) { + it.where().findFirst()?.enableKeyForwardingOnInvite = enable + } + } + override fun setDeviceKeysUploaded(uploaded: Boolean) { doRealmTransaction(realmConfiguration) { it.where().findFirst()?.deviceKeysSentToServer = uploaded diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 4ca9d44f98..56222ab88e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017 +import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018 import org.matrix.android.sdk.internal.util.time.Clock import timber.log.Timber import javax.inject.Inject @@ -52,7 +53,7 @@ internal class RealmCryptoStoreMigration @Inject constructor( // 0, 1, 2: legacy Riot-Android // 3: migrate to RiotX schema // 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6) - val schemaVersion = 17L + val schemaVersion = 18L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.d("Migrating Realm Crypto from $oldVersion to $newVersion") @@ -74,5 +75,6 @@ internal class RealmCryptoStoreMigration @Inject constructor( if (oldVersion < 15) MigrateCryptoTo015(realm).perform() if (oldVersion < 16) MigrateCryptoTo016(realm).perform() if (oldVersion < 17) MigrateCryptoTo017(realm).perform() + if (oldVersion < 18) MigrateCryptoTo018(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt new file mode 100644 index 0000000000..d0989bc7d5 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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.store.db.migration + +import io.realm.DynamicRealm +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields +import org.matrix.android.sdk.internal.util.database.RealmMigrator + +/** + * Support for MSC3061 (share room keys for past messages as a flag) + */ +internal class MigrateCryptoTo018(realm: DynamicRealm) : RealmMigrator(realm, 18) { + + override fun doMigrate(realm: DynamicRealm) { + realm.schema.get("CryptoMetadataEntity") + ?.addField(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, Boolean::class.java) + ?.transform { obj -> + // default to false + obj.setBoolean(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, false) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt index 63ed0e537e..88708f824e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt @@ -35,6 +35,11 @@ internal open class CryptoMetadataEntity( var globalBlacklistUnverifiedDevices: Boolean = false, // setting to enable or disable key gossiping var globalEnableKeyGossiping: Boolean = true, + + // MSC3061: Sharing room keys for past messages + // If set to true key history will be shared to invited users with respect to room setting + var enableKeyForwardingOnInvite: Boolean = false, + // The keys backup version currently used. Null means no backup. var backupVersion: String? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt index b5b46a3f5a..113e780e5c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt @@ -21,6 +21,7 @@ import com.squareup.moshi.Moshi import dagger.Module import dagger.Provides import okhttp3.ConnectionSpec +import okhttp3.Dispatcher import okhttp3.OkHttpClient import okhttp3.Protocol import okhttp3.logging.HttpLoggingInterceptor @@ -73,7 +74,9 @@ internal object NetworkModule { apiInterceptor: ApiInterceptor ): OkHttpClient { val spec = ConnectionSpec.Builder(matrixConfiguration.connectionSpec).build() - + val dispatcher = Dispatcher().apply { + maxRequestsPerHost = 20 + } return OkHttpClient.Builder() // workaround for #4669 .protocols(listOf(Protocol.HTTP_1_1)) @@ -94,6 +97,7 @@ internal object NetworkModule { addInterceptor(curlLoggingInterceptor) } } + .dispatcher(dispatcher) .connectionSpecs(Collections.singletonList(spec)) .applyMatrixConfiguration(matrixConfiguration) .build() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 331119530e..20708b3814 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -152,7 +152,7 @@ internal class DefaultMembershipService @AssistedInject constructor( } private suspend fun sendShareHistoryKeysIfNeeded(userId: String) { - if (!matrixConfiguration.cryptoConfig.shouldShareKeyHistory) return + if (!cryptoService.isShareKeysOnInviteEnabled()) return // TODO not sure it's the right way to get the latest messages in a room val sessionInfo = Realm.getInstance(monarchy.realmConfiguration).use { ChunkEntity.findLatestSessionInfo(it, roomId) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 6d91dc3348..ea039f0ed8 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -168,6 +168,8 @@ class VectorPreferences @Inject constructor( private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY" private const val SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY = "SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY" + const val SETTINGS_LABS_MSC3061_SHARE_KEYS_HISTORY = "SETTINGS_LABS_MSC3061_SHARE_KEYS_HISTORY" + // SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS private const val SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM = "SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM" const val SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB = "SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB" diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt index 3b9fdc5e55..1ed045757b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.text.method.LinkMovementMethod import android.widget.TextView import androidx.preference.Preference +import androidx.preference.SwitchPreference import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.preference.VectorSwitchPreference @@ -57,6 +58,16 @@ class VectorSettingsLabsFragment @Inject constructor( false } } + + findPreference(VectorPreferences.SETTINGS_LABS_MSC3061_SHARE_KEYS_HISTORY)?.let { pref -> + // ensure correct default + pref.isChecked = session.cryptoService().isShareKeysOnInviteEnabled() + + pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { + session.cryptoService().enableShareKeyOnInvite(pref.isChecked) + true + } + } } /** diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 933f3f0602..bc1b59ebd4 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2958,6 +2958,9 @@ Enable LaTeX mathematics Restart the application for the change to take effect. + MSC3061: Sharing room keys for past messages + When inviting in an encrypted room that is sharing history, encrypted history will be visible. + Create Poll Poll question or topic diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml index 9b9293e2e2..555470e643 100644 --- a/vector/src/main/res/xml/vector_settings_labs.xml +++ b/vector/src/main/res/xml/vector_settings_labs.xml @@ -45,6 +45,13 @@ android:key="SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB" android:title="@string/labs_show_unread_notifications_as_tab" /> + + Date: Wed, 15 Jun 2022 15:35:42 +0200 Subject: [PATCH 083/136] kdoc --- .../sdk/api/session/crypto/CryptoService.kt | 2 +- .../crypto/model/ForwardedRoomKeyContent.kt | 3 +- .../events/model/content/RoomKeyContent.kt | 3 +- .../room/model/RoomHistoryVisibility.kt | 2 +- .../sdk/internal/crypto/MXOlmDevice.kt | 1 + .../sdk/internal/crypto/MegolmSessionData.kt | 2 +- .../algorithms/megolm/MXMegolmDecryption.kt | 4 +-- .../keysbackup/DefaultKeysBackupService.kt | 2 +- .../keysbackup/model/rest/KeyBackupData.kt | 2 +- .../crypto/model/InboundGroupSessionData.kt | 4 +-- .../model/MXInboundMegolmSessionWrapper.kt | 2 +- .../model/OlmInboundGroupSessionWrapper2.kt | 2 +- .../model/OutboundGroupSessionWrapper.kt | 2 +- .../internal/crypto/store/IMXCryptoStore.kt | 8 ++--- .../store/db/migration/MigrateCryptoTo018.kt | 36 ------------------- 15 files changed, 19 insertions(+), 56 deletions(-) delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index 112382c4e9..a5e05f69e0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -193,7 +193,7 @@ interface CryptoService { fun prepareToEncrypt(roomId: String, callback: MatrixCallback) /** - * Share all inbound sessions of the last chunk messages to the provided userId devices + * Share all inbound sessions of the last chunk messages to the provided userId devices. */ suspend fun sendSharedHistoryKeys(roomId: String, userId: String, sessionInfoSet: Set?) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt index dbee04de88..664cd00e94 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/model/ForwardedRoomKeyContent.kt @@ -72,8 +72,7 @@ data class ForwardedRoomKeyContent( val senderClaimedEd25519Key: String? = null, /** - * MSC3061 - * Identifies keys that were sent when the room's visibility setting was set to world_readable or shared + * MSC3061 Identifies keys that were sent when the room's visibility setting was set to world_readable or shared. */ @Json(name = "org.matrix.msc3061.shared_history") val sharedHistory: Boolean? = false, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt index 75162f8ace..5b18d29ea0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/content/RoomKeyContent.kt @@ -41,8 +41,7 @@ data class RoomKeyContent( val chainIndex: Any? = null, /** - * MSC3061 - * Identifies keys that were sent when the room's visibility setting was set to world_readable or shared + * MSC3061 Identifies keys that were sent when the room's visibility setting was set to world_readable or shared. */ @Json(name = "org.matrix.msc3061.shared_history") val sharedHistory: Boolean? = false diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt index 3648d6b883..2b0ea1d8fb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomHistoryVisibility.kt @@ -50,7 +50,7 @@ enum class RoomHistoryVisibility { } /** - * Room history should be shared only if room visibility is world_readable or shared + * Room history should be shared only if room visibility is world_readable or shared. */ internal fun RoomHistoryVisibility.shouldShareHistory() = this == RoomHistoryVisibility.WORLD_READABLE || this == RoomHistoryVisibility.SHARED diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index 192df3ff0c..c4a6488258 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -601,6 +601,7 @@ internal class MXOlmDevice @Inject constructor( * @param forwardingCurve25519KeyChain Devices involved in forwarding this session to us. * @param keysClaimed Other keys the sender claims. * @param exportFormat true if the megolm keys are in export format + * @param sharedHistory MSC3061, this key is sharable on invite * @return true if the operation succeeds. */ fun addInboundGroupSession(sessionId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt index 43b88c0b42..ca0bdc8a0e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MegolmSessionData.kt @@ -73,7 +73,7 @@ internal data class MegolmSessionData( /** * Flag that indicates whether or not the current inboundSession will be shared to - * invited users to decrypt past messages + * invited users to decrypt past messages. */ // When this feature lands in spec name = shared_history should be used @Json(name = "org.matrix.msc3061.shared_history") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index d884293ae1..410b74e19f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -300,7 +300,7 @@ internal class MXMegolmDecryption( } /** - * Returns boolean shared key flag, if enabled with respect to matrix configuration + * Returns boolean shared key flag, if enabled with respect to matrix configuration. */ private fun RoomKeyContent.getSharedKey(): Boolean { if (!cryptoStore.isShareKeysOnInviteEnabled()) return false @@ -308,7 +308,7 @@ internal class MXMegolmDecryption( } /** - * Check if the some messages can be decrypted with a new session + * Check if the some messages can be decrypted with a new session. * * @param roomId the room id where the new Megolm session has been created for, may be null when importing from external sessions * @param senderKey the session sender key diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 2e52325e73..49cf60d051 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -1469,7 +1469,7 @@ internal class DefaultKeysBackupService @Inject constructor( } /** - * Returns boolean shared key flag, if enabled with respect to matrix configuration + * Returns boolean shared key flag, if enabled with respect to matrix configuration. */ private fun MXInboundMegolmSessionWrapper.getSharedKey(): Boolean { if (!cryptoStore.isShareKeysOnInviteEnabled()) return false diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt index 675bf12248..1817b18e2a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/rest/KeyBackupData.kt @@ -54,7 +54,7 @@ internal data class KeyBackupData( /** * Flag that indicates whether or not the current inboundSession will be shared to - * invited users to decrypt past messages + * invited users to decrypt past messages. */ @Json(name = "org.matrix.msc3061.shared_history") val sharedHistory: Boolean = false diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt index 0e60791282..2ce36aa209 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/InboundGroupSessionData.kt @@ -43,9 +43,9 @@ data class InboundGroupSessionData( /** * Flag that indicates whether or not the current inboundSession will be shared to - *invited users to decrypt past messages + * invited users to decrypt past messages. */ @Json(name = "shared_history") val sharedHistory: Boolean = false, -) + ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt index 0820b1c207..2772b34835 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/MXInboundMegolmSessionWrapper.kt @@ -34,7 +34,7 @@ data class MXInboundMegolmSessionWrapper( val safeSessionId = tryOrNull("Fail to get megolm session Id") { session.sessionIdentifier() } /** - * Export the inbound group session keys + * Export the inbound group session keys. * @param index the index to export. If null, the first known index will be used * @return the inbound group session as MegolmSessionData if the operation succeeds */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt index f19275a8a1..4b37e97e0d 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt @@ -107,8 +107,8 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { /** * Export the inbound group session keys. - * @param index the index to export. If null, the first known index will be used * @param sharedHistory the flag that indicates whether or not the session can be shared + * @param index the index to export. If null, the first known index will be used * @return the inbound group session as MegolmSessionData if the operation succeeds */ fun exportKeys(sharedHistory: Boolean = false, index: Long? = null): MegolmSessionData? { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt index e02953c9e0..5a6d1f4bc1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OutboundGroupSessionWrapper.kt @@ -22,7 +22,7 @@ internal data class OutboundGroupSessionWrapper( val outboundGroupSession: OlmOutboundGroupSession, val creationTime: Long, /** - * As per MSC 3061, declares if this key could be shared when inviting a new user to the room + * As per MSC 3061, declares if this key could be shared when inviting a new user to the room. */ val sharedHistory: Boolean = false ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 3a07149b58..0413fc730c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -67,7 +67,7 @@ internal interface IMXCryptoStore { fun getInboundGroupSessions(): List /** - * Retrieve the known inbound group sessions for the specified room + * Retrieve the known inbound group sessions for the specified room. * * @param roomId The roomId that the sessions will be returned * @return the list of all known group sessions, for the provided roomId @@ -276,7 +276,7 @@ internal interface IMXCryptoStore { /** * Sets a boolean flag that will determine whether or not room history (existing inbound sessions) - * will be shared to new user invites + * will be shared to new user invites. * * @param roomId the room id * @param shouldShareHistory The boolean flag @@ -345,7 +345,7 @@ internal interface IMXCryptoStore { fun getInboundGroupSession(sessionId: String, senderKey: String, sharedHistory: Boolean): MXInboundMegolmSessionWrapper? /** - * Get the current outbound group session for this encrypted room + * Get the current outbound group session for this encrypted room. */ fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper? @@ -355,7 +355,7 @@ internal interface IMXCryptoStore { fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?) /** - * Remove an inbound group session + * Remove an inbound group session. * * @param sessionId the session identifier. * @param senderKey the base64-encoded curve25519 key of the sender. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt deleted file mode 100644 index d0989bc7d5..0000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo018.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 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.store.db.migration - -import io.realm.DynamicRealm -import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields -import org.matrix.android.sdk.internal.util.database.RealmMigrator - -/** - * Support for MSC3061 (share room keys for past messages as a flag) - */ -internal class MigrateCryptoTo018(realm: DynamicRealm) : RealmMigrator(realm, 18) { - - override fun doMigrate(realm: DynamicRealm) { - realm.schema.get("CryptoMetadataEntity") - ?.addField(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, Boolean::class.java) - ?.transform { obj -> - // default to false - obj.setBoolean(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, false) - } - } -} From b0907de582b893299c9eeaeaf746e0b0358d09ec Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Jun 2022 09:14:38 +0200 Subject: [PATCH 084/136] Fix migration --- .../crypto/store/db/RealmCryptoStore.kt | 2 -- .../store/db/RealmCryptoStoreMigration.kt | 4 +--- .../store/db/migration/MigrateCryptoTo017.kt | 18 +++++++++++++++--- .../settings/VectorSettingsLabsFragment.kt | 1 + 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 0a59820261..20ca357d1a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -26,7 +26,6 @@ import io.realm.RealmConfiguration import io.realm.Sort import io.realm.kotlin.createObject import io.realm.kotlin.where -import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.logger.LoggerTag @@ -112,7 +111,6 @@ internal class RealmCryptoStore @Inject constructor( private val crossSigningKeysMapper: CrossSigningKeysMapper, @UserId private val userId: String, @DeviceId private val deviceId: String?, - private val matrixConfiguration: MatrixConfiguration, private val clock: Clock, ) : IMXCryptoStore { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 56222ab88e..4ca9d44f98 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -35,7 +35,6 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017 -import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018 import org.matrix.android.sdk.internal.util.time.Clock import timber.log.Timber import javax.inject.Inject @@ -53,7 +52,7 @@ internal class RealmCryptoStoreMigration @Inject constructor( // 0, 1, 2: legacy Riot-Android // 3: migrate to RiotX schema // 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6) - val schemaVersion = 18L + val schemaVersion = 17L override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) { Timber.d("Migrating Realm Crypto from $oldVersion to $newVersion") @@ -75,6 +74,5 @@ internal class RealmCryptoStoreMigration @Inject constructor( if (oldVersion < 15) MigrateCryptoTo015(realm).perform() if (oldVersion < 16) MigrateCryptoTo016(realm).perform() if (oldVersion < 17) MigrateCryptoTo017(realm).perform() - if (oldVersion < 18) MigrateCryptoTo018(realm).perform() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt index 2e847ab28e..afb0e0186d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt @@ -20,6 +20,7 @@ import io.realm.DynamicRealm import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm @@ -28,14 +29,25 @@ import org.matrix.android.sdk.internal.util.database.RealmMigrator import timber.log.Timber /** - * Version 17L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061 - * Also migrates how megolm session are stored to avoid additional serialized frozen class + * Version 17L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061. + * Also migrates how megolm session are stored to avoid additional serialized frozen class. */ internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 17) { override fun doMigrate(realm: DynamicRealm) { realm.schema.get("CryptoRoomEntity") - ?.addField(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java) + ?.addField(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java)?.transform { + // We don't have access to the session database to check for the state here and set the good value. + // But for now as it's behind a lab flag, will set to false and force initial sync when enabled + it.setBoolean(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, false) + } + + realm.schema.get("CryptoMetadataEntity") + ?.addField(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, Boolean::class.java) + ?.transform { obj -> + // default to false + obj.setBoolean(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, false) + } val moshiAdapter = MoshiProvider.providesMoshi().adapter(InboundGroupSessionData::class.java) diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt index 1ed045757b..70908d7560 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsLabsFragment.kt @@ -65,6 +65,7 @@ class VectorSettingsLabsFragment @Inject constructor( pref.onPreferenceClickListener = Preference.OnPreferenceClickListener { session.cryptoService().enableShareKeyOnInvite(pref.isChecked) + MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = true)) true } } From a885ff5e479315afd239ffe847d981fc54f90d6d Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Jun 2022 11:09:31 +0200 Subject: [PATCH 085/136] Fix test --- .../sdk/internal/crypto/CryptoStoreHelper.kt | 6 --- .../crypto/E2eeShareKeysHistoryTest.kt | 49 +++++++++++++------ 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt index d8890e2b38..ba1afd4758 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt @@ -17,8 +17,6 @@ package org.matrix.android.sdk.internal.crypto import io.realm.RealmConfiguration -import org.matrix.android.sdk.api.MatrixConfiguration -import org.matrix.android.sdk.common.TestRoomDisplayNameFallbackProvider import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule @@ -38,10 +36,6 @@ internal class CryptoStoreHelper { crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi()), userId = "userId_" + Random.nextInt(), deviceId = "deviceId_sample", - matrixConfiguration = MatrixConfiguration( - applicationFlavor = "TestFlavor", - roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider() - ), clock = DefaultClock(), ) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index ee1ad73ed8..4dcbe9f2b4 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -27,6 +27,7 @@ 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.query.QueryStringValue import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent @@ -45,7 +46,6 @@ 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.SessionTestParams -import org.matrix.android.sdk.common.TestConstants @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) @@ -249,13 +249,16 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { val e2eRoomID = cryptoTestData.roomId // Alice - val aliceSession = cryptoTestData.firstSession + val aliceSession = cryptoTestData.firstSession.also { + it.cryptoService().enableShareKeyOnInvite(true) + } val aliceRoomPOV = aliceSession.roomService().getRoom(e2eRoomID)!! // val aliceCryptoStore = (aliceSession.cryptoService() as DefaultCryptoService).cryptoStoreForTesting // Bob - val bobSession = cryptoTestData.secondSession - val bobRoomPOV = bobSession!!.roomService().getRoom(e2eRoomID)!! + val bobSession = cryptoTestData.secondSession!! + + val bobRoomPOV = bobSession.roomService().getRoom(e2eRoomID)!! assertEquals(bobRoomPOV.roomSummary()?.joinedMembersCount, 2) Log.v("#E2E TEST ROTATION", "Alice and Bob are in roomId: $e2eRoomID") @@ -266,9 +269,10 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { // Bob should be able to decrypt the message var firstAliceMessageMegolmSessionId: String? = null + val bobRoomPov = bobSession.roomService().getRoom(e2eRoomID) testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID) + val timelineEvent = bobRoomPov ?.timelineService() ?.getTimelineEvent(aliceMessageId!!) (timelineEvent != null && @@ -276,7 +280,10 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { timelineEvent.root.getClearType() == EventType.MESSAGE).also { if (it) { firstAliceMessageMegolmSessionId = timelineEvent?.root?.content?.get("session_id") as? String - Log.v("#E2E TEST", "Bob can decrypt the message: ${timelineEvent?.root?.getDecryptedTextSummary()}") + Log.v( + "#E2E TEST", + "Bob can decrypt the message (sid:$firstAliceMessageMegolmSessionId): ${timelineEvent?.root?.getDecryptedTextSummary()}" + ) } } } @@ -288,7 +295,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { sendMessageInRoom(aliceRoomPOV, "Other msg", testHelper)?.let { secondMessage -> testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID) + val timelineEvent = bobRoomPov ?.timelineService() ?.getTimelineEvent(secondMessage) (timelineEvent != null && @@ -296,6 +303,10 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { timelineEvent.root.getClearType() == EventType.MESSAGE).also { if (it) { secondAliceMessageSessionId = timelineEvent?.root?.content?.get("session_id") as? String + Log.v( + "#E2E TEST", + "Bob can decrypt the message (sid:$secondAliceMessageSessionId): ${timelineEvent?.root?.getDecryptedTextSummary()}" + ) } } } @@ -305,7 +316,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { Log.v("#E2E TEST ROTATION", "No rotation needed yet") // Let's change the room history visibility - testHelper.waitWithLatch { + testHelper.runBlockingTest { aliceRoomPOV.stateService() .sendStateEvent( eventType = EventType.STATE_ROOM_HISTORY_VISIBILITY, @@ -314,7 +325,14 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { historyVisibilityStr = nextRoomHistoryVisibility.historyVisibilityStr ).toContent() ) - it.countDown() + } + + // ensure that the state did synced down + testHelper.waitWithLatch { latch -> + testHelper.retryPeriodicallyWithLatch(latch) { + aliceRoomPOV.stateService().getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty)?.content + ?.toModel()?.historyVisibility == nextRoomHistoryVisibility.historyVisibility + } } testHelper.waitWithLatch { latch -> @@ -333,7 +351,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { sendMessageInRoom(aliceRoomPOV, "Message after visibility change", testHelper)?.let { thirdMessage -> testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { - val timelineEvent = bobSession.roomService().getRoom(e2eRoomID) + val timelineEvent = bobRoomPov ?.timelineService() ?.getTimelineEvent(thirdMessage) (timelineEvent != null && @@ -362,14 +380,14 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } private fun sendMessageInRoom(aliceRoomPOV: Room, text: String, testHelper: CommonTestHelper): String? { + val timeline = aliceRoomPOV.timelineService().createTimeline(null, TimelineSettings(60)) + timeline.start() aliceRoomPOV.sendService().sendTextMessage(text) var sentEventId: String? = null - testHelper.waitWithLatch(4 * TestConstants.timeOutMillis) { latch -> - val timeline = aliceRoomPOV.timelineService().createTimeline(null, TimelineSettings(60)) - timeline.start() + testHelper.waitWithLatch { latch -> testHelper.retryPeriodicallyWithLatch(latch) { val decryptedMsg = timeline.getSnapshot() - .filter { it.root.getClearType() == EventType.MESSAGE } + .filter { it.root.isEncrypted() || it.root.getClearType() == EventType.MESSAGE } .also { list -> val message = list.joinToString(",", "[", "]") { "${it.root.type}|${it.root.sendState}" } Log.v("#E2E TEST", "Timeline snapshot is $message") @@ -379,9 +397,8 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { sentEventId = decryptedMsg?.eventId decryptedMsg != null } - - timeline.dispose() } + timeline.dispose() return sentEventId } From 5a67c39c7f85270731352fedd50993127acf1d43 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Jun 2022 11:54:39 +0200 Subject: [PATCH 086/136] reuse code for test --- .../crypto/E2eeShareKeysHistoryTest.kt | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index 4dcbe9f2b4..933de69c93 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -38,10 +38,7 @@ import org.matrix.android.sdk.api.session.room.failure.JoinRoomFailure import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent -import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.shouldShareHistory -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.common.CommonTestHelper import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest import org.matrix.android.sdk.common.CryptoTestHelper @@ -380,26 +377,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { } private fun sendMessageInRoom(aliceRoomPOV: Room, text: String, testHelper: CommonTestHelper): String? { - val timeline = aliceRoomPOV.timelineService().createTimeline(null, TimelineSettings(60)) - timeline.start() - aliceRoomPOV.sendService().sendTextMessage(text) - var sentEventId: String? = null - testHelper.waitWithLatch { latch -> - testHelper.retryPeriodicallyWithLatch(latch) { - val decryptedMsg = timeline.getSnapshot() - .filter { it.root.isEncrypted() || it.root.getClearType() == EventType.MESSAGE } - .also { list -> - val message = list.joinToString(",", "[", "]") { "${it.root.type}|${it.root.sendState}" } - Log.v("#E2E TEST", "Timeline snapshot is $message") - } - .filter { it.root.sendState == SendState.SYNCED } - .firstOrNull { it.root.getClearContent().toModel()?.body?.startsWith(text) == true } - sentEventId = decryptedMsg?.eventId - decryptedMsg != null - } - } - timeline.dispose() - return sentEventId + return testHelper.sendTextMessage(aliceRoomPOV, text, 1).firstOrNull()?.eventId } private fun ensureMembersHaveJoined(aliceSession: Session, otherAccounts: List, e2eRoomID: String, testHelper: CommonTestHelper) { From e7322e852448b9aa18da90d9504e5f4c91348278 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 16 Jun 2022 12:22:16 +0200 Subject: [PATCH 087/136] outdated configuration --- .../sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index 4d213dfd9a..48a25f2a8b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -247,7 +247,7 @@ internal class MXMegolmEncryption( /** * Share the device keys of a an user. * - * @param session the session info + * @param sessionInfo the session info * @param devicesByUser the devices map */ private suspend fun shareUserDevicesKey(sessionInfo: MXOutboundSessionInfo, From fb5f0cbd004d56791896a5bde00ae09fc6e0b62d Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 28 Jun 2022 16:09:52 +0200 Subject: [PATCH 088/136] Fix test compilation --- .../android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt index 933de69c93..32a95008b1 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/E2eeShareKeysHistoryTest.kt @@ -336,7 +336,7 @@ class E2eeShareKeysHistoryTest : InstrumentedTest { testHelper.retryPeriodicallyWithLatch(latch) { val roomVisibility = aliceSession.getRoom(e2eRoomID)!! .stateService() - .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY) + .getStateEvent(EventType.STATE_ROOM_HISTORY_VISIBILITY, QueryStringValue.IsEmpty) ?.content ?.toModel() Log.v("#E2E TEST ROTATION", "Room visibility changed from: ${initRoomHistoryVisibility.name} to: ${roomVisibility?.historyVisibility?.name}") From 08cb6de83d917566fd6db059ad1ac2e39e180a23 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 1 Jul 2022 11:08:35 +0200 Subject: [PATCH 089/136] Fix migration --- .../crypto/model/OlmInboundGroupSessionWrapper2.kt | 7 +++---- .../crypto/store/db/migration/MigrateCryptoTo017.kt | 8 ++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt index 4b37e97e0d..600fcb1003 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/OlmInboundGroupSessionWrapper2.kt @@ -107,11 +107,11 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { /** * Export the inbound group session keys. - * @param sharedHistory the flag that indicates whether or not the session can be shared * @param index the index to export. If null, the first known index will be used + * * @return the inbound group session as MegolmSessionData if the operation succeeds */ - fun exportKeys(sharedHistory: Boolean = false, index: Long? = null): MegolmSessionData? { + fun exportKeys(index: Long? = null): MegolmSessionData? { return try { if (null == forwardingCurve25519KeyChain) { forwardingCurve25519KeyChain = ArrayList() @@ -133,8 +133,7 @@ internal class OlmInboundGroupSessionWrapper2 : Serializable { roomId = roomId, sessionId = safeOlmInboundGroupSession.sessionIdentifier(), sessionKey = safeOlmInboundGroupSession.export(wantedIndex), - algorithm = MXCRYPTO_ALGORITHM_MEGOLM, - sharedHistory = sharedHistory + algorithm = MXCRYPTO_ALGORITHM_MEGOLM ) } catch (e: Exception) { Timber.e(e, "## export() : senderKey $senderKey failed") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt index afb0e0186d..8904c412cd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/migration/MigrateCryptoTo017.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields +import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntityFields import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.util.database.RealmMigrator @@ -42,6 +43,13 @@ internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 17 it.setBoolean(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, false) } + realm.schema.get("OutboundGroupSessionInfoEntity") + ?.addField(OutboundGroupSessionInfoEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java)?.transform { + // We don't have access to the session database to check for the state here and set the good value. + // But for now as it's behind a lab flag, will set to false and force initial sync when enabled + it.setBoolean(OutboundGroupSessionInfoEntityFields.SHOULD_SHARE_HISTORY, false) + } + realm.schema.get("CryptoMetadataEntity") ?.addField(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, Boolean::class.java) ?.transform { obj -> From 28ca03cc6786503060cdaa955a454fe87100c613 Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 1 Jul 2022 11:47:44 +0200 Subject: [PATCH 090/136] Simple check to prevent frozen class modificaiton --- tools/check/check_code_quality.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh index 910616176c..79a42083d3 100755 --- a/tools/check/check_code_quality.sh +++ b/tools/check/check_code_quality.sh @@ -16,6 +16,21 @@ # limitations under the License. # +####################################################################################################################### +# Check frozen class modification +####################################################################################################################### + +echo "Check if frozen class modified" +git diff "HEAD@{1}" --name-only | grep -e OlmInboundGroupSessionWrapper.kt -e OlmInboundGroupSessionWrapper2.kt +FROZEN_CHANGED=$? +if [ ${FROZEN_CHANGED} -eq 0 ]; then + echo "❌ FROZEN CLASS CHANGED ERROR" + exit 1 +else + echo "Frozen check OK" +fi + + ####################################################################################################################### # Check drawable quantity ####################################################################################################################### From 1297ccd45c66a16c5ddcb60e0a3c29c20d192980 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 1 Jul 2022 11:58:01 +0200 Subject: [PATCH 091/136] Fix first compilation errors --- .../java/im/vector/app/core/platform/VectorBaseActivity.kt | 2 +- .../im/vector/app/features/devtools/RoomDevToolActivity.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt index 571d2d38c0..66b613d292 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorBaseActivity.kt @@ -417,7 +417,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), Maver } } - override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration?) { + override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean, newConfig: Configuration) { super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig) Timber.w("onMultiWindowModeChanged. isInMultiWindowMode: $isInMultiWindowMode") diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt index 6f661c5164..6225741310 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt @@ -174,8 +174,8 @@ class RoomDevToolActivity : SimpleFragmentActivity(), FragmentManager.OnBackStac super.onDestroy() } - override fun onPrepareOptionsMenu(menu: Menu?): Boolean = withState(viewModel) { state -> - menu?.forEach { + override fun onPrepareOptionsMenu(menu: Menu): Boolean = withState(viewModel) { state -> + menu.forEach { val isVisible = when (it.itemId) { R.id.menuItemEdit -> { state.displayMode is RoomDevToolViewState.Mode.StateEventDetail From a92fae6d251e8291e26c711da7105d3ebe8caf1b Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 1 Jul 2022 12:00:26 +0200 Subject: [PATCH 092/136] add change log --- changelog.d/6434.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6434.misc diff --git a/changelog.d/6434.misc b/changelog.d/6434.misc new file mode 100644 index 0000000000..faef5bc040 --- /dev/null +++ b/changelog.d/6434.misc @@ -0,0 +1 @@ +Add code check to prevent modification of frozen class From e6e079a071ad0d7a6cc48a07174e410d0b5750b7 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 24 May 2022 16:51:28 +0100 Subject: [PATCH 093/136] adding barebone phone confirmation fragment, copied from the phone number input --- .../im/vector/app/core/di/FragmentModule.kt | 6 + .../FtueAuthPhoneConfirmationFragment.kt | 94 +++++++++++++ .../onboarding/ftueauth/FtueAuthVariant.kt | 21 ++- .../fragment_ftue_phone_confirmation.xml | 131 ++++++++++++++++++ 4 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneConfirmationFragment.kt create mode 100644 vector/src/main/res/layout/fragment_ftue_phone_confirmation.xml diff --git a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt index 143818f3e5..bb11ec0dc5 100644 --- a/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/FragmentModule.kt @@ -110,6 +110,7 @@ import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyStyleCaptchaFrag import im.vector.app.features.onboarding.ftueauth.FtueAuthLegacyWaitForEmailFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthPersonalizationCompleteFragment +import im.vector.app.features.onboarding.ftueauth.FtueAuthPhoneConfirmationFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthPhoneEntryFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordFragment import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordMailConfirmationFragment @@ -515,6 +516,11 @@ interface FragmentModule { @FragmentKey(FtueAuthPhoneEntryFragment::class) fun bindFtueAuthPhoneEntryFragment(fragment: FtueAuthPhoneEntryFragment): Fragment + @Binds + @IntoMap + @FragmentKey(FtueAuthPhoneConfirmationFragment::class) + fun bindFtueAuthPhoneConfirmationFragment(fragment: FtueAuthPhoneConfirmationFragment): Fragment + @Binds @IntoMap @FragmentKey(FtueAuthChooseDisplayNameFragment::class) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneConfirmationFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneConfirmationFragment.kt new file mode 100644 index 0000000000..5b4435bba1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthPhoneConfirmationFragment.kt @@ -0,0 +1,94 @@ +/* + * 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.os.Bundle +import android.os.Parcelable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope +import im.vector.app.R +import im.vector.app.core.extensions.associateContentStateWith +import im.vector.app.core.extensions.autofillPhoneNumber +import im.vector.app.core.extensions.content +import im.vector.app.core.extensions.editText +import im.vector.app.core.extensions.setOnImeDoneListener +import im.vector.app.databinding.FragmentFtuePhoneConfirmationBinding +import im.vector.app.features.onboarding.OnboardingAction +import im.vector.app.features.onboarding.RegisterAction +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.api.auth.registration.RegisterThreePid +import reactivecircus.flowbinding.android.widget.textChanges +import javax.inject.Inject + +@Parcelize +data class FtueAuthPhoneConfirmationFragmentArgument( + val msisdn: String +) : Parcelable + +class FtueAuthPhoneConfirmationFragment @Inject constructor( + private val phoneNumberParser: PhoneNumberParser +) : AbstractFtueAuthFragment() { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtuePhoneConfirmationBinding { + return FragmentFtuePhoneConfirmationBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupViews() + } + + private fun setupViews() { + views.phoneEntryInput.associateContentStateWith(button = views.phoneEntrySubmit) + views.phoneEntryInput.setOnImeDoneListener { updatePhoneNumber() } + views.phoneEntrySubmit.debouncedClicks { updatePhoneNumber() } + + views.phoneEntryInput.editText().textChanges() + .onEach { + views.phoneEntryInput.error = null + views.phoneEntrySubmit.isEnabled = it.isNotBlank() + } + .launchIn(viewLifecycleOwner.lifecycleScope) + + views.phoneEntryInput.autofillPhoneNumber() + } + + private fun updatePhoneNumber() { + val number = views.phoneEntryInput.content() + + when (val result = phoneNumberParser.parseInternationalNumber(number)) { + PhoneNumberParser.Result.ErrorInvalidNumber -> views.phoneEntryInput.error = getString(R.string.login_msisdn_error_other) + PhoneNumberParser.Result.ErrorMissingInternationalCode -> views.phoneEntryInput.error = getString(R.string.login_msisdn_error_not_international) + is PhoneNumberParser.Result.Success -> { + val (countryCode, phoneNumber) = result + viewModel.handle(OnboardingAction.PostRegisterAction(RegisterAction.AddThreePid(RegisterThreePid.Msisdn(phoneNumber, countryCode)))) + } + } + } + + override fun onError(throwable: Throwable) { + views.phoneEntryInput.error = errorFormatter.toHumanReadable(throwable) + } + + override fun resetViewModel() { + viewModel.handle(OnboardingAction.ResetAuthenticationAttempt) + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt index df5c6e79ef..bb8c523b5f 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt @@ -199,12 +199,7 @@ class FtueAuthVariant( openWaitForEmailVerification(viewEvents.email) } is OnboardingViewEvents.OnSendMsisdnSuccess -> { - // Pop the enter Msisdn Fragment - supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) - addRegistrationStageFragmentToBackstack( - FtueAuthGenericTextInputFormFragment::class.java, - FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, viewEvents.msisdn), - ) + openMsisdnConfirmation(viewEvents.msisdn) } is OnboardingViewEvents.Failure, is OnboardingViewEvents.Loading -> @@ -432,6 +427,20 @@ class FtueAuthVariant( } } + private fun openMsisdnConfirmation(msisdn: String) { + supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE) + when { + vectorFeatures.isOnboardingCombinedRegisterEnabled() -> addRegistrationStageFragmentToBackstack( + FtueAuthPhoneConfirmationFragment::class.java, + FtueAuthPhoneConfirmationFragmentArgument(msisdn), + ) + else -> addRegistrationStageFragmentToBackstack( + FtueAuthGenericTextInputFormFragment::class.java, + FtueAuthGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, msisdn), + ) + } + } + private fun onTerms(stage: Stage.Terms) { when { vectorFeatures.isOnboardingCombinedRegisterEnabled() -> addRegistrationStageFragmentToBackstack( diff --git a/vector/src/main/res/layout/fragment_ftue_phone_confirmation.xml b/vector/src/main/res/layout/fragment_ftue_phone_confirmation.xml new file mode 100644 index 0000000000..32b54bcef3 --- /dev/null +++ b/vector/src/main/res/layout/fragment_ftue_phone_confirmation.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +