Files
buzz/crates/buzz-relay/src/api/admin/auth.rs
Kalvin C 68e670e001 feat: add read-only deployment moderation dashboard (#1999)
Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1ntr8jjcqq6gt06q5avvqttfjgpwshmra22pcmcagdnukw4ja4nqqsa9g54 <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@sprout-oss.stage.blox.sqprod.co>
2026-07-17 12:50:06 -07:00

63 lines
1.6 KiB
Rust

use axum::http::{header, HeaderMap};
use super::error::ApiError;
use crate::state::AppState;
pub(crate) fn is_admin_host(state: &AppState, headers: &HeaderMap) -> bool {
let Some(config) = state.config.admin.as_ref() else {
return false;
};
headers
.get(header::HOST)
.and_then(|value| value.to_str().ok())
.is_some_and(|host| host == config.host)
}
pub fn authorize(state: &AppState, headers: &HeaderMap) -> Result<(), ApiError> {
let config = state
.config
.admin
.as_ref()
.ok_or_else(ApiError::not_found)?;
if !is_admin_host(state, headers) {
return Err(ApiError::forbidden());
}
if headers.get(header::ORIGIN).is_some_and(|origin| {
origin
.to_str()
.map_or(true, |origin| !origin_matches_host(origin, &config.host))
}) {
return Err(ApiError::forbidden());
}
Ok(())
}
fn origin_matches_host(origin: &str, host: &str) -> bool {
origin
.strip_prefix("https://")
.or_else(|| origin.strip_prefix("http://"))
== Some(host)
}
#[cfg(test)]
mod tests {
use super::origin_matches_host;
#[test]
fn browser_origin_must_match_admin_host() {
assert!(origin_matches_host(
"https://admin.example.com",
"admin.example.com"
));
assert!(origin_matches_host(
"http://admin.localhost:3000",
"admin.localhost:3000"
));
assert!(!origin_matches_host(
"https://attacker.example",
"admin.example.com"
));
assert!(!origin_matches_host("null", "admin.example.com"));
}
}