1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
7
8use crate::handlers::WorkspacesWebService;
9use crate::state::WorkspacesWebState;
10use crate::types::{
11 BrowserSettings, DataProtectionSettings, IdentityProvider, IpAccessSettings, IpRule,
12 NetworkSettings, Portal, Session, SessionLogger, TrustStore, TrustStoreCertificate,
13 UserAccessLoggingSettings, UserSettings,
14};
15
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
18pub struct WorkspacesWebStateView {
19 #[serde(default)]
20 pub portals: HashMap<String, PortalView>,
21 #[serde(default)]
22 pub browser_settings: HashMap<String, BrowserSettingsView>,
23 #[serde(default)]
24 pub network_settings: HashMap<String, NetworkSettingsView>,
25 #[serde(default)]
26 pub user_access_logging_settings: HashMap<String, UserAccessLoggingSettingsView>,
27 #[serde(default)]
28 pub user_settings: HashMap<String, UserSettingsView>,
29 #[serde(default)]
30 pub identity_providers: HashMap<String, IdentityProviderView>,
31 #[serde(default)]
32 pub ip_access_settings: HashMap<String, IpAccessSettingsView>,
33 #[serde(default)]
34 pub trust_stores: HashMap<String, TrustStoreView>,
35 #[serde(default)]
36 pub data_protection_settings: HashMap<String, DataProtectionSettingsView>,
37 #[serde(default)]
38 pub session_loggers: HashMap<String, SessionLoggerView>,
39 #[serde(default)]
40 pub sessions: HashMap<String, SessionView>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct PortalView {
45 pub portal_arn: String,
46 pub portal_endpoint: String,
47 pub display_name: String,
48 pub portal_status: String,
49 pub browser_type: String,
50 pub renderer_type: String,
51 pub creation_date: String,
52 pub browser_settings_arn: Option<String>,
53 pub network_settings_arn: Option<String>,
54 pub user_access_logging_settings_arn: Option<String>,
55 pub user_settings_arn: Option<String>,
56 #[serde(default)]
57 pub trust_store_arn: Option<String>,
58 #[serde(default)]
59 pub ip_access_settings_arn: Option<String>,
60 #[serde(default)]
61 pub data_protection_settings_arn: Option<String>,
62 #[serde(default)]
63 pub session_logger_arn: Option<String>,
64 #[serde(default)]
65 pub tags: HashMap<String, String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct BrowserSettingsView {
70 pub browser_settings_arn: String,
71 pub browser_policy: Option<String>,
72 #[serde(default)]
73 pub associated_portal_arns: Vec<String>,
74 #[serde(default)]
75 pub tags: HashMap<String, String>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct NetworkSettingsView {
80 pub network_settings_arn: String,
81 pub vpc_id: String,
82 #[serde(default)]
83 pub subnet_ids: Vec<String>,
84 #[serde(default)]
85 pub security_group_ids: Vec<String>,
86 #[serde(default)]
87 pub associated_portal_arns: Vec<String>,
88 #[serde(default)]
89 pub tags: HashMap<String, String>,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct UserAccessLoggingSettingsView {
94 pub user_access_logging_settings_arn: String,
95 pub kinesis_stream_arn: String,
96 #[serde(default)]
97 pub associated_portal_arns: Vec<String>,
98 #[serde(default)]
99 pub tags: HashMap<String, String>,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct UserSettingsView {
104 pub user_settings_arn: String,
105 pub copy_allowed: String,
106 pub paste_allowed: String,
107 pub download_allowed: String,
108 pub upload_allowed: String,
109 pub print_allowed: String,
110 pub disconnect_timeout_in_minutes: Option<i32>,
111 pub idle_disconnect_timeout_in_minutes: Option<i32>,
112 #[serde(default)]
113 pub associated_portal_arns: Vec<String>,
114 #[serde(default)]
115 pub tags: HashMap<String, String>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct IdentityProviderView {
120 pub identity_provider_arn: String,
121 pub portal_arn: String,
122 pub identity_provider_name: String,
123 pub identity_provider_type: String,
124 #[serde(default)]
125 pub identity_provider_details: HashMap<String, String>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct IpRuleView {
130 pub ip_range: String,
131 pub description: Option<String>,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct IpAccessSettingsView {
136 pub ip_access_settings_arn: String,
137 pub display_name: Option<String>,
138 pub description: Option<String>,
139 #[serde(default)]
140 pub ip_rules: Vec<IpRuleView>,
141 #[serde(default)]
142 pub associated_portal_arns: Vec<String>,
143 pub creation_date: String,
144 #[serde(default)]
145 pub tags: HashMap<String, String>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct TrustStoreCertificateView {
150 pub thumbprint: String,
151 pub body: String,
152 pub issuer: Option<String>,
153 pub subject: Option<String>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct TrustStoreView {
158 pub trust_store_arn: String,
159 #[serde(default)]
160 pub certificate_list: Vec<TrustStoreCertificateView>,
161 #[serde(default)]
162 pub associated_portal_arns: Vec<String>,
163 #[serde(default)]
164 pub tags: HashMap<String, String>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct DataProtectionSettingsView {
169 pub data_protection_settings_arn: String,
170 pub display_name: Option<String>,
171 pub description: Option<String>,
172 #[serde(default)]
173 pub associated_portal_arns: Vec<String>,
174 pub creation_date: String,
175 #[serde(default)]
176 pub tags: HashMap<String, String>,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct SessionLoggerView {
181 pub session_logger_arn: String,
182 pub display_name: Option<String>,
183 #[serde(default)]
184 pub associated_portal_arns: Vec<String>,
185 pub creation_date: String,
186 #[serde(default)]
187 pub tags: HashMap<String, String>,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct SessionView {
192 pub session_id: String,
193 pub portal_id: String,
194 pub username: Option<String>,
195 pub status: String,
196 pub start_time: String,
197 pub end_time: Option<String>,
198 #[serde(default)]
199 pub client_ip_addresses: Vec<String>,
200}
201
202impl From<&WorkspacesWebState> for WorkspacesWebStateView {
205 fn from(state: &WorkspacesWebState) -> Self {
206 WorkspacesWebStateView {
207 portals: state
208 .portals
209 .iter()
210 .map(|(k, v)| {
211 (
212 k.clone(),
213 PortalView {
214 portal_arn: v.portal_arn.clone(),
215 portal_endpoint: v.portal_endpoint.clone(),
216 display_name: v.display_name.clone(),
217 portal_status: v.portal_status.clone(),
218 browser_type: v.browser_type.clone(),
219 renderer_type: v.renderer_type.clone(),
220 creation_date: v.creation_date.to_rfc3339(),
221 browser_settings_arn: v.browser_settings_arn.clone(),
222 network_settings_arn: v.network_settings_arn.clone(),
223 user_access_logging_settings_arn: v
224 .user_access_logging_settings_arn
225 .clone(),
226 user_settings_arn: v.user_settings_arn.clone(),
227 trust_store_arn: v.trust_store_arn.clone(),
228 ip_access_settings_arn: v.ip_access_settings_arn.clone(),
229 data_protection_settings_arn: v.data_protection_settings_arn.clone(),
230 session_logger_arn: v.session_logger_arn.clone(),
231 tags: v.tags.clone(),
232 },
233 )
234 })
235 .collect(),
236 browser_settings: state
237 .browser_settings
238 .iter()
239 .map(|(k, v)| {
240 (
241 k.clone(),
242 BrowserSettingsView {
243 browser_settings_arn: v.browser_settings_arn.clone(),
244 browser_policy: v.browser_policy.clone(),
245 associated_portal_arns: v.associated_portal_arns.clone(),
246 tags: v.tags.clone(),
247 },
248 )
249 })
250 .collect(),
251 network_settings: state
252 .network_settings
253 .iter()
254 .map(|(k, v)| {
255 (
256 k.clone(),
257 NetworkSettingsView {
258 network_settings_arn: v.network_settings_arn.clone(),
259 vpc_id: v.vpc_id.clone(),
260 subnet_ids: v.subnet_ids.clone(),
261 security_group_ids: v.security_group_ids.clone(),
262 associated_portal_arns: v.associated_portal_arns.clone(),
263 tags: v.tags.clone(),
264 },
265 )
266 })
267 .collect(),
268 user_access_logging_settings: state
269 .user_access_logging_settings
270 .iter()
271 .map(|(k, v)| {
272 (
273 k.clone(),
274 UserAccessLoggingSettingsView {
275 user_access_logging_settings_arn: v
276 .user_access_logging_settings_arn
277 .clone(),
278 kinesis_stream_arn: v.kinesis_stream_arn.clone(),
279 associated_portal_arns: v.associated_portal_arns.clone(),
280 tags: v.tags.clone(),
281 },
282 )
283 })
284 .collect(),
285 user_settings: state
286 .user_settings
287 .iter()
288 .map(|(k, v)| {
289 (
290 k.clone(),
291 UserSettingsView {
292 user_settings_arn: v.user_settings_arn.clone(),
293 copy_allowed: v.copy_allowed.clone(),
294 paste_allowed: v.paste_allowed.clone(),
295 download_allowed: v.download_allowed.clone(),
296 upload_allowed: v.upload_allowed.clone(),
297 print_allowed: v.print_allowed.clone(),
298 disconnect_timeout_in_minutes: v.disconnect_timeout_in_minutes,
299 idle_disconnect_timeout_in_minutes: v
300 .idle_disconnect_timeout_in_minutes,
301 associated_portal_arns: v.associated_portal_arns.clone(),
302 tags: v.tags.clone(),
303 },
304 )
305 })
306 .collect(),
307 identity_providers: state
308 .identity_providers
309 .iter()
310 .map(|(k, v)| {
311 (
312 k.clone(),
313 IdentityProviderView {
314 identity_provider_arn: v.identity_provider_arn.clone(),
315 portal_arn: v.portal_arn.clone(),
316 identity_provider_name: v.identity_provider_name.clone(),
317 identity_provider_type: v.identity_provider_type.clone(),
318 identity_provider_details: v.identity_provider_details.clone(),
319 },
320 )
321 })
322 .collect(),
323 ip_access_settings: state
324 .ip_access_settings
325 .iter()
326 .map(|(k, v)| {
327 (
328 k.clone(),
329 IpAccessSettingsView {
330 ip_access_settings_arn: v.ip_access_settings_arn.clone(),
331 display_name: v.display_name.clone(),
332 description: v.description.clone(),
333 ip_rules: v
334 .ip_rules
335 .iter()
336 .map(|r| IpRuleView {
337 ip_range: r.ip_range.clone(),
338 description: r.description.clone(),
339 })
340 .collect(),
341 associated_portal_arns: v.associated_portal_arns.clone(),
342 creation_date: v.creation_date.to_rfc3339(),
343 tags: v.tags.clone(),
344 },
345 )
346 })
347 .collect(),
348 trust_stores: state
349 .trust_stores
350 .iter()
351 .map(|(k, v)| {
352 (
353 k.clone(),
354 TrustStoreView {
355 trust_store_arn: v.trust_store_arn.clone(),
356 certificate_list: v
357 .certificate_list
358 .iter()
359 .map(|c| TrustStoreCertificateView {
360 thumbprint: c.thumbprint.clone(),
361 body: c.body.clone(),
362 issuer: c.issuer.clone(),
363 subject: c.subject.clone(),
364 })
365 .collect(),
366 associated_portal_arns: v.associated_portal_arns.clone(),
367 tags: v.tags.clone(),
368 },
369 )
370 })
371 .collect(),
372 data_protection_settings: state
373 .data_protection_settings
374 .iter()
375 .map(|(k, v)| {
376 (
377 k.clone(),
378 DataProtectionSettingsView {
379 data_protection_settings_arn: v.data_protection_settings_arn.clone(),
380 display_name: v.display_name.clone(),
381 description: v.description.clone(),
382 associated_portal_arns: v.associated_portal_arns.clone(),
383 creation_date: v.creation_date.to_rfc3339(),
384 tags: v.tags.clone(),
385 },
386 )
387 })
388 .collect(),
389 session_loggers: state
390 .session_loggers
391 .iter()
392 .map(|(k, v)| {
393 (
394 k.clone(),
395 SessionLoggerView {
396 session_logger_arn: v.session_logger_arn.clone(),
397 display_name: v.display_name.clone(),
398 associated_portal_arns: v.associated_portal_arns.clone(),
399 creation_date: v.creation_date.to_rfc3339(),
400 tags: v.tags.clone(),
401 },
402 )
403 })
404 .collect(),
405 sessions: state
406 .sessions
407 .iter()
408 .map(|(k, v)| {
409 (
410 k.clone(),
411 SessionView {
412 session_id: v.session_id.clone(),
413 portal_id: v.portal_id.clone(),
414 username: v.username.clone(),
415 status: v.status.clone(),
416 start_time: v.start_time.to_rfc3339(),
417 end_time: v.end_time.map(|t| t.to_rfc3339()),
418 client_ip_addresses: v.client_ip_addresses.clone(),
419 },
420 )
421 })
422 .collect(),
423 }
424 }
425}
426
427impl From<WorkspacesWebStateView> for WorkspacesWebState {
430 fn from(view: WorkspacesWebStateView) -> Self {
431 use chrono::{DateTime, Utc};
432 WorkspacesWebState {
433 portals: view
434 .portals
435 .into_iter()
436 .map(|(k, v)| {
437 let creation_date = DateTime::parse_from_rfc3339(&v.creation_date)
438 .map(|dt| dt.with_timezone(&Utc))
439 .unwrap_or_else(|_| Utc::now());
440 (
441 k,
442 Portal {
443 portal_arn: v.portal_arn,
444 portal_endpoint: v.portal_endpoint,
445 display_name: v.display_name,
446 portal_status: v.portal_status,
447 browser_type: v.browser_type,
448 renderer_type: v.renderer_type,
449 creation_date,
450 browser_settings_arn: v.browser_settings_arn,
451 network_settings_arn: v.network_settings_arn,
452 user_access_logging_settings_arn: v.user_access_logging_settings_arn,
453 user_settings_arn: v.user_settings_arn,
454 trust_store_arn: v.trust_store_arn,
455 ip_access_settings_arn: v.ip_access_settings_arn,
456 data_protection_settings_arn: v.data_protection_settings_arn,
457 session_logger_arn: v.session_logger_arn,
458 tags: v.tags,
459 },
460 )
461 })
462 .collect(),
463 browser_settings: view
464 .browser_settings
465 .into_iter()
466 .map(|(k, v)| {
467 (
468 k,
469 BrowserSettings {
470 browser_settings_arn: v.browser_settings_arn,
471 browser_policy: v.browser_policy,
472 associated_portal_arns: v.associated_portal_arns,
473 tags: v.tags,
474 },
475 )
476 })
477 .collect(),
478 network_settings: view
479 .network_settings
480 .into_iter()
481 .map(|(k, v)| {
482 (
483 k,
484 NetworkSettings {
485 network_settings_arn: v.network_settings_arn,
486 vpc_id: v.vpc_id,
487 subnet_ids: v.subnet_ids,
488 security_group_ids: v.security_group_ids,
489 associated_portal_arns: v.associated_portal_arns,
490 tags: v.tags,
491 },
492 )
493 })
494 .collect(),
495 user_access_logging_settings: view
496 .user_access_logging_settings
497 .into_iter()
498 .map(|(k, v)| {
499 (
500 k,
501 UserAccessLoggingSettings {
502 user_access_logging_settings_arn: v.user_access_logging_settings_arn,
503 kinesis_stream_arn: v.kinesis_stream_arn,
504 associated_portal_arns: v.associated_portal_arns,
505 tags: v.tags,
506 },
507 )
508 })
509 .collect(),
510 user_settings: view
511 .user_settings
512 .into_iter()
513 .map(|(k, v)| {
514 (
515 k,
516 UserSettings {
517 user_settings_arn: v.user_settings_arn,
518 copy_allowed: v.copy_allowed,
519 paste_allowed: v.paste_allowed,
520 download_allowed: v.download_allowed,
521 upload_allowed: v.upload_allowed,
522 print_allowed: v.print_allowed,
523 disconnect_timeout_in_minutes: v.disconnect_timeout_in_minutes,
524 idle_disconnect_timeout_in_minutes: v
525 .idle_disconnect_timeout_in_minutes,
526 associated_portal_arns: v.associated_portal_arns,
527 tags: v.tags,
528 },
529 )
530 })
531 .collect(),
532 identity_providers: view
533 .identity_providers
534 .into_iter()
535 .map(|(k, v)| {
536 (
537 k,
538 IdentityProvider {
539 identity_provider_arn: v.identity_provider_arn,
540 portal_arn: v.portal_arn,
541 identity_provider_name: v.identity_provider_name,
542 identity_provider_type: v.identity_provider_type,
543 identity_provider_details: v.identity_provider_details,
544 },
545 )
546 })
547 .collect(),
548 ip_access_settings: view
549 .ip_access_settings
550 .into_iter()
551 .map(|(k, v)| {
552 let creation_date = DateTime::parse_from_rfc3339(&v.creation_date)
553 .map(|dt| dt.with_timezone(&Utc))
554 .unwrap_or_else(|_| Utc::now());
555 (
556 k,
557 IpAccessSettings {
558 ip_access_settings_arn: v.ip_access_settings_arn,
559 display_name: v.display_name,
560 description: v.description,
561 ip_rules: v
562 .ip_rules
563 .into_iter()
564 .map(|r| IpRule {
565 ip_range: r.ip_range,
566 description: r.description,
567 })
568 .collect(),
569 associated_portal_arns: v.associated_portal_arns,
570 creation_date,
571 tags: v.tags,
572 },
573 )
574 })
575 .collect(),
576 trust_stores: view
577 .trust_stores
578 .into_iter()
579 .map(|(k, v)| {
580 (
581 k,
582 TrustStore {
583 trust_store_arn: v.trust_store_arn,
584 certificate_list: v
585 .certificate_list
586 .into_iter()
587 .map(|c| TrustStoreCertificate {
588 thumbprint: c.thumbprint,
589 body: c.body,
590 issuer: c.issuer,
591 subject: c.subject,
592 })
593 .collect(),
594 associated_portal_arns: v.associated_portal_arns,
595 tags: v.tags,
596 },
597 )
598 })
599 .collect(),
600 data_protection_settings: view
601 .data_protection_settings
602 .into_iter()
603 .map(|(k, v)| {
604 let creation_date = DateTime::parse_from_rfc3339(&v.creation_date)
605 .map(|dt| dt.with_timezone(&Utc))
606 .unwrap_or_else(|_| Utc::now());
607 (
608 k,
609 DataProtectionSettings {
610 data_protection_settings_arn: v.data_protection_settings_arn,
611 display_name: v.display_name,
612 description: v.description,
613 associated_portal_arns: v.associated_portal_arns,
614 creation_date,
615 tags: v.tags,
616 },
617 )
618 })
619 .collect(),
620 session_loggers: view
621 .session_loggers
622 .into_iter()
623 .map(|(k, v)| {
624 let creation_date = DateTime::parse_from_rfc3339(&v.creation_date)
625 .map(|dt| dt.with_timezone(&Utc))
626 .unwrap_or_else(|_| Utc::now());
627 (
628 k,
629 SessionLogger {
630 session_logger_arn: v.session_logger_arn,
631 display_name: v.display_name,
632 associated_portal_arns: v.associated_portal_arns,
633 creation_date,
634 tags: v.tags,
635 },
636 )
637 })
638 .collect(),
639 sessions: view
640 .sessions
641 .into_iter()
642 .map(|(k, v)| {
643 let start_time = DateTime::parse_from_rfc3339(&v.start_time)
644 .map(|dt| dt.with_timezone(&Utc))
645 .unwrap_or_else(|_| Utc::now());
646 let end_time = v.end_time.as_deref().and_then(|t| {
647 DateTime::parse_from_rfc3339(t)
648 .ok()
649 .map(|dt| dt.with_timezone(&Utc))
650 });
651 (
652 k,
653 Session {
654 session_id: v.session_id,
655 portal_id: v.portal_id,
656 username: v.username,
657 status: v.status,
658 start_time,
659 end_time,
660 client_ip_addresses: v.client_ip_addresses,
661 },
662 )
663 })
664 .collect(),
665 }
666 }
667}
668
669impl StatefulService for WorkspacesWebService {
672 type StateView = WorkspacesWebStateView;
673
674 async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
675 let state = self.state.get(account_id, region);
676 let guard = state.read().await;
677 WorkspacesWebStateView::from(&*guard)
678 }
679
680 async fn restore(
681 &self,
682 account_id: &str,
683 region: &str,
684 view: Self::StateView,
685 ) -> Result<(), StateViewError> {
686 let state = self.state.get(account_id, region);
687 {
688 let mut guard = state.write().await;
689 *guard = WorkspacesWebState::from(view);
690 }
691 self.notify_state_changed(account_id, region).await;
692 Ok(())
693 }
694
695 async fn merge(
696 &self,
697 account_id: &str,
698 region: &str,
699 view: Self::StateView,
700 ) -> Result<(), StateViewError> {
701 let state = self.state.get(account_id, region);
702 {
703 let mut guard = state.write().await;
704 let new_state = WorkspacesWebState::from(view);
705 guard.portals.extend(new_state.portals);
706 guard.browser_settings.extend(new_state.browser_settings);
707 guard.network_settings.extend(new_state.network_settings);
708 guard
709 .user_access_logging_settings
710 .extend(new_state.user_access_logging_settings);
711 guard.user_settings.extend(new_state.user_settings);
712 guard
713 .identity_providers
714 .extend(new_state.identity_providers);
715 guard
716 .ip_access_settings
717 .extend(new_state.ip_access_settings);
718 guard.trust_stores.extend(new_state.trust_stores);
719 guard
720 .data_protection_settings
721 .extend(new_state.data_protection_settings);
722 guard.session_loggers.extend(new_state.session_loggers);
723 guard.sessions.extend(new_state.sessions);
724 }
725 self.notify_state_changed(account_id, region).await;
726 Ok(())
727 }
728
729 fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
730 &self.notifier
731 }
732}