+5
-32
src/api/actor/preferences.rs
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+1
src/api/repo/record/write.rs
+4
src/api/server/account_status.rs
+4
src/api/server/account_status.rs
···
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
+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,
+2
src/api/server/migration.rs
+2
src/api/server/migration.rs
+5
-4
src/api/server/passkey_account.rs
+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
+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
+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
+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
+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
+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,
+5
-7
src/oauth/scopes/permission_set.rs
+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
+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
+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