mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 00:12:09 +03:00
Add lock-code field in ManagementScreen
This commit is contained in:
parent
bccfdb6afb
commit
c10d75e9b7
@ -100,9 +100,6 @@ class _DesktopManagementStateNotifier extends ManagementStateNotifier {
|
||||
{String currentLockCode = '',
|
||||
String newLockCode = '',
|
||||
bool reboot = false}) async {
|
||||
if (reboot) {
|
||||
state = const AsyncValue.loading();
|
||||
}
|
||||
await _session.command('configure', target: _subpath, params: {
|
||||
...config.toJson(),
|
||||
'cur_lock_code': currentLockCode,
|
||||
|
@ -213,7 +213,8 @@ class _DesktopPivStateNotifier extends PivStateNotifier {
|
||||
return const PinVerificationStatus.success();
|
||||
} on RpcError catch (e) {
|
||||
if (e.status == 'invalid-pin') {
|
||||
return PinVerificationStatus.failure(e.body['attempts_remaining']);
|
||||
return PinVerificationStatus.failure(
|
||||
PivPinFailureReason.invalidPin(e.body['attempts_remaining']));
|
||||
}
|
||||
rethrow;
|
||||
} finally {
|
||||
|
@ -173,6 +173,8 @@
|
||||
"@_app_configuration": {},
|
||||
"s_toggle_applications": "Anwendungen umschalten",
|
||||
"s_toggle_interfaces": null,
|
||||
"p_toggle_applications_desc": null,
|
||||
"p_toggle_interfaces_desc": null,
|
||||
"l_toggle_applications_desc": null,
|
||||
"l_toggle_interfaces_desc": null,
|
||||
"s_reconfiguring_yk": "YubiKey wird neu konfiguriert\u2026",
|
||||
@ -195,6 +197,12 @@
|
||||
},
|
||||
"s_fido_disabled": "FIDO2 deaktiviert",
|
||||
"l_webauthn_req_fido2": "WebAuthn erfordert, dass die FIDO2 Anwendung auf Ihrem YubiKey aktiviert ist",
|
||||
"s_lock_code": null,
|
||||
"l_wrong_lock_code": null,
|
||||
"s_show_lock_code": null,
|
||||
"s_hide_lock_code": null,
|
||||
"p_lock_code_required_desc": null,
|
||||
|
||||
|
||||
"@_connectivity_issues": {},
|
||||
"l_helper_not_responding": "Der Helper-Prozess antwortet nicht",
|
||||
|
@ -173,6 +173,8 @@
|
||||
"@_app_configuration": {},
|
||||
"s_toggle_applications": "Toggle applications",
|
||||
"s_toggle_interfaces": "Toggle interfaces",
|
||||
"p_toggle_applications_desc": "Independently enable or disable applications over available transports.",
|
||||
"p_toggle_interfaces_desc": "Independently enable or disable USB interfaces.",
|
||||
"l_toggle_applications_desc": "Enable/disable applications",
|
||||
"l_toggle_interfaces_desc": "Enable/disable interfaces",
|
||||
"s_reconfiguring_yk": "Reconfiguring YubiKey\u2026",
|
||||
@ -195,6 +197,12 @@
|
||||
},
|
||||
"s_fido_disabled": "FIDO2 disabled",
|
||||
"l_webauthn_req_fido2": "WebAuthn requires the FIDO2 application to be enabled on your YubiKey",
|
||||
"s_lock_code": "Lock code",
|
||||
"l_wrong_lock_code": "Wrong lock code",
|
||||
"s_show_lock_code": "Show lock code",
|
||||
"s_hide_lock_code": "Hide lock code",
|
||||
"p_lock_code_required_desc": "The action you are about to perform requires the configuration lock code to be entered.",
|
||||
|
||||
|
||||
"@_connectivity_issues": {},
|
||||
"l_helper_not_responding": "The Helper process isn't responding",
|
||||
|
@ -173,6 +173,8 @@
|
||||
"@_app_configuration": {},
|
||||
"s_toggle_applications": "Changer les applications",
|
||||
"s_toggle_interfaces": null,
|
||||
"p_toggle_applications_desc": null,
|
||||
"p_toggle_interfaces_desc": null,
|
||||
"l_toggle_applications_desc": null,
|
||||
"l_toggle_interfaces_desc": null,
|
||||
"s_reconfiguring_yk": "Reconfiguration de la YubiKey\u2026",
|
||||
@ -195,6 +197,12 @@
|
||||
},
|
||||
"s_fido_disabled": "FIDO2 désactivé",
|
||||
"l_webauthn_req_fido2": "WebAuthn demande que le FIDO2 soit activé sur votre YubiKey",
|
||||
"s_lock_code": null,
|
||||
"l_wrong_lock_code": null,
|
||||
"s_show_lock_code": null,
|
||||
"s_hide_lock_code": null,
|
||||
"p_lock_code_required_desc": null,
|
||||
|
||||
|
||||
"@_connectivity_issues": {},
|
||||
"l_helper_not_responding": "Le processus Helper ne réponds pas",
|
||||
|
@ -173,6 +173,8 @@
|
||||
"@_app_configuration": {},
|
||||
"s_toggle_applications": "アプリケーションの切替え",
|
||||
"s_toggle_interfaces": null,
|
||||
"p_toggle_applications_desc": null,
|
||||
"p_toggle_interfaces_desc": null,
|
||||
"l_toggle_applications_desc": null,
|
||||
"l_toggle_interfaces_desc": null,
|
||||
"s_reconfiguring_yk": "YubiKeyを再構成しています\u2026",
|
||||
@ -195,6 +197,12 @@
|
||||
},
|
||||
"s_fido_disabled": "FIDO2が無効になっています",
|
||||
"l_webauthn_req_fido2": "WebAuthnでは、YubiKeyでFIDO2アプリケーションを有効にする必要があります",
|
||||
"s_lock_code": null,
|
||||
"l_wrong_lock_code": null,
|
||||
"s_show_lock_code": null,
|
||||
"s_hide_lock_code": null,
|
||||
"p_lock_code_required_desc": null,
|
||||
|
||||
|
||||
"@_connectivity_issues": {},
|
||||
"l_helper_not_responding": "ヘルパープロセスが応答していません",
|
||||
|
@ -173,6 +173,8 @@
|
||||
"@_app_configuration": {},
|
||||
"s_toggle_applications": "Przełączanie funkcji",
|
||||
"s_toggle_interfaces": "Przełącz interfejsy",
|
||||
"p_toggle_applications_desc": null,
|
||||
"p_toggle_interfaces_desc": null,
|
||||
"l_toggle_applications_desc": null,
|
||||
"l_toggle_interfaces_desc": null,
|
||||
"s_reconfiguring_yk": "Rekonfigurowanie YubiKey\u2026",
|
||||
@ -195,6 +197,12 @@
|
||||
},
|
||||
"s_fido_disabled": "FIDO2 wyłączone",
|
||||
"l_webauthn_req_fido2": "WebAuthn wymaga włączenia funkcji FIDO2 w kluczu YubiKey",
|
||||
"s_lock_code": null,
|
||||
"l_wrong_lock_code": null,
|
||||
"s_show_lock_code": null,
|
||||
"s_hide_lock_code": null,
|
||||
"p_lock_code_required_desc": null,
|
||||
|
||||
|
||||
"@_connectivity_issues": {},
|
||||
"l_helper_not_responding": "Proces pomocnika nie odpowiada",
|
||||
|
@ -23,6 +23,8 @@ import 'package:material_symbols_icons/symbols.dart';
|
||||
import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../core/models.dart';
|
||||
import '../../widgets/app_input_decoration.dart';
|
||||
import '../../widgets/app_text_field.dart';
|
||||
import '../../widgets/delayed_visibility.dart';
|
||||
import '../../widgets/responsive_dialog.dart';
|
||||
import '../models.dart';
|
||||
@ -176,6 +178,13 @@ class ManagementScreen extends ConsumerStatefulWidget {
|
||||
class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
late Map<Transport, int> _enabled;
|
||||
late int _interfaces;
|
||||
final _lockCodeController = TextEditingController();
|
||||
final _lockCodeFocus = FocusNode();
|
||||
bool _lockCodeIsWrong = false;
|
||||
String _lockCodeError = '';
|
||||
bool _isObscure = true;
|
||||
final lockCodeLength = 32;
|
||||
bool _configuring = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -185,6 +194,60 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
widget.deviceData.info.config.enabledCapabilities[Transport.usb] ?? 0);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_lockCodeController.dispose();
|
||||
_lockCodeFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _buildLockCodeForm(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(l10n.p_lock_code_required_desc),
|
||||
AppTextField(
|
||||
obscureText: _isObscure,
|
||||
maxLength: lockCodeLength,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
controller: _lockCodeController,
|
||||
focusNode: _lockCodeFocus,
|
||||
decoration: AppInputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: l10n.s_lock_code,
|
||||
errorText: _lockCodeIsWrong ? _lockCodeError : null,
|
||||
errorMaxLines: 3,
|
||||
prefixIcon: const Icon(Symbols.pin),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isObscure ? Symbols.visibility : Symbols.visibility_off),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isObscure = !_isObscure;
|
||||
});
|
||||
},
|
||||
tooltip:
|
||||
_isObscure ? l10n.s_show_lock_code : l10n.s_hide_lock_code,
|
||||
),
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_lockCodeIsWrong = false;
|
||||
});
|
||||
},
|
||||
onSubmitted: (_) => _submitForm,
|
||||
).init()
|
||||
]
|
||||
.map((e) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: e,
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCapabilitiesForm(
|
||||
BuildContext context, WidgetRef ref, DeviceInfo info) {
|
||||
return _CapabilitiesForm(
|
||||
@ -200,6 +263,20 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
|
||||
void _submitCapabilitiesForm() async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final isLocked = widget.deviceData.info.isLocked;
|
||||
|
||||
if (isLocked && !Format.hex.isValid(_lockCodeController.text)) {
|
||||
_lockCodeController.selection = TextSelection(
|
||||
baseOffset: 0, extentOffset: _lockCodeController.text.length);
|
||||
_lockCodeFocus.requestFocus();
|
||||
setState(() {
|
||||
_lockCodeError =
|
||||
l10n.l_invalid_format_allowed_chars(Format.hex.allowedCharacters);
|
||||
_lockCodeIsWrong = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final bool reboot;
|
||||
if (widget.deviceData.node is UsbYubiKeyNode) {
|
||||
// Reboot if USB device descriptor is changed.
|
||||
@ -215,6 +292,9 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
|
||||
Function()? close;
|
||||
try {
|
||||
setState(() {
|
||||
_configuring = true;
|
||||
});
|
||||
if (reboot) {
|
||||
// This will take longer, show a message
|
||||
close = showMessage(
|
||||
@ -226,13 +306,24 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
await ref
|
||||
.read(managementStateProvider(widget.deviceData.node.path).notifier)
|
||||
.writeConfig(
|
||||
widget.deviceData.info.config
|
||||
.copyWith(enabledCapabilities: _enabled),
|
||||
reboot: reboot,
|
||||
);
|
||||
widget.deviceData.info.config
|
||||
.copyWith(enabledCapabilities: _enabled),
|
||||
reboot: reboot,
|
||||
currentLockCode: _lockCodeController.text);
|
||||
if (!mounted) return;
|
||||
if (!reboot) Navigator.pop(context);
|
||||
showMessage(context, l10n.s_config_updated);
|
||||
} catch (_) {
|
||||
if (isLocked) {
|
||||
_lockCodeController.selection = TextSelection(
|
||||
baseOffset: 0, extentOffset: _lockCodeController.text.length);
|
||||
_lockCodeFocus.requestFocus();
|
||||
setState(() {
|
||||
_lockCodeIsWrong = true;
|
||||
_configuring = false;
|
||||
_lockCodeError = l10n.l_wrong_lock_code;
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
close?.call();
|
||||
}
|
||||
@ -250,6 +341,9 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
|
||||
void _submitModeForm() async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
setState(() {
|
||||
_configuring = true;
|
||||
});
|
||||
await ref
|
||||
.read(managementStateProvider(widget.deviceData.node.path).notifier)
|
||||
.setMode(interfaces: _interfaces);
|
||||
@ -312,8 +406,22 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
.enabledCapabilities[Transport.usb] ??
|
||||
0);
|
||||
}
|
||||
if (info.isLocked) {
|
||||
final lockCode = _lockCodeController.text.replaceAll(' ', '');
|
||||
canSave = canSave &&
|
||||
lockCode.length == lockCodeLength &&
|
||||
!_lockCodeIsWrong;
|
||||
}
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 18.0, vertical: 8),
|
||||
child: Text(hasConfig
|
||||
? l10n.p_toggle_applications_desc
|
||||
: l10n.p_toggle_interfaces_desc),
|
||||
),
|
||||
hasConfig
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
@ -322,7 +430,27 @@ class _ManagementScreenState extends ConsumerState<ManagementScreen> {
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: _buildModeForm(context, ref, info),
|
||||
)
|
||||
),
|
||||
if (info.isLocked)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0)
|
||||
.copyWith(top: 28),
|
||||
child: _buildLockCodeForm(context),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: info.isLocked ? 4.0 : 24.0,
|
||||
bottom: 4,
|
||||
left: 18.0,
|
||||
right: 18.0),
|
||||
child: Visibility(
|
||||
visible: _configuring,
|
||||
maintainSize: true,
|
||||
maintainAnimation: true,
|
||||
maintainState: true,
|
||||
child: const LinearProgressIndicator(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user