mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 10:11:52 +03:00
Various PIV UI improvements.
This commit is contained in:
parent
a0232eb0ca
commit
0c37afc0e2
@ -282,6 +282,8 @@
|
||||
"p_change_management_key_desc": "Change your management key. You can optionally choose to allow the PIN to be used instead of the management key.",
|
||||
"l_management_key_changed": "Management key changed",
|
||||
"l_default_key_used": "Default management key used",
|
||||
"s_generate_random": "Generate random",
|
||||
"s_use_default": "Use default",
|
||||
"l_warning_default_key": "Warning: Default key used",
|
||||
"s_protect_key": "Protect with PIN",
|
||||
"l_pin_protected_key": "PIN can be used instead",
|
||||
|
@ -42,24 +42,26 @@ class ManageKeyDialog extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
late bool _hasMetadata;
|
||||
late bool _defaultKeyUsed;
|
||||
late bool _usesStoredKey;
|
||||
late bool _storeKey;
|
||||
String _currentKeyOrPin = '';
|
||||
bool _currentIsWrong = false;
|
||||
int _attemptsRemaining = -1;
|
||||
ManagementKeyType _keyType = ManagementKeyType.tdes;
|
||||
final _currentController = TextEditingController();
|
||||
final _keyController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_hasMetadata = widget.pivState.metadata != null;
|
||||
_defaultKeyUsed =
|
||||
widget.pivState.metadata?.managementKeyMetadata.defaultValue ?? false;
|
||||
_usesStoredKey = widget.pivState.protectedKey;
|
||||
if (!_usesStoredKey && _defaultKeyUsed) {
|
||||
_currentKeyOrPin = defaultManagementKey;
|
||||
_currentController.text = defaultManagementKey;
|
||||
}
|
||||
_storeKey = _usesStoredKey;
|
||||
}
|
||||
@ -67,13 +69,14 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
@override
|
||||
void dispose() {
|
||||
_keyController.dispose();
|
||||
_currentController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
final notifier = ref.read(pivStateProvider(widget.path).notifier);
|
||||
if (_usesStoredKey) {
|
||||
final status = (await notifier.verifyPin(_currentKeyOrPin)).when(
|
||||
final status = (await notifier.verifyPin(_currentController.text)).when(
|
||||
success: () => true,
|
||||
failure: (attemptsRemaining) {
|
||||
setState(() {
|
||||
@ -87,7 +90,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!await notifier.authenticate(_currentKeyOrPin)) {
|
||||
if (!await notifier.authenticate(_currentController.text)) {
|
||||
setState(() {
|
||||
_currentIsWrong = true;
|
||||
});
|
||||
@ -126,9 +129,10 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
ManagementKeyType.tdes;
|
||||
final hexLength = _keyType.keyLength * 2;
|
||||
final protected = widget.pivState.protectedKey;
|
||||
final currentKeyOrPin = _currentController.text;
|
||||
final currentLenOk = protected
|
||||
? _currentKeyOrPin.length >= 4
|
||||
: _currentKeyOrPin.length == currentType.keyLength * 2;
|
||||
? currentKeyOrPin.length >= 4
|
||||
: currentKeyOrPin.length == currentType.keyLength * 2;
|
||||
final newLenOk = _keyController.text.length == hexLength;
|
||||
|
||||
return ResponsiveDialog(
|
||||
@ -153,6 +157,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
autofillHints: const [AutofillHints.password],
|
||||
key: keys.pinPukField,
|
||||
maxLength: 8,
|
||||
controller: _currentController,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: l10n.s_pin,
|
||||
@ -166,7 +171,6 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_currentIsWrong = false;
|
||||
_currentKeyOrPin = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -175,16 +179,34 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
key: keys.managementKeyField,
|
||||
autofocus: !_defaultKeyUsed,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
initialValue: _defaultKeyUsed ? defaultManagementKey : null,
|
||||
controller: _currentController,
|
||||
readOnly: _defaultKeyUsed,
|
||||
maxLength: !_defaultKeyUsed ? currentType.keyLength * 2 : null,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: l10n.s_current_management_key,
|
||||
prefixIcon: const Icon(Icons.password_outlined),
|
||||
prefixIcon: const Icon(Icons.key_outlined),
|
||||
errorText: _currentIsWrong ? l10n.l_wrong_key : null,
|
||||
errorMaxLines: 3,
|
||||
helperText: _defaultKeyUsed ? l10n.l_default_key_used : null,
|
||||
suffixIcon: _hasMetadata
|
||||
? null
|
||||
: IconButton(
|
||||
icon: Icon(_defaultKeyUsed
|
||||
? Icons.auto_awesome
|
||||
: Icons.auto_awesome_outlined),
|
||||
tooltip: l10n.s_use_default,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_defaultKeyUsed = !_defaultKeyUsed;
|
||||
if (_defaultKeyUsed) {
|
||||
_currentController.text = defaultManagementKey;
|
||||
} else {
|
||||
_currentController.clear();
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(
|
||||
@ -194,7 +216,6 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_currentIsWrong = false;
|
||||
_currentKeyOrPin = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -211,10 +232,11 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: l10n.s_new_management_key,
|
||||
prefixIcon: const Icon(Icons.password_outlined),
|
||||
prefixIcon: const Icon(Icons.key_outlined),
|
||||
enabled: currentLenOk,
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: l10n.s_generate_random,
|
||||
onPressed: currentLenOk
|
||||
? () {
|
||||
final random = Random.secure();
|
||||
|
@ -114,7 +114,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
: l10n.s_current_puk,
|
||||
prefixIcon: const Icon(Icons.password_outlined),
|
||||
errorText: _currentIsWrong
|
||||
? (widget.target == ManageTarget.puk
|
||||
? (widget.target == ManageTarget.pin
|
||||
? l10n.l_wrong_pin_attempts_remaining(
|
||||
_attemptsRemaining)
|
||||
: l10n.l_wrong_puk_attempts_remaining(
|
||||
|
@ -105,7 +105,8 @@ class _CertificateListItem extends StatelessWidget {
|
||||
),
|
||||
title: slot.getDisplayName(l10n),
|
||||
subtitle: certInfo != null
|
||||
? certInfo.subject
|
||||
// Simplify subtitle by stripping "CN=", etc.
|
||||
? certInfo.subject.replaceAll(RegExp(r'[A-Z]+='), ' ').trimLeft()
|
||||
: pivSlot.hasKey == true
|
||||
? l10n.l_key_no_certificate
|
||||
: l10n.l_no_certificate,
|
||||
|
@ -81,6 +81,8 @@ class SlotDialog extends ConsumerWidget {
|
||||
child: TooltipIfTruncated(
|
||||
text: value,
|
||||
style: subtitleStyle,
|
||||
tooltip: value.replaceAllMapped(
|
||||
RegExp(r',([A-Z]+)='), (match) => '\n${match[1]}='),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -19,8 +19,9 @@ import 'package:flutter/material.dart';
|
||||
class TooltipIfTruncated extends StatelessWidget {
|
||||
final String text;
|
||||
final TextStyle style;
|
||||
final String? tooltip;
|
||||
const TooltipIfTruncated(
|
||||
{super.key, required this.text, required this.style});
|
||||
{super.key, required this.text, required this.style, this.tooltip});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -41,7 +42,7 @@ class TooltipIfTruncated extends StatelessWidget {
|
||||
return textPainter.didExceedMaxLines
|
||||
? Tooltip(
|
||||
margin: const EdgeInsets.all(16),
|
||||
message: text,
|
||||
message: tooltip ?? text,
|
||||
child: textWidget,
|
||||
)
|
||||
: textWidget;
|
||||
|
Loading…
Reference in New Issue
Block a user