1use async_trait::async_trait;
2use std::sync::Arc;
3
4use camel_api::security_policy::{AuthorizationDecision, Principal, SecurityPolicy};
5use camel_api::{CamelError, Exchange};
6
7use crate::token_authenticator::TokenAuthenticator;
8
9pub const PRINCIPAL_KEY: &str = "camel.auth.principal";
11
12async fn authenticate(
26 exchange: &mut Exchange,
27 authenticator: &dyn TokenAuthenticator,
28 trust_upstream_principal: bool,
29) -> Result<Principal, CamelError> {
30 let token = exchange
32 .input
33 .header_ic("authorization")
34 .and_then(|v| v.as_str())
35 .and_then(|s| s.strip_prefix("Bearer "))
36 .map(|s| s.to_string());
37
38 if let Some(token) = token {
39 let principal = authenticator.authenticate_bearer(&token).await?;
40 if let Ok(value) = serde_json::to_value(&principal) {
42 exchange.set_property(PRINCIPAL_KEY, value);
43 }
44 return Ok(principal);
45 }
46
47 if trust_upstream_principal {
48 extract_principal_from_exchange(exchange)
49 } else {
50 Err(CamelError::Unauthenticated(
51 "no Bearer token and trust_upstream_principal is false".into(),
52 ))
53 }
54}
55
56fn extract_principal_from_exchange(exchange: &Exchange) -> Result<Principal, CamelError> {
58 exchange
59 .property(PRINCIPAL_KEY)
60 .and_then(|v| serde_json::from_value::<Principal>(v.clone()).ok())
61 .ok_or_else(|| CamelError::Unauthenticated("no principal in exchange".into()))
62}
63
64pub struct RolePolicy {
71 required_roles: Vec<String>,
72 all_required: bool,
73 trust_upstream_principal: bool,
77 authenticator: Arc<dyn TokenAuthenticator>,
78}
79
80impl RolePolicy {
81 pub fn new(
82 required_roles: Vec<String>,
83 all_required: bool,
84 trust_upstream_principal: bool,
85 authenticator: Arc<dyn TokenAuthenticator>,
86 ) -> Self {
87 Self {
88 required_roles,
89 all_required,
90 trust_upstream_principal,
91 authenticator,
92 }
93 }
94}
95
96#[async_trait]
97impl SecurityPolicy for RolePolicy {
98 async fn evaluate(&self, exchange: &mut Exchange) -> Result<AuthorizationDecision, CamelError> {
99 let principal = authenticate(
100 exchange,
101 &*self.authenticator,
102 self.trust_upstream_principal,
103 )
104 .await?;
105
106 let missing: Vec<String> = self
107 .required_roles
108 .iter()
109 .filter(|r| !principal.has_role(r))
110 .cloned()
111 .collect();
112
113 let granted = if self.all_required {
114 missing.is_empty()
115 } else {
116 self.required_roles.is_empty() || missing.len() < self.required_roles.len()
117 };
118
119 if granted {
120 Ok(AuthorizationDecision::Granted { principal })
121 } else {
122 let actual = principal.roles.clone();
123 Ok(AuthorizationDecision::Denied {
124 reason: format!("missing required role(s): {}", missing.join(", ")), required: self.required_roles.clone(),
126 actual,
127 })
128 }
129 }
130}
131
132pub struct ScopePolicy {
139 required_scopes: Vec<String>,
140 all_required: bool,
141 trust_upstream_principal: bool,
145 authenticator: Arc<dyn TokenAuthenticator>,
146}
147
148impl ScopePolicy {
149 pub fn new(
150 required_scopes: Vec<String>,
151 all_required: bool,
152 trust_upstream_principal: bool,
153 authenticator: Arc<dyn TokenAuthenticator>,
154 ) -> Self {
155 Self {
156 required_scopes,
157 all_required,
158 trust_upstream_principal,
159 authenticator,
160 }
161 }
162}
163
164#[async_trait]
165impl SecurityPolicy for ScopePolicy {
166 async fn evaluate(&self, exchange: &mut Exchange) -> Result<AuthorizationDecision, CamelError> {
167 let principal = authenticate(
168 exchange,
169 &*self.authenticator,
170 self.trust_upstream_principal,
171 )
172 .await?;
173
174 let missing: Vec<String> = self
175 .required_scopes
176 .iter()
177 .filter(|s| !principal.has_scope(s))
178 .cloned()
179 .collect();
180
181 let granted = if self.all_required {
182 missing.is_empty()
183 } else {
184 self.required_scopes.is_empty() || missing.len() < self.required_scopes.len()
185 };
186
187 if granted {
188 Ok(AuthorizationDecision::Granted { principal })
189 } else {
190 let actual = principal.scopes.clone();
191 Ok(AuthorizationDecision::Denied {
192 reason: format!("missing required scope(s): {}", missing.join(", ")),
193 required: self.required_scopes.clone(),
194 actual,
195 })
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use crate::jwt::JwtValidator;
204 use crate::types::AuthError;
205 use camel_api::Message;
206
207 fn test_principal(roles: Vec<&str>, scopes: Vec<&str>) -> Principal {
208 Principal {
209 subject: "test-user".into(),
210 issuer: "test".into(),
211 audience: vec![],
212 roles: roles.iter().map(|s| s.to_string()).collect(),
213 scopes: scopes.iter().map(|s| s.to_string()).collect(),
214 claims: serde_json::Value::Null,
215 }
216 }
217
218 struct MockJwtValidator {
220 principal: Principal,
221 }
222
223 #[async_trait]
224 impl JwtValidator for MockJwtValidator {
225 async fn validate(&self, _token: &str) -> Result<Principal, AuthError> {
226 Ok(self.principal.clone())
227 }
228 }
229
230 fn mock_validator(principal: Principal) -> Arc<dyn TokenAuthenticator> {
231 Arc::new(MockJwtValidator { principal })
232 }
233
234 fn exchange_with_bearer(principal: Principal) -> Exchange {
236 let validator_principal = principal.clone();
237 let mut msg = Message::default();
238 msg.set_header(
239 "Authorization",
240 serde_json::Value::String("Bearer mock-token".into()),
241 );
242 let mut ex = Exchange::new(msg);
244 let value = serde_json::to_value(&validator_principal).unwrap();
245 ex.set_property(PRINCIPAL_KEY, value);
246 ex
247 }
248
249 fn exchange_with_principal(principal: Principal) -> Exchange {
251 let mut ex = Exchange::new(Message::default());
252 let value = serde_json::to_value(&principal).unwrap();
253 ex.set_property(PRINCIPAL_KEY, value);
254 ex
255 }
256
257 #[tokio::test]
258 async fn role_policy_grants_when_role_present() {
259 let principal = test_principal(vec!["admin"], vec![]);
260 let policy = RolePolicy::new(
261 vec!["admin".into()],
262 true,
263 false,
264 mock_validator(principal.clone()),
265 );
266 let mut ex = exchange_with_bearer(principal);
267 let decision = policy.evaluate(&mut ex).await.unwrap();
268 assert!(matches!(decision, AuthorizationDecision::Granted { .. }));
269 }
270
271 #[tokio::test]
272 async fn role_policy_denies_when_role_missing() {
273 let principal = test_principal(vec!["user"], vec![]);
274 let policy = RolePolicy::new(
275 vec!["admin".into()],
276 true,
277 false,
278 mock_validator(principal.clone()),
279 );
280 let mut ex = exchange_with_bearer(principal);
281 let decision = policy.evaluate(&mut ex).await.unwrap();
282 assert!(matches!(decision, AuthorizationDecision::Denied { .. }));
283 }
284
285 #[tokio::test]
286 async fn role_policy_any_required() {
287 let principal = test_principal(vec!["user"], vec![]);
288 let policy = RolePolicy::new(
289 vec!["admin".into(), "user".into()],
290 false,
291 false,
292 mock_validator(principal.clone()),
293 );
294 let mut ex = exchange_with_bearer(principal);
295 let decision = policy.evaluate(&mut ex).await.unwrap();
296 assert!(matches!(decision, AuthorizationDecision::Granted { .. }));
297 }
298
299 #[tokio::test]
300 async fn scope_policy_grants() {
301 let principal = test_principal(vec![], vec!["read"]);
302 let policy = ScopePolicy::new(
303 vec!["read".into()],
304 true,
305 false,
306 mock_validator(principal.clone()),
307 );
308 let mut ex = exchange_with_bearer(principal);
309 let decision = policy.evaluate(&mut ex).await.unwrap();
310 assert!(matches!(decision, AuthorizationDecision::Granted { .. }));
311 }
312
313 #[tokio::test]
314 async fn unauthenticated_when_no_principal_and_no_header() {
315 struct FailValidator;
317 #[async_trait]
318 impl JwtValidator for FailValidator {
319 async fn validate(&self, _token: &str) -> Result<Principal, AuthError> {
320 panic!("should not be called")
321 }
322 }
323 let policy = RolePolicy::new(vec!["admin".into()], true, false, Arc::new(FailValidator));
324 let mut ex = Exchange::new(Message::default());
325 let result = policy.evaluate(&mut ex).await;
326 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
327 }
328
329 #[tokio::test]
330 async fn principal_fallback_denied_by_default() {
331 let principal = test_principal(vec!["admin"], vec![]);
334 let policy = RolePolicy::new(
335 vec!["admin".into()],
336 true,
337 false, mock_validator(principal.clone()),
339 );
340 let mut ex = exchange_with_principal(principal); let result = policy.evaluate(&mut ex).await;
342 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
343 }
344
345 #[tokio::test]
346 async fn principal_fallback_allowed_with_opt_in() {
347 let principal = test_principal(vec!["admin"], vec![]);
349 let policy = RolePolicy::new(
350 vec!["admin".into()],
351 true,
352 true, mock_validator(principal.clone()),
354 );
355 let mut ex = exchange_with_principal(principal); let decision = policy.evaluate(&mut ex).await.unwrap();
357 assert!(matches!(decision, AuthorizationDecision::Granted { .. }));
358 }
359
360 #[tokio::test]
361 async fn scope_policy_fallback_denied_by_default() {
362 let principal = test_principal(vec![], vec!["read"]);
364 let policy = ScopePolicy::new(
365 vec!["read".into()],
366 true,
367 false, mock_validator(principal.clone()),
369 );
370 let mut ex = exchange_with_principal(principal);
371 let result = policy.evaluate(&mut ex).await;
372 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
373 }
374}