use chio_http_core::HttpMethod;
use crate::extensions::ChioExtensions;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PolicyDecision {
SessionAllow,
DenyByDefault,
}
pub struct DefaultPolicy;
impl DefaultPolicy {
#[must_use]
pub fn for_method(method: HttpMethod) -> PolicyDecision {
if method.is_safe() {
PolicyDecision::SessionAllow
} else {
PolicyDecision::DenyByDefault
}
}
#[must_use]
pub fn for_method_with_extensions(
method: HttpMethod,
extensions: &ChioExtensions,
) -> PolicyDecision {
if extensions.approval_required == Some(true) {
return PolicyDecision::DenyByDefault;
}
if let Some(has_side_effects) = extensions.side_effects {
return if has_side_effects {
PolicyDecision::DenyByDefault
} else {
PolicyDecision::SessionAllow
};
}
Self::for_method(method)
}
#[must_use]
pub fn has_side_effects(method: HttpMethod, extensions: &ChioExtensions) -> bool {
if let Some(explicit) = extensions.side_effects {
return explicit;
}
method.requires_capability()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn safe_methods_allow() {
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Get),
PolicyDecision::SessionAllow
);
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Head),
PolicyDecision::SessionAllow
);
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Options),
PolicyDecision::SessionAllow
);
}
#[test]
fn unsafe_methods_deny() {
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Post),
PolicyDecision::DenyByDefault
);
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Put),
PolicyDecision::DenyByDefault
);
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Patch),
PolicyDecision::DenyByDefault
);
assert_eq!(
DefaultPolicy::for_method(HttpMethod::Delete),
PolicyDecision::DenyByDefault
);
}
#[test]
fn extension_side_effects_override() {
let mut ext = ChioExtensions {
side_effects: Some(true),
..Default::default()
};
assert_eq!(
DefaultPolicy::for_method_with_extensions(HttpMethod::Get, &ext),
PolicyDecision::DenyByDefault
);
ext.side_effects = Some(false);
assert_eq!(
DefaultPolicy::for_method_with_extensions(HttpMethod::Post, &ext),
PolicyDecision::SessionAllow
);
}
#[test]
fn approval_required_always_denies() {
let ext = ChioExtensions {
approval_required: Some(true),
side_effects: Some(false),
..Default::default()
};
assert_eq!(
DefaultPolicy::for_method_with_extensions(HttpMethod::Get, &ext),
PolicyDecision::DenyByDefault
);
}
#[test]
fn has_side_effects_follows_method() {
let ext = ChioExtensions::default();
assert!(!DefaultPolicy::has_side_effects(HttpMethod::Get, &ext));
assert!(DefaultPolicy::has_side_effects(HttpMethod::Post, &ext));
}
#[test]
fn has_side_effects_respects_override() {
let mut ext = ChioExtensions {
side_effects: Some(true),
..Default::default()
};
assert!(DefaultPolicy::has_side_effects(HttpMethod::Get, &ext));
ext.side_effects = Some(false);
assert!(!DefaultPolicy::has_side_effects(HttpMethod::Delete, &ext));
}
}