1use chio_http_core::HttpMethod;
8
9use crate::extensions::ChioExtensions;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PolicyDecision {
14 SessionAllow,
17 DenyByDefault,
19}
20
21pub struct DefaultPolicy;
24
25impl DefaultPolicy {
26 #[must_use]
29 pub fn for_method(method: HttpMethod) -> PolicyDecision {
30 if method.is_safe() {
31 PolicyDecision::SessionAllow
32 } else {
33 PolicyDecision::DenyByDefault
34 }
35 }
36
37 #[must_use]
44 pub fn for_method_with_extensions(
45 method: HttpMethod,
46 extensions: &ChioExtensions,
47 ) -> PolicyDecision {
48 if extensions.approval_required == Some(true) {
50 return PolicyDecision::DenyByDefault;
51 }
52
53 if let Some(has_side_effects) = extensions.side_effects {
55 return if has_side_effects {
56 PolicyDecision::DenyByDefault
57 } else {
58 PolicyDecision::SessionAllow
59 };
60 }
61
62 Self::for_method(method)
63 }
64
65 #[must_use]
68 pub fn has_side_effects(method: HttpMethod, extensions: &ChioExtensions) -> bool {
69 if let Some(explicit) = extensions.side_effects {
70 return explicit;
71 }
72 method.requires_capability()
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn safe_methods_allow() {
82 assert_eq!(
83 DefaultPolicy::for_method(HttpMethod::Get),
84 PolicyDecision::SessionAllow
85 );
86 assert_eq!(
87 DefaultPolicy::for_method(HttpMethod::Head),
88 PolicyDecision::SessionAllow
89 );
90 assert_eq!(
91 DefaultPolicy::for_method(HttpMethod::Options),
92 PolicyDecision::SessionAllow
93 );
94 }
95
96 #[test]
97 fn unsafe_methods_deny() {
98 assert_eq!(
99 DefaultPolicy::for_method(HttpMethod::Post),
100 PolicyDecision::DenyByDefault
101 );
102 assert_eq!(
103 DefaultPolicy::for_method(HttpMethod::Put),
104 PolicyDecision::DenyByDefault
105 );
106 assert_eq!(
107 DefaultPolicy::for_method(HttpMethod::Patch),
108 PolicyDecision::DenyByDefault
109 );
110 assert_eq!(
111 DefaultPolicy::for_method(HttpMethod::Delete),
112 PolicyDecision::DenyByDefault
113 );
114 }
115
116 #[test]
117 fn extension_side_effects_override() {
118 let mut ext = ChioExtensions {
119 side_effects: Some(true),
120 ..Default::default()
121 };
122 assert_eq!(
123 DefaultPolicy::for_method_with_extensions(HttpMethod::Get, &ext),
124 PolicyDecision::DenyByDefault
125 );
126
127 ext.side_effects = Some(false);
128 assert_eq!(
129 DefaultPolicy::for_method_with_extensions(HttpMethod::Post, &ext),
130 PolicyDecision::SessionAllow
131 );
132 }
133
134 #[test]
135 fn approval_required_always_denies() {
136 let ext = ChioExtensions {
137 approval_required: Some(true),
138 side_effects: Some(false),
139 ..Default::default()
140 };
141
142 assert_eq!(
143 DefaultPolicy::for_method_with_extensions(HttpMethod::Get, &ext),
144 PolicyDecision::DenyByDefault
145 );
146 }
147
148 #[test]
149 fn has_side_effects_follows_method() {
150 let ext = ChioExtensions::default();
151 assert!(!DefaultPolicy::has_side_effects(HttpMethod::Get, &ext));
152 assert!(DefaultPolicy::has_side_effects(HttpMethod::Post, &ext));
153 }
154
155 #[test]
156 fn has_side_effects_respects_override() {
157 let mut ext = ChioExtensions {
158 side_effects: Some(true),
159 ..Default::default()
160 };
161 assert!(DefaultPolicy::has_side_effects(HttpMethod::Get, &ext));
162
163 ext.side_effects = Some(false);
164 assert!(!DefaultPolicy::has_side_effects(HttpMethod::Delete, &ext));
165 }
166}