Allow homeserver to disable encryption in Element Pro (#7198)

* Allow HS to disable encryption in Element Pro

Add `force_disable_e2ee` support in the Element well known retriever and cache to allow a homeserver to disable encrypted room creation and enabling encryption in an existing room in Element Pro clients

* Bump enterprise repo back to `main` branch
This commit is contained in:
Jorge Martin Espinosa
2026-07-15 13:29:16 +02:00
committed by GitHub
parent 03f3e8b3e0
commit 62cc7e0505
19 changed files with 97 additions and 9 deletions
@@ -41,10 +41,12 @@ dependencies {
implementation(projects.libraries.uiStrings)
implementation(projects.services.analytics.api)
implementation(projects.libraries.featureflag.api)
implementation(projects.features.enterprise.api)
testCommonDependencies(libs, true)
testImplementation(projects.services.analytics.test)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.testtags)
testImplementation(projects.features.enterprise.test)
}
@@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyPermissions
import io.element.android.features.securityandprivacy.api.securityAndPrivacyPermissions
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
@@ -63,6 +64,7 @@ class SecurityAndPrivacyPresenter(
private val matrixClient: MatrixClient,
private val room: JoinedRoom,
private val featureFlagService: FeatureFlagService,
private val sessionEnterpriseService: SessionEnterpriseService,
) : Presenter<SecurityAndPrivacyState> {
@AssistedFactory
interface Factory {
@@ -83,6 +85,10 @@ class SecurityAndPrivacyPresenter(
val homeserverName = remember { matrixClient.userIdServerName() }
val roomInfo by room.roomInfoFlow.collectAsState()
val isEncryptionDisabledByHomeserver by produceState(false) {
value = sessionEnterpriseService.isEncryptionDisabledByHomeserver()
}
val savedIsVisibleInRoomDirectory = remember { mutableStateOf<AsyncData<Boolean>>(AsyncData.Uninitialized) }
LaunchedEffect(Unit) {
isRoomVisibleInRoomDirectory(savedIsVisibleInRoomDirectory)
@@ -247,6 +253,7 @@ class SecurityAndPrivacyPresenter(
isSpace = roomInfo.isSpace,
selectableJoinedSpaces = selectableJoinedSpaces,
spaceSelectionMode = spaceSelectionMode,
isEncryptionDisabledByHomeserver = isEncryptionDisabledByHomeserver,
eventSink = ::handleEvent,
)
@@ -27,6 +27,7 @@ data class SecurityAndPrivacyState(
// the settings the user wants to apply.
val editedSettings: SecurityAndPrivacySettings,
val homeserverName: String,
val isEncryptionDisabledByHomeserver: Boolean,
val showEnableEncryptionConfirmation: Boolean,
private val isKnockEnabled: Boolean,
val saveAction: AsyncAction<Unit>,
@@ -84,7 +85,7 @@ data class SecurityAndPrivacyState(
editedSettings.roomAccess.canConfigureRoomVisibility()
val showHistoryVisibilitySection = permissions.canChangeHistoryVisibility && !isSpace
val showEncryptionSection = permissions.canChangeEncryption && !isSpace
val showEncryptionSection = !isEncryptionDisabledByHomeserver && permissions.canChangeEncryption && !isSpace
@Composable
fun spaceMemberDescription(): String {
@@ -138,6 +138,7 @@ fun aSecurityAndPrivacyState(
isSpace: Boolean = false,
selectableJoinedSpaces: Set<SpaceRoom> = emptySet(),
spaceSelectionMode: SpaceSelectionMode = SpaceSelectionMode.None,
isEncryptionDisabledByHomeserver: Boolean = false,
eventSink: (SecurityAndPrivacyEvent) -> Unit = {}
) = SecurityAndPrivacyState(
editedSettings = editedSettings,
@@ -150,5 +151,6 @@ fun aSecurityAndPrivacyState(
isSpace = isSpace,
selectableJoinedSpaces = selectableJoinedSpaces.toImmutableSet(),
spaceSelectionMode = spaceSelectionMode,
isEncryptionDisabledByHomeserver = isEncryptionDisabledByHomeserver,
eventSink = eventSink,
)
@@ -8,6 +8,7 @@
package io.element.android.features.securityandprivacy.impl.root
import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeSessionEnterpriseService
import io.element.android.features.securityandprivacy.impl.FakeSecurityAndPrivacyNavigator
import io.element.android.features.securityandprivacy.impl.SecurityAndPrivacyNavigator
import io.element.android.features.securityandprivacy.impl.manageauthorizedspaces.SpaceSelectionStateHolder
@@ -1046,6 +1047,20 @@ class SecurityAndPrivacyPresenterTest {
}
}
@Test
fun `present - showEncryptionSection is false if the HS disabled encryption`() = runTest {
val sessionEnterpriseService = FakeSessionEnterpriseService(isEncryptionDisabledResult = { true })
val presenter = createSecurityAndPrivacyPresenter(
sessionEnterpriseService = sessionEnterpriseService,
)
presenter.test {
skipItems(1)
// The HS has disabled encryption, so the encryption section should not be shown
assertThat(awaitItem().showEncryptionSection).isFalse()
}
}
private fun roomPermissions(
canChangeRoomAccess: Boolean = true,
canChangeHistoryVisibility: Boolean = true,
@@ -1084,6 +1099,9 @@ class SecurityAndPrivacyPresenterTest {
),
),
spaceSelectionStateHolder: SpaceSelectionStateHolder = SpaceSelectionStateHolder(),
sessionEnterpriseService: FakeSessionEnterpriseService = FakeSessionEnterpriseService(
isEncryptionDisabledResult = { false },
)
): SecurityAndPrivacyPresenter {
return SecurityAndPrivacyPresenter(
room = room,
@@ -1091,6 +1109,7 @@ class SecurityAndPrivacyPresenterTest {
navigator = navigator,
featureFlagService = featureFlagService,
spaceSelectionStateHolder = spaceSelectionStateHolder,
sessionEnterpriseService = sessionEnterpriseService,
)
}
}