-
Notifications
You must be signed in to change notification settings - Fork 3
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
1 parent
1181e1f
commit 1581979
Showing
31 changed files
with
1,308 additions
and
0 deletions.
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
98
openid-data-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsPathPointer.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,98 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import at.asitplus.jsonpath.core.NodeList | ||
import at.asitplus.jsonpath.core.NodeListEntry | ||
import at.asitplus.jsonpath.core.NormalizedJsonPath | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.json.JsonElement | ||
import kotlin.jvm.JvmInline | ||
|
||
/** | ||
* 6.4. Claims Path Pointer | ||
* | ||
* A claims path pointer is a pointer into the JSON structure of the Verifiable Credential, | ||
* identifying one or more claims. A claims path pointer MUST be a non-empty array of strings and | ||
* non-negative integers. A string value indicates that the respective key is to be selected, a | ||
* null value indicates that all elements of the currently selected array(s) are to be selected; | ||
* and a non-negative integer indicates that the respective index in an array is to be selected. | ||
* The path is formed as follows: | ||
* Start with an empty array and repeat the following until the full path is formed. | ||
* To address a particular claim within an object, append the key (claim name) to the array. | ||
* To address an element within an array, append the index to the array (as a non-negative, 0-based | ||
* integer).To address all elements within an array, append a null value to the array. Verifiers | ||
* MUST NOT point to the same claim more than once in a single query. Wallets SHOULD ignore such | ||
* duplicate claim queries. | ||
*/ | ||
@Serializable | ||
@JvmInline | ||
value class DCQLClaimsPathPointer( | ||
val segments: List<DCQLClaimsPathPointerSegment>, | ||
) { | ||
init { | ||
validate() | ||
} | ||
|
||
constructor(startSegment: String) : this( | ||
listOf(DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNameSegment(startSegment)) | ||
) | ||
|
||
constructor(startSegment: UInt) : this( | ||
listOf(DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerIndexSegment(startSegment)) | ||
) | ||
|
||
constructor(@Suppress("UNUSED_PARAMETER") nullValue: Nothing?) : this( | ||
listOf(DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment) | ||
) | ||
|
||
|
||
operator fun plus(other: DCQLClaimsPathPointer) = DCQLClaimsPathPointer( | ||
segments + other.segments | ||
) | ||
|
||
operator fun plus(key: String) = DCQLClaimsPathPointer( | ||
segments + DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNameSegment(key) | ||
) | ||
|
||
operator fun plus(index: UInt) = DCQLClaimsPathPointer( | ||
segments + DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerIndexSegment(index) | ||
) | ||
|
||
operator fun plus(@Suppress("UNUSED_PARAMETER") nullValue: Nothing?) = DCQLClaimsPathPointer( | ||
segments + DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment | ||
) | ||
|
||
/** | ||
* 6.4.1. Processing | ||
* | ||
* In detail, the array is processed by the Wallet from left to right as follows: | ||
* Select the root element of the Credential, i.e., the top-level JSON object. | ||
* | ||
* Process the query of the claims path pointer array from left to right: | ||
* If the component is a string, select the element in the respective key in the currently | ||
* selected element(s). If any of the currently selected element(s) is not an object, abort | ||
* processing and return an error. If the key does not exist in any element currently selected, | ||
* remove that element from the selection. | ||
* | ||
* If the component is null, select all elements of the currently selected array(s). If any of | ||
* the currently selected element(s) is not an array, abort processing and return an error.If | ||
* the component is a non-negative integer, select the element at the respective index in the | ||
* currently selected array(s). If any of the currently selected element(s) is not an array, | ||
* abort processing and return an error. If the index does not exist in a selected array, | ||
* remove that array from the selection.If the set of elements currently selected is empty, | ||
* abort processing and return an error.The result of the processing is the set of elements | ||
* which is requested for presentation. | ||
*/ | ||
fun query(jsonElement: JsonElement): NodeList { | ||
var nodeList = listOf(NodeListEntry(NormalizedJsonPath(), jsonElement)) | ||
segments.forEach { | ||
nodeList = it.query(nodeList) | ||
} | ||
return nodeList | ||
} | ||
|
||
private fun validate() { | ||
if (segments.isEmpty()) { | ||
throw IllegalArgumentException("Value must not be the empty list.") | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...c/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsPathPointerNullSegmentSerializer.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 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import at.asitplus.signum.indispensable.io.TransformingSerializerTemplate | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.json.JsonNull | ||
|
||
object DCQLClaimsPathPointerNullSegmentSerializer : KSerializer<DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment> by TransformingSerializerTemplate<DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment, JsonNull>( | ||
parent = JsonNull.serializer(), | ||
encodeAs = { | ||
JsonNull | ||
}, | ||
|
||
decodeAs = { | ||
DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment | ||
} | ||
) |
54 changes: 54 additions & 0 deletions
54
...ata-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsPathPointerSegment.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,54 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import at.asitplus.catching | ||
import at.asitplus.jsonpath.core.NodeList | ||
import at.asitplus.jsonpath.core.NodeListEntry | ||
import at.asitplus.openid.third_party.at.asitplus.jsonpath.core.plus | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.json.jsonArray | ||
import kotlinx.serialization.json.jsonObject | ||
import kotlin.jvm.JvmInline | ||
|
||
@Serializable(with = DCQLClaimsPathPointerSegmentSerializer::class) | ||
sealed interface DCQLClaimsPathPointerSegment { | ||
fun query(nodeList: NodeList): NodeList | ||
|
||
@JvmInline | ||
value class DCQLClaimsPathPointerNameSegment(val name: String) : DCQLClaimsPathPointerSegment { | ||
override fun query(nodeList: NodeList) = nodeList.mapNotNull { | ||
catching { | ||
NodeListEntry( | ||
normalizedJsonPath = it.normalizedJsonPath + name, | ||
value = it.value.jsonObject[name]!! | ||
) | ||
}.getOrNull() | ||
} | ||
} | ||
|
||
@JvmInline | ||
value class DCQLClaimsPathPointerIndexSegment(val index: UInt) : DCQLClaimsPathPointerSegment { | ||
override fun query(nodeList: NodeList) = nodeList.mapNotNull { | ||
catching { | ||
NodeListEntry( | ||
normalizedJsonPath = it.normalizedJsonPath + index, | ||
value = it.value.jsonArray[index.toInt()] | ||
) | ||
}.getOrNull() | ||
} | ||
} | ||
|
||
@Serializable(with = DCQLClaimsPathPointerNullSegmentSerializer::class) | ||
data object DCQLClaimsPathPointerNullSegment : DCQLClaimsPathPointerSegment { | ||
override fun query(nodeList: NodeList) = nodeList.mapNotNull { claimQueryResult -> | ||
catching { | ||
claimQueryResult.value.jsonArray.mapIndexed { index, jsonElement -> | ||
NodeListEntry( | ||
normalizedJsonPath = claimQueryResult.normalizedJsonPath + index.toUInt(), | ||
value = jsonElement | ||
) | ||
} | ||
}.getOrNull() | ||
}.flatten() | ||
} | ||
} | ||
|
39 changes: 39 additions & 0 deletions
39
...s/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsPathPointerSegmentSerializer.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,39 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import at.asitplus.signum.indispensable.io.TransformingSerializerTemplate | ||
import kotlinx.serialization.KSerializer | ||
import kotlinx.serialization.json.JsonNull | ||
import kotlinx.serialization.json.JsonPrimitive | ||
import kotlinx.serialization.json.long | ||
import kotlinx.serialization.json.longOrNull | ||
|
||
object DCQLClaimsPathPointerSegmentSerializer : KSerializer<DCQLClaimsPathPointerSegment> by TransformingSerializerTemplate<DCQLClaimsPathPointerSegment, JsonPrimitive>( | ||
parent = JsonPrimitive.serializer(), | ||
encodeAs = { | ||
when (it) { | ||
is DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNameSegment -> { | ||
JsonPrimitive(it.name) | ||
} | ||
|
||
is DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerIndexSegment -> { | ||
JsonPrimitive(it.index.toLong()) | ||
} | ||
|
||
is DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment -> { | ||
JsonNull | ||
} | ||
} | ||
}, | ||
|
||
decodeAs = { | ||
when { | ||
it is JsonNull -> DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNullSegment | ||
|
||
it.longOrNull != null -> DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerIndexSegment( | ||
it.long.toUInt() | ||
) | ||
|
||
else -> DCQLClaimsPathPointerSegment.DCQLClaimsPathPointerNameSegment(it.content) | ||
} | ||
} | ||
) |
60 changes: 60 additions & 0 deletions
60
openid-data-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsQuery.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,60 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import at.asitplus.KmmResult | ||
import at.asitplus.catching | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable(with = DCQLClaimsQuerySerializer::class) | ||
interface DCQLClaimsQuery { | ||
/** | ||
* OID4VP draft 23: id: REQUIRED if claim_sets is present in the Credential Query; OPTIONAL | ||
* otherwise. A string identifying the particular claim. The value MUST be a non-empty string | ||
* consisting of alphanumeric, underscore (_) or hyphen (-) characters. Within the particular | ||
* claims array, the same id MUST NOT be present more than once. | ||
*/ | ||
val id: DCQLClaimsQueryIdentifier? | ||
|
||
/** | ||
* OID4VP draft 23: values: OPTIONAL. An array of strings, integers or boolean values that | ||
* specifies the expected values of the claim. If the values property is present, the Wallet | ||
* SHOULD return the claim only if the type and value of the claim both match for at least one | ||
* of the elements in the array. Details of the processing rules are defined in Section 6.3.1.1. | ||
*/ | ||
val values: List<DCQLExpectedClaimValue>? | ||
|
||
object SerialNames { | ||
const val ID = "id" | ||
const val VALUES = "values" | ||
} | ||
|
||
fun <Credential : Any> executeClaimsQueryAgainstCredential( | ||
credentialQuery: DCQLCredentialQuery, | ||
credential: Credential, | ||
credentialStructureExtractor: (Credential) -> DCQLCredentialClaimStructure, | ||
): KmmResult<DCQLClaimsQueryResult> = catching { | ||
when (this) { | ||
is DCQLJsonClaimsQuery -> { | ||
executeJsonClaimsQueryAgainstCredential( | ||
credentialQuery = credentialQuery, | ||
credential = credential, | ||
credentialStructureExtractor = { | ||
credentialStructureExtractor(it) as DCQLCredentialClaimStructure.JsonBasedDCQLCredentialClaimStructure | ||
} | ||
).getOrThrow() | ||
} | ||
|
||
is DCQLIsoMdocClaimsQuery -> { | ||
executeIsoMdocClaimsQueryAgainstCredential( | ||
credentialQuery = credentialQuery, | ||
credential = credential, | ||
credentialStructureExtractor = { | ||
credentialStructureExtractor(it) as DCQLCredentialClaimStructure.IsoMdocDCQLCredentialClaimStructure | ||
} | ||
).getOrThrow() | ||
} | ||
|
||
else -> throw IllegalStateException("Unsupported claim query type") | ||
} | ||
} | ||
} | ||
|
28 changes: 28 additions & 0 deletions
28
...d-data-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsQueryIdentifier.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,28 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import kotlinx.serialization.Serializable | ||
import kotlin.jvm.JvmInline | ||
|
||
/** | ||
* A string identifying the particular claim. The value MUST be a non-empty string consisting of | ||
* alphanumeric, underscore (_) or hyphen (-) characters. Within the particular claims array, the | ||
* same id MUST NOT be present more than once. | ||
*/ | ||
@Serializable | ||
@JvmInline | ||
value class DCQLClaimsQueryIdentifier(val string: String) { | ||
init { | ||
validate() | ||
} | ||
|
||
private fun validate() { | ||
if(string.isEmpty()) { | ||
throw IllegalArgumentException("Value must not be the empty string.") | ||
} | ||
string.forEach { | ||
if(it != '_' && it != '-' && !it.isLetterOrDigit()) { | ||
throw IllegalArgumentException("Value must only consist of alphanumeric, underscore (_) or hyphen (-) characters.") | ||
} | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
openid-data-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsQueryResult.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,15 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import at.asitplus.jsonpath.core.NodeList | ||
|
||
sealed interface DCQLClaimsQueryResult { | ||
class JsonClaimsQueryResult( | ||
val nodeList: NodeList | ||
) : DCQLClaimsQueryResult | ||
|
||
class IsoMdocClaimsQueryResult( | ||
val namespace: String, | ||
val claimName: String, | ||
val claimValue: Any, | ||
) : DCQLClaimsQueryResult | ||
} |
16 changes: 16 additions & 0 deletions
16
...d-data-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLClaimsQuerySerializer.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 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import kotlinx.serialization.DeserializationStrategy | ||
import kotlinx.serialization.json.JsonContentPolymorphicSerializer | ||
import kotlinx.serialization.json.JsonElement | ||
import kotlinx.serialization.json.jsonObject | ||
|
||
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() | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
...ata-classes/src/commonMain/kotlin/at/asitplus/openid/dcql/DCQLCredentialClaimStructure.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,13 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import kotlinx.serialization.json.JsonElement | ||
import kotlin.jvm.JvmInline | ||
|
||
sealed interface DCQLCredentialClaimStructure { | ||
@JvmInline | ||
value class JsonBasedDCQLCredentialClaimStructure(val jsonElement: JsonElement) : DCQLCredentialClaimStructure | ||
|
||
@JvmInline | ||
value class IsoMdocDCQLCredentialClaimStructure(val namespaceClaimValueMap: Map<String, Map<String, Any?>>) : | ||
DCQLCredentialClaimStructure | ||
} |
8 changes: 8 additions & 0 deletions
8
...commonMain/kotlin/at/asitplus/openid/dcql/DCQLCredentialMetadataAndValidityConstraints.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,8 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable(with = DCQLCredentialMetadataAndValidityConstraintsSerializer::class) | ||
interface DCQLCredentialMetadataAndValidityConstraints | ||
|
||
|
20 changes: 20 additions & 0 deletions
20
.../kotlin/at/asitplus/openid/dcql/DCQLCredentialMetadataAndValidityConstraintsSerializer.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,20 @@ | ||
package at.asitplus.openid.dcql | ||
|
||
import kotlinx.serialization.DeserializationStrategy | ||
import kotlinx.serialization.json.JsonContentPolymorphicSerializer | ||
import kotlinx.serialization.json.JsonElement | ||
import kotlinx.serialization.json.jsonObject | ||
|
||
object DCQLCredentialMetadataAndValidityConstraintsSerializer : | ||
JsonContentPolymorphicSerializer<DCQLCredentialMetadataAndValidityConstraints>( | ||
DCQLCredentialMetadataAndValidityConstraints::class | ||
) { | ||
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<DCQLCredentialMetadataAndValidityConstraints> { | ||
val parameters = element.jsonObject | ||
return when { | ||
DCQLSdJwtCredentialMetadataAndValidityConstraints.SerialNames.VCT_VALUES in parameters -> DCQLSdJwtCredentialMetadataAndValidityConstraints.serializer() | ||
DCQLIsoMdocCredentialMetadataAndValidityConstraints.SerialNames.DOCTYPE_VALUE in parameters -> DCQLIsoMdocCredentialMetadataAndValidityConstraints.serializer() | ||
else -> throw IllegalArgumentException("Deserializer not found") | ||
} | ||
} | ||
} |
Oops, something went wrong.