Skip to content

Commit

Permalink
Merge pull request #34 from uport-project/uport_project/support/depre…
Browse files Browse the repository at this point in the history
…cate-universaldid-singleton

UniversalDID deprecation
  • Loading branch information
mirceanis authored Dec 3, 2019
2 parents fc507c2 + e60035c commit 74dd14a
Show file tree
Hide file tree
Showing 12 changed files with 484 additions and 41 deletions.
39 changes: 1 addition & 38 deletions jwt/src/main/java/me/uport/sdk/jwt/JWTTools.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import kotlinx.serialization.json.JsonException
import me.uport.sdk.core.*
import me.uport.sdk.ethrdid.EthrDIDNetwork
import me.uport.sdk.ethrdid.EthrDIDResolver
import me.uport.sdk.httpsdid.WebDIDResolver
import me.uport.sdk.jsonrpc.JsonRPC
import me.uport.sdk.jwt.JWTUtils.Companion.normalizeKnownDID
import me.uport.sdk.jwt.JWTUtils.Companion.splitToken
import me.uport.sdk.jwt.model.ArbitraryMapSerializer
Expand All @@ -25,7 +21,6 @@ import me.uport.sdk.universaldid.PublicKeyType.Companion.EcdsaPublicKeySecp256k1
import me.uport.sdk.universaldid.PublicKeyType.Companion.Secp256k1SignatureVerificationKey2018
import me.uport.sdk.universaldid.PublicKeyType.Companion.Secp256k1VerificationKey2018
import me.uport.sdk.universaldid.UniversalDID
import me.uport.sdk.uportdid.UportDIDResolver
import org.kethereum.crypto.toAddress
import org.kethereum.encodings.decodeBase58
import org.kethereum.extensions.toBigInteger
Expand Down Expand Up @@ -67,39 +62,8 @@ typealias VerificationMethod = (
* It defaults to `null`
*/
class JWTTools(
private val timeProvider: ITimeProvider = SystemTimeProvider,
preferredNetwork: EthNetwork? = null
private val timeProvider: ITimeProvider = SystemTimeProvider
) {
init {

// blank did declarations
val blankUportDID = "did:uport:2nQs23uc3UN6BBPqGHpbudDxBkeDRn553BB"
val blankEthrDID = "did:ethr:0x0000000000000000000000000000000000000000"
val blankHttpsDID = "did:https:example.com"

// register default Ethr DID resolver if Universal DID is unable to resolve blank Ethr DID
if (!UniversalDID.canResolve(blankEthrDID)) {
val defaultRPC = JsonRPC(preferredNetwork?.rpcUrl ?: Networks.mainnet.rpcUrl)
val defaultRegistry = preferredNetwork?.ethrDidRegistry
?: Networks.mainnet.ethrDidRegistry
UniversalDID.registerResolver(
EthrDIDResolver.Builder()
.addNetwork(EthrDIDNetwork("", defaultRegistry, defaultRPC, "0x1"))
.build()
)
}

// register default Uport DID resolver if Universal DID is unable to resolve blank Uport DID
if (!UniversalDID.canResolve(blankUportDID)) {
val defaultRPC = JsonRPC(preferredNetwork?.rpcUrl ?: Networks.rinkeby.rpcUrl)
UniversalDID.registerResolver(UportDIDResolver(defaultRPC))
}

// register default https DID resolver if Universal DID is unable to resolve blank https DID
if (!UniversalDID.canResolve(blankHttpsDID)) {
UniversalDID.registerResolver(WebDIDResolver())
}
}

/**
* This coroutine method creates a signed JWT from a [payload] Map and an abstracted [Signer]
Expand Down Expand Up @@ -380,7 +344,6 @@ class JWTTools(
} else {
return matches.isNotEmpty()
}

}

private fun verifyRecoverableES256K(
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ include ':uport-did'
include ':ethr-did'
include ':universal-did'
include ':jwt'
include ':uport-defaults'

include ':jwt-test'
2 changes: 1 addition & 1 deletion universal-did/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialization_version"

testImplementation "junit:junit:$junit_version"
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
testImplementation "com.github.uport-project.kotlin-common:test-helpers:$uport_kotlin_common_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,34 @@ interface DIDResolver {
* Check if the [potentialDID] can be resolved by this resolver.
*/
fun canResolve(potentialDID: String): Boolean
}

/**
*
* Builds a [DIDResolverImpl]
* This class creates an[DIDResolverImpl] object and enables the registration of [DIDResolver] using the Builder pattern
*
*/
class Builder {

private val didResolver = DIDResolverImpl()

/**
*
* Register's a [DIDResolver] to the [DIDResolverImpl]
* @return this [Builder] instance
*
*/
fun addResolver(resolver: DIDResolver): Builder {
didResolver.registerResolver(resolver)
return this
}

/**
* @return returns the configured [DIDResolver] object
*/
fun build(): DIDResolver {
return didResolver
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package me.uport.sdk.universaldid

internal class DIDResolverImpl: DIDResolver {

private val resolvers = mapOf<String, DIDResolver>().toMutableMap()

/**
* Register a resolver for a particular DID [method]
*/
fun registerResolver(resolver: DIDResolver) {
if (resolver.method.isBlank()) {
return
}
resolvers[resolver.method] = resolver
}

/**
* @hide
*/
fun clearResolvers() = resolvers.clear()

/**
* This universal resolver can't be used for any one particular did but for all [DIDResolver]s
* that have been added using [registerResolver]
*/
override val method: String = ""

/**
* Checks if any of the registered resolvers can resolve
*/
override fun canResolve(potentialDID: String): Boolean {
val resolver = resolvers.values.find {
it.canResolve(potentialDID)
}
return (resolver != null)
}

/**
* Looks for a [DIDResolver] that can resolve the provided [did] either by method if the did contains one or by trial
*
* @throws IllegalStateException if the proper resolver is not registered or produces `null`
* @throws IllegalArgumentException if the given [did] has no `method` but could be resolved by one of the registered resolvers and that one fails with `null`
*/
override suspend fun resolve(did: String): DIDDocument {
val (method, _) = parse(did)

if (method.isBlank() || !resolvers.containsKey(method)) {
//if there is no clear mapping to a resolver, try each one that claims it can resolve
return resolvers.filterValues {
it.canResolve(did)
}.values.mapNotNull {
try {
it.resolve(did)
} catch (ex: Exception) {
null
}
}.firstOrNull()
?: throw IllegalArgumentException("The provided did ($did) could not be resolved by any of the ${resolvers.size} registered resolvers")
} //no else clause, carry on

if (resolvers.containsKey(method)) {
return resolvers[method]?.resolve(did)
?: throw IllegalStateException("There DIDResolver for '$method' failed to resolve '$did' for an unknown reason.")
} else {
throw IllegalStateException("There is no DIDResolver registered to resolve '$method' DIDs and none of the other ${resolvers.size} registered ones can do it.")
}
}

/**
* @hide
*/
internal fun parse(did: String): Pair<String, String> {
val matchResult = didPattern.find(did) ?: return ("" to "")
val (method, identifier) = matchResult.destructured
return (method to identifier)
}

//language=RegExp
private val didPattern = "^did:(.*?):(.+)".toRegex()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ import me.uport.sdk.universaldid.UniversalDID.registerResolver
*
* Known implementations of [DIDResolver] are [ethr-did] and [uport-did]
*/
@Suppress("DEPRECATION")
@Deprecated(
"Resolving DIDs using the UniversalDID singleton is deprecated " +
"in favor of using the DIDResolver Builder." +
"This will be removed in the next major release.",
ReplaceWith(
"""val resolver : DIDResolver = DIDResolver.Builder
.addResolver(ethrDidResolver)
.addResolver(uportDidResolver)
.addResolver(webDidResolver)
.build()"""
)
)
object UniversalDID : DIDResolver {

private val resolvers = mapOf<String, DIDResolver>().toMutableMap()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package me.uport.sdk.universaldid

import assertk.assertThat
import assertk.assertions.isEmpty
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import kotlinx.coroutines.runBlocking
import me.uport.sdk.testhelpers.coAssert
import org.junit.Test

class DIDResolverImplTest {

private val testDDO = object : DIDDocument {
override val context: String = "test context"
override val id: String = "1234"
override val publicKey: List<PublicKeyEntry> = emptyList()
override val authentication: List<AuthenticationEntry> = emptyList()
override val service: List<ServiceEntry> = emptyList()
}

private val testResolver = object : DIDResolver {
override fun canResolve(potentialDID: String): Boolean = true

override val method: String = "test"

override suspend fun resolve(did: String): DIDDocument {
return if (did.contains("test")) testDDO else throw IllegalArgumentException("can't use test resolver")
}
}

@Test
fun `blank resolves to error`() {

val resolver = DIDResolverImpl()
resolver.clearResolvers()

coAssert {
resolver.resolve("")
}.thrownError {
isInstanceOf(IllegalArgumentException::class)
}
}

@Test
fun `testResolver resolves to error with blank`() {

val resolver = DIDResolverImpl()
resolver.clearResolvers()
resolver.registerResolver(testResolver)

coAssert {
resolver.resolve("")
}.thrownError {
isInstanceOf(IllegalArgumentException::class)
}
}

@Test
fun `can register and find resolver`() = runBlocking {

val resolver = DIDResolverImpl()
resolver.clearResolvers()
resolver.registerResolver(testResolver)

val ddo = resolver.resolve("did:test:this is a test did")
assertThat(ddo).isEqualTo(testDDO)
}

private val validDIDs = listOf(
"did:generic:0x0011223344556677889900112233445566778899",
"did:generic:01234",
"did:generic:has spaces",
"did:generic:more:colons",
"did:generic:01234#fragment-attached",
"did:generic:01234?key=value",
"did:generic:01234?key=value&other-key=other-value"
)

private val invalidDIDs = listOf(
"",
"0x0011223344556677889900112233445566778899",
"ethr:0x0011223344556677889900112233445566778899",
"did:ethr",
"did::something",
"did:ethr:"
)

@Test
fun `parses dids correctly`() {

val resolver = DIDResolverImpl()

validDIDs.forEach {
val (method, _) = resolver.parse(it)
assertThat(method).isEqualTo("generic")
}

invalidDIDs.forEach {
val (method, _) = resolver.parse(it)
assertThat(method).isEmpty()
}
}

@Test
fun `serializes to basic doc`() {
val docString = DIDDocumentImpl("test context", "example document").toJson()
//language=json
assertThat(docString).isEqualTo("""{"@context":"test context","id":"example document","publicKey":[],"authentication":[],"service":[]}""")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class UniversalDIDTest {
override suspend fun resolve(did: String): DIDDocument {
return if (did.contains("test")) testDDO else throw IllegalArgumentException("can't use test resolver")
}

}

@Test
Expand Down
21 changes: 21 additions & 0 deletions uport-defaults/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apply plugin: 'java-library'
apply plugin: "kotlin"
apply plugin: "maven"
apply plugin: "com.jfrog.bintray"

project.ext.description = "Default values and configuration for DIDResolver"


dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
api "com.github.uport-project.kotlin-common:jsonrpc:$uport_kotlin_common_version"
api "com.github.uport-project.kotlin-common:signer-common:$uport_kotlin_common_version"

api project(":universal-did")
api project(":uport-did")
api project(":ethr-did")
api project(":web-did")

testImplementation "junit:junit:$junit_version"
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
}
Loading

0 comments on commit 74dd14a

Please sign in to comment.