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
d638d61e2b
commit
4904c4b751
14 changed files with 85 additions and 179 deletions
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
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.di.CacheDirectory
|
||||
import io.element.android.libraries.matrix.impl.analytics.UtdTracker
|
||||
|
|
@ -46,11 +47,9 @@ class RustMatrixClientFactory @Inject constructor(
|
|||
private val utdTracker: UtdTracker,
|
||||
) {
|
||||
suspend fun create(sessionData: SessionData): RustMatrixClient = withContext(coroutineDispatchers.io) {
|
||||
val client = getBaseClientBuilder(sessionData.sessionPath)
|
||||
.homeserverUrl(sessionData.homeserverUrl)
|
||||
val client = getBaseClientBuilder(sessionData.sessionPath, sessionData.passphrase)
|
||||
.serverNameOrHomeserverUrl(sessionData.homeserverUrl)
|
||||
.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() }
|
||||
|
||||
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()
|
||||
.sessionPath(sessionPath)
|
||||
.passphrase(passphrase)
|
||||
.slidingSyncProxy(AuthenticationConfig.SLIDING_SYNC_PROXY_URL)
|
||||
.userAgent(userAgentProvider.provide())
|
||||
.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"))
|
||||
.let {
|
||||
// Sadly ClientBuilder.proxy() does not accept null :/
|
||||
// Tracked by https://github.com/matrix-org/matrix-rust-sdk/issues/3159
|
||||
val proxy = proxyProvider.provides()
|
||||
if (proxy != null) {
|
||||
it.proxy(proxy)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
.run {
|
||||
// Workaround for non-nullable proxy parameter in the SDK, since each call to the ClientBuilder returns a new reference we need to keep
|
||||
proxyProvider.provides()?.let { proxy(it) } ?: this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,22 +17,14 @@
|
|||
package io.element.android.libraries.matrix.impl.auth
|
||||
|
||||
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 {
|
||||
val message = this.message ?: "Unknown error"
|
||||
return when (this) {
|
||||
is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(message)
|
||||
is RustAuthenticationException.Generic -> AuthenticationException.Generic(message)
|
||||
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(message)
|
||||
is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.SdkQrCodeLoginData
|
||||
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.keys.PassphraseGenerator
|
||||
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.LoginType
|
||||
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.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.HumanQrLoginException
|
||||
import org.matrix.rustcomponents.sdk.OidcAuthenticationData
|
||||
import org.matrix.rustcomponents.sdk.QrCodeDecodeException
|
||||
import org.matrix.rustcomponents.sdk.QrLoginProgress
|
||||
import org.matrix.rustcomponents.sdk.QrLoginProgressListener
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import uniffi.matrix_sdk.OidcAuthorizationData
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
import org.matrix.rustcomponents.sdk.AuthenticationService as RustAuthenticationService
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
@SingleIn(AppScope::class)
|
||||
|
|
@ -64,28 +61,15 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
baseDirectory: File,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val sessionStore: SessionStore,
|
||||
userAgentProvider: UserAgentProvider,
|
||||
private val rustMatrixClientFactory: RustMatrixClientFactory,
|
||||
private val passphraseGenerator: PassphraseGenerator,
|
||||
userCertificatesProvider: UserCertificatesProvider,
|
||||
proxyProvider: ProxyProvider,
|
||||
private val oidcConfigurationProvider: OidcConfigurationProvider,
|
||||
) : MatrixAuthenticationService {
|
||||
// Passphrase which will be used for new sessions. Existing sessions will use the passphrase
|
||||
// stored in the SessionData.
|
||||
private val pendingPassphrase = getDatabasePassphrase()
|
||||
private val sessionPath = File(baseDirectory, UUID.randomUUID().toString()).absolutePath
|
||||
private val authService: RustAuthenticationService = RustAuthenticationService(
|
||||
sessionPath = sessionPath,
|
||||
passphrase = pendingPassphrase,
|
||||
proxy = proxyProvider.provides(),
|
||||
userAgent = userAgentProvider.provide(),
|
||||
additionalRootCertificates = userCertificatesProvider.provides(),
|
||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
||||
customSlidingSyncProxy = null,
|
||||
sessionDelegate = null,
|
||||
crossProcessRefreshLockId = null,
|
||||
)
|
||||
private var currentClient: Client? = null
|
||||
private var currentHomeserver = MutableStateFlow<MatrixHomeServerDetails?>(null)
|
||||
|
||||
override fun loggedInStateFlow(): Flow<LoggedInState> {
|
||||
|
|
@ -132,11 +116,14 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
override suspend fun setHomeserver(homeserver: String): Result<Unit> =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
authService.configureHomeserver(homeserver)
|
||||
val homeServerDetails = authService.homeserverDetails()?.map()
|
||||
if (homeServerDetails != null) {
|
||||
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
|
||||
}
|
||||
val client = getBaseClientBuilder()
|
||||
.serverNameOrHomeserverUrl(homeserver)
|
||||
.build()
|
||||
currentClient = client
|
||||
val homeServerDetails = client.homeserverLoginDetails().map()
|
||||
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
|
||||
}.onFailure {
|
||||
clear()
|
||||
}.mapFailure { failure ->
|
||||
failure.mapAuthenticationException()
|
||||
}
|
||||
|
|
@ -145,15 +132,16 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
override suspend fun login(username: String, password: String): Result<SessionId> =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val client = authService.login(username, password, "Element X Android", null)
|
||||
val sessionData = client.use {
|
||||
it.session().toSessionData(
|
||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||
client.login(username, password, "Element X Android", null)
|
||||
val sessionData = client.session()
|
||||
.toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.PASSWORD,
|
||||
passphrase = pendingPassphrase,
|
||||
sessionPath = sessionPath,
|
||||
)
|
||||
}
|
||||
clear()
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}.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> {
|
||||
return withContext(coroutineDispatchers.io) {
|
||||
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()
|
||||
pendingOidcAuthenticationData = oidcAuthenticationData
|
||||
pendingOidcAuthorizationData = oidcAuthenticationData
|
||||
OidcDetails(url)
|
||||
}.mapFailure { failure ->
|
||||
failure.mapAuthenticationException()
|
||||
|
|
@ -179,8 +168,8 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
override suspend fun cancelOidcLogin(): Result<Unit> {
|
||||
return withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
pendingOidcAuthenticationData?.close()
|
||||
pendingOidcAuthenticationData = null
|
||||
pendingOidcAuthorizationData?.close()
|
||||
pendingOidcAuthorizationData = null
|
||||
}.mapFailure { failure ->
|
||||
failure.mapAuthenticationException()
|
||||
}
|
||||
|
|
@ -193,18 +182,18 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
override suspend fun loginWithOidc(callbackUrl: String): Result<SessionId> {
|
||||
return withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val urlForOidcLogin = pendingOidcAuthenticationData ?: error("You need to call `getOidcUrl()` first")
|
||||
val client = authService.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
|
||||
val sessionData = client.use {
|
||||
it.session().toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.OIDC,
|
||||
passphrase = pendingPassphrase,
|
||||
sessionPath = sessionPath,
|
||||
)
|
||||
}
|
||||
pendingOidcAuthenticationData?.close()
|
||||
pendingOidcAuthenticationData = null
|
||||
val client = currentClient ?: error("You need to call `setHomeserver()` first")
|
||||
val urlForOidcLogin = pendingOidcAuthorizationData ?: error("You need to call `getOidcUrl()` first")
|
||||
client.loginWithOidcCallback(urlForOidcLogin, callbackUrl)
|
||||
val sessionData = client.session().toSessionData(
|
||||
isTokenValid = true,
|
||||
loginType = LoginType.OIDC,
|
||||
passphrase = pendingPassphrase,
|
||||
sessionPath = sessionPath,
|
||||
)
|
||||
clear()
|
||||
pendingOidcAuthorizationData?.close()
|
||||
pendingOidcAuthorizationData = null
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}.mapFailure { failure ->
|
||||
|
|
@ -216,8 +205,7 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
override suspend fun loginWithQrCode(qrCodeData: MatrixQrCodeLoginData, progress: (QrCodeLoginStep) -> Unit) =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val client = rustMatrixClientFactory.getBaseClientBuilder(sessionPath)
|
||||
.passphrase(pendingPassphrase)
|
||||
val client = rustMatrixClientFactory.getBaseClientBuilder(sessionPath, pendingPassphrase)
|
||||
.buildWithQrCode(
|
||||
qrCodeData = (qrCodeData as SdkQrCodeLoginData).rustQrCodeData,
|
||||
oidcConfiguration = oidcConfigurationProvider.get(),
|
||||
|
|
@ -252,4 +240,13 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
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,
|
||||
passphrase: String?,
|
||||
sessionPath: String,
|
||||
homeserverUrl: String? = null,
|
||||
) = SessionData(
|
||||
userId = userId,
|
||||
deviceId = deviceId,
|
||||
accessToken = accessToken,
|
||||
refreshToken = refreshToken,
|
||||
homeserverUrl = homeserverUrl,
|
||||
homeserverUrl = homeserverUrl ?: this.homeserverUrl,
|
||||
oidcData = oidcData,
|
||||
slidingSyncProxy = slidingSyncProxy,
|
||||
loginTimestamp = Date(),
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.EventTimelineItem
|
||||
import org.matrix.rustcomponents.sdk.FormattedBody
|
||||
import org.matrix.rustcomponents.sdk.MessageFormat
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||
|
|
@ -252,7 +251,6 @@ class RustTimeline(
|
|||
|
||||
override fun close() {
|
||||
inner.close()
|
||||
specialModeEventTimelineItem?.destroy()
|
||||
}
|
||||
|
||||
private suspend fun fetchMembers() = withContext(dispatcher) {
|
||||
|
|
@ -329,14 +327,10 @@ class RustTimeline(
|
|||
runCatching<Unit> {
|
||||
when {
|
||||
originalEventId != null -> {
|
||||
val editedEvent = specialModeEventTimelineItem ?: inner.getEventTimelineItemByEventId(originalEventId.value)
|
||||
editedEvent.use {
|
||||
inner.edit(
|
||||
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
||||
editItem = it,
|
||||
)
|
||||
}
|
||||
specialModeEventTimelineItem = null
|
||||
inner.edit(
|
||||
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
||||
eventId = originalEventId.value,
|
||||
)
|
||||
}
|
||||
transactionId != null -> {
|
||||
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(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
|
|
@ -369,19 +351,7 @@ class RustTimeline(
|
|||
): Result<Unit> = withContext(dispatcher) {
|
||||
runCatching {
|
||||
val msg = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map())
|
||||
if (fromNotification) {
|
||||
// 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
|
||||
}
|
||||
inner.sendReply(msg, eventId.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.google.common.truth.ThrowableSubject
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||
import org.junit.Test
|
||||
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
||||
import org.matrix.rustcomponents.sdk.ClientBuildException
|
||||
|
||||
class AuthenticationExceptionMappingTest {
|
||||
@Test
|
||||
|
|
@ -39,64 +39,21 @@ class AuthenticationExceptionMappingTest {
|
|||
|
||||
@Test
|
||||
fun `mapping specific exceptions map to their kotlin counterparts`() {
|
||||
assertThat(RustAuthenticationException.ClientMissing("Client missing").mapAuthenticationException())
|
||||
.isException<AuthenticationException.ClientMissing>("Client missing")
|
||||
assertThat(ClientBuildException.Generic("Unknown error").mapAuthenticationException())
|
||||
.isException<AuthenticationException.Generic>("Unknown error")
|
||||
|
||||
assertThat(RustAuthenticationException.Generic("Generic").mapAuthenticationException()).isException<AuthenticationException.Generic>("Generic")
|
||||
|
||||
assertThat(RustAuthenticationException.InvalidServerName("Invalid server name").mapAuthenticationException())
|
||||
assertThat(ClientBuildException.InvalidServerName("Invalid server name").mapAuthenticationException())
|
||||
.isException<AuthenticationException.InvalidServerName>("Invalid server name")
|
||||
|
||||
assertThat(RustAuthenticationException.SessionMissing("Session missing").mapAuthenticationException())
|
||||
.isException<AuthenticationException.SessionMissing>("Session missing")
|
||||
assertThat(ClientBuildException.Sdk("SDK issue").mapAuthenticationException())
|
||||
.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")
|
||||
}
|
||||
|
||||
@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) {
|
||||
isInstanceOf(T::class.java)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue