mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 14:59:02 +08:00
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:
parent
57834840b8
commit
1729ee337f
@ -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(),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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() {}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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))?;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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::*;
|
||||
|
@ -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);
|
||||
|
49
src/ipc.rs
49
src/ipc.rs
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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") {
|
||||
|
@ -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>;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user