mirror of
https://github.com/mamoe/mirai.git
synced 2024-12-29 10:00:13 +08:00
[core/auth] Wrap exception from BotAuthorization as BotAuthorizationException; add tests
This commit is contained in:
parent
b18e62b3b6
commit
bda5d54bd3
@ -5370,6 +5370,10 @@ public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
||||
public final class net/mamoe/mirai/message/data/visitor/MessageVisitorKt {
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/network/BotAuthorizationException : net/mamoe/mirai/network/LoginFailedException {
|
||||
public final fun getAuthorization ()Lnet/mamoe/mirai/auth/BotAuthorization;
|
||||
}
|
||||
|
||||
public abstract class net/mamoe/mirai/network/CustomLoginFailedException : net/mamoe/mirai/network/LoginFailedException {
|
||||
public fun <init> (Z)V
|
||||
public fun <init> (ZLjava/lang/String;)V
|
||||
|
@ -5370,6 +5370,10 @@ public final class net/mamoe/mirai/message/data/XmlMessageBuilder$ItemBuilder {
|
||||
public final class net/mamoe/mirai/message/data/visitor/MessageVisitorKt {
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/network/BotAuthorizationException : net/mamoe/mirai/network/LoginFailedException {
|
||||
public final fun getAuthorization ()Lnet/mamoe/mirai/auth/BotAuthorization;
|
||||
}
|
||||
|
||||
public abstract class net/mamoe/mirai/network/CustomLoginFailedException : net/mamoe/mirai/network/LoginFailedException {
|
||||
public fun <init> (Z)V
|
||||
public fun <init> (ZLjava/lang/String;)V
|
||||
|
@ -12,6 +12,7 @@
|
||||
package net.mamoe.mirai.network
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.auth.BotAuthorization
|
||||
import net.mamoe.mirai.utils.LoginSolver
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
|
||||
@ -75,6 +76,19 @@ public class NoStandardInputForCaptchaException @MiraiInternalApi constructor(
|
||||
public override val cause: Throwable? = null
|
||||
) : LoginFailedException(true, "no standard input for captcha")
|
||||
|
||||
/**
|
||||
* 表示在登录过程中, [BotAuthorization] 抛出的异常.
|
||||
* @since 2.15
|
||||
*/
|
||||
public class BotAuthorizationException @MiraiInternalApi constructor(
|
||||
public val authorization: BotAuthorization,
|
||||
cause: Throwable?,
|
||||
) : LoginFailedException(
|
||||
killBot = true,
|
||||
"BotAuthorization(${authorization}) threw an exception during authorization process. See cause below.",
|
||||
cause
|
||||
)
|
||||
|
||||
/**
|
||||
* 当前 [LoginSolver] 不支持此验证方式
|
||||
*
|
||||
|
@ -11,5 +11,13 @@ package net.mamoe.mirai.utils.channels
|
||||
|
||||
public class ProducerFailureException(
|
||||
override val message: String? = "Producer failed to produce a value, see cause",
|
||||
override val cause: Throwable?
|
||||
) : Exception()
|
||||
override var cause: Throwable?
|
||||
) : Exception() {
|
||||
private val unwrapped: Throwable by lazy {
|
||||
val cause = cause ?: return@lazy this
|
||||
this.cause = null
|
||||
cause.also { addSuppressed(this) }
|
||||
}
|
||||
|
||||
public fun unwrap(): Throwable = unwrapped
|
||||
}
|
@ -285,7 +285,8 @@ class OnDemandChannelTest {
|
||||
}
|
||||
assertTrue { channel.isClosed }
|
||||
|
||||
// The exception looks like this, though I don't know why there are two causes.
|
||||
// The exception looks like this.
|
||||
// The first cause is stacktrace-recovered by coroutines, and the second is the original one.
|
||||
|
||||
//net.mamoe.mirai.utils.channels.ProducerFailureException: Producer failed to produce a value, see cause
|
||||
// at net.mamoe.mirai.utils.channels.CoroutineOnDemandReceiveChannel.receiveOrNull(OnDemandChannelImpl.kt:164)
|
||||
|
@ -17,8 +17,13 @@ import net.mamoe.mirai.utils.TestOnly
|
||||
|
||||
internal class BotAccount(
|
||||
internal val id: Long,
|
||||
val authorization: BotAuthorization,
|
||||
authorization: BotAuthorization,
|
||||
) {
|
||||
var authorization: BotAuthorization = authorization
|
||||
// FIXME: Making this mutable is very bad.
|
||||
// But I had to do this because the current test framework is bad, and I don't have time to do a major rewrite.
|
||||
@TestOnly set
|
||||
|
||||
@TestOnly // to be compatible with your local tests :)
|
||||
constructor(
|
||||
id: Long, pwd: String
|
||||
|
@ -58,7 +58,7 @@ internal class AuthControl(
|
||||
val rsp = try {
|
||||
userDecisions.receiveOrNull() ?: SsoProcessorImpl.AuthMethod.NotAvailable
|
||||
} catch (e: ProducerFailureException) {
|
||||
SsoProcessorImpl.AuthMethod.Error(e)
|
||||
SsoProcessorImpl.AuthMethod.Error(e.unwrap())
|
||||
}
|
||||
|
||||
logger.debug { "[AuthControl/acquire] Authorization responded: $rsp" }
|
||||
|
@ -32,10 +32,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.UrlDeviceVerificat
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse.Captcha
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.*
|
||||
import net.mamoe.mirai.network.LoginFailedException
|
||||
import net.mamoe.mirai.network.RetryLaterException
|
||||
import net.mamoe.mirai.network.UnsupportedSliderCaptchaException
|
||||
import net.mamoe.mirai.network.WrongPasswordException
|
||||
import net.mamoe.mirai.network.*
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
@ -117,7 +114,7 @@ internal interface SsoSession {
|
||||
*
|
||||
* Used by `NettyNetworkHandler.StateConnecting`.
|
||||
*/
|
||||
internal class SsoProcessorImpl(
|
||||
internal open class SsoProcessorImpl(
|
||||
val ssoContext: SsoProcessorContext,
|
||||
) : SsoProcessor {
|
||||
|
||||
@ -155,10 +152,22 @@ internal class SsoProcessorImpl(
|
||||
get() = ssoContext.bot.configuration
|
||||
}
|
||||
|
||||
protected open suspend fun doSlowLogin(
|
||||
handler: NetworkHandler,
|
||||
loginType: LoginType
|
||||
) {
|
||||
SlowLoginImpl(handler, loginType).doLogin()
|
||||
}
|
||||
|
||||
protected open suspend fun doFastLogin(handler: NetworkHandler) {
|
||||
FastLoginImpl(handler).doLogin()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do login. Throws [LoginFailedException] if failed
|
||||
* Throws [LoginFailedException] if failed. Any other exceptions are considered as internal error.
|
||||
*/
|
||||
override suspend fun login(handler: NetworkHandler) {
|
||||
final override suspend fun login(handler: NetworkHandler) {
|
||||
|
||||
fun initAndStartAuthControl() {
|
||||
authControl = AuthControl(
|
||||
@ -194,7 +203,7 @@ internal class SsoProcessorImpl(
|
||||
if (client.wLoginSigInfoInitialized) {
|
||||
ssoContext.bot.components[EcdhInitialPublicKeyUpdater].refreshInitialPublicKeyAndApplyEcdh()
|
||||
kotlin.runCatching {
|
||||
FastLoginImpl(handler).doLogin()
|
||||
doFastLogin(handler)
|
||||
}.onFailure { e ->
|
||||
initAndStartAuthControl()
|
||||
authControl!!.exceptionCollector.collect(e)
|
||||
@ -220,7 +229,7 @@ internal class SsoProcessorImpl(
|
||||
when (val authw = authControl0.acquireAuth().also { nextAuthMethod = it }) {
|
||||
is AuthMethod.Error -> {
|
||||
authControl = null
|
||||
throw authw.exception
|
||||
throw BotAuthorizationException(ssoContext.account.authorization, authw.exception)
|
||||
}
|
||||
|
||||
AuthMethod.NotAvailable -> {
|
||||
@ -229,7 +238,8 @@ internal class SsoProcessorImpl(
|
||||
}
|
||||
|
||||
is AuthMethod.Pwd -> {
|
||||
SlowLoginImpl(handler, LoginType.Password(authw.passwordMd5)).doLogin()
|
||||
val loginType = LoginType.Password(authw.passwordMd5)
|
||||
doSlowLogin(handler, loginType)
|
||||
}
|
||||
|
||||
AuthMethod.QRCode -> {
|
||||
@ -237,7 +247,8 @@ internal class SsoProcessorImpl(
|
||||
handler, client
|
||||
).process(handler, client)
|
||||
|
||||
SlowLoginImpl(handler, LoginType.QRCode(rsp)).doLogin()
|
||||
val loginType = LoginType.QRCode(rsp)
|
||||
doSlowLogin(handler, loginType)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,7 +282,6 @@ internal class SsoProcessorImpl(
|
||||
|
||||
}
|
||||
|
||||
|
||||
sealed class AuthMethod {
|
||||
object NotAvailable : AuthMethod() {
|
||||
override fun toString(): String = "NotAvailable"
|
||||
@ -288,7 +298,9 @@ internal class SsoProcessorImpl(
|
||||
/**
|
||||
* Exception in [BotAuthorization]
|
||||
*/
|
||||
class Error(val exception: Throwable) : AuthMethod() {
|
||||
class Error(
|
||||
val exception: Throwable // unwrapped
|
||||
) : AuthMethod() {
|
||||
override fun toString(): String = "Error[$exception]@${hashCode()}"
|
||||
}
|
||||
}
|
||||
@ -296,10 +308,6 @@ internal class SsoProcessorImpl(
|
||||
private var authControl: AuthControl? = null
|
||||
|
||||
override suspend fun sendRegister(handler: NetworkHandler): StatSvc.Register.Response {
|
||||
return registerClientOnline(handler).also { registerResp = it }
|
||||
}
|
||||
|
||||
private suspend fun registerClientOnline(handler: NetworkHandler): StatSvc.Register.Response {
|
||||
return handler.sendAndExpect(StatSvc.Register.online(client)).also {
|
||||
registerResp = it
|
||||
}
|
||||
@ -317,7 +325,7 @@ internal class SsoProcessorImpl(
|
||||
|
||||
// we have exactly two methods----slow and fast.
|
||||
|
||||
private abstract inner class LoginStrategy(
|
||||
protected abstract inner class LoginStrategy(
|
||||
val handler: NetworkHandler,
|
||||
) {
|
||||
protected val context get() = handler.context
|
||||
@ -478,12 +486,12 @@ internal class SsoProcessorImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LoginType {
|
||||
protected sealed class LoginType {
|
||||
class Password(val passwordMd5: SecretsProtection.EscapedByteBuffer) : LoginType()
|
||||
class QRCode(val qrCodeLoginData: QRCodeLoginData) : LoginType()
|
||||
}
|
||||
|
||||
private inner class FastLoginImpl(handler: NetworkHandler) : LoginStrategy(handler) {
|
||||
protected inner class FastLoginImpl(handler: NetworkHandler) : LoginStrategy(handler) {
|
||||
override suspend fun doLogin() {
|
||||
val login10 = handler.sendAndExpect(WtLogin10(client))
|
||||
check(login10 is LoginPacketResponse.Success) { "Fast login failed: $login10" }
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.auth
|
||||
|
||||
import net.mamoe.mirai.auth.BotAuthInfo
|
||||
import net.mamoe.mirai.auth.BotAuthResult
|
||||
import net.mamoe.mirai.auth.BotAuthSession
|
||||
import net.mamoe.mirai.auth.BotAuthorization
|
||||
import net.mamoe.mirai.internal.network.components.SsoProcessor
|
||||
import net.mamoe.mirai.internal.network.components.SsoProcessorContext
|
||||
import net.mamoe.mirai.internal.network.components.SsoProcessorImpl
|
||||
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTestWithSelector
|
||||
import net.mamoe.mirai.internal.network.framework.PacketReplierDslBuilder
|
||||
import net.mamoe.mirai.internal.network.framework.buildPacketReplier
|
||||
|
||||
internal abstract class AbstractBotAuthTest : AbstractCommonNHTestWithSelector() {
|
||||
init {
|
||||
// Use real processor to test login
|
||||
overrideComponents[SsoProcessor] = SsoProcessorImpl(overrideComponents[SsoProcessorContext])
|
||||
}
|
||||
|
||||
protected fun setAuthorization(authorize: (session: BotAuthSession, info: BotAuthInfo) -> BotAuthResult) {
|
||||
// Run a real SsoProcessor, just without sending packets
|
||||
bot.account.authorization = object : BotAuthorization {
|
||||
override suspend fun authorize(session: BotAuthSession, info: BotAuthInfo): BotAuthResult {
|
||||
return authorize(session, info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the same replier even after reconnection
|
||||
protected fun usePacketReplierThroughout(builderAction: PacketReplierDslBuilder.() -> Unit) {
|
||||
val replier = buildPacketReplier {
|
||||
builderAction()
|
||||
}
|
||||
onEachNetworkInstance {
|
||||
addPacketReplier(replier) // share the decisions
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.auth
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||
import net.mamoe.mirai.network.BotAuthorizationException
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertIs
|
||||
|
||||
internal class BotAuthorizationTest : AbstractBotAuthTest() {
|
||||
@Test
|
||||
fun `authorization failure throws BotAuthorizationException`() = runTest {
|
||||
// Run a real SsoProcessor, just without sending packets
|
||||
setAuthorization { _, _ ->
|
||||
throw IllegalStateException("Oops")
|
||||
}
|
||||
|
||||
usePacketReplierThroughout {
|
||||
expect(WtLogin.ExchangeEmp) reply { WtLogin.Login.LoginPacketResponse.Error(bot, 1, "", "", "") }
|
||||
expect(WtLogin.Login) reply { WtLogin.Login.LoginPacketResponse.Success(bot) }
|
||||
expect(StatSvc.Register) reply { StatSvc.Register.Response(SvcRespRegister()) }
|
||||
}
|
||||
|
||||
assertFailsWith<BotAuthorizationException> {
|
||||
bot.login()
|
||||
}.run {
|
||||
val cause = cause
|
||||
assertIs<IllegalStateException>(cause)
|
||||
assertEquals("Oops", cause.message)
|
||||
}
|
||||
|
||||
// Stacktrace like this:
|
||||
|
||||
//net.mamoe.mirai.network.BotAuthorizationException: BotAuthorization(net.mamoe.mirai.internal.network.auth.AbstractBotAuthTest$authorization failure throws BotAuthorizationException$1$2@157c6b0f) threw an exception during authorization process. See cause below.
|
||||
// at net.mamoe.mirai.internal.network.components.SsoProcessorImpl.login(SsoProcessor.kt:232)
|
||||
//Caused by: java.lang.IllegalStateException: Oops
|
||||
// at net.mamoe.mirai.internal.network.auth.AbstractBotAuthTest$authorization failure throws BotAuthorizationException$1$2.authorize(AbstractBotAuthTest.kt:44)
|
||||
// (Coroutine boundary)
|
||||
// at net.mamoe.mirai.utils.channels.CoroutineOnDemandReceiveChannel.receiveOrNull(OnDemandChannelImpl.kt:237)
|
||||
// (Coroutine creation stacktrace)
|
||||
// at net.mamoe.mirai.internal.network.handler.CommonNetworkHandler$StateConnecting.startState(CommonNetworkHandler.kt:244)
|
||||
//Caused by: java.lang.IllegalStateException: Oops
|
||||
// at net.mamoe.mirai.internal.network.auth.AbstractBotAuthTest$authorization failure throws BotAuthorizationException$1$2.authorize(AbstractBotAuthTest.kt:44)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user