1use std::sync::Arc;
5
6use serde::Deserialize;
7
8use slim_auth::auth_provider::{AuthProvider, AuthVerifier};
9use slim_config::auth::jwt::Config as JwtConfig;
10use slim_config::auth::static_jwt::Config as StaticJwtConfig;
11use slim_config::component::configuration::{Configuration, ConfigurationError};
12use slim_config::component::id::ID;
13use slim_config::grpc::client::ClientConfig;
14use slim_config::grpc::server::ServerConfig;
15use slim_datapath::message_processing::MessageProcessor;
16
17use crate::service::{ControlPlane, ControlPlaneSettings};
18
19#[derive(Default, Debug, Clone, Deserialize, PartialEq, serde::Serialize)]
20#[serde(rename_all = "snake_case")]
21pub enum TokenProviderAuthConfig {
22 SharedSecret(String),
23 StaticJwt(StaticJwtConfig),
24 Jwt(JwtConfig),
25 #[default]
26 None,
27}
28
29#[derive(Default, Debug, Clone, Deserialize, PartialEq, serde::Serialize)]
30#[serde(rename_all = "snake_case")]
31pub enum TokenVerifierAuthConfig {
32 SharedSecret(String),
33 Jwt(JwtConfig),
34 #[default]
35 None,
36}
37
38#[derive(Debug, Clone, Deserialize, Default, PartialEq)]
40pub struct Config {
41 #[serde(default)]
43 pub servers: Vec<ServerConfig>,
44
45 #[serde(default)]
47 pub clients: Vec<ClientConfig>,
48
49 #[serde(with = "serde_yaml::with::singleton_map")]
51 #[serde(default)]
52 pub token_provider: TokenProviderAuthConfig,
53
54 #[serde(with = "serde_yaml::with::singleton_map")]
56 #[serde(default)]
57 pub token_verifier: TokenVerifierAuthConfig,
58}
59
60impl Config {
61 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn with_servers(self, servers: Vec<ServerConfig>) -> Self {
68 Self { servers, ..self }
69 }
70
71 pub fn with_clients(self, clients: Vec<ClientConfig>) -> Self {
73 Self { clients, ..self }
74 }
75
76 pub fn with_token_provider_auth(self, auth: TokenProviderAuthConfig) -> Self {
78 Self {
79 token_provider: auth,
80 ..self
81 }
82 }
83
84 pub fn with_token_verifier_auth(self, auth: TokenVerifierAuthConfig) -> Self {
86 Self {
87 token_verifier: auth,
88 ..self
89 }
90 }
91
92 pub fn servers(&self) -> &[ServerConfig] {
94 &self.servers
95 }
96
97 pub fn clients(&self) -> &[ClientConfig] {
99 &self.clients
100 }
101
102 fn get_token_provider_auth(&self) -> Option<AuthProvider> {
103 match &self.token_provider {
104 TokenProviderAuthConfig::SharedSecret(secret) => Some(
105 AuthProvider::shared_secret_from_str("control-plane", secret),
106 ),
107 TokenProviderAuthConfig::StaticJwt(static_jwt_config) => {
108 let provider = static_jwt_config
109 .build_static_token_provider()
110 .expect("Failed to build StaticTokenProvider");
111 Some(AuthProvider::static_token(provider))
112 }
113 TokenProviderAuthConfig::Jwt(jwt_config) => {
114 let provider = jwt_config
115 .get_provider()
116 .expect("Failed to build JwtTokenProvider");
117 Some(AuthProvider::jwt_signer(provider))
118 }
119 TokenProviderAuthConfig::None => None,
120 }
121 }
122
123 fn get_token_verifier_auth(&self) -> Option<AuthVerifier> {
124 match &self.token_verifier {
125 TokenVerifierAuthConfig::SharedSecret(secret) => Some(
126 AuthVerifier::shared_secret_from_str("control-plane", secret),
127 ),
128 TokenVerifierAuthConfig::Jwt(jwt_config) => {
129 let verifier = jwt_config
130 .get_verifier()
131 .expect("Failed to build JwtTokenVerifier");
132 Some(AuthVerifier::jwt_verifier(verifier))
133 }
134 TokenVerifierAuthConfig::None => None,
135 }
136 }
137
138 pub fn into_service(
140 &self,
141 id: ID,
142 group_name: Option<String>,
143 rx_drain: drain::Watch,
144 message_processor: Arc<MessageProcessor>,
145 servers: &[ServerConfig],
146 ) -> ControlPlane {
147 let auth_provider = self.get_token_provider_auth();
148 let auth_verifier = self.get_token_verifier_auth();
149
150 ControlPlane::new(ControlPlaneSettings {
151 id,
152 group_name,
153 servers: self.servers.clone(),
154 clients: self.clients.clone(),
155 drain_rx: rx_drain,
156 message_processor,
157 pubsub_servers: servers.to_vec(),
158 auth_provider,
159 auth_verifier,
160 })
161 }
162}
163
164impl Configuration for Config {
165 fn validate(&self) -> Result<(), ConfigurationError> {
166 for server in self.servers.iter() {
168 server.validate()?;
169 }
170
171 for client in &self.clients {
172 client.validate()?;
173 }
174
175 Ok(())
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use slim_config::auth::jwt::Config as JwtConfig;
183 use slim_config::auth::static_jwt::Config as StaticJwtConfig;
184 use slim_config::component::id::{ID, Kind};
185 use slim_config::grpc::client::ClientConfig;
186 use slim_config::grpc::server::ServerConfig;
187 use slim_datapath::message_processing::MessageProcessor;
188 use std::sync::Arc;
189
190 fn create_test_server_config() -> ServerConfig {
191 ServerConfig::with_endpoint("127.0.0.1:50051")
192 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure())
193 }
194
195 fn create_test_client_config() -> ClientConfig {
196 ClientConfig::with_endpoint("http://127.0.0.1:50051")
197 .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure())
198 }
199
200 #[test]
201 fn test_config_new() {
202 let config = Config::new();
203 assert!(config.servers.is_empty());
204 assert!(config.clients.is_empty());
205 assert_eq!(config.token_provider, TokenProviderAuthConfig::None);
206 assert_eq!(config.token_verifier, TokenVerifierAuthConfig::None);
207 }
208
209 #[test]
210 fn test_config_default() {
211 let config = Config::default();
212 assert!(config.servers.is_empty());
213 assert!(config.clients.is_empty());
214 assert_eq!(config.token_provider, TokenProviderAuthConfig::None);
215 assert_eq!(config.token_verifier, TokenVerifierAuthConfig::None);
216 }
217
218 #[test]
219 fn test_config_with_servers() {
220 let server_config = create_test_server_config();
221 let config = Config::new().with_servers(vec![server_config.clone()]);
222
223 assert_eq!(config.servers.len(), 1);
224 assert_eq!(config.servers[0], server_config);
225 assert!(config.clients.is_empty());
226 }
227
228 #[test]
229 fn test_config_with_clients() {
230 let client_config = create_test_client_config();
231 let config = Config::new().with_clients(vec![client_config.clone()]);
232
233 assert_eq!(config.clients.len(), 1);
234 assert_eq!(config.clients[0], client_config);
235 assert!(config.servers.is_empty());
236 }
237
238 #[test]
239 fn test_config_with_token_provider_auth_shared_secret() {
240 let auth = TokenProviderAuthConfig::SharedSecret("test-secret".to_string());
241 let config = Config::new().with_token_provider_auth(auth.clone());
242
243 assert_eq!(config.token_provider, auth);
244 assert_eq!(config.token_verifier, TokenVerifierAuthConfig::None);
245 }
246
247 #[test]
248 fn test_config_with_token_provider_auth_static_jwt() {
249 let static_jwt_config = StaticJwtConfig::with_file("test-key".to_string());
250 let auth = TokenProviderAuthConfig::StaticJwt(static_jwt_config);
251 let config = Config::new().with_token_provider_auth(auth.clone());
252
253 assert_eq!(config.token_provider, auth);
254 }
255
256 #[test]
257 fn test_config_with_token_provider_auth_jwt() {
258 use slim_auth::jwt::{Algorithm, Key, KeyData, KeyFormat};
259 use slim_config::auth::jwt::{Claims, JwtKey};
260 use std::time::Duration;
261
262 let claims = Claims::default();
263 let duration = Duration::from_secs(3600);
264 let key = JwtKey::Encoding(Key {
265 algorithm: Algorithm::HS256,
266 format: KeyFormat::Pem,
267 key: KeyData::Str("test-secret".to_string()),
268 });
269 let jwt_config = JwtConfig::new(claims, duration, key);
270 let auth = TokenProviderAuthConfig::Jwt(jwt_config);
271 let config = Config::new().with_token_provider_auth(auth.clone());
272
273 assert_eq!(config.token_provider, auth);
274 }
275
276 #[test]
277 fn test_config_with_token_verifier_auth_shared_secret() {
278 let auth = TokenVerifierAuthConfig::SharedSecret("test-secret".to_string());
279 let config = Config::new().with_token_verifier_auth(auth.clone());
280
281 assert_eq!(config.token_verifier, auth);
282 assert_eq!(config.token_provider, TokenProviderAuthConfig::None);
283 }
284
285 #[test]
286 fn test_config_with_token_verifier_auth_jwt() {
287 use slim_auth::jwt::{Algorithm, Key, KeyData, KeyFormat};
288 use slim_config::auth::jwt::{Claims, JwtKey};
289 use std::time::Duration;
290
291 let claims = Claims::default();
292 let duration = Duration::from_secs(3600);
293 let key = JwtKey::Decoding(Key {
294 algorithm: Algorithm::HS256,
295 format: KeyFormat::Pem,
296 key: KeyData::Str("test-secret".to_string()),
297 });
298 let jwt_config = JwtConfig::new(claims, duration, key);
299 let auth = TokenVerifierAuthConfig::Jwt(jwt_config);
300 let config = Config::new().with_token_verifier_auth(auth.clone());
301
302 assert_eq!(config.token_verifier, auth);
303 }
304
305 #[test]
306 fn test_config_servers_getter() {
307 let server_config = create_test_server_config();
308 let config = Config::new().with_servers(vec![server_config.clone()]);
309
310 let servers = config.servers();
311 assert_eq!(servers.len(), 1);
312 assert_eq!(servers[0], server_config);
313 }
314
315 #[test]
316 fn test_config_clients_getter() {
317 let client_config = create_test_client_config();
318 let config = Config::new().with_clients(vec![client_config.clone()]);
319
320 let clients = config.clients();
321 assert_eq!(clients.len(), 1);
322 assert_eq!(clients[0], client_config);
323 }
324
325 #[test]
326 fn test_config_chaining() {
327 let server_config = create_test_server_config();
328 let client_config = create_test_client_config();
329 let provider_auth = TokenProviderAuthConfig::SharedSecret("provider-secret".to_string());
330 let verifier_auth = TokenVerifierAuthConfig::SharedSecret("verifier-secret".to_string());
331
332 let config = Config::new()
333 .with_servers(vec![server_config.clone()])
334 .with_clients(vec![client_config.clone()])
335 .with_token_provider_auth(provider_auth.clone())
336 .with_token_verifier_auth(verifier_auth.clone());
337
338 assert_eq!(config.servers.len(), 1);
339 assert_eq!(config.clients.len(), 1);
340 assert_eq!(config.token_provider, provider_auth);
341 assert_eq!(config.token_verifier, verifier_auth);
342 }
343
344 #[test]
345 fn test_config_validate_empty() {
346 let config = Config::new();
347 assert!(config.validate().is_ok());
348 }
349
350 #[test]
351 fn test_config_validate_with_valid_servers_and_clients() {
352 let server_config = create_test_server_config();
353 let client_config = create_test_client_config();
354 let config = Config::new()
355 .with_servers(vec![server_config])
356 .with_clients(vec![client_config]);
357
358 assert!(config.validate().is_ok());
359 }
360
361 #[test]
362 fn test_token_provider_auth_config_equality() {
363 let secret1 = TokenProviderAuthConfig::SharedSecret("secret1".to_string());
364 let secret2 = TokenProviderAuthConfig::SharedSecret("secret1".to_string());
365 let secret3 = TokenProviderAuthConfig::SharedSecret("secret2".to_string());
366
367 assert_eq!(secret1, secret2);
368 assert_ne!(secret1, secret3);
369 }
370
371 #[test]
372 fn test_token_verifier_auth_config_equality() {
373 let secret1 = TokenVerifierAuthConfig::SharedSecret("secret1".to_string());
374 let secret2 = TokenVerifierAuthConfig::SharedSecret("secret1".to_string());
375 let secret3 = TokenVerifierAuthConfig::SharedSecret("secret2".to_string());
376
377 assert_eq!(secret1, secret2);
378 assert_ne!(secret1, secret3);
379 }
380
381 #[test]
382 fn test_config_clone() {
383 let server_config = create_test_server_config();
384 let client_config = create_test_client_config();
385 let auth = TokenProviderAuthConfig::SharedSecret("test-secret".to_string());
386
387 let config1 = Config::new()
388 .with_servers(vec![server_config])
389 .with_clients(vec![client_config])
390 .with_token_provider_auth(auth);
391
392 let config2 = config1.clone();
393
394 assert_eq!(config1.servers, config2.servers);
395 assert_eq!(config1.clients, config2.clients);
396 assert_eq!(config1.token_provider, config2.token_provider);
397 assert_eq!(config1.token_verifier, config2.token_verifier);
398 }
399
400 #[tokio::test]
401 async fn test_config_into_service() {
402 let server_config = create_test_server_config();
403 let client_config = create_test_client_config();
404 let config = Config::new()
405 .with_servers(vec![server_config.clone()])
406 .with_clients(vec![client_config]);
407
408 let id = ID::new_with_name(Kind::new("slim").unwrap(), "test-instance").unwrap();
409 let group_name = Some("test-group".to_string());
410 let (_, rx_drain) = drain::channel();
411 let message_processor = Arc::new(MessageProcessor::with_drain_channel(rx_drain.clone()));
412 let servers = vec![server_config];
413
414 let _control_plane =
415 config.into_service(id, group_name, rx_drain, message_processor, &servers);
416 }
417
418 #[test]
419 fn test_config_debug_trait() {
420 let config = Config::new();
421 let debug_str = format!("{:?}", config);
422 assert!(debug_str.contains("Config"));
423 assert!(debug_str.contains("servers"));
424 assert!(debug_str.contains("clients"));
425 }
426
427 mod serde_tests {
428 use super::*;
429 use serde_json;
430
431 #[test]
432 fn test_token_provider_auth_config_serialize_shared_secret() {
433 let auth = TokenProviderAuthConfig::SharedSecret("test-secret".to_string());
434 let json = serde_json::to_string(&auth).unwrap();
435 assert!(json.contains("shared_secret"));
436 assert!(json.contains("test-secret"));
437 }
438
439 #[test]
440 fn test_token_provider_auth_config_deserialize_shared_secret() {
441 let json = r#"{"shared_secret":"test-secret"}"#;
442 let auth: TokenProviderAuthConfig = serde_json::from_str(json).unwrap();
443
444 match auth {
445 TokenProviderAuthConfig::SharedSecret(secret) => {
446 assert_eq!(secret, "test-secret");
447 }
448 _ => panic!("Expected SharedSecret variant"),
449 }
450 }
451
452 #[test]
453 fn test_config_validate_with_multiple_servers() {
454 let server1 = create_test_server_config();
455 let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
456 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
457
458 let config = Config::new().with_servers(vec![server1, server2]);
459 assert!(config.validate().is_ok());
460 }
461
462 #[test]
463 fn test_config_validate_with_multiple_clients() {
464 let client1 = create_test_client_config();
465 let client2 = ClientConfig::with_endpoint("http://127.0.0.1:50052")
466 .with_tls_setting(slim_config::tls::client::TlsClientConfig::insecure());
467
468 let config = Config::new().with_clients(vec![client1, client2]);
469 assert!(config.validate().is_ok());
470 }
471
472 #[test]
473 fn test_config_with_all_auth_combinations() {
474 let provider_auth =
475 TokenProviderAuthConfig::SharedSecret("provider-secret".to_string());
476 let verifier_auth =
477 TokenVerifierAuthConfig::SharedSecret("verifier-secret".to_string());
478
479 let config = Config::new()
480 .with_token_provider_auth(provider_auth.clone())
481 .with_token_verifier_auth(verifier_auth.clone());
482
483 assert_eq!(config.token_provider, provider_auth);
484 assert_eq!(config.token_verifier, verifier_auth);
485 }
486
487 #[test]
488 fn test_empty_servers_slice() {
489 let config = Config::new();
490 let servers = config.servers();
491 assert!(servers.is_empty());
492 assert_eq!(servers.len(), 0);
493 }
494
495 #[test]
496 fn test_empty_clients_slice() {
497 let config = Config::new();
498 let clients = config.clients();
499 assert!(clients.is_empty());
500 assert_eq!(clients.len(), 0);
501 }
502
503 #[test]
504 fn test_config_partial_eq() {
505 let config1 = Config::new();
506 let config2 = Config::new();
507
508 assert_eq!(config1, config2);
510
511 let server_config = create_test_server_config();
513 let config3 = config1.clone().with_servers(vec![server_config]);
514
515 assert_ne!(config1, config3);
517 }
518
519 #[test]
520 fn test_mixed_auth_types() {
521 use slim_auth::jwt::{Algorithm, Key, KeyData, KeyFormat};
522
523 let static_jwt =
524 TokenProviderAuthConfig::StaticJwt(StaticJwtConfig::with_file("test-token.jwt"));
525
526 let jwt = TokenVerifierAuthConfig::Jwt(JwtConfig::new(
527 slim_config::auth::jwt::Claims::default(),
528 std::time::Duration::from_secs(3600),
529 slim_config::auth::jwt::JwtKey::Decoding(Key {
530 algorithm: Algorithm::HS256,
531 format: KeyFormat::Pem,
532 key: KeyData::Str("test-key".to_string()),
533 }),
534 ));
535
536 let config = Config::new()
537 .with_token_provider_auth(static_jwt.clone())
538 .with_token_verifier_auth(jwt.clone());
539
540 assert_eq!(config.token_provider, static_jwt);
541 assert_eq!(config.token_verifier, jwt);
542 }
543
544 mod edge_case_tests {
545 use super::*;
546
547 #[test]
548 fn test_config_builder_pattern_reuse() {
549 let base_config = Config::new();
550
551 let config1 = base_config
552 .clone()
553 .with_servers(vec![create_test_server_config()]);
554 let config2 = base_config
555 .clone()
556 .with_clients(vec![create_test_client_config()]);
557
558 assert!(base_config.servers.is_empty());
560 assert!(base_config.clients.is_empty());
561
562 assert_eq!(config1.servers.len(), 1);
564 assert!(config1.clients.is_empty());
565
566 assert!(config2.servers.is_empty());
567 assert_eq!(config2.clients.len(), 1);
568 }
569
570 #[test]
571 fn test_config_overwrite_behavior() {
572 let server1 = create_test_server_config();
573 let server2 = ServerConfig::with_endpoint("127.0.0.1:50052")
574 .with_tls_settings(slim_config::tls::server::TlsServerConfig::insecure());
575
576 let config = Config::new()
577 .with_servers(vec![server1])
578 .with_servers(vec![server2.clone()]); assert_eq!(config.servers.len(), 1);
581 assert_eq!(config.servers[0], server2);
582 }
583
584 #[test]
585 fn test_auth_config_none_variants() {
586 let config = Config::new();
587
588 assert_eq!(config.token_provider, TokenProviderAuthConfig::None);
589 assert_eq!(config.token_verifier, TokenVerifierAuthConfig::None);
590
591 let config_with_provider =
593 config
594 .clone()
595 .with_token_provider_auth(TokenProviderAuthConfig::SharedSecret(
596 "secret".to_string(),
597 ));
598
599 assert_ne!(
600 config_with_provider.token_provider,
601 TokenProviderAuthConfig::None
602 );
603 assert_eq!(
604 config_with_provider.token_verifier,
605 TokenVerifierAuthConfig::None
606 );
607 }
608 }
609
610 #[test]
611 fn test_token_verifier_auth_config_serialize_shared_secret() {
612 let auth = TokenVerifierAuthConfig::SharedSecret("test-secret".to_string());
613 let json = serde_json::to_string(&auth).unwrap();
614 assert!(json.contains("shared_secret"));
615 assert!(json.contains("test-secret"));
616 }
617
618 #[test]
619 fn test_token_verifier_auth_config_deserialize_shared_secret() {
620 let json = r#"{"shared_secret":"test-secret"}"#;
621 let auth: TokenVerifierAuthConfig = serde_json::from_str(json).unwrap();
622
623 match auth {
624 TokenVerifierAuthConfig::SharedSecret(secret) => {
625 assert_eq!(secret, "test-secret");
626 }
627 _ => panic!("Expected SharedSecret variant"),
628 }
629 }
630 }
631}