objectstore_server/extractors/
service.rs1use axum::extract::FromRequestParts;
2use axum::http::{header, request::Parts};
3
4use crate::auth::{AuthAwareService, AuthContext};
5use crate::endpoints::common::ApiError;
6use crate::state::ServiceState;
7
8const BEARER_PREFIX: &str = "Bearer ";
9
10const OBJECTSTORE_AUTH_HEADER: &str = "x-os-auth";
15
16impl FromRequestParts<ServiceState> for AuthAwareService {
17 type Rejection = ApiError;
18
19 async fn from_request_parts(
20 parts: &mut Parts,
21 state: &ServiceState,
22 ) -> Result<Self, Self::Rejection> {
23 let encoded_token = parts
24 .headers
25 .get(OBJECTSTORE_AUTH_HEADER)
26 .or_else(|| parts.headers.get(header::AUTHORIZATION))
27 .and_then(|v| v.to_str().ok())
28 .and_then(strip_bearer);
29
30 let enforce = state.config.auth.enforce;
31 let auth_result = AuthContext::from_encoded_jwt(encoded_token, &state.key_directory)
33 .inspect_err(|err| err.log(None, None, enforce));
34
35 let auth_context = match enforce {
39 true => Some(auth_result?),
40 false => auth_result.ok(),
41 };
42
43 AuthAwareService::new(
44 state.service.clone(),
45 auth_context,
46 state.config.auth.enforce,
47 )
48 }
49}
50
51fn strip_bearer(header_value: &str) -> Option<&str> {
52 let (prefix, tail) = header_value.split_at_checked(BEARER_PREFIX.len())?;
53 if prefix.eq_ignore_ascii_case(BEARER_PREFIX) {
54 Some(tail)
55 } else {
56 None
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_strip_bearer() {
66 assert_eq!(strip_bearer("Bearer tokenvalue"), Some("tokenvalue"));
68 assert_eq!(strip_bearer("bearer tokenvalue"), Some("tokenvalue"));
69 assert_eq!(strip_bearer("BEARER tokenvalue"), Some("tokenvalue"));
70
71 assert_eq!(strip_bearer("Token tokenvalue"), None);
73 assert_eq!(strip_bearer("Bearer"), None);
74
75 assert_eq!(strip_bearer("Bearer⚠️tokenvalue"), None);
77 }
78}