1use std::sync::Arc;
10
11use async_trait::async_trait;
12use http::StatusCode;
13use serde_json::{json, Map, Value};
14use tokio::sync::Mutex as AsyncMutex;
15use uuid::Uuid;
16
17use fakecloud_core::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
18use fakecloud_persistence::SnapshotStore;
19
20use crate::persistence::save_snapshot;
21use crate::state::{SharedTransferState, TransferData};
22
23pub const TRANSFER_ACTIONS: &[&str] = &[
25 "CreateAccess",
26 "CreateAgreement",
27 "CreateConnector",
28 "CreateProfile",
29 "CreateServer",
30 "CreateUser",
31 "CreateWebApp",
32 "CreateWorkflow",
33 "DeleteAccess",
34 "DeleteAgreement",
35 "DeleteCertificate",
36 "DeleteConnector",
37 "DeleteHostKey",
38 "DeleteProfile",
39 "DeleteServer",
40 "DeleteSshPublicKey",
41 "DeleteUser",
42 "DeleteWebApp",
43 "DeleteWebAppCustomization",
44 "DeleteWorkflow",
45 "DescribeAccess",
46 "DescribeAgreement",
47 "DescribeCertificate",
48 "DescribeConnector",
49 "DescribeExecution",
50 "DescribeHostKey",
51 "DescribeProfile",
52 "DescribeSecurityPolicy",
53 "DescribeServer",
54 "DescribeUser",
55 "DescribeWebApp",
56 "DescribeWebAppCustomization",
57 "DescribeWorkflow",
58 "ImportCertificate",
59 "ImportHostKey",
60 "ImportSshPublicKey",
61 "ListAccesses",
62 "ListAgreements",
63 "ListCertificates",
64 "ListConnectors",
65 "ListExecutions",
66 "ListFileTransferResults",
67 "ListHostKeys",
68 "ListProfiles",
69 "ListSecurityPolicies",
70 "ListServers",
71 "ListTagsForResource",
72 "ListUsers",
73 "ListWebApps",
74 "ListWorkflows",
75 "SendWorkflowStepState",
76 "StartDirectoryListing",
77 "StartFileTransfer",
78 "StartRemoteDelete",
79 "StartRemoteMove",
80 "StartServer",
81 "StopServer",
82 "TagResource",
83 "TestConnection",
84 "TestIdentityProvider",
85 "UntagResource",
86 "UpdateAccess",
87 "UpdateAgreement",
88 "UpdateCertificate",
89 "UpdateConnector",
90 "UpdateHostKey",
91 "UpdateProfile",
92 "UpdateServer",
93 "UpdateUser",
94 "UpdateWebApp",
95 "UpdateWebAppCustomization",
96];
97
98pub struct TransferService {
99 state: SharedTransferState,
100 snapshot_store: Option<Arc<dyn SnapshotStore>>,
101 snapshot_lock: Arc<AsyncMutex<()>>,
102}
103
104impl TransferService {
105 pub fn new(state: SharedTransferState) -> Self {
106 Self {
107 state,
108 snapshot_store: None,
109 snapshot_lock: Arc::new(AsyncMutex::new(())),
110 }
111 }
112
113 pub fn with_snapshot_store(mut self, store: Arc<dyn SnapshotStore>) -> Self {
114 self.snapshot_store = Some(store);
115 self
116 }
117
118 async fn save(&self) {
119 save_snapshot(
120 &self.state,
121 self.snapshot_store.clone(),
122 &self.snapshot_lock,
123 )
124 .await;
125 }
126}
127
128#[async_trait]
129impl AwsService for TransferService {
130 fn service_name(&self) -> &str {
131 "transfer"
132 }
133
134 async fn handle(&self, request: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
135 let mutates = is_mutating(request.action.as_str());
136 let result = dispatch(self, &request);
137 if mutates && matches!(result.as_ref(), Ok(resp) if resp.status.is_success()) {
138 self.save().await;
139 }
140 result
141 }
142
143 fn supported_actions(&self) -> &[&str] {
144 TRANSFER_ACTIONS
145 }
146}
147
148fn is_mutating(action: &str) -> bool {
152 !(action.starts_with("Describe") || action.starts_with("List") || action.starts_with("Test"))
153}
154
155fn dispatch(s: &TransferService, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
156 let b = parse(req)?;
157 crate::validate::validate_input(req.action.as_str(), &b)?;
158 let ctx = Ctx {
159 account: req.account_id.clone(),
160 region: req.region.clone(),
161 };
162 match req.action.as_str() {
163 "CreateServer" => s.create_server(&ctx, &b),
165 "DescribeServer" => s.describe_server(&ctx, &b),
166 "UpdateServer" => s.update_server(&ctx, &b),
167 "DeleteServer" => s.delete_server(&ctx, &b),
168 "StartServer" => s.start_server(&ctx, &b),
169 "StopServer" => s.stop_server(&ctx, &b),
170 "ListServers" => s.list_servers(&ctx, &b),
171 "CreateUser" => s.create_user(&ctx, &b),
173 "DescribeUser" => s.describe_user(&ctx, &b),
174 "UpdateUser" => s.update_user(&ctx, &b),
175 "DeleteUser" => s.delete_user(&ctx, &b),
176 "ListUsers" => s.list_users(&ctx, &b),
177 "ImportSshPublicKey" => s.import_ssh_public_key(&ctx, &b),
179 "DeleteSshPublicKey" => s.delete_ssh_public_key(&ctx, &b),
180 "ImportHostKey" => s.import_host_key(&ctx, &b),
182 "UpdateHostKey" => s.update_host_key(&ctx, &b),
183 "DescribeHostKey" => s.describe_host_key(&ctx, &b),
184 "DeleteHostKey" => s.delete_host_key(&ctx, &b),
185 "ListHostKeys" => s.list_host_keys(&ctx, &b),
186 "CreateAccess" => s.create_access(&ctx, &b),
188 "UpdateAccess" => s.update_access(&ctx, &b),
189 "DescribeAccess" => s.describe_access(&ctx, &b),
190 "DeleteAccess" => s.delete_access(&ctx, &b),
191 "ListAccesses" => s.list_accesses(&ctx, &b),
192 "CreateWorkflow" => s.create_workflow(&ctx, &b),
194 "DescribeWorkflow" => s.describe_workflow(&ctx, &b),
195 "DeleteWorkflow" => s.delete_workflow(&ctx, &b),
196 "ListWorkflows" => s.list_workflows(&ctx, &b),
197 "SendWorkflowStepState" => s.send_workflow_step_state(&ctx, &b),
198 "DescribeExecution" => s.describe_execution(&ctx, &b),
200 "ListExecutions" => s.list_executions(&ctx, &b),
201 "CreateAgreement" => s.create_agreement(&ctx, &b),
203 "UpdateAgreement" => s.update_agreement(&ctx, &b),
204 "DescribeAgreement" => s.describe_agreement(&ctx, &b),
205 "DeleteAgreement" => s.delete_agreement(&ctx, &b),
206 "ListAgreements" => s.list_agreements(&ctx, &b),
207 "CreateConnector" => s.create_connector(&ctx, &b),
209 "UpdateConnector" => s.update_connector(&ctx, &b),
210 "DescribeConnector" => s.describe_connector(&ctx, &b),
211 "DeleteConnector" => s.delete_connector(&ctx, &b),
212 "ListConnectors" => s.list_connectors(&ctx, &b),
213 "TestConnection" => s.test_connection(&ctx, &b),
214 "StartDirectoryListing" => s.start_directory_listing(&ctx, &b),
215 "StartFileTransfer" => s.start_file_transfer(&ctx, &b),
216 "StartRemoteDelete" => s.start_remote_delete(&ctx, &b),
217 "StartRemoteMove" => s.start_remote_move(&ctx, &b),
218 "ListFileTransferResults" => s.list_file_transfer_results(&ctx, &b),
219 "CreateProfile" => s.create_profile(&ctx, &b),
221 "UpdateProfile" => s.update_profile(&ctx, &b),
222 "DescribeProfile" => s.describe_profile(&ctx, &b),
223 "DeleteProfile" => s.delete_profile(&ctx, &b),
224 "ListProfiles" => s.list_profiles(&ctx, &b),
225 "ImportCertificate" => s.import_certificate(&ctx, &b),
227 "UpdateCertificate" => s.update_certificate(&ctx, &b),
228 "DescribeCertificate" => s.describe_certificate(&ctx, &b),
229 "DeleteCertificate" => s.delete_certificate(&ctx, &b),
230 "ListCertificates" => s.list_certificates(&ctx, &b),
231 "DescribeSecurityPolicy" => s.describe_security_policy(&b),
233 "ListSecurityPolicies" => s.list_security_policies(&b),
234 "CreateWebApp" => s.create_web_app(&ctx, &b),
236 "UpdateWebApp" => s.update_web_app(&ctx, &b),
237 "DescribeWebApp" => s.describe_web_app(&ctx, &b),
238 "DeleteWebApp" => s.delete_web_app(&ctx, &b),
239 "ListWebApps" => s.list_web_apps(&ctx, &b),
240 "UpdateWebAppCustomization" => s.update_web_app_customization(&ctx, &b),
241 "DescribeWebAppCustomization" => s.describe_web_app_customization(&ctx, &b),
242 "DeleteWebAppCustomization" => s.delete_web_app_customization(&ctx, &b),
243 "TestIdentityProvider" => s.test_identity_provider(&ctx, &b),
245 "TagResource" => s.tag_resource(&ctx, &b),
247 "UntagResource" => s.untag_resource(&ctx, &b),
248 "ListTagsForResource" => s.list_tags_for_resource(&ctx, &b),
249 _ => Err(AwsServiceError::action_not_implemented(
250 s.service_name(),
251 &req.action,
252 )),
253 }
254}
255
256struct Ctx {
260 account: String,
261 region: String,
262}
263
264fn ok(v: Value) -> Result<AwsResponse, AwsServiceError> {
265 Ok(AwsResponse::json_value(StatusCode::OK, v))
266}
267
268fn parse(req: &AwsRequest) -> Result<Value, AwsServiceError> {
269 if req.body.is_empty() {
270 return Ok(json!({}));
271 }
272 serde_json::from_slice(&req.body)
273 .map_err(|e| invalid_request(&format!("Request body is malformed: {e}")))
274}
275
276fn invalid_request(msg: &str) -> AwsServiceError {
277 AwsServiceError::aws_error(StatusCode::BAD_REQUEST, "InvalidRequestException", msg)
278}
279
280fn not_found(msg: &str) -> AwsServiceError {
281 AwsServiceError::aws_error(StatusCode::NOT_FOUND, "ResourceNotFoundException", msg)
282}
283
284fn resource_exists(msg: &str) -> AwsServiceError {
285 AwsServiceError::aws_error(StatusCode::CONFLICT, "ResourceExistsException", msg)
287}
288
289fn req_str<'a>(b: &'a Value, f: &str) -> Result<&'a str, AwsServiceError> {
291 b.get(f)
292 .and_then(Value::as_str)
293 .filter(|s| !s.is_empty())
294 .ok_or_else(|| invalid_request(&format!("{f} is required.")))
295}
296
297fn opt_str<'a>(b: &'a Value, f: &str) -> Option<&'a str> {
298 b.get(f).and_then(Value::as_str).filter(|s| !s.is_empty())
299}
300
301fn now_ts() -> f64 {
302 chrono::Utc::now().timestamp() as f64
303}
304
305fn hex17() -> String {
307 let raw = Uuid::new_v4().simple().to_string();
308 raw.chars()
309 .filter(|c| c.is_ascii_hexdigit())
310 .take(17)
311 .collect()
312}
313
314fn arn(ctx: &Ctx, path: &str) -> String {
315 format!("arn:aws:transfer:{}:{}:{}", ctx.region, ctx.account, path)
316}
317
318fn copy_present(src: &Value, dst: &mut Map<String, Value>, fields: &[&str]) {
320 for f in fields {
321 if let Some(v) = src.get(*f) {
322 dst.insert((*f).to_string(), v.clone());
323 }
324 }
325}
326
327fn store_tags(data: &mut TransferData, arn: &str, b: &Value) {
329 if let Some(tags) = b.get("Tags").and_then(Value::as_array) {
330 let entry = data.tags.entry(arn.to_string()).or_default();
331 for t in tags {
332 if let (Some(k), Some(v)) = (
333 t.get("Key").and_then(Value::as_str),
334 t.get("Value").and_then(Value::as_str),
335 ) {
336 entry.insert(k.to_string(), v.to_string());
337 }
338 }
339 }
340}
341
342fn tags_list(data: &TransferData, arn: &str) -> Option<Value> {
345 data.tags.get(arn).filter(|m| !m.is_empty()).map(|m| {
346 Value::Array(
347 m.iter()
348 .map(|(k, v)| json!({ "Key": k, "Value": v }))
349 .collect(),
350 )
351 })
352}
353
354fn paginate(rows: Vec<Value>, b: &Value) -> (Vec<Value>, Option<String>) {
357 let start = b
358 .get("NextToken")
359 .and_then(Value::as_str)
360 .and_then(|t| t.parse::<usize>().ok())
361 .unwrap_or(0);
362 let max = b
363 .get("MaxResults")
364 .and_then(Value::as_u64)
365 .map(|m| m.clamp(1, 1000) as usize)
366 .unwrap_or(1000);
367 let end = start.saturating_add(max).min(rows.len());
368 let page = rows.get(start..end).unwrap_or(&[]).to_vec();
369 let next = if end < rows.len() {
370 Some(end.to_string())
371 } else {
372 None
373 };
374 (page, next)
375}
376
377fn list_response(key: &str, rows: Vec<Value>, b: &Value, extra: &[(&str, Value)]) -> Value {
380 let (page, next) = paginate(rows, b);
381 let mut out = Map::new();
382 for (k, v) in extra {
383 out.insert((*k).to_string(), v.clone());
384 }
385 out.insert(key.to_string(), Value::Array(page));
386 if let Some(t) = next {
387 out.insert("NextToken".to_string(), json!(t));
388 }
389 Value::Object(out)
390}
391
392const SERVER_FIELDS: &[&str] = &[
396 "Certificate",
397 "ProtocolDetails",
398 "EndpointDetails",
399 "EndpointType",
400 "IdentityProviderDetails",
401 "IdentityProviderType",
402 "LoggingRole",
403 "PostAuthenticationLoginBanner",
404 "PreAuthenticationLoginBanner",
405 "Protocols",
406 "SecurityPolicyName",
407 "WorkflowDetails",
408 "StructuredLogDestinations",
409 "S3StorageOptions",
410 "IpAddressType",
411 "Domain",
412];
413
414const UPDATE_SERVER_FIELDS: &[&str] = &[
419 "Certificate",
420 "ProtocolDetails",
421 "EndpointDetails",
422 "EndpointType",
423 "IdentityProviderDetails",
424 "IdentityProviderType",
425 "LoggingRole",
426 "PostAuthenticationLoginBanner",
427 "PreAuthenticationLoginBanner",
428 "Protocols",
429 "SecurityPolicyName",
430 "WorkflowDetails",
431 "StructuredLogDestinations",
432 "S3StorageOptions",
433 "IpAddressType",
434];
435
436impl TransferService {
437 fn require_server<'a>(
438 &self,
439 data: &'a TransferData,
440 server_id: &str,
441 ) -> Result<&'a Value, AwsServiceError> {
442 data.servers
443 .get(server_id)
444 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))
445 }
446
447 fn create_server(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
448 let server_id = format!("s-{}", hex17());
449 let server_arn = arn(ctx, &format!("server/{server_id}"));
450 let mut srv = Map::new();
451 srv.insert("Arn".into(), json!(server_arn));
452 srv.insert("ServerId".into(), json!(server_id));
453 srv.insert(
454 "Domain".into(),
455 b.get("Domain").cloned().unwrap_or(json!("S3")),
456 );
457 srv.insert(
458 "EndpointType".into(),
459 b.get("EndpointType").cloned().unwrap_or(json!("PUBLIC")),
460 );
461 srv.insert(
462 "IdentityProviderType".into(),
463 b.get("IdentityProviderType")
464 .cloned()
465 .unwrap_or(json!("SERVICE_MANAGED")),
466 );
467 srv.insert(
470 "Protocols".into(),
471 b.get("Protocols").cloned().unwrap_or(json!(["SFTP"])),
472 );
473 srv.insert("State".into(), json!("ONLINE"));
476 srv.insert("UserCount".into(), json!(0));
477 srv.insert(
478 "HostKeyFingerprint".into(),
479 json!(format!("SHA256:{}", hex17())),
480 );
481 copy_present(b, &mut srv, SERVER_FIELDS);
482 let srv = Value::Object(srv);
483 let mut guard = self.state.write();
484 let data = guard.get_or_create(&ctx.account);
485 data.servers.insert(server_id.clone(), srv);
486 store_tags(data, &server_arn, b);
487 ok(json!({ "ServerId": server_id }))
488 }
489
490 fn describe_server(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
491 let server_id = req_str(b, "ServerId")?;
492 let guard = self.state.read();
493 let data = guard
494 .get(&ctx.account)
495 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
496 let srv = self.require_server(data, server_id)?;
497 let mut out = srv.as_object().unwrap().clone();
498 let arn = out
499 .get("Arn")
500 .and_then(Value::as_str)
501 .unwrap_or("")
502 .to_string();
503 if let Some(tags) = tags_list(data, &arn) {
504 out.insert("Tags".into(), tags);
505 }
506 let count = data
508 .users
509 .keys()
510 .filter(|k| k.starts_with(&format!("{server_id}/")))
511 .count();
512 out.insert("UserCount".into(), json!(count));
513 ok(json!({ "Server": Value::Object(out) }))
514 }
515
516 fn update_server(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
517 let server_id = req_str(b, "ServerId")?.to_string();
518 let mut guard = self.state.write();
519 let data = guard.get_or_create(&ctx.account);
520 let srv = data
521 .servers
522 .get_mut(&server_id)
523 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
524 let obj = srv.as_object_mut().unwrap();
525 copy_present(b, obj, UPDATE_SERVER_FIELDS);
526 ok(json!({ "ServerId": server_id }))
527 }
528
529 fn delete_server(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
530 let server_id = req_str(b, "ServerId")?.to_string();
531 let mut guard = self.state.write();
532 let data = guard.get_or_create(&ctx.account);
533 let srv = data
534 .servers
535 .remove(&server_id)
536 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
537 if let Some(arn) = srv.get("Arn").and_then(Value::as_str) {
538 data.tags.remove(arn);
539 }
540 let prefix = format!("{server_id}/");
541 let mut child_arns: Vec<String> = Vec::new();
544 for map in [
545 &data.users,
546 &data.accesses,
547 &data.host_keys,
548 &data.agreements,
549 ] {
550 for (k, v) in map.iter() {
551 if k.starts_with(&prefix) {
552 if let Some(arn) = v.get("Arn").and_then(Value::as_str) {
553 child_arns.push(arn.to_string());
554 }
555 }
556 }
557 }
558 for arn in &child_arns {
559 data.tags.remove(arn);
560 }
561 data.users.retain(|k, _| !k.starts_with(&prefix));
562 data.accesses.retain(|k, _| !k.starts_with(&prefix));
563 data.host_keys.retain(|k, _| !k.starts_with(&prefix));
564 data.agreements.retain(|k, _| !k.starts_with(&prefix));
565 ok(json!({}))
566 }
567
568 fn start_server(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
569 self.set_server_state(ctx, b, "ONLINE")
570 }
571
572 fn stop_server(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
573 self.set_server_state(ctx, b, "OFFLINE")
574 }
575
576 fn set_server_state(
577 &self,
578 ctx: &Ctx,
579 b: &Value,
580 state: &str,
581 ) -> Result<AwsResponse, AwsServiceError> {
582 let server_id = req_str(b, "ServerId")?.to_string();
583 let mut guard = self.state.write();
584 let data = guard.get_or_create(&ctx.account);
585 let srv = data
586 .servers
587 .get_mut(&server_id)
588 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
589 srv.as_object_mut()
592 .unwrap()
593 .insert("State".into(), json!(state));
594 ok(json!({}))
595 }
596
597 fn list_servers(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
598 let guard = self.state.read();
599 let rows = guard
600 .get(&ctx.account)
601 .map(|d| d.servers.values().map(|s| listed_server(s, d)).collect())
602 .unwrap_or_default();
603 ok(list_response("Servers", rows, b, &[]))
604 }
605}
606
607fn listed_server(srv: &Value, data: &TransferData) -> Value {
608 let server_id = srv.get("ServerId").and_then(Value::as_str).unwrap_or("");
609 let count = data
610 .users
611 .keys()
612 .filter(|k| k.starts_with(&format!("{server_id}/")))
613 .count();
614 let mut out = Map::new();
615 for f in [
616 "Arn",
617 "Domain",
618 "IdentityProviderType",
619 "EndpointType",
620 "LoggingRole",
621 "ServerId",
622 "State",
623 ] {
624 if let Some(v) = srv.get(f) {
625 out.insert(f.to_string(), v.clone());
626 }
627 }
628 out.insert("UserCount".into(), json!(count));
629 Value::Object(out)
630}
631
632const USER_FIELDS: &[&str] = &[
635 "HomeDirectory",
636 "HomeDirectoryMappings",
637 "HomeDirectoryType",
638 "Policy",
639 "PosixProfile",
640 "Role",
641];
642
643impl TransferService {
644 fn create_user(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
645 let server_id = req_str(b, "ServerId")?.to_string();
646 let user_name = req_str(b, "UserName")?.to_string();
647 req_str(b, "Role")?;
648 let mut guard = self.state.write();
649 let data = guard.get_or_create(&ctx.account);
650 self.require_server(data, &server_id)?;
651 let key = format!("{server_id}/{user_name}");
652 if data.users.contains_key(&key) {
653 return Err(resource_exists(&format!(
654 "User {user_name} already exists on server {server_id}."
655 )));
656 }
657 let user_arn = arn(ctx, &format!("user/{server_id}/{user_name}"));
658 let mut user = Map::new();
659 user.insert("Arn".into(), json!(user_arn));
660 user.insert("UserName".into(), json!(user_name));
661 copy_present(b, &mut user, USER_FIELDS);
662 let mut ssh_keys = Vec::new();
663 if let Some(body) = opt_str(b, "SshPublicKeyBody") {
664 ssh_keys.push(json!({
665 "SshPublicKeyId": format!("key-{}", hex17()),
666 "SshPublicKeyBody": body,
667 "DateImported": now_ts()
668 }));
669 }
670 user.insert("SshPublicKeys".into(), json!(ssh_keys));
671 data.users.insert(key, Value::Object(user));
672 store_tags(data, &user_arn, b);
673 ok(json!({ "ServerId": server_id, "UserName": user_name }))
674 }
675
676 fn describe_user(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
677 let server_id = req_str(b, "ServerId")?.to_string();
678 let user_name = req_str(b, "UserName")?.to_string();
679 let guard = self.state.read();
680 let data = guard
681 .get(&ctx.account)
682 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
683 self.require_server(data, &server_id)?;
684 let user = data
685 .users
686 .get(&format!("{server_id}/{user_name}"))
687 .ok_or_else(|| not_found(&format!("User {user_name} does not exist.")))?;
688 let mut out = user.as_object().unwrap().clone();
689 let arn = out
690 .get("Arn")
691 .and_then(Value::as_str)
692 .unwrap_or("")
693 .to_string();
694 if let Some(tags) = tags_list(data, &arn) {
695 out.insert("Tags".into(), tags);
696 }
697 ok(json!({ "ServerId": server_id, "User": Value::Object(out) }))
698 }
699
700 fn update_user(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
701 let server_id = req_str(b, "ServerId")?.to_string();
702 let user_name = req_str(b, "UserName")?.to_string();
703 let mut guard = self.state.write();
704 let data = guard.get_or_create(&ctx.account);
705 self.require_server(data, &server_id)?;
706 let user = data
707 .users
708 .get_mut(&format!("{server_id}/{user_name}"))
709 .ok_or_else(|| not_found(&format!("User {user_name} does not exist.")))?;
710 copy_present(b, user.as_object_mut().unwrap(), USER_FIELDS);
711 ok(json!({ "ServerId": server_id, "UserName": user_name }))
712 }
713
714 fn delete_user(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
715 let server_id = req_str(b, "ServerId")?.to_string();
716 let user_name = req_str(b, "UserName")?.to_string();
717 let mut guard = self.state.write();
718 let data = guard.get_or_create(&ctx.account);
719 self.require_server(data, &server_id)?;
720 let user = data
721 .users
722 .remove(&format!("{server_id}/{user_name}"))
723 .ok_or_else(|| not_found(&format!("User {user_name} does not exist.")))?;
724 if let Some(arn) = user.get("Arn").and_then(Value::as_str) {
725 data.tags.remove(arn);
726 }
727 ok(json!({}))
728 }
729
730 fn list_users(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
731 let server_id = req_str(b, "ServerId")?.to_string();
732 let guard = self.state.read();
733 let data = guard
734 .get(&ctx.account)
735 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
736 self.require_server(data, &server_id)?;
737 let prefix = format!("{server_id}/");
738 let rows: Vec<Value> = data
739 .users
740 .iter()
741 .filter(|(k, _)| k.starts_with(&prefix))
742 .map(|(_, u)| listed_user(u))
743 .collect();
744 ok(list_response(
745 "Users",
746 rows,
747 b,
748 &[("ServerId", json!(server_id))],
749 ))
750 }
751
752 fn import_ssh_public_key(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
753 let server_id = req_str(b, "ServerId")?.to_string();
754 let user_name = req_str(b, "UserName")?.to_string();
755 let body = req_str(b, "SshPublicKeyBody")?.to_string();
756 let mut guard = self.state.write();
757 let data = guard.get_or_create(&ctx.account);
758 self.require_server(data, &server_id)?;
759 let user = data
760 .users
761 .get_mut(&format!("{server_id}/{user_name}"))
762 .ok_or_else(|| not_found(&format!("User {user_name} does not exist.")))?;
763 let key_id = format!("key-{}", hex17());
764 let keys = user
765 .as_object_mut()
766 .unwrap()
767 .entry("SshPublicKeys")
768 .or_insert_with(|| json!([]));
769 if let Some(arr) = keys.as_array_mut() {
770 if arr
771 .iter()
772 .any(|k| k.get("SshPublicKeyBody").and_then(Value::as_str) == Some(body.as_str()))
773 {
774 return Err(resource_exists("The SSH public key already exists."));
775 }
776 arr.push(json!({
777 "SshPublicKeyId": key_id,
778 "SshPublicKeyBody": body,
779 "DateImported": now_ts()
780 }));
781 }
782 ok(json!({
783 "ServerId": server_id,
784 "SshPublicKeyId": key_id,
785 "UserName": user_name
786 }))
787 }
788
789 fn delete_ssh_public_key(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
790 let server_id = req_str(b, "ServerId")?.to_string();
791 let user_name = req_str(b, "UserName")?.to_string();
792 let key_id = req_str(b, "SshPublicKeyId")?.to_string();
793 let mut guard = self.state.write();
794 let data = guard.get_or_create(&ctx.account);
795 self.require_server(data, &server_id)?;
796 let user = data
797 .users
798 .get_mut(&format!("{server_id}/{user_name}"))
799 .ok_or_else(|| not_found(&format!("User {user_name} does not exist.")))?;
800 let keys = user
801 .as_object_mut()
802 .unwrap()
803 .get_mut("SshPublicKeys")
804 .and_then(Value::as_array_mut);
805 let removed = match keys {
806 Some(arr) => {
807 let before = arr.len();
808 arr.retain(|k| {
809 k.get("SshPublicKeyId").and_then(Value::as_str) != Some(key_id.as_str())
810 });
811 arr.len() != before
812 }
813 None => false,
814 };
815 if !removed {
816 return Err(not_found(&format!(
817 "SSH public key {key_id} does not exist."
818 )));
819 }
820 ok(json!({}))
821 }
822}
823
824fn listed_user(user: &Value) -> Value {
825 let mut out = Map::new();
826 for f in [
827 "Arn",
828 "HomeDirectory",
829 "HomeDirectoryType",
830 "Role",
831 "UserName",
832 ] {
833 if let Some(v) = user.get(f) {
834 out.insert(f.to_string(), v.clone());
835 }
836 }
837 let count = user
838 .get("SshPublicKeys")
839 .and_then(Value::as_array)
840 .map(|a| a.len())
841 .unwrap_or(0);
842 out.insert("SshPublicKeyCount".into(), json!(count));
843 Value::Object(out)
844}
845
846fn host_key_type(body: &str) -> &'static str {
852 for token in [
856 "ssh-ed25519",
857 "ecdsa-sha2-nistp256",
858 "ecdsa-sha2-nistp384",
859 "ecdsa-sha2-nistp521",
860 ] {
861 if body.contains(token) {
862 return token;
863 }
864 }
865 "ssh-rsa"
866}
867
868impl TransferService {
869 fn import_host_key(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
870 let server_id = req_str(b, "ServerId")?.to_string();
871 let host_key_body = req_str(b, "HostKeyBody")?.to_string();
872 let mut guard = self.state.write();
873 let data = guard.get_or_create(&ctx.account);
874 self.require_server(data, &server_id)?;
875 let host_key_id = format!("hostkey-{}", hex17());
876 let key = format!("{server_id}/{host_key_id}");
877 let hk_arn = arn(ctx, &format!("host-key/{server_id}/{host_key_id}"));
878 let mut hk = Map::new();
879 hk.insert("Arn".into(), json!(hk_arn));
880 hk.insert("HostKeyId".into(), json!(host_key_id));
881 hk.insert(
882 "HostKeyFingerprint".into(),
883 json!(format!("SHA256:{}", hex17())),
884 );
885 hk.insert("Type".into(), json!(host_key_type(&host_key_body)));
886 hk.insert("DateImported".into(), json!(now_ts()));
887 copy_present(b, &mut hk, &["Description"]);
888 data.host_keys.insert(key, Value::Object(hk));
889 store_tags(data, &hk_arn, b);
890 ok(json!({ "ServerId": server_id, "HostKeyId": host_key_id }))
891 }
892
893 fn update_host_key(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
894 let server_id = req_str(b, "ServerId")?.to_string();
895 let host_key_id = req_str(b, "HostKeyId")?.to_string();
896 let description = req_str(b, "Description")?.to_string();
897 let mut guard = self.state.write();
898 let data = guard.get_or_create(&ctx.account);
899 let hk = data
900 .host_keys
901 .get_mut(&format!("{server_id}/{host_key_id}"))
902 .ok_or_else(|| not_found(&format!("Host key {host_key_id} does not exist.")))?;
903 hk.as_object_mut()
904 .unwrap()
905 .insert("Description".into(), json!(description));
906 ok(json!({ "ServerId": server_id, "HostKeyId": host_key_id }))
907 }
908
909 fn describe_host_key(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
910 let server_id = req_str(b, "ServerId")?.to_string();
911 let host_key_id = req_str(b, "HostKeyId")?.to_string();
912 let guard = self.state.read();
913 let data = guard
914 .get(&ctx.account)
915 .ok_or_else(|| not_found(&format!("Host key {host_key_id} does not exist.")))?;
916 let hk = data
917 .host_keys
918 .get(&format!("{server_id}/{host_key_id}"))
919 .ok_or_else(|| not_found(&format!("Host key {host_key_id} does not exist.")))?;
920 let mut out = hk.as_object().unwrap().clone();
921 let arn = out
922 .get("Arn")
923 .and_then(Value::as_str)
924 .unwrap_or("")
925 .to_string();
926 if let Some(tags) = tags_list(data, &arn) {
927 out.insert("Tags".into(), tags);
928 }
929 ok(json!({ "HostKey": Value::Object(out) }))
930 }
931
932 fn delete_host_key(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
933 let server_id = req_str(b, "ServerId")?.to_string();
934 let host_key_id = req_str(b, "HostKeyId")?.to_string();
935 let mut guard = self.state.write();
936 let data = guard.get_or_create(&ctx.account);
937 let hk = data
938 .host_keys
939 .remove(&format!("{server_id}/{host_key_id}"))
940 .ok_or_else(|| not_found(&format!("Host key {host_key_id} does not exist.")))?;
941 if let Some(arn) = hk.get("Arn").and_then(Value::as_str) {
942 data.tags.remove(arn);
943 }
944 ok(json!({}))
945 }
946
947 fn list_host_keys(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
948 let server_id = req_str(b, "ServerId")?.to_string();
949 let guard = self.state.read();
950 let data = guard
951 .get(&ctx.account)
952 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
953 self.require_server(data, &server_id)?;
954 let prefix = format!("{server_id}/");
955 let rows: Vec<Value> = data
956 .host_keys
957 .iter()
958 .filter(|(k, _)| k.starts_with(&prefix))
959 .map(|(_, hk)| listed_host_key(hk))
960 .collect();
961 ok(list_response(
962 "HostKeys",
963 rows,
964 b,
965 &[("ServerId", json!(server_id))],
966 ))
967 }
968}
969
970fn listed_host_key(hk: &Value) -> Value {
971 let mut out = Map::new();
972 for (src, dst) in [
973 ("Arn", "Arn"),
974 ("HostKeyId", "HostKeyId"),
975 ("HostKeyFingerprint", "Fingerprint"),
976 ("Description", "Description"),
977 ("Type", "Type"),
978 ("DateImported", "DateImported"),
979 ] {
980 if let Some(v) = hk.get(src) {
981 out.insert(dst.to_string(), v.clone());
982 }
983 }
984 Value::Object(out)
985}
986
987const ACCESS_FIELDS: &[&str] = &[
990 "HomeDirectory",
991 "HomeDirectoryMappings",
992 "HomeDirectoryType",
993 "Policy",
994 "PosixProfile",
995 "Role",
996];
997
998impl TransferService {
999 fn create_access(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1000 let server_id = req_str(b, "ServerId")?.to_string();
1001 let external_id = req_str(b, "ExternalId")?.to_string();
1002 req_str(b, "Role")?;
1003 let mut guard = self.state.write();
1004 let data = guard.get_or_create(&ctx.account);
1005 self.require_server(data, &server_id)?;
1006 let key = format!("{server_id}/{external_id}");
1007 if data.accesses.contains_key(&key) {
1008 return Err(resource_exists(&format!(
1009 "Access for {external_id} already exists."
1010 )));
1011 }
1012 let mut acc = Map::new();
1013 acc.insert("ExternalId".into(), json!(external_id));
1014 copy_present(b, &mut acc, ACCESS_FIELDS);
1015 data.accesses.insert(key, Value::Object(acc));
1016 ok(json!({ "ServerId": server_id, "ExternalId": external_id }))
1017 }
1018
1019 fn update_access(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1020 let server_id = req_str(b, "ServerId")?.to_string();
1021 let external_id = req_str(b, "ExternalId")?.to_string();
1022 let mut guard = self.state.write();
1023 let data = guard.get_or_create(&ctx.account);
1024 self.require_server(data, &server_id)?;
1025 let acc = data
1026 .accesses
1027 .get_mut(&format!("{server_id}/{external_id}"))
1028 .ok_or_else(|| not_found(&format!("Access {external_id} does not exist.")))?;
1029 copy_present(b, acc.as_object_mut().unwrap(), ACCESS_FIELDS);
1030 ok(json!({ "ServerId": server_id, "ExternalId": external_id }))
1031 }
1032
1033 fn describe_access(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1034 let server_id = req_str(b, "ServerId")?.to_string();
1035 let external_id = req_str(b, "ExternalId")?.to_string();
1036 let guard = self.state.read();
1037 let data = guard
1038 .get(&ctx.account)
1039 .ok_or_else(|| not_found(&format!("Access {external_id} does not exist.")))?;
1040 let acc = data
1041 .accesses
1042 .get(&format!("{server_id}/{external_id}"))
1043 .ok_or_else(|| not_found(&format!("Access {external_id} does not exist.")))?;
1044 ok(json!({ "ServerId": server_id, "Access": acc }))
1045 }
1046
1047 fn delete_access(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1048 let server_id = req_str(b, "ServerId")?.to_string();
1049 let external_id = req_str(b, "ExternalId")?.to_string();
1050 let mut guard = self.state.write();
1051 let data = guard.get_or_create(&ctx.account);
1052 data.accesses
1053 .remove(&format!("{server_id}/{external_id}"))
1054 .ok_or_else(|| not_found(&format!("Access {external_id} does not exist.")))?;
1055 ok(json!({}))
1056 }
1057
1058 fn list_accesses(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1059 let server_id = req_str(b, "ServerId")?.to_string();
1060 let guard = self.state.read();
1061 let data = guard
1062 .get(&ctx.account)
1063 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
1064 self.require_server(data, &server_id)?;
1065 let prefix = format!("{server_id}/");
1066 let rows: Vec<Value> = data
1067 .accesses
1068 .iter()
1069 .filter(|(k, _)| k.starts_with(&prefix))
1070 .map(|(_, a)| {
1071 let mut out = Map::new();
1072 for f in ["HomeDirectory", "HomeDirectoryType", "Role", "ExternalId"] {
1073 if let Some(v) = a.get(f) {
1074 out.insert(f.to_string(), v.clone());
1075 }
1076 }
1077 Value::Object(out)
1078 })
1079 .collect();
1080 ok(list_response(
1081 "Accesses",
1082 rows,
1083 b,
1084 &[("ServerId", json!(server_id))],
1085 ))
1086 }
1087}
1088
1089impl TransferService {
1092 fn create_workflow(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1093 if b.get("Steps").and_then(Value::as_array).is_none() {
1094 return Err(invalid_request("Steps is required."));
1095 }
1096 let workflow_id = format!("w-{}", hex17());
1097 let wf_arn = arn(ctx, &format!("workflow/{workflow_id}"));
1098 let mut wf = Map::new();
1099 wf.insert("Arn".into(), json!(wf_arn));
1100 wf.insert("WorkflowId".into(), json!(workflow_id));
1101 copy_present(b, &mut wf, &["Description", "Steps", "OnExceptionSteps"]);
1102 let mut guard = self.state.write();
1103 let data = guard.get_or_create(&ctx.account);
1104 data.workflows
1105 .insert(workflow_id.clone(), Value::Object(wf));
1106 store_tags(data, &wf_arn, b);
1107 ok(json!({ "WorkflowId": workflow_id }))
1108 }
1109
1110 fn describe_workflow(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1111 let workflow_id = req_str(b, "WorkflowId")?.to_string();
1112 let guard = self.state.read();
1113 let data = guard
1114 .get(&ctx.account)
1115 .ok_or_else(|| not_found(&format!("Workflow {workflow_id} does not exist.")))?;
1116 let wf = data
1117 .workflows
1118 .get(&workflow_id)
1119 .ok_or_else(|| not_found(&format!("Workflow {workflow_id} does not exist.")))?;
1120 let mut out = wf.as_object().unwrap().clone();
1121 let arn = out
1122 .get("Arn")
1123 .and_then(Value::as_str)
1124 .unwrap_or("")
1125 .to_string();
1126 if let Some(tags) = tags_list(data, &arn) {
1127 out.insert("Tags".into(), tags);
1128 }
1129 ok(json!({ "Workflow": Value::Object(out) }))
1130 }
1131
1132 fn delete_workflow(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1133 let workflow_id = req_str(b, "WorkflowId")?.to_string();
1134 let mut guard = self.state.write();
1135 let data = guard.get_or_create(&ctx.account);
1136 let wf = data
1137 .workflows
1138 .remove(&workflow_id)
1139 .ok_or_else(|| not_found(&format!("Workflow {workflow_id} does not exist.")))?;
1140 if let Some(arn) = wf.get("Arn").and_then(Value::as_str) {
1141 data.tags.remove(arn);
1142 }
1143 let prefix = format!("{workflow_id}/");
1144 data.executions.retain(|k, _| !k.starts_with(&prefix));
1145 ok(json!({}))
1146 }
1147
1148 fn list_workflows(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1149 let guard = self.state.read();
1150 let rows: Vec<Value> = guard
1151 .get(&ctx.account)
1152 .map(|d| {
1153 d.workflows
1154 .values()
1155 .map(|w| {
1156 let mut out = Map::new();
1157 for f in ["WorkflowId", "Description", "Arn"] {
1158 if let Some(v) = w.get(f) {
1159 out.insert(f.to_string(), v.clone());
1160 }
1161 }
1162 Value::Object(out)
1163 })
1164 .collect()
1165 })
1166 .unwrap_or_default();
1167 ok(list_response("Workflows", rows, b, &[]))
1168 }
1169
1170 fn send_workflow_step_state(
1171 &self,
1172 ctx: &Ctx,
1173 b: &Value,
1174 ) -> Result<AwsResponse, AwsServiceError> {
1175 let workflow_id = req_str(b, "WorkflowId")?.to_string();
1176 let execution_id = req_str(b, "ExecutionId")?.to_string();
1177 req_str(b, "Token")?;
1178 req_str(b, "Status")?;
1179 let guard = self.state.read();
1180 let data = guard
1181 .get(&ctx.account)
1182 .ok_or_else(|| not_found(&format!("Execution {execution_id} does not exist.")))?;
1183 if !data
1184 .executions
1185 .contains_key(&format!("{workflow_id}/{execution_id}"))
1186 {
1187 return Err(not_found(&format!(
1188 "Execution {execution_id} does not exist."
1189 )));
1190 }
1191 ok(json!({}))
1192 }
1193
1194 fn describe_execution(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1195 let workflow_id = req_str(b, "WorkflowId")?.to_string();
1196 let execution_id = req_str(b, "ExecutionId")?.to_string();
1197 let guard = self.state.read();
1198 let data = guard
1199 .get(&ctx.account)
1200 .ok_or_else(|| not_found(&format!("Execution {execution_id} does not exist.")))?;
1201 let exec = data
1202 .executions
1203 .get(&format!("{workflow_id}/{execution_id}"))
1204 .ok_or_else(|| not_found(&format!("Execution {execution_id} does not exist.")))?;
1205 ok(json!({ "WorkflowId": workflow_id, "Execution": exec }))
1206 }
1207
1208 fn list_executions(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1209 let workflow_id = req_str(b, "WorkflowId")?.to_string();
1210 let guard = self.state.read();
1211 let data = guard
1212 .get(&ctx.account)
1213 .ok_or_else(|| not_found(&format!("Workflow {workflow_id} does not exist.")))?;
1214 if !data.workflows.contains_key(&workflow_id) {
1215 return Err(not_found(&format!(
1216 "Workflow {workflow_id} does not exist."
1217 )));
1218 }
1219 let prefix = format!("{workflow_id}/");
1220 let rows: Vec<Value> = data
1221 .executions
1222 .iter()
1223 .filter(|(k, _)| k.starts_with(&prefix))
1224 .map(|(_, e)| {
1225 let mut out = Map::new();
1226 for f in [
1227 "ExecutionId",
1228 "InitialFileLocation",
1229 "ServiceMetadata",
1230 "Status",
1231 ] {
1232 if let Some(v) = e.get(f) {
1233 out.insert(f.to_string(), v.clone());
1234 }
1235 }
1236 Value::Object(out)
1237 })
1238 .collect();
1239 ok(list_response(
1240 "Executions",
1241 rows,
1242 b,
1243 &[("WorkflowId", json!(workflow_id))],
1244 ))
1245 }
1246}
1247
1248const AGREEMENT_FIELDS: &[&str] = &[
1251 "Description",
1252 "Status",
1253 "LocalProfileId",
1254 "PartnerProfileId",
1255 "BaseDirectory",
1256 "AccessRole",
1257 "PreserveFilename",
1258 "EnforceMessageSigning",
1259 "CustomDirectories",
1260];
1261
1262impl TransferService {
1263 fn create_agreement(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1264 let server_id = req_str(b, "ServerId")?.to_string();
1265 req_str(b, "LocalProfileId")?;
1266 req_str(b, "PartnerProfileId")?;
1267 req_str(b, "AccessRole")?;
1268 let mut guard = self.state.write();
1269 let data = guard.get_or_create(&ctx.account);
1270 self.require_server(data, &server_id)?;
1271 let agreement_id = format!("a-{}", hex17());
1272 let ag_arn = arn(ctx, &format!("agreement/{agreement_id}"));
1273 let mut ag = Map::new();
1274 ag.insert("Arn".into(), json!(ag_arn));
1275 ag.insert("AgreementId".into(), json!(agreement_id));
1276 ag.insert("ServerId".into(), json!(server_id));
1277 ag.insert(
1278 "Status".into(),
1279 b.get("Status").cloned().unwrap_or(json!("ACTIVE")),
1280 );
1281 copy_present(b, &mut ag, AGREEMENT_FIELDS);
1282 data.agreements
1283 .insert(format!("{server_id}/{agreement_id}"), Value::Object(ag));
1284 store_tags(data, &ag_arn, b);
1285 ok(json!({ "AgreementId": agreement_id }))
1286 }
1287
1288 fn update_agreement(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1289 let agreement_id = req_str(b, "AgreementId")?.to_string();
1290 let server_id = req_str(b, "ServerId")?.to_string();
1291 let mut guard = self.state.write();
1292 let data = guard.get_or_create(&ctx.account);
1293 let ag = data
1294 .agreements
1295 .get_mut(&format!("{server_id}/{agreement_id}"))
1296 .ok_or_else(|| not_found(&format!("Agreement {agreement_id} does not exist.")))?;
1297 copy_present(b, ag.as_object_mut().unwrap(), AGREEMENT_FIELDS);
1298 ok(json!({ "AgreementId": agreement_id }))
1299 }
1300
1301 fn describe_agreement(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1302 let agreement_id = req_str(b, "AgreementId")?.to_string();
1303 let server_id = req_str(b, "ServerId")?.to_string();
1304 let guard = self.state.read();
1305 let data = guard
1306 .get(&ctx.account)
1307 .ok_or_else(|| not_found(&format!("Agreement {agreement_id} does not exist.")))?;
1308 let ag = data
1309 .agreements
1310 .get(&format!("{server_id}/{agreement_id}"))
1311 .ok_or_else(|| not_found(&format!("Agreement {agreement_id} does not exist.")))?;
1312 let mut out = ag.as_object().unwrap().clone();
1313 let arn = out
1314 .get("Arn")
1315 .and_then(Value::as_str)
1316 .unwrap_or("")
1317 .to_string();
1318 if let Some(tags) = tags_list(data, &arn) {
1319 out.insert("Tags".into(), tags);
1320 }
1321 ok(json!({ "Agreement": Value::Object(out) }))
1322 }
1323
1324 fn delete_agreement(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1325 let agreement_id = req_str(b, "AgreementId")?.to_string();
1326 let server_id = req_str(b, "ServerId")?.to_string();
1327 let mut guard = self.state.write();
1328 let data = guard.get_or_create(&ctx.account);
1329 let ag = data
1330 .agreements
1331 .remove(&format!("{server_id}/{agreement_id}"))
1332 .ok_or_else(|| not_found(&format!("Agreement {agreement_id} does not exist.")))?;
1333 if let Some(arn) = ag.get("Arn").and_then(Value::as_str) {
1334 data.tags.remove(arn);
1335 }
1336 ok(json!({}))
1337 }
1338
1339 fn list_agreements(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1340 let server_id = req_str(b, "ServerId")?.to_string();
1341 let guard = self.state.read();
1342 let data = guard
1343 .get(&ctx.account)
1344 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
1345 self.require_server(data, &server_id)?;
1346 let prefix = format!("{server_id}/");
1347 let rows: Vec<Value> = data
1348 .agreements
1349 .iter()
1350 .filter(|(k, _)| k.starts_with(&prefix))
1351 .map(|(_, a)| {
1352 let mut out = Map::new();
1353 for f in [
1354 "Arn",
1355 "AgreementId",
1356 "Description",
1357 "Status",
1358 "ServerId",
1359 "LocalProfileId",
1360 "PartnerProfileId",
1361 ] {
1362 if let Some(v) = a.get(f) {
1363 out.insert(f.to_string(), v.clone());
1364 }
1365 }
1366 Value::Object(out)
1367 })
1368 .collect();
1369 ok(list_response("Agreements", rows, b, &[]))
1370 }
1371}
1372
1373const CONNECTOR_FIELDS: &[&str] = &[
1376 "Url",
1377 "As2Config",
1378 "AccessRole",
1379 "LoggingRole",
1380 "SftpConfig",
1381 "SecurityPolicyName",
1382 "IpAddressType",
1383];
1384
1385impl TransferService {
1386 fn require_connector<'a>(
1387 &self,
1388 data: &'a TransferData,
1389 connector_id: &str,
1390 ) -> Result<&'a Value, AwsServiceError> {
1391 data.connectors
1392 .get(connector_id)
1393 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))
1394 }
1395
1396 fn create_connector(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1397 req_str(b, "AccessRole")?;
1398 let connector_id = format!("c-{}", hex17());
1399 let c_arn = arn(ctx, &format!("connector/{connector_id}"));
1400 let mut c = Map::new();
1401 c.insert("Arn".into(), json!(c_arn));
1402 c.insert("ConnectorId".into(), json!(connector_id));
1403 c.insert("EgressType".into(), json!("SERVICE_MANAGED"));
1404 c.insert("Status".into(), json!("ACTIVE"));
1405 c.insert(
1406 "ServiceManagedEgressIpAddresses".into(),
1407 json!(["3.219.0.10", "52.4.0.20"]),
1408 );
1409 copy_present(b, &mut c, CONNECTOR_FIELDS);
1410 let mut guard = self.state.write();
1411 let data = guard.get_or_create(&ctx.account);
1412 data.connectors
1413 .insert(connector_id.clone(), Value::Object(c));
1414 store_tags(data, &c_arn, b);
1415 ok(json!({ "ConnectorId": connector_id }))
1416 }
1417
1418 fn update_connector(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1419 let connector_id = req_str(b, "ConnectorId")?.to_string();
1420 let mut guard = self.state.write();
1421 let data = guard.get_or_create(&ctx.account);
1422 let c = data
1423 .connectors
1424 .get_mut(&connector_id)
1425 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1426 copy_present(b, c.as_object_mut().unwrap(), CONNECTOR_FIELDS);
1427 ok(json!({ "ConnectorId": connector_id }))
1428 }
1429
1430 fn describe_connector(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1431 let connector_id = req_str(b, "ConnectorId")?.to_string();
1432 let guard = self.state.read();
1433 let data = guard
1434 .get(&ctx.account)
1435 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1436 let c = self.require_connector(data, &connector_id)?;
1437 let mut out = c.as_object().unwrap().clone();
1438 let arn = out
1439 .get("Arn")
1440 .and_then(Value::as_str)
1441 .unwrap_or("")
1442 .to_string();
1443 if let Some(tags) = tags_list(data, &arn) {
1444 out.insert("Tags".into(), tags);
1445 }
1446 ok(json!({ "Connector": Value::Object(out) }))
1447 }
1448
1449 fn delete_connector(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1450 let connector_id = req_str(b, "ConnectorId")?.to_string();
1451 let mut guard = self.state.write();
1452 let data = guard.get_or_create(&ctx.account);
1453 let c = data
1454 .connectors
1455 .remove(&connector_id)
1456 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1457 if let Some(arn) = c.get("Arn").and_then(Value::as_str) {
1458 data.tags.remove(arn);
1459 }
1460 let prefix = format!("{connector_id}/");
1461 data.file_transfers.retain(|k, _| !k.starts_with(&prefix));
1462 ok(json!({}))
1463 }
1464
1465 fn list_connectors(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1466 let guard = self.state.read();
1467 let rows: Vec<Value> = guard
1468 .get(&ctx.account)
1469 .map(|d| {
1470 d.connectors
1471 .values()
1472 .map(|c| {
1473 let mut out = Map::new();
1474 for f in ["Arn", "ConnectorId", "Url"] {
1475 if let Some(v) = c.get(f) {
1476 out.insert(f.to_string(), v.clone());
1477 }
1478 }
1479 Value::Object(out)
1480 })
1481 .collect()
1482 })
1483 .unwrap_or_default();
1484 ok(list_response("Connectors", rows, b, &[]))
1485 }
1486
1487 fn test_connection(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1488 let connector_id = req_str(b, "ConnectorId")?.to_string();
1489 let guard = self.state.read();
1490 let data = guard
1491 .get(&ctx.account)
1492 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1493 self.require_connector(data, &connector_id)?;
1494 ok(json!({
1495 "ConnectorId": connector_id,
1496 "Status": "OK",
1497 "StatusMessage": "Connection succeeded"
1498 }))
1499 }
1500
1501 fn start_directory_listing(
1502 &self,
1503 ctx: &Ctx,
1504 b: &Value,
1505 ) -> Result<AwsResponse, AwsServiceError> {
1506 let connector_id = req_str(b, "ConnectorId")?.to_string();
1507 req_str(b, "RemoteDirectoryPath")?;
1508 req_str(b, "OutputDirectoryPath")?;
1509 let guard = self.state.read();
1510 let data = guard
1511 .get(&ctx.account)
1512 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1513 self.require_connector(data, &connector_id)?;
1514 let listing_id = Uuid::new_v4().to_string();
1515 ok(json!({
1516 "ListingId": listing_id,
1517 "OutputFileName": format!("{connector_id}-{listing_id}.json")
1518 }))
1519 }
1520
1521 fn start_file_transfer(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1522 let connector_id = req_str(b, "ConnectorId")?.to_string();
1523 let mut guard = self.state.write();
1524 let data = guard.get_or_create(&ctx.account);
1525 self.require_connector(data, &connector_id)?;
1526 let transfer_id = Uuid::new_v4().to_string();
1527 let mut results = Vec::new();
1530 for field in ["SendFilePaths", "RetrieveFilePaths"] {
1531 if let Some(paths) = b.get(field).and_then(Value::as_array) {
1532 for p in paths.iter().filter_map(Value::as_str) {
1533 results.push(json!({ "FilePath": p, "StatusCode": "COMPLETED" }));
1534 }
1535 }
1536 }
1537 data.file_transfers.insert(
1538 format!("{connector_id}/{transfer_id}"),
1539 json!({ "Results": results }),
1540 );
1541 ok(json!({ "TransferId": transfer_id }))
1542 }
1543
1544 fn start_remote_delete(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1545 let connector_id = req_str(b, "ConnectorId")?.to_string();
1546 req_str(b, "DeletePath")?;
1547 let guard = self.state.read();
1548 let data = guard
1549 .get(&ctx.account)
1550 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1551 self.require_connector(data, &connector_id)?;
1552 ok(json!({ "DeleteId": Uuid::new_v4().to_string() }))
1553 }
1554
1555 fn start_remote_move(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1556 let connector_id = req_str(b, "ConnectorId")?.to_string();
1557 req_str(b, "SourcePath")?;
1558 req_str(b, "TargetPath")?;
1559 let guard = self.state.read();
1560 let data = guard
1561 .get(&ctx.account)
1562 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1563 self.require_connector(data, &connector_id)?;
1564 ok(json!({ "MoveId": Uuid::new_v4().to_string() }))
1565 }
1566
1567 fn list_file_transfer_results(
1568 &self,
1569 ctx: &Ctx,
1570 b: &Value,
1571 ) -> Result<AwsResponse, AwsServiceError> {
1572 let connector_id = req_str(b, "ConnectorId")?.to_string();
1573 let transfer_id = req_str(b, "TransferId")?.to_string();
1574 let guard = self.state.read();
1575 let data = guard
1576 .get(&ctx.account)
1577 .ok_or_else(|| not_found(&format!("Connector {connector_id} does not exist.")))?;
1578 self.require_connector(data, &connector_id)?;
1579 let rows: Vec<Value> = data
1580 .file_transfers
1581 .get(&format!("{connector_id}/{transfer_id}"))
1582 .and_then(|t| t.get("Results"))
1583 .and_then(Value::as_array)
1584 .cloned()
1585 .unwrap_or_default();
1586 ok(list_response("FileTransferResults", rows, b, &[]))
1587 }
1588}
1589
1590impl TransferService {
1593 fn create_profile(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1594 req_str(b, "As2Id")?;
1595 req_str(b, "ProfileType")?;
1596 let profile_id = format!("p-{}", hex17());
1597 let p_arn = arn(ctx, &format!("profile/{profile_id}"));
1598 let mut p = Map::new();
1599 p.insert("Arn".into(), json!(p_arn));
1600 p.insert("ProfileId".into(), json!(profile_id));
1601 copy_present(b, &mut p, &["As2Id", "ProfileType", "CertificateIds"]);
1602 let mut guard = self.state.write();
1603 let data = guard.get_or_create(&ctx.account);
1604 data.profiles.insert(profile_id.clone(), Value::Object(p));
1605 store_tags(data, &p_arn, b);
1606 ok(json!({ "ProfileId": profile_id }))
1607 }
1608
1609 fn update_profile(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1610 let profile_id = req_str(b, "ProfileId")?.to_string();
1611 let mut guard = self.state.write();
1612 let data = guard.get_or_create(&ctx.account);
1613 let p = data
1614 .profiles
1615 .get_mut(&profile_id)
1616 .ok_or_else(|| not_found(&format!("Profile {profile_id} does not exist.")))?;
1617 copy_present(b, p.as_object_mut().unwrap(), &["CertificateIds"]);
1618 ok(json!({ "ProfileId": profile_id }))
1619 }
1620
1621 fn describe_profile(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1622 let profile_id = req_str(b, "ProfileId")?.to_string();
1623 let guard = self.state.read();
1624 let data = guard
1625 .get(&ctx.account)
1626 .ok_or_else(|| not_found(&format!("Profile {profile_id} does not exist.")))?;
1627 let p = data
1628 .profiles
1629 .get(&profile_id)
1630 .ok_or_else(|| not_found(&format!("Profile {profile_id} does not exist.")))?;
1631 let mut out = p.as_object().unwrap().clone();
1632 let arn = out
1633 .get("Arn")
1634 .and_then(Value::as_str)
1635 .unwrap_or("")
1636 .to_string();
1637 if let Some(tags) = tags_list(data, &arn) {
1638 out.insert("Tags".into(), tags);
1639 }
1640 ok(json!({ "Profile": Value::Object(out) }))
1641 }
1642
1643 fn delete_profile(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1644 let profile_id = req_str(b, "ProfileId")?.to_string();
1645 let mut guard = self.state.write();
1646 let data = guard.get_or_create(&ctx.account);
1647 let p = data
1648 .profiles
1649 .remove(&profile_id)
1650 .ok_or_else(|| not_found(&format!("Profile {profile_id} does not exist.")))?;
1651 if let Some(arn) = p.get("Arn").and_then(Value::as_str) {
1652 data.tags.remove(arn);
1653 }
1654 ok(json!({}))
1655 }
1656
1657 fn list_profiles(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1658 let filter = opt_str(b, "ProfileType").map(String::from);
1659 let guard = self.state.read();
1660 let rows: Vec<Value> = guard
1661 .get(&ctx.account)
1662 .map(|d| {
1663 d.profiles
1664 .values()
1665 .filter(|p| {
1666 filter
1667 .as_deref()
1668 .is_none_or(|f| p.get("ProfileType").and_then(Value::as_str) == Some(f))
1669 })
1670 .map(|p| {
1671 let mut out = Map::new();
1672 for f in ["Arn", "ProfileId", "As2Id", "ProfileType"] {
1673 if let Some(v) = p.get(f) {
1674 out.insert(f.to_string(), v.clone());
1675 }
1676 }
1677 Value::Object(out)
1678 })
1679 .collect()
1680 })
1681 .unwrap_or_default();
1682 ok(list_response("Profiles", rows, b, &[]))
1683 }
1684}
1685
1686fn parse_certificate_fields(pem: &str) -> Option<(String, f64, f64)> {
1692 use x509_cert::der::DecodePem;
1693 use x509_cert::Certificate;
1694
1695 let cert = Certificate::from_pem(pem.as_bytes()).ok()?;
1696 let tbs = &cert.tbs_certificate;
1697 let serial = tbs
1699 .serial_number
1700 .as_bytes()
1701 .iter()
1702 .map(|byte| format!("{byte:02x}"))
1703 .collect::<String>();
1704 let not_before = tbs.validity.not_before.to_unix_duration().as_secs() as f64;
1705 let not_after = tbs.validity.not_after.to_unix_duration().as_secs() as f64;
1706 Some((serial, not_before, not_after))
1707}
1708
1709impl TransferService {
1710 fn import_certificate(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1711 req_str(b, "Usage")?;
1712 let certificate_pem = req_str(b, "Certificate")?.to_string();
1713 let certificate_id = format!("cert-{}", hex17());
1714 let c_arn = arn(ctx, &format!("certificate/{certificate_id}"));
1715 let mut c = Map::new();
1716 c.insert("Arn".into(), json!(c_arn));
1717 c.insert("CertificateId".into(), json!(certificate_id));
1718 c.insert("Status".into(), json!("ACTIVE"));
1719 c.insert("Type".into(), json!("CERTIFICATE"));
1720 if let Some((serial, not_before, not_after)) = parse_certificate_fields(&certificate_pem) {
1724 c.insert("Serial".into(), json!(serial));
1725 c.insert("NotBeforeDate".into(), json!(not_before));
1726 c.insert("NotAfterDate".into(), json!(not_after));
1727 }
1728 copy_present(
1729 b,
1730 &mut c,
1731 &[
1732 "Usage",
1733 "Certificate",
1734 "CertificateChain",
1735 "ActiveDate",
1736 "InactiveDate",
1737 "Description",
1738 ],
1739 );
1740 let mut guard = self.state.write();
1741 let data = guard.get_or_create(&ctx.account);
1742 data.certificates
1743 .insert(certificate_id.clone(), Value::Object(c));
1744 store_tags(data, &c_arn, b);
1745 ok(json!({ "CertificateId": certificate_id }))
1746 }
1747
1748 fn update_certificate(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1749 let certificate_id = req_str(b, "CertificateId")?.to_string();
1750 let mut guard = self.state.write();
1751 let data = guard.get_or_create(&ctx.account);
1752 let c = data
1753 .certificates
1754 .get_mut(&certificate_id)
1755 .ok_or_else(|| not_found(&format!("Certificate {certificate_id} does not exist.")))?;
1756 copy_present(
1757 b,
1758 c.as_object_mut().unwrap(),
1759 &["ActiveDate", "InactiveDate", "Description"],
1760 );
1761 ok(json!({ "CertificateId": certificate_id }))
1762 }
1763
1764 fn describe_certificate(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1765 let certificate_id = req_str(b, "CertificateId")?.to_string();
1766 let guard = self.state.read();
1767 let data = guard
1768 .get(&ctx.account)
1769 .ok_or_else(|| not_found(&format!("Certificate {certificate_id} does not exist.")))?;
1770 let c = data
1771 .certificates
1772 .get(&certificate_id)
1773 .ok_or_else(|| not_found(&format!("Certificate {certificate_id} does not exist.")))?;
1774 let mut out = c.as_object().unwrap().clone();
1775 let arn = out
1776 .get("Arn")
1777 .and_then(Value::as_str)
1778 .unwrap_or("")
1779 .to_string();
1780 if let Some(tags) = tags_list(data, &arn) {
1781 out.insert("Tags".into(), tags);
1782 }
1783 ok(json!({ "Certificate": Value::Object(out) }))
1784 }
1785
1786 fn delete_certificate(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1787 let certificate_id = req_str(b, "CertificateId")?.to_string();
1788 let mut guard = self.state.write();
1789 let data = guard.get_or_create(&ctx.account);
1790 let c = data
1791 .certificates
1792 .remove(&certificate_id)
1793 .ok_or_else(|| not_found(&format!("Certificate {certificate_id} does not exist.")))?;
1794 if let Some(arn) = c.get("Arn").and_then(Value::as_str) {
1795 data.tags.remove(arn);
1796 }
1797 ok(json!({}))
1798 }
1799
1800 fn list_certificates(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1801 let guard = self.state.read();
1802 let rows: Vec<Value> = guard
1803 .get(&ctx.account)
1804 .map(|d| {
1805 d.certificates
1806 .values()
1807 .map(|c| {
1808 let mut out = Map::new();
1809 for f in [
1810 "Arn",
1811 "CertificateId",
1812 "Usage",
1813 "Status",
1814 "ActiveDate",
1815 "InactiveDate",
1816 "Type",
1817 "Description",
1818 ] {
1819 if let Some(v) = c.get(f) {
1820 out.insert(f.to_string(), v.clone());
1821 }
1822 }
1823 Value::Object(out)
1824 })
1825 .collect()
1826 })
1827 .unwrap_or_default();
1828 ok(list_response("Certificates", rows, b, &[]))
1829 }
1830}
1831
1832const SECURITY_POLICIES: &[&str] = &[
1836 "TransferSecurityPolicy-2018-11",
1837 "TransferSecurityPolicy-2020-06",
1838 "TransferSecurityPolicy-2022-03",
1839 "TransferSecurityPolicy-2023-05",
1840 "TransferSecurityPolicy-2024-01",
1841 "TransferSecurityPolicy-FIPS-2020-06",
1842 "TransferSecurityPolicy-FIPS-2023-05",
1843 "TransferSecurityPolicy-FIPS-2024-01",
1844 "TransferSecurityPolicy-FIPS-2024-05",
1845 "TransferSecurityPolicy-PQ-SSH-Experimental-2023-04",
1846 "TransferSecurityPolicy-PQ-SSH-FIPS-Experimental-2023-04",
1847 "TransferSecurityPolicy-Restricted-2018-11",
1848 "TransferSecurityPolicy-Restricted-2020-06",
1849];
1850
1851impl TransferService {
1852 fn describe_security_policy(&self, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1853 let name = req_str(b, "SecurityPolicyName")?;
1854 if !SECURITY_POLICIES.contains(&name) {
1855 return Err(not_found(&format!(
1856 "Security policy {name} does not exist."
1857 )));
1858 }
1859 let fips = name.contains("FIPS");
1860 let policy_type = if name.contains("Connector") {
1861 "CONNECTOR"
1862 } else {
1863 "SERVER"
1864 };
1865 ok(json!({
1866 "SecurityPolicy": {
1867 "SecurityPolicyName": name,
1868 "Fips": fips,
1869 "Type": policy_type,
1870 "SshCiphers": ["aes256-gcm@openssh.com", "aes128-gcm@openssh.com"],
1871 "SshKexs": ["ecdh-sha2-nistp256", "diffie-hellman-group-exchange-sha256"],
1872 "SshMacs": ["hmac-sha2-256", "hmac-sha2-512"],
1873 "TlsCiphers": ["TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384"]
1874 }
1875 }))
1876 }
1877
1878 fn list_security_policies(&self, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1879 let rows: Vec<Value> = SECURITY_POLICIES.iter().map(|p| json!(p)).collect();
1880 ok(list_response("SecurityPolicyNames", rows, b, &[]))
1881 }
1882}
1883
1884const WEB_APP_FIELDS: &[&str] = &["AccessEndpoint", "WebAppUnits", "WebAppEndpointPolicy"];
1887
1888impl TransferService {
1889 fn create_web_app(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1890 if b.get("IdentityProviderDetails").is_none() {
1891 return Err(invalid_request("IdentityProviderDetails is required."));
1892 }
1893 let web_app_id = format!("webapp-{}", hex17());
1894 let w_arn = arn(ctx, &format!("webapp/{web_app_id}"));
1895 let mut w = Map::new();
1896 w.insert("Arn".into(), json!(w_arn));
1897 w.insert("WebAppId".into(), json!(web_app_id));
1898 w.insert(
1899 "WebAppEndpoint".into(),
1900 json!(format!(
1901 "{web_app_id}.transfer.{}.amazonaws.com",
1902 ctx.region
1903 )),
1904 );
1905 w.insert("EndpointType".into(), json!("STANDARD"));
1906 if let Some(idp) = b.get("IdentityProviderDetails") {
1907 w.insert("DescribedIdentityProviderDetails".into(), idp.clone());
1908 }
1909 copy_present(b, &mut w, WEB_APP_FIELDS);
1910 let mut guard = self.state.write();
1911 let data = guard.get_or_create(&ctx.account);
1912 data.web_apps.insert(web_app_id.clone(), Value::Object(w));
1913 store_tags(data, &w_arn, b);
1914 ok(json!({ "WebAppId": web_app_id }))
1915 }
1916
1917 fn update_web_app(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1918 let web_app_id = req_str(b, "WebAppId")?.to_string();
1919 let mut guard = self.state.write();
1920 let data = guard.get_or_create(&ctx.account);
1921 let w = data
1922 .web_apps
1923 .get_mut(&web_app_id)
1924 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
1925 let obj = w.as_object_mut().unwrap();
1926 if let Some(idp) = b.get("IdentityProviderDetails") {
1927 obj.insert("DescribedIdentityProviderDetails".into(), idp.clone());
1928 }
1929 copy_present(b, obj, WEB_APP_FIELDS);
1930 ok(json!({ "WebAppId": web_app_id }))
1931 }
1932
1933 fn describe_web_app(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1934 let web_app_id = req_str(b, "WebAppId")?.to_string();
1935 let guard = self.state.read();
1936 let data = guard
1937 .get(&ctx.account)
1938 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
1939 let w = data
1940 .web_apps
1941 .get(&web_app_id)
1942 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
1943 let mut out = w.as_object().unwrap().clone();
1944 let arn = out
1945 .get("Arn")
1946 .and_then(Value::as_str)
1947 .unwrap_or("")
1948 .to_string();
1949 if let Some(tags) = tags_list(data, &arn) {
1950 out.insert("Tags".into(), tags);
1951 }
1952 ok(json!({ "WebApp": Value::Object(out) }))
1953 }
1954
1955 fn delete_web_app(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1956 let web_app_id = req_str(b, "WebAppId")?.to_string();
1957 let mut guard = self.state.write();
1958 let data = guard.get_or_create(&ctx.account);
1959 let w = data
1960 .web_apps
1961 .remove(&web_app_id)
1962 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
1963 data.web_app_customizations.remove(&web_app_id);
1964 if let Some(arn) = w.get("Arn").and_then(Value::as_str) {
1965 data.tags.remove(arn);
1966 }
1967 ok(json!({}))
1968 }
1969
1970 fn list_web_apps(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
1971 let guard = self.state.read();
1972 let rows: Vec<Value> = guard
1973 .get(&ctx.account)
1974 .map(|d| {
1975 d.web_apps
1976 .values()
1977 .map(|w| {
1978 let mut out = Map::new();
1979 for f in [
1980 "Arn",
1981 "WebAppId",
1982 "AccessEndpoint",
1983 "WebAppEndpoint",
1984 "EndpointType",
1985 ] {
1986 if let Some(v) = w.get(f) {
1987 out.insert(f.to_string(), v.clone());
1988 }
1989 }
1990 Value::Object(out)
1991 })
1992 .collect()
1993 })
1994 .unwrap_or_default();
1995 ok(list_response("WebApps", rows, b, &[]))
1996 }
1997
1998 fn update_web_app_customization(
1999 &self,
2000 ctx: &Ctx,
2001 b: &Value,
2002 ) -> Result<AwsResponse, AwsServiceError> {
2003 let web_app_id = req_str(b, "WebAppId")?.to_string();
2004 let mut guard = self.state.write();
2005 let data = guard.get_or_create(&ctx.account);
2006 let w = data
2007 .web_apps
2008 .get(&web_app_id)
2009 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
2010 let arn = w
2011 .get("Arn")
2012 .and_then(Value::as_str)
2013 .unwrap_or("")
2014 .replace("webapp/", "webapp-customization/");
2015 let mut cust = Map::new();
2016 cust.insert("Arn".into(), json!(arn));
2017 cust.insert("WebAppId".into(), json!(web_app_id));
2018 copy_present(b, &mut cust, &["Title", "LogoFile", "FaviconFile"]);
2019 data.web_app_customizations
2020 .insert(web_app_id.clone(), Value::Object(cust));
2021 ok(json!({ "WebAppId": web_app_id }))
2022 }
2023
2024 fn describe_web_app_customization(
2025 &self,
2026 ctx: &Ctx,
2027 b: &Value,
2028 ) -> Result<AwsResponse, AwsServiceError> {
2029 let web_app_id = req_str(b, "WebAppId")?.to_string();
2030 let guard = self.state.read();
2031 let data = guard
2032 .get(&ctx.account)
2033 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
2034 if let Some(cust) = data.web_app_customizations.get(&web_app_id) {
2035 return ok(json!({ "WebAppCustomization": cust }));
2036 }
2037 let w = data
2040 .web_apps
2041 .get(&web_app_id)
2042 .ok_or_else(|| not_found(&format!("Web app {web_app_id} does not exist.")))?;
2043 let arn = w
2044 .get("Arn")
2045 .and_then(Value::as_str)
2046 .unwrap_or("")
2047 .replace("webapp/", "webapp-customization/");
2048 ok(json!({
2049 "WebAppCustomization": { "Arn": arn, "WebAppId": web_app_id }
2050 }))
2051 }
2052
2053 fn delete_web_app_customization(
2054 &self,
2055 ctx: &Ctx,
2056 b: &Value,
2057 ) -> Result<AwsResponse, AwsServiceError> {
2058 let web_app_id = req_str(b, "WebAppId")?.to_string();
2059 let mut guard = self.state.write();
2060 let data = guard.get_or_create(&ctx.account);
2061 if !data.web_apps.contains_key(&web_app_id) {
2062 return Err(not_found(&format!("Web app {web_app_id} does not exist.")));
2063 }
2064 data.web_app_customizations.remove(&web_app_id);
2065 ok(json!({}))
2066 }
2067}
2068
2069impl TransferService {
2072 fn test_identity_provider(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
2073 let server_id = req_str(b, "ServerId")?.to_string();
2074 req_str(b, "UserName")?;
2075 let guard = self.state.read();
2076 let data = guard
2077 .get(&ctx.account)
2078 .ok_or_else(|| not_found(&format!("Server {server_id} does not exist.")))?;
2079 let srv = self.require_server(data, &server_id)?;
2080 let idp_type = srv
2081 .get("IdentityProviderType")
2082 .and_then(Value::as_str)
2083 .unwrap_or("SERVICE_MANAGED");
2084 let url = srv
2085 .get("IdentityProviderDetails")
2086 .and_then(|d| d.get("Url"))
2087 .and_then(Value::as_str)
2088 .unwrap_or("")
2089 .to_string();
2090 ok(json!({
2091 "Response": format!("{{\"IdentityProviderType\":\"{idp_type}\"}}"),
2092 "StatusCode": 200,
2093 "Message": "",
2094 "Url": url
2095 }))
2096 }
2097}
2098
2099impl TransferService {
2102 fn tag_resource(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
2103 let resource_arn = req_str(b, "Arn")?.to_string();
2104 if b.get("Tags").and_then(Value::as_array).is_none() {
2105 return Err(invalid_request("Tags is required."));
2106 }
2107 let mut guard = self.state.write();
2108 let data = guard.get_or_create(&ctx.account);
2109 store_tags(data, &resource_arn, b);
2110 ok(json!({}))
2111 }
2112
2113 fn untag_resource(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
2114 let resource_arn = req_str(b, "Arn")?.to_string();
2115 let keys: Vec<String> = b
2116 .get("TagKeys")
2117 .and_then(Value::as_array)
2118 .map(|a| {
2119 a.iter()
2120 .filter_map(|v| v.as_str().map(String::from))
2121 .collect()
2122 })
2123 .ok_or_else(|| invalid_request("TagKeys is required."))?;
2124 let mut guard = self.state.write();
2125 let data = guard.get_or_create(&ctx.account);
2126 if let Some(entry) = data.tags.get_mut(&resource_arn) {
2127 for k in &keys {
2128 entry.remove(k);
2129 }
2130 }
2131 ok(json!({}))
2132 }
2133
2134 fn list_tags_for_resource(&self, ctx: &Ctx, b: &Value) -> Result<AwsResponse, AwsServiceError> {
2135 let resource_arn = req_str(b, "Arn")?.to_string();
2136 let guard = self.state.read();
2137 let tags: Vec<Value> = guard
2138 .get(&ctx.account)
2139 .and_then(|d| d.tags.get(&resource_arn))
2140 .map(|m| {
2141 m.iter()
2142 .map(|(k, v)| json!({ "Key": k, "Value": v }))
2143 .collect()
2144 })
2145 .unwrap_or_default();
2146 ok(list_response(
2149 "Tags",
2150 tags,
2151 b,
2152 &[("Arn", json!(resource_arn))],
2153 ))
2154 }
2155}