Skip to content

Commit

Permalink
Add: polymorphic hierarchy for ClaimsQuery and CredentialMetadataAndV…
Browse files Browse the repository at this point in the history
…alidityConstraints
  • Loading branch information
acrusage-iaik committed Jan 16, 2025
1 parent 656d003 commit 6652346
Show file tree
Hide file tree
Showing 11 changed files with 729 additions and 344 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import at.asitplus.signum.indispensable.io.TransformingSerializerTemplate
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject

object DCQLClaimsQuerySerializer :
KSerializer<DCQLClaimsQuery> by TransformingSerializerTemplate<DCQLClaimsQuery, JsonObject>(
parent = JsonObject.serializer(),
encodeAs = {
when (it) {
is DCQLClaimsQuery.Reader -> it.jsonObject
is DCQLClaimsQuery.Builder -> it.jsonObject
is DCQLClaimsQuery.Extension -> throw IllegalStateException("Instance must be a reader or a builder.")
}
},
decodeAs = {
DCQLClaimsQuery.Reader.Instance(it)
object DCQLClaimsQuerySerializer : JsonContentPolymorphicSerializer<DCQLClaimsQuery>(DCQLClaimsQuery::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<DCQLClaimsQuery> {
val parameters = element.jsonObject
return when {
DCQLIsoMdocClaimsQuery.SerialNames.NAMESPACE in parameters || DCQLIsoMdocClaimsQuery.SerialNames.CLAIM_NAME in parameters -> DCQLIsoMdocClaimsQuery.serializer()
else -> DCQLJsonClaimsQuery.serializer()
}
)
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import kotlin.jvm.JvmInline

@Serializable(with = DCQLCredentialMetadataAndValidityConstraintsSerializer::class)
sealed interface DCQLCredentialMetadataAndValidityConstraints {


interface Extension : DCQLCredentialMetadataAndValidityConstraints

sealed interface Reader : DCQLCredentialMetadataAndValidityConstraints {
val jsonObject: JsonObject

@Serializable
@JvmInline
value class Instance(override val jsonObject: JsonObject) : Reader {
init {
validate(this)
}
}
}

sealed interface Builder : DCQLCredentialMetadataAndValidityConstraints {
val jsonObject: JsonObject
}
interface DCQLCredentialMetadataAndValidityConstraints {

companion object {
fun validate(constraints: DCQLCredentialMetadataAndValidityConstraints) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import at.asitplus.signum.indispensable.io.TransformingSerializerTemplate
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject

object DCQLCredentialMetadataAndValidityConstraintsSerializer :
KSerializer<DCQLCredentialMetadataAndValidityConstraints> by TransformingSerializerTemplate<DCQLCredentialMetadataAndValidityConstraints, JsonObject>(
parent = JsonObject.serializer(),
encodeAs = {
when (it) {
is DCQLCredentialMetadataAndValidityConstraints.Reader -> it.jsonObject
is DCQLCredentialMetadataAndValidityConstraints.Builder -> it.jsonObject

is DCQLCredentialMetadataAndValidityConstraints.Extension -> {
throw IllegalStateException("Instance must be a reader or a builder.")
}
}
},
decodeAs = {
DCQLCredentialMetadataAndValidityConstraints.Reader.Instance(it)
JsonContentPolymorphicSerializer<DCQLCredentialMetadataAndValidityConstraints>(
DCQLCredentialMetadataAndValidityConstraints::class
) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<DCQLCredentialMetadataAndValidityConstraints> {
val parameters = element.jsonObject
return when {
DCQLIsoMdocCredentialMetadataAndValidityConstraints.SerialNames.DOCTYPE_VALUE in parameters -> DCQLIsoMdocCredentialMetadataAndValidityConstraints.serializer()
else -> throw IllegalArgumentException("Deserializer not found")
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable(with = DCQLIsoMdocClaimsQuerySerializer::class)
sealed interface DCQLIsoMdocClaimsQuery : DCQLClaimsQuery {
/**
* OID4VP draft 23: namespace: REQUIRED if the Credential Format is based on the mdoc format
* defined in [ISO.18013-5]; MUST NOT be present otherwise. The value MUST be a string that
* specifies the namespace of the data element within the mdoc, e.g., org.iso.18013.5.1.
*/
val namespace: String?

/**
* OID4VP draft 23: claim_name: REQUIRED if the Credential Format is based on mdoc format
* defined in [ISO.18013-5]; MUST NOT be present otherwise. The value MUST be a string that
* specifies the data element identifier of the data element within the provided namespace in
* the mdoc, e.g., first_name.
*/
val claimName: String?

object SerialNames {
const val NAMESPACE = "namespace"
const val CLAIM_NAME = "claim_name"
}

@Serializable
data class Instance(
@SerialName(DCQLClaimsQuery.SerialNames.ID)
override val id: DCQLCredentialQueryIdentifier?,
@SerialName(DCQLClaimsQuery.SerialNames.VALUES)
override val values: List<DCQLExpectedClaimValue>?,
@SerialName(SerialNames.NAMESPACE)
override val namespace: String? = null,
@SerialName(SerialNames.CLAIM_NAME)
override val claimName: String? = null,
) : DCQLIsoMdocClaimsQuery
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement

object DCQLIsoMdocClaimsQuerySerializer :
JsonContentPolymorphicSerializer<DCQLIsoMdocClaimsQuery>(DCQLIsoMdocClaimsQuery::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<DCQLIsoMdocClaimsQuery> {
return DCQLIsoMdocClaimsQuery.Instance.serializer()
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import at.asitplus.wallet.lib.data.vckJsonSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
import kotlin.jvm.JvmInline

@Serializable
sealed interface DCQLIsoMdocCredentialMetadataAndValidityConstraints :
DCQLCredentialMetadataAndValidityConstraints.Extension {
@Serializable(with = DCQLIsoMdocCredentialMetadataAndValidityConstraintsSerializer::class)
interface DCQLIsoMdocCredentialMetadataAndValidityConstraints : DCQLCredentialMetadataAndValidityConstraints {
/**
* OID4VP draft 23: doctype_value: OPTIONAL. String that specifies an allowed value for the
* doctype of the requested Verifiable Credential. It MUST be a valid doctype identifier as
Expand All @@ -23,45 +16,10 @@ sealed interface DCQLIsoMdocCredentialMetadataAndValidityConstraints :
const val DOCTYPE_VALUE = "doctype_value"
}

@Serializable
data class Instance(
@SerialName(SerialNames.DOCTYPE_VALUE)
override val doctypeValue: String?
) : DCQLIsoMdocCredentialMetadataAndValidityConstraints
}


interface Extension : DCQLIsoMdocCredentialMetadataAndValidityConstraints

sealed interface Reader : DCQLIsoMdocCredentialMetadataAndValidityConstraints,
DCQLCredentialMetadataAndValidityConstraints.Reader {
@Serializable
@JvmInline
value class Instance(override val jsonObject: JsonObject) : Reader {
init {
validate(this)
}

override val doctypeValue: String?
get() = jsonObject[SerialNames.DOCTYPE_VALUE]?.let {
vckJsonSerializer.decodeFromJsonElement<String>(it)
}
}
}

sealed interface Builder : DCQLIsoMdocCredentialMetadataAndValidityConstraints,
DCQLCredentialMetadataAndValidityConstraints.Builder {
@Serializable
data class Instance(
@SerialName(SerialNames.DOCTYPE_VALUE)
override val doctypeValue: String?
) : Builder {
init {
validate(this)
}
override val jsonObject: JsonObject
get() = vckJsonSerializer.encodeToJsonElement(this).jsonObject
}
}

companion object {
fun validate(constraints: DCQLIsoMdocCredentialMetadataAndValidityConstraints) = constraints.run {
DCQLCredentialMetadataAndValidityConstraints.validate(this)
doctypeValue
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement

object DCQLIsoMdocCredentialMetadataAndValidityConstraintsSerializer :
JsonContentPolymorphicSerializer<DCQLIsoMdocCredentialMetadataAndValidityConstraints>(
DCQLIsoMdocCredentialMetadataAndValidityConstraints::class
) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<DCQLIsoMdocCredentialMetadataAndValidityConstraints> {
return DCQLIsoMdocCredentialMetadataAndValidityConstraints.Instance.serializer()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable(with = DCQLJsonClaimsQuerySerializer::class)
interface DCQLJsonClaimsQuery : DCQLClaimsQuery {
/**
* OID4VP draft 23: path: REQUIRED if the Credential Format uses a JSON-based claims structure
* (e.g., IETF SD-JWT VC and W3C Verifiable Credentials); MUST NOT be present otherwise. The
* value MUST be a non-empty array representing a claims path pointer that specifies the path
* to a claim within the Verifiable Credential, as defined in Section 6.4.
*/
val path: DCQLClaimsPathPointer?

object SerialNames {
const val PATH = "path"
}

@Serializable
data class Instance(
@SerialName(DCQLClaimsQuery.SerialNames.ID)
override val id: DCQLCredentialQueryIdentifier?,
@SerialName(DCQLClaimsQuery.SerialNames.VALUES)
override val values: List<DCQLExpectedClaimValue>?,
@SerialName(SerialNames.PATH)
override val path: DCQLClaimsPathPointer?,
) : DCQLJsonClaimsQuery
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement

object DCQLJsonClaimsQuerySerializer : JsonContentPolymorphicSerializer<DCQLJsonClaimsQuery>(
DCQLJsonClaimsQuery::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<DCQLJsonClaimsQuery> {
return DCQLJsonClaimsQuery.Instance.serializer()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,35 @@ package at.asitplus.wallet.lib.data.oidc.oid4vp.dcql

import at.asitplus.wallet.lib.data.vckJsonSerializer
import io.kotest.core.spec.style.FreeSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject

class ClaimsQueryTest : FreeSpec({
"building" - {
val value = DCQLIsoMdocCredentialMetadataAndValidityConstraints.Builder.Instance(
doctypeValue = "test"
)
"building" - {
"iso" {
val value = DCQLIsoMdocClaimsQuery.Builder.Instance(
id = DCQLCredentialQueryIdentifier("test"),
path = DCQLClaimsPathPointer(listOf()),
namespace = "testNamespace",
claimName = "testClaimKey",
)

val base: DCQLCredentialMetadataAndValidityConstraints = value
vckJsonSerializer.encodeToJsonElement(base) shouldBe vckJsonSerializer.encodeToJsonElement(value)
}
})
val base: DCQLClaimsQuery = value
val serialized = vckJsonSerializer.encodeToJsonElement(base)
serialized shouldBe vckJsonSerializer.encodeToJsonElement(value)
serialized.jsonObject.entries shouldHaveSize 4
}
"sd-jwt" {
val value = DCQLSdJwtCredentialMetadataAndValidityConstraints.Builder.Instance(
vctValues = listOf("test")
)

val base: DCQLCredentialMetadataAndValidityConstraints = value
val serialized = vckJsonSerializer.encodeToJsonElement(base)
serialized shouldBe vckJsonSerializer.encodeToJsonElement(value)
serialized.jsonObject.entries shouldHaveSize 1
}
}
})

0 comments on commit 6652346

Please sign in to comment.