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 serverComboBox = OutlineComboBox<Server>()
private val usernameTextField = OutlineTextField() private val usernameTextField = OutlineTextField(128)
private val passwordField = OutlinePasswordField() private val passwordField = OutlinePasswordField()
private val mfaTextField = OutlineTextField(128)
private val okAction = OkAction(I18n.getString("termora.settings.account.login")) private val okAction = OkAction(I18n.getString("termora.settings.account.login"))
private val cancelAction = super.createCancelAction() private val cancelAction = super.createCancelAction()
private val cancelButton = super.createJButtonForAction(cancelAction) private val cancelButton = super.createJButtonForAction(cancelAction)
@@ -70,7 +71,7 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
override fun createCenterPanel(): JComponent { override fun createCenterPanel(): JComponent {
val layout = FormLayout( val layout = FormLayout(
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, pref", "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 var rows = 1
@@ -92,6 +93,8 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
serverComboBox.addItem(Server(server.name, server.server)) serverComboBox.addItem(Server(server.name, server.server))
} }
mfaTextField.placeholderText = I18n.getString("termora.settings.account.mfa")
serverComboBox.renderer = object : DefaultListCellRenderer() { serverComboBox.renderer = object : DefaultListCellRenderer() {
override fun getListCellRendererComponent( override fun getListCellRendererComponent(
list: JList<*>?, list: JList<*>?,
@@ -227,6 +230,8 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
.add(registerLink).xy(5, rows).apply { rows += step } .add(registerLink).xy(5, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows) .add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
.add(passwordField).xy(3, rows).apply { rows += step } .add(passwordField).xy(3, rows).apply { rows += step }
.add("MFA:").xy(1, rows)
.add(mfaTextField).xy(3, rows).apply { rows += step }
.build() .build()
} }
@@ -336,6 +341,7 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
okAction.isEnabled = false okAction.isEnabled = false
usernameTextField.isEnabled = false usernameTextField.isEnabled = false
passwordField.isEnabled = false passwordField.isEnabled = false
mfaTextField.isEnabled = false
serverComboBox.isEnabled = false serverComboBox.isEnabled = false
cancelButton.isVisible = false cancelButton.isVisible = false
onLogin(server) onLogin(server)
@@ -357,7 +363,10 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
val loginJob = swingCoroutineScope.launch(Dispatchers.IO) { val loginJob = swingCoroutineScope.launch(Dispatchers.IO) {
try { 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) { withContext(Dispatchers.Swing) {
super.doOKAction() super.doOKAction()
} }
@@ -382,6 +391,7 @@ class LoginServerDialog(owner: Window) : DialogWrapper(owner) {
passwordField.isEnabled = true passwordField.isEnabled = true
serverComboBox.isEnabled = true serverComboBox.isEnabled = true
cancelButton.isVisible = true cancelButton.isVisible = true
mfaTextField.isEnabled = true
} }
isLoggingIn.compareAndSet(true, false) 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()) { if (accountManager.isLocally().not()) {
throw IllegalStateException("Already logged in") throw IllegalStateException("Already logged in")
@@ -39,19 +39,19 @@ class ServerManager private constructor() {
} }
try { try {
doLogin(server, username, password) doLogin(server, username, password, mfa)
} finally { } finally {
isLoggingIn.compareAndSet(true, false) 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) val serverInfo = getServerInfo(server)
// call login // call login
val loginResponse = callLogin(serverInfo, server, username, password) val loginResponse = callLogin(serverInfo, server, username, password, mfa)
// call me // call me
val meResponse = callMe(server, loginResponse.accessToken) val meResponse = callMe(server, loginResponse.accessToken)
@@ -106,10 +106,16 @@ class ServerManager private constructor() {
return ohMyJson.decodeFromString<ServerInfo>(AccountHttp.execute(request = request)) 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 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()) .toRequestBody("application/json".toMediaType())
val request = Request.Builder() 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.not-support-register=This server does not support account registration
termora.settings.account.login=Log in termora.settings.account.login=Log in
termora.settings.account.server=Server termora.settings.account.server=Server
termora.settings.account.mfa=MFA is optional
termora.settings.account.locally=locally termora.settings.account.locally=locally
termora.settings.account.lifetime=Lifetime termora.settings.account.lifetime=Lifetime
termora.settings.account.upgrade=Upgrade termora.settings.account.upgrade=Upgrade

View File

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

View File

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