trust this device to skip 2fa (#9012)

* trust this device to skip 2fa

Signed-off-by: 21pages <sunboeasy@gmail.com>

* Update connection.rs

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
21pages 2024-08-12 18:08:33 +08:00 committed by GitHub
parent 57834840b8
commit 1729ee337f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 845 additions and 22 deletions

View File

@ -1831,6 +1831,7 @@ void changeBot({Function()? callback}) async {
void change2fa({Function()? callback}) async {
if (bind.mainHasValid2FaSync()) {
await bind.mainSetOption(key: "2fa", value: "");
await bind.mainClearTrustedDevices();
callback?.call();
return;
}
@ -1898,6 +1899,7 @@ void enter2FaDialog(
SessionID sessionId, OverlayDialogManager dialogManager) async {
final controller = TextEditingController();
final RxBool submitReady = false.obs;
final RxBool trustThisDevice = false.obs;
dialogManager.dismissAll();
dialogManager.show((setState, close, context) {
@ -1907,7 +1909,7 @@ void enter2FaDialog(
}
submit() {
gFFI.send2FA(sessionId, controller.text.trim());
gFFI.send2FA(sessionId, controller.text.trim(), trustThisDevice.value);
close();
dialogManager.showLoading(translate('Logging in...'),
onCancel: closeConnection);
@ -1921,9 +1923,27 @@ void enter2FaDialog(
onChanged: () => submitReady.value = codeField.isReady,
);
final trustField = Obx(() => CheckboxListTile(
contentPadding: const EdgeInsets.all(0),
dense: true,
controlAffinity: ListTileControlAffinity.leading,
title: Text(translate("Trust this device")),
value: trustThisDevice.value,
onChanged: (value) {
if (value == null) return;
trustThisDevice.value = value;
},
));
return CustomAlertDialog(
title: Text(translate('enter-2fa-title')),
content: codeField,
content: Column(
children: [
codeField,
if (bind.sessionGetEnableTrustedDevices(sessionId: sessionId))
trustField,
],
),
actions: [
dialogButton('Cancel',
onPressed: cancel,
@ -2313,3 +2333,157 @@ void checkUnlockPinDialog(String correctPin, Function() passCallback) {
);
});
}
void confrimDeleteTrustedDevicesDialog(
RxList<TrustedDevice> trustedDevices, RxList<Uint8List> selectedDevices) {
CommonConfirmDialog(gFFI.dialogManager, '${translate('Confirm Delete')}?',
() async {
if (selectedDevices.isEmpty) return;
if (selectedDevices.length == trustedDevices.length) {
await bind.mainClearTrustedDevices();
trustedDevices.clear();
selectedDevices.clear();
} else {
final json = jsonEncode(selectedDevices.map((e) => e.toList()).toList());
await bind.mainRemoveTrustedDevices(json: json);
trustedDevices.removeWhere((element) {
return selectedDevices.contains(element.hwid);
});
selectedDevices.clear();
}
});
}
void manageTrustedDeviceDialog() async {
RxList<TrustedDevice> trustedDevices = (await TrustedDevice.get()).obs;
RxList<Uint8List> selectedDevices = RxList.empty();
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("Manage trusted devices")),
content: trustedDevicesTable(trustedDevices, selectedDevices),
actions: [
Obx(() => dialogButton(translate("Delete"),
onPressed: selectedDevices.isEmpty
? null
: () {
confrimDeleteTrustedDevicesDialog(
trustedDevices,
selectedDevices,
);
},
isOutline: false)
.marginOnly(top: 12)),
dialogButton(translate("Close"), onPressed: close, isOutline: true)
.marginOnly(top: 12),
],
onCancel: close,
);
});
}
class TrustedDevice {
late final Uint8List hwid;
late final int time;
late final String id;
late final String name;
late final String platform;
TrustedDevice.fromJson(Map<String, dynamic> json) {
final hwidList = json['hwid'] as List<dynamic>;
hwid = Uint8List.fromList(hwidList.cast<int>());
time = json['time'];
id = json['id'];
name = json['name'];
platform = json['platform'];
}
String daysRemaining() {
final expiry = time + 90 * 24 * 60 * 60 * 1000;
final remaining = expiry - DateTime.now().millisecondsSinceEpoch;
if (remaining < 0) {
return '0';
}
return (remaining / (24 * 60 * 60 * 1000)).toStringAsFixed(0);
}
static Future<List<TrustedDevice>> get() async {
final List<TrustedDevice> devices = List.empty(growable: true);
try {
final devicesJson = await bind.mainGetTrustedDevices();
if (devicesJson.isNotEmpty) {
final devicesList = json.decode(devicesJson);
if (devicesList is List) {
for (var device in devicesList) {
devices.add(TrustedDevice.fromJson(device));
}
}
}
} catch (e) {
print(e.toString());
}
devices.sort((a, b) => b.time.compareTo(a.time));
return devices;
}
}
Widget trustedDevicesTable(
RxList<TrustedDevice> devices, RxList<Uint8List> selectedDevices) {
RxBool selectAll = false.obs;
setSelectAll() {
if (selectedDevices.isNotEmpty &&
selectedDevices.length == devices.length) {
selectAll.value = true;
} else {
selectAll.value = false;
}
}
devices.listen((_) {
setSelectAll();
});
selectedDevices.listen((_) {
setSelectAll();
});
return FittedBox(
child: Obx(() => DataTable(
columns: [
DataColumn(
label: Checkbox(
value: selectAll.value,
onChanged: (value) {
if (value == true) {
selectedDevices.clear();
selectedDevices.addAll(devices.map((e) => e.hwid));
} else {
selectedDevices.clear();
}
},
)),
DataColumn(label: Text(translate('Platform'))),
DataColumn(label: Text(translate('ID'))),
DataColumn(label: Text(translate('Username'))),
DataColumn(label: Text(translate('Days remaining'))),
],
rows: devices.map((device) {
return DataRow(cells: [
DataCell(Checkbox(
value: selectedDevices.contains(device.hwid),
onChanged: (value) {
if (value == null) return;
if (value) {
selectedDevices.remove(device.hwid);
selectedDevices.add(device.hwid);
} else {
selectedDevices.remove(device.hwid);
}
},
)),
DataCell(Text(device.platform)),
DataCell(Text(device.id)),
DataCell(Text(device.name)),
DataCell(Text(device.daysRemaining())),
]);
}).toList(),
)),
);
}

View File

@ -136,6 +136,7 @@ const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper";
const String kOptionStopService = "stop-service";
const String kOptionDirectxCapture = "enable-directx-capture";
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
const String kOptionEnableTrustedDevices = "enable-trusted-devices";
// buildin opitons
const String kOptionHideServerSetting = "hide-server-settings";

View File

@ -783,8 +783,33 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
onChangedBot(!hasBot.value);
},
).marginOnly(left: _kCheckBoxLeftMargin + 30);
final trust = Row(
children: [
Flexible(
child: Tooltip(
waitDuration: Duration(milliseconds: 300),
message: translate("enable-trusted-devices-tip"),
child: _OptionCheckBox(context, "Enable trusted devices",
kOptionEnableTrustedDevices,
enabled: !locked, update: (v) {
setState(() {});
}),
),
),
if (mainGetBoolOptionSync(kOptionEnableTrustedDevices))
ElevatedButton(
onPressed: locked
? null
: () {
manageTrustedDeviceDialog();
},
child: Text(translate('Manage trusted devices')))
],
).marginOnly(left: 30);
return Column(
children: [tfa, bot],
children: [tfa, bot, trust],
);
}

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
@ -87,6 +88,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
var _hideServer = false;
var _hideProxy = false;
var _hideNetwork = false;
var _enableTrustedDevices = false;
_SettingsState() {
_enableAbr = option2bool(
@ -113,6 +115,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
_hideNetwork =
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
}
@override
@ -243,18 +246,57 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
],
));
final List<AbstractSettingsTile> enhancementsTiles = [];
final List<AbstractSettingsTile> shareScreenTiles = [
final enable2fa = bind.mainHasValid2FaSync();
final List<AbstractSettingsTile> tfaTiles = [
SettingsTile.switchTile(
title: Text(translate('enable-2fa-title')),
initialValue: bind.mainHasValid2FaSync(),
onToggle: (_) async {
initialValue: enable2fa,
onToggle: (v) async {
update() async {
setState(() {});
}
change2fa(callback: update);
if (v == false) {
CommonConfirmDialog(
gFFI.dialogManager, translate('cancel-2fa-confirm-tip'), () {
change2fa(callback: update);
});
} else {
change2fa(callback: update);
}
},
),
if (enable2fa)
SettingsTile.switchTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate('Enable trusted devices')),
Text(translate('enable-trusted-devices-tip'),
style: Theme.of(context).textTheme.bodySmall),
],
),
initialValue: _enableTrustedDevices,
onToggle: isOptionFixed(kOptionEnableTrustedDevices)
? null
: (v) async {
mainSetBoolOption(kOptionEnableTrustedDevices, v);
setState(() {
_enableTrustedDevices = v;
});
},
),
if (enable2fa && _enableTrustedDevices)
SettingsTile(
title: Text(translate('Manage trusted devices')),
trailing: Icon(Icons.arrow_forward_ios),
onPressed: (context) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return _ManageTrustedDevices();
}));
})
];
final List<AbstractSettingsTile> shareScreenTiles = [
SettingsTile.switchTile(
title: Text(translate('Deny LAN discovery')),
initialValue: _denyLANDiscovery,
@ -642,6 +684,11 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
),
],
),
if (isAndroid &&
!disabledSettings &&
!outgoingOnly &&
!hideSecuritySettings)
SettingsSection(title: Text('2FA'), tiles: tfaTiles),
if (isAndroid &&
!disabledSettings &&
!outgoingOnly &&
@ -963,6 +1010,51 @@ class __DisplayPageState extends State<_DisplayPage> {
}
}
class _ManageTrustedDevices extends StatefulWidget {
const _ManageTrustedDevices();
@override
State<_ManageTrustedDevices> createState() => __ManageTrustedDevicesState();
}
class __ManageTrustedDevicesState extends State<_ManageTrustedDevices> {
RxList<TrustedDevice> trustedDevices = RxList.empty(growable: true);
RxList<Uint8List> selectedDevices = RxList.empty();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(translate('Manage trusted devices')),
centerTitle: true,
actions: [
Obx(() => IconButton(
icon: Icon(Icons.delete, color: Colors.white),
onPressed: selectedDevices.isEmpty
? null
: () {
confrimDeleteTrustedDevicesDialog(
trustedDevices, selectedDevices);
}))
],
),
body: FutureBuilder(
future: TrustedDevice.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
final devices = snapshot.data as List<TrustedDevice>;
trustedDevices = devices.obs;
return trustedDevicesTable(trustedDevices, selectedDevices);
}),
);
}
}
class _RadioEntry {
final String label;
final String value;

View File

@ -2611,8 +2611,9 @@ class FFI {
remember: remember);
}
void send2FA(SessionID sessionId, String code) {
bind.sessionSend2Fa(sessionId: sessionId, code: code);
void send2FA(SessionID sessionId, String code, bool trustThisDevice) {
bind.sessionSend2Fa(
sessionId: sessionId, code: code, trustThisDevice: trustThisDevice);
}
/// Close the remote session.

View File

@ -142,7 +142,10 @@ class RustdeskImpl {
}
Future<void> sessionSend2Fa(
{required UuidValue sessionId, required String code, dynamic hint}) {
{required UuidValue sessionId,
required String code,
required bool trustThisDevice,
dynamic hint}) {
return Future(() => js.context.callMethod('setByName', ['send_2fa', code]));
}
@ -1630,5 +1633,22 @@ class RustdeskImpl {
throw UnimplementedError();
}
bool sessionGetEnableTrustedDevices(
{required UuidValue sessionId, dynamic hint}) {
throw UnimplementedError();
}
Future<String> mainGetTrustedDevices({dynamic hint}) {
throw UnimplementedError();
}
Future<void> mainRemoveTrustedDevices({required String json, dynamic hint}) {
throw UnimplementedError();
}
Future<void> mainClearTrustedDevices({dynamic hint}) {
throw UnimplementedError();
}
void dispose() {}
}

View File

@ -82,10 +82,12 @@ message LoginRequest {
string version = 11;
OSLogin os_login = 12;
string my_platform = 13;
bytes hwid = 14;
}
message Auth2FA {
string code = 1;
bytes hwid = 2;
}
message ChatMessage { string text = 1; }
@ -137,6 +139,7 @@ message LoginResponse {
string error = 1;
PeerInfo peer_info = 2;
}
bool enable_trusted_devices = 3;
}
message TouchScaleUpdate {

View File

@ -10,6 +10,7 @@ use std::{
};
use anyhow::Result;
use bytes::Bytes;
use rand::Rng;
use regex::Regex;
use serde as de;
@ -52,6 +53,7 @@ lazy_static::lazy_static! {
static ref CONFIG: RwLock<Config> = RwLock::new(Config::load());
static ref CONFIG2: RwLock<Config2> = RwLock::new(Config2::load());
static ref LOCAL_CONFIG: RwLock<LocalConfig> = RwLock::new(LocalConfig::load());
static ref TRUSTED_DEVICES: RwLock<(Vec<TrustedDevice>, bool)> = Default::default();
static ref ONLINE: Mutex<HashMap<String, i64>> = Default::default();
pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
Some(key) if !key.is_empty() => key,
@ -210,6 +212,8 @@ pub struct Config2 {
serial: i32,
#[serde(default, deserialize_with = "deserialize_string")]
unlock_pin: String,
#[serde(default, deserialize_with = "deserialize_string")]
trusted_devices: String,
#[serde(default)]
socks: Option<Socks5Server>,
@ -998,6 +1002,7 @@ impl Config {
}
config.password = password.into();
config.store();
Self::clear_trusted_devices();
}
pub fn get_permanent_password() -> String {
@ -1104,6 +1109,64 @@ impl Config {
config.store();
}
pub fn get_trusted_devices_json() -> String {
serde_json::to_string(&Self::get_trusted_devices()).unwrap_or_default()
}
pub fn get_trusted_devices() -> Vec<TrustedDevice> {
let (devices, synced) = TRUSTED_DEVICES.read().unwrap().clone();
if synced {
return devices;
}
let devices = CONFIG2.read().unwrap().trusted_devices.clone();
let (devices, succ, store) = decrypt_str_or_original(&devices, PASSWORD_ENC_VERSION);
if succ {
let mut devices: Vec<TrustedDevice> =
serde_json::from_str(&devices).unwrap_or_default();
let len = devices.len();
devices.retain(|d| !d.outdate());
if store || devices.len() != len {
Self::set_trusted_devices(devices.clone());
}
*TRUSTED_DEVICES.write().unwrap() = (devices.clone(), true);
devices
} else {
Default::default()
}
}
fn set_trusted_devices(mut trusted_devices: Vec<TrustedDevice>) {
trusted_devices.retain(|d| !d.outdate());
let devices = serde_json::to_string(&trusted_devices).unwrap_or_default();
let max_len = 1024 * 1024;
if devices.bytes().len() > max_len {
log::error!("Trusted devices too large: {}", devices.bytes().len());
return;
}
let devices = encrypt_str_or_original(&devices, PASSWORD_ENC_VERSION, max_len);
let mut config = CONFIG2.write().unwrap();
config.trusted_devices = devices;
config.store();
*TRUSTED_DEVICES.write().unwrap() = (trusted_devices, true);
}
pub fn add_trusted_device(device: TrustedDevice) {
let mut devices = Self::get_trusted_devices();
devices.retain(|d| d.hwid != device.hwid);
devices.push(device);
Self::set_trusted_devices(devices);
}
pub fn remove_trusted_devices(hwids: &Vec<Bytes>) {
let mut devices = Self::get_trusted_devices();
devices.retain(|d| !hwids.contains(&d.hwid));
Self::set_trusted_devices(devices);
}
pub fn clear_trusted_devices() {
Self::set_trusted_devices(Default::default());
}
pub fn get() -> Config {
return CONFIG.read().unwrap().clone();
}
@ -1934,6 +1997,22 @@ impl Group {
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct TrustedDevice {
pub hwid: Bytes,
pub time: i64,
pub id: String,
pub name: String,
pub platform: String,
}
impl TrustedDevice {
pub fn outdate(&self) -> bool {
const DAYS_90: i64 = 90 * 24 * 60 * 60 * 1000;
self.time + DAYS_90 < crate::get_time()
}
}
deserialize_default!(deserialize_string, String);
deserialize_default!(deserialize_bool, bool);
deserialize_default!(deserialize_i32, i32);
@ -2123,6 +2202,7 @@ pub mod keys {
pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture";
pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str =
"enable-android-software-encoding-half-scale";
pub const OPTION_ENABLE_TRUSTED_DEVICES: &str = "enable-trusted-devices";
// buildin options
pub const OPTION_DISPLAY_NAME: &str = "display-name";
@ -2264,6 +2344,7 @@ pub mod keys {
OPTION_PRESET_ADDRESS_BOOK_TAG,
OPTION_ENABLE_DIRECTX_CAPTURE,
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
OPTION_ENABLE_TRUSTED_DEVICES,
];
// BUILDIN_SETTINGS

View File

@ -4,7 +4,7 @@ use hbb_common::{
config::Config,
get_time,
password_security::{decrypt_vec_or_original, encrypt_vec_or_original},
tokio, ResultType,
ResultType,
};
use serde_derive::{Deserialize, Serialize};
use std::sync::Mutex;
@ -165,9 +165,7 @@ pub async fn send_2fa_code_to_telegram(text: &str, bot: TelegramBot) -> ResultTy
pub fn get_chatid_telegram(bot_token: &str) -> ResultType<Option<String>> {
let url = format!("https://api.telegram.org/bot{}/getUpdates", bot_token);
// because caller is in tokio runtime, so we must call post_request_sync in new thread.
let handle = std::thread::spawn(move || {
crate::post_request_sync(url, "".to_owned(), "")
});
let handle = std::thread::spawn(move || crate::post_request_sync(url, "".to_owned(), ""));
let resp = handle.join().map_err(|_| anyhow!("Thread panicked"))??;
let value = serde_json::from_str::<serde_json::Value>(&resp).map_err(|e| anyhow!(e))?;

View File

@ -1329,6 +1329,7 @@ pub struct LoginConfigHandler {
pub peer_info: Option<PeerInfo>,
password_source: PasswordSource, // where the sent password comes from
shared_password: Option<String>, // Store the shared password
pub enable_trusted_devices: bool,
}
impl Deref for LoginConfigHandler {
@ -2156,6 +2157,11 @@ impl LoginConfigHandler {
let my_platform = whoami::platform().to_string();
#[cfg(target_os = "android")]
let my_platform = "Android".into();
let hwid = if self.get_option("trust-this-device") == "Y" {
crate::get_hwid()
} else {
Bytes::new()
};
let mut lr = LoginRequest {
username: pure_id,
password: password.into(),
@ -2171,6 +2177,7 @@ impl LoginConfigHandler {
..Default::default()
})
.into(),
hwid,
..Default::default()
};
match self.conn_type {
@ -2827,6 +2834,12 @@ pub fn handle_login_error(
interface.msgbox("re-input-password", err, "Do you want to enter again?", "");
true
} else if err == LOGIN_MSG_2FA_WRONG || err == REQUIRE_2FA {
let enabled = lc.read().unwrap().get_option("trust-this-device") == "Y";
if enabled {
lc.write()
.unwrap()
.set_option("trust-this-device".to_string(), "".to_string());
}
interface.msgbox("input-2fa", err, "", "");
true
} else if LOGIN_ERROR_MAP.contains_key(err) {

View File

@ -1135,6 +1135,10 @@ impl<T: InvokeUiSession> Remote<T> {
}
Some(message::Union::LoginResponse(lr)) => match lr.union {
Some(login_response::Union::Error(err)) => {
if err == client::REQUIRE_2FA {
self.handler.lc.write().unwrap().enable_trusted_devices =
lr.enable_trusted_devices;
}
if !self.handler.handle_login_error(&err) {
return false;
}

View File

@ -1494,6 +1494,15 @@ pub fn is_empty_uni_link(arg: &str) -> bool {
arg[prefix.len()..].chars().all(|c| c == '/')
}
pub fn get_hwid() -> Bytes {
use sha2::{Digest, Sha256};
let uuid = hbb_common::get_uuid();
let mut hasher = Sha256::new();
hasher.update(&uuid);
Bytes::from(hasher.finalize().to_vec())
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -208,12 +208,21 @@ pub fn session_login(
}
}
pub fn session_send2fa(session_id: SessionID, code: String) {
pub fn session_send2fa(session_id: SessionID, code: String, trust_this_device: bool) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.send2fa(code);
session.send2fa(code, trust_this_device);
}
}
pub fn session_get_enable_trusted_devices(session_id: SessionID) -> SyncReturn<bool> {
let v = if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.get_enable_trusted_devices()
} else {
false
};
SyncReturn(v)
}
pub fn session_close(session_id: SessionID) {
if let Some(session) = sessions::remove_session_by_session_id(&session_id) {
session.close_event_stream(session_id);
@ -2240,6 +2249,18 @@ pub fn main_check_hwcodec() {
check_hwcodec()
}
pub fn main_get_trusted_devices() -> String {
get_trusted_devices()
}
pub fn main_remove_trusted_devices(json: String) {
remove_trusted_devices(&json)
}
pub fn main_clear_trusted_devices() {
clear_trusted_devices()
}
pub fn session_request_new_display_init_msgs(session_id: SessionID, display: usize) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.request_init_msgs(display);

View File

@ -25,7 +25,9 @@ use hbb_common::{
config::{self, Config, Config2},
futures::StreamExt as _,
futures_util::sink::SinkExt,
log, password_security as password, timeout,
log, password_security as password,
sodiumoxide::base64,
timeout,
tokio::{
self,
io::{AsyncRead, AsyncWrite},
@ -260,6 +262,8 @@ pub enum Data {
// Although the key is not neccessary, it is used to avoid hardcoding the key.
WaylandScreencastRestoreToken((String, String)),
HwCodecConfig(Option<String>),
RemoveTrustedDevices(Vec<Bytes>),
ClearTrustedDevices,
}
#[tokio::main(flavor = "current_thread")]
@ -486,6 +490,8 @@ async fn handle(data: Data, stream: &mut Connection) {
value = crate::audio_service::get_voice_call_input_device();
} else if name == "unlock-pin" {
value = Some(Config::get_unlock_pin());
} else if name == "trusted-devices" {
value = Some(Config::get_trusted_devices_json());
} else {
value = None;
}
@ -638,6 +644,12 @@ async fn handle(data: Data, stream: &mut Connection) {
);
}
}
Data::RemoveTrustedDevices(v) => {
Config::remove_trusted_devices(&v);
}
Data::ClearTrustedDevices => {
Config::clear_trusted_devices();
}
_ => {}
}
}
@ -866,6 +878,17 @@ pub async fn set_config_async(name: &str, value: String) -> ResultType<()> {
Ok(())
}
#[tokio::main(flavor = "current_thread")]
pub async fn set_data(data: &Data) -> ResultType<()> {
set_data_async(data).await
}
pub async fn set_data_async(data: &Data) -> ResultType<()> {
let mut c = connect(1000, "").await?;
c.send(data).await?;
Ok(())
}
#[tokio::main(flavor = "current_thread")]
pub async fn set_config(name: &str, value: String) -> ResultType<()> {
set_config_async(name, value).await
@ -926,6 +949,30 @@ pub fn get_unlock_pin() -> String {
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn get_trusted_devices() -> String {
if let Ok(Some(v)) = get_config("trusted-devices") {
v
} else {
Config::get_trusted_devices_json()
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn remove_trusted_devices(hwids: Vec<Bytes>) {
Config::remove_trusted_devices(&hwids);
allow_err!(set_data(&Data::RemoveTrustedDevices(hwids)));
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn clear_trusted_devices() {
Config::clear_trusted_devices();
allow_err!(set_data(&Data::ClearTrustedDevices));
}
pub fn get_id() -> String {
if let Ok(Some(v)) = get_config("id") {
// update salt also, so that next time reinstallation not causing first-time auto-login failure

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "الوثوق بهذا الجهاز"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Даверыць гэтую прыладу"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Доверете се на това устройство"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Confia en aquest dispositiu"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "不少于{}个字符"),
("Wrong PIN", "PIN 码错误"),
("Set PIN", "设置 PIN 码"),
("Enable trusted devices", "启用信任设备"),
("Manage trusted devices", "管理信任设备"),
("Trust this device", "信任此设备"),
("Platform", "平台"),
("Days remaining", "剩余天数"),
("enable-trusted-devices-tip", "允许受信任的设备跳过 2FA 验证"),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Důvěřovat tomuto zařízení"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Husk denne enhed"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "Erfordert mindestens {} Zeichen"),
("Wrong PIN", "Falsche PIN"),
("Set PIN", "PIN festlegen"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Diesem Gerät vertrauen"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Εμπιστεύομαι αυτή την συσκευή"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -232,6 +232,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("cancel-2fa-confirm-tip", "Are you sure you want to cancel 2FA?"),
("cancel-bot-confirm-tip", "Are you sure you want to cancel Telegram bot?"),
("About RustDesk", ""),
("network_error_tip", "Please check your network connection, then click retry.")
("network_error_tip", "Please check your network connection, then click retry."),
("enable-trusted-devices-tip", "Skip 2FA verification on trusted devices"),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Confiar en este dispositivo"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Gailu honetaz fidatu"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "حداقل به {} کاراکترها نیاز دارد"),
("Wrong PIN", "پین اشتباه است"),
("Set PIN", "پین را تنظیم کنید"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "به این دستگاه اعتماد کنید"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Faire confiance à cet appareil"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Vjeruj ovom uređaju"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Izinkan perangkat ini"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "Richiede almeno {} caratteri"),
("Wrong PIN", "PIN errato"),
("Set PIN", "Imposta PIN"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Registra questo dispositivo come attendibile"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "このデバイスを信頼する"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "이 장치 신뢰"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Pasitikėk šiuo įrenginiu"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Uzticēties šai ierīcei"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Husk denne enheten"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "Vereist minstens {} tekens"),
("Wrong PIN", "Verkeerde PIN-code"),
("Set PIN", "PIN-code instellen"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Vertrouw dit apparaat"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Dodaj to urządzenie do zaufanych"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", "PIN Errado"),
("Set PIN", "Definir PIN"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Confiar neste dispositivo"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Acest dispozitiv este de încredere"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "Требуется не менее {} символов"),
("Wrong PIN", "Неправильный PIN-код"),
("Set PIN", "Установить PIN-код"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Доверенное устройство"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Dôverovať tomuto zariadeniu"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Lita på denna enhet"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "เชื่อถืออุปกรณ์นี้"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", ""),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "信任此裝置"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", "Потрібно щонайменше {} символів"),
("Wrong PIN", "Неправильний PIN-код"),
("Set PIN", "Встановити PIN-код"),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Довірений пристрій"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Requires at least {} characters", ""),
("Wrong PIN", ""),
("Set PIN", ""),
("Enable trusted devices", ""),
("Manage trusted devices", ""),
("Trust this device", "Tin thiết bị này"),
("Platform", ""),
("Days remaining", ""),
("enable-trusted-devices-tip", ""),
].iter().cloned().collect();
}

View File

@ -27,7 +27,7 @@ use hbb_common::platform::linux::run_cmds;
#[cfg(target_os = "android")]
use hbb_common::protobuf::EnumOrUnknown;
use hbb_common::{
config::{self, Config},
config::{self, Config, TrustedDevice},
fs::{self, can_enable_overwrite_detection},
futures::{SinkExt, StreamExt},
get_time, get_version_number,
@ -1482,6 +1482,9 @@ impl Connection {
let mut msg_out = Message::new();
let mut res = LoginResponse::new();
res.set_error(err.to_string());
if err.to_string() == crate::client::REQUIRE_2FA {
res.enable_trusted_devices = Self::enable_trusted_devices();
}
msg_out.set_login_response(res);
self.send(msg_out).await;
}
@ -1623,11 +1626,32 @@ impl Connection {
}
}
#[inline]
fn enable_trusted_devices() -> bool {
config::option2bool(
config::keys::OPTION_ENABLE_TRUSTED_DEVICES,
&Config::get_option(config::keys::OPTION_ENABLE_TRUSTED_DEVICES),
)
}
async fn handle_login_request_without_validation(&mut self, lr: &LoginRequest) {
self.lr = lr.clone();
if let Some(o) = lr.option.as_ref() {
self.options_in_login = Some(o.clone());
}
if self.require_2fa.is_some() && !lr.hwid.is_empty() && Self::enable_trusted_devices() {
let devices = Config::get_trusted_devices();
if let Some(device) = devices.iter().find(|d| d.hwid == lr.hwid) {
if !device.outdate()
&& device.id == lr.my_id
&& device.name == lr.my_name
&& device.platform == lr.my_platform
{
log::info!("2FA bypassed by trusted devices");
self.require_2fa = None;
}
}
}
self.video_ack_required = lr.video_ack_required;
}
@ -1841,6 +1865,15 @@ impl Connection {
},
);
}
if !tfa.hwid.is_empty() && Self::enable_trusted_devices() {
Config::add_trusted_device(TrustedDevice {
hwid: tfa.hwid,
time: hbb_common::get_time(),
id: self.lr.my_id.clone(),
name: self.lr.my_name.clone(),
platform: self.lr.my_platform.clone(),
});
}
} else {
self.update_failure(failure, false, 1);
self.send_login_error(crate::client::LOGIN_MSG_2FA_WRONG)

View File

@ -268,7 +268,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
view.close();
return;
}
handler.send2fa(res.code);
handler.send2fa(res.code, res.trust_this_device || false);
msgbox("connecting", "Connecting...", "Logging in...");
};
} else if (type == "session-login" || type == "session-re-login") {

View File

@ -66,9 +66,11 @@ class MsgboxComponent: Reactor.Component {
}
function get2faContent() {
var enable_trusted_devices = handler.get_enable_trusted_devices();
return <div .form>
<div>{translate('enter-2fa-title')}</div>
<div .code><input name='code' type='text' .outline-focus /></div>
{enable_trusted_devices ? <div><button|checkbox(trust_this_device) {ts}>{translate('Trust this device')}</button></div> : ""}
</div>;
}

View File

@ -433,7 +433,8 @@ impl sciter::EventHandler for SciterSession {
fn is_port_forward();
fn is_rdp();
fn login(String, String, String, bool);
fn send2fa(String);
fn send2fa(String, bool);
fn get_enable_trusted_devices();
fn new_rdp();
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
fn enter(String);

View File

@ -1471,3 +1471,28 @@ pub fn set_unlock_pin(pin: String) -> String {
Err(err) => err.to_string(),
}
}
#[cfg(feature = "flutter")]
pub fn get_trusted_devices() -> String {
#[cfg(any(target_os = "android", target_os = "ios"))]
return Config::get_trusted_devices_json();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
return ipc::get_trusted_devices();
}
#[cfg(feature = "flutter")]
pub fn remove_trusted_devices(json: &str) {
let hwids = serde_json::from_str::<Vec<Bytes>>(json).unwrap_or_default();
#[cfg(any(target_os = "android", target_os = "ios"))]
Config::remove_trusted_devices(&hwids);
#[cfg(not(any(target_os = "android", target_os = "ios")))]
ipc::remove_trusted_devices(hwids);
}
#[cfg(feature = "flutter")]
pub fn clear_trusted_devices() {
#[cfg(any(target_os = "android", target_os = "ios"))]
Config::clear_trusted_devices();
#[cfg(not(any(target_os = "android", target_os = "ios")))]
ipc::clear_trusted_devices();
}

View File

@ -1156,15 +1156,29 @@ impl<T: InvokeUiSession> Session<T> {
self.send(Data::Login((os_username, os_password, password, remember)));
}
pub fn send2fa(&self, code: String) {
pub fn send2fa(&self, code: String, trust_this_device: bool) {
let mut msg_out = Message::new();
let hwid = if trust_this_device {
crate::get_hwid()
} else {
Bytes::new()
};
self.lc.write().unwrap().set_option(
"trust-this-device".to_string(),
if trust_this_device { "Y" } else { "" }.to_string(),
);
msg_out.set_auth_2fa(Auth2FA {
code,
hwid,
..Default::default()
});
self.send(Data::Message(msg_out));
}
pub fn get_enable_trusted_devices(&self) -> bool {
self.lc.read().unwrap().enable_trusted_devices
}
pub fn new_rdp(&self) {
self.send(Data::NewRDP);
}