mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 16:22:49 +00:00
Make sure enforce_element_pro is taken into account in FOSS (#7631)
* Make sure `enforce_element_pro` is taken into account in FOSS * Make sure we crate `RustTemporaryMatrixClient` instances with a proper `serverName` value
This commit is contained in:
committed by
GitHub
parent
3b2e416855
commit
dcd0bc782f
@@ -10,6 +10,7 @@ import extension.testCommonDependencies
|
||||
*/
|
||||
plugins {
|
||||
id("io.element.android-compose-library")
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -22,8 +23,10 @@ dependencies {
|
||||
implementation(projects.libraries.compound)
|
||||
api(projects.features.enterprise.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.androidutils)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.wellknown.api)
|
||||
implementation(libs.serialization.json)
|
||||
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
|
||||
+35
-2
@@ -14,18 +14,43 @@ import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.features.enterprise.api.EnterpriseService
|
||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
||||
import io.element.android.libraries.core.extensions.mapCatchingExceptions
|
||||
import io.element.android.libraries.core.uri.ensureProtocol
|
||||
import io.element.android.libraries.matrix.api.ClientUrlContentFetcher
|
||||
import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import timber.log.Timber
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultEnterpriseService : EnterpriseService {
|
||||
class DefaultEnterpriseService(
|
||||
private val temporaryMatrixClientFactory: TemporaryMatrixClientFactory,
|
||||
private val jsonProvider: JsonProvider,
|
||||
) : EnterpriseService {
|
||||
override suspend fun isEnterpriseUser(sessionId: SessionId) = false
|
||||
override suspend fun tweakMasUrl(url: String, urlContentFetcher: ClientUrlContentFetcher) = url
|
||||
override fun homeserverAllowList(): List<String> = emptyList()
|
||||
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
|
||||
override suspend fun isElementProEnforced(serverName: String): Boolean = false
|
||||
override suspend fun isElementProEnforced(serverName: String): Boolean {
|
||||
val temporaryMatrixClient = temporaryMatrixClientFactory.create(serverName).getOrElse { return false }
|
||||
return temporaryMatrixClient.use { client ->
|
||||
val baseUrl = serverName.ensureProtocol().removeSuffix("/")
|
||||
// We'll always perform a network request here since we're not interested in any cached value.
|
||||
client.getUrl("$baseUrl/.well-known/element/element.json")
|
||||
.mapCatchingExceptions { response ->
|
||||
val remoteConfig = jsonProvider().decodeFromString<MinimalEnterpriseConfig>(String(response))
|
||||
remoteConfig.enforceElementPro ?: false
|
||||
}
|
||||
.onFailure {
|
||||
Timber.e(it, "Failed to fetch enterprise config for checking if Element Pro is enforced for $serverName")
|
||||
}
|
||||
.getOrElse { false }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun overrideBrandColor(sessionId: SessionId?, brandColor: String?) = Unit
|
||||
|
||||
@@ -46,3 +71,11 @@ class DefaultEnterpriseService : EnterpriseService {
|
||||
|
||||
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* A minimal version of the enterprise config that only contains the fields we need to check if Element Pro is enforced.
|
||||
*/
|
||||
@Serializable
|
||||
private data class MinimalEnterpriseConfig(
|
||||
@SerialName("enforce_element_pro") val enforceElementPro: Boolean? = null,
|
||||
)
|
||||
|
||||
+45
-11
@@ -12,33 +12,40 @@ import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
import io.element.android.features.enterprise.api.BugReportUrl
|
||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
||||
import io.element.android.libraries.core.uri.ensureProtocol
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER_URL
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClient
|
||||
import io.element.android.libraries.matrix.test.FakeTemporaryMatrixClientFactory
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.element.android.tests.testutils.lambda.value
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultEnterpriseServiceTest {
|
||||
@Test
|
||||
fun homeserverWhitelist() {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.homeserverAllowList()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isAllowedToConnectToHomeserver is true for all homeserver urls`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.isAllowedToConnectToHomeserver(A_HOMESERVER_URL)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isEnterpriseUser always return false`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.isEnterpriseUser(A_SESSION_ID)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `semanticColorsFlow always emits the same value`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.semanticColorsFlow(null).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isEqualTo(SemanticColorsLightDark.default)
|
||||
@@ -48,7 +55,7 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `brandColorsFlow always emits null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.brandColorsFlow(null).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isNull()
|
||||
@@ -58,7 +65,7 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `semanticColorsFlow always emits the same value for a session`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.semanticColorsFlow(A_SESSION_ID).test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isEqualTo(SemanticColorsLightDark.default)
|
||||
@@ -68,25 +75,25 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `overrideBrandColor has no effect`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.overrideBrandColor(A_SESSION_ID, "aColor")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `firebasePushGateway returns null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.firebasePushGateway()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unifiedPushDefaultPushGateway returns null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.unifiedPushDefaultPushGateway()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bugReportUrlFlow only emits UseDefault`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
defaultEnterpriseService.bugReportUrlFlow(A_SESSION_ID).test {
|
||||
assertThat(awaitItem()).isEqualTo(BugReportUrl.UseDefault)
|
||||
awaitComplete()
|
||||
@@ -95,7 +102,34 @@ class DefaultEnterpriseServiceTest {
|
||||
|
||||
@Test
|
||||
fun `getNoisyNotificationChannelId returns null`() = runTest {
|
||||
val defaultEnterpriseService = DefaultEnterpriseService()
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService()
|
||||
assertThat(defaultEnterpriseService.getNoisyNotificationChannelId(A_SESSION_ID)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isElementProEnforced checks using a temporary client`() = runTest {
|
||||
val closeLambda = lambdaRecorder<Unit> {}
|
||||
val getUrlLambda = lambdaRecorder<String, Result<ByteArray>> {
|
||||
Result.success("""{"enforce_element_pro": true}""".toByteArray())
|
||||
}
|
||||
val client = FakeTemporaryMatrixClient(
|
||||
getUrlResult = getUrlLambda,
|
||||
closeLambda = closeLambda,
|
||||
)
|
||||
val defaultEnterpriseService = createDefaultEnterpriseService(client = client)
|
||||
assertThat(defaultEnterpriseService.isElementProEnforced(A_HOMESERVER_URL)).isTrue()
|
||||
|
||||
// Verify that the temporary client was used to fetch the URL and then closed
|
||||
val expectedUrl = "${A_HOMESERVER_URL.ensureProtocol()}/.well-known/element/element.json"
|
||||
getUrlLambda.assertions().isCalledOnce().with(value(expectedUrl))
|
||||
closeLambda.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
private fun createDefaultEnterpriseService(
|
||||
client: FakeTemporaryMatrixClient = FakeTemporaryMatrixClient(),
|
||||
jsonProvider: JsonProvider = { Json }
|
||||
) = DefaultEnterpriseService(
|
||||
temporaryMatrixClientFactory = FakeTemporaryMatrixClientFactory(createResult = { Result.success(client) }),
|
||||
jsonProvider = jsonProvider,
|
||||
)
|
||||
}
|
||||
|
||||
+9
-1
@@ -10,9 +10,11 @@ package io.element.android.libraries.matrix.impl
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.core.uri.ensureProtocol
|
||||
import io.element.android.libraries.matrix.api.TemporaryMatrixClient
|
||||
import io.element.android.libraries.matrix.api.TemporaryMatrixClientFactory
|
||||
import io.element.android.libraries.matrix.impl.paths.SessionPathsFactory
|
||||
import java.net.URL
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class RustTemporaryMatrixClientFactory(
|
||||
@@ -21,6 +23,12 @@ class RustTemporaryMatrixClientFactory(
|
||||
) : TemporaryMatrixClientFactory {
|
||||
override suspend fun create(serverName: String): Result<TemporaryMatrixClient> {
|
||||
return runCatchingExceptions {
|
||||
// In case the 'serverName' is a full URL, we need to extract the host and port to pass to the client builder.
|
||||
val parsedUrl = URL(serverName.ensureProtocol())
|
||||
val domain = parsedUrl.host ?: error("Invalid server name: $serverName")
|
||||
val port = parsedUrl.port
|
||||
val formattedServerName = if (port != -1) "$domain:$port" else domain
|
||||
|
||||
val sessionPaths = sessionPathsFactory.create()
|
||||
val client = rustMatrixClientFactory.getBaseClientBuilder(
|
||||
sessionPaths = sessionPaths,
|
||||
@@ -28,7 +36,7 @@ class RustTemporaryMatrixClientFactory(
|
||||
slidingSyncType = ClientBuilderSlidingSync.Native,
|
||||
isMessageSearchAvailable = false,
|
||||
)
|
||||
.serverName(serverName)
|
||||
.serverName(formattedServerName)
|
||||
.build()
|
||||
RustTemporaryMatrixClient(client, sessionPaths)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user