Something went wrong. Try again.
Listen to git commits for a specific repo and run a shell command
Something went wrong. Try again.
5.5 kB · 194 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195use trust_dns_resolver::Resolver;use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
#[derive(Debug)]pub struct DidDoc { pub did: String, pub pds: String,}
fn get_txt_did(handle: &String) -> Result<String, ()> { println!(" Trying dns TXT for _atproto.{}", handle); // create a txt resolver let resolver = match Resolver::new(ResolverConfig::default(), ResolverOpts::default()) { Ok(val) => val, Err(_) => { println!(" Couldn't create a DNS resolver"); return Err(()); } };
// resolve _atproto.handle to a TXT record let txt_res = match resolver.txt_lookup("_atproto.".to_owned() + &handle) { Ok(val) => val, Err(_) => { println!(" Couldn't resolve to a TXT record"); return Err(()); } // collect all entries and convert to strings } .into_iter() .map(|x| x.to_string()) .collect::<Vec<_>>();
// filter entries which do not start with `did=` let did_res = txt_res .clone() .extract_if(.., |x| x.starts_with("did=")) .collect::<Vec<_>>(); // only 1 did= can exist // https://atproto.com/specs/handle#:~:text=If%20multiple%20valid%20records%20with%20different%20DIDs%20are%20present,%20resolution%20should%20fail. if did_res.len() != 1 { println!(" Found too many DIDs for this handle"); return Err(()); }
return Ok(did_res[0][4..].to_string());}
fn get_http_did(handle: &String) -> Result<String, ()> { println!( " Trying https for https://{}/.well-known/atproto-did", handle ); let res = match reqwest::blocking::get("https://".to_owned() + handle + "/.well-known/atproto-did") { Ok(val) => val, Err(_) => { println!(" GET request failed"); return Err(()); } };
// as per spec, non 2xx code means failure if !res.status().is_success() { println!( " Got non 2xx status code: {}", res.status().as_str() ); return Err(()); }
let did_unparsed = match res.text() { Ok(val) => val, Err(_) => { println!(" Missing or malformed body response"); return Err(()); } };
let did = did_unparsed.trim();
if !did.starts_with("did:") { println!(" Did not find a DID"); return Err(()); }; return Ok(String::from(did));}
fn parse_doc(did: String, text: String) -> Result<DidDoc, ()> { let text_json = match json::parse(&text) { Ok(val) => val, Err(_) => { println!(" Malformed DID document"); return Err(()); } };
for service in text_json["service"].members() { if service["id"] .as_str() .is_some_and(|x| x.ends_with("#atproto_pds")) && service["type"] .as_str() .is_some_and(|x| x == "AtprotoPersonalDataServer") && let Some(pds) = service["serviceEndpoint"].as_str() { return Ok(DidDoc { did: did, pds: pds.to_string(), }); } }
println!(" Missing fields from DID document"); return Err(());}
fn get_plc_doc(plc: &str) -> Result<DidDoc, ()> { let res = match reqwest::blocking::get("https://plc.directory/did:plc:".to_owned() + plc) { Ok(val) => val, Err(_) => { println!(" GET request failed"); return Err(()); } };
if !res.status().is_success() { println!(" Got non 2xx status code: {}", res.status().as_str());
return Err(()); }
return parse_doc( "did:plc:".to_owned() + plc, match res.text() { Ok(val) => val, Err(_) => { println!(" Missing or malformed body response"); return Err(()); } }, );}
fn get_web_doc(web: &str) -> Result<DidDoc, ()> { let res = match reqwest::blocking::get("https://".to_owned() + web + "/.well-known/did.json") { Ok(val) => val, Err(_) => { println!(" GET request failed"); return Err(()); } };
if !res.status().is_success() { println!(" Got non 2xx status code: {}", res.status().as_str()); return Err(()); }
return parse_doc( "did:web:".to_owned() + web, match res.text() { Ok(val) => val, Err(_) => { println!(" Missing or malformed body response"); return Err(()); } }, );}
pub fn get_did(handle: &String) -> Result<DidDoc, ()> { println!(" Getting DID for {}", handle); let did = if let Ok(did) = get_txt_did(&handle) { did } else { if let Ok(did) = get_http_did(&handle) { did } else { println!(" Could not get a DID"); return Err(()); } };
println!(" Getting DID document for {}", did); let did_doc = if did.starts_with("did:plc:") { get_plc_doc(&did[8..]) } else if did.starts_with("did:web:") { get_web_doc(&did[8..]) } else { println!(" Could not get a DID document"); Err(()) };
return did_doc;}