mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-12-23 18:22:39 +03:00
Merge PR #1385.
This commit is contained in:
commit
2b3771c67a
@ -110,7 +110,7 @@ class _FidoLockedPage extends ConsumerWidget {
|
||||
header: l10n.s_fingerprints_get_started,
|
||||
message: l10n.p_set_fingerprints_desc,
|
||||
keyActionsBuilder: hasActions ? _buildActions : null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(state),
|
||||
keyActionsBadge: fingerprintsShowActionsNotifier(state),
|
||||
);
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ class _FidoLockedPage extends ConsumerWidget {
|
||||
header: l10n.s_pin_change_required,
|
||||
message: l10n.l_pin_change_required_desc,
|
||||
keyActionsBuilder: hasActions ? _buildActions : null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(state),
|
||||
keyActionsBadge: fingerprintsShowActionsNotifier(state),
|
||||
actionsBuilder: (context, expanded) => [
|
||||
if (!expanded)
|
||||
ActionChip(
|
||||
@ -201,7 +201,7 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
|
||||
? (context) =>
|
||||
fingerprintsBuildActions(context, widget.node, widget.state, 0)
|
||||
: null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(widget.state),
|
||||
keyActionsBadge: fingerprintsShowActionsNotifier(widget.state),
|
||||
);
|
||||
}
|
||||
|
||||
@ -296,7 +296,7 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
|
||||
? (context) => fingerprintsBuildActions(
|
||||
context, widget.node, widget.state, fingerprints.length)
|
||||
: null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(widget.state),
|
||||
keyActionsBadge: fingerprintsShowActionsNotifier(widget.state),
|
||||
builder: (context, expanded) {
|
||||
// De-select if window is resized to be non-expanded.
|
||||
if (!expanded && _selected != null) {
|
||||
|
@ -26,10 +26,12 @@ import '../models.dart';
|
||||
import 'add_fingerprint_dialog.dart';
|
||||
import 'pin_dialog.dart';
|
||||
|
||||
bool fidoShowActionsNotifier(FidoState state) {
|
||||
return (state.alwaysUv && !state.hasPin) ||
|
||||
state.bioEnroll == false ||
|
||||
state.forcePinChange;
|
||||
bool passkeysShowActionsNotifier(FidoState state) {
|
||||
return (state.alwaysUv && !state.hasPin) || state.forcePinChange;
|
||||
}
|
||||
|
||||
bool fingerprintsShowActionsNotifier(FidoState state) {
|
||||
return !state.hasPin || state.bioEnroll == false || state.forcePinChange;
|
||||
}
|
||||
|
||||
Widget passkeysBuildActions(
|
||||
@ -63,8 +65,9 @@ Widget _fidoBuildActions(BuildContext context, DeviceNode node, FidoState state,
|
||||
: state.hasPin
|
||||
? l10n.l_unlock_pin_first
|
||||
: l10n.l_set_pin_first,
|
||||
trailing: fingerprints == 0
|
||||
? Icon(Icons.warning_amber, color: colors.tertiary)
|
||||
trailing: fingerprints == 0 || fingerprints == -1
|
||||
? Icon(Icons.warning_amber,
|
||||
color: state.unlocked ? colors.tertiary : null)
|
||||
: null,
|
||||
onTap: state.unlocked && fingerprints < 5
|
||||
? (context) {
|
||||
@ -90,7 +93,7 @@ Widget _fidoBuildActions(BuildContext context, DeviceNode node, FidoState state,
|
||||
? (state.forcePinChange
|
||||
? l10n.s_pin_change_required
|
||||
: l10n.s_fido_pin_protection)
|
||||
: l10n.l_fido_pin_protection_optional,
|
||||
: l10n.s_fido_pin_protection,
|
||||
trailing: state.alwaysUv && !state.hasPin || state.forcePinChange
|
||||
? Icon(Icons.warning_amber, color: colors.tertiary)
|
||||
: null,
|
||||
|
@ -132,7 +132,7 @@ class _FidoLockedPage extends ConsumerWidget {
|
||||
: l10n.l_register_sk_on_websites,
|
||||
footnote: isBio ? null : l10n.l_non_passkeys_note,
|
||||
keyActionsBuilder: hasActions ? _buildActions : null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(state),
|
||||
keyActionsBadge: passkeysShowActionsNotifier(state),
|
||||
);
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ class _FidoLockedPage extends ConsumerWidget {
|
||||
message: l10n.l_register_sk_on_websites,
|
||||
footnote: l10n.l_non_passkeys_note,
|
||||
keyActionsBuilder: hasActions ? _buildActions : null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(state),
|
||||
keyActionsBadge: passkeysShowActionsNotifier(state),
|
||||
);
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ class _FidoLockedPage extends ConsumerWidget {
|
||||
header: l10n.s_pin_change_required,
|
||||
message: l10n.l_pin_change_required_desc,
|
||||
keyActionsBuilder: hasActions ? _buildActions : null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(state),
|
||||
keyActionsBadge: passkeysShowActionsNotifier(state),
|
||||
);
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
|
||||
? (context) =>
|
||||
passkeysBuildActions(context, widget.node, widget.state)
|
||||
: null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(widget.state),
|
||||
keyActionsBadge: passkeysShowActionsNotifier(widget.state),
|
||||
);
|
||||
}
|
||||
|
||||
@ -257,7 +257,7 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
|
||||
? (context) =>
|
||||
passkeysBuildActions(context, widget.node, widget.state)
|
||||
: null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(widget.state),
|
||||
keyActionsBadge: passkeysShowActionsNotifier(widget.state),
|
||||
footnote: l10n.l_non_passkeys_note,
|
||||
);
|
||||
}
|
||||
@ -358,7 +358,7 @@ class _FidoUnlockedPageState extends ConsumerState<_FidoUnlockedPage> {
|
||||
? (context) =>
|
||||
passkeysBuildActions(context, widget.node, widget.state)
|
||||
: null,
|
||||
keyActionsBadge: fidoShowActionsNotifier(widget.state),
|
||||
keyActionsBadge: passkeysShowActionsNotifier(widget.state),
|
||||
builder: (context, expanded) {
|
||||
// De-select if window is resized to be non-expanded.
|
||||
if (!expanded && _selected != null) {
|
||||
|
@ -274,6 +274,10 @@
|
||||
"name": {}
|
||||
}
|
||||
},
|
||||
"l_warning_default_pin": null,
|
||||
"l_warning_default_puk": null,
|
||||
"l_default_pin_used": null,
|
||||
"l_default_puk_used": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Passwort",
|
||||
|
@ -274,6 +274,10 @@
|
||||
"name": {}
|
||||
}
|
||||
},
|
||||
"l_warning_default_pin": "Warning: Default PIN used",
|
||||
"l_warning_default_puk": "Warning: Default PUK used",
|
||||
"l_default_pin_used": "Default PIN used",
|
||||
"l_default_puk_used": "Default PUK used",
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Password",
|
||||
|
@ -274,6 +274,10 @@
|
||||
"name": {}
|
||||
}
|
||||
},
|
||||
"l_warning_default_pin": null,
|
||||
"l_warning_default_puk": null,
|
||||
"l_default_pin_used": null,
|
||||
"l_default_puk_used": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Mot de passe",
|
||||
|
@ -274,6 +274,10 @@
|
||||
"name": {}
|
||||
}
|
||||
},
|
||||
"l_warning_default_pin": null,
|
||||
"l_warning_default_puk": null,
|
||||
"l_default_pin_used": null,
|
||||
"l_default_puk_used": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "パスワード",
|
||||
|
@ -274,6 +274,10 @@
|
||||
"name": {}
|
||||
}
|
||||
},
|
||||
"l_warning_default_pin": null,
|
||||
"l_warning_default_puk": null,
|
||||
"l_default_pin_used": null,
|
||||
"l_default_puk_used": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Hasło",
|
||||
|
@ -26,6 +26,8 @@ const defaultManagementKey = '010203040506070801020304050607080102030405060708';
|
||||
const defaultManagementKeyType = ManagementKeyType.tdes;
|
||||
const defaultKeyType = KeyType.eccp256;
|
||||
const defaultGenerateType = GenerateType.certificate;
|
||||
const defaultPin = '123456';
|
||||
const defaultPuk = '12345678';
|
||||
|
||||
enum GenerateType {
|
||||
publicKey,
|
||||
|
@ -27,12 +27,23 @@ import '../models.dart';
|
||||
import 'manage_key_dialog.dart';
|
||||
import 'manage_pin_puk_dialog.dart';
|
||||
|
||||
bool pivShowActionsNotifier(PivState state) {
|
||||
final usingDefaultPin = state.metadata?.pinMetadata.defaultValue == true;
|
||||
final usingDefaultPuk = state.metadata?.pukMetadata.defaultValue == true;
|
||||
final usingDefaultMgmtKey =
|
||||
state.metadata?.managementKeyMetadata.defaultValue == true;
|
||||
|
||||
return usingDefaultPin || usingDefaultPuk || usingDefaultMgmtKey;
|
||||
}
|
||||
|
||||
Widget pivBuildActions(BuildContext context, DevicePath devicePath,
|
||||
PivState pivState, WidgetRef ref) {
|
||||
final colors = Theme.of(context).buttonTheme.colorScheme ??
|
||||
Theme.of(context).colorScheme;
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
final usingDefaultPin = pivState.metadata?.pinMetadata.defaultValue == true;
|
||||
final usingDefaultPuk = pivState.metadata?.pukMetadata.defaultValue == true;
|
||||
final usingDefaultMgmtKey =
|
||||
pivState.metadata?.managementKeyMetadata.defaultValue == true;
|
||||
|
||||
@ -53,9 +64,11 @@ Widget pivBuildActions(BuildContext context, DevicePath devicePath,
|
||||
? (pukAttempts != 0
|
||||
? l10n.l_piv_pin_blocked
|
||||
: l10n.l_piv_pin_puk_blocked)
|
||||
: usingDefaultPin
|
||||
? '${l10n.l_attempts_remaining(pivState.pinAttempts)}\n${l10n.l_warning_default_pin}'
|
||||
: l10n.l_attempts_remaining(pivState.pinAttempts),
|
||||
icon: const Icon(Icons.pin_outlined),
|
||||
trailing: pinBlocked ? alertIcon : null,
|
||||
trailing: pinBlocked || usingDefaultPin ? alertIcon : null,
|
||||
onTap: !(pinBlocked && pukAttempts == 0)
|
||||
? (context) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
@ -63,6 +76,7 @@ Widget pivBuildActions(BuildContext context, DevicePath devicePath,
|
||||
context: context,
|
||||
builder: (context) => ManagePinPukDialog(
|
||||
devicePath,
|
||||
pivState,
|
||||
target: pinBlocked
|
||||
? ManageTarget.unblock
|
||||
: ManageTarget.pin,
|
||||
@ -77,16 +91,21 @@ Widget pivBuildActions(BuildContext context, DevicePath devicePath,
|
||||
subtitle: pukAttempts != null
|
||||
? (pukAttempts == 0
|
||||
? l10n.l_piv_pin_puk_blocked
|
||||
: usingDefaultPuk
|
||||
? '${l10n.l_attempts_remaining(pukAttempts)}\n${l10n.l_warning_default_puk}'
|
||||
: l10n.l_attempts_remaining(pukAttempts))
|
||||
: usingDefaultPuk
|
||||
? l10n.l_warning_default_puk
|
||||
: null,
|
||||
icon: const Icon(Icons.pin_outlined),
|
||||
trailing: pukAttempts == 0 ? alertIcon : null,
|
||||
trailing: pukAttempts == 0 || usingDefaultPuk ? alertIcon : null,
|
||||
onTap: pukAttempts != 0
|
||||
? (context) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManagePinPukDialog(devicePath,
|
||||
builder: (context) => ManagePinPukDialog(
|
||||
devicePath, pivState,
|
||||
target: ManageTarget.puk),
|
||||
);
|
||||
}
|
||||
|
@ -24,14 +24,16 @@ import '../../widgets/app_input_decoration.dart';
|
||||
import '../../widgets/app_text_field.dart';
|
||||
import '../../widgets/responsive_dialog.dart';
|
||||
import '../keys.dart' as keys;
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
|
||||
enum ManageTarget { pin, puk, unblock }
|
||||
|
||||
class ManagePinPukDialog extends ConsumerStatefulWidget {
|
||||
final DevicePath path;
|
||||
final PivState pivState;
|
||||
final ManageTarget target;
|
||||
const ManagePinPukDialog(this.path,
|
||||
const ManagePinPukDialog(this.path, this.pivState,
|
||||
{super.key, this.target = ManageTarget.pin});
|
||||
|
||||
@override
|
||||
@ -40,7 +42,7 @@ class ManagePinPukDialog extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
String _currentPin = '';
|
||||
final _currentPinController = TextEditingController();
|
||||
String _newPin = '';
|
||||
String _confirmPin = '';
|
||||
bool _currentIsWrong = false;
|
||||
@ -48,13 +50,40 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
bool _isObscureCurrent = true;
|
||||
bool _isObscureNew = true;
|
||||
bool _isObscureConfirm = true;
|
||||
late bool _defaultPinUsed;
|
||||
late bool _defaultPukUsed;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_defaultPinUsed =
|
||||
widget.pivState.metadata?.pinMetadata.defaultValue ?? false;
|
||||
_defaultPukUsed =
|
||||
widget.pivState.metadata?.pukMetadata.defaultValue ?? false;
|
||||
if (widget.target == ManageTarget.pin && _defaultPinUsed) {
|
||||
_currentPinController.text = defaultPin;
|
||||
}
|
||||
if (widget.target != ManageTarget.pin && _defaultPukUsed) {
|
||||
_currentPinController.text = defaultPuk;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_currentPinController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_submit() async {
|
||||
final notifier = ref.read(pivStateProvider(widget.path).notifier);
|
||||
final result = await switch (widget.target) {
|
||||
ManageTarget.pin => notifier.changePin(_currentPin, _newPin),
|
||||
ManageTarget.puk => notifier.changePuk(_currentPin, _newPin),
|
||||
ManageTarget.unblock => notifier.unblockPin(_currentPin, _newPin),
|
||||
ManageTarget.pin =>
|
||||
notifier.changePin(_currentPinController.text, _newPin),
|
||||
ManageTarget.puk =>
|
||||
notifier.changePuk(_currentPinController.text, _newPin),
|
||||
ManageTarget.unblock =>
|
||||
notifier.unblockPin(_currentPinController.text, _newPin),
|
||||
};
|
||||
|
||||
result.when(success: () {
|
||||
@ -71,16 +100,17 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
setState(() {
|
||||
_attemptsRemaining = attemptsRemaining;
|
||||
_currentIsWrong = true;
|
||||
_currentPin = '';
|
||||
});
|
||||
_currentPinController.clear();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final currentPin = _currentPinController.text;
|
||||
final isValid =
|
||||
_newPin.isNotEmpty && _newPin == _confirmPin && _currentPin.isNotEmpty;
|
||||
_newPin.isNotEmpty && _newPin == _confirmPin && currentPin.isNotEmpty;
|
||||
|
||||
final titleText = switch (widget.target) {
|
||||
ManageTarget.pin => l10n.s_change_pin,
|
||||
@ -88,6 +118,11 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
ManageTarget.unblock => l10n.s_unblock_pin,
|
||||
};
|
||||
|
||||
final showDefaultPinUsed =
|
||||
widget.target == ManageTarget.pin && _defaultPinUsed;
|
||||
final showDefaultPukUsed =
|
||||
widget.target != ManageTarget.pin && _defaultPukUsed;
|
||||
|
||||
return ResponsiveDialog(
|
||||
title: Text(titleText),
|
||||
actions: [
|
||||
@ -107,13 +142,20 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
? l10n.p_enter_current_pin_or_reset
|
||||
: l10n.p_enter_current_puk_or_reset),
|
||||
AppTextField(
|
||||
autofocus: true,
|
||||
autofocus: !(showDefaultPinUsed || showDefaultPukUsed),
|
||||
obscureText: _isObscureCurrent,
|
||||
maxLength: 8,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
key: keys.pinPukField,
|
||||
readOnly: showDefaultPinUsed || showDefaultPukUsed,
|
||||
controller: _currentPinController,
|
||||
decoration: AppInputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
helperText: showDefaultPinUsed
|
||||
? l10n.l_default_pin_used
|
||||
: showDefaultPukUsed
|
||||
? l10n.l_default_puk_used
|
||||
: null,
|
||||
labelText: widget.target == ManageTarget.pin
|
||||
? l10n.s_current_pin
|
||||
: l10n.s_current_puk,
|
||||
@ -144,7 +186,6 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_currentIsWrong = false;
|
||||
_currentPin = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -152,6 +193,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
widget.target == ManageTarget.puk ? l10n.s_puk : l10n.s_pin)),
|
||||
AppTextField(
|
||||
key: keys.newPinPukField,
|
||||
autofocus: showDefaultPinUsed || showDefaultPukUsed,
|
||||
obscureText: _isObscureNew,
|
||||
maxLength: 8,
|
||||
autofillHints: const [AutofillHints.newPassword],
|
||||
@ -174,7 +216,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
: (_isObscureNew ? l10n.s_show_puk : l10n.s_hide_puk),
|
||||
),
|
||||
// Old YubiKeys allowed a 4 digit PIN
|
||||
enabled: _currentPin.length >= 4,
|
||||
enabled: currentPin.length >= 4,
|
||||
),
|
||||
textInputAction: TextInputAction.next,
|
||||
onChanged: (value) {
|
||||
@ -212,7 +254,7 @@ class _ManagePinPukDialogState extends ConsumerState<ManagePinPukDialog> {
|
||||
? (_isObscureConfirm ? l10n.s_show_pin : l10n.s_hide_pin)
|
||||
: (_isObscureConfirm ? l10n.s_show_puk : l10n.s_hide_puk),
|
||||
),
|
||||
enabled: _currentPin.length >= 4 && _newPin.length >= 6,
|
||||
enabled: currentPin.length >= 4 && _newPin.length >= 6,
|
||||
),
|
||||
textInputAction: TextInputAction.done,
|
||||
onChanged: (value) {
|
||||
|
@ -158,6 +158,7 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
? (context) => pivBuildActions(
|
||||
context, widget.devicePath, pivState, ref)
|
||||
: null,
|
||||
keyActionsBadge: pivShowActionsNotifier(pivState),
|
||||
builder: (context, expanded) {
|
||||
// De-select if window is resized to be non-expanded.
|
||||
if (!expanded && _selected != null) {
|
||||
|
Loading…
Reference in New Issue
Block a user