chore: account MFA

This commit is contained in:
hstyi
2025-06-26 09:56:38 +08:00
committed by hstyi
parent 17082c5fb8
commit 7ba8e177b1
5 changed files with 28 additions and 9 deletions

View File

@@ -37,8 +37,9 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
}
private val serverComboBox = OutlineComboBox<Server>()
private val usernameTextField = OutlineTextField()
private val usernameTextField = OutlineTextField(128)
private val passwordField = OutlinePasswordField()
private val mfaTextField = OutlineTextField(128)
private val okAction = OkAction(I18n.getString("termora.settings.account.login"))
private val cancelAction = super.createCancelAction()
private val cancelButton = super.createJButtonForAction(cancelAction)
@@ -70,7 +71,7 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
override fun createCenterPanel(): JComponent {
val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, pref",
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN"
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN"
)
var rows = 1
@@ -92,6 +93,8 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
serverComboBox.addItem(Server(server.name, server.server))
}
mfaTextField.placeholderText = I18n.getString("termora.settings.account.mfa")
serverComboBox.renderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(
list: JList<*>?,
@@ -227,6 +230,8 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
.add(registerLink).xy(5, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
.add(passwordField).xy(3, rows).apply { rows += step }
.add("MFA:").xy(1, rows)
.add(mfaTextField).xy(3, rows).apply { rows += step }
.build()
}
@@ -336,6 +341,7 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
okAction.isEnabled = false
usernameTextField.isEnabled = false
passwordField.isEnabled = false
mfaTextField.isEnabled = false
serverComboBox.isEnabled = false
cancelButton.isVisible = false
onLogin(server)
@@ -357,7 +363,10 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
val loginJob = swingCoroutineScope.launch(Dispatchers.IO) {
try {
ServerManager.getInstance().login(server, usernameTextField.text, String(passwordField.password))
ServerManager.getInstance().login(
server, usernameTextField.text,
String(passwordField.password), mfaTextField.text.trim()
)
withContext(Dispatchers.Swing) {
super.doOKAction()
}
@@ -382,6 +391,7 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
passwordField.isEnabled = true
serverComboBox.isEnabled = true
cancelButton.isVisible = true
mfaTextField.isEnabled = true
}
isLoggingIn.compareAndSet(true, false)
}

View File

@@ -28,7 +28,7 @@ class ServerManager private constructor() {
/**
* 登录,不报错就是登录成功
*/
fun login(server: Server, username: String, password: String) {
fun login(server: Server, username: String, password: String, mfa: String) {
if (accountManager.isLocally().not()) {
throw IllegalStateException("Already logged in")
@@ -39,19 +39,19 @@ class ServerManager private constructor() {
}
try {
doLogin(server, username, password)
doLogin(server, username, password, mfa)
} finally {
isLoggingIn.compareAndSet(true, false)
}
}
private fun doLogin(server: Server, username: String, password: String) {
private fun doLogin(server: Server, username: String, password: String, mfa: String) {
// 服务器信息
val serverInfo = getServerInfo(server)
// call login
val loginResponse = callLogin(serverInfo, server, username, password)
val loginResponse = callLogin(serverInfo, server, username, password, mfa)
// call me
val meResponse = callMe(server, loginResponse.accessToken)
@@ -106,10 +106,16 @@ class ServerManager private constructor() {
return ohMyJson.decodeFromString<ServerInfo>(AccountHttp.execute(request = request))
}
private fun callLogin(serverInfo: ServerInfo, server: Server, username: String, password: String): LoginResponse {
private fun callLogin(
serverInfo: ServerInfo,
server: Server,
username: String,
password: String,
mfa: String
): LoginResponse {
val passwordHex = DigestUtils.sha256Hex("${serverInfo.salt}:${username}:${password}")
val requestBody = ohMyJson.encodeToString(mapOf("email" to username, "password" to passwordHex))
val requestBody = ohMyJson.encodeToString(mapOf("email" to username, "password" to passwordHex, "mfa" to mfa))
.toRequestBody("application/json".toMediaType())
val request = Request.Builder()

View File

@@ -100,6 +100,7 @@ termora.settings.account.register=Register
termora.settings.account.not-support-register=This server does not support account registration
termora.settings.account.login=Log in
termora.settings.account.server=Server
termora.settings.account.mfa=MFA is optional
termora.settings.account.locally=locally
termora.settings.account.lifetime=Lifetime
termora.settings.account.upgrade=Upgrade

View File

@@ -109,6 +109,7 @@ termora.settings.account.login=登录
termora.settings.account.register=注册
termora.settings.account.not-support-register=该服务器不支持注册账号
termora.settings.account.server=服务器
termora.settings.account.mfa=多因素验证是可选的
termora.settings.account.locally=本地的
termora.settings.account.lifetime=长期
termora.settings.account.verify=验证

View File

@@ -121,6 +121,7 @@ termora.settings.account.login=登入
termora.settings.account.register=註冊
termora.settings.account.not-support-register=此伺服器不支援註冊帳號
termora.settings.account.server=伺服器
termora.settings.account.mfa=多因素驗證是可選的
termora.settings.account.locally=本地的
termora.settings.account.lifetime=長期
termora.settings.account.verify=驗證