Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
nitro-neal committed Oct 24, 2023
1 parent d145bd4 commit c9bfb61
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 34 deletions.
1 change: 1 addition & 0 deletions credentials/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies {
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-jackson:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("io.ktor:ktor-client-logging:$ktor_version")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package web5.sdk.credentials

import com.danubetech.verifiablecredentials.CredentialSubject
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.ResponseException
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.isSuccess
import io.ktor.serialization.jackson.jackson
import kotlinx.coroutines.runBlocking
import web5.sdk.dids.DidResolvers
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.net.URI
Expand Down Expand Up @@ -68,7 +73,7 @@ public object StatusListCredential {
statusListCredentialId: String,
issuer: String,
statusPurpose: StatusPurpose,
issuedCredentials: List<VerifiableCredential>
issuedCredentials: Iterable<VerifiableCredential>
): VerifiableCredential {
val statusListIndexes: List<String>
val bitString: String
Expand All @@ -80,6 +85,24 @@ public object StatusListCredential {
throw RuntimeException("An error occurred during the creation of the status list credential: ${e.message}", e)
}

try {
URI.create(statusListCredentialId)
} catch (e: Exception) {
throw IllegalArgumentException("status list credential id is not a valid URI", e)

Check warning on line 91 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L90-L91

Added lines #L90 - L91 were not covered by tests
}

try {
URI.create(issuer)
} catch (e: Exception) {
throw IllegalArgumentException("issuer is not a valid URI", e)

Check warning on line 97 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L96-L97

Added lines #L96 - L97 were not covered by tests
}

try {
DidResolvers.resolve(issuer)
} catch (e: Exception) {
throw IllegalArgumentException("issuer: $issuer not resolvable", e)

Check warning on line 103 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L102-L103

Added lines #L102 - L103 were not covered by tests
}

val claims = mapOf(STATUS_PURPOSE to statusPurpose.toString().lowercase(), ENCODED_LIST to bitString)
val credSubject = CredentialSubject.builder()
.id(URI.create(statusListCredentialId))
Expand Down Expand Up @@ -123,23 +146,23 @@ public object StatusListCredential {
val statusListCredStatusPurpose: String? =
statusListCredential.vcDataModel.credentialSubject.jsonObject[STATUS_PURPOSE] as? String?

if (statusListEntryValue.statusPurpose == null) {
throw IllegalArgumentException("Status purpose in the credential to validate is null")
require(statusListEntryValue.statusPurpose != null) {
"Status purpose in the credential to validate is null"

Check warning on line 150 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L150

Added line #L150 was not covered by tests
}

if (statusListCredStatusPurpose == null) {
throw IllegalArgumentException("Status purpose in the status list credential is null")
require(statusListCredStatusPurpose != null) {
"Status purpose in the status list credential is null"

Check warning on line 154 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L154

Added line #L154 was not covered by tests
}

if (statusListEntryValue.statusPurpose != statusListCredStatusPurpose) {
throw IllegalArgumentException("Status purposes do not match between the credentials")
require(statusListEntryValue.statusPurpose == statusListCredStatusPurpose) {
"Status purposes do not match between the credentials"

Check warning on line 158 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L158

Added line #L158 was not covered by tests
}

val compressedBitstring: String? =
statusListCredential.vcDataModel.credentialSubject.jsonObject[ENCODED_LIST] as? String?

if (compressedBitstring == null || compressedBitstring.isEmpty()) {
throw IllegalArgumentException("Compressed bitstring is null or empty")
require(!compressedBitstring.isNullOrEmpty()) {
"Compressed bitstring is null or empty"

Check warning on line 165 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L165

Added line #L165 was not covered by tests
}

val credentialIndex = statusListEntryValue.statusListIndex
Expand All @@ -165,30 +188,35 @@ public object StatusListCredential {
* val isRevoked = validateCredentialInStatusList(credentialToCheck)
* ```
*/
public suspend fun validateCredentialInStatusList(
public fun validateCredentialInStatusList(
credentialToValidate: VerifiableCredential,
httpClient: HttpClient? = null // default HTTP client but can be overridden

Check warning on line 193 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L193

Added line #L193 was not covered by tests
): Boolean {

var isDefaultClient = false
val clientToUse = httpClient ?: defaultHttpClient().also { isDefaultClient = true }

try {
val statusListEntryValue: StatusList2021Entry =
StatusList2021Entry.fromJsonObject(credentialToValidate.vcDataModel.credentialStatus.jsonObject)
val statusListCredential =
clientToUse.fetchStatusListCredential(statusListEntryValue.statusListCredential.toString())

return validateCredentialInStatusList(credentialToValidate, statusListCredential)
} finally {
if (isDefaultClient) {
clientToUse.close()
return runBlocking {
var isDefaultClient = false
val clientToUse = httpClient ?: defaultHttpClient().also { isDefaultClient = true }

try {
val statusListEntryValue: StatusList2021Entry =
StatusList2021Entry.fromJsonObject(credentialToValidate.vcDataModel.credentialStatus.jsonObject)
val statusListCredential =
clientToUse.fetchStatusListCredential(statusListEntryValue.statusListCredential.toString())

return@runBlocking validateCredentialInStatusList(credentialToValidate, statusListCredential)
} finally {
if (isDefaultClient) {
clientToUse.close()

Check warning on line 208 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L208

Added line #L208 was not covered by tests
}
}
}
}

private fun defaultHttpClient(): HttpClient {
return HttpClient(CIO)
return HttpClient(CIO) {
install(ContentNegotiation) {
jackson { jacksonObjectMapper() }

Check warning on line 217 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L215-L217

Added lines #L215 - L217 were not covered by tests
}
}
}

private suspend fun HttpClient.fetchStatusListCredential(url: String): VerifiableCredential {
Expand Down Expand Up @@ -217,7 +245,7 @@ public object StatusListCredential {
*/
private fun prepareCredentialsForStatusList(
statusPurpose: StatusPurpose,
credentials: List<VerifiableCredential>
credentials: Iterable<VerifiableCredential>
): List<String> {
val duplicateSet = mutableSetOf<String>()
for (vc in credentials) {
Expand Down Expand Up @@ -257,16 +285,16 @@ public object StatusListCredential {
for (index in statusListIndexes) {
val indexInt = index.toIntOrNull()

if (indexInt == null || indexInt < 0) {
throw IllegalArgumentException("invalid status list index: $index")
require(indexInt != null && indexInt >= 0) {
"invalid status list index: $index"
}

if (indexInt >= bitSetSize) {
require(indexInt < bitSetSize) {
throw IndexOutOfBoundsException("invalid status list index: $index, index is larger than the bitset size")
}

if (!duplicateCheck.add(indexInt)) {
throw IllegalArgumentException("duplicate status list index value found: $indexInt")
require(duplicateCheck.add(indexInt)) {
"duplicate status list index value found: $indexInt"

Check warning on line 297 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L297

Added line #L297 was not covered by tests
}

bitSet.set(indexInt)
Expand All @@ -291,8 +319,8 @@ public object StatusListCredential {
val decoded: ByteArray
try {
decoded = Base64.getDecoder().decode(compressedBitstring)
} catch (e: IllegalArgumentException) {
throw Exception("decoding compressed bitstring", e)
} catch (e: Exception) {
throw RuntimeException("decoding compressed bitstring", e)

Check warning on line 323 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L322-L323

Added lines #L322 - L323 were not covered by tests
}

val bitstringInputStream = ByteArrayInputStream(decoded)
Expand All @@ -301,7 +329,7 @@ public object StatusListCredential {
try {
GZIPInputStream(bitstringInputStream).use { it.copyTo(byteArrayOutputStream) }
} catch (e: Exception) {
throw Exception("unzipping status list bitstring using GZIP", e)
throw RuntimeException("unzipping status list bitstring using GZIP", e)

Check warning on line 332 in credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt

View check run for this annotation

Codecov / codecov/patch

credentials/src/main/kotlin/web5/sdk/credentials/StatusListCredential.kt#L331-L332

Added lines #L331 - L332 were not covered by tests
}

val unzipped = byteArrayOutputStream.toByteArray()
Expand Down

0 comments on commit c9bfb61

Please sign in to comment.