schedule device info update in fido and oath

This commit is contained in:
Adam Velebil 2024-09-05 07:27:41 +02:00
parent 3873ec4514
commit fb2bec0b5e
No known key found for this signature in database
GPG Key ID: C9B1E4A3CBBD2E10
5 changed files with 34 additions and 11 deletions

View File

@ -30,9 +30,7 @@ import com.yubico.yubikit.core.YubiKeyDevice
import com.yubico.yubikit.core.smartcard.scp.ScpKeyParams import com.yubico.yubikit.core.smartcard.scp.ScpKeyParams
import com.yubico.yubikit.management.Capability import com.yubico.yubikit.management.Capability
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import kotlin.coroutines.suspendCoroutine
interface DeviceListener { interface DeviceListener {
// a USB device is connected // a USB device is connected

View File

@ -17,6 +17,7 @@
package com.yubico.authenticator.fido package com.yubico.authenticator.fido
import com.yubico.authenticator.device.DeviceManager import com.yubico.authenticator.device.DeviceManager
import com.yubico.authenticator.device.Info
import com.yubico.authenticator.fido.data.YubiKitFidoSession import com.yubico.authenticator.fido.data.YubiKitFidoSession
import com.yubico.authenticator.yubikit.DeviceInfoHelper.Companion.getDeviceInfo import com.yubico.authenticator.yubikit.DeviceInfoHelper.Companion.getDeviceInfo
import com.yubico.authenticator.yubikit.withConnection import com.yubico.authenticator.yubikit.withConnection
@ -24,11 +25,15 @@ import com.yubico.yubikit.android.transport.usb.UsbYubiKeyDevice
import com.yubico.yubikit.core.fido.FidoConnection import com.yubico.yubikit.core.fido.FidoConnection
import com.yubico.yubikit.core.util.Result import com.yubico.yubikit.core.util.Result
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.util.Timer
import java.util.TimerTask
import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.cancellation.CancellationException
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import kotlin.concurrent.schedule
class FidoConnectionHelper(private val deviceManager: DeviceManager) { class FidoConnectionHelper(private val deviceManager: DeviceManager) {
private var pendingAction: FidoAction? = null private var pendingAction: FidoAction? = null
private var deviceInfoTimer: TimerTask? = null
fun invokePending(fidoSession: YubiKitFidoSession) { fun invokePending(fidoSession: YubiKitFidoSession) {
pendingAction?.let { action -> pendingAction?.let { action ->
@ -38,6 +43,7 @@ class FidoConnectionHelper(private val deviceManager: DeviceManager) {
} }
fun cancelPending() { fun cancelPending() {
deviceInfoTimer?.cancel()
pendingAction?.let { action -> pendingAction?.let { action ->
action.invoke(Result.failure(CancellationException())) action.invoke(Result.failure(CancellationException()))
pendingAction = null pendingAction = null
@ -67,7 +73,7 @@ class FidoConnectionHelper(private val deviceManager: DeviceManager) {
block(YubiKitFidoSession(it)) block(YubiKitFidoSession(it))
}.also { }.also {
if (updateDeviceInfo) { if (updateDeviceInfo) {
deviceManager.setDeviceInfo(getDeviceInfo(device)) scheduleDeviceInfoUpdate(getDeviceInfo(device))
} }
} }
@ -91,6 +97,14 @@ class FidoConnectionHelper(private val deviceManager: DeviceManager) {
} }
} }
fun scheduleDeviceInfoUpdate(deviceInfo: Info?) {
deviceInfoTimer?.cancel()
deviceInfoTimer = Timer("update-device-info", false).schedule(500) {
logger.debug("Updating device info")
deviceManager.setDeviceInfo(deviceInfo)
}
}
companion object { companion object {
private val logger = LoggerFactory.getLogger(FidoConnectionHelper::class.java) private val logger = LoggerFactory.getLogger(FidoConnectionHelper::class.java)
} }

View File

@ -199,7 +199,7 @@ class FidoManager(
} }
if (updateDeviceInfo.getAndSet(false)) { if (updateDeviceInfo.getAndSet(false)) {
deviceManager.setDeviceInfo(getDeviceInfo(device)) connectionHelper.scheduleDeviceInfoUpdate(getDeviceInfo(device))
} }
} catch (e: Exception) { } catch (e: Exception) {
// something went wrong, try to get DeviceInfo from any available connection type // something went wrong, try to get DeviceInfo from any available connection type

View File

@ -26,6 +26,7 @@ import com.yubico.authenticator.*
import com.yubico.authenticator.device.Capabilities import com.yubico.authenticator.device.Capabilities
import com.yubico.authenticator.device.DeviceListener import com.yubico.authenticator.device.DeviceListener
import com.yubico.authenticator.device.DeviceManager import com.yubico.authenticator.device.DeviceManager
import com.yubico.authenticator.device.Info
import com.yubico.authenticator.device.UnknownDevice import com.yubico.authenticator.device.UnknownDevice
import com.yubico.authenticator.oath.data.Code import com.yubico.authenticator.oath.data.Code
import com.yubico.authenticator.oath.data.CodeType import com.yubico.authenticator.oath.data.CodeType
@ -63,10 +64,12 @@ import kotlinx.serialization.encodeToString
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.IOException import java.io.IOException
import java.net.URI import java.net.URI
import java.util.Timer
import java.util.TimerTask
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import kotlin.random.Random import kotlin.concurrent.schedule
typealias OathAction = (Result<YubiKitOathSession, Exception>) -> Unit typealias OathAction = (Result<YubiKitOathSession, Exception>) -> Unit
@ -108,8 +111,10 @@ class OathManager(
private var refreshJob: Job? = null private var refreshJob: Job? = null
private var addToAny = false private var addToAny = false
private val updateDeviceInfo = AtomicBoolean(false) private val updateDeviceInfo = AtomicBoolean(false)
private var deviceInfoTimer: TimerTask? = null
override fun onPause() { override fun onPause() {
deviceInfoTimer?.cancel()
// cancel any pending actions, except for addToAny // cancel any pending actions, except for addToAny
if (!addToAny) { if (!addToAny) {
pendingAction?.let { pendingAction?.let {
@ -294,7 +299,7 @@ class OathManager(
) )
if (updateDeviceInfo.getAndSet(false)) { if (updateDeviceInfo.getAndSet(false)) {
deviceManager.setDeviceInfo(getDeviceInfo(device)) scheduleDeviceInfoUpdate(getDeviceInfo(device))
} }
} catch (e: Exception) { } catch (e: Exception) {
// OATH not enabled/supported, try to get DeviceInfo over other USB interfaces // OATH not enabled/supported, try to get DeviceInfo over other USB interfaces
@ -675,6 +680,14 @@ class OathManager(
return credential.data return credential.data
} }
fun scheduleDeviceInfoUpdate(deviceInfo: Info?) {
deviceInfoTimer?.cancel()
deviceInfoTimer = Timer("update-device-info", false).schedule(500) {
logger.debug("Updating device info")
deviceManager.setDeviceInfo(deviceInfo)
}
}
private suspend fun <T> useOathSession( private suspend fun <T> useOathSession(
unlock: Boolean = true, unlock: Boolean = true,
updateDeviceInfo: Boolean = false, updateDeviceInfo: Boolean = false,
@ -702,7 +715,7 @@ class OathManager(
block(getOathSession(it)) block(getOathSession(it))
}.also { }.also {
if (updateDeviceInfo) { if (updateDeviceInfo) {
deviceManager.setDeviceInfo(getDeviceInfo(device)) scheduleDeviceInfoUpdate(getDeviceInfo(device))
} }
} }

View File

@ -393,8 +393,7 @@ class _FidoMethodChannelNotifier extends MethodChannelNotifier {
Future<dynamic> reset() async => invoke('reset', { Future<dynamic> reset() async => invoke('reset', {
'operationSuccess': l10n.s_nfc_fido_reset_success, 'operationSuccess': l10n.s_nfc_fido_reset_success,
'operationFailure': l10n.s_nfc_fido_reset_failure, 'operationFailure': l10n.s_nfc_fido_reset_failure
'showSuccess': true
}); });
Future<dynamic> setPin(String newPin, {String? oldPin}) async => Future<dynamic> setPin(String newPin, {String? oldPin}) async =>
@ -405,8 +404,7 @@ class _FidoMethodChannelNotifier extends MethodChannelNotifier {
: l10n.s_pin_set, : l10n.s_pin_set,
'operationFailure': oldPin != null 'operationFailure': oldPin != null
? l10n.s_nfc_fido_change_pin_failure ? l10n.s_nfc_fido_change_pin_failure
: l10n.s_nfc_fido_set_pin_failure, : l10n.s_nfc_fido_set_pin_failure
'showSuccess': true
}); });
Future<dynamic> unlock(String pin) async => invoke('unlock', { Future<dynamic> unlock(String pin) async => invoke('unlock', {