1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4use std::task::{Context, Poll};
5
6use tower::{Layer, Service};
7
8use camel_api::security_policy::{
9 AuthContext, AuthPrincipal, AuthorizationDecision, SecurityPolicy, TransportId,
10 store_principal_properties,
11};
12use camel_api::{CamelError, Exchange};
13
14#[derive(Clone)]
25pub struct SecurityPolicyLayer {
26 policy: Arc<dyn SecurityPolicy>,
27 transport: TransportId,
28}
29
30impl SecurityPolicyLayer {
31 pub fn new(policy: Arc<dyn SecurityPolicy>, transport: TransportId) -> Self {
32 Self { policy, transport }
33 }
34}
35
36impl<S> Layer<S> for SecurityPolicyLayer {
37 type Service = SecurityPolicyService<S>;
38
39 fn layer(&self, inner: S) -> Self::Service {
40 SecurityPolicyService {
41 inner,
42 policy: Arc::clone(&self.policy),
43 transport: self.transport,
44 }
45 }
46}
47
48pub struct SecurityPolicyService<S> {
49 inner: S,
50 policy: Arc<dyn SecurityPolicy>,
51 transport: TransportId,
52}
53
54impl<S: Clone> Clone for SecurityPolicyService<S> {
55 fn clone(&self) -> Self {
56 Self {
57 inner: self.inner.clone(),
58 policy: Arc::clone(&self.policy),
59 transport: self.transport,
60 }
61 }
62}
63
64impl<S> Service<Exchange> for SecurityPolicyService<S>
65where
66 S: Service<Exchange, Response = Exchange, Error = CamelError> + Clone + Send + 'static,
67 S::Future: Send,
68{
69 type Response = Exchange;
70 type Error = CamelError;
71 type Future = Pin<Box<dyn Future<Output = Result<Exchange, CamelError>> + Send>>;
72
73 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
74 self.inner.poll_ready(cx)
75 }
76
77 fn call(&mut self, exchange: Exchange) -> Self::Future {
78 let policy = Arc::clone(&self.policy);
79 let transport = self.transport;
80 let clone = self.inner.clone();
81 let inner = std::mem::replace(&mut self.inner, clone);
82
83 Box::pin(async move {
84 let principal = camel_auth::kernel::read_carrier(&exchange).ok_or_else(|| {
90 CamelError::Unauthenticated("no authenticated principal present".to_string())
91 })?;
92 evaluate(policy, exchange, &principal, transport, inner).await
93 })
94 }
95}
96
97async fn evaluate<S>(
103 policy: Arc<dyn SecurityPolicy>,
104 mut exchange: Exchange,
105 principal: &dyn AuthPrincipal,
106 transport: TransportId,
107 mut inner: S,
108) -> Result<Exchange, CamelError>
109where
110 S: Service<Exchange, Response = Exchange, Error = CamelError> + Send + 'static,
111 S::Future: Send,
112{
113 let auth = AuthContext {
114 principal,
115 transport,
116 };
117 match policy.evaluate(&mut exchange, &auth).await {
118 Ok(AuthorizationDecision::Granted { principal }) => {
119 store_principal_properties(&mut exchange, &principal);
120 inner.call(exchange).await
121 }
122 Ok(AuthorizationDecision::Denied {
123 reason,
124 required,
125 actual,
126 }) => {
127 let msg =
128 format!("Access denied: {reason}. Required: {required:?}, actual: {actual:?}");
129 Err(CamelError::Unauthorized(msg))
130 }
131 Err(e) => Err(e),
132 _ => Err(CamelError::Unauthorized(
134 "access denied by security policy".to_string(),
135 )),
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use async_trait::async_trait;
143 use camel_api::security_policy::{
144 AccessMode, CredentialSource, PRINCIPAL_AUDIENCE_KEY, PRINCIPAL_CLAIMS_KEY,
145 PRINCIPAL_ISSUER_KEY, PRINCIPAL_KEY, PRINCIPAL_ROLES_KEY, PRINCIPAL_SCOPES_KEY,
146 PRINCIPAL_SUBJECT_KEY, Principal, RouteSecurityPlan,
147 };
148 use camel_api::{BoxProcessor, BoxProcessorExt, Message};
149 use camel_auth::TokenAuthenticator;
150 use camel_auth::credential_source::ExtractedToken;
151 use camel_auth::kernel::{KERNEL_PRINCIPAL_KEY, install_carrier, kernel_authenticate};
152 use camel_auth::native_auth::{
153 NativeCredential, NativeCredentialSecret, NativeCredentialStore,
154 };
155 use camel_auth::{ProviderEntry, ProviderRegistry, StaticTokenAuthenticator};
156 use std::sync::Mutex;
157 use std::sync::atomic::{AtomicU32, Ordering};
158 use tower::ServiceExt;
159 use zeroize::Zeroizing;
160
161 fn make_exchange() -> Exchange {
162 Exchange::new(Message::new("test"))
163 }
164
165 fn ok_processor() -> BoxProcessor {
166 BoxProcessor::from_fn(|ex| Box::pin(async move { Ok(ex) }))
167 }
168
169 fn test_principal() -> Principal {
170 Principal {
171 subject: "user1".into(),
172 issuer: "test-issuer".into(),
173 audience: vec!["api".into()],
174 scopes: vec!["read".into()],
175 roles: vec!["admin".into()],
176 claims: serde_json::json!({"sub": "user1"}),
177 }
178 }
179
180 fn static_authenticator(token: &str) -> Arc<dyn TokenAuthenticator> {
183 let store = NativeCredentialStore::try_new(vec![NativeCredential {
184 secret: NativeCredentialSecret::Plaintext {
185 value: Zeroizing::new(token.to_string()),
186 },
187 principal: test_principal(),
188 }])
189 .unwrap();
190 Arc::new(StaticTokenAuthenticator::new(store))
191 }
192
193 fn provider_registry(id: &str, token: &str) -> ProviderRegistry {
195 let registry = ProviderRegistry::new();
196 registry.register(
197 id,
198 ProviderEntry {
199 authenticator: static_authenticator(token),
200 audience_binding: None,
201 },
202 );
203 registry
204 }
205
206 fn authenticated_plan(provider_ref: &str) -> RouteSecurityPlan {
207 RouteSecurityPlan {
208 access_mode: AccessMode::Authenticated,
209 provider_ref: Some(provider_ref.to_string()),
210 transport: TransportId::Http,
211 credential_sources: vec![CredentialSource::AuthorizationHeader],
212 audience_binding: None,
213 }
214 }
215
216 fn credentials(token: &str) -> ExtractedToken {
217 ExtractedToken {
218 token: token.to_string(),
219 source: CredentialSource::AuthorizationHeader,
220 }
221 }
222
223 fn policy_only_layer(policy: Arc<dyn SecurityPolicy>) -> SecurityPolicyLayer {
225 SecurityPolicyLayer::new(policy, TransportId::Http)
226 }
227
228 async fn minted_principal() -> camel_auth::AuthenticatedPrincipal {
231 let providers = provider_registry("idp-a", "t-a");
232 let plan = authenticated_plan("idp-a");
233 kernel_authenticate(&plan, &providers, &credentials("t-a"))
234 .await
235 .expect("kernel mints test carrier")
236 }
237
238 async fn carrier_exchange() -> Exchange {
240 let principal = minted_principal().await;
241 let mut exchange = make_exchange();
242 install_carrier(&mut exchange, &principal);
243 exchange
244 }
245
246 struct GrantPolicy;
247 #[async_trait]
248 impl SecurityPolicy for GrantPolicy {
249 async fn evaluate(
250 &self,
251 _exchange: &mut Exchange,
252 _auth: &AuthContext<'_>,
253 ) -> Result<AuthorizationDecision, CamelError> {
254 Ok(AuthorizationDecision::Granted {
255 principal: test_principal(),
256 })
257 }
258 }
259
260 struct DenyPolicy;
261 #[async_trait]
262 impl SecurityPolicy for DenyPolicy {
263 async fn evaluate(
264 &self,
265 _exchange: &mut Exchange,
266 _auth: &AuthContext<'_>,
267 ) -> Result<AuthorizationDecision, CamelError> {
268 Ok(AuthorizationDecision::Denied {
269 reason: "missing role".into(),
270 required: vec!["admin".into()],
271 actual: vec!["user".into()],
272 })
273 }
274 }
275
276 struct FailPolicy;
277 #[async_trait]
278 impl SecurityPolicy for FailPolicy {
279 async fn evaluate(
280 &self,
281 _exchange: &mut Exchange,
282 _auth: &AuthContext<'_>,
283 ) -> Result<AuthorizationDecision, CamelError> {
284 Err(CamelError::Unauthenticated("invalid token".into()))
285 }
286 }
287
288 struct RecordingGrantPolicy {
290 count: AtomicU32,
291 seen: Mutex<Vec<(String, String)>>, }
293
294 #[async_trait]
295 impl SecurityPolicy for RecordingGrantPolicy {
296 async fn evaluate(
297 &self,
298 _exchange: &mut Exchange,
299 auth: &AuthContext<'_>,
300 ) -> Result<AuthorizationDecision, CamelError> {
301 self.count.fetch_add(1, Ordering::SeqCst);
302 self.seen.lock().unwrap().push((
303 auth.principal.principal().subject.clone(),
304 auth.principal.provider_id().to_string(),
305 ));
306 Ok(AuthorizationDecision::Granted {
307 principal: test_principal(),
308 })
309 }
310 }
311
312 #[tokio::test]
313 async fn test_granted_stores_properties() {
314 let layer = policy_only_layer(Arc::new(GrantPolicy));
315 let mut svc = layer.layer(ok_processor());
316 let result = svc
317 .ready()
318 .await
319 .unwrap()
320 .call(carrier_exchange().await)
321 .await;
322 assert!(result.is_ok());
323 let ex = result.unwrap();
324 assert_eq!(
325 ex.property(PRINCIPAL_SUBJECT_KEY),
326 Some(&serde_json::Value::String("user1".into()))
327 );
328 assert_eq!(
329 ex.property(PRINCIPAL_ISSUER_KEY),
330 Some(&serde_json::Value::String("test-issuer".into()))
331 );
332 assert!(ex.property(PRINCIPAL_KEY).is_some());
333 }
334
335 #[tokio::test]
336 async fn test_denied_returns_unauthorized_error() {
337 let layer = policy_only_layer(Arc::new(DenyPolicy));
338 let mut svc = layer.layer(ok_processor());
339 let result = svc
340 .ready()
341 .await
342 .unwrap()
343 .call(carrier_exchange().await)
344 .await;
345 assert!(result.is_err());
346 match result.unwrap_err() {
347 CamelError::Unauthorized(msg) => assert!(msg.contains("missing role")),
348 other => panic!("expected Unauthorized, got: {other:?}"),
349 }
350 }
351
352 #[tokio::test]
353 async fn test_denied_error_contains_required_actual() {
354 let layer = policy_only_layer(Arc::new(DenyPolicy));
355 let mut svc = layer.layer(ok_processor());
356 let result = svc
357 .ready()
358 .await
359 .unwrap()
360 .call(carrier_exchange().await)
361 .await;
362 let msg = match result.unwrap_err() {
363 CamelError::Unauthorized(msg) => msg,
364 other => panic!("expected Unauthorized, got: {other:?}"),
365 };
366 assert!(msg.contains("admin"));
367 assert!(msg.contains("user"));
368 }
369
370 #[tokio::test]
371 async fn test_evaluate_error_propagates() {
372 let layer = policy_only_layer(Arc::new(FailPolicy));
373 let mut svc = layer.layer(ok_processor());
374 let result = svc
375 .ready()
376 .await
377 .unwrap()
378 .call(carrier_exchange().await)
379 .await;
380 match result.unwrap_err() {
381 CamelError::Unauthenticated(msg) => assert!(msg.contains("invalid token")),
382 other => panic!("expected Unauthenticated, got: {other:?}"),
383 }
384 }
385
386 #[tokio::test]
387 async fn test_multiple_calls_share_policy() {
388 let count = Arc::new(AtomicU32::new(0));
389 struct CountingPolicy {
390 count: Arc<AtomicU32>,
391 }
392 #[async_trait]
393 impl SecurityPolicy for CountingPolicy {
394 async fn evaluate(
395 &self,
396 _exchange: &mut Exchange,
397 _auth: &AuthContext<'_>,
398 ) -> Result<AuthorizationDecision, CamelError> {
399 self.count.fetch_add(1, Ordering::SeqCst);
400 Ok(AuthorizationDecision::Granted {
401 principal: Principal {
402 subject: "user1".into(),
403 issuer: "test".into(),
404 audience: vec![],
405 scopes: vec![],
406 roles: vec![],
407 claims: serde_json::Value::Null,
408 },
409 })
410 }
411 }
412 let policy = Arc::new(CountingPolicy {
413 count: Arc::clone(&count),
414 });
415 let layer = policy_only_layer(Arc::clone(&policy) as Arc<dyn SecurityPolicy>);
416 let mut svc = layer.layer(ok_processor());
417 for _ in 0..3 {
418 let result = svc
419 .ready()
420 .await
421 .unwrap()
422 .call(carrier_exchange().await)
423 .await;
424 assert!(result.is_ok());
425 }
426 assert_eq!(count.load(Ordering::SeqCst), 3);
427 }
428
429 #[tokio::test]
430 async fn test_granted_all_property_json_formats() {
431 let layer = policy_only_layer(Arc::new(GrantPolicy));
432 let mut svc = layer.layer(ok_processor());
433 let result = svc
434 .ready()
435 .await
436 .unwrap()
437 .call(carrier_exchange().await)
438 .await;
439 let ex = result.unwrap();
440
441 let roles: Vec<String> =
442 serde_json::from_str(ex.property(PRINCIPAL_ROLES_KEY).unwrap().as_str().unwrap())
443 .unwrap();
444 assert_eq!(roles, vec!["admin"]);
445
446 let scopes: Vec<String> =
447 serde_json::from_str(ex.property(PRINCIPAL_SCOPES_KEY).unwrap().as_str().unwrap())
448 .unwrap();
449 assert_eq!(scopes, vec!["read"]);
450
451 let audience: Vec<String> = serde_json::from_str(
452 ex.property(PRINCIPAL_AUDIENCE_KEY)
453 .unwrap()
454 .as_str()
455 .unwrap(),
456 )
457 .unwrap();
458 assert_eq!(audience, vec!["api"]);
459
460 let claims: serde_json::Value =
461 serde_json::from_str(ex.property(PRINCIPAL_CLAIMS_KEY).unwrap().as_str().unwrap())
462 .unwrap();
463 assert_eq!(claims["sub"], "user1");
464 }
465
466 #[tokio::test]
467 async fn test_granted_empty_principal_fields() {
468 struct EmptyPrincipalPolicy;
469 #[async_trait]
470 impl SecurityPolicy for EmptyPrincipalPolicy {
471 async fn evaluate(
472 &self,
473 _exchange: &mut Exchange,
474 _auth: &AuthContext<'_>,
475 ) -> Result<AuthorizationDecision, CamelError> {
476 Ok(AuthorizationDecision::Granted {
477 principal: Principal {
478 subject: "minimal".into(),
479 issuer: String::new(),
480 audience: vec![],
481 scopes: vec![],
482 roles: vec![],
483 claims: serde_json::Value::Null,
484 },
485 })
486 }
487 }
488 let layer = policy_only_layer(Arc::new(EmptyPrincipalPolicy));
489 let mut svc = layer.layer(ok_processor());
490 let result = svc
491 .ready()
492 .await
493 .unwrap()
494 .call(carrier_exchange().await)
495 .await;
496 let ex = result.unwrap();
497
498 assert_eq!(
499 ex.property(PRINCIPAL_SUBJECT_KEY),
500 Some(&serde_json::Value::String("minimal".into()))
501 );
502 assert_eq!(
503 ex.property(PRINCIPAL_ISSUER_KEY),
504 Some(&serde_json::Value::String(String::new()))
505 );
506 let roles: Vec<String> =
507 serde_json::from_str(ex.property(PRINCIPAL_ROLES_KEY).unwrap().as_str().unwrap())
508 .unwrap();
509 assert!(roles.is_empty());
510 }
511
512 #[tokio::test]
513 async fn test_layer_clone_produces_working_service() {
514 let layer = policy_only_layer(Arc::new(GrantPolicy));
515 let mut svc1 = layer.layer(ok_processor());
516 let svc2 = svc1.clone();
517
518 let r1 = svc1
519 .ready()
520 .await
521 .unwrap()
522 .call(carrier_exchange().await)
523 .await;
524 let mut svc2 = svc2;
525 let r2 = svc2
526 .ready()
527 .await
528 .unwrap()
529 .call(carrier_exchange().await)
530 .await;
531 assert!(r1.is_ok());
532 assert!(r2.is_ok());
533 }
534
535 #[tokio::test]
536 async fn test_granted_preserves_original_exchange_properties() {
537 struct GrantPolicy;
538 #[async_trait]
539 impl SecurityPolicy for GrantPolicy {
540 async fn evaluate(
541 &self,
542 _exchange: &mut Exchange,
543 _auth: &AuthContext<'_>,
544 ) -> Result<AuthorizationDecision, CamelError> {
545 Ok(AuthorizationDecision::Granted {
546 principal: Principal {
547 subject: "u".into(),
548 issuer: "i".into(),
549 audience: vec![],
550 scopes: vec![],
551 roles: vec![],
552 claims: serde_json::Value::Null,
553 },
554 })
555 }
556 }
557 let layer = policy_only_layer(Arc::new(GrantPolicy));
558 let mut svc = layer.layer(ok_processor());
559 let mut ex = carrier_exchange().await;
560 ex.set_property("custom.key", "custom-value");
561 let result = svc.ready().await.unwrap().call(ex).await;
562 let ex = result.unwrap();
563 assert_eq!(
564 ex.property("custom.key"),
565 Some(&serde_json::Value::String("custom-value".into()))
566 );
567 assert!(ex.property(PRINCIPAL_SUBJECT_KEY).is_some());
568 }
569
570 #[tokio::test]
573 async fn layer_denies_without_typed_principal_or_token() {
574 let policy = Arc::new(RecordingGrantPolicy {
575 count: AtomicU32::new(0),
576 seen: Mutex::new(Vec::new()),
577 });
578 let layer = SecurityPolicyLayer::new(
579 Arc::clone(&policy) as Arc<dyn SecurityPolicy>,
580 TransportId::Http,
581 );
582 let mut svc = layer.layer(ok_processor());
583 let result = svc.ready().await.unwrap().call(make_exchange()).await;
584 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
585 assert_eq!(policy.count.load(Ordering::SeqCst), 0);
586 }
587
588 #[tokio::test]
589 async fn layer_grants_with_typed_principal() {
590 let policy = Arc::new(RecordingGrantPolicy {
591 count: AtomicU32::new(0),
592 seen: Mutex::new(Vec::new()),
593 });
594 let layer = SecurityPolicyLayer::new(
595 Arc::clone(&policy) as Arc<dyn SecurityPolicy>,
596 TransportId::Http,
597 );
598
599 let providers = provider_registry("idp-a", "t-a");
601 let plan = authenticated_plan("idp-a");
602 let principal = kernel_authenticate(&plan, &providers, &credentials("t-a"))
603 .await
604 .unwrap();
605 let mut exchange = make_exchange();
606 install_carrier(&mut exchange, &principal);
607
608 let mut svc = layer.layer(ok_processor());
609 let result = svc.ready().await.unwrap().call(exchange).await;
610 assert!(result.is_ok());
611 assert_eq!(policy.count.load(Ordering::SeqCst), 1);
612 let seen = policy.seen.lock().unwrap();
613 assert_eq!(seen.len(), 1);
614 assert_eq!(seen[0].0, "user1");
615 assert_eq!(seen[0].1, "idp-a");
616 }
617
618 #[tokio::test]
619 async fn layer_bearer_token_without_carrier_denies() {
620 let policy = Arc::new(RecordingGrantPolicy {
624 count: AtomicU32::new(0),
625 seen: Mutex::new(Vec::new()),
626 });
627 let layer = SecurityPolicyLayer::new(
628 Arc::clone(&policy) as Arc<dyn SecurityPolicy>,
629 TransportId::Http,
630 );
631 let mut exchange = make_exchange();
632 exchange.input.set_header("Authorization", "Bearer t-a");
633 let mut svc = layer.layer(ok_processor());
634 let result = svc.ready().await.unwrap().call(exchange).await;
635 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
636 assert_eq!(
637 policy.count.load(Ordering::SeqCst),
638 0,
639 "the policy must never evaluate a carrier-less Exchange"
640 );
641 }
642
643 #[tokio::test]
644 async fn policy_only_route_without_carrier_denies() {
645 let policy = Arc::new(RecordingGrantPolicy {
649 count: AtomicU32::new(0),
650 seen: Mutex::new(Vec::new()),
651 });
652 let layer = SecurityPolicyLayer::new(
653 Arc::clone(&policy) as Arc<dyn SecurityPolicy>,
654 TransportId::Http,
655 );
656 let mut svc = layer.layer(ok_processor());
657 let result = svc.ready().await.unwrap().call(make_exchange()).await;
658 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
659 assert_eq!(policy.count.load(Ordering::SeqCst), 0);
660 }
661
662 #[tokio::test]
663 async fn spoofed_extension_value_does_not_authorize() {
664 let layer = SecurityPolicyLayer::new(Arc::new(GrantPolicy), TransportId::Http);
665 let mut exchange = make_exchange();
666 exchange.set_extension(KERNEL_PRINCIPAL_KEY, Arc::new("x".to_string()));
669 let mut svc = layer.layer(ok_processor());
670 let result = svc.ready().await.unwrap().call(exchange).await;
671 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
672 }
673
674 #[tokio::test]
675 async fn spoofed_legacy_property_without_token_denies() {
676 let layer = SecurityPolicyLayer::new(Arc::new(GrantPolicy), TransportId::Http);
677 let mut exchange = make_exchange();
678 store_principal_properties(&mut exchange, &test_principal());
681 let mut svc = layer.layer(ok_processor());
682 let result = svc.ready().await.unwrap().call(exchange).await;
683 assert!(matches!(result, Err(CamelError::Unauthenticated(_))));
684 }
685}