11package xyz.block.dap
22
3- import com.fasterxml.jackson.annotation.JsonIgnore
3+ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
44import io.ktor.client.HttpClient
55import io.ktor.client.engine.HttpClientEngine
66import io.ktor.client.engine.okhttp.OkHttp
@@ -24,38 +24,66 @@ import java.net.InetAddress
2424import java.net.URL
2525import java.net.UnknownHostException
2626
27- // This implements part of the DAP resolution process.
28- // See [Resolver] for the full resolution process.
29- // See the [DAP spec](https://github.com/TBD54566975/dap#resolution)
30- class RegistryDidResolver (
31- configuration : RegistryDidResolverConfiguration
32- ) {
33- private val engine: HttpClientEngine = configuration.engine ? : OkHttp .create {
34- val appCache = Cache (File (" cacheDir" , " okhttpcache" ), 10 * 1024 * 1024 )
35- val bootstrapClient = OkHttpClient .Builder ().cache(appCache).build()
27+ // This uses a modified version of the scheme used in web5-kt to provide customization
28+ // of the configuration (see `DidWeb`).
29+ // The difference is that we provide two overloaded functions for constructing the resolver,
30+ // which look like two overloaded constructors to the caller.
31+ // This is instead of a single function and the use of the `Default` companion object.
32+ // We also don't need to expose both a `RegistryDidResolver` and `RegistryDidResolverApi`.
3633
37- val dns = DnsOverHttps .Builder ()
38- .client(bootstrapClient)
39- .url(" https://dns.quad9.net/dns-query" .toHttpUrl())
40- .bootstrapDnsHosts(
41- InetAddress .getByName(" 9.9.9.9" ),
42- InetAddress .getByName(" 149.112.112.112" )
43- )
44- .build()
34+ /* *
35+ * A RegistryDidResolver with the default configuration.
36+ */
37+ fun RegistryDidResolver (): RegistryDidResolver = RegistryDidResolver .default
4538
46- val client = bootstrapClient.newBuilder().dns(dns).build()
47- preconfigured = client
48- }
39+ /* *
40+ * Constructs a RegistryDidResolver with the block configuration applied.
41+ */
42+ fun RegistryDidResolver (
43+ blockConfiguration : RegistryDidResolverConfiguration .() -> Unit
44+ ): RegistryDidResolver {
45+ val config = RegistryDidResolverConfiguration ().apply (blockConfiguration)
46+ return RegistryDidResolverImpl (config)
47+ }
4948
50- private val client = HttpClient (engine) {
51- install(ContentNegotiation ) {
52- jackson { mapper }
49+ // This allows the RegistryDidResolver to be sealed
50+ private class RegistryDidResolverImpl (
51+ configuration : RegistryDidResolverConfiguration
52+ ) : RegistryDidResolver(configuration)
53+
54+ /* *
55+ * Given the URL for the DAP registry and a DAP, resolves the DID using the registry.
56+ */
57+ sealed class RegistryDidResolver (
58+ configuration : RegistryDidResolverConfiguration
59+ ) {
60+ companion object {
61+ /* *
62+ * A singleton RegistryDidResolver with the default configuration
63+ */
64+ internal val default: RegistryDidResolver by lazy {
65+ RegistryDidResolverImpl (RegistryDidResolverConfiguration ())
5366 }
5467 }
5568
56- private val mapper = Json .jsonMapper
69+ /* *
70+ * Resolves the DAP using the registry, to retrieve the DID.
71+ * Does NOT verify Any proof in the response from the registry.
72+ */
73+ fun getUnprovenDid (dapRegistryUrl : URL , dap : Dap ): Did =
74+ getDidWithProof(dapRegistryUrl, dap).did
5775
58- fun getDid (dapRegistryUrl : URL , dap : Dap ): Did {
76+ /*
77+ // TODO - implement proof verification of the DID returned by the registry
78+ // TODO - What should this do if there is no proof?
79+ fun getProvableDid(dapRegistryUrl: URL, dap: Dap): Did {
80+ val didWithProof = getDidWithProof(dapRegistryUrl, dap)
81+ // TODO - verify the proof using web5-kt
82+ return didWithProof.did
83+ }
84+ */
85+
86+ internal fun getDidWithProof (dapRegistryUrl : URL , dap : Dap ): DidWithProof {
5987 val fullUrl = URL (" $dapRegistryUrl /daps/${dap.handle} " )
6088
6189 val resp: HttpResponse = try {
@@ -64,46 +92,86 @@ class RegistryDidResolver(
6492 contentType(ContentType .Application .Json )
6593 }
6694 }
67- } catch (e: UnknownHostException ) {
68- throw RegistryDidResolutionException (" Failed to reach DAP Registry " , e)
95+ } catch (e: Throwable ) {
96+ throw RegistryDidResolutionException (" Error fetching DAP from registry " , e)
6997 }
7098
7199 val body = runBlocking { resp.bodyAsText() }
72100 if (! resp.status.isSuccess()) {
73101 throw RegistryDidResolutionException (" Failed to read from DAP registry" )
74102 }
75- val resolutionResponse = mapper.readValue(body, DapRegistryResolutionResponse ::class .java)
76- // TODO - should we verify the proof here?
103+ val resolutionResponse = try {
104+ mapper.readValue(body, DapRegistryResolutionResponse ::class .java)
105+ } catch (e: Throwable ) {
106+ throw RegistryDidResolutionException (" Failed to parse DAP registry response" , e)
107+ }
77108 if (resolutionResponse.did == null ) {
78109 throw RegistryDidResolutionException (" DAP registry did not return a DID" )
79110 }
80111
81- return Did .parse(resolutionResponse.did)
112+ val did = try {
113+ Did .parse(resolutionResponse.did)
114+ } catch (e: Throwable ) {
115+ throw RegistryDidResolutionException (" Failed to parse DID from DAP registry response" , e)
116+ }
117+ return DidWithProof (did, resolutionResponse.proof)
118+ }
119+
120+ private val engine: HttpClientEngine = configuration.engine ? : OkHttp .create {
121+ val appCache = Cache (File (" cacheDir" , " okhttpcache" ), 10 * 1024 * 1024 )
122+ val bootstrapClient = OkHttpClient .Builder ().cache(appCache).build()
123+
124+ val dns = DnsOverHttps .Builder ()
125+ .client(bootstrapClient)
126+ .url(" https://dns.quad9.net/dns-query" .toHttpUrl())
127+ .bootstrapDnsHosts(
128+ InetAddress .getByName(" 9.9.9.9" ),
129+ InetAddress .getByName(" 149.112.112.112" )
130+ )
131+ .build()
132+
133+ val client = bootstrapClient.newBuilder().dns(dns).build()
134+ preconfigured = client
135+ }
136+
137+ private val client = HttpClient (engine) {
138+ install(ContentNegotiation ) {
139+ jackson { mapper }
140+ }
82141 }
142+
143+ private val mapper = Json .jsonMapper
83144}
84145
85- class Proof (
146+ internal data class DidWithProof (
147+ val did : Did ,
148+ val proof : Proof ?
149+ )
150+
151+ @JsonIgnoreProperties(ignoreUnknown = true )
152+ data class Proof (
86153 val id : String ,
87154 val handle : String ,
88155 val did : String ,
89156 val domain : String ,
90157 val signature : String
91158)
92159
93- class DapRegistryResolutionResponse (
160+ @JsonIgnoreProperties(ignoreUnknown = true )
161+ data class DapRegistryResolutionResponse (
94162 val did : String? ,
95- @JsonIgnore val proof : Proof ?
163+ val proof : Proof ?
96164)
97165
166+ /* *
167+ * Configuration options for the [RegistryDidResolver].
168+ *
169+ * - [engine] is used to override the default ktor engine, which is [OkHttp].
170+ */
98171class RegistryDidResolverConfiguration internal constructor(
99172 var engine : HttpClientEngine ? = null
100173)
101174
102- fun RegistryDidResolver (configuration : RegistryDidResolverConfiguration .() -> Unit ): RegistryDidResolver {
103- val config = RegistryDidResolverConfiguration ().apply (configuration)
104- return RegistryDidResolver (config)
105- }
106-
107175class RegistryDidResolutionException : Throwable {
108176 constructor (message: String , cause: Throwable ? ) : super (message, cause)
109177 constructor (message: String ) : super (message)
0 commit comments