Better 2fa verification support (#6782)
* Better 2fa verification support Signed-off-by: fufesou <shuanglongchen@yeah.net> * 2FA login verification, add request filed 'tfa_code' Signed-off-by: fufesou <shuanglongchen@yeah.net> * 2fa dialog Signed-off-by: fufesou <shuanglongchen@yeah.net> * msgbox, title Signed-off-by: fufesou <shuanglongchen@yeah.net> * Feat, oidc login, 2fa Signed-off-by: fufesou <shuanglongchen@yeah.net> --------- Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
89150317e1
commit
7e3a0c4ded
@ -11,9 +11,11 @@ class HttpType {
|
|||||||
static const kAuthReqTypeMobile = "mobile";
|
static const kAuthReqTypeMobile = "mobile";
|
||||||
static const kAuthReqTypeSMSCode = "sms_code";
|
static const kAuthReqTypeSMSCode = "sms_code";
|
||||||
static const kAuthReqTypeEmailCode = "email_code";
|
static const kAuthReqTypeEmailCode = "email_code";
|
||||||
|
static const kAuthReqTypeTfaCode = "tfa_code";
|
||||||
|
|
||||||
static const kAuthResTypeToken = "access_token";
|
static const kAuthResTypeToken = "access_token";
|
||||||
static const kAuthResTypeEmailCheck = "email_check";
|
static const kAuthResTypeEmailCheck = "email_check";
|
||||||
|
static const kAuthResTypeTfaCheck = "tfa_check";
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UserStatus { kDisabled, kNormal, kUnverified }
|
enum UserStatus { kDisabled, kNormal, kUnverified }
|
||||||
@ -118,6 +120,7 @@ class LoginRequest {
|
|||||||
bool? autoLogin;
|
bool? autoLogin;
|
||||||
String? type;
|
String? type;
|
||||||
String? verificationCode;
|
String? verificationCode;
|
||||||
|
String? tfaCode;
|
||||||
|
|
||||||
LoginRequest(
|
LoginRequest(
|
||||||
{this.username,
|
{this.username,
|
||||||
@ -126,7 +129,8 @@ class LoginRequest {
|
|||||||
this.uuid,
|
this.uuid,
|
||||||
this.autoLogin,
|
this.autoLogin,
|
||||||
this.type,
|
this.type,
|
||||||
this.verificationCode});
|
this.verificationCode,
|
||||||
|
this.tfaCode});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
final Map<String, dynamic> data = <String, dynamic>{};
|
||||||
@ -139,6 +143,7 @@ class LoginRequest {
|
|||||||
if (verificationCode != null) {
|
if (verificationCode != null) {
|
||||||
data['verificationCode'] = verificationCode;
|
data['verificationCode'] = verificationCode;
|
||||||
}
|
}
|
||||||
|
if (tfaCode != null) data['tfaCode'] = tfaCode;
|
||||||
|
|
||||||
Map<String, dynamic> deviceInfo = {};
|
Map<String, dynamic> deviceInfo = {};
|
||||||
try {
|
try {
|
||||||
@ -154,13 +159,15 @@ class LoginRequest {
|
|||||||
class LoginResponse {
|
class LoginResponse {
|
||||||
String? access_token;
|
String? access_token;
|
||||||
String? type;
|
String? type;
|
||||||
|
String? tfa_type;
|
||||||
UserPayload? user;
|
UserPayload? user;
|
||||||
|
|
||||||
LoginResponse({this.access_token, this.type, this.user});
|
LoginResponse({this.access_token, this.type, this.tfa_type, this.user});
|
||||||
|
|
||||||
LoginResponse.fromJson(Map<String, dynamic> json) {
|
LoginResponse.fromJson(Map<String, dynamic> json) {
|
||||||
access_token = json['access_token'];
|
access_token = json['access_token'];
|
||||||
type = json['type'];
|
type = json['type'];
|
||||||
|
tfa_type = json['tfa_type'];
|
||||||
user = json['user'] != null ? UserPayload.fromJson(json['user']) : null;
|
user = json['user'] != null ? UserPayload.fromJson(json['user']) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,6 +427,54 @@ Future<bool?> loginDialog() async {
|
|||||||
close(false);
|
close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleLoginResponse(LoginResponse resp, bool storeIfAccessToken,
|
||||||
|
void Function([dynamic])? close) async {
|
||||||
|
switch (resp.type) {
|
||||||
|
case HttpType.kAuthResTypeToken:
|
||||||
|
if (resp.access_token != null) {
|
||||||
|
if (storeIfAccessToken) {
|
||||||
|
await bind.mainSetLocalOption(
|
||||||
|
key: 'access_token', value: resp.access_token!);
|
||||||
|
await bind.mainSetLocalOption(
|
||||||
|
key: 'user_info', value: jsonEncode(resp.user ?? {}));
|
||||||
|
}
|
||||||
|
if (close != null) {
|
||||||
|
close(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HttpType.kAuthResTypeEmailCheck:
|
||||||
|
bool? isEmailVerification;
|
||||||
|
if (resp.tfa_type == null ||
|
||||||
|
resp.tfa_type == HttpType.kAuthResTypeEmailCheck) {
|
||||||
|
isEmailVerification = true;
|
||||||
|
} else if (resp.tfa_type == HttpType.kAuthResTypeTfaCheck) {
|
||||||
|
isEmailVerification = false;
|
||||||
|
} else {
|
||||||
|
passwordMsg = "Failed, bad tfa type from server";
|
||||||
|
}
|
||||||
|
if (isEmailVerification != null) {
|
||||||
|
if (isMobile) {
|
||||||
|
if (close != null) close(false);
|
||||||
|
verificationCodeDialog(resp.user, isEmailVerification);
|
||||||
|
} else {
|
||||||
|
setState(() => isInProgress = false);
|
||||||
|
final res =
|
||||||
|
await verificationCodeDialog(resp.user, isEmailVerification);
|
||||||
|
if (res == true) {
|
||||||
|
if (close != null) close(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
passwordMsg = "Failed, bad response from server";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onLogin() async {
|
onLogin() async {
|
||||||
// validate
|
// validate
|
||||||
if (username.text.isEmpty) {
|
if (username.text.isEmpty) {
|
||||||
@ -447,35 +495,7 @@ Future<bool?> loginDialog() async {
|
|||||||
uuid: await bind.mainGetUuid(),
|
uuid: await bind.mainGetUuid(),
|
||||||
autoLogin: true,
|
autoLogin: true,
|
||||||
type: HttpType.kAuthReqTypeAccount));
|
type: HttpType.kAuthReqTypeAccount));
|
||||||
|
await handleLoginResponse(resp, true, close);
|
||||||
switch (resp.type) {
|
|
||||||
case HttpType.kAuthResTypeToken:
|
|
||||||
if (resp.access_token != null) {
|
|
||||||
await bind.mainSetLocalOption(
|
|
||||||
key: 'access_token', value: resp.access_token!);
|
|
||||||
await bind.mainSetLocalOption(
|
|
||||||
key: 'user_info', value: jsonEncode(resp.user ?? {}));
|
|
||||||
close(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HttpType.kAuthResTypeEmailCheck:
|
|
||||||
if (isMobile) {
|
|
||||||
close(true);
|
|
||||||
verificationCodeDialog(resp.user);
|
|
||||||
} else {
|
|
||||||
setState(() => isInProgress = false);
|
|
||||||
final res = await verificationCodeDialog(resp.user);
|
|
||||||
if (res == true) {
|
|
||||||
close(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
passwordMsg = "Failed, bad response from server";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} on RequestException catch (err) {
|
} on RequestException catch (err) {
|
||||||
passwordMsg = translate(err.cause);
|
passwordMsg = translate(err.cause);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -506,15 +526,21 @@ Future<bool?> loginDialog() async {
|
|||||||
.map((e) => ConfigOP(op: e['name'], icon: e['icon']))
|
.map((e) => ConfigOP(op: e['name'], icon: e['icon']))
|
||||||
.toList(),
|
.toList(),
|
||||||
curOP: curOP,
|
curOP: curOP,
|
||||||
cbLogin: (Map<String, dynamic> authBody) {
|
cbLogin: (Map<String, dynamic> authBody) async {
|
||||||
|
LoginResponse? resp;
|
||||||
try {
|
try {
|
||||||
// access_token is already stored in the rust side.
|
// access_token is already stored in the rust side.
|
||||||
gFFI.userModel.getLoginResponseFromAuthBody(authBody);
|
resp =
|
||||||
|
gFFI.userModel.getLoginResponseFromAuthBody(authBody);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
'Failed to parse oidc login body: "$authBody"');
|
'Failed to parse oidc login body: "$authBody"');
|
||||||
}
|
}
|
||||||
close(true);
|
close(true);
|
||||||
|
|
||||||
|
if (resp != null) {
|
||||||
|
handleLoginResponse(resp, false, null);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -583,7 +609,8 @@ Future<bool?> loginDialog() async {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool?> verificationCodeDialog(UserPayload? user) async {
|
Future<bool?> verificationCodeDialog(
|
||||||
|
UserPayload? user, bool isEmailVerification) async {
|
||||||
var autoLogin = true;
|
var autoLogin = true;
|
||||||
var isInProgress = false;
|
var isInProgress = false;
|
||||||
String? errorText;
|
String? errorText;
|
||||||
@ -614,6 +641,7 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
|
|||||||
try {
|
try {
|
||||||
final resp = await gFFI.userModel.login(LoginRequest(
|
final resp = await gFFI.userModel.login(LoginRequest(
|
||||||
verificationCode: code.text,
|
verificationCode: code.text,
|
||||||
|
tfaCode: isEmailVerification ? null : code.text,
|
||||||
username: user?.name,
|
username: user?.name,
|
||||||
id: await bind.mainGetMyId(),
|
id: await bind.mainGetMyId(),
|
||||||
uuid: await bind.mainGetUuid(),
|
uuid: await bind.mainGetUuid(),
|
||||||
@ -648,20 +676,22 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
|
|||||||
content: Column(
|
content: Column(
|
||||||
children: [
|
children: [
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: user?.email == null,
|
offstage: !isEmailVerification || user?.email == null,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Email", prefixIcon: Icon(Icons.email)),
|
labelText: "Email", prefixIcon: Icon(Icons.email)),
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
controller: TextEditingController(text: user?.email),
|
controller: TextEditingController(text: user?.email),
|
||||||
)),
|
)),
|
||||||
const SizedBox(height: 8),
|
isEmailVerification ? const SizedBox(height: 8) : const Offstage(),
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
title: '${translate("Verification code")}:',
|
title:
|
||||||
|
'${translate(isEmailVerification ? "Verification code" : "2FA code")}:',
|
||||||
controller: code,
|
controller: code,
|
||||||
errorText: errorText,
|
errorText: errorText,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
helperText: translate('verification_tip'),
|
helperText: translate(
|
||||||
|
isEmailVerification ? 'verification_tip' : '2fa_tip'),
|
||||||
),
|
),
|
||||||
/*
|
/*
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
|
@ -95,6 +95,8 @@ pub struct UserPayload {
|
|||||||
pub struct AuthBody {
|
pub struct AuthBody {
|
||||||
pub access_token: String,
|
pub access_token: String,
|
||||||
pub r#type: String,
|
pub r#type: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tfa_type: String,
|
||||||
pub user: UserPayload,
|
pub user: UserPayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,15 +240,17 @@ impl OidcSession {
|
|||||||
while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout {
|
while OIDC_SESSION.read().unwrap().keep_querying && begin.elapsed() < query_timeout {
|
||||||
match Self::query(&api_server, &code_url.code, &id, &uuid) {
|
match Self::query(&api_server, &code_url.code, &id, &uuid) {
|
||||||
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
|
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
|
||||||
if remember_me {
|
if auth_body.r#type == "access_token" {
|
||||||
LocalConfig::set_option(
|
if remember_me {
|
||||||
"access_token".to_owned(),
|
LocalConfig::set_option(
|
||||||
auth_body.access_token.clone(),
|
"access_token".to_owned(),
|
||||||
);
|
auth_body.access_token.clone(),
|
||||||
LocalConfig::set_option(
|
);
|
||||||
"user_info".to_owned(),
|
LocalConfig::set_option(
|
||||||
serde_json::json!({ "name": auth_body.user.name, "status": auth_body.user.status }).to_string(),
|
"user_info".to_owned(),
|
||||||
);
|
serde_json::json!({ "name": auth_body.user.name, "status": auth_body.user.status }).to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OIDC_SESSION
|
OIDC_SESSION
|
||||||
.write()
|
.write()
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "用户提权后,不能显示多个显示器。若要控制多显示器,请安装后再试。"),
|
("capture_display_elevated_connections_tip", "用户提权后,不能显示多个显示器。若要控制多显示器,请安装后再试。"),
|
||||||
("Swap control-command key", "交换Control键和Command键"),
|
("Swap control-command key", "交换Control键和Command键"),
|
||||||
("swap-left-right-mouse", "交换鼠标左右键"),
|
("swap-left-right-mouse", "交换鼠标左右键"),
|
||||||
|
("2FA code", "2FA code"),
|
||||||
|
("2fa_tip", "请输入授权 app 中的 2FA code"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Snímání více displejů není podporováno v uživatelském režimu se zvýšenými oprávněními. Pokud chcete ovládat více displejů, zkuste to znovu po instalaci."),
|
("capture_display_elevated_connections_tip", "Snímání více displejů není podporováno v uživatelském režimu se zvýšenými oprávněními. Pokud chcete ovládat více displejů, zkuste to znovu po instalaci."),
|
||||||
("Swap control-command key", "Prohození klávesy control-command"),
|
("Swap control-command key", "Prohození klávesy control-command"),
|
||||||
("swap-left-right-mouse", "Prohodit levé a pravé tlačítko myši"),
|
("swap-left-right-mouse", "Prohodit levé a pravé tlačítko myši"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Das Erfassen mehrerer Bildschirme wird im erweiterten Benutzermodus nicht unterstützt. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."),
|
("capture_display_elevated_connections_tip", "Das Erfassen mehrerer Bildschirme wird im erweiterten Benutzermodus nicht unterstützt. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."),
|
||||||
("Swap control-command key", "Steuerungs- und Befehlstasten tauschen"),
|
("Swap control-command key", "Steuerungs- und Befehlstasten tauschen"),
|
||||||
("swap-left-right-mouse", "Linke und rechte Maustaste tauschen"),
|
("swap-left-right-mouse", "Linke und rechte Maustaste tauschen"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -210,5 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("input_source_2_tip", "Input source 2"),
|
("input_source_2_tip", "Input source 2"),
|
||||||
("capture_display_elevated_connections_tip", "Capturing multiple displays is not supported in the elevated user mode. Please try again after installation if you want to control multiple displays."),
|
("capture_display_elevated_connections_tip", "Capturing multiple displays is not supported in the elevated user mode. Please try again after installation if you want to control multiple displays."),
|
||||||
("swap-left-right-mouse", "Swap left-right mouse button"),
|
("swap-left-right-mouse", "Swap left-right mouse button"),
|
||||||
|
("2FA code", "2FA code"),
|
||||||
|
("2fa_tip", "Please enter the your 2FA code in the authentication app."),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "La captura de múltiples pantallas en el modo de usaurio con privilegios elevados no está soportada. Por favor, inténtalo de nuevo tras la instalación si quieres controlar múltiples pantallas."),
|
("capture_display_elevated_connections_tip", "La captura de múltiples pantallas en el modo de usaurio con privilegios elevados no está soportada. Por favor, inténtalo de nuevo tras la instalación si quieres controlar múltiples pantallas."),
|
||||||
("Swap control-command key", "Intercambiar teclas control-comando"),
|
("Swap control-command key", "Intercambiar teclas control-comando"),
|
||||||
("swap-left-right-mouse", "Intercambiar botones derecho-izquierdo del ratón"),
|
("swap-left-right-mouse", "Intercambiar botones derecho-izquierdo del ratón"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Mitme ekraani jäädvustamine ei ole kõrgendatud kasutajarežiimis toetatud. Kui soovid juhtida mitut ekraani, palun proovi uuesti pärast paigaldamist."),
|
("capture_display_elevated_connections_tip", "Mitme ekraani jäädvustamine ei ole kõrgendatud kasutajarežiimis toetatud. Kui soovid juhtida mitut ekraani, palun proovi uuesti pärast paigaldamist."),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", "Vaheta vasak ja parem hiirenupp"),
|
("swap-left-right-mouse", "Vaheta vasak ja parem hiirenupp"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "La capture de plusieurs écrans n'est pas prise en charge en mode utilisateur avec privilièges. Veuillez réessayer après l'installation si vous souhaitez contrôler plusieurs écrans."),
|
("capture_display_elevated_connections_tip", "La capture de plusieurs écrans n'est pas prise en charge en mode utilisateur avec privilièges. Veuillez réessayer après l'installation si vous souhaitez contrôler plusieurs écrans."),
|
||||||
("Swap control-command key", "Échanger la touche de controle-commande"),
|
("Swap control-command key", "Échanger la touche de controle-commande"),
|
||||||
("swap-left-right-mouse", "Intervertir le bouton gauche et droit de la souris"),
|
("swap-left-right-mouse", "Intervertir le bouton gauche et droit de la souris"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "La cattura di più display non è supportata nella modalità utente con privilegi elevati. Se vuoi controllare più display riprova dopo l'installazione."),
|
("capture_display_elevated_connections_tip", "La cattura di più display non è supportata nella modalità utente con privilegi elevati. Se vuoi controllare più display riprova dopo l'installazione."),
|
||||||
("Swap control-command key", "Scambia tasto controllo-comando"),
|
("Swap control-command key", "Scambia tasto controllo-comando"),
|
||||||
("swap-left-right-mouse", "Scambia pulsante sinistro-destro mouse"),
|
("swap-left-right-mouse", "Scambia pulsante sinistro-destro mouse"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "권한 상승된 사용자 모드에서는 다중 디스플레이 캡처가 지원되지 않습니다. 다중 디스플레이를 제어하려면 설치 후 재시도하세요."),
|
("capture_display_elevated_connections_tip", "권한 상승된 사용자 모드에서는 다중 디스플레이 캡처가 지원되지 않습니다. 다중 디스플레이를 제어하려면 설치 후 재시도하세요."),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Vairāku displeju uzņemšana netiek atbalstīta paaugstinātā lietotāja režīmā. Lūdzu, mēģiniet vēlreiz pēc instalēšanas, ja vēlaties kontrolēt vairākus displejus."),
|
("capture_display_elevated_connections_tip", "Vairāku displeju uzņemšana netiek atbalstīta paaugstinātā lietotāja režīmā. Lūdzu, mēģiniet vēlreiz pēc instalēšanas, ja vēlaties kontrolēt vairākus displejus."),
|
||||||
("Swap control-command key", "Apmainīt vadības un komandas taustiņu"),
|
("Swap control-command key", "Apmainīt vadības un komandas taustiņu"),
|
||||||
("swap-left-right-mouse", "Apmainīt kreiso un labo peles pogu"),
|
("swap-left-right-mouse", "Apmainīt kreiso un labo peles pogu"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Scannen van meerdere schermen wordt niet ondersteund in de bevoorrechte gebruikersmodus. Als je meerdere schermen wilt bedienen, probeer het dan opnieuw na de installatie."),
|
("capture_display_elevated_connections_tip", "Scannen van meerdere schermen wordt niet ondersteund in de bevoorrechte gebruikersmodus. Als je meerdere schermen wilt bedienen, probeer het dan opnieuw na de installatie."),
|
||||||
("Swap control-command key", "Wissel controle-commando toets"),
|
("Swap control-command key", "Wissel controle-commando toets"),
|
||||||
("swap-left-right-mouse", "wissel-links-rechts-muis"),
|
("swap-left-right-mouse", "wissel-links-rechts-muis"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Przechwytywanie wielu ekranów nie jest obsługiwane w trybie użytkownika z podwyższonym poziomem uprawnień. Jeśli chcesz sterować wieloma wyświetlaczami, spróbuj ponownie po instalacji."),
|
("capture_display_elevated_connections_tip", "Przechwytywanie wielu ekranów nie jest obsługiwane w trybie użytkownika z podwyższonym poziomem uprawnień. Jeśli chcesz sterować wieloma wyświetlaczami, spróbuj ponownie po instalacji."),
|
||||||
("Swap control-command key", "Zamiana przycisków sterujących myszki"),
|
("Swap control-command key", "Zamiana przycisków sterujących myszki"),
|
||||||
("swap-left-right-mouse", "Zamień przyciski myszki (lewy - prawy)"),
|
("swap-left-right-mouse", "Zamień przyciski myszki (lewy - prawy)"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Захват экрана нескольких дисплеев не поддерживается в режиме повышенных прав. Повторите попытку после установки, если хотите управлять несколькими дисплеями."),
|
("capture_display_elevated_connections_tip", "Захват экрана нескольких дисплеев не поддерживается в режиме повышенных прав. Повторите попытку после установки, если хотите управлять несколькими дисплеями."),
|
||||||
("Swap control-command key", "Поменять кнопки управления и команд"),
|
("Swap control-command key", "Поменять кнопки управления и команд"),
|
||||||
("swap-left-right-mouse", "Поменять левую и правую кнопки мыши"),
|
("swap-left-right-mouse", "Поменять левую и правую кнопки мыши"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "Snímanie viacerých displejov nie je podporované v režime privilegovaného používateľa. Ak chcete ovládať viac displejov, skúste to po inštalácii znova."),
|
("capture_display_elevated_connections_tip", "Snímanie viacerých displejov nie je podporované v režime privilegovaného používateľa. Ak chcete ovládať viac displejov, skúste to po inštalácii znova."),
|
||||||
("Swap control-command key", "Vymeniť kláves ovládania a príkazu"),
|
("Swap control-command key", "Vymeniť kláves ovládania a príkazu"),
|
||||||
("swap-left-right-mouse", "Prehodiť ľavé a pravé tlačidlo myši"),
|
("swap-left-right-mouse", "Prehodiť ľavé a pravé tlačidlo myši"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", "В режимі розширених прав захоплення декількох дисплеїв не підтримується. Якщо ви хочете керувати декількома дисплеями, будь ласка, спробуйте це після встановлення."),
|
("capture_display_elevated_connections_tip", "В режимі розширених прав захоплення декількох дисплеїв не підтримується. Якщо ви хочете керувати декількома дисплеями, будь ласка, спробуйте це після встановлення."),
|
||||||
("Swap control-command key", "Поміняти місцями клавіші Control та Command"),
|
("Swap control-command key", "Поміняти місцями клавіші Control та Command"),
|
||||||
("swap-left-right-mouse", "Поміняти місцями ліву та праву кнопки миші"),
|
("swap-left-right-mouse", "Поміняти місцями ліву та праву кнопки миші"),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -578,5 +578,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("capture_display_elevated_connections_tip", ""),
|
("capture_display_elevated_connections_tip", ""),
|
||||||
("Swap control-command key", ""),
|
("Swap control-command key", ""),
|
||||||
("swap-left-right-mouse", ""),
|
("swap-left-right-mouse", ""),
|
||||||
|
("2FA code", ""),
|
||||||
|
("2fa_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -1223,7 +1223,7 @@ function login() {
|
|||||||
if (data.type == 'email_check') {
|
if (data.type == 'email_check') {
|
||||||
abLoading = false;
|
abLoading = false;
|
||||||
show_progress(-1);
|
show_progress(-1);
|
||||||
on_email_check(data);
|
on_2fa_check(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handler.set_local_option("access_token", data.access_token);
|
handler.set_local_option("access_token", data.access_token);
|
||||||
@ -1241,12 +1241,14 @@ function login() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_email_check(last_msg) {
|
function on_2fa_check(last_msg) {
|
||||||
|
var isEmailCheck = !last_msg.tfa_type || last_msg.tfa_type == 'email_check';
|
||||||
|
|
||||||
var emailHint = last_msg.user.email;
|
var emailHint = last_msg.user.email;
|
||||||
msgbox("custom-email-verification-code", translate('Verification code'), <div .form .set-password>
|
msgbox("custom-2fa-verification-code", translate('Verification code'), <div .form .set-password>
|
||||||
<div><span>{translate('Email')}:</span><span>{emailHint}</span></div>
|
{ isEmailCheck && <div><span>{translate('Email')}:</span><span>{emailHint}</span></div> }
|
||||||
<div><span>{translate('Verification code')}:</span><input|text name="verification_code" .outline-focus /></div>
|
<div><span>{translate(isEmailCheck ? 'Verification code' : '2FA code')}:</span><input|text name="verification_code" .outline-focus /></div>
|
||||||
<div style="font-size:0.9em; margin-bottom:1em;">{translate('verification_tip')}</div>
|
<div style="font-size:0.9em; margin-bottom:1em;">{translate(isEmailCheck ? 'verification_tip' : '2fa_tip')}</div>
|
||||||
</div>, "",
|
</div>, "",
|
||||||
function(res=null, show_progress) {
|
function(res=null, show_progress) {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
@ -1258,7 +1260,8 @@ function on_email_check(last_msg) {
|
|||||||
}
|
}
|
||||||
abLoading = true;
|
abLoading = true;
|
||||||
var url = handler.get_api_server();
|
var url = handler.get_api_server();
|
||||||
httpRequest(url + "/api/login", #post, {username: last_msg.user.name, id: my_id, uuid: handler.get_uuid(), type: 'email_code', autoLogin: true, verificationCode: code, deviceInfo: getDeviceInfo()},
|
const loginData = {username: last_msg.user.name, id: my_id, uuid: handler.get_uuid(), type: 'email_code', verificationCode: code, tfaCode: isEmailCheck ? '' : code, deviceInfo: getDeviceInfo()};
|
||||||
|
httpRequest(url + "/api/login", #post, loginData,
|
||||||
function(data) {
|
function(data) {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
abLoading = false;
|
abLoading = false;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user