mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 00:12:09 +03:00
Merge PR #1626
This commit is contained in:
commit
bb1027f531
@ -30,6 +30,7 @@ import '../../widgets/delayed_visibility.dart';
|
||||
import '../../widgets/file_drop_target.dart';
|
||||
import '../message.dart';
|
||||
import '../shortcuts.dart';
|
||||
import '../state.dart';
|
||||
import 'fs_dialog.dart';
|
||||
import 'keys.dart';
|
||||
import 'navigation.dart';
|
||||
@ -764,23 +765,49 @@ class _GestureDetectorAppBar extends StatelessWidget
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
|
||||
class CapabilityBadge extends StatelessWidget {
|
||||
class CapabilityBadge extends ConsumerWidget {
|
||||
final Capability capability;
|
||||
final bool noTooltip;
|
||||
|
||||
const CapabilityBadge(this.capability, {super.key});
|
||||
const CapabilityBadge(this.capability, {super.key, this.noTooltip = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final text = Text(capability.getDisplayName(l10n));
|
||||
final (fipsCapable, fipsApproved) = ref
|
||||
.watch(currentDeviceDataProvider)
|
||||
.valueOrNull
|
||||
?.info
|
||||
.getFipsStatus(capability) ??
|
||||
(false, false);
|
||||
final label = fipsCapable
|
||||
? Row(
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.shield,
|
||||
color: colorScheme.onSecondaryContainer,
|
||||
size: 12,
|
||||
fill: fipsApproved ? 1 : 0,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
text,
|
||||
],
|
||||
)
|
||||
: text;
|
||||
return Badge(
|
||||
backgroundColor: colorScheme.secondaryContainer,
|
||||
textColor: colorScheme.onSecondaryContainer,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
largeSize: MediaQuery.of(context).textScaler.scale(20),
|
||||
label: Text(
|
||||
capability.getDisplayName(l10n),
|
||||
),
|
||||
label: fipsCapable && !noTooltip
|
||||
? Tooltip(
|
||||
message:
|
||||
fipsApproved ? l10n.l_fips_approved : l10n.l_fips_capable,
|
||||
child: label,
|
||||
)
|
||||
: label,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -92,9 +92,14 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
|
||||
runSpacing: 8,
|
||||
children: Capability.values
|
||||
.where((c) => enabledCapabilities & c.value != 0)
|
||||
.map((c) => CapabilityBadge(c))
|
||||
.map((c) => CapabilityBadge(c, noTooltip: true))
|
||||
.toList(),
|
||||
),
|
||||
if (widget.deviceData.info.fipsCapable != 0)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: _FipsLegend(),
|
||||
),
|
||||
if (serial != null) ...[
|
||||
const SizedBox(height: 32.0),
|
||||
_DeviceColor(
|
||||
@ -131,6 +136,62 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
class _FipsLegend extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return Opacity(
|
||||
opacity: 0.6,
|
||||
child: Wrap(
|
||||
runSpacing: 0,
|
||||
spacing: 16,
|
||||
children: [
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(right: 4),
|
||||
child: Icon(
|
||||
Symbols.shield,
|
||||
size: 12,
|
||||
fill: 0.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: l10n.l_fips_capable,
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
children: [
|
||||
const WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(right: 4),
|
||||
child: Icon(
|
||||
Symbols.shield,
|
||||
size: 12,
|
||||
fill: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: l10n.l_fips_approved,
|
||||
style: Theme.of(context).textTheme.bodySmall),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeviceContent extends ConsumerWidget {
|
||||
final YubiKeyData deviceData;
|
||||
final KeyCustomization? initialCustomization;
|
||||
@ -195,6 +256,29 @@ class _DeviceContent extends ConsumerWidget {
|
||||
.titleSmall
|
||||
?.apply(color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
if (deviceData.info.pinComplexity)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: RichText(
|
||||
text: TextSpan(children: [
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 4),
|
||||
child: Icon(
|
||||
Symbols.check,
|
||||
size: 16,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
)),
|
||||
TextSpan(
|
||||
text: l10n.l_pin_complexity,
|
||||
style: Theme.of(context).textTheme.titleSmall?.apply(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ Widget homeBuildActions(
|
||||
BuildContext context, YubiKeyData? deviceData, WidgetRef ref) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final hasFeature = ref.watch(featureProvider);
|
||||
final interfacesLocked = deviceData?.info.resetBlocked != 0;
|
||||
final managementAvailability = hasFeature(features.management) &&
|
||||
switch (deviceData?.info.version) {
|
||||
Version version => (version.major > 4 || // YK5 and up
|
||||
@ -56,16 +57,21 @@ Widget homeBuildActions(
|
||||
title: deviceData.info.version.major > 4
|
||||
? l10n.s_toggle_applications
|
||||
: l10n.s_toggle_interfaces,
|
||||
subtitle: deviceData.info.version.major > 4
|
||||
? l10n.l_toggle_applications_desc
|
||||
: l10n.l_toggle_interfaces_desc,
|
||||
onTap: (context) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManagementScreen(deviceData),
|
||||
);
|
||||
},
|
||||
subtitle: interfacesLocked
|
||||
? 'Requires factory reset' // TODO: Replace with l10n
|
||||
: (deviceData.info.version.major > 4
|
||||
? l10n.l_toggle_applications_desc
|
||||
: l10n.l_toggle_interfaces_desc),
|
||||
onTap: interfacesLocked
|
||||
? null
|
||||
: (context) {
|
||||
Navigator.of(context)
|
||||
.popUntil((route) => route.isFirst);
|
||||
showBlurDialog(
|
||||
context: context,
|
||||
builder: (context) => ManagementScreen(deviceData),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (getResetCapabilities(hasFeature).any((c) =>
|
||||
c.value &
|
||||
|
@ -160,6 +160,8 @@
|
||||
}
|
||||
},
|
||||
"l_firmware_version": null,
|
||||
"l_fips_capable": null,
|
||||
"l_fips_approved": null,
|
||||
|
||||
"@_yubikey_interactions": {},
|
||||
"l_insert_yk": "YubiKey anschließen",
|
||||
@ -345,6 +347,7 @@
|
||||
"l_warning_default_puk": null,
|
||||
"l_default_pin_used": null,
|
||||
"l_default_puk_used": null,
|
||||
"l_pin_complexity": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Passwort",
|
||||
@ -354,6 +357,7 @@
|
||||
"s_show_password": null,
|
||||
"s_hide_password": null,
|
||||
"l_optional_password_protection": "Optionaler Passwortschutz",
|
||||
"l_password_protection": null,
|
||||
"s_new_password": "Neues Passwort",
|
||||
"s_current_password": "Aktuelles Passwort",
|
||||
"s_confirm_password": "Passwort bestätigen",
|
||||
@ -367,6 +371,7 @@
|
||||
"l_keystore_unavailable": "Passwortspeicher des Betriebssystems nicht verfügbar",
|
||||
"l_remember_pw_failed": "Konnte Passwort nicht speichern",
|
||||
"l_unlock_first": "Zuerst mit Passwort entsperren",
|
||||
"l_set_password_first": null,
|
||||
"l_enter_oath_pw": "Das OATH-Passwort für Ihren YubiKey eingeben",
|
||||
"p_enter_current_password_or_reset": "Geben Sie Ihr aktuelles Passwort ein. Wenn Sie Ihr Passwort nicht wissen, müssen Sie den YubiKey zurücksetzen.",
|
||||
"p_enter_new_password": "Geben Sie Ihr neues Passwort ein. Ein Passwort kann Buchstaben, Ziffern und spezielle Zeichen enthalten.",
|
||||
|
@ -160,6 +160,8 @@
|
||||
}
|
||||
},
|
||||
"l_firmware_version": "Firmware version: {version}",
|
||||
"l_fips_capable": "FIPS capable",
|
||||
"l_fips_approved": "FIPS approved",
|
||||
|
||||
"@_yubikey_interactions": {},
|
||||
"l_insert_yk": "Insert your YubiKey",
|
||||
@ -345,6 +347,7 @@
|
||||
"l_warning_default_puk": "Warning: Default PUK used",
|
||||
"l_default_pin_used": "Default PIN used",
|
||||
"l_default_puk_used": "Default PUK used",
|
||||
"l_pin_complexity": "PIN complexity enforced",
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Password",
|
||||
@ -354,6 +357,7 @@
|
||||
"s_show_password": "Show password",
|
||||
"s_hide_password": "Hide password",
|
||||
"l_optional_password_protection": "Optional password protection",
|
||||
"l_password_protection": "Password protection of accounts",
|
||||
"s_new_password": "New password",
|
||||
"s_current_password": "Current password",
|
||||
"s_confirm_password": "Confirm password",
|
||||
@ -367,6 +371,7 @@
|
||||
"l_keystore_unavailable": "OS Keystore unavailable",
|
||||
"l_remember_pw_failed": "Failed to remember password",
|
||||
"l_unlock_first": "Unlock with password first",
|
||||
"l_set_password_first": "Set a password first",
|
||||
"l_enter_oath_pw": "Enter the OATH password for your YubiKey",
|
||||
"p_enter_current_password_or_reset": "Enter your current password. If you don't know your password, you'll need to reset the YubiKey.",
|
||||
"p_enter_new_password": "Enter your new password. A password may contain letters, numbers and special characters.",
|
||||
|
@ -160,6 +160,8 @@
|
||||
}
|
||||
},
|
||||
"l_firmware_version": "Version du firmware : {version}",
|
||||
"l_fips_capable": null,
|
||||
"l_fips_approved": null,
|
||||
|
||||
"@_yubikey_interactions": {},
|
||||
"l_insert_yk": "Insérez votre YubiKey",
|
||||
@ -345,6 +347,7 @@
|
||||
"l_warning_default_puk": "Attention : PUK par défaut utilisé",
|
||||
"l_default_pin_used": "Code PIN par défaut utilisé",
|
||||
"l_default_puk_used": "PUK par défaut utilisé",
|
||||
"l_pin_complexity": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Mot de passe",
|
||||
@ -354,6 +357,7 @@
|
||||
"s_show_password": "Montrer mot de passe",
|
||||
"s_hide_password": "Masquer mot de passe",
|
||||
"l_optional_password_protection": "Protection par mot de passe facultative",
|
||||
"l_password_protection": null,
|
||||
"s_new_password": "Nouveau mot de passe",
|
||||
"s_current_password": "Mot de passe actuel",
|
||||
"s_confirm_password": "Confirmer mot de passe",
|
||||
@ -367,6 +371,7 @@
|
||||
"l_keystore_unavailable": "OS Keystore indisponible",
|
||||
"l_remember_pw_failed": "Mémorisation mot de passe impossible",
|
||||
"l_unlock_first": "Débloquez d'abord avec mot de passe",
|
||||
"l_set_password_first": null,
|
||||
"l_enter_oath_pw": "Saisissez le mot de passe OATH de votre YubiKey",
|
||||
"p_enter_current_password_or_reset": "Saisissez votre mot de passe actuel. Vous ne connaissez votre mot de passe\u00a0? Réinitialisez la YubiKey.",
|
||||
"p_enter_new_password": "Saisissez votre nouveau mot de passe. Un mot de passe peut inclure des lettres, chiffres et caractères spéciaux.",
|
||||
|
@ -160,6 +160,8 @@
|
||||
}
|
||||
},
|
||||
"l_firmware_version": "ファームウェアバージョン: {version}",
|
||||
"l_fips_capable": null,
|
||||
"l_fips_approved": null,
|
||||
|
||||
"@_yubikey_interactions": {},
|
||||
"l_insert_yk": "YubiKeyを挿入してください",
|
||||
@ -345,6 +347,7 @@
|
||||
"l_warning_default_puk": "警告: デフォルトのPUKが使用されています",
|
||||
"l_default_pin_used": "デフォルトのPINが使用されています",
|
||||
"l_default_puk_used": "既定のPUKを使用",
|
||||
"l_pin_complexity": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "パスワード",
|
||||
@ -354,6 +357,7 @@
|
||||
"s_show_password": "パスワードを表示",
|
||||
"s_hide_password": "パスワードを非表示",
|
||||
"l_optional_password_protection": "オプションのパスワード保護",
|
||||
"l_password_protection": null,
|
||||
"s_new_password": "新しいパスワード",
|
||||
"s_current_password": "現在のパスワード",
|
||||
"s_confirm_password": "パスワードを確認",
|
||||
@ -367,6 +371,7 @@
|
||||
"l_keystore_unavailable": "OSのキーストアを利用できません",
|
||||
"l_remember_pw_failed": "パスワードを記憶できませんでした",
|
||||
"l_unlock_first": "最初にパスワードでロックを解除",
|
||||
"l_set_password_first": null,
|
||||
"l_enter_oath_pw": "YubiKeyのOATHパスワードを入力",
|
||||
"p_enter_current_password_or_reset": "現在のパスワードを入力してください。パスワードがわからない場合は、YubiKeyをリセットする必要があります。",
|
||||
"p_enter_new_password": "新しいパスワードを入力してください。パスワードには文字、数字、特殊文字を含めることができます。",
|
||||
|
@ -160,6 +160,8 @@
|
||||
}
|
||||
},
|
||||
"l_firmware_version": null,
|
||||
"l_fips_capable": null,
|
||||
"l_fips_approved": null,
|
||||
|
||||
"@_yubikey_interactions": {},
|
||||
"l_insert_yk": "Podłącz klucz YubiKey",
|
||||
@ -345,6 +347,7 @@
|
||||
"l_warning_default_puk": null,
|
||||
"l_default_pin_used": null,
|
||||
"l_default_puk_used": null,
|
||||
"l_pin_complexity": null,
|
||||
|
||||
"@_passwords": {},
|
||||
"s_password": "Hasło",
|
||||
@ -354,6 +357,7 @@
|
||||
"s_show_password": "Pokaż hasło",
|
||||
"s_hide_password": "Ukryj hasło",
|
||||
"l_optional_password_protection": "Opcjonalna ochrona hasłem",
|
||||
"l_password_protection": null,
|
||||
"s_new_password": "Nowe hasło",
|
||||
"s_current_password": "Aktualne hasło",
|
||||
"s_confirm_password": "Potwierdź hasło",
|
||||
@ -367,6 +371,7 @@
|
||||
"l_keystore_unavailable": "Magazyn kluczy systemu operacyjnego jest niedostępny",
|
||||
"l_remember_pw_failed": "Nie udało się zapamiętać hasła",
|
||||
"l_unlock_first": "Najpierw odblokuj hasłem",
|
||||
"l_set_password_first": null,
|
||||
"l_enter_oath_pw": "Wprowadź hasło OATH dla klucza YubiKey",
|
||||
"p_enter_current_password_or_reset": "Wprowadź aktualne hasło. Jeśli go nie znasz, musisz zresetować klucz YubiKey.",
|
||||
"p_enter_new_password": "Wprowadź nowe hasło. Może ono zawierać litery, cyfry i znaki specjalne.",
|
||||
|
@ -23,6 +23,7 @@ import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../../management/models.dart';
|
||||
import '../features.dart' as features;
|
||||
import '../icon_provider/icon_pack_dialog.dart';
|
||||
import '../keys.dart' as keys;
|
||||
@ -39,6 +40,28 @@ Widget oathBuildActions(
|
||||
}) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final capacity = oathState.capacity;
|
||||
final (fipsCapable, fipsApproved) = ref
|
||||
.watch(currentDeviceDataProvider)
|
||||
.valueOrNull
|
||||
?.info
|
||||
.getFipsStatus(Capability.oath) ??
|
||||
(false, false);
|
||||
|
||||
final String? subtitle;
|
||||
final bool enabled;
|
||||
if (used == null) {
|
||||
subtitle = l10n.l_unlock_first;
|
||||
enabled = false;
|
||||
} else if (fipsCapable & !fipsApproved) {
|
||||
subtitle = l10n.l_set_password_first;
|
||||
enabled = false;
|
||||
} else if (capacity != null) {
|
||||
subtitle = l10n.l_accounts_used(used, capacity);
|
||||
enabled = capacity > used;
|
||||
} else {
|
||||
subtitle = null;
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
@ -47,14 +70,10 @@ Widget oathBuildActions(
|
||||
feature: features.actionsAdd,
|
||||
key: keys.addAccountAction,
|
||||
title: l10n.s_add_account,
|
||||
subtitle: used == null
|
||||
? l10n.l_unlock_first
|
||||
: (capacity != null
|
||||
? l10n.l_accounts_used(used, capacity)
|
||||
: null),
|
||||
subtitle: subtitle,
|
||||
actionStyle: ActionStyle.primary,
|
||||
icon: const Icon(Symbols.person_add_alt),
|
||||
onTap: used != null && (capacity == null || capacity > used)
|
||||
onTap: enabled
|
||||
? (context) async {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
await addOathAccount(context, ref, devicePath, oathState);
|
||||
@ -82,7 +101,7 @@ Widget oathBuildActions(
|
||||
feature: features.actionsPassword,
|
||||
title:
|
||||
oathState.hasKey ? l10n.s_manage_password : l10n.s_set_password,
|
||||
subtitle: l10n.l_optional_password_protection,
|
||||
subtitle: l10n.l_password_protection,
|
||||
icon: const Icon(Symbols.password),
|
||||
onTap: (context) {
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
|
@ -310,7 +310,16 @@ class PivActions extends ConsumerWidget {
|
||||
}
|
||||
|
||||
List<ActionItem> buildSlotActions(
|
||||
PivState pivState, PivSlot slot, AppLocalizations l10n) {
|
||||
PivState pivState, PivSlot slot, bool fipsUnready, AppLocalizations l10n) {
|
||||
if (fipsUnready) {
|
||||
// TODO: Decide on final look and move strings to .arb file.
|
||||
return [
|
||||
ActionItem(
|
||||
icon: const Icon(Symbols.add),
|
||||
title: 'Provision slot',
|
||||
subtitle: 'Change from default PIN/PUK/Management key first'),
|
||||
];
|
||||
}
|
||||
final hasCert = slot.certInfo != null;
|
||||
final hasKey = slot.metadata != null;
|
||||
final canDeleteOrMoveKey = hasKey && pivState.version.isAtLeast(5, 7);
|
||||
|
@ -25,6 +25,7 @@ import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../core/models.dart';
|
||||
import '../../management/models.dart';
|
||||
import '../../widgets/app_input_decoration.dart';
|
||||
import '../../widgets/app_text_field.dart';
|
||||
import '../../widgets/app_text_form_field.dart';
|
||||
@ -176,6 +177,17 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
? currentKeyOrPin.length >= 4
|
||||
: currentKeyOrPin.length == currentType.keyLength * 2;
|
||||
final newLenOk = _keyController.text.length == hexLength;
|
||||
final (fipsCapable, fipsApproved) = ref
|
||||
.watch(currentDeviceDataProvider)
|
||||
.valueOrNull
|
||||
?.info
|
||||
.getFipsStatus(Capability.piv) ??
|
||||
(false, false);
|
||||
final fipsUnready = fipsCapable && !fipsApproved;
|
||||
final managementKeyTypes = ManagementKeyType.values.toList();
|
||||
if (fipsCapable) {
|
||||
managementKeyTypes.remove(ManagementKeyType.tdes);
|
||||
}
|
||||
|
||||
return ResponsiveDialog(
|
||||
title: Text(l10n.l_change_management_key),
|
||||
@ -334,7 +346,7 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
children: [
|
||||
if (widget.pivState.metadata != null)
|
||||
ChoiceFilterChip<ManagementKeyType>(
|
||||
items: ManagementKeyType.values,
|
||||
items: managementKeyTypes,
|
||||
value: _keyType,
|
||||
selected: _keyType != currentType,
|
||||
itemBuilder: (value) => Text(value.getDisplayName(l10n)),
|
||||
@ -344,16 +356,17 @@ class _ManageKeyDialogState extends ConsumerState<ManageKeyDialog> {
|
||||
});
|
||||
},
|
||||
),
|
||||
FilterChip(
|
||||
key: keys.pinLockManagementKeyChip,
|
||||
label: Text(l10n.s_protect_key),
|
||||
selected: _storeKey,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
_storeKey = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!fipsUnready)
|
||||
FilterChip(
|
||||
key: keys.pinLockManagementKeyChip,
|
||||
label: Text(l10n.s_protect_key),
|
||||
selected: _storeKey,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
_storeKey = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
]),
|
||||
]
|
||||
.map((e) => Padding(
|
||||
|
@ -24,6 +24,7 @@ import 'package:material_symbols_icons/symbols.dart';
|
||||
import '../../app/message.dart';
|
||||
import '../../app/models.dart';
|
||||
import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../../app/views/app_failure_page.dart';
|
||||
import '../../app/views/app_list_item.dart';
|
||||
@ -57,6 +58,14 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final hasFeature = ref.watch(featureProvider);
|
||||
final (fipsCapable, fipsApproved) = ref
|
||||
.watch(currentDeviceDataProvider)
|
||||
.valueOrNull
|
||||
?.info
|
||||
.getFipsStatus(Capability.piv) ??
|
||||
(false, false);
|
||||
final fipsUnready = fipsCapable && !fipsApproved;
|
||||
|
||||
return ref.watch(pivStateProvider(widget.devicePath)).when(
|
||||
loading: () => MessagePage(
|
||||
title: l10n.s_certificates,
|
||||
@ -168,8 +177,8 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
ActionListSection.fromMenuActions(
|
||||
context,
|
||||
l10n.s_actions,
|
||||
actions:
|
||||
buildSlotActions(pivState, selected, l10n),
|
||||
actions: buildSlotActions(
|
||||
pivState, selected, fipsUnready, l10n),
|
||||
),
|
||||
],
|
||||
)
|
||||
@ -210,6 +219,7 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
e,
|
||||
expanded: expanded,
|
||||
selected: e == selected,
|
||||
fipsUnready: fipsUnready,
|
||||
),
|
||||
),
|
||||
...shownRetiredSlots.map(
|
||||
@ -218,6 +228,7 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
e,
|
||||
expanded: expanded,
|
||||
selected: e == selected,
|
||||
fipsUnready: fipsUnready,
|
||||
),
|
||||
)
|
||||
],
|
||||
@ -238,9 +249,12 @@ class _CertificateListItem extends ConsumerWidget {
|
||||
final PivSlot pivSlot;
|
||||
final bool expanded;
|
||||
final bool selected;
|
||||
final bool fipsUnready;
|
||||
|
||||
const _CertificateListItem(this.pivState, this.pivSlot,
|
||||
{required this.expanded, required this.selected});
|
||||
{required this.expanded,
|
||||
required this.selected,
|
||||
required this.fipsUnready});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@ -275,8 +289,8 @@ class _CertificateListItem extends ConsumerWidget {
|
||||
),
|
||||
tapIntent: isDesktop && !expanded ? null : OpenIntent(pivSlot),
|
||||
doubleTapIntent: isDesktop && !expanded ? OpenIntent(pivSlot) : null,
|
||||
buildPopupActions: hasFeature(features.slots)
|
||||
? (context) => buildSlotActions(pivState, pivSlot, l10n)
|
||||
buildPopupActions: hasFeature(features.slots) && !fipsUnready
|
||||
? (context) => buildSlotActions(pivState, pivSlot, fipsUnready, l10n)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import '../../app/shortcuts.dart';
|
||||
import '../../app/state.dart';
|
||||
import '../../app/views/action_list.dart';
|
||||
import '../../app/views/fs_dialog.dart';
|
||||
import '../../management/models.dart';
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import 'actions.dart';
|
||||
@ -34,12 +35,13 @@ class SlotDialog extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// TODO: Solve this in a cleaner way
|
||||
final node = ref.watch(currentDeviceDataProvider).valueOrNull?.node;
|
||||
if (node == null) {
|
||||
var keyData = ref.watch(currentDeviceDataProvider).valueOrNull;
|
||||
if (keyData == null) {
|
||||
// The rest of this method assumes there is a device, and will throw an exception if not.
|
||||
// This will never be shown, as the dialog will be immediately closed
|
||||
return const SizedBox();
|
||||
}
|
||||
final devicePath = keyData.node.path;
|
||||
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
@ -48,8 +50,11 @@ class SlotDialog extends ConsumerWidget {
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
);
|
||||
|
||||
final pivState = ref.watch(pivStateProvider(node.path)).valueOrNull;
|
||||
final slotData = ref.watch(pivSlotsProvider(node.path).select((value) =>
|
||||
final (fipsCapable, fipsApproved) =
|
||||
keyData.info.getFipsStatus(Capability.piv);
|
||||
|
||||
final pivState = ref.watch(pivStateProvider(devicePath)).valueOrNull;
|
||||
final slotData = ref.watch(pivSlotsProvider(devicePath).select((value) =>
|
||||
value.whenOrNull(
|
||||
data: (data) =>
|
||||
data.firstWhere((element) => element.slot == pivSlot))));
|
||||
@ -61,7 +66,7 @@ class SlotDialog extends ConsumerWidget {
|
||||
final certInfo = slotData.certInfo;
|
||||
final metadata = slotData.metadata;
|
||||
return PivActions(
|
||||
devicePath: node.path,
|
||||
devicePath: devicePath,
|
||||
pivState: pivState,
|
||||
builder: (context) => ItemShortcuts(
|
||||
item: slotData,
|
||||
@ -113,7 +118,8 @@ class SlotDialog extends ConsumerWidget {
|
||||
ActionListSection.fromMenuActions(
|
||||
context,
|
||||
l10n.s_actions,
|
||||
actions: buildSlotActions(pivState, slotData, l10n),
|
||||
actions: buildSlotActions(
|
||||
pivState, slotData, fipsCapable && !fipsApproved, l10n),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user