50 lines
1.5 KiB
Rust
50 lines
1.5 KiB
Rust
use crate::error::AppError;
|
|
use axum::http::HeaderMap;
|
|
use subtle::ConstantTimeEq;
|
|
use tracing::warn;
|
|
|
|
const COOKIE_NAME: &str = "admin";
|
|
|
|
fn extract_token(headers: &HeaderMap) -> Option<String> {
|
|
if let Some(auth) = headers.get("Authorization").and_then(|h| h.to_str().ok()) {
|
|
if let Some(token) = auth.strip_prefix("Bearer ") {
|
|
return Some(token.to_string());
|
|
}
|
|
}
|
|
if let Some(cookie_header) = headers.get("Cookie").and_then(|h| h.to_str().ok()) {
|
|
for part in cookie_header.split(';') {
|
|
let part = part.trim();
|
|
if let Some(value) = part.strip_prefix(&format!("{}=", COOKIE_NAME)) {
|
|
return Some(value.to_string());
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn token_matches(provided: &str, expected: &str) -> bool {
|
|
let a = provided.as_bytes();
|
|
let b = expected.as_bytes();
|
|
if a.len() != b.len() {
|
|
// Still do a constant-time compare to make timing uniform on the same-length path.
|
|
let _ = a.ct_eq(a);
|
|
return false;
|
|
}
|
|
a.ct_eq(b).into()
|
|
}
|
|
|
|
pub fn is_authed(headers: &HeaderMap, admin_token: &str) -> bool {
|
|
match extract_token(headers) {
|
|
Some(t) => token_matches(&t, admin_token),
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
pub fn check_auth(headers: &HeaderMap, admin_token: &str) -> Result<(), AppError> {
|
|
if !is_authed(headers, admin_token) {
|
|
warn!("Unauthorized access attempt");
|
|
return Err(AppError::Unauthorized);
|
|
}
|
|
Ok(())
|
|
}
|