objectstore_server/extractors/
service.rs

1use 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 encoded_token = parts
18            .headers
19            .get(header::AUTHORIZATION)
20            .and_then(|v| v.to_str().ok())
21            .and_then(strip_bearer);
22
23        let enforce = state.config.auth.enforce;
24        // Attempt to decode / verify the JWT, logging failure
25        let auth_result = AuthContext::from_encoded_jwt(encoded_token, &state.key_directory)
26            .inspect_err(|err| err.log(None, None, enforce));
27
28        // If auth enforcement is enabled, `from_encoded_jwt()` must have succeeded.
29        // If auth enforcement is disabled, we'll pass the context along if it succeeded but will
30        // still proceed with `None` if it failed.
31        let auth_context = match enforce {
32            true => Some(auth_result?),
33            false => auth_result.ok(),
34        };
35
36        AuthAwareService::new(
37            state.service.clone(),
38            auth_context,
39            state.config.auth.enforce,
40        )
41    }
42}
43
44fn strip_bearer(header_value: &str) -> Option<&str> {
45    let (prefix, tail) = header_value.split_at_checked(BEARER_PREFIX.len())?;
46    if prefix.eq_ignore_ascii_case(BEARER_PREFIX) {
47        Some(tail)
48    } else {
49        None
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_strip_bearer() {
59        // Prefix matches
60        assert_eq!(strip_bearer("Bearer tokenvalue"), Some("tokenvalue"));
61        assert_eq!(strip_bearer("bearer tokenvalue"), Some("tokenvalue"));
62        assert_eq!(strip_bearer("BEARER tokenvalue"), Some("tokenvalue"));
63
64        // Prefix doesn't match
65        assert_eq!(strip_bearer("Token tokenvalue"), None);
66        assert_eq!(strip_bearer("Bearer"), None);
67
68        // No character boundary at end of expected prefix
69        assert_eq!(strip_bearer("Bearer⚠️tokenvalue"), None);
70    }
71}