this repo has no description

Better auth extractor use

lewis 8b61506c 08bec9b4

+5 -32
src/api/actor/preferences.rs
··· 1 use crate::api::error::ApiError; 2 use crate::state::AppState; 3 use axum::{ 4 Json, ··· 33 } 34 pub async fn get_preferences( 35 State(state): State<AppState>, 36 - headers: axum::http::HeaderMap, 37 ) -> Response { 38 - let token = match crate::auth::extract_bearer_token_from_header( 39 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 40 - ) { 41 - Some(t) => t, 42 - None => { 43 - return ApiError::AuthenticationRequired.into_response(); 44 - } 45 - }; 46 - let auth_user = 47 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 48 - Ok(user) => user, 49 - Err(_) => { 50 - return ApiError::AuthenticationFailed(None).into_response(); 51 - } 52 - }; 53 let has_full_access = auth_user.permissions().has_full_access(); 54 let user_id: uuid::Uuid = 55 match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", &*auth_user.did) ··· 117 } 118 pub async fn put_preferences( 119 State(state): State<AppState>, 120 - headers: axum::http::HeaderMap, 121 Json(input): Json<PutPreferencesInput>, 122 ) -> Response { 123 - let token = match crate::auth::extract_bearer_token_from_header( 124 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 125 - ) { 126 - Some(t) => t, 127 - None => { 128 - return ApiError::AuthenticationRequired.into_response(); 129 - } 130 - }; 131 - let auth_user = 132 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 133 - Ok(user) => user, 134 - Err(_) => { 135 - return ApiError::AuthenticationFailed(None).into_response(); 136 - } 137 - }; 138 let has_full_access = auth_user.permissions().has_full_access(); 139 let user_id: uuid::Uuid = 140 match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", &*auth_user.did)
··· 1 use crate::api::error::ApiError; 2 + use crate::auth::BearerAuthAllowDeactivated; 3 use crate::state::AppState; 4 use axum::{ 5 Json, ··· 34 } 35 pub async fn get_preferences( 36 State(state): State<AppState>, 37 + auth: BearerAuthAllowDeactivated, 38 ) -> Response { 39 + let auth_user = auth.0; 40 let has_full_access = auth_user.permissions().has_full_access(); 41 let user_id: uuid::Uuid = 42 match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", &*auth_user.did) ··· 104 } 105 pub async fn put_preferences( 106 State(state): State<AppState>, 107 + auth: BearerAuthAllowDeactivated, 108 Json(input): Json<PutPreferencesInput>, 109 ) -> Response { 110 + let auth_user = auth.0; 111 let has_full_access = auth_user.permissions().has_full_access(); 112 let user_id: uuid::Uuid = 113 match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", &*auth_user.did)
+17 -3
src/api/age_assurance.rs
··· 1 - use crate::auth::{extract_bearer_token_from_header, validate_bearer_token}; 2 use crate::state::AppState; 3 use axum::{ 4 Json, ··· 36 let auth_header = headers.get("Authorization").and_then(|h| h.to_str().ok()); 37 tracing::debug!(?auth_header, "age assurance: extracting token"); 38 39 - let token = extract_bearer_token_from_header(auth_header)?; 40 tracing::debug!("age assurance: got token, validating"); 41 42 - let auth_user = match validate_bearer_token(&state.db, &token).await { 43 Ok(user) => { 44 tracing::debug!(did = %user.did, "age assurance: validated user"); 45 user
··· 1 + use crate::auth::{extract_auth_token_from_header, validate_token_with_dpop}; 2 use crate::state::AppState; 3 use axum::{ 4 Json, ··· 36 let auth_header = headers.get("Authorization").and_then(|h| h.to_str().ok()); 37 tracing::debug!(?auth_header, "age assurance: extracting token"); 38 39 + let extracted = extract_auth_token_from_header(auth_header)?; 40 tracing::debug!("age assurance: got token, validating"); 41 42 + let dpop_proof = headers.get("DPoP").and_then(|h| h.to_str().ok()); 43 + let http_uri = "/"; 44 + 45 + let auth_user = match validate_token_with_dpop( 46 + &state.db, 47 + &extracted.token, 48 + extracted.is_dpop, 49 + dpop_proof, 50 + "GET", 51 + http_uri, 52 + false, 53 + false, 54 + ) 55 + .await 56 + { 57 Ok(user) => { 58 tracing::debug!(did = %user.did, "age assurance: validated user"); 59 user
+5 -4
src/api/identity/account.rs
··· 1 use super::did::verify_did_web; 2 use crate::api::error::ApiError; 3 use crate::api::repo::record::utils::create_signed_commit; 4 - use crate::auth::{ServiceTokenVerifier, extract_bearer_token_from_header, is_service_token}; 5 use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key}; 6 use crate::state::{AppState, RateLimitKind}; 7 use crate::types::{Did, Handle, PlainPassword}; ··· 96 .into_response(); 97 } 98 99 - let migration_auth = if let Some(token) = 100 - extract_bearer_token_from_header(headers.get("Authorization").and_then(|h| h.to_str().ok())) 101 - { 102 if is_service_token(&token) { 103 let verifier = ServiceTokenVerifier::new(); 104 match verifier
··· 1 use super::did::verify_did_web; 2 use crate::api::error::ApiError; 3 use crate::api::repo::record::utils::create_signed_commit; 4 + use crate::auth::{ServiceTokenVerifier, is_service_token}; 5 use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key}; 6 use crate::state::{AppState, RateLimitKind}; 7 use crate::types::{Did, Handle, PlainPassword}; ··· 96 .into_response(); 97 } 98 99 + let migration_auth = if let Some(extracted) = crate::auth::extract_auth_token_from_header( 100 + headers.get("Authorization").and_then(|h| h.to_str().ok()), 101 + ) { 102 + let token = extracted.token; 103 if is_service_token(&token) { 104 let verifier = ServiceTokenVerifier::new(); 105 match verifier
+5 -26
src/api/identity/did.rs
··· 1 use crate::api::{ApiError, DidResponse, EmptyResponse}; 2 use crate::plc::signing_key_to_did_key; 3 use crate::state::AppState; 4 use axum::{ ··· 522 523 pub async fn get_recommended_did_credentials( 524 State(state): State<AppState>, 525 - headers: axum::http::HeaderMap, 526 ) -> Response { 527 - let token = match crate::auth::extract_bearer_token_from_header( 528 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 529 - ) { 530 - Some(t) => t, 531 - None => { 532 - return ApiError::AuthenticationRequired.into_response(); 533 - } 534 - }; 535 - let auth_user = 536 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 537 - Ok(user) => user, 538 - Err(e) => return ApiError::from(e).into_response(), 539 - }; 540 let user = match sqlx::query!( 541 "SELECT handle FROM users u JOIN user_keys k ON u.id = k.user_id WHERE u.did = $1", 542 &auth_user.did ··· 601 602 pub async fn update_handle( 603 State(state): State<AppState>, 604 - headers: axum::http::HeaderMap, 605 Json(input): Json<UpdateHandleInput>, 606 ) -> Response { 607 - let token = match crate::auth::extract_bearer_token_from_header( 608 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 609 - ) { 610 - Some(t) => t, 611 - None => return ApiError::AuthenticationRequired.into_response(), 612 - }; 613 - let auth_user = 614 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 615 - Ok(user) => user, 616 - Err(e) => return ApiError::from(e).into_response(), 617 - }; 618 if let Err(e) = crate::auth::scope_check::check_identity_scope( 619 auth_user.is_oauth, 620 auth_user.scope.as_deref(),
··· 1 use crate::api::{ApiError, DidResponse, EmptyResponse}; 2 + use crate::auth::BearerAuthAllowDeactivated; 3 use crate::plc::signing_key_to_did_key; 4 use crate::state::AppState; 5 use axum::{ ··· 523 524 pub async fn get_recommended_did_credentials( 525 State(state): State<AppState>, 526 + auth: BearerAuthAllowDeactivated, 527 ) -> Response { 528 + let auth_user = auth.0; 529 let user = match sqlx::query!( 530 "SELECT handle FROM users u JOIN user_keys k ON u.id = k.user_id WHERE u.did = $1", 531 &auth_user.did ··· 590 591 pub async fn update_handle( 592 State(state): State<AppState>, 593 + auth: BearerAuthAllowDeactivated, 594 Json(input): Json<UpdateHandleInput>, 595 ) -> Response { 596 + let auth_user = auth.0; 597 if let Err(e) = crate::auth::scope_check::check_identity_scope( 598 auth_user.is_oauth, 599 auth_user.scope.as_deref(),
+3 -12
src/api/identity/plc/request.rs
··· 1 use crate::api::EmptyResponse; 2 use crate::api::error::ApiError; 3 use crate::state::AppState; 4 use axum::{ 5 extract::State, ··· 14 15 pub async fn request_plc_operation_signature( 16 State(state): State<AppState>, 17 - headers: axum::http::HeaderMap, 18 ) -> Response { 19 - let token = match crate::auth::extract_bearer_token_from_header( 20 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 21 - ) { 22 - Some(t) => t, 23 - None => return ApiError::AuthenticationRequired.into_response(), 24 - }; 25 - let auth_user = 26 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 27 - Ok(user) => user, 28 - Err(e) => return ApiError::from(e).into_response(), 29 - }; 30 if let Err(e) = crate::auth::scope_check::check_identity_scope( 31 auth_user.is_oauth, 32 auth_user.scope.as_deref(),
··· 1 use crate::api::EmptyResponse; 2 use crate::api::error::ApiError; 3 + use crate::auth::BearerAuthAllowDeactivated; 4 use crate::state::AppState; 5 use axum::{ 6 extract::State, ··· 15 16 pub async fn request_plc_operation_signature( 17 State(state): State<AppState>, 18 + auth: BearerAuthAllowDeactivated, 19 ) -> Response { 20 + let auth_user = auth.0; 21 if let Err(e) = crate::auth::scope_check::check_identity_scope( 22 auth_user.is_oauth, 23 auth_user.scope.as_deref(),
+3 -12
src/api/identity/plc/sign.rs
··· 1 use crate::api::ApiError; 2 use crate::circuit_breaker::with_circuit_breaker; 3 use crate::plc::{PlcClient, PlcError, PlcService, create_update_op, sign_operation}; 4 use crate::state::AppState; ··· 39 40 pub async fn sign_plc_operation( 41 State(state): State<AppState>, 42 - headers: axum::http::HeaderMap, 43 Json(input): Json<SignPlcOperationInput>, 44 ) -> Response { 45 - let bearer = match crate::auth::extract_bearer_token_from_header( 46 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 47 - ) { 48 - Some(t) => t, 49 - None => return ApiError::AuthenticationRequired.into_response(), 50 - }; 51 - let auth_user = 52 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &bearer).await { 53 - Ok(user) => user, 54 - Err(e) => return ApiError::from(e).into_response(), 55 - }; 56 if let Err(e) = crate::auth::scope_check::check_identity_scope( 57 auth_user.is_oauth, 58 auth_user.scope.as_deref(),
··· 1 use crate::api::ApiError; 2 + use crate::auth::BearerAuthAllowDeactivated; 3 use crate::circuit_breaker::with_circuit_breaker; 4 use crate::plc::{PlcClient, PlcError, PlcService, create_update_op, sign_operation}; 5 use crate::state::AppState; ··· 40 41 pub async fn sign_plc_operation( 42 State(state): State<AppState>, 43 + auth: BearerAuthAllowDeactivated, 44 Json(input): Json<SignPlcOperationInput>, 45 ) -> Response { 46 + let auth_user = auth.0; 47 if let Err(e) = crate::auth::scope_check::check_identity_scope( 48 auth_user.is_oauth, 49 auth_user.scope.as_deref(),
+3 -16
src/api/identity/plc/submit.rs
··· 1 use crate::api::{ApiError, EmptyResponse}; 2 use crate::circuit_breaker::with_circuit_breaker; 3 use crate::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation}; 4 use crate::state::AppState; ··· 19 20 pub async fn submit_plc_operation( 21 State(state): State<AppState>, 22 - headers: axum::http::HeaderMap, 23 Json(input): Json<SubmitPlcOperationInput>, 24 ) -> Response { 25 - let bearer = match crate::auth::extract_bearer_token_from_header( 26 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 27 - ) { 28 - Some(t) => t, 29 - None => { 30 - return ApiError::AuthenticationRequired.into_response(); 31 - } 32 - }; 33 - let auth_user = 34 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &bearer).await { 35 - Ok(user) => user, 36 - Err(e) => { 37 - return ApiError::from(e).into_response(); 38 - } 39 - }; 40 if let Err(e) = crate::auth::scope_check::check_identity_scope( 41 auth_user.is_oauth, 42 auth_user.scope.as_deref(),
··· 1 use crate::api::{ApiError, EmptyResponse}; 2 + use crate::auth::BearerAuthAllowDeactivated; 3 use crate::circuit_breaker::with_circuit_breaker; 4 use crate::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation}; 5 use crate::state::AppState; ··· 20 21 pub async fn submit_plc_operation( 22 State(state): State<AppState>, 23 + auth: BearerAuthAllowDeactivated, 24 Json(input): Json<SubmitPlcOperationInput>, 25 ) -> Response { 26 + let auth_user = auth.0; 27 if let Err(e) = crate::auth::scope_check::check_identity_scope( 28 auth_user.is_oauth, 29 auth_user.scope.as_deref(),
+3 -14
src/api/moderation/mod.rs
··· 1 use crate::api::ApiError; 2 use crate::api::proxy_client::{is_ssrf_safe, proxy_client}; 3 use crate::state::AppState; 4 use axum::{ 5 Json, ··· 41 42 pub async fn create_report( 43 State(state): State<AppState>, 44 - headers: axum::http::HeaderMap, 45 Json(input): Json<CreateReportInput>, 46 ) -> Response { 47 - let token = match crate::auth::extract_bearer_token_from_header( 48 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 49 - ) { 50 - Some(t) => t, 51 - None => return ApiError::AuthenticationRequired.into_response(), 52 - }; 53 - 54 - let auth_user = 55 - match crate::auth::validate_bearer_token_allow_takendown(&state.db, &token).await { 56 - Ok(user) => user, 57 - Err(e) => return ApiError::from(e).into_response(), 58 - }; 59 - 60 let did = &auth_user.did; 61 62 if let Some((service_url, service_did)) = get_report_service_config() {
··· 1 use crate::api::ApiError; 2 use crate::api::proxy_client::{is_ssrf_safe, proxy_client}; 3 + use crate::auth::extractor::BearerAuthAllowTakendown; 4 use crate::state::AppState; 5 use axum::{ 6 Json, ··· 42 43 pub async fn create_report( 44 State(state): State<AppState>, 45 + auth: BearerAuthAllowTakendown, 46 Json(input): Json<CreateReportInput>, 47 ) -> Response { 48 + let auth_user = auth.0; 49 let did = &auth_user.did; 50 51 if let Some((service_url, service_did)) = get_report_service_config() {
+7 -44
src/api/notification_prefs.rs
··· 1 use crate::api::error::ApiError; 2 - use crate::auth::validate_bearer_token; 3 use crate::state::AppState; 4 use axum::{ 5 Json, 6 extract::State, 7 - http::HeaderMap, 8 response::{IntoResponse, Response}, 9 }; 10 use serde::{Deserialize, Serialize}; ··· 25 pub signal_verified: bool, 26 } 27 28 - pub async fn get_notification_prefs(State(state): State<AppState>, headers: HeaderMap) -> Response { 29 - let token = match crate::auth::extract_bearer_token_from_header( 30 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 31 - ) { 32 - Some(t) => t, 33 - None => return ApiError::AuthenticationRequired.into_response(), 34 - }; 35 - let user = match validate_bearer_token(&state.db, &token).await { 36 - Ok(u) => u, 37 - Err(_) => { 38 - return ApiError::AuthenticationFailed(None).into_response(); 39 - } 40 - }; 41 let row = match sqlx::query( 42 r#" 43 SELECT ··· 100 pub notifications: Vec<NotificationHistoryEntry>, 101 } 102 103 - pub async fn get_notification_history( 104 - State(state): State<AppState>, 105 - headers: HeaderMap, 106 - ) -> Response { 107 - let token = match crate::auth::extract_bearer_token_from_header( 108 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 109 - ) { 110 - Some(t) => t, 111 - None => return ApiError::AuthenticationRequired.into_response(), 112 - }; 113 - let user = match validate_bearer_token(&state.db, &token).await { 114 - Ok(u) => u, 115 - Err(_) => { 116 - return ApiError::AuthenticationFailed(None).into_response(); 117 - } 118 - }; 119 120 let user_id: uuid::Uuid = 121 match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", &user.did) ··· 253 254 pub async fn update_notification_prefs( 255 State(state): State<AppState>, 256 - headers: HeaderMap, 257 Json(input): Json<UpdateNotificationPrefsInput>, 258 ) -> Response { 259 - let token = match crate::auth::extract_bearer_token_from_header( 260 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 261 - ) { 262 - Some(t) => t, 263 - None => return ApiError::AuthenticationRequired.into_response(), 264 - }; 265 - let user = match validate_bearer_token(&state.db, &token).await { 266 - Ok(u) => u, 267 - Err(_) => { 268 - return ApiError::AuthenticationFailed(None).into_response(); 269 - } 270 - }; 271 272 let user_row = match sqlx::query!( 273 "SELECT id, handle, email FROM users WHERE did = $1",
··· 1 use crate::api::error::ApiError; 2 + use crate::auth::BearerAuth; 3 use crate::state::AppState; 4 use axum::{ 5 Json, 6 extract::State, 7 response::{IntoResponse, Response}, 8 }; 9 use serde::{Deserialize, Serialize}; ··· 24 pub signal_verified: bool, 25 } 26 27 + pub async fn get_notification_prefs(State(state): State<AppState>, auth: BearerAuth) -> Response { 28 + let user = auth.0; 29 let row = match sqlx::query( 30 r#" 31 SELECT ··· 88 pub notifications: Vec<NotificationHistoryEntry>, 89 } 90 91 + pub async fn get_notification_history(State(state): State<AppState>, auth: BearerAuth) -> Response { 92 + let user = auth.0; 93 94 let user_id: uuid::Uuid = 95 match sqlx::query_scalar!("SELECT id FROM users WHERE did = $1", &user.did) ··· 227 228 pub async fn update_notification_prefs( 229 State(state): State<AppState>, 230 + auth: BearerAuth, 231 Json(input): Json<UpdateNotificationPrefsInput>, 232 ) -> Response { 233 + let user = auth.0; 234 235 let user_row = match sqlx::query!( 236 "SELECT id, handle, email FROM users WHERE did = $1",
+19 -11
src/api/proxy.rs
··· 214 info!("Proxying {} request to {}", method_verb, target_url); 215 216 let client = proxy_client(); 217 - let mut request_builder = client.request(method_verb, &target_url); 218 219 let mut auth_header_val = headers.get("Authorization").cloned(); 220 - if let Some(token) = crate::auth::extract_bearer_token_from_header( 221 headers.get("Authorization").and_then(|h| h.to_str().ok()), 222 ) { 223 - match crate::auth::validate_bearer_token(&state.db, &token).await { 224 Ok(auth_user) => { 225 if let Err(e) = crate::auth::scope_check::check_rpc_scope( 226 auth_user.is_oauth, ··· 254 Err(e) => { 255 warn!("Token validation failed: {:?}", e); 256 if matches!(e, crate::auth::TokenValidationError::TokenExpired) { 257 - let auth_header_str = headers 258 - .get("Authorization") 259 - .and_then(|h| h.to_str().ok()) 260 - .unwrap_or(""); 261 - let is_dpop = auth_header_str 262 - .trim() 263 - .get(..5) 264 - .is_some_and(|s| s.eq_ignore_ascii_case("dpop ")); 265 let scheme = if is_dpop { "DPoP" } else { "Bearer" }; 266 let www_auth = format!( 267 "{} error=\"invalid_token\", error_description=\"Token has expired\"",
··· 214 info!("Proxying {} request to {}", method_verb, target_url); 215 216 let client = proxy_client(); 217 + let mut request_builder = client.request(method_verb.clone(), &target_url); 218 219 let mut auth_header_val = headers.get("Authorization").cloned(); 220 + if let Some(extracted) = crate::auth::extract_auth_token_from_header( 221 headers.get("Authorization").and_then(|h| h.to_str().ok()), 222 ) { 223 + let token = extracted.token; 224 + let dpop_proof = headers.get("DPoP").and_then(|h| h.to_str().ok()); 225 + let http_uri = uri.to_string(); 226 + 227 + match crate::auth::validate_token_with_dpop( 228 + &state.db, 229 + &token, 230 + extracted.is_dpop, 231 + dpop_proof, 232 + method_verb.as_str(), 233 + &http_uri, 234 + false, 235 + false, 236 + ) 237 + .await 238 + { 239 Ok(auth_user) => { 240 if let Err(e) = crate::auth::scope_check::check_rpc_scope( 241 auth_user.is_oauth, ··· 269 Err(e) => { 270 warn!("Token validation failed: {:?}", e); 271 if matches!(e, crate::auth::TokenValidationError::TokenExpired) { 272 + let is_dpop = extracted.is_dpop; 273 let scheme = if is_dpop { "DPoP" } else { "Bearer" }; 274 let www_auth = format!( 275 "{} error=\"invalid_token\", error_description=\"Token has expired\"",
+25 -18
src/api/repo/blob.rs
··· 1 use crate::api::error::ApiError; 2 - use crate::auth::{ServiceTokenVerifier, is_service_token}; 3 use crate::delegation::{self, DelegationActionType}; 4 use crate::state::AppState; 5 use crate::util::get_max_blob_size; ··· 45 headers: axum::http::HeaderMap, 46 body: Body, 47 ) -> Response { 48 - let Some(token) = crate::auth::extract_bearer_token_from_header( 49 headers.get("Authorization").and_then(|h| h.to_str().ok()), 50 - ) else { 51 - return ApiError::AuthenticationRequired.into_response(); 52 }; 53 54 let is_service_auth = is_service_token(&token); 55 ··· 74 } 75 } 76 } else { 77 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 78 Ok(user) => { 79 let mime_type_for_check = headers 80 .get("content-type") ··· 283 284 pub async fn list_missing_blobs( 285 State(state): State<AppState>, 286 - headers: axum::http::HeaderMap, 287 Query(params): Query<ListMissingBlobsParams>, 288 ) -> Response { 289 - let Some(token) = crate::auth::extract_bearer_token_from_header( 290 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 291 - ) else { 292 - return ApiError::AuthenticationRequired.into_response(); 293 - }; 294 - let auth_user = 295 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 296 - Ok(user) => user, 297 - Err(_) => { 298 - return ApiError::AuthenticationFailed(None).into_response(); 299 - } 300 - }; 301 let did = auth_user.did; 302 let user_query = sqlx::query!("SELECT id FROM users WHERE did = $1", did.as_str()) 303 .fetch_optional(&state.db)
··· 1 use crate::api::error::ApiError; 2 + use crate::auth::{BearerAuthAllowDeactivated, ServiceTokenVerifier, is_service_token}; 3 use crate::delegation::{self, DelegationActionType}; 4 use crate::state::AppState; 5 use crate::util::get_max_blob_size; ··· 45 headers: axum::http::HeaderMap, 46 body: Body, 47 ) -> Response { 48 + let extracted = match crate::auth::extract_auth_token_from_header( 49 headers.get("Authorization").and_then(|h| h.to_str().ok()), 50 + ) { 51 + Some(t) => t, 52 + None => return ApiError::AuthenticationRequired.into_response(), 53 }; 54 + let token = extracted.token; 55 56 let is_service_auth = is_service_token(&token); 57 ··· 76 } 77 } 78 } else { 79 + let dpop_proof = headers.get("DPoP").and_then(|h| h.to_str().ok()); 80 + let http_uri = format!( 81 + "https://{}/xrpc/com.atproto.repo.uploadBlob", 82 + std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()) 83 + ); 84 + match crate::auth::validate_token_with_dpop( 85 + &state.db, 86 + &token, 87 + extracted.is_dpop, 88 + dpop_proof, 89 + "POST", 90 + &http_uri, 91 + true, 92 + false, 93 + ) 94 + .await 95 + { 96 Ok(user) => { 97 let mime_type_for_check = headers 98 .get("content-type") ··· 301 302 pub async fn list_missing_blobs( 303 State(state): State<AppState>, 304 + auth: BearerAuthAllowDeactivated, 305 Query(params): Query<ListMissingBlobsParams>, 306 ) -> Response { 307 + let auth_user = auth.0; 308 let did = auth_user.did; 309 let user_query = sqlx::query!("SELECT id FROM users WHERE did = $1", did.as_str()) 310 .fetch_optional(&state.db)
+3 -12
src/api/repo/import.rs
··· 1 use crate::api::EmptyResponse; 2 use crate::api::error::ApiError; 3 use crate::api::repo::record::create_signed_commit; 4 use crate::state::AppState; 5 use crate::sync::import::{ImportError, apply_import, parse_car}; 6 use crate::sync::verify::CarVerifier; ··· 20 21 pub async fn import_repo( 22 State(state): State<AppState>, 23 - headers: axum::http::HeaderMap, 24 body: Bytes, 25 ) -> Response { 26 let accepting_imports = std::env::var("ACCEPTING_REPO_IMPORTS") ··· 41 )) 42 .into_response(); 43 } 44 - let token = match crate::auth::extract_bearer_token_from_header( 45 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 46 - ) { 47 - Some(t) => t, 48 - None => return ApiError::AuthenticationRequired.into_response(), 49 - }; 50 - let auth_user = 51 - match crate::auth::validate_bearer_token_allow_deactivated(&state.db, &token).await { 52 - Ok(user) => user, 53 - Err(e) => return ApiError::from(e).into_response(), 54 - }; 55 let did = &auth_user.did; 56 let user = match sqlx::query!( 57 "SELECT id, handle, deactivated_at, takedown_ref FROM users WHERE did = $1",
··· 1 use crate::api::EmptyResponse; 2 use crate::api::error::ApiError; 3 use crate::api::repo::record::create_signed_commit; 4 + use crate::auth::BearerAuthAllowDeactivated; 5 use crate::state::AppState; 6 use crate::sync::import::{ImportError, apply_import, parse_car}; 7 use crate::sync::verify::CarVerifier; ··· 21 22 pub async fn import_repo( 23 State(state): State<AppState>, 24 + auth: BearerAuthAllowDeactivated, 25 body: Bytes, 26 ) -> Response { 27 let accepting_imports = std::env::var("ACCEPTING_REPO_IMPORTS") ··· 42 )) 43 .into_response(); 44 } 45 + let auth_user = auth.0; 46 let did = &auth_user.did; 47 let user = match sqlx::query!( 48 "SELECT id, handle, deactivated_at, takedown_ref FROM users WHERE did = $1",
+3 -10
src/api/repo/record/batch.rs
··· 2 use super::write::has_verified_comms_channel; 3 use crate::api::error::ApiError; 4 use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids}; 5 use crate::delegation::{self, DelegationActionType}; 6 use crate::repo::tracking::TrackingBlockStore; 7 use crate::state::AppState; ··· 85 86 pub async fn apply_writes( 87 State(state): State<AppState>, 88 - headers: axum::http::HeaderMap, 89 Json(input): Json<ApplyWritesInput>, 90 ) -> Response { 91 info!( ··· 93 input.repo, 94 input.writes.len() 95 ); 96 - let Some(token) = crate::auth::extract_bearer_token_from_header( 97 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 98 - ) else { 99 - return ApiError::AuthenticationRequired.into_response(); 100 - }; 101 - let auth_user = match crate::auth::validate_bearer_token(&state.db, &token).await { 102 - Ok(user) => user, 103 - Err(_) => return ApiError::AuthenticationFailed(None).into_response(), 104 - }; 105 let did = auth_user.did.clone(); 106 let is_oauth = auth_user.is_oauth; 107 let scope = auth_user.scope;
··· 2 use super::write::has_verified_comms_channel; 3 use crate::api::error::ApiError; 4 use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids}; 5 + use crate::auth::BearerAuth; 6 use crate::delegation::{self, DelegationActionType}; 7 use crate::repo::tracking::TrackingBlockStore; 8 use crate::state::AppState; ··· 86 87 pub async fn apply_writes( 88 State(state): State<AppState>, 89 + auth: BearerAuth, 90 Json(input): Json<ApplyWritesInput>, 91 ) -> Response { 92 info!( ··· 94 input.repo, 95 input.writes.len() 96 ); 97 + let auth_user = auth.0; 98 let did = auth_user.did.clone(); 99 let is_oauth = auth_user.is_oauth; 100 let scope = auth_user.scope;
+1
src/api/repo/record/write.rs
··· 77 http_method, 78 http_uri, 79 false, 80 ) 81 .await 82 .map_err(|e| {
··· 77 http_method, 78 http_uri, 79 false, 80 + false, 81 ) 82 .await 83 .map_err(|e| {
+4
src/api/server/account_status.rs
··· 59 "GET", 60 &http_uri, 61 true, 62 ) 63 .await 64 { ··· 370 "POST", 371 &http_uri, 372 true, 373 ) 374 .await 375 { ··· 561 "POST", 562 &http_uri, 563 false, 564 ) 565 .await 566 { ··· 646 "POST", 647 &http_uri, 648 true, 649 ) 650 .await 651 {
··· 59 "GET", 60 &http_uri, 61 true, 62 + false, 63 ) 64 .await 65 { ··· 371 "POST", 372 &http_uri, 373 true, 374 + false, 375 ) 376 .await 377 { ··· 563 "POST", 564 &http_uri, 565 false, 566 + false, 567 ) 568 .await 569 { ··· 649 "POST", 650 &http_uri, 651 true, 652 + false, 653 ) 654 .await 655 {
+2 -12
src/api/server/email.rs
··· 193 194 pub async fn update_email( 195 State(state): State<AppState>, 196 - headers: axum::http::HeaderMap, 197 Json(input): Json<UpdateEmailInput>, 198 ) -> Response { 199 - let Some(bearer_token) = crate::auth::extract_bearer_token_from_header( 200 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 201 - ) else { 202 - return ApiError::AuthenticationRequired.into_response(); 203 - }; 204 - 205 - let auth_result = crate::auth::validate_bearer_token(&state.db, &bearer_token).await; 206 - let auth_user = match auth_result { 207 - Ok(user) => user, 208 - Err(e) => return ApiError::from(e).into_response(), 209 - }; 210 211 if let Err(e) = crate::auth::scope_check::check_account_scope( 212 auth_user.is_oauth,
··· 193 194 pub async fn update_email( 195 State(state): State<AppState>, 196 + auth: BearerAuth, 197 Json(input): Json<UpdateEmailInput>, 198 ) -> Response { 199 + let auth_user = auth.0; 200 201 if let Err(e) = crate::auth::scope_check::check_account_scope( 202 auth_user.is_oauth,
+2
src/api/server/migration.rs
··· 58 "POST", 59 &http_uri, 60 true, 61 ) 62 .await 63 { ··· 224 "GET", 225 &http_uri, 226 true, 227 ) 228 .await 229 {
··· 58 "POST", 59 &http_uri, 60 true, 61 + false, 62 ) 63 .await 64 { ··· 225 "GET", 226 &http_uri, 227 true, 228 + false, 229 ) 230 .await 231 {
+5 -4
src/api/server/passkey_account.rs
··· 18 use uuid::Uuid; 19 20 use crate::api::repo::record::utils::create_signed_commit; 21 - use crate::auth::{ServiceTokenVerifier, extract_bearer_token_from_header, is_service_token}; 22 use crate::state::{AppState, RateLimitKind}; 23 use crate::types::{Did, Handle, PlainPassword}; 24 use crate::validation::validate_password; ··· 108 .into_response(); 109 } 110 111 - let byod_auth = if let Some(token) = 112 - extract_bearer_token_from_header(headers.get("Authorization").and_then(|h| h.to_str().ok())) 113 - { 114 if is_service_token(&token) { 115 let verifier = ServiceTokenVerifier::new(); 116 match verifier
··· 18 use uuid::Uuid; 19 20 use crate::api::repo::record::utils::create_signed_commit; 21 + use crate::auth::{ServiceTokenVerifier, is_service_token}; 22 use crate::state::{AppState, RateLimitKind}; 23 use crate::types::{Did, Handle, PlainPassword}; 24 use crate::validation::validate_password; ··· 108 .into_response(); 109 } 110 111 + let byod_auth = if let Some(extracted) = crate::auth::extract_auth_token_from_header( 112 + headers.get("Authorization").and_then(|h| h.to_str().ok()), 113 + ) { 114 + let token = extracted.token; 115 if is_service_token(&token) { 116 let verifier = ServiceTokenVerifier::new(); 117 match verifier
+10 -9
src/api/server/session.rs
··· 365 pub async fn delete_session( 366 State(state): State<AppState>, 367 headers: axum::http::HeaderMap, 368 ) -> Response { 369 - let token = match crate::auth::extract_bearer_token_from_header( 370 headers.get("Authorization").and_then(|h| h.to_str().ok()), 371 ) { 372 Some(t) => t, 373 None => return ApiError::AuthenticationRequired.into_response(), 374 }; 375 - let jti = match crate::auth::get_jti_from_token(&token) { 376 Ok(jti) => jti, 377 Err(_) => return ApiError::AuthenticationFailed(None).into_response(), 378 }; 379 - let did = crate::auth::get_did_from_token(&token).ok(); 380 match sqlx::query!("DELETE FROM session_tokens WHERE access_jti = $1", jti) 381 .execute(&state.db) 382 .await ··· 408 tracing::warn!(ip = %client_ip, "Refresh session rate limit exceeded"); 409 return ApiError::RateLimitExceeded(None).into_response(); 410 } 411 - let refresh_token = match crate::auth::extract_bearer_token_from_header( 412 headers.get("Authorization").and_then(|h| h.to_str().ok()), 413 ) { 414 Some(t) => t, 415 None => return ApiError::AuthenticationRequired.into_response(), 416 }; 417 let refresh_jti = match crate::auth::get_jti_from_token(&refresh_token) { 418 Ok(jti) => jti, 419 Err(_) => { ··· 1048 headers: HeaderMap, 1049 auth: BearerAuth, 1050 ) -> Response { 1051 - let current_jti = headers 1052 - .get("authorization") 1053 - .and_then(|v| v.to_str().ok()) 1054 - .and_then(|v| v.strip_prefix("Bearer ")) 1055 - .and_then(|token| crate::auth::get_jti_from_token(token).ok()); 1056 1057 let Some(ref jti) = current_jti else { 1058 return ApiError::InvalidToken(None).into_response();
··· 365 pub async fn delete_session( 366 State(state): State<AppState>, 367 headers: axum::http::HeaderMap, 368 + _auth: BearerAuth, 369 ) -> Response { 370 + let extracted = match crate::auth::extract_auth_token_from_header( 371 headers.get("Authorization").and_then(|h| h.to_str().ok()), 372 ) { 373 Some(t) => t, 374 None => return ApiError::AuthenticationRequired.into_response(), 375 }; 376 + let jti = match crate::auth::get_jti_from_token(&extracted.token) { 377 Ok(jti) => jti, 378 Err(_) => return ApiError::AuthenticationFailed(None).into_response(), 379 }; 380 + let did = crate::auth::get_did_from_token(&extracted.token).ok(); 381 match sqlx::query!("DELETE FROM session_tokens WHERE access_jti = $1", jti) 382 .execute(&state.db) 383 .await ··· 409 tracing::warn!(ip = %client_ip, "Refresh session rate limit exceeded"); 410 return ApiError::RateLimitExceeded(None).into_response(); 411 } 412 + let extracted = match crate::auth::extract_auth_token_from_header( 413 headers.get("Authorization").and_then(|h| h.to_str().ok()), 414 ) { 415 Some(t) => t, 416 None => return ApiError::AuthenticationRequired.into_response(), 417 }; 418 + let refresh_token = extracted.token; 419 let refresh_jti = match crate::auth::get_jti_from_token(&refresh_token) { 420 Ok(jti) => jti, 421 Err(_) => { ··· 1050 headers: HeaderMap, 1051 auth: BearerAuth, 1052 ) -> Response { 1053 + let current_jti = crate::auth::extract_auth_token_from_header( 1054 + headers.get("authorization").and_then(|v| v.to_str().ok()), 1055 + ) 1056 + .and_then(|extracted| crate::auth::get_jti_from_token(&extracted.token).ok()); 1057 1058 let Some(ref jti) = current_jti else { 1059 return ApiError::InvalidToken(None).into_response();
+21 -16
src/api/temp.rs
··· 1 use crate::api::error::ApiError; 2 - use crate::auth::{extract_bearer_token_from_header, validate_bearer_token}; 3 use crate::state::AppState; 4 use axum::{ 5 Json, ··· 23 } 24 25 pub async fn check_signup_queue(State(state): State<AppState>, headers: HeaderMap) -> Response { 26 - if let Some(token) = 27 - extract_bearer_token_from_header(headers.get("Authorization").and_then(|h| h.to_str().ok())) 28 - && let Ok(user) = validate_bearer_token(&state.db, &token).await 29 - && user.is_oauth 30 { 31 - return ApiError::Forbidden.into_response(); 32 } 33 Json(CheckSignupQueueOutput { 34 activated: true, ··· 52 53 pub async fn dereference_scope( 54 State(state): State<AppState>, 55 - headers: HeaderMap, 56 Json(input): Json<DereferenceScopeInput>, 57 ) -> Response { 58 - let Some(token) = extract_bearer_token_from_header( 59 - headers.get("Authorization").and_then(|h| h.to_str().ok()), 60 - ) else { 61 - return ApiError::AuthenticationRequired.into_response(); 62 - }; 63 - 64 - if validate_bearer_token(&state.db, &token).await.is_err() { 65 - return ApiError::AuthenticationFailed(None).into_response(); 66 - } 67 68 let scope_parts: Vec<&str> = input.scope.split_whitespace().collect(); 69 let mut resolved_scopes: Vec<String> = Vec::new();
··· 1 use crate::api::error::ApiError; 2 + use crate::auth::{BearerAuth, extract_auth_token_from_header, validate_token_with_dpop}; 3 use crate::state::AppState; 4 use axum::{ 5 Json, ··· 23 } 24 25 pub async fn check_signup_queue(State(state): State<AppState>, headers: HeaderMap) -> Response { 26 + if let Some(extracted) = 27 + extract_auth_token_from_header(headers.get("Authorization").and_then(|h| h.to_str().ok())) 28 { 29 + let dpop_proof = headers.get("DPoP").and_then(|h| h.to_str().ok()); 30 + if let Ok(user) = validate_token_with_dpop( 31 + &state.db, 32 + &extracted.token, 33 + extracted.is_dpop, 34 + dpop_proof, 35 + "GET", 36 + "/", 37 + false, 38 + false, 39 + ) 40 + .await 41 + && user.is_oauth 42 + { 43 + return ApiError::Forbidden.into_response(); 44 + } 45 } 46 Json(CheckSignupQueueOutput { 47 activated: true, ··· 65 66 pub async fn dereference_scope( 67 State(state): State<AppState>, 68 + auth: BearerAuth, 69 Json(input): Json<DereferenceScopeInput>, 70 ) -> Response { 71 + let _ = auth; 72 73 let scope_parts: Vec<&str> = input.scope.split_whitespace().collect(); 74 let mut resolved_scopes: Vec<String> = Vec::new();
+58 -2
src/auth/extractor.rs
··· 5 }; 6 7 use super::{ 8 - AuthenticatedUser, TokenValidationError, validate_bearer_token_cached, 9 - validate_bearer_token_cached_allow_deactivated, validate_token_with_dpop, 10 }; 11 use crate::api::error::ApiError; 12 use crate::state::AppState; ··· 136 method, 137 &uri, 138 false, 139 ) 140 .await 141 { ··· 191 method, 192 &uri, 193 true, 194 ) 195 .await 196 { ··· 216 } 217 } 218 219 pub struct BearerAuthAdmin(pub AuthenticatedUser); 220 221 impl FromRequestParts<AppState> for BearerAuthAdmin { ··· 247 dpop_proof, 248 method, 249 &uri, 250 false, 251 ) 252 .await
··· 5 }; 6 7 use super::{ 8 + AuthenticatedUser, TokenValidationError, validate_bearer_token_allow_takendown, 9 + validate_bearer_token_cached, validate_bearer_token_cached_allow_deactivated, 10 + validate_token_with_dpop, 11 }; 12 use crate::api::error::ApiError; 13 use crate::state::AppState; ··· 137 method, 138 &uri, 139 false, 140 + false, 141 ) 142 .await 143 { ··· 193 method, 194 &uri, 195 true, 196 + false, 197 ) 198 .await 199 { ··· 219 } 220 } 221 222 + pub struct BearerAuthAllowTakendown(pub AuthenticatedUser); 223 + 224 + impl FromRequestParts<AppState> for BearerAuthAllowTakendown { 225 + type Rejection = AuthError; 226 + 227 + async fn from_request_parts( 228 + parts: &mut Parts, 229 + state: &AppState, 230 + ) -> Result<Self, Self::Rejection> { 231 + let auth_header = parts 232 + .headers 233 + .get(AUTHORIZATION) 234 + .ok_or(AuthError::MissingToken)? 235 + .to_str() 236 + .map_err(|_| AuthError::InvalidFormat)?; 237 + 238 + let extracted = 239 + extract_auth_token_from_header(Some(auth_header)).ok_or(AuthError::InvalidFormat)?; 240 + 241 + if extracted.is_dpop { 242 + let dpop_proof = parts.headers.get("dpop").and_then(|h| h.to_str().ok()); 243 + let method = parts.method.as_str(); 244 + let uri = build_full_url(&parts.uri.to_string()); 245 + 246 + match validate_token_with_dpop( 247 + &state.db, 248 + &extracted.token, 249 + true, 250 + dpop_proof, 251 + method, 252 + &uri, 253 + false, 254 + true, 255 + ) 256 + .await 257 + { 258 + Ok(user) => Ok(BearerAuthAllowTakendown(user)), 259 + Err(TokenValidationError::AccountDeactivated) => Err(AuthError::AccountDeactivated), 260 + Err(TokenValidationError::TokenExpired) => Err(AuthError::TokenExpired), 261 + Err(_) => Err(AuthError::AuthenticationFailed), 262 + } 263 + } else { 264 + match validate_bearer_token_allow_takendown(&state.db, &extracted.token).await { 265 + Ok(user) => Ok(BearerAuthAllowTakendown(user)), 266 + Err(TokenValidationError::AccountDeactivated) => Err(AuthError::AccountDeactivated), 267 + Err(TokenValidationError::TokenExpired) => Err(AuthError::TokenExpired), 268 + Err(_) => Err(AuthError::AuthenticationFailed), 269 + } 270 + } 271 + } 272 + } 273 + 274 pub struct BearerAuthAdmin(pub AuthenticatedUser); 275 276 impl FromRequestParts<AppState> for BearerAuthAdmin { ··· 302 dpop_proof, 303 method, 304 &uri, 305 + false, 306 false, 307 ) 308 .await
+6 -2
src/auth/mod.rs
··· 416 let _ = cache.delete(&status_cache_key).await; 417 } 418 419 pub async fn validate_token_with_dpop( 420 db: &PgPool, 421 token: &str, ··· 424 http_method: &str, 425 http_uri: &str, 426 allow_deactivated: bool, 427 ) -> Result<AuthenticatedUser, TokenValidationError> { 428 if !is_dpop_token { 429 - if allow_deactivated { 430 return validate_bearer_token_allow_deactivated(db, token).await; 431 } else { 432 return validate_bearer_token(db, token).await; ··· 464 if !allow_deactivated && status.is_deactivated() { 465 return Err(TokenValidationError::AccountDeactivated); 466 } 467 - if status.is_takendown() { 468 return Err(TokenValidationError::AccountTakedown); 469 } 470 let key_bytes = if let (Some(kb), Some(ev)) =
··· 416 let _ = cache.delete(&status_cache_key).await; 417 } 418 419 + #[allow(clippy::too_many_arguments)] 420 pub async fn validate_token_with_dpop( 421 db: &PgPool, 422 token: &str, ··· 425 http_method: &str, 426 http_uri: &str, 427 allow_deactivated: bool, 428 + allow_takendown: bool, 429 ) -> Result<AuthenticatedUser, TokenValidationError> { 430 if !is_dpop_token { 431 + if allow_takendown { 432 + return validate_bearer_token_allow_takendown(db, token).await; 433 + } else if allow_deactivated { 434 return validate_bearer_token_allow_deactivated(db, token).await; 435 } else { 436 return validate_bearer_token(db, token).await; ··· 468 if !allow_deactivated && status.is_deactivated() { 469 return Err(TokenValidationError::AccountDeactivated); 470 } 471 + if !allow_takendown && status.is_takendown() { 472 return Err(TokenValidationError::AccountTakedown); 473 } 474 let key_bytes = if let (Some(kb), Some(ev)) =
+3 -1
src/oauth/client.rs
··· 82 .connect_timeout(std::time::Duration::from_secs(10)) 83 .pool_max_idle_per_host(10) 84 .pool_idle_timeout(std::time::Duration::from_secs(90)) 85 - .user_agent("Tranquil-PDS/1.0 (ATProto; +https://tangled.org/lewis.moe/bspds-sandbox)") 86 .build() 87 .unwrap_or_else(|_| Client::new()), 88 cache_ttl_secs,
··· 82 .connect_timeout(std::time::Duration::from_secs(10)) 83 .pool_max_idle_per_host(10) 84 .pool_idle_timeout(std::time::Duration::from_secs(90)) 85 + .user_agent( 86 + "Tranquil-PDS/1.0 (ATProto; +https://tangled.org/lewis.moe/bspds-sandbox)", 87 + ) 88 .build() 89 .unwrap_or_else(|_| Client::new()), 90 cache_ttl_secs,
+8 -3
src/oauth/endpoints/authorize.rs
··· 56 } 57 58 fn is_granular_scope(s: &str) -> bool { 59 - s.starts_with("repo:") || s.starts_with("repo?") || s == "repo" 60 - || s.starts_with("blob:") || s.starts_with("blob?") || s == "blob" 61 - || s.starts_with("rpc:") || s.starts_with("rpc?") 62 || s.starts_with("account:") 63 || s.starts_with("identity:") 64 }
··· 56 } 57 58 fn is_granular_scope(s: &str) -> bool { 59 + s.starts_with("repo:") 60 + || s.starts_with("repo?") 61 + || s == "repo" 62 + || s.starts_with("blob:") 63 + || s.starts_with("blob?") 64 + || s == "blob" 65 + || s.starts_with("rpc:") 66 + || s.starts_with("rpc?") 67 || s.starts_with("account:") 68 || s.starts_with("identity:") 69 }
+5 -7
src/oauth/scopes/permission_set.rs
··· 57 async fn expand_permission_set(nsid: &str) -> Result<String, String> { 58 { 59 let cache = LEXICON_CACHE.read().await; 60 - if let Some(cached) = cache.get(nsid) { 61 - if cached.cached_at.elapsed().as_secs() < CACHE_TTL_SECS { 62 - debug!(nsid, "Using cached permission set expansion"); 63 - return Ok(cached.expanded_scope.clone()); 64 - } 65 } 66 } 67 ··· 156 157 #[cfg(test)] 158 mod tests { 159 - use super::*; 160 - 161 #[test] 162 fn test_nsid_to_url() { 163 let nsid = "io.atcr.authFullApp";
··· 57 async fn expand_permission_set(nsid: &str) -> Result<String, String> { 58 { 59 let cache = LEXICON_CACHE.read().await; 60 + if let Some(cached) = cache.get(nsid) 61 + && cached.cached_at.elapsed().as_secs() < CACHE_TTL_SECS 62 + { 63 + debug!(nsid, "Using cached permission set expansion"); 64 + return Ok(cached.expanded_scope.clone()); 65 } 66 } 67 ··· 156 157 #[cfg(test)] 158 mod tests { 159 #[test] 160 fn test_nsid_to_url() { 161 let nsid = "io.atcr.authFullApp";
+15 -3
src/sync/deprecated.rs
··· 1 use crate::api::error::ApiError; 2 - use crate::auth::{extract_bearer_token_from_header, validate_bearer_token_allow_takendown}; 3 use crate::state::AppState; 4 use crate::sync::car::encode_car_header; 5 use crate::sync::util::assert_repo_availability; ··· 19 const MAX_REPO_BLOCKS_TRAVERSAL: usize = 20_000; 20 21 async fn check_admin_or_self(state: &AppState, headers: &HeaderMap, did: &str) -> bool { 22 - let token = match extract_bearer_token_from_header( 23 headers.get("Authorization").and_then(|h| h.to_str().ok()), 24 ) { 25 Some(t) => t, 26 None => return false, 27 }; 28 - match validate_bearer_token_allow_takendown(&state.db, &token).await { 29 Ok(auth_user) => auth_user.is_admin || auth_user.did == did, 30 Err(_) => false, 31 }
··· 1 use crate::api::error::ApiError; 2 use crate::state::AppState; 3 use crate::sync::car::encode_car_header; 4 use crate::sync::util::assert_repo_availability; ··· 18 const MAX_REPO_BLOCKS_TRAVERSAL: usize = 20_000; 19 20 async fn check_admin_or_self(state: &AppState, headers: &HeaderMap, did: &str) -> bool { 21 + let extracted = match crate::auth::extract_auth_token_from_header( 22 headers.get("Authorization").and_then(|h| h.to_str().ok()), 23 ) { 24 Some(t) => t, 25 None => return false, 26 }; 27 + let dpop_proof = headers.get("DPoP").and_then(|h| h.to_str().ok()); 28 + let http_uri = "/"; 29 + match crate::auth::validate_token_with_dpop( 30 + &state.db, 31 + &extracted.token, 32 + extracted.is_dpop, 33 + dpop_proof, 34 + "GET", 35 + http_uri, 36 + false, 37 + true, 38 + ) 39 + .await 40 + { 41 Ok(auth_user) => auth_user.is_admin || auth_user.did == did, 42 Err(_) => false, 43 }
+4 -4
tests/dpop_unit.rs
··· 191 let verifier = DPoPVerifier::new(b"test-secret-32-bytes-long!!!!!!!"); 192 let url = "https://pds.example/xrpc/foo"; 193 194 - let (proof_301s_future, _) = create_dpop_proof("GET", url, 301, "ES256", None, None); 195 let result = verifier.verify_proof(&proof_301s_future, "GET", url, None); 196 assert!( 197 result.is_err(), 198 - "301s in future should exceed clock skew tolerance" 199 ); 200 201 - let (proof_301s_past, _) = create_dpop_proof("GET", url, -301, "ES256", None, None); 202 let result = verifier.verify_proof(&proof_301s_past, "GET", url, None); 203 assert!( 204 result.is_err(), 205 - "301s in past should exceed clock skew tolerance" 206 ); 207 } 208
··· 191 let verifier = DPoPVerifier::new(b"test-secret-32-bytes-long!!!!!!!"); 192 let url = "https://pds.example/xrpc/foo"; 193 194 + let (proof_301s_future, _) = create_dpop_proof("GET", url, 310, "ES256", None, None); 195 let result = verifier.verify_proof(&proof_301s_future, "GET", url, None); 196 assert!( 197 result.is_err(), 198 + "310s in future should exceed clock skew tolerance" 199 ); 200 201 + let (proof_301s_past, _) = create_dpop_proof("GET", url, -310, "ES256", None, None); 202 let result = verifier.verify_proof(&proof_301s_past, "GET", url, None); 203 assert!( 204 result.is_err(), 205 + "310s in past should exceed clock skew tolerance" 206 ); 207 } 208