prople_did_core/verifiable/
vc.rs

1use rst_common::standard::serde::{self, Deserialize, Serialize};
2use rst_common::standard::serde_json::{self, Value};
3
4use crate::types::DIDError;
5use crate::types::{JSONValue, ToJCS, ToJSON, Validator};
6
7use crate::verifiable::proof::types::{ProofError, Proofable};
8use crate::verifiable::proof::Proof;
9
10pub type Context = String;
11pub type ID = String;
12pub type Type = String;
13pub type SRI = String;
14pub type Issuer = String;
15
16/// `VC` is a main object to hold entity credential
17///
18/// Ref: <https://www.w3.org/TR/vc-data-model-2.0/>
19#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
20#[serde(crate = "self::serde")]
21pub struct VC {
22    #[serde(rename = "@context")]
23    pub contexts: Vec<Context>,
24
25    #[serde(rename = "type")]
26    pub types: Vec<Type>,
27
28    #[serde(rename = "credentialSubject")]
29    pub credential_subject: Value,
30
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub proof: Option<Proof>,
33
34    pub id: ID,
35    pub issuer: String,
36}
37
38impl VC {
39    pub fn new(id: ID, issuer: Issuer) -> Self {
40        Self {
41            id,
42            issuer,
43            contexts: Vec::new(),
44            types: Vec::new(),
45            credential_subject: Value::Null,
46            proof: None,
47        }
48    }
49
50    pub fn add_context(&mut self, context: Context) -> &mut Self {
51        self.contexts.push(context);
52        self
53    }
54
55    pub fn add_type(&mut self, t: Type) -> &mut Self {
56        self.types.push(t);
57        self
58    }
59
60    pub fn set_credential(&mut self, credential: Value) -> &mut Self {
61        self.credential_subject = credential;
62        self
63    }
64
65    pub fn proof(&mut self, proof: Proof) -> &mut Self {
66        self.proof = Some(proof);
67        self
68    }
69}
70
71impl ToJSON for VC {
72    fn to_json(&self) -> Result<JSONValue, DIDError> {
73        let validation = self.validate();
74        match validation {
75            Ok(_) => {
76                let jsonstr = serde_json::to_string(self)
77                    .map_err(|err| DIDError::GenerateJSONError(err.to_string()))?;
78
79                Ok(JSONValue::from(jsonstr))
80            }
81            Err(err) => Err(err),
82        }
83    }
84}
85
86impl ToJCS for VC {
87    fn to_jcs(&self) -> Result<String, DIDError> {
88        let validation = self.validate();
89        match validation {
90            Ok(_) => serde_jcs::to_string(self)
91                .map_err(|err| DIDError::GenerateJSONError(err.to_string())),
92            Err(err) => Err(err),
93        }
94    }
95}
96
97impl Validator for VC {
98    fn validate(&self) -> Result<(), DIDError> {
99        if self.contexts.is_empty() {
100            return Err(DIDError::GenerateVCError(String::from(
101                "vc_error: empty context",
102            )));
103        }
104
105        if self.types.is_empty() {
106            return Err(DIDError::GenerateVCError(String::from(
107                "vc_error: empty types",
108            )));
109        }
110
111        if self.credential_subject == Value::Null {
112            return Err(DIDError::GenerateVCError(String::from(
113                "vc_error: credential should not be null",
114            )));
115        }
116
117        Ok(())
118    }
119}
120
121impl Proofable for VC {
122    fn get_proof(&self) -> Option<Proof> {
123        self.proof.to_owned()
124    }
125
126    fn setup_proof(&mut self, proof: Proof) -> &mut Self {
127        self.proof(proof);
128        self
129    }
130
131    fn parse_json_bytes(bytes: Vec<u8>) -> Result<Self, ProofError> {
132        let parsed: Self = serde_json::from_slice(bytes.as_slice())
133            .map_err(|err| ProofError::ProofGenerationError(err.to_string()))?;
134
135        Ok(parsed)
136    }
137
138    fn remove_proof(&self) -> Self {
139        let mut vc = self.clone();
140        vc.proof = None;
141        vc
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[derive(Serialize, Deserialize)]
150    #[serde(crate = "self::serde")]
151    struct FakeCredentialProperties {
152        pub user_agent: String,
153        pub user_did: String,
154    }
155
156    #[derive(Serialize, Deserialize)]
157    #[serde(crate = "self::serde")]
158    struct FakeCredentialSubject {
159        id: String,
160        connection: FakeCredentialProperties,
161    }
162
163    #[test]
164    fn test_generate_credential() {
165        let mut vc = VC::new(String::from("id"), String::from("issuer"));
166        vc.add_context(String::from("context1"))
167            .add_context("context2".to_string())
168            .add_type("VerifiableCredential".to_string());
169
170        let credential_props = FakeCredentialProperties {
171            user_agent: String::from("test_agent"),
172            user_did: String::from("test_did"),
173        };
174
175        let credential = FakeCredentialSubject {
176            id: String::from("id"),
177            connection: credential_props,
178        };
179
180        let credential_value = serde_json::to_value(credential);
181        assert!(!credential_value.is_err());
182
183        vc.set_credential(credential_value.unwrap());
184        let json_str = vc.to_json();
185        assert!(!json_str.is_err());
186
187        let expected_json = r#"{"@context":["context1","context2"],"type":["VerifiableCredential"],"credentialSubject":{"connection":{"user_agent":"test_agent","user_did":"test_did"},"id":"id"},"id":"id","issuer":"issuer"}"#;
188        assert_eq!(json_str.unwrap(), JSONValue::from(expected_json))
189    }
190
191    #[test]
192    fn test_generate_credential_jcs() {
193        let mut vc = VC::new(String::from("id"), String::from("issuer"));
194        vc.add_context(String::from("context1"))
195            .add_context("context2".to_string())
196            .add_type("VerifiableCredential".to_string());
197
198        let credential_props = FakeCredentialProperties {
199            user_agent: String::from("test_agent"),
200            user_did: String::from("test_did"),
201        };
202
203        let credential = FakeCredentialSubject {
204            id: String::from("id"),
205            connection: credential_props,
206        };
207
208        let credential_value = serde_json::to_value(credential);
209        assert!(!credential_value.is_err());
210
211        vc.set_credential(credential_value.unwrap());
212        let json_str = vc.to_jcs();
213        assert!(!json_str.is_err());
214    }
215
216    #[test]
217    fn test_generate_empty_context() {
218        let vc = VC::new(String::from("id"), String::from("issuer"));
219        let try_json = vc.to_json();
220        assert!(try_json.is_err());
221
222        assert_eq!(
223            DIDError::GenerateVCError(String::from("vc_error: empty context")),
224            try_json.unwrap_err()
225        )
226    }
227
228    #[test]
229    fn test_generate_empty_types() {
230        let mut vc = VC::new(String::from("id"), String::from("issuer"));
231        vc.add_context(String::from("context1"));
232        vc.add_context(String::from("context2"));
233
234        let try_json = vc.to_json();
235        assert!(try_json.is_err());
236
237        assert_eq!(
238            DIDError::GenerateVCError(String::from("vc_error: empty types")),
239            try_json.unwrap_err()
240        )
241    }
242
243    #[test]
244    fn test_generate_empty_credential() {
245        let mut vc = VC::new(String::from("id"), String::from("issuer"));
246        vc.add_context(String::from("context1"))
247            .add_context("context2".to_string())
248            .add_type("VerifiableCredential".to_string());
249
250        let try_json = vc.to_json();
251        assert!(try_json.is_err());
252
253        assert_eq!(
254            DIDError::GenerateVCError(String::from("vc_error: credential should not be null")),
255            try_json.unwrap_err()
256        )
257    }
258}