This commit is contained in:
Dain Nilsson 2024-03-19 17:36:18 +01:00
commit 3a12c3f9a3
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
3 changed files with 63 additions and 23 deletions

View File

@ -42,6 +42,7 @@ from yubikit.logging import LOG_LEVEL
from ykman.pcsc import list_devices, YK_READER_NAME
from smartcard.Exceptions import SmartcardException, NoCardException
from smartcard.pcsc.PCSCExceptions import EstablishContextException
from smartcard.CardMonitoring import CardObserver, CardMonitor
from hashlib import sha256
from dataclasses import asdict
from typing import Mapping, Tuple
@ -263,9 +264,6 @@ class AbstractDeviceNode(RpcNode):
class UsbDeviceNode(AbstractDeviceNode):
def __init__(self, device, info):
super().__init__(device, info)
def _supports_connection(self, conn_type):
return self._device.pid.supports_connection(conn_type)
@ -308,15 +306,53 @@ class UsbDeviceNode(AbstractDeviceNode):
raise ConnectionException("fido", e)
class _ReaderObserver(CardObserver):
def __init__(self, device):
self.device = device
self.card = None
self.data = None
def update(self, observable, actions):
added, removed = actions
for card in added:
if card.reader == self.device.reader.name:
if card != self.card:
self.card = card
break
else:
self.card = None
self.data = None
logger.debug(f"NFC card: {self.card}")
class ReaderDeviceNode(AbstractDeviceNode):
def __init__(self, device, info):
super().__init__(device, info)
self._observer = _ReaderObserver(device)
self._monitor = CardMonitor()
self._monitor.addObserver(self._observer)
def close(self):
self._monitor.deleteObserver(self._observer)
super().close()
def get_data(self):
try:
with self._device.open_connection(SmartCardConnection) as conn:
return dict(self._read_data(conn), present=True)
except NoCardException:
return dict(present=False, status="no-card")
except ValueError:
return dict(present=False, status="unknown-device")
if self._observer.data is None:
card = self._observer.card
if card is None:
return dict(present=False, status="no-card")
try:
with self._device.open_connection(SmartCardConnection) as conn:
self._observer.data = dict(self._read_data(conn), present=True)
except NoCardException:
return dict(present=False, status="no-card")
except ValueError:
self._observer.data = dict(present=False, status="unknown-device")
return self._observer.data
@action(closes_child=False)
def get(self, params, event, signal):
return super().get(params, event, signal)
@child
def ccid(self):

View File

@ -31,9 +31,8 @@ import 'state.dart';
const _usbPollDelay = Duration(milliseconds: 500);
const _nfcPollDelay = Duration(milliseconds: 2500);
const _nfcAttachPollDelay = Duration(seconds: 1);
const _nfcDetachPollDelay = Duration(seconds: 5);
const _nfcPollReadersDelay = Duration(milliseconds: 2500);
const _nfcPollCardDelay = Duration(seconds: 1);
final _log = Logger('desktop.devices');
@ -197,7 +196,7 @@ class NfcDeviceNotifier extends StateNotifier<List<NfcReaderNode>> {
}
if (mounted) {
_pollTimer = Timer(_nfcPollDelay, _pollReaders);
_pollTimer = Timer(_nfcPollReadersDelay, _pollReaders);
}
}
}
@ -260,7 +259,7 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
void _notifyWindowState(WindowState windowState) {
if (windowState.active) {
_pollReader();
_pollCard();
} else {
_pollTimer?.cancel();
// TODO: Should we clear the key here?
@ -276,16 +275,23 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
super.dispose();
}
void _pollReader() async {
void _pollCard() async {
_pollTimer?.cancel();
final node = _deviceNode!;
try {
_log.debug('Polling for USB device changes...');
_log.debug('Polling for NFC device changes...');
var result = await _rpc?.command('get', node.path.segments);
if (mounted && result != null) {
if (result['data']['present']) {
state = AsyncValue.data(YubiKeyData(node, result['data']['name'],
DeviceInfo.fromJson(result['data']['info'])));
final oldState = state.valueOrNull;
final newState = YubiKeyData(node, result['data']['name'],
DeviceInfo.fromJson(result['data']['info']));
if (oldState != null && oldState != newState) {
// Ensure state is cleared
state = const AsyncValue.loading();
} else {
state = AsyncValue.data(newState);
}
} else {
final status = result['data']['status'];
// Only update if status is not changed
@ -298,9 +304,7 @@ class CurrentDeviceDataNotifier extends StateNotifier<AsyncValue<YubiKeyData>> {
_log.error('Error polling NFC', jsonEncode(e));
}
if (mounted) {
_pollTimer = Timer(
state is AsyncData ? _nfcDetachPollDelay : _nfcAttachPollDelay,
_pollReader);
_pollTimer = Timer(_nfcPollCardDelay, _pollCard);
}
}
}

View File

@ -295,7 +295,7 @@ class DesktopCredentialListNotifier extends OathCredentialListNotifier {
code = OathCode.fromJson(result);
}
_log.debug('Calculate', jsonEncode(code));
if (update && mounted) {
if (update && mounted && state != null) {
final creds = state!.toList();
final i = creds.indexWhere((e) => e.credential.id == credential.id);
state = creds..[i] = creds[i].copyWith(code: code);