trustchain_http/
middleware.rs1use axum::{
3 body::Body,
4 extract::Path,
5 http::{Request, StatusCode},
6 middleware::Next,
7 response::IntoResponse,
8 Json,
9};
10use did_ion::{
11 sidetree::{DIDSuffix, Sidetree},
12 ION,
13};
14use lazy_static::lazy_static;
15use serde_json::json;
16use trustchain_ion::config::ion_config;
17use trustchain_ion::ion::IONTest;
18
19lazy_static! {
20 static ref ION_DID_PREFIX: String = format!("did:{}", ION::METHOD);
21 static ref ION_DID_TEST_PREFIX: String =
22 format!("{}:{}", &*ION_DID_PREFIX, IONTest::NETWORK.unwrap());
23}
24
25fn error_message(did: &str, expected_prefix: &str) -> serde_json::Value {
27 json!({
28 "error":
29 format!(
30 "DID: {} does not match expected prefix: {}",
31 did,
32 expected_prefix
33 )
34 })
35}
36
37fn validate_did_str(
38 did: &str,
39 mongo_database_ion_core: &str,
40) -> Result<(), (StatusCode, Json<serde_json::Value>)> {
41 let did_split = did.rsplit_once(':');
42 if did_split.is_none() {
43 return Err((
44 StatusCode::BAD_REQUEST,
45 Json(json!({"error": format!("InvalidDID: {}", did)})),
46 ));
47 }
48 let (did_prefix, ion_did_suffix) = did_split.unwrap();
49
50 if did_prefix != *ION_DID_PREFIX && did_prefix != *ION_DID_TEST_PREFIX {
52 return Ok(());
53 }
54
55 let ion_did_suffix = DIDSuffix(ion_did_suffix.to_string());
57 if let Err(err) = ION::validate_did_suffix(&ion_did_suffix) {
58 return Err((
59 StatusCode::BAD_REQUEST,
60 Json(
61 json!({"error": format!("DID: {did} does not have a valid ION suffix with error: {err}")}),
62 ),
63 ));
64 };
65
66 if mongo_database_ion_core.contains("testnet") && did_prefix != *ION_DID_TEST_PREFIX {
68 return Err((
69 StatusCode::BAD_REQUEST,
70 Json(error_message(did, &ION_DID_TEST_PREFIX)),
71 ));
72 }
73
74 if mongo_database_ion_core.contains("mainnet") && did_prefix != *ION_DID_PREFIX {
76 return Err((
77 StatusCode::BAD_REQUEST,
78 Json(error_message(did, &ION_DID_PREFIX)),
79 ));
80 }
81 Ok(())
82}
83pub async fn validate_did(
86 Path(did): Path<String>,
87 request: Request<Body>,
88 next: Next<Body>,
89) -> impl IntoResponse {
90 tracing::info!(did);
91 match validate_did_str(&did, &ion_config().mongo_database_ion_core) {
92 Ok(_) => Ok(next.run(request).await),
93 Err(e) => Err(e),
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_strings() {
103 assert_eq!("did:ion", *ION_DID_PREFIX);
104 assert_eq!("did:ion:test", *ION_DID_TEST_PREFIX);
105 }
106
107 #[test]
108 fn test_valid_did() {
109 for (did, network) in [
111 (
112 "did:ion:test:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
113 "testnet",
114 ),
115 (
116 "did:ion:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
117 "mainnet",
118 ),
119 (
120 "did:key:z6MkhG98a8j2d3jqia13vrWqzHwHAgKTv9NjYEgdV3ndbEdD",
121 "testnet",
122 ),
123 ] {
124 assert!(validate_did_str(did, network).is_ok());
125 }
126 for (did, network) in [
128 (
130 "did:ion:test:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65",
131 "testnet",
132 ),
133 (
135 "did:ion:test:1iAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
136 "testnet",
137 ),
138 (
140 "did:ion:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
141 "testnet",
142 ),
143 (
145 "did:ion:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65",
146 "mainnet",
147 ),
148 (
150 "did:ion:1iAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
151 "mainnet",
152 ),
153 (
155 "did:ion:test:EiAtHHKFJWAk5AsM3tgCut3OiBY4ekHTf66AAjoysXL65Q",
156 "mainnet",
157 ),
158 ] {
159 assert!(validate_did_str(did, network).is_err());
160 }
161 }
162}