mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00: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 {
|
void change2fa({Function()? callback}) async {
|
||||||
if (bind.mainHasValid2FaSync()) {
|
if (bind.mainHasValid2FaSync()) {
|
||||||
await bind.mainSetOption(key: "2fa", value: "");
|
await bind.mainSetOption(key: "2fa", value: "");
|
||||||
|
await bind.mainClearTrustedDevices();
|
||||||
callback?.call();
|
callback?.call();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1898,6 +1899,7 @@ void enter2FaDialog(
|
|||||||
SessionID sessionId, OverlayDialogManager dialogManager) async {
|
SessionID sessionId, OverlayDialogManager dialogManager) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final RxBool submitReady = false.obs;
|
final RxBool submitReady = false.obs;
|
||||||
|
final RxBool trustThisDevice = false.obs;
|
||||||
|
|
||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
dialogManager.show((setState, close, context) {
|
dialogManager.show((setState, close, context) {
|
||||||
@ -1907,7 +1909,7 @@ void enter2FaDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
gFFI.send2FA(sessionId, controller.text.trim());
|
gFFI.send2FA(sessionId, controller.text.trim(), trustThisDevice.value);
|
||||||
close();
|
close();
|
||||||
dialogManager.showLoading(translate('Logging in...'),
|
dialogManager.showLoading(translate('Logging in...'),
|
||||||
onCancel: closeConnection);
|
onCancel: closeConnection);
|
||||||
@ -1921,9 +1923,27 @@ void enter2FaDialog(
|
|||||||
onChanged: () => submitReady.value = codeField.isReady,
|
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(
|
return CustomAlertDialog(
|
||||||
title: Text(translate('enter-2fa-title')),
|
title: Text(translate('enter-2fa-title')),
|
||||||
content: codeField,
|
content: Column(
|
||||||
|
children: [
|
||||||
|
codeField,
|
||||||
|
if (bind.sessionGetEnableTrustedDevices(sessionId: sessionId))
|
||||||
|
trustField,
|
||||||
|
],
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
dialogButton('Cancel',
|
dialogButton('Cancel',
|
||||||
onPressed: 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 kOptionStopService = "stop-service";
|
||||||
const String kOptionDirectxCapture = "enable-directx-capture";
|
const String kOptionDirectxCapture = "enable-directx-capture";
|
||||||
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
|
const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification";
|
||||||
|
const String kOptionEnableTrustedDevices = "enable-trusted-devices";
|
||||||
|
|
||||||
// buildin opitons
|
// buildin opitons
|
||||||
const String kOptionHideServerSetting = "hide-server-settings";
|
const String kOptionHideServerSetting = "hide-server-settings";
|
||||||
|
|||||||
@ -783,8 +783,33 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
onChangedBot(!hasBot.value);
|
onChangedBot(!hasBot.value);
|
||||||
},
|
},
|
||||||
).marginOnly(left: _kCheckBoxLeftMargin + 30);
|
).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(
|
return Column(
|
||||||
children: [tfa, bot],
|
children: [tfa, bot, trust],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common/widgets/setting_widgets.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 _hideServer = false;
|
||||||
var _hideProxy = false;
|
var _hideProxy = false;
|
||||||
var _hideNetwork = false;
|
var _hideNetwork = false;
|
||||||
|
var _enableTrustedDevices = false;
|
||||||
|
|
||||||
_SettingsState() {
|
_SettingsState() {
|
||||||
_enableAbr = option2bool(
|
_enableAbr = option2bool(
|
||||||
@ -113,6 +115,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
|
_hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
|
||||||
_hideNetwork =
|
_hideNetwork =
|
||||||
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
|
bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y';
|
||||||
|
_enableTrustedDevices = mainGetBoolOptionSync(kOptionEnableTrustedDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -243,18 +246,57 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
],
|
],
|
||||||
));
|
));
|
||||||
final List<AbstractSettingsTile> enhancementsTiles = [];
|
final List<AbstractSettingsTile> enhancementsTiles = [];
|
||||||
final List<AbstractSettingsTile> shareScreenTiles = [
|
final enable2fa = bind.mainHasValid2FaSync();
|
||||||
|
final List<AbstractSettingsTile> tfaTiles = [
|
||||||
SettingsTile.switchTile(
|
SettingsTile.switchTile(
|
||||||
title: Text(translate('enable-2fa-title')),
|
title: Text(translate('enable-2fa-title')),
|
||||||
initialValue: bind.mainHasValid2FaSync(),
|
initialValue: enable2fa,
|
||||||
onToggle: (_) async {
|
onToggle: (v) async {
|
||||||
update() async {
|
update() async {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (v == false) {
|
||||||
|
CommonConfirmDialog(
|
||||||
|
gFFI.dialogManager, translate('cancel-2fa-confirm-tip'), () {
|
||||||
change2fa(callback: update);
|
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(
|
SettingsTile.switchTile(
|
||||||
title: Text(translate('Deny LAN discovery')),
|
title: Text(translate('Deny LAN discovery')),
|
||||||
initialValue: _denyLANDiscovery,
|
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 &&
|
if (isAndroid &&
|
||||||
!disabledSettings &&
|
!disabledSettings &&
|
||||||
!outgoingOnly &&
|
!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 {
|
class _RadioEntry {
|
||||||
final String label;
|
final String label;
|
||||||
final String value;
|
final String value;
|
||||||
|
|||||||
@ -2611,8 +2611,9 @@ class FFI {
|
|||||||
remember: remember);
|
remember: remember);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send2FA(SessionID sessionId, String code) {
|
void send2FA(SessionID sessionId, String code, bool trustThisDevice) {
|
||||||
bind.sessionSend2Fa(sessionId: sessionId, code: code);
|
bind.sessionSend2Fa(
|
||||||
|
sessionId: sessionId, code: code, trustThisDevice: trustThisDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close the remote session.
|
/// Close the remote session.
|
||||||
|
|||||||
@ -142,7 +142,10 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sessionSend2Fa(
|
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]));
|
return Future(() => js.context.callMethod('setByName', ['send_2fa', code]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1630,5 +1633,22 @@ class RustdeskImpl {
|
|||||||
throw UnimplementedError();
|
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() {}
|
void dispose() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -82,10 +82,12 @@ message LoginRequest {
|
|||||||
string version = 11;
|
string version = 11;
|
||||||
OSLogin os_login = 12;
|
OSLogin os_login = 12;
|
||||||
string my_platform = 13;
|
string my_platform = 13;
|
||||||
|
bytes hwid = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Auth2FA {
|
message Auth2FA {
|
||||||
string code = 1;
|
string code = 1;
|
||||||
|
bytes hwid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ChatMessage { string text = 1; }
|
message ChatMessage { string text = 1; }
|
||||||
@ -137,6 +139,7 @@ message LoginResponse {
|
|||||||
string error = 1;
|
string error = 1;
|
||||||
PeerInfo peer_info = 2;
|
PeerInfo peer_info = 2;
|
||||||
}
|
}
|
||||||
|
bool enable_trusted_devices = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TouchScaleUpdate {
|
message TouchScaleUpdate {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use bytes::Bytes;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde as de;
|
use serde as de;
|
||||||
@ -52,6 +53,7 @@ lazy_static::lazy_static! {
|
|||||||
static ref CONFIG: RwLock<Config> = RwLock::new(Config::load());
|
static ref CONFIG: RwLock<Config> = RwLock::new(Config::load());
|
||||||
static ref CONFIG2: RwLock<Config2> = RwLock::new(Config2::load());
|
static ref CONFIG2: RwLock<Config2> = RwLock::new(Config2::load());
|
||||||
static ref LOCAL_CONFIG: RwLock<LocalConfig> = RwLock::new(LocalConfig::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();
|
static ref ONLINE: Mutex<HashMap<String, i64>> = Default::default();
|
||||||
pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
|
pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new(match option_env!("RENDEZVOUS_SERVER") {
|
||||||
Some(key) if !key.is_empty() => key,
|
Some(key) if !key.is_empty() => key,
|
||||||
@ -210,6 +212,8 @@ pub struct Config2 {
|
|||||||
serial: i32,
|
serial: i32,
|
||||||
#[serde(default, deserialize_with = "deserialize_string")]
|
#[serde(default, deserialize_with = "deserialize_string")]
|
||||||
unlock_pin: String,
|
unlock_pin: String,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_string")]
|
||||||
|
trusted_devices: String,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
socks: Option<Socks5Server>,
|
socks: Option<Socks5Server>,
|
||||||
@ -998,6 +1002,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
config.password = password.into();
|
config.password = password.into();
|
||||||
config.store();
|
config.store();
|
||||||
|
Self::clear_trusted_devices();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_permanent_password() -> String {
|
pub fn get_permanent_password() -> String {
|
||||||
@ -1104,6 +1109,64 @@ impl Config {
|
|||||||
config.store();
|
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 {
|
pub fn get() -> Config {
|
||||||
return CONFIG.read().unwrap().clone();
|
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_string, String);
|
||||||
deserialize_default!(deserialize_bool, bool);
|
deserialize_default!(deserialize_bool, bool);
|
||||||
deserialize_default!(deserialize_i32, i32);
|
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_DIRECTX_CAPTURE: &str = "enable-directx-capture";
|
||||||
pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str =
|
pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str =
|
||||||
"enable-android-software-encoding-half-scale";
|
"enable-android-software-encoding-half-scale";
|
||||||
|
pub const OPTION_ENABLE_TRUSTED_DEVICES: &str = "enable-trusted-devices";
|
||||||
|
|
||||||
// buildin options
|
// buildin options
|
||||||
pub const OPTION_DISPLAY_NAME: &str = "display-name";
|
pub const OPTION_DISPLAY_NAME: &str = "display-name";
|
||||||
@ -2264,6 +2344,7 @@ pub mod keys {
|
|||||||
OPTION_PRESET_ADDRESS_BOOK_TAG,
|
OPTION_PRESET_ADDRESS_BOOK_TAG,
|
||||||
OPTION_ENABLE_DIRECTX_CAPTURE,
|
OPTION_ENABLE_DIRECTX_CAPTURE,
|
||||||
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
|
OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE,
|
||||||
|
OPTION_ENABLE_TRUSTED_DEVICES,
|
||||||
];
|
];
|
||||||
|
|
||||||
// BUILDIN_SETTINGS
|
// BUILDIN_SETTINGS
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use hbb_common::{
|
|||||||
config::Config,
|
config::Config,
|
||||||
get_time,
|
get_time,
|
||||||
password_security::{decrypt_vec_or_original, encrypt_vec_or_original},
|
password_security::{decrypt_vec_or_original, encrypt_vec_or_original},
|
||||||
tokio, ResultType,
|
ResultType,
|
||||||
};
|
};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::sync::Mutex;
|
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>> {
|
pub fn get_chatid_telegram(bot_token: &str) -> ResultType<Option<String>> {
|
||||||
let url = format!("https://api.telegram.org/bot{}/getUpdates", bot_token);
|
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.
|
// because caller is in tokio runtime, so we must call post_request_sync in new thread.
|
||||||
let handle = std::thread::spawn(move || {
|
let handle = std::thread::spawn(move || crate::post_request_sync(url, "".to_owned(), ""));
|
||||||
crate::post_request_sync(url, "".to_owned(), "")
|
|
||||||
});
|
|
||||||
let resp = handle.join().map_err(|_| anyhow!("Thread panicked"))??;
|
let resp = handle.join().map_err(|_| anyhow!("Thread panicked"))??;
|
||||||
let value = serde_json::from_str::<serde_json::Value>(&resp).map_err(|e| anyhow!(e))?;
|
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>,
|
pub peer_info: Option<PeerInfo>,
|
||||||
password_source: PasswordSource, // where the sent password comes from
|
password_source: PasswordSource, // where the sent password comes from
|
||||||
shared_password: Option<String>, // Store the shared password
|
shared_password: Option<String>, // Store the shared password
|
||||||
|
pub enable_trusted_devices: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for LoginConfigHandler {
|
impl Deref for LoginConfigHandler {
|
||||||
@ -2156,6 +2157,11 @@ impl LoginConfigHandler {
|
|||||||
let my_platform = whoami::platform().to_string();
|
let my_platform = whoami::platform().to_string();
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
let my_platform = "Android".into();
|
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 {
|
let mut lr = LoginRequest {
|
||||||
username: pure_id,
|
username: pure_id,
|
||||||
password: password.into(),
|
password: password.into(),
|
||||||
@ -2171,6 +2177,7 @@ impl LoginConfigHandler {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
|
hwid,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
match self.conn_type {
|
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?", "");
|
interface.msgbox("re-input-password", err, "Do you want to enter again?", "");
|
||||||
true
|
true
|
||||||
} else if err == LOGIN_MSG_2FA_WRONG || err == REQUIRE_2FA {
|
} 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, "", "");
|
interface.msgbox("input-2fa", err, "", "");
|
||||||
true
|
true
|
||||||
} else if LOGIN_ERROR_MAP.contains_key(err) {
|
} 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(message::Union::LoginResponse(lr)) => match lr.union {
|
||||||
Some(login_response::Union::Error(err)) => {
|
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) {
|
if !self.handler.handle_login_error(&err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1494,6 +1494,15 @@ pub fn is_empty_uni_link(arg: &str) -> bool {
|
|||||||
arg[prefix.len()..].chars().all(|c| c == '/')
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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) {
|
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) {
|
pub fn session_close(session_id: SessionID) {
|
||||||
if let Some(session) = sessions::remove_session_by_session_id(&session_id) {
|
if let Some(session) = sessions::remove_session_by_session_id(&session_id) {
|
||||||
session.close_event_stream(session_id);
|
session.close_event_stream(session_id);
|
||||||
@ -2240,6 +2249,18 @@ pub fn main_check_hwcodec() {
|
|||||||
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) {
|
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) {
|
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||||
session.request_init_msgs(display);
|
session.request_init_msgs(display);
|
||||||
|
|||||||
49
src/ipc.rs
49
src/ipc.rs
@ -25,7 +25,9 @@ use hbb_common::{
|
|||||||
config::{self, Config, Config2},
|
config::{self, Config, Config2},
|
||||||
futures::StreamExt as _,
|
futures::StreamExt as _,
|
||||||
futures_util::sink::SinkExt,
|
futures_util::sink::SinkExt,
|
||||||
log, password_security as password, timeout,
|
log, password_security as password,
|
||||||
|
sodiumoxide::base64,
|
||||||
|
timeout,
|
||||||
tokio::{
|
tokio::{
|
||||||
self,
|
self,
|
||||||
io::{AsyncRead, AsyncWrite},
|
io::{AsyncRead, AsyncWrite},
|
||||||
@ -260,6 +262,8 @@ pub enum Data {
|
|||||||
// Although the key is not neccessary, it is used to avoid hardcoding the key.
|
// Although the key is not neccessary, it is used to avoid hardcoding the key.
|
||||||
WaylandScreencastRestoreToken((String, String)),
|
WaylandScreencastRestoreToken((String, String)),
|
||||||
HwCodecConfig(Option<String>),
|
HwCodecConfig(Option<String>),
|
||||||
|
RemoveTrustedDevices(Vec<Bytes>),
|
||||||
|
ClearTrustedDevices,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[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();
|
value = crate::audio_service::get_voice_call_input_device();
|
||||||
} else if name == "unlock-pin" {
|
} else if name == "unlock-pin" {
|
||||||
value = Some(Config::get_unlock_pin());
|
value = Some(Config::get_unlock_pin());
|
||||||
|
} else if name == "trusted-devices" {
|
||||||
|
value = Some(Config::get_trusted_devices_json());
|
||||||
} else {
|
} else {
|
||||||
value = None;
|
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(())
|
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")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
pub async fn set_config(name: &str, value: String) -> ResultType<()> {
|
pub async fn set_config(name: &str, value: String) -> ResultType<()> {
|
||||||
set_config_async(name, value).await
|
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 {
|
pub fn get_id() -> String {
|
||||||
if let Ok(Some(v)) = get_config("id") {
|
if let Ok(Some(v)) = get_config("id") {
|
||||||
// update salt also, so that next time reinstallation not causing first-time auto-login failure
|
// 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", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "الوثوق بهذا الجهاز"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "Даверыць гэтую прыладу"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "Доверете се на това устройство"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", "不少于{}个字符"),
|
("Requires at least {} characters", "不少于{}个字符"),
|
||||||
("Wrong PIN", "PIN 码错误"),
|
("Wrong PIN", "PIN 码错误"),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].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"),
|
("Requires at least {} characters", "Erfordert mindestens {} Zeichen"),
|
||||||
("Wrong PIN", "Falsche PIN"),
|
("Wrong PIN", "Falsche PIN"),
|
||||||
("Set PIN", "PIN festlegen"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "Εμπιστεύομαι αυτή την συσκευή"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].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-2fa-confirm-tip", "Are you sure you want to cancel 2FA?"),
|
||||||
("cancel-bot-confirm-tip", "Are you sure you want to cancel Telegram bot?"),
|
("cancel-bot-confirm-tip", "Are you sure you want to cancel Telegram bot?"),
|
||||||
("About RustDesk", ""),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", "حداقل به {} کاراکترها نیاز دارد"),
|
("Requires at least {} characters", "حداقل به {} کاراکترها نیاز دارد"),
|
||||||
("Wrong PIN", "پین اشتباه است"),
|
("Wrong PIN", "پین اشتباه است"),
|
||||||
("Set PIN", "پین را تنظیم کنید"),
|
("Set PIN", "پین را تنظیم کنید"),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "به این دستگاه اعتماد کنید"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].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"),
|
("Requires at least {} characters", "Richiede almeno {} caratteri"),
|
||||||
("Wrong PIN", "PIN errato"),
|
("Wrong PIN", "PIN errato"),
|
||||||
("Set PIN", "Imposta PIN"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "このデバイスを信頼する"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "이 장치 신뢰"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].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"),
|
("Requires at least {} characters", "Vereist minstens {} tekens"),
|
||||||
("Wrong PIN", "Verkeerde PIN-code"),
|
("Wrong PIN", "Verkeerde PIN-code"),
|
||||||
("Set PIN", "PIN-code instellen"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", "PIN Errado"),
|
("Wrong PIN", "PIN Errado"),
|
||||||
("Set PIN", "Definir PIN"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", "Требуется не менее {} символов"),
|
("Requires at least {} characters", "Требуется не менее {} символов"),
|
||||||
("Wrong PIN", "Неправильный PIN-код"),
|
("Wrong PIN", "Неправильный PIN-код"),
|
||||||
("Set PIN", "Установить PIN-код"),
|
("Set PIN", "Установить PIN-код"),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "Доверенное устройство"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "เชื่อถืออุปกรณ์นี้"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", ""),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set PIN", ""),
|
("Set PIN", ""),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "信任此裝置"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", "Потрібно щонайменше {} символів"),
|
("Requires at least {} characters", "Потрібно щонайменше {} символів"),
|
||||||
("Wrong PIN", "Неправильний PIN-код"),
|
("Wrong PIN", "Неправильний PIN-код"),
|
||||||
("Set PIN", "Встановити PIN-код"),
|
("Set PIN", "Встановити PIN-код"),
|
||||||
|
("Enable trusted devices", ""),
|
||||||
|
("Manage trusted devices", ""),
|
||||||
|
("Trust this device", "Довірений пристрій"),
|
||||||
|
("Platform", ""),
|
||||||
|
("Days remaining", ""),
|
||||||
|
("enable-trusted-devices-tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -636,5 +636,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Requires at least {} characters", ""),
|
("Requires at least {} characters", ""),
|
||||||
("Wrong PIN", ""),
|
("Wrong PIN", ""),
|
||||||
("Set 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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ use hbb_common::platform::linux::run_cmds;
|
|||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use hbb_common::protobuf::EnumOrUnknown;
|
use hbb_common::protobuf::EnumOrUnknown;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::{self, Config},
|
config::{self, Config, TrustedDevice},
|
||||||
fs::{self, can_enable_overwrite_detection},
|
fs::{self, can_enable_overwrite_detection},
|
||||||
futures::{SinkExt, StreamExt},
|
futures::{SinkExt, StreamExt},
|
||||||
get_time, get_version_number,
|
get_time, get_version_number,
|
||||||
@ -1482,6 +1482,9 @@ impl Connection {
|
|||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
let mut res = LoginResponse::new();
|
let mut res = LoginResponse::new();
|
||||||
res.set_error(err.to_string());
|
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);
|
msg_out.set_login_response(res);
|
||||||
self.send(msg_out).await;
|
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) {
|
async fn handle_login_request_without_validation(&mut self, lr: &LoginRequest) {
|
||||||
self.lr = lr.clone();
|
self.lr = lr.clone();
|
||||||
if let Some(o) = lr.option.as_ref() {
|
if let Some(o) = lr.option.as_ref() {
|
||||||
self.options_in_login = Some(o.clone());
|
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;
|
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 {
|
} else {
|
||||||
self.update_failure(failure, false, 1);
|
self.update_failure(failure, false, 1);
|
||||||
self.send_login_error(crate::client::LOGIN_MSG_2FA_WRONG)
|
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();
|
view.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handler.send2fa(res.code);
|
handler.send2fa(res.code, res.trust_this_device || false);
|
||||||
msgbox("connecting", "Connecting...", "Logging in...");
|
msgbox("connecting", "Connecting...", "Logging in...");
|
||||||
};
|
};
|
||||||
} else if (type == "session-login" || type == "session-re-login") {
|
} else if (type == "session-login" || type == "session-re-login") {
|
||||||
|
|||||||
@ -66,9 +66,11 @@ class MsgboxComponent: Reactor.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function get2faContent() {
|
function get2faContent() {
|
||||||
|
var enable_trusted_devices = handler.get_enable_trusted_devices();
|
||||||
return <div .form>
|
return <div .form>
|
||||||
<div>{translate('enter-2fa-title')}</div>
|
<div>{translate('enter-2fa-title')}</div>
|
||||||
<div .code><input name='code' type='text' .outline-focus /></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>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -433,7 +433,8 @@ impl sciter::EventHandler for SciterSession {
|
|||||||
fn is_port_forward();
|
fn is_port_forward();
|
||||||
fn is_rdp();
|
fn is_rdp();
|
||||||
fn login(String, String, String, bool);
|
fn login(String, String, String, bool);
|
||||||
fn send2fa(String);
|
fn send2fa(String, bool);
|
||||||
|
fn get_enable_trusted_devices();
|
||||||
fn new_rdp();
|
fn new_rdp();
|
||||||
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
|
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
|
||||||
fn enter(String);
|
fn enter(String);
|
||||||
|
|||||||
@ -1471,3 +1471,28 @@ pub fn set_unlock_pin(pin: String) -> String {
|
|||||||
Err(err) => err.to_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)));
|
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 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 {
|
msg_out.set_auth_2fa(Auth2FA {
|
||||||
code,
|
code,
|
||||||
|
hwid,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.send(Data::Message(msg_out));
|
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) {
|
pub fn new_rdp(&self) {
|
||||||
self.send(Data::NewRDP);
|
self.send(Data::NewRDP);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user