Skip to content

Commit

Permalink
claim metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
UMR1352 committed Sep 20, 2024
1 parent d278060 commit 0a1ed27
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 4 deletions.
104 changes: 104 additions & 0 deletions identity_credential/src/sd_jwt_vc/metadata/claim.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::ops::Deref;

use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use serde_json::Value;

/// Information about a particular claim for displaying and validation purposes.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClaimMetadata {
/// [`ClaimPath`] of the claim or claims that are being addressed.
pub path: ClaimPath,
/// Object containing display information for the claim.
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub display: Vec<ClaimDisplay>,
/// A string indicating whether the claim is selectively disclosable.
pub sd: Option<ClaimDisclosability>,
/// A string defining the ID of the claim for reference in the SVG template.
pub svg_id: Option<String>,
}

/// A non-empty list of string, `null` values, or non-negative integers.
/// It is used to selected a particular claim in the credential or a
/// set of claims. See [Claim Path](https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-05.html#name-claim-path) for more information.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "Vec<ClaimPathSegment>")]
pub struct ClaimPath(Vec<ClaimPathSegment>);

impl TryFrom<Vec<ClaimPathSegment>> for ClaimPath {
type Error = anyhow::Error;
fn try_from(value: Vec<ClaimPathSegment>) -> Result<Self, Self::Error> {
if value.is_empty() {
Err(anyhow::anyhow!("`ClaimPath` cannot be empty"))
} else {
Ok(Self(value))
}
}
}

impl Deref for ClaimPath {
type Target = [ClaimPathSegment];
fn deref(&self) -> &Self::Target {
&self.0
}
}

/// A single segment of a [`ClaimPath`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged, try_from = "Value")]
pub enum ClaimPathSegment {
/// JSON object property.
Name(String),
/// JSON array entry.
Position(usize),
/// All properties or entries.
#[serde(serialize_with = "serialize_all_variant")]
All,
}

impl TryFrom<Value> for ClaimPathSegment {
type Error = anyhow::Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Null => Ok(ClaimPathSegment::All),
Value::String(s) => Ok(ClaimPathSegment::Name(s)),
Value::Number(n) => n
.as_u64()
.ok_or_else(|| anyhow::anyhow!("expected number greater or equal to 0"))
.map(|n| ClaimPathSegment::Position(n as usize)),
_ => Err(anyhow::anyhow!("expected either a string, number, or null")),
}
}
}

fn serialize_all_variant<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_none()
}

/// Information about whether a given claim is selectively disclosable.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ClaimDisclosability {
/// The issuer **must** make the claim selectively disclosable.
Always,
/// The issuer **may** make the claim selectively disclosable.
#[default]
Allowed,
/// The issuer **must not** make the claim selectively disclosable.
Never,
}

/// Display information for a given claim.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClaimDisplay {
/// A language tag as defined in [RFC5646](https://www.rfc-editor.org/rfc/rfc5646.txt).
pub lang: String,
/// A human-readable label for the claim.
pub label: String,
/// A human-readable description for the claim.
pub description: Option<String>,
}
2 changes: 2 additions & 0 deletions identity_credential/src/sd_jwt_vc/metadata/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Copyright 2020-2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

mod claim;
mod display;
mod integrity;
mod issuer;
mod vc_type;

pub use claim::*;
pub use display::*;
pub use integrity::*;
pub use issuer::*;
Expand Down
15 changes: 11 additions & 4 deletions identity_credential/src/sd_jwt_vc/metadata/vc_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::sd_jwt_vc::Error;
use crate::sd_jwt_vc::Resolver;
use crate::sd_jwt_vc::Result;

use super::ClaimMetadata;
use super::DisplayMetadata;
use super::IntegrityMetadata;

Expand All @@ -31,8 +32,12 @@ pub struct TypeMetadata {
/// Either an embedded schema or a reference to one.
#[serde(flatten)]
pub schema: Option<TypeSchema>,
/// An object containing display information for the type.
pub display: Option<DisplayMetadata>,
/// A list containing display information for the type.
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub display: Vec<DisplayMetadata>,
/// A list of [`ClaimMetadata`] containing information about particular claims.
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub claims: Vec<ClaimMetadata>,
}

impl TypeMetadata {
Expand Down Expand Up @@ -186,7 +191,8 @@ mod tests {
description: None,
extends: None,
extends_integrity: None,
display: None,
display: vec![],
claims: vec![],
schema: Some(TypeSchema::Object {
schema: json!({
"$schema": "https://json-schema.org/draft/2020-12/schema",
Expand All @@ -209,7 +215,8 @@ mod tests {
description: None,
extends: None,
extends_integrity: None,
display: None,
display: vec![],
claims: vec![],
schema: Some(TypeSchema::Uri {
schema_uri: Url::parse("https://example.com/vc_types/1").unwrap(),
schema_uri_integrity: None,
Expand Down

0 comments on commit 0a1ed27

Please sign in to comment.