hashtree_cli/server/
auth.rs1use axum::{
2 body::Body,
3 extract::State,
4 http::{header, Request, Response, StatusCode},
5 middleware::Next,
6};
7use crate::storage::HashtreeStore;
8use crate::webrtc::WebRTCState;
9use std::collections::HashSet;
10use std::sync::Arc;
11
12#[derive(Clone)]
13pub struct AppState {
14 pub store: Arc<HashtreeStore>,
15 pub auth: Option<AuthCredentials>,
16 pub webrtc_peers: Option<Arc<WebRTCState>>,
18 pub max_upload_bytes: usize,
20 pub public_writes: bool,
23 pub allowed_pubkeys: HashSet<String>,
25}
26
27#[derive(Clone)]
28pub struct AuthCredentials {
29 pub username: String,
30 pub password: String,
31}
32
33pub async fn auth_middleware(
35 State(state): State<AppState>,
36 request: Request<Body>,
37 next: Next,
38) -> Result<Response<Body>, StatusCode> {
39 let Some(auth) = &state.auth else {
41 return Ok(next.run(request).await);
42 };
43
44 let auth_header = request
46 .headers()
47 .get(header::AUTHORIZATION)
48 .and_then(|v| v.to_str().ok());
49
50 let authorized = if let Some(header_value) = auth_header {
51 if let Some(credentials) = header_value.strip_prefix("Basic ") {
52 use base64::Engine;
53 let engine = base64::engine::general_purpose::STANDARD;
54 if let Ok(decoded) = engine.decode(credentials) {
55 if let Ok(decoded_str) = String::from_utf8(decoded) {
56 let expected = format!("{}:{}", auth.username, auth.password);
57 decoded_str == expected
58 } else {
59 false
60 }
61 } else {
62 false
63 }
64 } else {
65 false
66 }
67 } else {
68 false
69 };
70
71 if authorized {
72 Ok(next.run(request).await)
73 } else {
74 Ok(Response::builder()
75 .status(StatusCode::UNAUTHORIZED)
76 .header(header::WWW_AUTHENTICATE, "Basic realm=\"hashtree\"")
77 .body(Body::from("Unauthorized"))
78 .unwrap())
79 }
80}