Merge pull request #1112 from Heap-Hop/android_password

Android refactor password
This commit is contained in:
RustDesk 2022-07-29 22:22:08 +08:00 committed by GitHub
commit cf88ca2bce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 196 additions and 101 deletions

View File

@ -313,3 +313,15 @@ class PermissionManager {
_current = ""; _current = "";
} }
} }
RadioListTile<T> getRadio<T>(
String name, T toValue, T curValue, void Function(T?) onChange) {
return RadioListTile<T>(
controlAffinity: ListTileControlAffinity.trailing,
title: Text(translate(name)),
value: toValue,
groupValue: curValue,
onChanged: onChange,
dense: true,
);
}

View File

@ -9,6 +9,10 @@ import 'model.dart';
const loginDialogTag = "LOGIN"; const loginDialogTag = "LOGIN";
final _emptyIdShow = translate("Generating ..."); final _emptyIdShow = translate("Generating ...");
const kUseTemporaryPassword = "use-temporary-password";
const kUsePermanentPassword = "use-permanent-password";
const kUseBothPasswords = "use-both-passwords";
class ServerModel with ChangeNotifier { class ServerModel with ChangeNotifier {
bool _isStart = false; // Android MainService status bool _isStart = false; // Android MainService status
bool _mediaOk = false; bool _mediaOk = false;
@ -16,6 +20,7 @@ class ServerModel with ChangeNotifier {
bool _audioOk = false; bool _audioOk = false;
bool _fileOk = false; bool _fileOk = false;
int _connectStatus = 0; // Rendezvous Server status int _connectStatus = 0; // Rendezvous Server status
String _verificationMethod = "";
final _serverId = TextEditingController(text: _emptyIdShow); final _serverId = TextEditingController(text: _emptyIdShow);
final _serverPasswd = TextEditingController(text: ""); final _serverPasswd = TextEditingController(text: "");
@ -34,6 +39,8 @@ class ServerModel with ChangeNotifier {
int get connectStatus => _connectStatus; int get connectStatus => _connectStatus;
String get verificationMethod => _verificationMethod;
TextEditingController get serverId => _serverId; TextEditingController get serverId => _serverId;
TextEditingController get serverPasswd => _serverPasswd; TextEditingController get serverPasswd => _serverPasswd;
@ -96,9 +103,29 @@ class ServerModel with ChangeNotifier {
debugPrint("clients not match!"); debugPrint("clients not match!");
updateClientState(res); updateClientState(res);
} }
updatePasswordModel();
}); });
} }
updatePasswordModel() {
var update = false;
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) {
_verificationMethod = verificationMethod;
update = true;
}
if (update) {
notifyListeners();
}
}
toggleAudio() async { toggleAudio() async {
if (!_audioOk && !await PermissionManager.check("audio")) { if (!_audioOk && !await PermissionManager.check("audio")) {
final res = await PermissionManager.request("audio"); final res = await PermissionManager.request("audio");
@ -195,7 +222,7 @@ class ServerModel with ChangeNotifier {
FFI.ffiModel.updateEventListener(""); FFI.ffiModel.updateEventListener("");
await FFI.invokeMethod("init_service"); await FFI.invokeMethod("init_service");
FFI.setByName("start_service"); FFI.setByName("start_service");
getIDPasswd(); _fetchID();
updateClientState(); updateClientState();
Wakelock.enable(); Wakelock.enable();
} }
@ -213,54 +240,33 @@ class ServerModel with ChangeNotifier {
await FFI.invokeMethod("init_input"); await FFI.invokeMethod("init_input");
} }
Future<bool> updatePassword(String pw) async { Future<bool> setPermanentPassword(String newPW) async {
final oldPasswd = _serverPasswd.text; FFI.setByName("permanent_password", newPW);
FFI.setByName("update_password", pw);
await Future.delayed(Duration(milliseconds: 500)); await Future.delayed(Duration(milliseconds: 500));
await getIDPasswd(force: true); final pw = FFI.getByName("permanent_password", newPW);
if (newPW == pw) {
// check result return true;
if (pw == "") {
if (_serverPasswd.text.isNotEmpty && _serverPasswd.text != oldPasswd) {
return true;
} else {
return false;
}
} else { } else {
if (_serverPasswd.text == pw) { return false;
return true;
} else {
return false;
}
} }
} }
getIDPasswd({bool force = false}) async { _fetchID() async {
if (!force && _serverId.text != _emptyIdShow && _serverPasswd.text != "") { final old = _serverId.text;
return;
}
var count = 0; var count = 0;
const maxCount = 10; const maxCount = 10;
while (count < maxCount) { while (count < maxCount) {
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
final id = FFI.getByName("server_id"); final id = FFI.getByName("server_id");
final passwd = FFI.getByName("server_password");
if (id.isEmpty) { if (id.isEmpty) {
continue; continue;
} else { } else {
_serverId.text = id; _serverId.text = id;
} }
if (passwd.isEmpty) { debugPrint("fetch id again at $count:id:${_serverId.text}");
continue;
} else {
_serverPasswd.text = passwd;
}
debugPrint(
"fetch id & passwd again at $count:id:${_serverId.text},passwd:${_serverPasswd.text}");
count++; count++;
if (_serverId.text != _emptyIdShow && _serverPasswd.text.isNotEmpty) { if (_serverId.text != old) {
break; break;
} }
} }

View File

@ -961,18 +961,6 @@ CheckboxListTile getToggle(
title: Text(translate(name))); title: Text(translate(name)));
} }
RadioListTile<String> getRadio(String name, String toValue, String curValue,
void Function(String?) onChange) {
return RadioListTile<String>(
controlAffinity: ListTileControlAffinity.trailing,
title: Text(translate(name)),
value: toValue,
groupValue: curValue,
onChanged: onChange,
dense: true,
);
}
void showOptions() { void showOptions() {
String quality = FFI.getByName('image_quality'); String quality = FFI.getByName('image_quality');
if (quality == '') quality = 'balanced'; if (quality == '') quality = 'balanced';

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/widgets/dialog.dart'; import 'package:flutter_hbb/widgets/dialog.dart';
@ -24,36 +26,84 @@ class ServerPage extends StatelessWidget implements PageShape {
return [ return [
PopupMenuItem( PopupMenuItem(
child: Text(translate("Change ID")), child: Text(translate("Change ID")),
padding: EdgeInsets.symmetric(horizontal: 16.0),
value: "changeID", value: "changeID",
enabled: false, enabled: false,
), ),
PopupMenuItem( PopupMenuItem(
child: Text(translate("Set your own password")), child: Text(translate("Set permanent password")),
value: "changePW", padding: EdgeInsets.symmetric(horizontal: 16.0),
enabled: FFI.serverModel.isStart, value: "setPermanentPassword",
enabled:
FFI.serverModel.verificationMethod != kUseTemporaryPassword,
), ),
PopupMenuItem( PopupMenuItem(
child: Text(translate("Refresh random password")), child: Text(translate("Set temporary password length")),
value: "refreshPW", padding: EdgeInsets.symmetric(horizontal: 16.0),
enabled: FFI.serverModel.isStart, value: "setTemporaryPasswordLength",
) enabled:
FFI.serverModel.verificationMethod != kUsePermanentPassword,
),
const PopupMenuDivider(),
PopupMenuItem(
padding: EdgeInsets.symmetric(horizontal: 0.0),
value: kUseTemporaryPassword,
child: Container(
child: ListTile(
title: Text(translate("Use temporary password")),
trailing: Icon(
Icons.check,
color: FFI.serverModel.verificationMethod ==
kUseTemporaryPassword
? null
: Color(0xFFFFFFFF),
))),
),
PopupMenuItem(
padding: EdgeInsets.symmetric(horizontal: 0.0),
value: kUsePermanentPassword,
child: ListTile(
title: Text(translate("Use permanent password")),
trailing: Icon(
Icons.check,
color: FFI.serverModel.verificationMethod ==
kUsePermanentPassword
? null
: Color(0xFFFFFFFF),
)),
),
PopupMenuItem(
padding: EdgeInsets.symmetric(horizontal: 0.0),
value: kUseBothPasswords,
child: ListTile(
title: Text(translate("Use both passwords")),
trailing: Icon(
Icons.check,
color: FFI.serverModel.verificationMethod !=
kUseTemporaryPassword &&
FFI.serverModel.verificationMethod !=
kUsePermanentPassword
? null
: Color(0xFFFFFFFF),
)),
),
]; ];
}, },
onSelected: (value) { onSelected: (value) {
if (value == "changeID") { if (value == "changeID") {
// TODO // TODO
} else if (value == "changePW") { } else if (value == "setPermanentPassword") {
updatePasswordDialog(); setPermanentPasswordDialog();
} else if (value == "refreshPW") { } else if (value == "setTemporaryPasswordLength") {
() async { setTemporaryPasswordLengthDialog();
showLoading(translate("Waiting")); } else if (value == kUsePermanentPassword ||
if (await FFI.serverModel.updatePassword("")) { value == kUseTemporaryPassword ||
showSuccess(); value == kUseBothPasswords) {
} else { Map<String, String> msg = Map()
showError(); ..["name"] = "verification-method"
} ..["value"] = value;
debugPrint("end updatePassword"); FFI.setByName('option', jsonEncode(msg));
}(); FFI.serverModel.updatePasswordModel();
} }
}) })
]; ];
@ -90,17 +140,13 @@ void checkService() async {
} }
} }
class ServerInfo extends StatefulWidget { class ServerInfo extends StatelessWidget {
@override
_ServerInfoState createState() => _ServerInfoState();
}
class _ServerInfoState extends State<ServerInfo> {
final model = FFI.serverModel; final model = FFI.serverModel;
var _passwdShow = false; final emptyController = TextEditingController(text: "-");
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isPermanent = model.verificationMethod == kUsePermanentPassword;
return model.isStart return model.isStart
? PaddingCard( ? PaddingCard(
child: Column( child: Column(
@ -123,24 +169,23 @@ class _ServerInfoState extends State<ServerInfo> {
), ),
TextFormField( TextFormField(
readOnly: true, readOnly: true,
obscureText: !_passwdShow,
style: TextStyle( style: TextStyle(
fontSize: 25.0, fontSize: 25.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: MyTheme.accent), color: MyTheme.accent),
controller: model.serverPasswd, controller: isPermanent ? emptyController : model.serverPasswd,
decoration: InputDecoration( decoration: InputDecoration(
icon: const Icon(Icons.lock), icon: const Icon(Icons.lock),
labelText: translate("Password"), labelText: translate("Password"),
labelStyle: TextStyle( labelStyle: TextStyle(
fontWeight: FontWeight.bold, color: MyTheme.accent50), fontWeight: FontWeight.bold, color: MyTheme.accent50),
suffix: IconButton( suffix: isPermanent
icon: Icon(Icons.visibility), ? null
onPressed: () { : IconButton(
setState(() { icon: const Icon(Icons.refresh),
_passwdShow = !_passwdShow; onPressed: () {
}); FFI.setByName("temporary_password");
})), })),
onSaved: (String? value) {}, onSaved: (String? value) {},
), ),
], ],

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@ -20,9 +21,10 @@ void showError({Duration duration = SEC1}) {
showToast(translate("Error"), duration: SEC1); showToast(translate("Error"), duration: SEC1);
} }
void updatePasswordDialog() { void setPermanentPasswordDialog() {
final p0 = TextEditingController(); final pw = FFI.getByName("permanent_password");
final p1 = TextEditingController(); final p0 = TextEditingController(text: pw);
final p1 = TextEditingController(text: pw);
var validateLength = false; var validateLength = false;
var validateSame = false; var validateSame = false;
DialogManager.show((setState, close) { DialogManager.show((setState, close) {
@ -86,7 +88,7 @@ void updatePasswordDialog() {
? () async { ? () async {
close(); close();
showLoading(translate("Waiting")); showLoading(translate("Waiting"));
if (await FFI.serverModel.updatePassword(p0.text)) { if (await FFI.serverModel.setPermanentPassword(p0.text)) {
showSuccess(); showSuccess();
} else { } else {
showError(); showError();
@ -100,6 +102,41 @@ void updatePasswordDialog() {
}); });
} }
void setTemporaryPasswordLengthDialog() {
List<String> 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<String, String> msg = Map()
..["name"] = "temporary-password-length"
..["value"] = newValue;
FFI.setByName("option", jsonEncode(msg));
FFI.setByName("temporary_password");
Future.delayed(Duration(milliseconds: 200), () {
close();
showSuccess();
});
};
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) { void enterPasswordDialog(String id) {
final controller = TextEditingController(); final controller = TextEditingController();
var remember = FFI.getByName('remember', id) == 'true'; var remember = FFI.getByName('remember', id) == 'true';

View File

@ -4,6 +4,7 @@ use crate::mobile::{self, Session};
use crate::common::{make_fd_to_json}; use crate::common::{make_fd_to_json};
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
use hbb_common::{ResultType, init_uuid}; use hbb_common::{ResultType, init_uuid};
use hbb_common::password_security::password;
use hbb_common::{ use hbb_common::{
config::{self, Config, LocalConfig, PeerConfig, ONLINE}, config::{self, Config, LocalConfig, PeerConfig, ONLINE},
fs, log, 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); 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 // File Action
"get_home_dir" => { "get_home_dir" => {
res = fs::get_home_as_string(); 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 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")] #[cfg(target_os = "android")]
"clients_state" => { "clients_state" => {
res = get_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 // Server Side
"update_password" => { "permanent_password" => {
todo!() Config::set_permanent_password(value)
}
"temporary_password" => {
password::update_temporary_password();
} }
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
"chat_server_mode" => { "chat_server_mode" => {