Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fully specializable credentials. #577

Merged
merged 3 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 214 additions & 37 deletions crates/claims/crates/vc/src/v1/syntax/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ use ssi_rdf::{Interpretation, LdEnvironment};
use std::{borrow::Cow, collections::BTreeMap, hash::Hash};
use xsd_types::DateTime;

use crate::syntax::{
not_null, value_or_array, IdOr, IdentifiedObject, IdentifiedTypedObject,
MaybeIdentifiedTypedObject, RequiredContextList, RequiredType, RequiredTypeSet,
TypeSerializationPolicy, Types,
use crate::{
syntax::{
not_null, value_or_array, IdOr, IdentifiedObject, IdentifiedTypedObject,
MaybeIdentifiedTypedObject, RequiredContextList, RequiredType, RequiredTypeSet,
TypeSerializationPolicy, Types,
},
Identified, MaybeIdentified, Typed,
};

use super::Context;
Expand All @@ -32,23 +35,34 @@ pub type JsonCredentialTypes<T = ()> = Types<CredentialType, T>;

/// JSON Credential, without required context nor type.
///
/// If you care about required context and/or type, use the
/// [`SpecializedJsonCredential`] type directly.
/// If you care about required context and/or type, or want to customize other
/// aspects of the credential, use the [`SpecializedJsonCredential`] type
/// directly.
pub type JsonCredential<S = json_syntax::Object> = SpecializedJsonCredential<S>;

/// Specialized JSON Credential with custom required context and type.
/// Specialized JSON Credential with custom types for each component.
///
/// If you don't care about required context and/or type, you can use the
/// If you don't care about the type of each component, you can use the
/// [`JsonCredential`] type alias instead.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound(
serialize = "S: Serialize",
deserialize = "S: Deserialize<'de>, C: RequiredContextList, T: RequiredTypeSet"
serialize = "Subject: Serialize, Issuer: Serialize, Status: Serialize, Evidence: Serialize, Schema: Serialize, RefreshService: Serialize, TermsOfUse: Serialize",
deserialize = "Subject: Deserialize<'de>, RequiredContext: RequiredContextList, RequiredType: RequiredTypeSet, Issuer: Deserialize<'de>, Status: Deserialize<'de>, Evidence: Deserialize<'de>, Schema: Deserialize<'de>, RefreshService: Deserialize<'de>, TermsOfUse: Deserialize<'de>"
))]
pub struct SpecializedJsonCredential<S = json_syntax::Object, C = (), T = ()> {
pub struct SpecializedJsonCredential<
Subject = json_syntax::Object,
RequiredContext = (),
RequiredType = (),
Issuer = IdOr<IdentifiedObject>,
Status = IdentifiedTypedObject,
Evidence = MaybeIdentifiedTypedObject,
Schema = IdentifiedTypedObject,
RefreshService = IdentifiedTypedObject,
TermsOfUse = MaybeIdentifiedTypedObject,
> {
/// JSON-LD context.
#[serde(rename = "@context")]
pub context: Context<C>,
pub context: Context<RequiredContext>,

/// Credential identifier.
#[serde(
Expand All @@ -60,7 +74,7 @@ pub struct SpecializedJsonCredential<S = json_syntax::Object, C = (), T = ()> {

/// Credential type.
#[serde(rename = "type")]
pub types: JsonCredentialTypes<T>,
pub types: JsonCredentialTypes<RequiredType>,

/// Credential subjects.
#[serde(rename = "credentialSubject")]
Expand All @@ -69,10 +83,10 @@ pub struct SpecializedJsonCredential<S = json_syntax::Object, C = (), T = ()> {
default,
skip_serializing_if = "Vec::is_empty"
)]
pub credential_subjects: Vec<S>,
pub credential_subjects: Vec<Subject>,

/// Issuer.
pub issuer: IdOr<IdentifiedObject>,
pub issuer: Issuer,

/// Issuance date.
///
Expand All @@ -92,7 +106,7 @@ pub struct SpecializedJsonCredential<S = json_syntax::Object, C = (), T = ()> {
default,
skip_serializing_if = "Vec::is_empty"
)]
pub credential_status: Vec<IdentifiedTypedObject>,
pub credential_status: Vec<Status>,

/// Terms of use.
#[serde(rename = "termsOfUse")]
Expand All @@ -101,43 +115,68 @@ pub struct SpecializedJsonCredential<S = json_syntax::Object, C = (), T = ()> {
default,
skip_serializing_if = "Vec::is_empty"
)]
pub terms_of_use: Vec<MaybeIdentifiedTypedObject>,
pub terms_of_use: Vec<TermsOfUse>,

/// Evidence.
#[serde(
with = "value_or_array",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub evidence: Vec<MaybeIdentifiedTypedObject>,
pub evidence: Vec<Evidence>,

#[serde(rename = "credentialSchema")]
#[serde(
with = "value_or_array",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub credential_schema: Vec<IdentifiedTypedObject>,
pub credential_schema: Vec<Schema>,

#[serde(rename = "refreshService")]
#[serde(
with = "value_or_array",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub refresh_services: Vec<IdentifiedTypedObject>,
pub refresh_services: Vec<RefreshService>,

#[serde(flatten)]
pub additional_properties: BTreeMap<String, json_syntax::Value>,
}

impl<S, C: RequiredContextList, T: RequiredTypeSet> SpecializedJsonCredential<S, C, T> {
impl<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
where
RequiredContext: RequiredContextList,
RequiredType: RequiredTypeSet,
{
/// Creates a new credential.
pub fn new(
id: Option<UriBuf>,
issuer: IdOr<IdentifiedObject>,
issuer: Issuer,
issuance_date: xsd_types::DateTime,
credential_subjects: Vec<S>,
credential_subjects: Vec<Subject>,
) -> Self {
Self {
context: Context::default(),
Expand All @@ -157,19 +196,86 @@ impl<S, C: RequiredContextList, T: RequiredTypeSet> SpecializedJsonCredential<S,
}
}

impl<S, C, T> JsonLdObject for SpecializedJsonCredential<S, C, T> {
impl<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
> JsonLdObject
for SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
{
fn json_ld_context(&self) -> Option<Cow<ssi_json_ld::syntax::Context>> {
Some(Cow::Borrowed(self.context.as_ref()))
}
}

impl<S, C, T> JsonLdNodeObject for SpecializedJsonCredential<S, C, T> {
impl<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
> JsonLdNodeObject
for SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
{
fn json_ld_type(&self) -> JsonLdTypes {
self.types.to_json_ld_types()
}
}

impl<S, C, T, E, P> ValidateClaims<E, P> for SpecializedJsonCredential<S, C, T>
impl<
Subject,
RequiredContext,
RequiredType,
Issuer: Identified,
Status: Identified + Typed,
Evidence: MaybeIdentified + Typed,
Schema: Identified + Typed,
RefreshService: Identified + Typed,
TermsOfUse: MaybeIdentified + Typed,
E,
P,
> ValidateClaims<E, P>
for SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
where
E: DateTimeProvider,
{
Expand All @@ -178,20 +284,64 @@ where
}
}

impl<S, C, T> crate::MaybeIdentified for SpecializedJsonCredential<S, C, T> {
impl<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
> crate::MaybeIdentified
for SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
{
fn id(&self) -> Option<&Uri> {
self.id.as_deref()
}
}

impl<S, C, T> crate::v1::Credential for SpecializedJsonCredential<S, C, T> {
type Subject = S;
type Issuer = IdOr<IdentifiedObject>;
type Status = IdentifiedTypedObject;
type RefreshService = IdentifiedTypedObject;
type TermsOfUse = MaybeIdentifiedTypedObject;
type Evidence = MaybeIdentifiedTypedObject;
type Schema = IdentifiedTypedObject;
impl<
Subject,
RequiredContext,
RequiredType,
Issuer: Identified,
Status: Identified + Typed,
Evidence: MaybeIdentified + Typed,
Schema: Identified + Typed,
RefreshService: Identified + Typed,
TermsOfUse: MaybeIdentified + Typed,
> crate::v1::Credential
for SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
{
type Subject = Subject;
type Issuer = Issuer;
type Status = Status;
type RefreshService = RefreshService;
type TermsOfUse = TermsOfUse;
type Evidence = Evidence;
type Schema = Schema;

fn id(&self) -> Option<&Uri> {
self.id.as_deref()
Expand Down Expand Up @@ -238,9 +388,36 @@ impl<S, C, T> crate::v1::Credential for SpecializedJsonCredential<S, C, T> {
}
}

impl<S, C, T> ssi_json_ld::Expandable for SpecializedJsonCredential<S, C, T>
impl<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
> ssi_json_ld::Expandable
for SpecializedJsonCredential<
Subject,
RequiredContext,
RequiredType,
Issuer,
Status,
Evidence,
Schema,
RefreshService,
TermsOfUse,
>
where
S: Serialize,
Subject: Serialize,
Issuer: Serialize,
Status: Serialize,
Evidence: Serialize,
Schema: Serialize,
RefreshService: Serialize,
TermsOfUse: Serialize,
{
type Error = JsonLdError;

Expand Down
Loading