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
10impl FromRequestParts<ServiceState> for AuthAwareService {
11 type Rejection = ApiError;
12
13 async fn from_request_parts(
14 parts: &mut Parts,
15 state: &ServiceState,
16 ) -> Result<Self, Self::Rejection> {
17 let inner = state.service.clone();
18 if !state.config.auth.enforce {
19 return Ok(AuthAwareService::new(inner, None));
20 }
21
22 let encoded_token = parts
23 .headers
24 .get(header::AUTHORIZATION)
25 .and_then(|v| v.to_str().ok())
26 .and_then(strip_bearer);
27
28 let context = AuthContext::from_encoded_jwt(encoded_token, &state.key_directory)
29 .inspect_err(|err| tracing::debug!("Authorization rejected: `{:?}`", err))?;
30
31 Ok(AuthAwareService::new(state.service.clone(), Some(context)))
32 }
33}
34
35fn strip_bearer(header_value: &str) -> Option<&str> {
36 let (prefix, tail) = header_value.split_at_checked(BEARER_PREFIX.len())?;
37 if prefix.eq_ignore_ascii_case(BEARER_PREFIX) {
38 Some(tail)
39 } else {
40 None
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn test_strip_bearer() {
50 assert_eq!(strip_bearer("Bearer tokenvalue"), Some("tokenvalue"));
52 assert_eq!(strip_bearer("bearer tokenvalue"), Some("tokenvalue"));
53 assert_eq!(strip_bearer("BEARER tokenvalue"), Some("tokenvalue"));
54
55 assert_eq!(strip_bearer("Token tokenvalue"), None);
57 assert_eq!(strip_bearer("Bearer"), None);
58
59 assert_eq!(strip_bearer("Bearer⚠️tokenvalue"), None);
61 }
62}