Skip to content

Commit 0b589bf

Browse files
committed
Cache MDS downloads to file in integration tests
MDS rate limits download requests, so rapid re-runs of the integration tests may fail due to the server rejecting requests with `429 Too Many Requests`. Caching the downloaded files across integration test runs helps prevent this. This comes at the cost of making the integration tests less reliable at testing the HTTP connection parts, in exchange for the benefit of making the tests less likely to fail for transient reasons.
1 parent 0a520a0 commit 0b589bf

4 files changed

Lines changed: 25 additions & 19 deletions

File tree

webauthn-server-attestation/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ val integrationTest = task<Test>("integrationTest") {
6161
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
6262
classpath = sourceSets["integrationTest"].runtimeClasspath
6363
shouldRunAfter(tasks.test)
64+
val mdsCacheDir = project.layout.buildDirectory.dir("fido-mds-cache").get().asFile
65+
mdsCacheDir.mkdir()
66+
environment("FIDO_MDS_CACHE_DIR", mdsCacheDir.absolutePath)
6467
}
6568
tasks["check"].dependsOn(integrationTest)
6669

webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/FidoMetadataDownloaderIntegrationTest.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,16 @@ class FidoMetadataDownloaderIntegrationTest
4040
blob shouldBe a[Success[_]]
4141
val trustRootCert =
4242
CertificateParser.parseDer(
43-
TestCaches.getTrustRootCache.get.get.getBytes
43+
TestCaches.trustRootCache.get.getBytes
4444
)
45+
4546
val certChain = TestCaches
4647
.cacheSynchronized(
4748
downloader
4849
.fetchHeaderCertChain(
4950
trustRootCert,
5051
downloader
51-
.parseBlob(TestCaches.getBlobCache.get.get)
52+
.parseBlob(TestCaches.blobCache.get)
5253
.getBlob
5354
.getHeader,
5455
)

webauthn-server-attestation/src/integrationTest/scala/com/yubico/fido/metadata/TestCaches.scala

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,27 @@ package com.yubico.fido.metadata
22

33
import com.yubico.webauthn.data.ByteArray
44

5-
import java.util.Optional
6-
import java.util.function.Consumer
7-
import java.util.function.Supplier
8-
import scala.jdk.OptionConverters.RichOption
5+
import java.nio.file.Path
6+
import scala.jdk.OptionConverters.RichOptional
97

108
object TestCaches {
119

1210
// Cache downloaded items to avoid unnecessary load on remote servers, and so tests don't have to wait for rate limiting
1311

14-
private var trustRootCache: Option[ByteArray] = None
15-
val getTrustRootCache: Supplier[Optional[ByteArray]] = () =>
16-
trustRootCache.toJava
17-
val setTrustRootCache: Consumer[ByteArray] = trustRoot => {
18-
trustRootCache = Some(trustRoot)
19-
}
20-
21-
private var blobCache: Option[ByteArray] = None
22-
val getBlobCache: Supplier[Optional[ByteArray]] = () => blobCache.toJava
23-
val setBlobCache: Consumer[ByteArray] = blob => { blobCache = Some(blob) }
12+
private val trustRootCacheFile = Path
13+
.of(sys.env.getOrElse("FIDO_MDS_CACHE_DIR", "."), "trust-root-cache.bin")
14+
.toFile
15+
private val blobCacheFile = Path
16+
.of(sys.env.getOrElse("FIDO_MDS_CACHE_DIR", "."), "blob-cache.bin")
17+
.toFile
18+
19+
def trustRootCache: Option[ByteArray] =
20+
cachedDefaultSettingsDownloader
21+
.build()
22+
.readCacheFile(trustRootCacheFile)
23+
.toScala
24+
def blobCache: Option[ByteArray] =
25+
cachedDefaultSettingsDownloader.build().readCacheFile(blobCacheFile).toScala
2426

2527
def cachedDefaultSettingsDownloader
2628
: FidoMetadataDownloader.FidoMetadataDownloaderBuilder =
@@ -30,9 +32,9 @@ object TestCaches {
3032
"Retrieval and use of this BLOB indicates acceptance of the appropriate agreement located at https://fidoalliance.org/metadata/metadata-legal-terms/"
3133
)
3234
.useDefaultTrustRoot()
33-
.useTrustRootCache(getTrustRootCache, setTrustRootCache)
35+
.useTrustRootCacheFile(trustRootCacheFile)
3436
.useDefaultBlob()
35-
.useBlobCache(getBlobCache, setBlobCache)
37+
.useBlobCacheFile(blobCacheFile)
3638

3739
/** Evaluate <code>expr</code> with an exclusive lock on the test cache. */
3840
def cacheSynchronized[A](expr: => A): A = {

webauthn-server-attestation/src/main/java/com/yubico/fido/metadata/FidoMetadataDownloader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ private Optional<MetadataBLOB> loadCachedBlobOnly(X509Certificate trustRootCerti
10481048
});
10491049
}
10501050

1051-
private Optional<ByteArray> readCacheFile(File cacheFile) throws IOException {
1051+
Optional<ByteArray> readCacheFile(File cacheFile) throws IOException {
10521052
if (cacheFile.exists() && cacheFile.canRead() && cacheFile.isFile()) {
10531053
try (FileInputStream f = new FileInputStream(cacheFile)) {
10541054
return Optional.of(readAll(f));

0 commit comments

Comments
 (0)