Skip to content

Commit

Permalink
Merge branch 'release/0.3.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
mirceanis committed Mar 20, 2020
2 parents c28cafd + d6c3b1c commit c269b10
Show file tree
Hide file tree
Showing 55 changed files with 2,105 additions and 241 deletions.
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ Support for other DID methods should be simple.
Write a DID resolver supporting the
[`DIDResolver`](https://github.com/uport-project/kotlin-did-jwt/blob/master/universal-did/src/main/java/me/uport/sdk/universaldid/DIDResolver.kt)
interface.
Install it using `UniversalDID.registerResolver(<your own resolver implementation>)`
Install it using
```kotlin
val resolver : DIDResolver = DIDResolver.Builder
.addResolver(ethrDidResolver)
.addResolver(/*...*/)
.build()
```

Once you've verified that it works, please add a PR adding it to the above list so people can find it.

If your DID method requires a different signing algorithm than what is already supported,
Expand All @@ -56,7 +63,7 @@ allprojects {
In your application `build.gradle` file, add:

```groovy
def did_jwt_version = "0.3.2"
def did_jwt_version = "0.3.5"
dependencies {
//...
implementation "com.github.uport-project.kotlin-did-jwt:jwt:$did_jwt_version"
Expand Down Expand Up @@ -111,12 +118,18 @@ but that is a more rigid structure and will be phased away in future releases.


```kotlin
val payload : JwtPayload = JwtTools().verify("<token>")
val resolver = EthrDIDResolver.Builder()
.addNetwork(EthrDIDNetwork("<name>", "<registryAddress>", "<JsonRPC>"))
.build()

val payload : JwtPayload = JWTTools().verify("<token>", resolver)
```

If the token is valid, the method returns the decoded payload,
otherwise throws a `InvalidJWTException` or `JWTEncodingException`

The function requires a DIDResolver which will be used to resolve DIDs during the verification

Verifying a token means checking that the signature was produced by a
key associated with the issuer DID (`iss` field).

Expand All @@ -136,6 +149,11 @@ so that only tokens intended for your app are considered valid.

## CHANGELOG

* 0.3.5
- feat: add credential status / revocation support (#35)(#42)
- support: bump dependencies (#44)
* 0.3.4
- feat: deprecate UniversalResolver singleton (#31)(#34)(#37)
* 0.3.3
- refactor: use kethereum 0.76.2 ( 1f730e39 )
- bugfix: resolve publicKey entries with null chars in their names ( #27 )
Expand Down
20 changes: 11 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@
buildscript {

ext {
kotlin_version = '1.3.50'
kotlin_serialization_version = '0.12.0'
coroutines_version = "1.3.0"
kotlin_version = '1.3.70'
kotlin_serialization_version = '0.20.0'
coroutines_version = "1.3.5"

junit_version = "4.12"
mockk_version = "1.9.3"
assertk_version = "0.13"
detekt_version = "1.0.0-RC14"
jacoco_version = "0.8.4"

okhttp_version = "3.14.1"
okhttp_version = "4.4.1"

bivrost_version = "v0.7.1"
kmnid_version = "0.4.2"
kethereum_version = "0.76.2"
khex_version = "1.0.0-RC3"
kmnid_version = "0.4.4"
kethereum_version = "0.81.4"
khex_version = "1.0.0-RC6"
khash_version = "1.0.0-RC5"
kbase58_version = "0.1"
spongycastle_version = "1.58.0.0"
uport_kotlin_common_version = "0.3.2"
uport_kotlin_common_version = "0.4.2"

current_release_version = "0.3.3"
current_release_version = "0.3.5"
}

repositories {
Expand Down
2 changes: 2 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignore:
- "ethr-status/src/main/java/me/uport/sdk/ethr_status/Revocation.kt"
16 changes: 16 additions & 0 deletions credential-status/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apply plugin: "java-library"
apply plugin: "kotlin"
apply plugin: "maven"

project.ext.description = "Wrapper for implementing credential statuses"

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

api project(":jwt")
api "com.github.uport-project.kotlin-common:test-helpers:$uport_kotlin_common_version"

testImplementation "junit:junit:$junit_version"
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package me.uport.credential_status

import me.uport.sdk.universaldid.DIDDocument

/**
* Generic interface representing a credential-status result
**/
interface CredentialStatus


/**
* Represents a status method entry that could be embedded in a W3C Verifiable Credential.
* Normally, only credentials that list a status method would need to be verified by it.
*
* ex:
* ```json
* status : { type: "EthrStatusRegistry2019", id: "rinkeby:0xregistryAddress" }
* ```
*
*/
data class StatusEntry(val type: String, val id: String)


/**
*
* The interface expected for status resolvers.
* `checkStatus` should be called with a raw credential and it should return a [CredentialStatus] result.
* It is advisable that classes that implement this interface also provide a way to easily register the correct
* Status method type.
*
*/
interface StatusResolver {

/*
* Holds the name of the method expected to be used in checking the credential-status
*/
val method: String

/**
*
* Checks the status of a given credential and returns a [CredentialStatus] or throws an error
*/
suspend fun checkStatus(credential: String, didDoc: DIDDocument): CredentialStatus
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package me.uport.credential_status

import me.uport.sdk.universaldid.DIDDocument
import me.uport.sdk.universaldid.UniversalDID.method
import me.uport.sdk.universaldid.UniversalDID.registerResolver

/**
*
* A class to abstract resolving credential statuses
* from specific implementations based on the [method] component of a resolver
*
* [StatusResolver] implementations need to be registered using [registerResolver]
*
* Known implementations of [StatusResolver] are [ethr-status]
*
*/
class UniversalStatusResolver : StatusResolver {


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


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


/**
* Looks for a [StatusResolver] that can check status using the provided [method]
*
* @throws IllegalStateException if the proper resolver is not registered or produces `null`
*/
override suspend fun checkStatus(credential: String, didDoc: DIDDocument): CredentialStatus {

val statusEntry = getStatusEntry(credential)

if (statusEntry.type.isBlank() || !resolvers.containsKey(statusEntry.type)) {
throw IllegalStateException("There is no StatusResolver registered to check status using '${statusEntry.type}' method.")
} else {
return resolvers[statusEntry.type]?.checkStatus(credential, didDoc)
?: throw IllegalStateException("There StatusResolver for '$statusEntry.type' failed to resolve for an unknown reason.")
}
}


/**
* Register a resolver for a particular [method]
*/
fun registerResolver(resolver: StatusResolver) {
if (resolver.method.isBlank()) {
return
}
resolvers[resolver.method] = resolver
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package me.uport.credential_status

import me.uport.sdk.jwt.JWTTools

/**
*
* Convenience method which is used to extract the status entry from a credential
*/
fun getStatusEntry(credential: String): StatusEntry {
val (_, payloadRaw) = JWTTools().decodeRaw(credential)
val status = payloadRaw["status"] as Map<String, String>?
?: throw IllegalArgumentException("No entry for status found in jwt")

return StatusEntry(
status["type"] ?: "",
status["id"] ?: ""
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@file:Suppress("UndocumentedPublicFunction", "UndocumentedPublicClass")

package me.uport.sdk.credential_status

import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import kotlinx.coroutines.runBlocking
import me.uport.credential_status.CredentialStatus
import me.uport.credential_status.StatusResolver
import me.uport.credential_status.UniversalStatusResolver
import me.uport.sdk.ethrdid.EthrDIDDocument
import me.uport.sdk.testhelpers.coAssert
import me.uport.sdk.universaldid.DIDDocument
import org.junit.Test
import java.math.BigInteger

class UniversalStatusResolverTests {

private val successfulCred =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJzdGF0dXMiOnsidHlwZSI6InRlc3QiLCJpZCI6InRlc3Q6MHgxMjM0NSJ9fQ.MFTcBa_41fN5NoNYxTNYi9WmssHIOHz7Y8CPTDTpG-E"

private val didDoc = EthrDIDDocument.fromJson(
"""{
"id": "did:ethr:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74",
"publicKey": [],
"authentication": [{
"type": "Secp256k1SignatureAuthentication2018",
"publicKey": "did:ethr:0xf3beac30c498d9e26865f34fcaa57dbb935b0d74#owner"
}],
"service": [],
"@context": "https://w3id.org/did/v1"
}"""
)

private val testResolver = object : StatusResolver {

override val method: String = "test"

override suspend fun checkStatus(
credential: String,
didDoc: DIDDocument
): CredentialStatus {
if (credential == successfulCred) {
return TestStatus(BigInteger.ONE)
} else {
throw IllegalStateException("")
}
}
}

private data class TestStatus(
val blockNumber: BigInteger
) : CredentialStatus

@Test
fun `throws error when no resolvers registered`() = runBlocking {
coAssert {
UniversalStatusResolver().checkStatus(
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJpYXQiOjE1NzMwNDczNTEsInN0YXR1cyI6eyJ0eXBlIjoiRXRoclN0YXR1c1JlZ2lzdHJ5MjAxOSIsImlkIjoicmlua2VieToweDFFNDY1MWRjYTVFZjM4NjM2ZTJFNEQ3QTZGZjRkMjQxM2ZDNTY0NTAifSwiaXNzIjoiZGlkOmV0aHI6MHgxZmNmOGZmNzhhYzUxMTdkOWM5OWI4MzBjNzRiNjY2OGQ2YWMzMjI5In0.MHabafA0UxJuQJ0Z-7Egb57WRlgj4_zf96B0LUhRyXgVDU5RABIczTTTXWjcuKVzhJc_-FuhRI8uQYmQQNxKzgA"
, didDoc
)
}.thrownError {
isInstanceOf(IllegalStateException::class)
}
}

@Test
fun `throws error when status entry is blank`() = runBlocking {
val resolver = UniversalStatusResolver()
resolver.registerResolver(testResolver)
coAssert {
resolver.checkStatus(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
didDoc
)
}.thrownError {
isInstanceOf(IllegalArgumentException::class)
}
}

@Test
fun `can register resolvers and use to check status`() = runBlocking {
val resolver = UniversalStatusResolver()
resolver.registerResolver(testResolver)
val result = resolver.checkStatus(successfulCred, didDoc)
assertThat(result).isEqualTo(TestStatus(BigInteger.ONE))
}
}
13 changes: 7 additions & 6 deletions ethr-did/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$kotlin_serialization_version"

implementation "com.github.gnosis.bivrost-kotlin:bivrost-solidity-types:$bivrost_version"
api "com.github.gnosis.bivrost-kotlin:bivrost-solidity-types:$bivrost_version"

api "com.github.komputing.kethereum:extensions:$kethereum_version"
api "com.github.komputing.kethereum:extensions_kotlin:$kethereum_version"
api "com.github.komputing.kethereum:model:$kethereum_version"
api "com.github.komputing.khex:extensions-jvm:$khex_version"
implementation "com.github.komputing.kethereum:base58:$kethereum_version"
implementation "com.github.komputing.kethereum:crypto:$kethereum_version"
implementation "com.github.komputing.kethereum:crypto_impl_spongycastle:$kethereum_version"
api "com.github.komputing.khex:extensions:$khex_version"
api "com.github.komputing:kbase58:$kbase58_version"
api "com.github.komputing.kethereum:crypto:$kethereum_version"
api "com.github.komputing.kethereum:crypto_impl_spongycastle:$kethereum_version"

api "com.github.uport-project.kotlin-common:core:$uport_kotlin_common_version"
api "com.github.uport-project.kotlin-common:jsonrpc:$uport_kotlin_common_version"
Expand All @@ -32,4 +32,5 @@ dependencies {
testImplementation "com.willowtreeapps.assertk:assertk-jvm:$assertk_version"
testImplementation project(":jwt-test")
testImplementation "com.github.uport-project.kotlin-common:test-helpers:$uport_kotlin_common_version"
testImplementation "com.squareup.okhttp3:okhttp:$okhttp_version"
}
Loading

0 comments on commit c269b10

Please sign in to comment.