photon_backend/
broker_security.rs1use crate::{PhotonError, Result};
4
5pub const ALLOW_INSECURE_BROKER_ENV: &str = "PHOTON_ALLOW_INSECURE_BROKER";
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum BrokerTransportSecurity {
14 #[default]
16 RequireTls,
17 AllowInsecurePlaintext,
19}
20
21impl BrokerTransportSecurity {
22 #[must_use]
24 pub fn from_env() -> Self {
25 match std::env::var(ALLOW_INSECURE_BROKER_ENV).as_deref() {
26 Ok("1" | "true" | "TRUE" | "yes" | "YES") => Self::AllowInsecurePlaintext,
27 _ => Self::RequireTls,
28 }
29 }
30
31 #[must_use]
33 pub const fn allows_plaintext(self) -> bool {
34 matches!(self, Self::AllowInsecurePlaintext)
35 }
36
37 pub fn check_endpoint(self, endpoint: &str) -> Result<()> {
47 if self.allows_plaintext() || endpoint_looks_tls(endpoint) {
48 return Ok(());
49 }
50 Err(PhotonError::Internal(format!(
51 "plaintext broker endpoint rejected under BrokerTransportSecurity::RequireTls \
52 (endpoint looks non-TLS). Opt in explicitly with .allow_insecure_plaintext() \
53 or {ALLOW_INSECURE_BROKER_ENV}=1 for development/CI only"
54 )))
55 }
56}
57
58fn endpoint_looks_tls(endpoint: &str) -> bool {
59 let trimmed = endpoint.trim();
60 let lower = trimmed.to_ascii_lowercase();
61 if lower.starts_with("tls://")
62 || lower.starts_with("ssl://")
63 || lower.starts_with("https://")
64 || lower.starts_with("nats+tls://")
65 || lower.starts_with("rediss://")
66 {
67 return true;
68 }
69 if trimmed.contains(',') {
71 return trimmed
72 .split(',')
73 .map(str::trim)
74 .filter(|s| !s.is_empty())
75 .all(endpoint_looks_tls);
76 }
77 false
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn require_tls_rejects_nats_plaintext() {
86 let err = BrokerTransportSecurity::RequireTls
87 .check_endpoint("nats://127.0.0.1:4222")
88 .expect_err("plaintext");
89 assert!(err.to_string().contains("plaintext"));
90 }
91
92 #[test]
93 fn require_tls_accepts_tls_scheme() {
94 BrokerTransportSecurity::RequireTls
95 .check_endpoint("tls://broker.example:4222")
96 .expect("tls ok");
97 }
98
99 #[test]
100 fn insecure_allows_nats_plaintext() {
101 BrokerTransportSecurity::AllowInsecurePlaintext
102 .check_endpoint("nats://127.0.0.1:4222")
103 .expect("insecure ok");
104 }
105}