-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
55 changed files
with
2,105 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
44 changes: 44 additions & 0 deletions
44
credential-status/src/main/java/me/uport/credential_status/StatusResolver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
57 changes: 57 additions & 0 deletions
57
credential-status/src/main/java/me/uport/credential_status/UniversalStatusResolver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
credential-status/src/main/java/me/uport/credential_status/Utils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] ?: "" | ||
) | ||
} |
89 changes: 89 additions & 0 deletions
89
...ntial-status/src/test/java/me/uport/sdk/credential_status/UniversalStatusResolverTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.