Bump Rust SDK version and adapt our code (#3068)
* Use the new SDK version * Adapt the authentication service to the new Rust SDK APIs * Remove `Timeline.enterSpecialMode(...)` as it's no longer necessary
This commit is contained in:
parent
f163852c4b
commit
cdbb46fa22
14 changed files with 85 additions and 179 deletions
|
|
@ -19,6 +19,18 @@ package io.element.android.appconfig
|
||||||
object AuthenticationConfig {
|
object AuthenticationConfig {
|
||||||
const val MATRIX_ORG_URL = "https://matrix.org"
|
const val MATRIX_ORG_URL = "https://matrix.org"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default homeserver url to sign in with, unless the user selects a different one.
|
||||||
|
*/
|
||||||
const val DEFAULT_HOMESERVER_URL = MATRIX_ORG_URL
|
const val DEFAULT_HOMESERVER_URL = MATRIX_ORG_URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL with some docs that explain what's sliding sync and how to add it to your home server.
|
||||||
|
*/
|
||||||
const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md"
|
const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a sliding sync proxy url, if not null, the proxy url in the .well-known file will be ignored.
|
||||||
|
*/
|
||||||
|
val SLIDING_SYNC_PROXY_URL: String? = null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
changelog.d/3068.misc
Normal file
1
changelog.d/3068.misc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Updated Rust SDK to `v0.2.28`. Fixed incompatibilities.
|
||||||
|
|
@ -621,16 +621,8 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
) = launch {
|
) = launch {
|
||||||
messageComposerContext.composerMode = composerMode
|
messageComposerContext.composerMode = composerMode
|
||||||
when (composerMode) {
|
when (composerMode) {
|
||||||
is MessageComposerMode.Reply -> {
|
|
||||||
timelineController.invokeOnCurrentTimeline {
|
|
||||||
enterSpecialMode(composerMode.eventId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is MessageComposerMode.Edit -> {
|
is MessageComposerMode.Edit -> {
|
||||||
setText(composerMode.content, markdownTextEditorState, richTextEditorState)
|
setText(composerMode.content, markdownTextEditorState, richTextEditorState)
|
||||||
timelineController.invokeOnCurrentTimeline {
|
|
||||||
enterSpecialMode(composerMode.eventId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ jsoup = "org.jsoup:jsoup:1.17.2"
|
||||||
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
|
||||||
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0"
|
||||||
timber = "com.jakewharton.timber:timber:5.0.1"
|
timber = "com.jakewharton.timber:timber:5.0.1"
|
||||||
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.27"
|
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.2.28"
|
||||||
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
|
||||||
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
|
||||||
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,7 @@
|
||||||
package io.element.android.libraries.matrix.api.auth
|
package io.element.android.libraries.matrix.api.auth
|
||||||
|
|
||||||
sealed class AuthenticationException(message: String) : Exception(message) {
|
sealed class AuthenticationException(message: String) : Exception(message) {
|
||||||
class ClientMissing(message: String) : AuthenticationException(message)
|
|
||||||
class InvalidServerName(message: String) : AuthenticationException(message)
|
class InvalidServerName(message: String) : AuthenticationException(message)
|
||||||
class SlidingSyncNotAvailable(message: String) : AuthenticationException(message)
|
class SlidingSyncNotAvailable(message: String) : AuthenticationException(message)
|
||||||
class SessionMissing(message: String) : AuthenticationException(message)
|
|
||||||
class Generic(message: String) : AuthenticationException(message)
|
class Generic(message: String) : AuthenticationException(message)
|
||||||
data class OidcError(val type: String, override val message: String) : AuthenticationException(message)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,6 @@ interface Timeline : AutoCloseable {
|
||||||
|
|
||||||
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
suspend fun enterSpecialMode(eventId: EventId?): Result<Unit>
|
|
||||||
|
|
||||||
suspend fun replyMessage(
|
suspend fun replyMessage(
|
||||||
eventId: EventId,
|
eventId: EventId,
|
||||||
body: String,
|
body: String,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl
|
package io.element.android.libraries.matrix.impl
|
||||||
|
|
||||||
|
import io.element.android.appconfig.AuthenticationConfig
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.di.CacheDirectory
|
import io.element.android.libraries.di.CacheDirectory
|
||||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||||
|
|
@ -46,11 +47,9 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
private val utdTracker: UtdTracker,
|
private val utdTracker: UtdTracker,
|
||||||
) {
|
) {
|
||||||
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
||||||
val client = getBaseClientBuilder(sessionData.sessionPath)
|
val client = getBaseClientBuilder(sessionData.sessionPath, sessionData.passphrase)
|
||||||
.homeserverUrl(sessionData.homeserverUrl)
|
.serverNameOrHomeserverUrl(sessionData.homeserverUrl)
|
||||||
.username(sessionData.userId)
|
.username(sessionData.userId)
|
||||||
.passphrase(sessionData.passphrase)
|
|
||||||
// FIXME Quick and dirty fix for stopping version requests on startup https://github.com/matrix-org/matrix-rust-sdk/pull/1376
|
|
||||||
.use { it.build() }
|
.use { it.build() }
|
||||||
|
|
||||||
client.restoreSession(sessionData.toSession())
|
client.restoreSession(sessionData.toSession())
|
||||||
|
|
@ -71,21 +70,20 @@ class RustMatrixClientFactory @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getBaseClientBuilder(sessionPath: String): ClientBuilder {
|
internal fun getBaseClientBuilder(sessionPath: String, passphrase: String?): ClientBuilder {
|
||||||
return ClientBuilder()
|
return ClientBuilder()
|
||||||
.sessionPath(sessionPath)
|
.sessionPath(sessionPath)
|
||||||
|
.passphrase(passphrase)
|
||||||
|
.slidingSyncProxy(AuthenticationConfig.SLIDING_SYNC_PROXY_URL)
|
||||||
.userAgent(userAgentProvider.provide())
|
.userAgent(userAgentProvider.provide())
|
||||||
.addRootCertificates(userCertificatesProvider.provides())
|
.addRootCertificates(userCertificatesProvider.provides())
|
||||||
|
.autoEnableBackups(true)
|
||||||
|
.autoEnableCrossSigning(true)
|
||||||
|
// FIXME Quick and dirty fix for stopping version requests on startup https://github.com/matrix-org/matrix-rust-sdk/pull/1376
|
||||||
.serverVersions(listOf("v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5"))
|
.serverVersions(listOf("v1.0", "v1.1", "v1.2", "v1.3", "v1.4", "v1.5"))
|
||||||
.let {
|
.run {
|
||||||
// Sadly ClientBuilder.proxy() does not accept null :/
|
// Workaround for non-nullable proxy parameter in the SDK, since each call to the ClientBuilder returns a new reference we need to keep
|
||||||
// Tracked by https://github.com/matrix-org/matrix-rust-sdk/issues/3159
|
proxyProvider.provides()?.let { proxy(it) } ?: this
|
||||||
val proxy = proxyProvider.provides()
|
|
||||||
if (proxy != null) {
|
|
||||||
it.proxy(proxy)
|
|
||||||
} else {
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,22 +17,14 @@
|
||||||
package io.element.android.libraries.matrix.impl.auth
|
package io.element.android.libraries.matrix.impl.auth
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||||
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
import org.matrix.rustcomponents.sdk.ClientBuildException as RustAuthenticationException
|
||||||
|
|
||||||
fun Throwable.mapAuthenticationException(): AuthenticationException {
|
fun Throwable.mapAuthenticationException(): AuthenticationException {
|
||||||
val message = this.message ?: "Unknown error"
|
val message = this.message ?: "Unknown error"
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(message)
|
|
||||||
is RustAuthenticationException.Generic -> AuthenticationException.Generic(message)
|
is RustAuthenticationException.Generic -> AuthenticationException.Generic(message)
|
||||||
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
|
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
|
||||||
is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(message)
|
|
||||||
is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(message)
|
is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(message)
|
||||||
is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message)
|
|
||||||
is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message)
|
|
||||||
is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message)
|
|
||||||
is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message)
|
|
||||||
is RustAuthenticationException.OidcCancelled -> AuthenticationException.OidcError("OidcCancelled", message)
|
|
||||||
is RustAuthenticationException.OidcCallbackUrlInvalid -> AuthenticationException.OidcError("OidcCallbackUrlInvalid", message)
|
|
||||||
else -> AuthenticationException.Generic(message)
|
else -> AuthenticationException.Generic(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,9 @@ import io.element.android.libraries.matrix.impl.RustMatrixClientFactory
|
||||||
import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper
|
import io.element.android.libraries.matrix.impl.auth.qrlogin.QrErrorMapper
|
||||||
import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData
|
import io.element.android.libraries.matrix.impl.auth.qrlogin.SdkQrCodeLoginData
|
||||||
import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep
|
import io.element.android.libraries.matrix.impl.auth.qrlogin.toStep
|
||||||
import io.element.android.libraries.matrix.impl.certificates.UserCertificatesProvider
|
|
||||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||||
import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
|
import io.element.android.libraries.matrix.impl.keys.PassphraseGenerator
|
||||||
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
import io.element.android.libraries.matrix.impl.mapper.toSessionData
|
||||||
import io.element.android.libraries.matrix.impl.proxy.ProxyProvider
|
|
||||||
import io.element.android.libraries.network.useragent.UserAgentProvider
|
|
||||||
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
||||||
import io.element.android.libraries.sessionstorage.api.LoginType
|
import io.element.android.libraries.sessionstorage.api.LoginType
|
||||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||||
|
|
@ -46,17 +43,17 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.rustcomponents.sdk.Client
|
||||||
import org.matrix.rustcomponents.sdk.HumanQrLoginException
|
import org.matrix.rustcomponents.sdk.HumanQrLoginException
|
||||||
import org.matrix.rustcomponents.sdk.OidcAuthenticationData
|
|
||||||
import org.matrix.rustcomponents.sdk.QrCodeDecodeException
|
import org.matrix.rustcomponents.sdk.QrCodeDecodeException
|
||||||
import org.matrix.rustcomponents.sdk.QrLoginProgress
|
import org.matrix.rustcomponents.sdk.QrLoginProgress
|
||||||
import org.matrix.rustcomponents.sdk.QrLoginProgressListener
|
import org.matrix.rustcomponents.sdk.QrLoginProgressListener
|
||||||
import org.matrix.rustcomponents.sdk.use
|
import org.matrix.rustcomponents.sdk.use
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import uniffi.matrix_sdk.OidcAuthorizationData
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.matrix.rustcomponents.sdk.AuthenticationService as RustAuthenticationService
|
|
||||||
|
|
||||||
@ContributesBinding(AppScope::class)
|
@ContributesBinding(AppScope::class)
|
||||||
@SingleIn(AppScope::class)
|
@SingleIn(AppScope::class)
|
||||||
|
|
@ -64,28 +61,15 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
baseDirectory: File,
|
baseDirectory: File,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val sessionStore: SessionStore,
|
private val sessionStore: SessionStore,
|
||||||
userAgentProvider: UserAgentProvider,
|
|
||||||
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
||||||
private val passphraseGenerator: PassphraseGenerator,
|
private val passphraseGenerator: PassphraseGenerator,
|
||||||
userCertificatesProvider: UserCertificatesProvider,
|
|
||||||
proxyProvider: ProxyProvider,
|
|
||||||
private val oidcConfigurationProvider: OidcConfigurationProvider,
|
private val oidcConfigurationProvider: OidcConfigurationProvider,
|
||||||
) : MatrixAuthenticationService {
|
) : MatrixAuthenticationService {
|
||||||
// Passphrase which will be used for new sessions. Existing sessions will use the passphrase
|
// Passphrase which will be used for new sessions. Existing sessions will use the passphrase
|
||||||
// stored in the SessionData.
|
// stored in the SessionData.
|
||||||
private val pendingPassphrase = getDatabasePassphrase()
|
private val pendingPassphrase = getDatabasePassphrase()
|
||||||
private val sessionPath = File(baseDirectory, UUID.randomUUID().toString()).absolutePath
|
private val sessionPath = File(baseDirectory, UUID.randomUUID().toString()).absolutePath
|
||||||
private val authService: RustAuthenticationService = RustAuthenticationService(
|
private var currentClient: Client? = null
|
||||||
sessionPath = sessionPath,
|
|
||||||
passphrase = pendingPassphrase,
|
|
||||||
proxy = proxyProvider.provides(),
|
|
||||||
userAgent = userAgentProvider.provide(),
|
|
||||||
additionalRootCertificates = userCertificatesProvider.provides(),
|
|
||||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
|
||||||
customSlidingSyncProxy = null,
|
|
||||||
sessionDelegate = null,
|
|
||||||
crossProcessRefreshLockId = null,
|
|
||||||
)
|
|
||||||
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||||
|
|
||||||
override fun loggedInStateFlow(): Flow<LoggedInState> {
|
override fun loggedInStateFlow(): Flow<LoggedInState> {
|
||||||
|
|
@ -132,11 +116,14 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
override suspend fun setHomeserver(homeserver: String): Result<Unit> =
|
override suspend fun setHomeserver(homeserver: String): Result<Unit> =
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
authService.configureHomeserver(homeserver)
|
val client = getBaseClientBuilder()
|
||||||
val homeServerDetails = authService.homeserverDetails()?.map()
|
.serverNameOrHomeserverUrl(homeserver)
|
||||||
if (homeServerDetails != null) {
|
.build()
|
||||||
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
|
currentClient = client
|
||||||
}
|
val homeServerDetails = client.homeserverLoginDetails().map()
|
||||||
|
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
|
||||||
|
}.onFailure {
|
||||||
|
clear()
|
||||||
}.mapFailure { failure ->
|
}.mapFailure { failure ->
|
||||||
failure.mapAuthenticationException()
|
failure.mapAuthenticationException()
|
||||||
}
|
}
|
||||||
|
|
@ -145,15 +132,16 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
override suspend fun login(username: String, password: String): Result<SessionId> =
|
override suspend fun login(username: String, password: String): Result<SessionId> =
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val client = authService.login(username, password, "Element X Android", null)
|
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||||
val sessionData = client.use {
|
client.login(username, password, "Element X Android", null)
|
||||||
it.session().toSessionData(
|
val sessionData = client.session()
|
||||||
|
.toSessionData(
|
||||||
isTokenValid = true,
|
isTokenValid = true,
|
||||||
loginType = LoginType.PASSWORD,
|
loginType = LoginType.PASSWORD,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
sessionPath = sessionPath,
|
sessionPath = sessionPath,
|
||||||
)
|
)
|
||||||
}
|
clear()
|
||||||
sessionStore.storeData(sessionData)
|
sessionStore.storeData(sessionData)
|
||||||
SessionId(sessionData.userId)
|
SessionId(sessionData.userId)
|
||||||
}.mapFailure { failure ->
|
}.mapFailure { failure ->
|
||||||
|
|
@ -161,14 +149,15 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var pendingOidcAuthenticationData: OidcAuthenticationData? = null
|
private var pendingOidcAuthorizationData: OidcAuthorizationData? = null
|
||||||
|
|
||||||
override suspend fun getOidcUrl(): Result<OidcDetails> {
|
override suspend fun getOidcUrl(): Result<OidcDetails> {
|
||||||
return withContext(coroutineDispatchers.io) {
|
return withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val oidcAuthenticationData = authService.urlForOidcLogin()
|
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||||
|
val oidcAuthenticationData = client.urlForOidcLogin(oidcConfigurationProvider.get())
|
||||||
val url = oidcAuthenticationData.loginUrl()
|
val url = oidcAuthenticationData.loginUrl()
|
||||||
pendingOidcAuthenticationData = oidcAuthenticationData
|
pendingOidcAuthorizationData = oidcAuthenticationData
|
||||||
OidcDetails(url)
|
OidcDetails(url)
|
||||||
}.mapFailure { failure ->
|
}.mapFailure { failure ->
|
||||||
failure.mapAuthenticationException()
|
failure.mapAuthenticationException()
|
||||||
|
|
@ -179,8 +168,8 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
override suspend fun cancelOidcLogin(): Result<Unit> {
|
override suspend fun cancelOidcLogin(): Result<Unit> {
|
||||||
return withContext(coroutineDispatchers.io) {
|
return withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
pendingOidcAuthenticationData?.close()
|
pendingOidcAuthorizationData?.close()
|
||||||
pendingOidcAuthenticationData = null
|
pendingOidcAuthorizationData = null
|
||||||
}.mapFailure { failure ->
|
}.mapFailure { failure ->
|
||||||
failure.mapAuthenticationException()
|
failure.mapAuthenticationException()
|
||||||
}
|
}
|
||||||
|
|
@ -193,18 +182,18 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> {
|
override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> {
|
||||||
return withContext(coroutineDispatchers.io) {
|
return withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val urlForOidcLogin = pendingOidcAuthenticationData ?: error("You need to call `getOidcUrl()` first")
|
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||||
val client = authService.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
|
val urlForOidcLogin = pendingOidcAuthorizationData ?: error("You need to call `getOidcUrl()` first")
|
||||||
val sessionData = client.use {
|
client.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
|
||||||
it.session().toSessionData(
|
val sessionData = client.session().toSessionData(
|
||||||
isTokenValid = true,
|
isTokenValid = true,
|
||||||
loginType = LoginType.OIDC,
|
loginType = LoginType.OIDC,
|
||||||
passphrase = pendingPassphrase,
|
passphrase = pendingPassphrase,
|
||||||
sessionPath = sessionPath,
|
sessionPath = sessionPath,
|
||||||
)
|
)
|
||||||
}
|
clear()
|
||||||
pendingOidcAuthenticationData?.close()
|
pendingOidcAuthorizationData?.close()
|
||||||
pendingOidcAuthenticationData = null
|
pendingOidcAuthorizationData = null
|
||||||
sessionStore.storeData(sessionData)
|
sessionStore.storeData(sessionData)
|
||||||
SessionId(sessionData.userId)
|
SessionId(sessionData.userId)
|
||||||
}.mapFailure { failure ->
|
}.mapFailure { failure ->
|
||||||
|
|
@ -216,8 +205,7 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) =
|
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) =
|
||||||
withContext(coroutineDispatchers.io) {
|
withContext(coroutineDispatchers.io) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val client = rustMatrixClientFactory.getBaseClientBuilder(sessionPath)
|
val client = rustMatrixClientFactory.getBaseClientBuilder(sessionPath, pendingPassphrase)
|
||||||
.passphrase(pendingPassphrase)
|
|
||||||
.buildWithQrCode(
|
.buildWithQrCode(
|
||||||
qrCodeData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData,
|
qrCodeData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData,
|
||||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
oidcConfiguration = oidcConfigurationProvider.get(),
|
||||||
|
|
@ -252,4 +240,13 @@ class RustMatrixAuthenticationService @Inject constructor(
|
||||||
Timber.e(throwable, "Failed to login with QR code")
|
Timber.e(throwable, "Failed to login with QR code")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getBaseClientBuilder() = rustMatrixClientFactory
|
||||||
|
.getBaseClientBuilder(sessionPath, pendingPassphrase)
|
||||||
|
.requiresSlidingSync()
|
||||||
|
|
||||||
|
private fun clear() {
|
||||||
|
currentClient?.close()
|
||||||
|
currentClient = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,13 @@ internal fun Session.toSessionData(
|
||||||
loginType: LoginType,
|
loginType: LoginType,
|
||||||
passphrase: String?,
|
passphrase: String?,
|
||||||
sessionPath: String,
|
sessionPath: String,
|
||||||
|
homeserverUrl: String? = null,
|
||||||
) = SessionData(
|
) = SessionData(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
deviceId = deviceId,
|
deviceId = deviceId,
|
||||||
accessToken = accessToken,
|
accessToken = accessToken,
|
||||||
refreshToken = refreshToken,
|
refreshToken = refreshToken,
|
||||||
homeserverUrl = homeserverUrl,
|
homeserverUrl = homeserverUrl ?: this.homeserverUrl,
|
||||||
oidcData = oidcData,
|
oidcData = oidcData,
|
||||||
slidingSyncProxy = slidingSyncProxy,
|
slidingSyncProxy = slidingSyncProxy,
|
||||||
loginTimestamp = Date(),
|
loginTimestamp = Date(),
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
|
||||||
import org.matrix.rustcomponents.sdk.FormattedBody
|
import org.matrix.rustcomponents.sdk.FormattedBody
|
||||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||||
|
|
@ -252,7 +251,6 @@ class RustTimeline(
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
inner.close()
|
inner.close()
|
||||||
specialModeEventTimelineItem?.destroy()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchMembers() = withContext(dispatcher) {
|
private suspend fun fetchMembers() = withContext(dispatcher) {
|
||||||
|
|
@ -329,14 +327,10 @@ class RustTimeline(
|
||||||
runCatching<Unit> {
|
runCatching<Unit> {
|
||||||
when {
|
when {
|
||||||
originalEventId != null -> {
|
originalEventId != null -> {
|
||||||
val editedEvent = specialModeEventTimelineItem ?: inner.getEventTimelineItemByEventId(originalEventId.value)
|
inner.edit(
|
||||||
editedEvent.use {
|
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
||||||
inner.edit(
|
eventId = originalEventId.value,
|
||||||
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
)
|
||||||
editItem = it,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
specialModeEventTimelineItem = null
|
|
||||||
}
|
}
|
||||||
transactionId != null -> {
|
transactionId != null -> {
|
||||||
error("Editing local echo is not supported yet.")
|
error("Editing local echo is not supported yet.")
|
||||||
|
|
@ -348,18 +342,6 @@ class RustTimeline(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var specialModeEventTimelineItem: EventTimelineItem? = null
|
|
||||||
|
|
||||||
override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> = withContext(dispatcher) {
|
|
||||||
runCatching {
|
|
||||||
specialModeEventTimelineItem?.destroy()
|
|
||||||
specialModeEventTimelineItem = null
|
|
||||||
specialModeEventTimelineItem = eventId?.let { inner.getEventTimelineItemByEventId(it.value) }
|
|
||||||
}.onFailure {
|
|
||||||
Timber.e(it, "Unable to retrieve event for special mode. Are you using the correct timeline?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun replyMessage(
|
override suspend fun replyMessage(
|
||||||
eventId: EventId,
|
eventId: EventId,
|
||||||
body: String,
|
body: String,
|
||||||
|
|
@ -369,19 +351,7 @@ class RustTimeline(
|
||||||
): Result<Unit> = withContext(dispatcher) {
|
): Result<Unit> = withContext(dispatcher) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val msg = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map())
|
val msg = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map())
|
||||||
if (fromNotification) {
|
inner.sendReply(msg, eventId.value)
|
||||||
// When replying from a notification, do not interfere with `specialModeEventTimelineItem`
|
|
||||||
val inReplyTo = inner.getEventTimelineItemByEventId(eventId.value)
|
|
||||||
inReplyTo.use { eventTimelineItem ->
|
|
||||||
inner.sendReply(msg, eventTimelineItem)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val inReplyTo = specialModeEventTimelineItem ?: inner.getEventTimelineItemByEventId(eventId.value)
|
|
||||||
inReplyTo.use { eventTimelineItem ->
|
|
||||||
inner.sendReply(msg, eventTimelineItem)
|
|
||||||
}
|
|
||||||
specialModeEventTimelineItem = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import com.google.common.truth.ThrowableSubject
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
import org.matrix.rustcomponents.sdk.ClientBuildException
|
||||||
|
|
||||||
class AuthenticationExceptionMappingTest {
|
class AuthenticationExceptionMappingTest {
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -39,64 +39,21 @@ class AuthenticationExceptionMappingTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `mapping specific exceptions map to their kotlin counterparts`() {
|
fun `mapping specific exceptions map to their kotlin counterparts`() {
|
||||||
assertThat(RustAuthenticationException.ClientMissing("Client missing").mapAuthenticationException())
|
assertThat(ClientBuildException.Generic("Unknown error").mapAuthenticationException())
|
||||||
.isException<AuthenticationException.ClientMissing>("Client missing")
|
.isException<AuthenticationException.Generic>("Unknown error")
|
||||||
|
|
||||||
assertThat(RustAuthenticationException.Generic("Generic").mapAuthenticationException()).isException<AuthenticationException.Generic>("Generic")
|
assertThat(ClientBuildException.InvalidServerName("Invalid server name").mapAuthenticationException())
|
||||||
|
|
||||||
assertThat(RustAuthenticationException.InvalidServerName("Invalid server name").mapAuthenticationException())
|
|
||||||
.isException<AuthenticationException.InvalidServerName>("Invalid server name")
|
.isException<AuthenticationException.InvalidServerName>("Invalid server name")
|
||||||
|
|
||||||
assertThat(RustAuthenticationException.SessionMissing("Session missing").mapAuthenticationException())
|
assertThat(ClientBuildException.Sdk("SDK issue").mapAuthenticationException())
|
||||||
.isException<AuthenticationException.SessionMissing>("Session missing")
|
.isException<AuthenticationException.Generic>("SDK issue")
|
||||||
|
|
||||||
assertThat(RustAuthenticationException.SlidingSyncNotAvailable("Sliding sync not available").mapAuthenticationException())
|
assertThat(ClientBuildException.SlidingSyncNotAvailable("Sliding sync not available").mapAuthenticationException())
|
||||||
.isException<AuthenticationException.SlidingSyncNotAvailable>("Sliding sync not available")
|
.isException<AuthenticationException.SlidingSyncNotAvailable>("Sliding sync not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `mapping Oidc related exceptions creates an 'OidcError' with different types`() {
|
|
||||||
assertIsOidcError(
|
|
||||||
throwable = RustAuthenticationException.OidcException("Oidc exception"),
|
|
||||||
type = "OidcException",
|
|
||||||
message = "Oidc exception"
|
|
||||||
)
|
|
||||||
assertIsOidcError(
|
|
||||||
throwable = RustAuthenticationException.OidcMetadataInvalid("Oidc metadata invalid"),
|
|
||||||
type = "OidcMetadataInvalid",
|
|
||||||
message = "Oidc metadata invalid"
|
|
||||||
)
|
|
||||||
assertIsOidcError(
|
|
||||||
throwable = RustAuthenticationException.OidcMetadataMissing("Oidc metadata missing"),
|
|
||||||
type = "OidcMetadataMissing",
|
|
||||||
message = "Oidc metadata missing"
|
|
||||||
)
|
|
||||||
assertIsOidcError(
|
|
||||||
throwable = RustAuthenticationException.OidcNotSupported("Oidc not supported"),
|
|
||||||
type = "OidcNotSupported",
|
|
||||||
message = "Oidc not supported"
|
|
||||||
)
|
|
||||||
assertIsOidcError(
|
|
||||||
throwable = RustAuthenticationException.OidcCancelled("Oidc cancelled"),
|
|
||||||
type = "OidcCancelled",
|
|
||||||
message = "Oidc cancelled"
|
|
||||||
)
|
|
||||||
assertIsOidcError(
|
|
||||||
throwable = RustAuthenticationException.OidcCallbackUrlInvalid("Oidc callback url invalid"),
|
|
||||||
type = "OidcCallbackUrlInvalid",
|
|
||||||
message = "Oidc callback url invalid"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T> ThrowableSubject.isException(message: String) {
|
private inline fun <reified T> ThrowableSubject.isException(message: String) {
|
||||||
isInstanceOf(T::class.java)
|
isInstanceOf(T::class.java)
|
||||||
hasMessageThat().isEqualTo(message)
|
hasMessageThat().isEqualTo(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertIsOidcError(throwable: Throwable, type: String, message: String) {
|
|
||||||
val authenticationException = throwable.mapAuthenticationException()
|
|
||||||
assertThat(authenticationException).isInstanceOf(AuthenticationException.OidcError::class.java)
|
|
||||||
assertThat((authenticationException as? AuthenticationException.OidcError)?.type).isEqualTo(type)
|
|
||||||
assertThat(authenticationException.message).isEqualTo(message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,12 +104,6 @@ class FakeTimeline(
|
||||||
mentions
|
mentions
|
||||||
)
|
)
|
||||||
|
|
||||||
var enterSpecialModeLambda: (eventId: EventId?) -> Result<Unit> = {
|
|
||||||
Result.success(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun enterSpecialMode(eventId: EventId?): Result<Unit> = enterSpecialModeLambda(eventId)
|
|
||||||
|
|
||||||
var replyMessageLambda: (
|
var replyMessageLambda: (
|
||||||
eventId: EventId,
|
eventId: EventId,
|
||||||
body: String,
|
body: String,
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ class MainActivity : ComponentActivity() {
|
||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory,
|
||||||
coroutineDispatchers = Singleton.coroutineDispatchers,
|
coroutineDispatchers = Singleton.coroutineDispatchers,
|
||||||
sessionStore = sessionStore,
|
sessionStore = sessionStore,
|
||||||
userAgentProvider = userAgentProvider,
|
|
||||||
rustMatrixClientFactory = RustMatrixClientFactory(
|
rustMatrixClientFactory = RustMatrixClientFactory(
|
||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory,
|
||||||
cacheDirectory = applicationContext.cacheDir,
|
cacheDirectory = applicationContext.cacheDir,
|
||||||
|
|
@ -65,8 +64,6 @@ class MainActivity : ComponentActivity() {
|
||||||
utdTracker = UtdTracker(NoopAnalyticsService()),
|
utdTracker = UtdTracker(NoopAnalyticsService()),
|
||||||
),
|
),
|
||||||
passphraseGenerator = NullPassphraseGenerator(),
|
passphraseGenerator = NullPassphraseGenerator(),
|
||||||
userCertificatesProvider = userCertificatesProvider,
|
|
||||||
proxyProvider = proxyProvider,
|
|
||||||
oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory),
|
oidcConfigurationProvider = OidcConfigurationProvider(baseDirectory),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue