diff --git a/webauthn-server-attestation/build.gradle.kts b/webauthn-server-attestation/build.gradle.kts index 713cd59e5..8043777de 100644 --- a/webauthn-server-attestation/build.gradle.kts +++ b/webauthn-server-attestation/build.gradle.kts @@ -61,6 +61,9 @@ val integrationTest = task("integrationTest") { testClassesDirs = sourceSets["integrationTest"].output.classesDirs classpath = sourceSets["integrationTest"].runtimeClasspath shouldRunAfter(tasks.test) + val mdsCacheDir = project.layout.buildDirectory.dir("fido-mds-cache").get().asFile + mdsCacheDir.mkdir() + environment("FIDO_MDS_CACHE_DIR", mdsCacheDir.absolutePath) } tasks["check"].dependsOn(integrationTest) diff --git a/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataDownloaderIntegrationTest.scala b/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataDownloaderIntegrationTest.scala index 644e48fa6..6973fa40f 100644 --- a/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataDownloaderIntegrationTest.scala +++ b/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataDownloaderIntegrationTest.scala @@ -40,15 +40,16 @@ class FidoMetadataDownloaderIntegrationTest blob shouldBe a[Success[_]] val trustRootCert = CertificateParser.parseDer( - TestCaches.getTrustRootCache.get.get.getBytes + TestCaches.trustRootCache.get.getBytes ) + val certChain = TestCaches .cacheSynchronized( downloader .fetchHeaderCertChain( trustRootCert, downloader - .parseBlob(TestCaches.getBlobCache.get.get) + .parseBlob(TestCaches.blobCache.get) .getBlob .getHeader, ) diff --git a/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataServiceIntegrationTest.scala b/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataServiceIntegrationTest.scala index 21b8de6e6..a0c4e433b 100644 --- a/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataServiceIntegrationTest.scala +++ b/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataServiceIntegrationTest.scala @@ -45,7 +45,14 @@ class FidoMetadataServiceIntegrationTest Try( FidoMetadataService .builder() - .useBlob(TestCaches.cacheSynchronized(downloader.loadCachedBlob())) + .useBlob( + TestCaches.cacheSynchronized( + // Since the integration tests cache downloads to file: + // Use refreshBlob() here to always exercise the HTTP part of the downloader, + // and loadCachedBlob() elsewhere to not cause unnecessary server load. + downloader.refreshBlob() + ) + ) .build() ) diff --git a/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/TestCaches.scala b/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/TestCaches.scala index 336b8d347..cd867bfc8 100644 --- a/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/TestCaches.scala +++ b/webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/TestCaches.scala @@ -2,25 +2,27 @@ package com.yubico.fido.metadata import com.yubico.webauthn.data.ByteArray -import java.util.Optional -import java.util.function.Consumer -import java.util.function.Supplier -import scala.jdk.OptionConverters.RichOption +import java.nio.file.Path +import scala.jdk.OptionConverters.RichOptional object TestCaches { // Cache downloaded items to avoid unnecessary load on remote servers, and so tests don't have to wait for rate limiting - private var trustRootCache: Option[ByteArray] = None - val getTrustRootCache: Supplier[Optional[ByteArray]] = () => - trustRootCache.toJava - val setTrustRootCache: Consumer[ByteArray] = trustRoot => { - trustRootCache = Some(trustRoot) - } - - private var blobCache: Option[ByteArray] = None - val getBlobCache: Supplier[Optional[ByteArray]] = () => blobCache.toJava - val setBlobCache: Consumer[ByteArray] = blob => { blobCache = Some(blob) } + private val trustRootCacheFile = Path + .of(sys.env.getOrElse("FIDO_MDS_CACHE_DIR", "."), "trust-root-cache.bin") + .toFile + private val blobCacheFile = Path + .of(sys.env.getOrElse("FIDO_MDS_CACHE_DIR", "."), "blob-cache.bin") + .toFile + + def trustRootCache: Option[ByteArray] = + cachedDefaultSettingsDownloader + .build() + .readCacheFile(trustRootCacheFile) + .toScala + def blobCache: Option[ByteArray] = + cachedDefaultSettingsDownloader.build().readCacheFile(blobCacheFile).toScala def cachedDefaultSettingsDownloader : FidoMetadataDownloader.FidoMetadataDownloaderBuilder = @@ -30,9 +32,9 @@ object TestCaches { "Retrieval and use of this BLOB indicates acceptance of the appropriate agreement located at https://fidoalliance.org/metadata/metadata-legal-terms/" ) .useDefaultTrustRoot() - .useTrustRootCache(getTrustRootCache, setTrustRootCache) + .useTrustRootCacheFile(trustRootCacheFile) .useDefaultBlob() - .useBlobCache(getBlobCache, setBlobCache) + .useBlobCacheFile(blobCacheFile) /** Evaluate expr with an exclusive lock on the test cache. */ def cacheSynchronized[A](expr: => A): A = { diff --git a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java index 09bae4768..3f2a3d2da 100644 --- a/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java +++ b/webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java @@ -1048,7 +1048,7 @@ private Optional loadCachedBlobOnly(X509Certificate trustRootCerti }); } - private Optional readCacheFile(File cacheFile) throws IOException { + Optional readCacheFile(File cacheFile) throws IOException { if (cacheFile.exists() && cacheFile.canRead() && cacheFile.isFile()) { try (FileInputStream f = new FileInputStream(cacheFile)) { return Optional.of(readAll(f));