mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-23 00:57:26 +03:00
Merge PR #1449
This commit is contained in:
commit
80637042ba
@ -399,12 +399,21 @@ class SlotNode(RpcNode):
|
||||
else None,
|
||||
)
|
||||
|
||||
@action(condition=lambda self: self.certificate)
|
||||
@action(condition=lambda self: self.certificate or self.metadata)
|
||||
def delete(self, params, event, signal):
|
||||
self.session.delete_certificate(self.slot)
|
||||
self.session.put_object(OBJECT_ID.CHUID, generate_chuid())
|
||||
delete_cert = params.pop("delete_cert", False)
|
||||
delete_key = params.pop("delete_key", False)
|
||||
|
||||
if not delete_cert and not delete_key:
|
||||
raise ValueError("Missing delete option")
|
||||
|
||||
if delete_cert:
|
||||
self.session.delete_certificate(self.slot)
|
||||
self.session.put_object(OBJECT_ID.CHUID, generate_chuid())
|
||||
self.certificate = None
|
||||
if delete_key:
|
||||
self.session.delete_key(self.slot)
|
||||
self._refresh()
|
||||
self.certificate = None
|
||||
return dict()
|
||||
|
||||
@action
|
||||
|
@ -317,8 +317,10 @@ class _DesktopPivSlotsNotifier extends PivSlotsNotifier {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> delete(SlotId slot) async {
|
||||
await _session.command('delete', target: ['slots', slot.hexId]);
|
||||
Future<void> delete(SlotId slot, bool deleteCert, bool deleteKey) async {
|
||||
await _session.command('delete',
|
||||
target: ['slots', slot.hexId],
|
||||
params: {'delete_cert': deleteCert, 'delete_key': deleteKey});
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
|
||||
|
@ -505,6 +505,10 @@
|
||||
"l_unsupported_key_type": null,
|
||||
"l_delete_certificate": null,
|
||||
"l_delete_certificate_desc": null,
|
||||
"l_delete_key": null,
|
||||
"l_delete_key_desc": null,
|
||||
"l_delete_certificate_or_key": null,
|
||||
"l_delete_certificate_or_key_desc": null,
|
||||
"s_issuer": null,
|
||||
"s_serial": null,
|
||||
"s_certificate_fingerprint": null,
|
||||
@ -522,14 +526,31 @@
|
||||
},
|
||||
"l_generating_private_key": null,
|
||||
"s_private_key_generated": null,
|
||||
"p_select_what_to_delete": null,
|
||||
"p_warning_delete_certificate": null,
|
||||
"p_warning_delete_key": null,
|
||||
"p_warning_delete_certificate_and_key": null,
|
||||
"q_delete_certificate_confirm": null,
|
||||
"@q_delete_certificate_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_key_confirm": null,
|
||||
"@q_delete_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_certificate_and_key_confirm": null,
|
||||
"@q_delete_certificate_and_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"l_certificate_deleted": null,
|
||||
"l_key_deleted": null,
|
||||
"l_certificate_and_key_deleted": null,
|
||||
"p_password_protected_file": null,
|
||||
"p_import_items_desc": null,
|
||||
"@p_import_items_desc": {
|
||||
|
@ -505,6 +505,10 @@
|
||||
"l_unsupported_key_type": "Unsupported key type",
|
||||
"l_delete_certificate": "Delete certificate",
|
||||
"l_delete_certificate_desc": "Remove the certificate from your YubiKey",
|
||||
"l_delete_key": "Delete key",
|
||||
"l_delete_key_desc": "Remove the key from your YubiKey",
|
||||
"l_delete_certificate_or_key": "Delete certificate/key",
|
||||
"l_delete_certificate_or_key_desc": "Remove the certificate or key from your YubiKey",
|
||||
"s_issuer": "Issuer",
|
||||
"s_serial": "Serial",
|
||||
"s_certificate_fingerprint": "Fingerprint",
|
||||
@ -522,14 +526,31 @@
|
||||
},
|
||||
"l_generating_private_key": "Generating private key\u2026",
|
||||
"s_private_key_generated": "Private key generated",
|
||||
"p_select_what_to_delete": "Select what to delete from the slot.",
|
||||
"p_warning_delete_certificate": "Warning! This action will delete the certificate from your YubiKey.",
|
||||
"p_warning_delete_key": "Warning! This action will delete the key from your YubiKey.",
|
||||
"p_warning_delete_certificate_and_key": "Warning! This action will delete the certificate and key from your YubiKey.",
|
||||
"q_delete_certificate_confirm": "Delete the certificate in PIV slot {slot}?",
|
||||
"@q_delete_certificate_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_key_confirm": "Delete the key in PIV slot {slot}?",
|
||||
"@q_delete_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_certificate_and_key_confirm": "Delete the certificate and key in PIV slot {slot}?",
|
||||
"@q_delete_certificate_and_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"l_certificate_deleted": "Certificate deleted",
|
||||
"l_key_deleted": "Key deleted",
|
||||
"l_certificate_and_key_deleted": "Certificate and key deleted",
|
||||
"p_password_protected_file": "The selected file is password protected. Enter the password to proceed.",
|
||||
"p_import_items_desc": "The following item(s) will be imported into PIV slot {slot}.",
|
||||
"@p_import_items_desc": {
|
||||
|
@ -505,6 +505,10 @@
|
||||
"l_unsupported_key_type": null,
|
||||
"l_delete_certificate": "Supprimer un certificat",
|
||||
"l_delete_certificate_desc": "Supprimer un certificat de votre YubiKey",
|
||||
"l_delete_key": null,
|
||||
"l_delete_key_desc": null,
|
||||
"l_delete_certificate_or_key": null,
|
||||
"l_delete_certificate_or_key_desc": null,
|
||||
"s_issuer": "Émetteur",
|
||||
"s_serial": "Série",
|
||||
"s_certificate_fingerprint": "Empreinte digitale",
|
||||
@ -522,14 +526,31 @@
|
||||
},
|
||||
"l_generating_private_key": "Génération d'une clé privée\u2026",
|
||||
"s_private_key_generated": "Clé privée générée",
|
||||
"p_select_what_to_delete": null,
|
||||
"p_warning_delete_certificate": "Attention! Cette action supprimera le certificat de votre YubiKey.",
|
||||
"p_warning_delete_key": null,
|
||||
"p_warning_delete_certificate_and_key": null,
|
||||
"q_delete_certificate_confirm": "Supprimer le certficat du slot PIV {slot}?",
|
||||
"@q_delete_certificate_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_key_confirm": null,
|
||||
"@q_delete_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_certificate_and_key_confirm": null,
|
||||
"@q_delete_certificate_and_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"l_certificate_deleted": "Certificat supprimé",
|
||||
"l_key_deleted": null,
|
||||
"l_certificate_and_key_deleted": null,
|
||||
"p_password_protected_file": "Le fichier sélectionné est protégé par un mot de passe. Enterez le mot de passe pour continuer.",
|
||||
"p_import_items_desc": "Les éléments suivants seront importés dans le slot PIV {slot}.",
|
||||
"@p_import_items_desc": {
|
||||
|
@ -505,6 +505,10 @@
|
||||
"l_unsupported_key_type": null,
|
||||
"l_delete_certificate": "証明書を削除",
|
||||
"l_delete_certificate_desc": "YubiKeyか証明書の削除",
|
||||
"l_delete_key": null,
|
||||
"l_delete_key_desc": null,
|
||||
"l_delete_certificate_or_key": null,
|
||||
"l_delete_certificate_or_key_desc": null,
|
||||
"s_issuer": "発行者",
|
||||
"s_serial": "シリアル番号",
|
||||
"s_certificate_fingerprint": "指紋",
|
||||
@ -522,14 +526,31 @@
|
||||
},
|
||||
"l_generating_private_key": "秘密鍵を生成しています\u2026",
|
||||
"s_private_key_generated": "秘密鍵を生成しました",
|
||||
"p_select_what_to_delete": null,
|
||||
"p_warning_delete_certificate": "警告!この操作によってYubiKeyから証明書が削除されます",
|
||||
"p_warning_delete_key": null,
|
||||
"p_warning_delete_certificate_and_key": null,
|
||||
"q_delete_certificate_confirm": "PIVスロット{slot}の証明書を削除しますか?",
|
||||
"@q_delete_certificate_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_key_confirm": null,
|
||||
"@q_delete_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_certificate_and_key_confirm": null,
|
||||
"@q_delete_certificate_and_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"l_certificate_deleted": "証明書が削除されました",
|
||||
"l_key_deleted": null,
|
||||
"l_certificate_and_key_deleted": null,
|
||||
"p_password_protected_file": "選択したファイルはパスワードで保護されています。パスワードを入力して続行します",
|
||||
"p_import_items_desc": "次のアイテムはPIVスロット{slot}にインポートされます",
|
||||
"@p_import_items_desc": {
|
||||
|
@ -505,6 +505,10 @@
|
||||
"l_unsupported_key_type": null,
|
||||
"l_delete_certificate": "Usuń certyfikat",
|
||||
"l_delete_certificate_desc": "Usuń certyfikat z klucza YubiKey",
|
||||
"l_delete_key": null,
|
||||
"l_delete_key_desc": null,
|
||||
"l_delete_certificate_or_key": null,
|
||||
"l_delete_certificate_or_key_desc": null,
|
||||
"s_issuer": "Wydawca",
|
||||
"s_serial": "Nr. seryjny",
|
||||
"s_certificate_fingerprint": "Odcisk palca",
|
||||
@ -522,14 +526,31 @@
|
||||
},
|
||||
"l_generating_private_key": "Generowanie prywatnego klucza\u2026",
|
||||
"s_private_key_generated": "Wygenerowano klucz prywatny",
|
||||
"p_select_what_to_delete": null,
|
||||
"p_warning_delete_certificate": "Uwaga! Ta czynność spowoduje usunięcie certyfikatu z klucza YubiKey.",
|
||||
"p_warning_delete_key": null,
|
||||
"p_warning_delete_certificate_and_key": null,
|
||||
"q_delete_certificate_confirm": "Usunąć certyfikat ze slotu PIV {slot}?",
|
||||
"@q_delete_certificate_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_key_confirm": null,
|
||||
"@q_delete_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"q_delete_certificate_and_key_confirm": null,
|
||||
"@q_delete_certificate_and_key_confirm": {
|
||||
"placeholders": {
|
||||
"slot": {}
|
||||
}
|
||||
},
|
||||
"l_certificate_deleted": "Certyfikat został usunięty",
|
||||
"l_key_deleted": null,
|
||||
"l_certificate_and_key_deleted": null,
|
||||
"p_password_protected_file": "Wybrany plik jest chroniony hasłem. Wprowadź je, aby kontynuować.",
|
||||
"p_import_items_desc": "Następujące elementy zostaną zaimportowane do slotu PIV {slot}.",
|
||||
"@p_import_items_desc": {
|
||||
|
@ -66,5 +66,5 @@ abstract class PivSlotsNotifier
|
||||
PinPolicy pinPolicy = PinPolicy.dfault,
|
||||
TouchPolicy touchPolicy = TouchPolicy.dfault,
|
||||
});
|
||||
Future<void> delete(SlotId slot);
|
||||
Future<void> delete(SlotId slot, bool deleteCert, bool deleteKey);
|
||||
}
|
||||
|
@ -263,6 +263,7 @@ class PivActions extends ConsumerWidget {
|
||||
context: context,
|
||||
builder: (context) => DeleteCertificateDialog(
|
||||
devicePath,
|
||||
pivState,
|
||||
intent.target,
|
||||
),
|
||||
) ??
|
||||
@ -283,9 +284,11 @@ class PivActions extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
List<ActionItem> buildSlotActions(PivSlot slot, AppLocalizations l10n) {
|
||||
List<ActionItem> buildSlotActions(
|
||||
PivState pivState, PivSlot slot, AppLocalizations l10n) {
|
||||
final hasCert = slot.certInfo != null;
|
||||
final hasKey = slot.metadata != null;
|
||||
final canDeleteKey = hasKey && pivState.version.isAtLeast(5, 7);
|
||||
return [
|
||||
ActionItem(
|
||||
key: keys.generateAction,
|
||||
@ -313,15 +316,6 @@ List<ActionItem> buildSlotActions(PivSlot slot, AppLocalizations l10n) {
|
||||
subtitle: l10n.l_export_certificate_desc,
|
||||
intent: ExportIntent(slot),
|
||||
),
|
||||
ActionItem(
|
||||
key: keys.deleteAction,
|
||||
feature: features.slotsDelete,
|
||||
actionStyle: ActionStyle.error,
|
||||
icon: const Icon(Symbols.delete),
|
||||
title: l10n.l_delete_certificate,
|
||||
subtitle: l10n.l_delete_certificate_desc,
|
||||
intent: DeleteIntent(slot),
|
||||
),
|
||||
] else if (hasKey) ...[
|
||||
ActionItem(
|
||||
key: keys.exportAction,
|
||||
@ -332,5 +326,23 @@ List<ActionItem> buildSlotActions(PivSlot slot, AppLocalizations l10n) {
|
||||
intent: ExportIntent(slot),
|
||||
),
|
||||
],
|
||||
if (hasCert || canDeleteKey)
|
||||
ActionItem(
|
||||
key: keys.deleteAction,
|
||||
feature: features.slotsDelete,
|
||||
actionStyle: ActionStyle.error,
|
||||
icon: const Icon(Symbols.delete),
|
||||
title: hasCert && canDeleteKey
|
||||
? l10n.l_delete_certificate_or_key
|
||||
: hasCert
|
||||
? l10n.l_delete_certificate
|
||||
: l10n.l_delete_key,
|
||||
subtitle: hasCert && canDeleteKey
|
||||
? l10n.l_delete_certificate_or_key_desc
|
||||
: hasCert
|
||||
? l10n.l_delete_certificate_desc
|
||||
: l10n.l_delete_key_desc,
|
||||
intent: DeleteIntent(slot),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
@ -27,34 +27,75 @@ import '../keys.dart' as keys;
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
|
||||
class DeleteCertificateDialog extends ConsumerWidget {
|
||||
class DeleteCertificateDialog extends ConsumerStatefulWidget {
|
||||
final DevicePath devicePath;
|
||||
final PivState pivState;
|
||||
final PivSlot pivSlot;
|
||||
const DeleteCertificateDialog(this.devicePath, this.pivSlot, {super.key});
|
||||
const DeleteCertificateDialog(this.devicePath, this.pivState, this.pivSlot,
|
||||
{super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<ConsumerStatefulWidget> createState() =>
|
||||
_DeleteCertificateDialogState();
|
||||
}
|
||||
|
||||
class _DeleteCertificateDialogState
|
||||
extends ConsumerState<DeleteCertificateDialog> {
|
||||
late bool _deleteCertificate;
|
||||
late bool _deleteKey;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_deleteCertificate = widget.pivSlot.certInfo != null;
|
||||
_deleteKey = widget.pivSlot.metadata != null &&
|
||||
widget.pivState.version.isAtLeast(5, 7);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final canDeleteCertificate = widget.pivSlot.certInfo != null;
|
||||
final canDeleteKey = widget.pivSlot.metadata != null &&
|
||||
widget.pivState.version.isAtLeast(5, 7);
|
||||
|
||||
return ResponsiveDialog(
|
||||
title: Text(l10n.l_delete_certificate),
|
||||
title: Text(canDeleteKey && canDeleteCertificate
|
||||
? l10n.l_delete_certificate_or_key
|
||||
: canDeleteCertificate
|
||||
? l10n.l_delete_certificate
|
||||
: l10n.l_delete_key),
|
||||
actions: [
|
||||
TextButton(
|
||||
key: keys.deleteButton,
|
||||
onPressed: () async {
|
||||
try {
|
||||
await ref
|
||||
.read(pivSlotsProvider(devicePath).notifier)
|
||||
.delete(pivSlot.slot);
|
||||
await ref.read(withContextProvider)(
|
||||
(context) async {
|
||||
Navigator.of(context).pop(true);
|
||||
showMessage(context, l10n.l_certificate_deleted);
|
||||
},
|
||||
);
|
||||
} on CancellationException catch (_) {
|
||||
// ignored
|
||||
}
|
||||
},
|
||||
onPressed: _deleteKey || _deleteCertificate
|
||||
? () async {
|
||||
try {
|
||||
await ref
|
||||
.read(pivSlotsProvider(widget.devicePath).notifier)
|
||||
.delete(widget.pivSlot.slot, _deleteCertificate,
|
||||
_deleteKey);
|
||||
|
||||
await ref.read(withContextProvider)(
|
||||
(context) async {
|
||||
String message;
|
||||
if (_deleteCertificate && _deleteKey) {
|
||||
message = l10n.l_certificate_and_key_deleted;
|
||||
} else if (_deleteCertificate) {
|
||||
message = l10n.l_certificate_deleted;
|
||||
} else {
|
||||
message = l10n.l_key_deleted;
|
||||
}
|
||||
|
||||
Navigator.of(context).pop(true);
|
||||
showMessage(context, message);
|
||||
},
|
||||
);
|
||||
} on CancellationException catch (_) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(l10n.s_delete),
|
||||
),
|
||||
],
|
||||
@ -63,9 +104,55 @@ class DeleteCertificateDialog extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(l10n.p_warning_delete_certificate),
|
||||
Text(l10n.q_delete_certificate_confirm(
|
||||
pivSlot.slot.getDisplayName(l10n))),
|
||||
if (_deleteCertificate || _deleteKey) ...[
|
||||
Text(
|
||||
_deleteCertificate && _deleteKey
|
||||
? l10n.p_warning_delete_certificate_and_key
|
||||
: _deleteCertificate
|
||||
? l10n.p_warning_delete_certificate
|
||||
: l10n.p_warning_delete_key,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w700),
|
||||
),
|
||||
Text(_deleteCertificate && _deleteKey
|
||||
? l10n.q_delete_certificate_and_key_confirm(
|
||||
widget.pivSlot.slot.getDisplayName(l10n))
|
||||
: _deleteCertificate
|
||||
? l10n.q_delete_certificate_confirm(
|
||||
widget.pivSlot.slot.getDisplayName(l10n))
|
||||
: l10n.q_delete_key_confirm(
|
||||
widget.pivSlot.slot.getDisplayName(l10n)))
|
||||
],
|
||||
if (!_deleteCertificate && !_deleteKey)
|
||||
Text(l10n.p_select_what_to_delete),
|
||||
if (canDeleteKey && canDeleteCertificate)
|
||||
Wrap(
|
||||
spacing: 4.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
if (canDeleteCertificate)
|
||||
FilterChip(
|
||||
label: Text(l10n.s_certificate),
|
||||
selected: _deleteCertificate,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
_deleteCertificate = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (canDeleteKey)
|
||||
FilterChip(
|
||||
label: Text(l10n.s_private_key),
|
||||
selected: _deleteKey,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
_deleteKey = value;
|
||||
});
|
||||
})
|
||||
],
|
||||
),
|
||||
]
|
||||
.map((e) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
|
@ -150,7 +150,8 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
ActionListSection.fromMenuActions(
|
||||
context,
|
||||
l10n.s_actions,
|
||||
actions: buildSlotActions(selected, l10n),
|
||||
actions:
|
||||
buildSlotActions(pivState, selected, l10n),
|
||||
),
|
||||
],
|
||||
)
|
||||
@ -186,6 +187,7 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
if (pivSlots?.hasValue == true)
|
||||
...pivSlots!.value.map(
|
||||
(e) => _CertificateListItem(
|
||||
pivState,
|
||||
e,
|
||||
expanded: expanded,
|
||||
selected: e == selected,
|
||||
@ -204,11 +206,12 @@ class _PivScreenState extends ConsumerState<PivScreen> {
|
||||
}
|
||||
|
||||
class _CertificateListItem extends ConsumerWidget {
|
||||
final PivState pivState;
|
||||
final PivSlot pivSlot;
|
||||
final bool expanded;
|
||||
final bool selected;
|
||||
|
||||
const _CertificateListItem(this.pivSlot,
|
||||
const _CertificateListItem(this.pivState, this.pivSlot,
|
||||
{required this.expanded, required this.selected});
|
||||
|
||||
@override
|
||||
@ -245,7 +248,7 @@ class _CertificateListItem extends ConsumerWidget {
|
||||
tapIntent: isDesktop && !expanded ? null : OpenIntent(pivSlot),
|
||||
doubleTapIntent: isDesktop && !expanded ? OpenIntent(pivSlot) : null,
|
||||
buildPopupActions: hasFeature(features.slots)
|
||||
? (context) => buildSlotActions(pivSlot, l10n)
|
||||
? (context) => buildSlotActions(pivState, pivSlot, l10n)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ class SlotDialog extends ConsumerWidget {
|
||||
ActionListSection.fromMenuActions(
|
||||
context,
|
||||
l10n.s_actions,
|
||||
actions: buildSlotActions(slotData, l10n),
|
||||
actions: buildSlotActions(pivState, slotData, l10n),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user