use crate::effect::HttpRequest;
pub const AUTH_KIND_HEADER: &str = "X-Auth-Kind";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AuthKind {
Session,
Bot,
}
impl AuthKind {
pub const fn as_str(self) -> &'static str {
match self {
AuthKind::Session => "session",
AuthKind::Bot => "bot",
}
}
pub fn from_str(value: &str) -> Option<Self> {
match value {
"session" => Some(AuthKind::Session),
"bot" => Some(AuthKind::Bot),
_ => None,
}
}
}
pub fn apply_auth_intent(
mut request: HttpRequest,
mut resolve: impl FnMut(AuthKind) -> Option<(String, String)>,
) -> (HttpRequest, Option<AuthKind>) {
let kind = request
.headers
.iter()
.find(|(name, _)| name.eq_ignore_ascii_case(AUTH_KIND_HEADER))
.and_then(|(_, value)| AuthKind::from_str(value));
request
.headers
.retain(|(name, _)| !name.eq_ignore_ascii_case(AUTH_KIND_HEADER));
if let Some(kind) = kind {
if let Some((auth_name, auth_value)) = resolve(kind) {
request
.headers
.retain(|(name, _)| !name.eq_ignore_ascii_case(&auth_name));
request.headers.push((auth_name, auth_value));
}
}
(request, kind)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auth_kind_roundtrip() {
for kind in [AuthKind::Session, AuthKind::Bot] {
assert_eq!(AuthKind::from_str(kind.as_str()), Some(kind));
}
}
#[test]
fn auth_kind_unknown_is_none() {
assert_eq!(AuthKind::from_str("admin"), None);
assert_eq!(AuthKind::from_str(""), None);
}
#[test]
fn auth_kind_header_is_intent_only_namespace() {
assert_eq!(AUTH_KIND_HEADER, "X-Auth-Kind");
}
#[test]
fn auth_intent_is_stripped_and_resolved_credential_overrides_stale_value() {
let request = HttpRequest {
method: "GET".into(),
url: "https://example.invalid".into(),
headers: vec![
(AUTH_KIND_HEADER.into(), "session".into()),
("cookieId".into(), "stale".into()),
("x-request-id".into(), "request-1".into()),
],
body: None,
};
let (prepared, kind) = apply_auth_intent(request, |requested| {
(requested == AuthKind::Session).then(|| ("cookieId".into(), "current".into()))
});
assert_eq!(kind, Some(AuthKind::Session));
assert!(!prepared
.headers
.iter()
.any(|(name, _)| name.eq_ignore_ascii_case(AUTH_KIND_HEADER)));
assert!(prepared
.headers
.iter()
.any(|(name, value)| name == "cookieId" && value == "current"));
assert!(prepared
.headers
.iter()
.any(|(name, value)| name == "x-request-id" && value == "request-1"));
}
}