From 2d31e44b546430ad850fc8190a38bf3079c7d08d Mon Sep 17 00:00:00 2001 From: csf Date: Fri, 29 Jul 2022 18:34:25 +0800 Subject: [PATCH] android refactor password --- flutter/lib/common.dart | 12 ++++ flutter/lib/models/server_model.dart | 70 +++++++++++---------- flutter/lib/pages/remote_page.dart | 12 ---- flutter/lib/pages/server_page.dart | 91 +++++++++++++++++----------- flutter/lib/widgets/dialog.dart | 41 +++++++++++-- src/mobile_ffi.rs | 43 +++++++------ 6 files changed, 167 insertions(+), 102 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index a7c5dfea0..ddce41a22 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -313,3 +313,15 @@ class PermissionManager { _current = ""; } } + +RadioListTile getRadio( + String name, T toValue, T curValue, void Function(T?) onChange) { + return RadioListTile( + controlAffinity: ListTileControlAffinity.trailing, + title: Text(translate(name)), + value: toValue, + groupValue: curValue, + onChanged: onChange, + dense: true, + ); +} diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 1ecad67ef..94a0f8187 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -9,6 +9,10 @@ import 'model.dart'; const loginDialogTag = "LOGIN"; final _emptyIdShow = translate("Generating ..."); +const kUseTemporaryPassword = "use-temporary-password"; +const kUsePermanentPassword = "use-permanent-password"; +const kUseBothPasswords = "use-both-passwords"; + class ServerModel with ChangeNotifier { bool _isStart = false; // Android MainService status bool _mediaOk = false; @@ -16,6 +20,7 @@ class ServerModel with ChangeNotifier { bool _audioOk = false; bool _fileOk = false; int _connectStatus = 0; // Rendezvous Server status + String _verificationMethod = ""; final _serverId = TextEditingController(text: _emptyIdShow); final _serverPasswd = TextEditingController(text: ""); @@ -34,6 +39,8 @@ class ServerModel with ChangeNotifier { int get connectStatus => _connectStatus; + String get verificationMethod => _verificationMethod; + TextEditingController get serverId => _serverId; TextEditingController get serverPasswd => _serverPasswd; @@ -82,13 +89,14 @@ class ServerModel with ChangeNotifier { }(); Timer.periodic(Duration(seconds: 1), (timer) { + var update = false; var status = int.tryParse(FFI.getByName('connect_statue')) ?? 0; if (status > 0) { status = 1; } if (status != _connectStatus) { _connectStatus = status; - notifyListeners(); + update = true; } final res = FFI.getByName('check_clients_length', _clients.length.toString()); @@ -96,6 +104,23 @@ class ServerModel with ChangeNotifier { debugPrint("clients not match!"); updateClientState(res); } + + final temporaryPassword = FFI.getByName("temporary_password"); + final verificationMethod = FFI.getByName("option", "verification-method"); + if (_serverPasswd.text != temporaryPassword) { + _serverPasswd.text = temporaryPassword; + update = true; + } + + if (_verificationMethod != verificationMethod) { + debugPrint("_verificationMethod changed: $verificationMethod"); + _verificationMethod = verificationMethod; + update = true; + } + + if (update) { + notifyListeners(); + } }); } @@ -195,7 +220,7 @@ class ServerModel with ChangeNotifier { FFI.ffiModel.updateEventListener(""); await FFI.invokeMethod("init_service"); FFI.setByName("start_service"); - getIDPasswd(); + _fetchID(); updateClientState(); Wakelock.enable(); } @@ -213,54 +238,33 @@ class ServerModel with ChangeNotifier { await FFI.invokeMethod("init_input"); } - Future updatePassword(String pw) async { - final oldPasswd = _serverPasswd.text; - FFI.setByName("update_password", pw); + Future setPermanentPassword(String newPW) async { + FFI.setByName("permanent_password", newPW); await Future.delayed(Duration(milliseconds: 500)); - await getIDPasswd(force: true); - - // check result - if (pw == "") { - if (_serverPasswd.text.isNotEmpty && _serverPasswd.text != oldPasswd) { - return true; - } else { - return false; - } + final pw = FFI.getByName("permanent_password", newPW); + if (newPW == pw) { + return true; } else { - if (_serverPasswd.text == pw) { - return true; - } else { - return false; - } + return false; } } - getIDPasswd({bool force = false}) async { - if (!force && _serverId.text != _emptyIdShow && _serverPasswd.text != "") { - return; - } + _fetchID() async { + final old = _serverId.text; var count = 0; const maxCount = 10; while (count < maxCount) { await Future.delayed(Duration(seconds: 1)); final id = FFI.getByName("server_id"); - final passwd = FFI.getByName("server_password"); if (id.isEmpty) { continue; } else { _serverId.text = id; } - if (passwd.isEmpty) { - continue; - } else { - _serverPasswd.text = passwd; - } - - debugPrint( - "fetch id & passwd again at $count:id:${_serverId.text},passwd:${_serverPasswd.text}"); + debugPrint("fetch id again at $count:id:${_serverId.text}"); count++; - if (_serverId.text != _emptyIdShow && _serverPasswd.text.isNotEmpty) { + if (_serverId.text != old) { break; } } diff --git a/flutter/lib/pages/remote_page.dart b/flutter/lib/pages/remote_page.dart index 7a3e489b0..265222837 100644 --- a/flutter/lib/pages/remote_page.dart +++ b/flutter/lib/pages/remote_page.dart @@ -961,18 +961,6 @@ CheckboxListTile getToggle( title: Text(translate(name))); } -RadioListTile getRadio(String name, String toValue, String curValue, - void Function(String?) onChange) { - return RadioListTile( - controlAffinity: ListTileControlAffinity.trailing, - title: Text(translate(name)), - value: toValue, - groupValue: curValue, - onChanged: onChange, - dense: true, - ); -} - void showOptions() { String quality = FFI.getByName('image_quality'); if (quality == '') quality = 'balanced'; diff --git a/flutter/lib/pages/server_page.dart b/flutter/lib/pages/server_page.dart index 02f2b4aca..279420d7e 100644 --- a/flutter/lib/pages/server_page.dart +++ b/flutter/lib/pages/server_page.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/widgets/dialog.dart'; @@ -28,32 +30,56 @@ class ServerPage extends StatelessWidget implements PageShape { enabled: false, ), PopupMenuItem( - child: Text(translate("Set your own password")), - value: "changePW", - enabled: FFI.serverModel.isStart, + child: Text(translate("Set permanent password")), + value: "setPermanentPassword", + enabled: + FFI.serverModel.verificationMethod != kUseTemporaryPassword, ), PopupMenuItem( - child: Text(translate("Refresh random password")), - value: "refreshPW", - enabled: FFI.serverModel.isStart, - ) + child: Text(translate("Set temporary password length")), + value: "setTemporaryPasswordLength", + enabled: + FFI.serverModel.verificationMethod != kUsePermanentPassword, + ), + const PopupMenuDivider(), + CheckedPopupMenuItem( + checked: + FFI.serverModel.verificationMethod == kUseTemporaryPassword, + padding: EdgeInsets.all(0), + value: kUseTemporaryPassword, + child: Text(translate("Use temporary password")), + ), + CheckedPopupMenuItem( + checked: + FFI.serverModel.verificationMethod == kUsePermanentPassword, + padding: EdgeInsets.all(0), + value: kUsePermanentPassword, + child: Text(translate("Use permanent password")), + ), + CheckedPopupMenuItem( + checked: FFI.serverModel.verificationMethod != + kUseTemporaryPassword && + FFI.serverModel.verificationMethod != kUsePermanentPassword, + padding: EdgeInsets.all(0), + value: kUseBothPasswords, + child: Text(translate("Use both passwords")), + ), ]; }, onSelected: (value) { if (value == "changeID") { // TODO - } else if (value == "changePW") { - updatePasswordDialog(); - } else if (value == "refreshPW") { - () async { - showLoading(translate("Waiting")); - if (await FFI.serverModel.updatePassword("")) { - showSuccess(); - } else { - showError(); - } - debugPrint("end updatePassword"); - }(); + } else if (value == "setPermanentPassword") { + setPermanentPasswordDialog(); + } else if (value == "setTemporaryPasswordLength") { + setTemporaryPasswordLengthDialog(); + } else if (value == kUsePermanentPassword || + value == kUseTemporaryPassword || + value == kUseBothPasswords) { + Map msg = Map() + ..["name"] = "verification-method" + ..["value"] = value; + FFI.setByName('option', jsonEncode(msg)); } }) ]; @@ -90,17 +116,13 @@ void checkService() async { } } -class ServerInfo extends StatefulWidget { - @override - _ServerInfoState createState() => _ServerInfoState(); -} - -class _ServerInfoState extends State { +class ServerInfo extends StatelessWidget { final model = FFI.serverModel; - var _passwdShow = false; + final emptyController = TextEditingController(text: "-"); @override Widget build(BuildContext context) { + final isPermanent = model.verificationMethod == kUsePermanentPassword; return model.isStart ? PaddingCard( child: Column( @@ -123,24 +145,23 @@ class _ServerInfoState extends State { ), TextFormField( readOnly: true, - obscureText: !_passwdShow, style: TextStyle( fontSize: 25.0, fontWeight: FontWeight.bold, color: MyTheme.accent), - controller: model.serverPasswd, + controller: isPermanent ? emptyController : model.serverPasswd, decoration: InputDecoration( icon: const Icon(Icons.lock), labelText: translate("Password"), labelStyle: TextStyle( fontWeight: FontWeight.bold, color: MyTheme.accent50), - suffix: IconButton( - icon: Icon(Icons.visibility), - onPressed: () { - setState(() { - _passwdShow = !_passwdShow; - }); - })), + suffix: isPermanent + ? null + : IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + FFI.setByName("temporary_password"); + })), onSaved: (String? value) {}, ), ], diff --git a/flutter/lib/widgets/dialog.dart b/flutter/lib/widgets/dialog.dart index 7781cfe40..106685071 100644 --- a/flutter/lib/widgets/dialog.dart +++ b/flutter/lib/widgets/dialog.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -20,9 +21,10 @@ void showError({Duration duration = SEC1}) { showToast(translate("Error"), duration: SEC1); } -void updatePasswordDialog() { - final p0 = TextEditingController(); - final p1 = TextEditingController(); +void setPermanentPasswordDialog() { + final pw = FFI.getByName("permanent_password"); + final p0 = TextEditingController(text: pw); + final p1 = TextEditingController(text: pw); var validateLength = false; var validateSame = false; DialogManager.show((setState, close) { @@ -86,7 +88,7 @@ void updatePasswordDialog() { ? () async { close(); showLoading(translate("Waiting")); - if (await FFI.serverModel.updatePassword(p0.text)) { + if (await FFI.serverModel.setPermanentPassword(p0.text)) { showSuccess(); } else { showError(); @@ -100,6 +102,37 @@ void updatePasswordDialog() { }); } +void setTemporaryPasswordLengthDialog() { + List lengths = ['6', '8', '10']; + String length = FFI.getByName('option', 'temporary-password-length'); + var index = lengths.indexOf(length); + if (index < 0) index = 0; + length = lengths[index]; + DialogManager.show((setState, close) { + final setLength = (newValue) { + final oldValue = length; + if (oldValue == newValue) return; + setState(() { + length = newValue; + }); + Map msg = Map() + ..["name"] = "temporary-password-length" + ..["value"] = newValue; + FFI.setByName("option", jsonEncode(msg)); + FFI.setByName("temporary_password"); + }; + return CustomAlertDialog( + title: Text(translate("Set temporary password length")), + content: Column( + mainAxisSize: MainAxisSize.min, + children: + lengths.map((e) => getRadio(e, e, length, setLength)).toList()), + actions: [], + contentPadding: 14, + ); + }, backDismiss: true, clickMaskDismiss: true); +} + void enterPasswordDialog(String id) { final controller = TextEditingController(); var remember = FFI.getByName('remember', id) == 'true'; diff --git a/src/mobile_ffi.rs b/src/mobile_ffi.rs index cf3405a06..2b456c24f 100644 --- a/src/mobile_ffi.rs +++ b/src/mobile_ffi.rs @@ -4,6 +4,7 @@ use crate::mobile::{self, Session}; use crate::common::{make_fd_to_json}; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; use hbb_common::{ResultType, init_uuid}; +use hbb_common::password_security::password; use hbb_common::{ config::{self, Config, LocalConfig, PeerConfig, ONLINE}, fs, log, @@ -115,22 +116,6 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co res = Session::get_option(arg); } } - "server_id" => { - res = Config::get_id(); - } - "server_password" => { - todo!() - } - "connect_statue" => { - res = ONLINE - .lock() - .unwrap() - .values() - .max() - .unwrap_or(&0) - .clone() - .to_string(); - } // File Action "get_home_dir" => { res = fs::get_home_as_string(); @@ -151,6 +136,25 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co } } // Server Side + "server_id" => { + res = Config::get_id(); + } + "permanent_password" => { + res = Config::get_permanent_password(); + } + "temporary_password" => { + res = password::temporary_password(); + } + "connect_statue" => { + res = ONLINE + .lock() + .unwrap() + .values() + .max() + .unwrap_or(&0) + .clone() + .to_string(); + } #[cfg(target_os = "android")] "clients_state" => { res = get_clients_state(); @@ -458,8 +462,11 @@ unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) { } } // Server Side - "update_password" => { - todo!() + "permanent_password" => { + Config::set_permanent_password(value) + } + "temporary_password" => { + password::update_temporary_password(); } #[cfg(target_os = "android")] "chat_server_mode" => {