@@ -2,6 +2,8 @@ package com.yubico.fido.metadata
22
33import com .fasterxml .jackson .databind .node .IntNode
44import com .fasterxml .jackson .databind .node .ObjectNode
5+ import com .yubico .fido .metadata .FidoMetadataDownloader .CachePolicyDecision
6+ import com .yubico .fido .metadata .FidoMetadataDownloader .FidoMetadataDownloaderBuilder
57import com .yubico .fido .metadata .FidoMetadataDownloaderException .Reason
68import com .yubico .internal .util .BinaryUtil
79import com .yubico .internal .util .JacksonCodecs
@@ -30,6 +32,7 @@ import org.scalatestplus.junit.JUnitRunner
3032import java .io .File
3133import java .io .FileInputStream
3234import java .io .FileOutputStream
35+ import java .io .IOException
3336import java .net .URL
3437import java .nio .charset .StandardCharsets
3538import java .security .DigestException
@@ -52,6 +55,7 @@ import javax.servlet.http.HttpServletResponse
5255import scala .jdk .CollectionConverters .ListHasAsScala
5356import scala .jdk .CollectionConverters .SeqHasAsJava
5457import scala .jdk .CollectionConverters .SetHasAsJava
58+ import scala .util .Failure
5559import scala .util .Success
5660import scala .util .Try
5761
@@ -1090,7 +1094,7 @@ class FidoMetadataDownloaderSpec
10901094 blob.getNo should equal(blobNo)
10911095 }
10921096
1093- it (" The cache is used if the BLOB download fails. " ) {
1097+ describe (" The cache is used if the BLOB download fails" ) {
10941098 val oldBlobNo = 1
10951099 val newBlobNo = 2
10961100
@@ -1120,22 +1124,25 @@ class FidoMetadataDownloaderSpec
11201124 )
11211125 )
11221126
1123- val (server, serverUrl, httpsCert) =
1124- makeHttpServer(
1125- Map (
1126- " /blob.jwt" -> (_ =>
1127- (
1128- HttpStatus .TOO_MANY_REQUESTS_429 ,
1129- newBlobJwt
1130- .getBytes(StandardCharsets .UTF_8 ),
1127+ def test [T ](
1128+ configure : FidoMetadataDownloaderBuilder => T =
1129+ (_ : FidoMetadataDownloaderBuilder ) => {}
1130+ ): MetadataBLOBPayload = {
1131+ val (server, serverUrl, httpsCert) =
1132+ makeHttpServer(
1133+ Map (
1134+ " /blob.jwt" -> (_ =>
1135+ (
1136+ HttpStatus .TOO_MANY_REQUESTS_429 ,
1137+ newBlobJwt
1138+ .getBytes(StandardCharsets .UTF_8 ),
1139+ )
11311140 )
11321141 )
11331142 )
1134- )
1135- startServer(server)
1143+ startServer(server)
11361144
1137- val blob = load(
1138- FidoMetadataDownloader
1145+ val builder = FidoMetadataDownloader
11391146 .builder()
11401147 .expectLegalHeader(
11411148 " Kom ihåg att du aldrig får snyta dig i mattan!"
@@ -1152,10 +1159,39 @@ class FidoMetadataDownloaderSpec
11521159 .clock(Clock .fixed(CertValidFrom , ZoneOffset .UTC ))
11531160 .useCrls(crls.asJava)
11541161 .trustHttpsCerts(httpsCert)
1155- .build()
1156- ).getPayload
1157- blob should not be null
1158- blob.getNo should equal(oldBlobNo)
1162+ configure(builder)
1163+ load(builder.build()).getPayload
1164+ }
1165+
1166+ it(" with the default settings." ) {
1167+ val blob = test()
1168+ blob should not be null
1169+ blob.getNo should equal(oldBlobNo)
1170+ }
1171+
1172+ it(" and the cache policy allows it." ) {
1173+ val blob = test(builder => {
1174+ builder.cachePolicy(_ => CachePolicyDecision .USE_CACHED )
1175+ })
1176+ blob should not be null
1177+ blob.getNo should equal(oldBlobNo)
1178+ }
1179+
1180+ it(" unless the cache policy decides to re-throw." ) {
1181+ val blob = Try (test(builder => {
1182+ builder.cachePolicy(_ => CachePolicyDecision .THROW )
1183+ }))
1184+ blob shouldBe a[Failure [_]]
1185+ blob.failed.get shouldBe an[IOException ]
1186+ }
1187+
1188+ it(" unless the cache policy returns null." ) {
1189+ val blob = Try (test(builder => {
1190+ builder.cachePolicy(_ => null )
1191+ }))
1192+ blob shouldBe a[Failure [_]]
1193+ blob.failed.get shouldBe a[NullPointerException ]
1194+ }
11591195 }
11601196 }
11611197
@@ -1806,7 +1842,7 @@ class FidoMetadataDownloaderSpec
18061842 blob.getNo should equal(oldBlobNo)
18071843 }
18081844
1809- it (" A newly downloaded BLOB is disregarded if it has an invalid signature but the cached one has a valid signature. " ) {
1845+ describe (" A newly downloaded BLOB is disregarded if it has an invalid signature but the cached one has a valid signature" ) {
18101846 val oldBlobNo = 1
18111847 val newBlobNo = 2
18121848
@@ -1858,32 +1894,70 @@ class FidoMetadataDownloaderSpec
18581894 )
18591895 .mkString(" ." )
18601896
1861- val (server, serverUrl, httpsCert) =
1862- makeHttpServer(" /blob.jwt" , badNewBlobJwt)
1863- startServer(server)
1897+ def test [T ](
1898+ configure : FidoMetadataDownloaderBuilder => T =
1899+ (_ : FidoMetadataDownloaderBuilder ) => {}
1900+ ): MetadataBLOBPayload = {
1901+ val (server, serverUrl, httpsCert) =
1902+ makeHttpServer(" /blob.jwt" , badNewBlobJwt)
1903+ startServer(server)
18641904
1865- val blob = load(
1866- FidoMetadataDownloader
1867- .builder()
1868- .expectLegalHeader(
1869- " Kom ihåg att du aldrig får snyta dig i mattan!"
1870- )
1871- .useTrustRoot(trustRootCert)
1872- .downloadBlob(new URL (s " ${serverUrl}/blob.jwt " ))
1873- .useBlobCache(
1874- () =>
1875- Optional .of(
1876- new ByteArray (oldBlobJwt.getBytes(StandardCharsets .UTF_8 ))
1877- ),
1878- _ => {},
1905+ val builder =
1906+ FidoMetadataDownloader
1907+ .builder()
1908+ .expectLegalHeader(
1909+ " Kom ihåg att du aldrig får snyta dig i mattan!"
1910+ )
1911+ .useTrustRoot(trustRootCert)
1912+ .downloadBlob(new URL (s " ${serverUrl}/blob.jwt " ))
1913+ .useBlobCache(
1914+ () =>
1915+ Optional .of(
1916+ new ByteArray (oldBlobJwt.getBytes(StandardCharsets .UTF_8 ))
1917+ ),
1918+ _ => {},
1919+ )
1920+ .clock(Clock .fixed(CertValidFrom , ZoneOffset .UTC ))
1921+ .useCrls(crls.asJava)
1922+ .trustHttpsCerts(httpsCert)
1923+ configure(builder)
1924+ load(builder.build()).getPayload
1925+ }
1926+
1927+ it(" under the default settings." ) {
1928+ val blob = test()
1929+ blob should not be null
1930+ blob.getNo should equal(oldBlobNo)
1931+ }
1932+
1933+ it(" if the cache policy allows falling back to the cache." ) {
1934+ val blob = test(builder => {
1935+ builder.cachePolicy((_ : Exception ) =>
1936+ CachePolicyDecision .USE_CACHED
18791937 )
1880- .clock(Clock .fixed(CertValidFrom , ZoneOffset .UTC ))
1881- .useCrls(crls.asJava)
1882- .trustHttpsCerts(httpsCert)
1883- .build()
1884- ).getPayload
1885- blob should not be null
1886- blob.getNo should equal(oldBlobNo)
1938+ })
1939+ blob should not be null
1940+ blob.getNo should equal(oldBlobNo)
1941+ }
1942+
1943+ it(" unless the cache policy decides to re-throw." ) {
1944+ val blob = Try (test(builder => {
1945+ builder.cachePolicy((_ : Exception ) => CachePolicyDecision .THROW )
1946+ }))
1947+ blob shouldBe a[Failure [_]]
1948+ blob.failed.get shouldBe a[FidoMetadataDownloaderException ]
1949+ blob.failed.get
1950+ .asInstanceOf [FidoMetadataDownloaderException ]
1951+ .getReason should be(Reason .BAD_SIGNATURE )
1952+ }
1953+
1954+ it(" unless the cache policy returns null." ) {
1955+ val blob = Try (test(builder => {
1956+ builder.cachePolicy((_ : Exception ) => null )
1957+ }))
1958+ blob shouldBe a[Failure [_]]
1959+ blob.failed.get shouldBe a[NullPointerException ]
1960+ }
18871961 }
18881962
18891963 it(" If verifyDownloadsOnly is not set, a cached BLOB may expire." ) {
0 commit comments