mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-27 23:21:06 +03:00
refact(password): Store permanent password as hashed verifier (#14619)
* refact(password): Store permanent password as hashed verifier Signed-off-by: fufesou <linlong1266@gmail.com> * fix(password): remove unused code Signed-off-by: fufesou <linlong1266@gmail.com> * fix(password): mobile, password dialog, width 500 Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -2377,8 +2377,9 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
|
||||
final password = uri.path.substring("/".length);
|
||||
if (password.isNotEmpty) {
|
||||
Timer(Duration(seconds: 1), () async {
|
||||
await bind.mainSetPermanentPassword(password: password);
|
||||
showToast(translate('Successful'));
|
||||
final ok =
|
||||
await bind.mainSetPermanentPasswordWithResult(password: password);
|
||||
showToast(translate(ok ? 'Successful' : 'Failed'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -908,12 +908,17 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
}
|
||||
|
||||
void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
final pw = await bind.mainGetPermanentPassword();
|
||||
final p0 = TextEditingController(text: pw);
|
||||
final p1 = TextEditingController(text: pw);
|
||||
final p0 = TextEditingController(text: "");
|
||||
final p1 = TextEditingController(text: "");
|
||||
var errMsg0 = "";
|
||||
var errMsg1 = "";
|
||||
final RxString rxPass = pw.trim().obs;
|
||||
final localPasswordSet =
|
||||
(await bind.mainGetCommon(key: "local-permanent-password-set")) == "true";
|
||||
final permanentPasswordSet =
|
||||
(await bind.mainGetCommon(key: "permanent-password-set")) == "true";
|
||||
final presetPassword = permanentPasswordSet && !localPasswordSet;
|
||||
var canSubmit = false;
|
||||
final RxString rxPass = "".obs;
|
||||
final rules = [
|
||||
DigitValidationRule(),
|
||||
UppercaseValidationRule(),
|
||||
@@ -922,9 +927,21 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
MinCharactersValidationRule(8),
|
||||
];
|
||||
final maxLength = bind.mainMaxEncryptLen();
|
||||
final statusTip = localPasswordSet
|
||||
? translate('password-hidden-tip')
|
||||
: (presetPassword ? translate('preset-password-in-use-tip') : '');
|
||||
final showStatusTipOnMobile =
|
||||
statusTip.isNotEmpty && !isDesktop && !isWebDesktop;
|
||||
|
||||
gFFI.dialogManager.show((setState, close, context) {
|
||||
submit() {
|
||||
updateCanSubmit() {
|
||||
canSubmit = p0.text.trim().isNotEmpty || p1.text.trim().isNotEmpty;
|
||||
}
|
||||
|
||||
submit() async {
|
||||
if (!canSubmit) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
errMsg0 = "";
|
||||
errMsg1 = "";
|
||||
@@ -947,7 +964,13 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
});
|
||||
return;
|
||||
}
|
||||
bind.mainSetPermanentPassword(password: pass);
|
||||
final ok = await bind.mainSetPermanentPasswordWithResult(password: pass);
|
||||
if (!ok) {
|
||||
setState(() {
|
||||
errMsg0 = '${translate('Prompt')}: ${translate("Failed")}';
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pass.isNotEmpty) {
|
||||
notEmptyCallback?.call();
|
||||
}
|
||||
@@ -955,14 +978,20 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate("Set Password")),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.key, color: MyTheme.accent),
|
||||
Text(translate("Set Password")).paddingOnly(left: 10),
|
||||
],
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 500),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
SizedBox(
|
||||
height: showStatusTipOnMobile ? 0.0 : 6.0,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
@@ -978,6 +1007,7 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
rxPass.value = value.trim();
|
||||
setState(() {
|
||||
errMsg0 = '';
|
||||
updateCanSubmit();
|
||||
});
|
||||
},
|
||||
maxLength: maxLength,
|
||||
@@ -989,9 +1019,9 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
children: [
|
||||
Expanded(child: PasswordStrengthIndicator(password: rxPass)),
|
||||
],
|
||||
).marginSymmetric(vertical: 8),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
).marginOnly(top: 2, bottom: showStatusTipOnMobile ? 2 : 8),
|
||||
SizedBox(
|
||||
height: showStatusTipOnMobile ? 0.0 : 8.0,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
@@ -1005,6 +1035,7 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
errMsg1 = '';
|
||||
updateCanSubmit();
|
||||
});
|
||||
},
|
||||
maxLength: maxLength,
|
||||
@@ -1012,11 +1043,23 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
if (statusTip.isNotEmpty)
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.info, color: Colors.amber, size: 18)
|
||||
.marginOnly(right: 6),
|
||||
Expanded(
|
||||
child: Text(
|
||||
statusTip,
|
||||
style: const TextStyle(fontSize: 13, height: 1.1),
|
||||
))
|
||||
],
|
||||
).marginOnly(top: 6, bottom: 2),
|
||||
SizedBox(
|
||||
height: showStatusTipOnMobile ? 0.0 : 8.0,
|
||||
),
|
||||
Obx(() => Wrap(
|
||||
runSpacing: 8,
|
||||
runSpacing: showStatusTipOnMobile ? 2.0 : 8.0,
|
||||
spacing: 4,
|
||||
children: rules.map((e) {
|
||||
var checked = e.validate(rxPass.value.trim());
|
||||
@@ -1036,11 +1079,67 @@ void setPasswordDialog({VoidCallback? notEmptyCallback}) async {
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||
dialogButton("OK", onPressed: submit),
|
||||
],
|
||||
onSubmit: submit,
|
||||
actions: (() {
|
||||
final cancelButton = dialogButton(
|
||||
"Cancel",
|
||||
icon: Icon(Icons.close_rounded),
|
||||
onPressed: close,
|
||||
isOutline: true,
|
||||
);
|
||||
final removeButton = dialogButton(
|
||||
"Remove",
|
||||
icon: Icon(Icons.delete_outline_rounded),
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
errMsg0 = "";
|
||||
errMsg1 = "";
|
||||
});
|
||||
final ok =
|
||||
await bind.mainSetPermanentPasswordWithResult(password: "");
|
||||
if (!ok) {
|
||||
setState(() {
|
||||
errMsg0 = '${translate('Prompt')}: ${translate("Failed")}';
|
||||
});
|
||||
return;
|
||||
}
|
||||
close();
|
||||
},
|
||||
buttonStyle: ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.red)),
|
||||
);
|
||||
final okButton = dialogButton(
|
||||
"OK",
|
||||
icon: Icon(Icons.done_rounded),
|
||||
onPressed: canSubmit ? submit : null,
|
||||
);
|
||||
if (!isDesktop && !isWebDesktop && localPasswordSet) {
|
||||
return [
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: Alignment.centerRight,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
cancelButton,
|
||||
const SizedBox(width: 4),
|
||||
removeButton,
|
||||
const SizedBox(width: 4),
|
||||
okButton,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
return [
|
||||
cancelButton,
|
||||
if (localPasswordSet) removeButton,
|
||||
okButton,
|
||||
];
|
||||
})(),
|
||||
onSubmit: canSubmit ? submit : null,
|
||||
onCancel: close,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1109,8 +1109,9 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
if (value ==
|
||||
passwordValues[passwordKeys
|
||||
.indexOf(kUsePermanentPassword)] &&
|
||||
(await bind.mainGetPermanentPassword())
|
||||
.isEmpty) {
|
||||
(await bind.mainGetCommon(
|
||||
key: "permanent-password-set")) !=
|
||||
"true") {
|
||||
if (isChangePermanentPasswordDisabled()) {
|
||||
await callback();
|
||||
return;
|
||||
|
||||
@@ -150,7 +150,8 @@ class _DropDownAction extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (value == kUsePermanentPassword &&
|
||||
(await bind.mainGetPermanentPassword()).isEmpty) {
|
||||
(await bind.mainGetCommon(key: "permanent-password-set")) !=
|
||||
"true") {
|
||||
if (isChangePermanentPasswordDisabled()) {
|
||||
callback();
|
||||
return;
|
||||
|
||||
@@ -12,100 +12,6 @@ void _showSuccess() {
|
||||
showToast(translate("Successful"));
|
||||
}
|
||||
|
||||
void _showError() {
|
||||
showToast(translate("Error"));
|
||||
}
|
||||
|
||||
void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async {
|
||||
final pw = await bind.mainGetPermanentPassword();
|
||||
final p0 = TextEditingController(text: pw);
|
||||
final p1 = TextEditingController(text: pw);
|
||||
var validateLength = false;
|
||||
var validateSame = false;
|
||||
dialogManager.show((setState, close, context) {
|
||||
submit() async {
|
||||
close();
|
||||
dialogManager.showLoading(translate("Waiting"));
|
||||
if (await gFFI.serverModel.setPermanentPassword(p0.text)) {
|
||||
dialogManager.dismissAll();
|
||||
_showSuccess();
|
||||
} else {
|
||||
dialogManager.dismissAll();
|
||||
_showError();
|
||||
}
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.password_rounded, color: MyTheme.accent),
|
||||
Text(translate('Set your own password')).paddingOnly(left: 10),
|
||||
],
|
||||
),
|
||||
content: Form(
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
TextFormField(
|
||||
autofocus: true,
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: translate('Password'),
|
||||
),
|
||||
controller: p0,
|
||||
validator: (v) {
|
||||
if (v == null) return null;
|
||||
final val = v.trim().length > 5;
|
||||
if (validateLength != val) {
|
||||
// use delay to make setState success
|
||||
Future.delayed(Duration(microseconds: 1),
|
||||
() => setState(() => validateLength = val));
|
||||
}
|
||||
return val
|
||||
? null
|
||||
: translate('Too short, at least 6 characters.');
|
||||
},
|
||||
).workaroundFreezeLinuxMint(),
|
||||
TextFormField(
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
decoration: InputDecoration(
|
||||
labelText: translate('Confirmation'),
|
||||
),
|
||||
controller: p1,
|
||||
validator: (v) {
|
||||
if (v == null) return null;
|
||||
final val = p0.text == v;
|
||||
if (validateSame != val) {
|
||||
Future.delayed(Duration(microseconds: 1),
|
||||
() => setState(() => validateSame = val));
|
||||
}
|
||||
return val
|
||||
? null
|
||||
: translate('The confirmation is not identical.');
|
||||
},
|
||||
).workaroundFreezeLinuxMint(),
|
||||
])),
|
||||
onCancel: close,
|
||||
onSubmit: (validateLength && validateSame) ? submit : null,
|
||||
actions: [
|
||||
dialogButton(
|
||||
'Cancel',
|
||||
icon: Icon(Icons.close_rounded),
|
||||
onPressed: close,
|
||||
isOutline: true,
|
||||
),
|
||||
dialogButton(
|
||||
'OK',
|
||||
icon: Icon(Icons.done_rounded),
|
||||
onPressed: (validateLength && validateSame) ? submit : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void setTemporaryPasswordLengthDialog(
|
||||
OverlayDialogManager dialogManager) async {
|
||||
List<String> lengths = ['6', '8', '10'];
|
||||
|
||||
@@ -471,17 +471,6 @@ class ServerModel with ChangeNotifier {
|
||||
WakelockManager.disable(_wakelockKey);
|
||||
}
|
||||
|
||||
Future<bool> setPermanentPassword(String newPW) async {
|
||||
await bind.mainSetPermanentPassword(password: newPW);
|
||||
await Future.delayed(Duration(milliseconds: 500));
|
||||
final pw = await bind.mainGetPermanentPassword();
|
||||
if (newPW == pw) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fetchID() async {
|
||||
final id = await bind.mainGetMyId();
|
||||
if (id != _serverId.id) {
|
||||
|
||||
@@ -1159,10 +1159,6 @@ class RustdeskImpl {
|
||||
return Future.value('');
|
||||
}
|
||||
|
||||
Future<String> mainGetPermanentPassword({dynamic hint}) {
|
||||
return Future.value('');
|
||||
}
|
||||
|
||||
Future<String> mainGetFingerprint({dynamic hint}) {
|
||||
return Future.value('');
|
||||
}
|
||||
@@ -1346,9 +1342,9 @@ class RustdeskImpl {
|
||||
throw UnimplementedError("mainUpdateTemporaryPassword");
|
||||
}
|
||||
|
||||
Future<void> mainSetPermanentPassword(
|
||||
Future<bool> mainSetPermanentPasswordWithResult(
|
||||
{required String password, dynamic hint}) {
|
||||
throw UnimplementedError("mainSetPermanentPassword");
|
||||
throw UnimplementedError("mainSetPermanentPasswordWithResult");
|
||||
}
|
||||
|
||||
Future<bool> mainCheckSuperUserPermission({dynamic hint}) {
|
||||
|
||||
Reference in New Issue
Block a user