implement purge-did admin route properly
This commit is contained in:
		
							parent
							
								
									d4efcecad4
								
							
						
					
					
						commit
						93267f3306
					
				
					 4 changed files with 66 additions and 13 deletions
				
			
		|  | @ -7,7 +7,7 @@ edition = "2021" | |||
| anyhow = "1.0.93" | ||||
| atrium-api = { version = "0.24.8", default-features = false, features = ["tokio"] } | ||||
| bytes = { version = "1.8.0", features = ["serde"] } | ||||
| clap = { version = "4.5.21", features = ["derive"] } | ||||
| clap = { version = "4.5.21", features = ["derive", "env"] } | ||||
| ecdsa = { version = "0.16.9", features = ["verifying"] } | ||||
| fastwebsockets = { version = "0.8.0", features = ["hyper", "unstable-split", "upgrade"] } | ||||
| http-body-util = "0.1.2" | ||||
|  |  | |||
							
								
								
									
										70
									
								
								src/admin.rs
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								src/admin.rs
									
									
									
									
									
								
							|  | @ -1,12 +1,19 @@ | |||
| use std::{io::Cursor, sync::Arc}; | ||||
| 
 | ||||
| use anyhow::{bail, Result}; | ||||
| use anyhow::{Context, Result}; | ||||
| use atrium_api::com::atproto::sync::subscribe_repos; | ||||
| use hyper::{body::Incoming, Request}; | ||||
| use bytes::Buf; | ||||
| use http_body_util::BodyExt; | ||||
| use hyper::{body::Incoming, Request, Response, StatusCode}; | ||||
| use ipld_core::ipld::Ipld; | ||||
| use serde::Deserialize; | ||||
| use serde_ipld_dagcbor::DecodeError; | ||||
| 
 | ||||
| use crate::{http::ServerResponse, wire_proto::StreamEventHeader, AppState}; | ||||
| use crate::{ | ||||
|     http::{body_full, ServerResponse}, | ||||
|     wire_proto::StreamEventHeader, | ||||
|     AppState, | ||||
| }; | ||||
| 
 | ||||
| pub fn purge_did(app: &AppState, did: &str) -> Result<()> { | ||||
|     // drop commits
 | ||||
|  | @ -45,16 +52,55 @@ pub fn purge_did(app: &AppState, did: &str) -> Result<()> { | |||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| // TODO: ban host
 | ||||
| 
 | ||||
| pub async fn handle_purge_did( | ||||
|     _app: Arc<AppState>, | ||||
|     _req: Request<Incoming>, | ||||
|     app: Arc<AppState>, | ||||
|     req: Request<Incoming>, | ||||
| ) -> Result<ServerResponse> { | ||||
|     // TODO:
 | ||||
|     //   - validate admin Authorization header
 | ||||
|     //   - parse JSON body for target did
 | ||||
|     //   - run purge_did function
 | ||||
|     // TODO: we should abstract all of the response building tbh its annoyinngggg.
 | ||||
|     // u should be able to return some Err and get back a nicely-formatted error response
 | ||||
| 
 | ||||
|     bail!("not yet implemented") | ||||
|     if app.admin_password.is_none() { | ||||
|         return Ok(Response::builder() | ||||
|             .status(StatusCode::SERVICE_UNAVAILABLE) | ||||
|             .header("Content-Type", "text/plain") | ||||
|             .body(body_full("admin routes are unavailable :("))?); | ||||
|     } | ||||
| 
 | ||||
|     let Some(auth_header) = req.headers().get(hyper::header::AUTHORIZATION) else { | ||||
|         return Ok(Response::builder() | ||||
|             .status(StatusCode::UNAUTHORIZED) | ||||
|             .header("Content-Type", "text/plain") | ||||
|             .body(body_full("missing authorization header :c"))?); | ||||
|     }; | ||||
| 
 | ||||
|     let given_password = auth_header.to_str()?.strip_prefix("Bearer "); | ||||
|     if given_password.is_none() || given_password != app.admin_password.as_deref() { | ||||
|         return Ok(Response::builder() | ||||
|             .status(StatusCode::FORBIDDEN) | ||||
|             .header("Content-Type", "text/plain") | ||||
|             .body(body_full("invalid credentials for admin route >:("))?); | ||||
|     } | ||||
| 
 | ||||
|     #[derive(Deserialize)] | ||||
|     struct PurgeDidBody { | ||||
|         did: String, | ||||
|     } | ||||
| 
 | ||||
|     let body = req.collect().await?.aggregate(); | ||||
|     let body = match serde_json::from_reader::<_, PurgeDidBody>(body.reader()) { | ||||
|         Ok(body) => body, | ||||
|         Err(_) => { | ||||
|             // TODO: surely we can build out an XRPC abstraction or something
 | ||||
|             return Ok(Response::builder() | ||||
|                 .status(400) | ||||
|                 .header("Content-Type", "text/plain") | ||||
|                 .body(body_full("failed to parse request body as JSON :("))?); | ||||
|         } | ||||
|     }; | ||||
|     purge_did(&app, &body.did)?; | ||||
| 
 | ||||
|     Ok(Response::builder() | ||||
|         .status(200) | ||||
|         .header("Content-Type", "text/plain") | ||||
|         .body(body_full("o7"))?) | ||||
| } | ||||
|  |  | |||
|  | @ -14,6 +14,8 @@ pub struct AppState { | |||
|     pub db_banned_hosts: sled::Tree, | ||||
| 
 | ||||
|     pub plc_resolver: Cow<'static, str>, | ||||
|     pub admin_password: Option<String>, | ||||
| 
 | ||||
|     pub known_good_hosts: Mutex<BTreeSet<String>>, | ||||
|     pub active_indexers: Mutex<BTreeSet<String>>, | ||||
| 
 | ||||
|  | @ -36,6 +38,7 @@ impl AppState { | |||
|             raw_block_tx, | ||||
| 
 | ||||
|             plc_resolver: Cow::Borrowed("plc.directory"), | ||||
|             admin_password: None, | ||||
|             known_good_hosts: Mutex::new(hosts.into_iter().collect()), | ||||
|             active_indexers: Default::default(), | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,6 +28,9 @@ struct Args { | |||
| 
 | ||||
|     #[arg(long)] | ||||
|     drop_user_cache: bool, | ||||
| 
 | ||||
|     #[arg(env = "ADMIN_PASSWORD")] | ||||
|     admin_password: Option<String>, | ||||
| } | ||||
| 
 | ||||
| #[tokio::main] | ||||
|  | @ -56,6 +59,7 @@ async fn main() -> Result<()> { | |||
|     if let Some(plc_directory) = args.plc_resolver { | ||||
|         server.plc_resolver = Cow::Owned(plc_directory); | ||||
|     } | ||||
|     server.admin_password = args.admin_password; | ||||
| 
 | ||||
|     let server = Arc::new(server); | ||||
|     let initial_hosts: Vec<String> = { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue