1use crate::{Region, RegionError};
2use regex::Regex;
3use std::sync::LazyLock;
4use url::Url;
5
6static REGION_HOST_REGEX: LazyLock<Regex> = LazyLock::new(|| {
8 Regex::new(r"^(?:https://){0,1}([^\.]+)\.([^\.]+)\.viturhosted\.net/?$").expect("Invalid regex")
9});
10
11static CTS_REGION_HOST_REGEX: LazyLock<Regex> = LazyLock::new(|| {
13 Regex::new(r"^(?:https://){0,1}([^\.]+)\.([^\.]+)\.cts\.cipherstashmanaged\.net/?$")
14 .expect("Invalid regex")
15});
16
17static DOMAIN_NAME: &str = "viturhosted.net";
19
20static CTS_DOMAIN_NAME: &str = "cts.cipherstashmanaged.net";
22
23pub trait ServiceDiscovery {
25 fn name(&self) -> &'static str;
26 fn fqdn(region: Region) -> String;
27 fn endpoint(region: Region) -> Result<Url, RegionError>;
28}
29
30pub struct ZeroKmsServiceDiscovery;
32
33impl ServiceDiscovery for ZeroKmsServiceDiscovery {
34 fn name(&self) -> &'static str {
35 "zerokms"
36 }
37
38 fn fqdn(region: Region) -> String {
41 format!("{}.{DOMAIN_NAME}", region.identifier())
42 }
43
44 fn endpoint(region: Region) -> Result<Url, RegionError> {
50 #[cfg(feature = "test_utils")]
51 if let Ok(override_url) = std::env::var("ZEROKMS_DISCOVERY_OVERRIDE") {
52 return Url::parse(&override_url).map_err(|e| {
53 RegionError::InvalidRegion(format!(
54 "Invalid ZEROKMS_DISCOVERY_OVERRIDE URL '{}': {}",
55 override_url, e
56 ))
57 });
58 }
59
60 Url::parse(&format!("https://{}.{DOMAIN_NAME}/", region.identifier())).map_err(|e| {
61 RegionError::InvalidRegion(format!(
62 "Invalid service URL for ZeroKMS in region {}: {}",
63 region.identifier(),
64 e
65 ))
66 })
67 }
68}
69
70impl ZeroKmsServiceDiscovery {
71 pub fn region_from_host_fqdn(host_fqdn: &str) -> Result<Region, RegionError> {
75 REGION_HOST_REGEX
76 .captures(host_fqdn)
77 .and_then(|caps| Some((caps.get(1)?, caps.get(2)?)))
78 .map(|(r, p)| format!("{}.{}", r.as_str(), p.as_str()))
79 .ok_or_else(|| RegionError::InvalidHostFqdn(host_fqdn.to_string()))
80 .and_then(|ident| Region::new(&ident))
81 }
82}
83
84pub struct CtsServiceDiscovery;
86
87impl ServiceDiscovery for CtsServiceDiscovery {
88 fn name(&self) -> &'static str {
89 "cts"
90 }
91
92 fn fqdn(region: Region) -> String {
93 format!("{}.{CTS_DOMAIN_NAME}", region.identifier())
94 }
95
96 fn endpoint(region: Region) -> Result<Url, RegionError> {
97 Url::parse(&format!(
98 "https://{}.{CTS_DOMAIN_NAME}/",
99 region.identifier()
100 ))
101 .map_err(|e| {
102 RegionError::InvalidRegion(format!(
103 "Invalid service URL for CTS in region {}: {}",
104 region.identifier(),
105 e
106 ))
107 })
108 }
109}
110
111impl CtsServiceDiscovery {
112 pub fn region_from_host_fqdn(host_fqdn: &str) -> Result<Region, RegionError> {
116 CTS_REGION_HOST_REGEX
117 .captures(host_fqdn)
118 .and_then(|caps| Some((caps.get(1)?, caps.get(2)?)))
119 .map(|(r, p)| format!("{}.{}", r.as_str(), p.as_str()))
120 .ok_or_else(|| RegionError::InvalidHostFqdn(host_fqdn.to_string()))
121 .and_then(|ident| Region::new(&ident))
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_region_from_host_fqdn() -> anyhow::Result<()> {
131 let host_fqdn = "us-west-1.aws.viturhosted.net";
132 let region = ZeroKmsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
133 assert_eq!(region.identifier(), "us-west-1.aws");
134
135 Ok(())
136 }
137
138 #[test]
139 fn test_region_from_host_endpoint() -> anyhow::Result<()> {
140 let host_fqdn = "https://us-west-1.aws.viturhosted.net";
141 let region = ZeroKmsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
142 assert_eq!(region.identifier(), "us-west-1.aws");
143 Ok(())
144 }
145
146 #[test]
147 fn test_region_from_host_endpoint_with_trailing_slash() -> anyhow::Result<()> {
148 let host_fqdn = "https://us-west-1.aws.viturhosted.net/";
149 let region = ZeroKmsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
150 assert_eq!(region.identifier(), "us-west-1.aws");
151 Ok(())
152 }
153
154 #[test]
155 fn test_cts_endpoint_ap_southeast_2() -> anyhow::Result<()> {
156 let region = Region::new("ap-southeast-2.aws")?;
157 let url = CtsServiceDiscovery::endpoint(region)?;
158 assert_eq!(
159 url.as_str(),
160 "https://ap-southeast-2.aws.cts.cipherstashmanaged.net/"
161 );
162 Ok(())
163 }
164
165 #[test]
166 fn test_cts_endpoint_us_east_1() -> anyhow::Result<()> {
167 let region = Region::new("us-east-1.aws")?;
168 let url = CtsServiceDiscovery::endpoint(region)?;
169 assert_eq!(
170 url.as_str(),
171 "https://us-east-1.aws.cts.cipherstashmanaged.net/"
172 );
173 Ok(())
174 }
175
176 #[test]
177 fn test_cts_region_from_host_fqdn() -> anyhow::Result<()> {
178 let host_fqdn = "us-east-1.aws.cts.cipherstashmanaged.net";
179 let region = CtsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
180 assert_eq!(region.identifier(), "us-east-1.aws");
181 Ok(())
182 }
183
184 #[test]
185 fn test_cts_region_from_host_endpoint() -> anyhow::Result<()> {
186 let host_fqdn = "https://ap-southeast-2.aws.cts.cipherstashmanaged.net";
187 let region = CtsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
188 assert_eq!(region.identifier(), "ap-southeast-2.aws");
189 Ok(())
190 }
191
192 #[test]
193 fn test_cts_region_from_host_endpoint_with_trailing_slash() -> anyhow::Result<()> {
194 let host_fqdn = "https://us-west-2.aws.cts.cipherstashmanaged.net/";
195 let region = CtsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
196 assert_eq!(region.identifier(), "us-west-2.aws");
197 Ok(())
198 }
199
200 #[test]
201 fn test_cts_fqdn_us_west_2() -> anyhow::Result<()> {
202 let region = Region::new("us-west-2.aws")?;
203 assert_eq!(
204 CtsServiceDiscovery::fqdn(region),
205 "us-west-2.aws.cts.cipherstashmanaged.net"
206 );
207 Ok(())
208 }
209
210 #[test]
211 fn test_zerokms_region_from_invalid_host() {
212 let result = ZeroKmsServiceDiscovery::region_from_host_fqdn("not-a-valid-host");
213 assert!(result.is_err());
214 }
215
216 #[test]
217 fn test_zerokms_region_from_wrong_domain() {
218 let result =
219 ZeroKmsServiceDiscovery::region_from_host_fqdn("us-east-1.aws.wrongdomain.net");
220 assert!(result.is_err());
221 }
222
223 #[test]
224 fn test_zerokms_region_from_missing_region_component() {
225 let result = ZeroKmsServiceDiscovery::region_from_host_fqdn("viturhosted.net");
226 assert!(result.is_err());
227 }
228
229 #[test]
230 fn test_cts_region_from_invalid_host() {
231 let result = CtsServiceDiscovery::region_from_host_fqdn("not-a-valid-host");
232 assert!(result.is_err());
233 }
234
235 #[test]
236 fn test_cts_region_from_wrong_domain() {
237 let result = CtsServiceDiscovery::region_from_host_fqdn("us-east-1.aws.wrongdomain.net");
238 assert!(result.is_err());
239 }
240
241 #[test]
242 fn test_cts_region_from_missing_region_component() {
243 let result = CtsServiceDiscovery::region_from_host_fqdn("cts.cipherstashmanaged.net");
244 assert!(result.is_err());
245 }
246}