Skip to content

Commit 8ca5f8b

Browse files
bmartyElementBot
andauthored
Let EnterpriseService prevent usage of homeserver (#4682)
* Add check on homeserver url. * Update screenshots * Add missing test * Update submodule link. --------- Co-authored-by: ElementBot <[email protected]>
1 parent 390337c commit 8ca5f8b

File tree

14 files changed

+108
-5
lines changed

14 files changed

+108
-5
lines changed

enterprise

features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface EnterpriseService {
1414
val isEnterpriseBuild: Boolean
1515
suspend fun isEnterpriseUser(sessionId: SessionId): Boolean
1616
fun defaultHomeserver(): String?
17+
suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean
1718

1819
fun semanticColorsLight(): SemanticColors
1920
fun semanticColorsDark(): SemanticColors

features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService {
2323
override suspend fun isEnterpriseUser(sessionId: SessionId) = false
2424

2525
override fun defaultHomeserver() = null
26+
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
2627

2728
override fun semanticColorsLight(): SemanticColors = compoundColorsLight
2829

features/enterprise/impl/src/test/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseServiceTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.features.enterprise.impl
99

1010
import com.google.common.truth.Truth.assertThat
11+
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
1112
import io.element.android.libraries.matrix.test.A_SESSION_ID
1213
import kotlinx.coroutines.test.runTest
1314
import org.junit.Test
@@ -25,6 +26,12 @@ class DefaultEnterpriseServiceTest {
2526
assertThat<String?>(defaultEnterpriseService.defaultHomeserver()).isNull()
2627
}
2728

29+
@Test
30+
fun `isAllowedToConnectToHomeserver is true for all homeserver urls`() = runTest {
31+
val defaultEnterpriseService = DefaultEnterpriseService()
32+
assertThat(defaultEnterpriseService.isAllowedToConnectToHomeserver(A_HOMESERVER_URL)).isTrue()
33+
}
34+
2835
@Test
2936
fun `isEnterpriseUser always return false`() = runTest {
3037
val defaultEnterpriseService = DefaultEnterpriseService()

features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class FakeEnterpriseService(
1717
override val isEnterpriseBuild: Boolean = false,
1818
private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() },
1919
private val defaultHomeserverResult: () -> String? = { A_FAKE_HOMESERVER },
20+
private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() },
2021
private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() },
2122
private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() },
2223
private val firebasePushGatewayResult: () -> String? = { lambdaError() },
@@ -30,6 +31,10 @@ class FakeEnterpriseService(
3031
return defaultHomeserverResult()
3132
}
3233

34+
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean = simulateLongTask {
35+
isAllowedToConnectToHomeserverResult(homeserverUrl)
36+
}
37+
3338
override fun semanticColorsLight(): SemanticColors {
3439
return semanticColorsLightResult()
3540
}

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenter.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import androidx.compose.runtime.MutableState
1212
import androidx.compose.runtime.mutableStateOf
1313
import androidx.compose.runtime.remember
1414
import androidx.compose.runtime.rememberCoroutineScope
15+
import io.element.android.features.enterprise.api.EnterpriseService
1516
import io.element.android.features.login.impl.accountprovider.AccountProvider
1617
import io.element.android.features.login.impl.accountprovider.AccountProviderDataSource
1718
import io.element.android.features.login.impl.error.ChangeServerError
@@ -26,6 +27,7 @@ import javax.inject.Inject
2627
class ChangeServerPresenter @Inject constructor(
2728
private val authenticationService: MatrixAuthenticationService,
2829
private val accountProviderDataSource: AccountProviderDataSource,
30+
private val enterpriseService: EnterpriseService,
2931
) : Presenter<ChangeServerState> {
3032
@Composable
3133
override fun present(): ChangeServerState {
@@ -53,6 +55,9 @@ class ChangeServerPresenter @Inject constructor(
5355
changeServerAction: MutableState<AsyncData<Unit>>,
5456
) = launch {
5557
suspend {
58+
if (enterpriseService.isAllowedToConnectToHomeserver(data.url).not()) {
59+
throw UnauthorizedAccountProviderException(data)
60+
}
5661
authenticationService.setHomeserver(data.url).map {
5762
authenticationService.getHomeserverDetails().value!!
5863
// Valid, remember user choice

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerStateProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package io.element.android.features.login.impl.changeserver
99

1010
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
11+
import io.element.android.features.login.impl.accountprovider.anAccountProvider
1112
import io.element.android.features.login.impl.error.ChangeServerError
1213
import io.element.android.libraries.architecture.AsyncData
1314
import io.element.android.libraries.ui.strings.CommonStrings
@@ -18,6 +19,7 @@ open class ChangeServerStateProvider : PreviewParameterProvider<ChangeServerStat
1819
aChangeServerState(),
1920
aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.Error(CommonStrings.error_unknown))),
2021
aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.SlidingSyncAlert)),
22+
aChangeServerState(changeServerAction = AsyncData.Failure(ChangeServerError.UnauthorizedAccountProvider(anAccountProvider()))),
2123
)
2224
}
2325

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerView.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ import androidx.compose.runtime.LaunchedEffect
1212
import androidx.compose.runtime.getValue
1313
import androidx.compose.runtime.rememberUpdatedState
1414
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.res.stringResource
1516
import androidx.compose.ui.tooling.preview.PreviewParameter
17+
import io.element.android.features.login.impl.R
1618
import io.element.android.features.login.impl.dialogs.SlidingSyncNotSupportedDialog
1719
import io.element.android.features.login.impl.error.ChangeServerError
1820
import io.element.android.libraries.architecture.AsyncData
1921
import io.element.android.libraries.designsystem.components.ProgressDialog
2022
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
2123
import io.element.android.libraries.designsystem.preview.ElementPreview
2224
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
25+
import io.element.android.libraries.designsystem.theme.LocalBuildMeta
2326

2427
@Composable
2528
fun ChangeServerView(
@@ -31,7 +34,7 @@ fun ChangeServerView(
3134
val eventSink = state.eventSink
3235
when (state.changeServerAction) {
3336
is AsyncData.Failure -> {
34-
when (val error = state.changeServerAction.error) {
37+
when (val error = state.changeServerAction.error as? ChangeServerError) {
3538
is ChangeServerError.Error -> {
3639
ErrorDialog(
3740
modifier = modifier,
@@ -53,6 +56,20 @@ fun ChangeServerView(
5356
}
5457
)
5558
}
59+
is ChangeServerError.UnauthorizedAccountProvider -> {
60+
ErrorDialog(
61+
modifier = modifier,
62+
content = stringResource(
63+
id = R.string.screen_change_server_error_unauthorized_homeserver,
64+
LocalBuildMeta.current.applicationName,
65+
error.accountProvider.title,
66+
),
67+
onSubmit = {
68+
eventSink.invoke(ChangeServerEvents.ClearError)
69+
}
70+
)
71+
}
72+
null -> Unit
5673
}
5774
}
5875
is AsyncData.Loading -> ProgressDialog()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.login.impl.changeserver
9+
10+
import io.element.android.features.login.impl.accountprovider.AccountProvider
11+
12+
class UnauthorizedAccountProviderException(
13+
val accountProvider: AccountProvider,
14+
) : Exception()

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/error/ChangeServerError.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import androidx.annotation.StringRes
1111
import androidx.compose.runtime.Composable
1212
import androidx.compose.ui.res.stringResource
1313
import io.element.android.features.login.impl.R
14+
import io.element.android.features.login.impl.accountprovider.AccountProvider
15+
import io.element.android.features.login.impl.changeserver.UnauthorizedAccountProviderException
1416
import io.element.android.libraries.matrix.api.auth.AuthenticationException
1517
import io.element.android.libraries.ui.strings.CommonStrings
1618

@@ -23,12 +25,17 @@ sealed class ChangeServerError : Throwable() {
2325
fun message(): String = messageStr ?: stringResource(messageId ?: CommonStrings.error_unknown)
2426
}
2527

28+
data class UnauthorizedAccountProvider(
29+
val accountProvider: AccountProvider,
30+
) : ChangeServerError()
31+
2632
data object SlidingSyncAlert : ChangeServerError()
2733

2834
companion object {
2935
fun from(error: Throwable): ChangeServerError = when (error) {
3036
is AuthenticationException.SlidingSyncVersion -> SlidingSyncAlert
3137
is AuthenticationException.Oidc -> Error(messageStr = error.message)
38+
is UnauthorizedAccountProviderException -> UnauthorizedAccountProvider(error.accountProvider)
3239
else -> Error(messageId = R.string.screen_change_server_error_invalid_homeserver)
3340
}
3441
}

0 commit comments

Comments
 (0)