diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 2e7026071..318843699 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -260,6 +260,7 @@ class _ConnectionPageState extends State bool checked = await bind.mainCheckSuperUserPermission(); if (checked) { bind.mainSetOption(key: "stop-service", value: ""); + bind.mainSetOption(key: "access-mode", value: ""); } }, child: Text(translate("Start Service"), style: textStyle)) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 0c4cc0aec..9787048fa 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -413,6 +413,13 @@ class _GeneralState extends State<_General> { } } +enum _AccessMode { + custom, + full, + view, + deny, +} + class _Safety extends StatefulWidget { const _Safety({Key? key}) : super(key: key); @@ -459,26 +466,113 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { Widget permissions(context) { bool enabled = !locked; - return _Card(title: 'Permissions', children: [ - _OptionCheckBox(context, 'Enable Keyboard/Mouse', 'enable-keyboard', - enabled: enabled), - _OptionCheckBox(context, 'Enable Clipboard', 'enable-clipboard', - enabled: enabled), - _OptionCheckBox(context, 'Enable File Transfer', 'enable-file-transfer', - enabled: enabled), - _OptionCheckBox(context, 'Enable Audio', 'enable-audio', - enabled: enabled), - _OptionCheckBox(context, 'Enable TCP Tunneling', 'enable-tunnel', - enabled: enabled), - _OptionCheckBox(context, 'Enable Remote Restart', 'enable-remote-restart', - enabled: enabled), - _OptionCheckBox( - context, 'Enable Recording Session', 'enable-record-session', - enabled: enabled), - _OptionCheckBox(context, 'Enable remote configuration modification', - 'allow-remote-config-modification', - enabled: enabled), - ]); + + return _futureBuilder(future: () async { + bool stopService = option2bool( + 'stop-service', await bind.mainGetOption(key: 'stop-service')); + final accessMode = await bind.mainGetOption(key: 'access-mode'); + return {'stopService': stopService, 'accessMode': accessMode}; + }(), hasData: (data) { + var map = data! as Map; + bool stopService = map['stopService'] as bool; + String accessMode = map['accessMode'] as String; + _AccessMode mode; + if (stopService) { + mode = _AccessMode.deny; + } else { + if (accessMode == 'full') { + mode = _AccessMode.full; + } else if (accessMode == 'view') { + mode = _AccessMode.view; + } else { + mode = _AccessMode.custom; + } + } + String initialKey; + bool? fakeValue; + switch (mode) { + case _AccessMode.custom: + initialKey = ''; + fakeValue = null; + break; + case _AccessMode.full: + initialKey = 'full'; + fakeValue = true; + break; + case _AccessMode.view: + initialKey = 'view'; + fakeValue = false; + break; + case _AccessMode.deny: + initialKey = 'deny'; + fakeValue = false; + break; + } + + return _Card(title: 'Permissions', children: [ + _ComboBox( + keys: [ + '', + 'full', + 'view', + 'deny' + ], + values: [ + translate('Custom'), + translate('Full Access'), + translate('Screen Share'), + translate('Deny remote access'), + ], + initialKey: initialKey, + onChanged: (mode) async { + String modeValue; + bool stopService; + if (mode == 'deny') { + modeValue = ''; + stopService = true; + } else { + modeValue = mode; + stopService = false; + } + await bind.mainSetOption(key: 'access-mode', value: modeValue); + await bind.mainSetOption( + key: 'stop-service', + value: bool2option('stop-service', stopService)); + setState(() {}); + }).marginOnly(left: _kContentHMargin), + Offstage( + offstage: mode == _AccessMode.deny, + child: Column( + children: [ + _OptionCheckBox( + context, 'Enable Keyboard/Mouse', 'enable-keyboard', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox(context, 'Enable Clipboard', 'enable-clipboard', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox( + context, 'Enable File Transfer', 'enable-file-transfer', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox(context, 'Enable Audio', 'enable-audio', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox(context, 'Enable TCP Tunneling', 'enable-tunnel', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox( + context, 'Enable Remote Restart', 'enable-remote-restart', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox( + context, 'Enable Recording Session', 'enable-record-session', + enabled: enabled, fakeValue: fakeValue), + _OptionCheckBox( + context, + 'Enable remote configuration modification', + 'allow-remote-config-modification', + enabled: enabled, + fakeValue: fakeValue), + ], + ), + ) + ]); + }); } Widget password(BuildContext context) { @@ -566,12 +660,6 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { Widget more(BuildContext context) { bool enabled = !locked; return _Card(title: 'Security', children: [ - _OptionCheckBox(context, 'Deny remote access', 'stop-service', - checkedIcon: const Icon( - Icons.warning_amber_rounded, - color: kColorWarn, - ), - enabled: enabled), Offstage( offstage: !Platform.isWindows, child: _OptionCheckBox(context, 'Enable RDP', 'enable-rdp', @@ -941,7 +1029,8 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, {Function()? update, bool reverse = false, bool enabled = true, - Icon? checkedIcon}) { + Icon? checkedIcon, + bool? fakeValue}) { return _futureBuilder( future: bind.mainGetOption(key: key), hasData: (data) { @@ -958,6 +1047,11 @@ Widget _OptionCheckBox(BuildContext context, String label, String key, } } + if (fakeValue != null) { + ref.value = fakeValue; + enabled = false; + } + return GestureDetector( child: Obx( () => Row( diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 3eadf75fd..e146a4bee 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -249,10 +249,12 @@ class DesktopTab extends StatelessWidget { var block = false.obs; return Obx(() => MouseRegion( onEnter: (_) async { - if (!option2bool( + var access_mode = await bind.mainGetOption(key: 'access-mode'); + var option = option2bool( 'allow-remote-config-modification', await bind.mainGetOption( - key: 'allow-remote-config-modification'))) { + key: 'allow-remote-config-modification')); + if (access_mode == 'view' || (access_mode.isEmpty && !option)) { var time0 = DateTime.now().millisecondsSinceEpoch; await bind.mainCheckMouseTime(); Timer(const Duration(milliseconds: 120), () async { diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 47b5f0bcb..c88b0182c 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", "其他"), ("Confirm before closing multiple tabs", "关闭多个标签页时向您确认"), ("Keyboard Settings", "键盘设置"), + ("Custom", "自定义"), + ("Full Access", "完全访问"), + ("Screen Share", "仅共享屏幕"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 2515f2231..f616d518b 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index df1a06f6d..3fe49d051 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 1b9973957..4942b3171 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 3c6b3b771..9bfea5440 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 3826650af..3b6419572 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 4b76b8941..b7dbd5636 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 98e15397c..aa5e34d09 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 437ec52cd..1ab7fd134 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 6856eca60..65c16567c 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 77f47964a..75a9c0c9b 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", "他の"), ("Confirm before closing multiple tabs", "同時に複数のタブを閉じる前に確認する"), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 4d0ff5f3e..eccb4d836 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 602413ba9..9a1e23c23 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index dc6cdb750..e96061729 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index ff3d31b83..e17967998 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 0c0150e88..c2965405d 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 4192f8490..95c59dede 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 819eee109..5064e1069 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index c1d93b224..e08a2ba76 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 7761c1ff3..117a5683b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index ab175cf11..d0bce5d77 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", "其他"), ("Confirm before closing multiple tabs", "關閉多個分頁前跟我確認"), ("Keyboard Settings", "鍵盤設置"), + ("Custom", "自定義"), + ("Full Access", "完全訪問"), + ("Screen Share", "僅共享屏幕"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 0399705b9..f17e17a93 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index db09d03a3..30e32a82c 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -374,5 +374,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", ""), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", ""), + ("Custom", ""), + ("Full Access", ""), + ("Screen Share", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 14c906bcf..d111757bf 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -167,12 +167,12 @@ impl Connection { port_forward_address: "".to_owned(), tx_to_cm, authorized: false, - keyboard: Config::get_option("enable-keyboard").is_empty(), - clipboard: Config::get_option("enable-clipboard").is_empty(), - audio: Config::get_option("enable-audio").is_empty(), - file: Config::get_option("enable-file-transfer").is_empty(), - restart: Config::get_option("enable-remote-restart").is_empty(), - recording: Config::get_option("enable-record-session").is_empty(), + keyboard: Connection::permission("enable-keyboard"), + clipboard: Connection::permission("enable-clipboard"), + audio: Connection::permission("enable-audio"), + file: Connection::permission("enable-file-transfer"), + restart: Connection::permission("enable-remote-restart"), + recording: Connection::permission("enable-record-session"), last_test_delay: 0, lock_after_session_end: false, show_remote_cursor: false, @@ -922,6 +922,20 @@ impl Connection { false } + pub fn permission(enable_prefix_option: &str) -> bool { + #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + let access_mode = Config::get_option("access-mode"); + if access_mode == "full" { + return true; + } else if access_mode == "view" { + return false; + } + } + return Config::get_option(enable_prefix_option).is_empty(); + } + async fn on_message(&mut self, msg: Message) -> bool { if let Some(message::Union::LoginRequest(lr)) = msg.union { self.lr = lr.clone(); @@ -950,7 +964,7 @@ impl Connection { } match lr.union { Some(login_request::Union::FileTransfer(ft)) => { - if !Config::get_option("enable-file-transfer").is_empty() { + if !Connection::permission("enable-file-transfer") { self.send_login_error("No permission of file transfer") .await; sleep(1.).await; @@ -965,8 +979,8 @@ impl Connection { pf.port = 3389; is_rdp = true; } - if is_rdp && !Config::get_option("enable-rdp").is_empty() - || !is_rdp && !Config::get_option("enable-tunnel").is_empty() + if is_rdp && !Connection::permission("enable-rdp") + || !is_rdp && !Connection::permission("enable-tunnel") { if is_rdp { self.send_login_error("No permission of RDP").await;