1use base64::Engine;
6use chrono::Utc;
7use http::header::ETAG;
8use http::{HeaderMap, StatusCode};
9use uuid::Uuid;
10
11use fakecloud_core::service::{AwsRequest, AwsResponse, AwsServiceError};
12
13use crate::functions::{
14 CloudFrontOriginAccessIdentityConfig, FunctionConfig, ImportSource, KeyGroupConfig,
15 MonitoringSubscriptionBody, PublicKeyConfig, StoredFunction, StoredKeyGroup,
16 StoredKeyValueStore, StoredMonitoringSubscription, StoredOriginAccessIdentity, StoredPublicKey,
17};
18use crate::policies::{
19 not_found, precondition_failed, require_if_match, rfc3339, route_id, xml_with_etag,
20};
21use crate::router::Route;
22use crate::service::{
23 aws_error, esc, generate_id_with_prefix, invalid_argument, xml_response, CloudFrontService,
24 DEFAULT_ACCOUNT,
25};
26use crate::xml_io;
27
28const NS: &str = crate::NAMESPACE;
29const XML_DECL: &str = r#"<?xml version="1.0" encoding="UTF-8"?>"#;
30
31impl CloudFrontService {
34 pub(crate) fn create_function(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
35 let parsed: CreateFunctionRequest = xml_io::from_xml_root(&req.body)
37 .map_err(|e| invalid_argument(format!("invalid CreateFunctionRequest XML: {e}")))?;
38 if parsed.name.is_empty() {
39 return Err(invalid_argument("CreateFunctionRequest.Name is required"));
40 }
41
42 let mut state = self.state.write();
43 let account = state
44 .accounts
45 .entry(DEFAULT_ACCOUNT.to_string())
46 .or_default();
47 if account.functions.contains_key(&parsed.name) {
48 return Err(aws_error(
49 StatusCode::CONFLICT,
50 "FunctionAlreadyExists",
51 format!("Function {} already exists", parsed.name),
52 ));
53 }
54 let now = Utc::now();
55 let etag = generate_id_with_prefix("E");
56 let function_arn = format!(
57 "arn:aws:cloudfront::{}:function/{}",
58 DEFAULT_ACCOUNT, parsed.name
59 );
60 let stored = StoredFunction {
61 name: parsed.name.clone(),
62 etag: etag.clone(),
63 status: "UNPUBLISHED".to_string(),
64 stage: "DEVELOPMENT".to_string(),
65 function_arn: function_arn.clone(),
66 created_time: now,
67 last_modified_time: now,
68 config: parsed.function_config,
69 function_code: parsed.function_code,
70 };
71 account
72 .functions
73 .insert(parsed.name.clone(), stored.clone());
74 drop(state);
75
76 let body = render_function_summary(&stored, "CreateFunctionResult");
77 Ok(xml_with_etag(StatusCode::CREATED, body, &etag, None))
78 }
79
80 pub(crate) fn describe_function(
81 &self,
82 req: &AwsRequest,
83 route: &Route,
84 ) -> Result<AwsResponse, AwsServiceError> {
85 let name = route_id(route, "Function")?;
86 let stage = parse_stage_query(&req.raw_query);
87 let state = self.state.read();
88 let f = state
89 .accounts
90 .get(DEFAULT_ACCOUNT)
91 .and_then(|a| a.functions.get(&name).cloned())
92 .ok_or_else(|| not_found("Function", &name))?;
93 drop(state);
94 let view = stage_view(&f, &stage);
95 let body = render_function_summary(&view, "DescribeFunctionResult");
96 Ok(xml_with_etag(StatusCode::OK, body, &view.etag, None))
97 }
98
99 pub(crate) fn get_function(
100 &self,
101 req: &AwsRequest,
102 route: &Route,
103 ) -> Result<AwsResponse, AwsServiceError> {
104 let name = route_id(route, "Function")?;
105 let stage = parse_stage_query(&req.raw_query);
106 let state = self.state.read();
107 let f = state
108 .accounts
109 .get(DEFAULT_ACCOUNT)
110 .and_then(|a| a.functions.get(&name).cloned())
111 .ok_or_else(|| not_found("Function", &name))?;
112 drop(state);
113 let view = stage_view(&f, &stage);
114 let mut headers = HeaderMap::new();
115 headers.insert(ETAG, view.etag.parse().unwrap());
116 let bytes = base64::engine::general_purpose::STANDARD
117 .decode(view.function_code.as_bytes())
118 .unwrap_or_default();
119 Ok(AwsResponse {
120 status: StatusCode::OK,
121 headers,
122 content_type: "application/octet-stream".to_string(),
123 body: fakecloud_core::service::ResponseBody::Bytes(bytes::Bytes::from(bytes)),
124 })
125 }
126
127 pub(crate) fn update_function(
128 &self,
129 req: &AwsRequest,
130 route: &Route,
131 ) -> Result<AwsResponse, AwsServiceError> {
132 let name = route_id(route, "Function")?;
133 let if_match = require_if_match(req)?;
134 let parsed: UpdateFunctionRequest = xml_io::from_xml_root(&req.body)
135 .map_err(|e| invalid_argument(format!("invalid UpdateFunctionRequest XML: {e}")))?;
136 let mut state = self.state.write();
137 let account = state
138 .accounts
139 .get_mut(DEFAULT_ACCOUNT)
140 .ok_or_else(|| not_found("Function", &name))?;
141 let f = account
142 .functions
143 .get_mut(&name)
144 .ok_or_else(|| not_found("Function", &name))?;
145 if f.etag != if_match {
146 return Err(precondition_failed());
147 }
148 f.config = parsed.function_config;
149 f.function_code = parsed.function_code;
150 f.etag = generate_id_with_prefix("E");
151 f.last_modified_time = Utc::now();
152 f.status = "UNPUBLISHED".to_string();
153 f.stage = "DEVELOPMENT".to_string();
154 let snap = f.clone();
155 drop(state);
156 let body = render_function_summary(&snap, "UpdateFunctionResult");
157 let mut headers = HeaderMap::new();
161 if let Ok(v) = http::HeaderValue::from_str(&snap.etag) {
162 headers.insert(ETAG, v.clone());
163 headers.insert("ETtag", v);
164 }
165 Ok(xml_response(StatusCode::OK, body, headers))
166 }
167
168 pub(crate) fn delete_function(
169 &self,
170 req: &AwsRequest,
171 route: &Route,
172 ) -> Result<AwsResponse, AwsServiceError> {
173 let name = route_id(route, "Function")?;
174 let if_match = require_if_match(req)?;
175 let mut state = self.state.write();
176 let account = state
177 .accounts
178 .get_mut(DEFAULT_ACCOUNT)
179 .ok_or_else(|| not_found("Function", &name))?;
180 let f = account
181 .functions
182 .get(&name)
183 .ok_or_else(|| not_found("Function", &name))?;
184 if f.etag != if_match {
185 return Err(precondition_failed());
186 }
187 account.functions.remove(&name);
188 drop(state);
189 Ok(crate::policies::empty(StatusCode::NO_CONTENT))
190 }
191
192 pub(crate) fn list_functions(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
193 let stage = parse_stage_query(&req.raw_query);
194 let state = self.state.read();
195 let mut items: Vec<StoredFunction> = state
196 .accounts
197 .get(DEFAULT_ACCOUNT)
198 .map(|a| a.functions.values().cloned().collect())
199 .unwrap_or_default();
200 drop(state);
201 items.sort_by(|a, b| a.name.cmp(&b.name));
202
203 let mut body = String::with_capacity(512);
204 body.push_str(XML_DECL);
205 body.push_str(&format!("<FunctionList xmlns=\"{NS}\">"));
206 body.push_str("<Marker></Marker>");
207 body.push_str("<MaxItems>100</MaxItems>");
208 body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
209 body.push_str("<Items>");
210 for f in &items {
211 let view = stage_view(f, &stage);
212 body.push_str(&render_function_summary_inner(&view));
213 }
214 body.push_str("</Items>");
215 body.push_str("</FunctionList>");
216 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
217 }
218
219 pub(crate) fn publish_function(
220 &self,
221 req: &AwsRequest,
222 route: &Route,
223 ) -> Result<AwsResponse, AwsServiceError> {
224 let name = route_id(route, "Function")?;
225 let if_match = require_if_match(req)?;
226 let mut state = self.state.write();
227 let account = state
228 .accounts
229 .get_mut(DEFAULT_ACCOUNT)
230 .ok_or_else(|| not_found("Function", &name))?;
231 let f = account
232 .functions
233 .get_mut(&name)
234 .ok_or_else(|| not_found("Function", &name))?;
235 if f.etag != if_match {
236 return Err(precondition_failed());
237 }
238 f.status = "DEPLOYED".to_string();
239 f.stage = "LIVE".to_string();
240 f.last_modified_time = Utc::now();
241 let snap = f.clone();
242 drop(state);
243 let body = render_function_summary(&snap, "PublishFunctionResult");
244 Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
245 }
246
247 pub(crate) fn test_function(
248 &self,
249 req: &AwsRequest,
250 route: &Route,
251 ) -> Result<AwsResponse, AwsServiceError> {
252 let name = route_id(route, "Function")?;
253 let if_match = require_if_match(req)?;
254 let state = self.state.read();
255 let f = state
256 .accounts
257 .get(DEFAULT_ACCOUNT)
258 .and_then(|a| a.functions.get(&name).cloned())
259 .ok_or_else(|| not_found("Function", &name))?;
260 drop(state);
261 if f.etag != if_match {
262 return Err(precondition_failed());
263 }
264
265 let mut body = String::with_capacity(512);
266 body.push_str(XML_DECL);
267 body.push_str(&format!("<TestResult xmlns=\"{NS}\">"));
268 body.push_str("<TestFunctionResult>");
269 body.push_str(&format!(
270 "<FunctionSummary>{}</FunctionSummary>",
271 render_function_summary_inner(&f)
272 .replacen("<FunctionSummary>", "", 1)
273 .replacen("</FunctionSummary>", "", 1)
274 ));
275 body.push_str("<ComputeUtilization>0</ComputeUtilization>");
276 body.push_str("<FunctionExecutionLogs></FunctionExecutionLogs>");
277 body.push_str("<FunctionErrorMessage></FunctionErrorMessage>");
278 body.push_str("<FunctionOutput>{}</FunctionOutput>");
279 body.push_str("</TestFunctionResult>");
280 body.push_str("</TestResult>");
281 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
282 }
283}
284
285impl CloudFrontService {
288 pub(crate) fn create_public_key(
289 &self,
290 req: &AwsRequest,
291 ) -> Result<AwsResponse, AwsServiceError> {
292 let cfg: PublicKeyConfig = xml_io::from_xml_root(&req.body)
293 .map_err(|e| invalid_argument(format!("invalid PublicKeyConfig XML: {e}")))?;
294 if cfg.name.is_empty() {
295 return Err(invalid_argument("PublicKeyConfig.Name is required"));
296 }
297 if cfg.encoded_key.is_empty() {
298 return Err(invalid_argument("PublicKeyConfig.EncodedKey is required"));
299 }
300 let mut state = self.state.write();
301 let account = state
302 .accounts
303 .entry(DEFAULT_ACCOUNT.to_string())
304 .or_default();
305 if let Some(existing) = account
306 .public_keys
307 .values()
308 .find(|p| p.config.caller_reference == cfg.caller_reference)
309 {
310 return Err(aws_error(
311 StatusCode::CONFLICT,
312 "PublicKeyAlreadyExists",
313 format!(
314 "PublicKey with same CallerReference exists: {}",
315 existing.id
316 ),
317 ));
318 }
319 let id = generate_id_with_prefix("K");
320 let etag = generate_id_with_prefix("E");
321 let stored = StoredPublicKey {
322 id: id.clone(),
323 etag: etag.clone(),
324 created_time: Utc::now(),
325 config: cfg,
326 };
327 account.public_keys.insert(id.clone(), stored.clone());
328 drop(state);
329 let body = render_public_key(&stored, "PublicKey");
330 Ok(xml_with_etag(StatusCode::CREATED, body, &etag, Some(&id)))
331 }
332
333 pub(crate) fn get_public_key(&self, route: &Route) -> Result<AwsResponse, AwsServiceError> {
334 let id = route_id(route, "PublicKey")?;
335 let state = self.state.read();
336 let p = state
337 .accounts
338 .get(DEFAULT_ACCOUNT)
339 .and_then(|a| a.public_keys.get(&id).cloned())
340 .ok_or_else(|| not_found("PublicKey", &id))?;
341 drop(state);
342 let body = render_public_key(&p, "PublicKey");
343 Ok(xml_with_etag(StatusCode::OK, body, &p.etag, None))
344 }
345
346 pub(crate) fn get_public_key_config(
347 &self,
348 route: &Route,
349 ) -> Result<AwsResponse, AwsServiceError> {
350 let id = route_id(route, "PublicKey")?;
351 let state = self.state.read();
352 let p = state
353 .accounts
354 .get(DEFAULT_ACCOUNT)
355 .and_then(|a| a.public_keys.get(&id).cloned())
356 .ok_or_else(|| not_found("PublicKey", &id))?;
357 drop(state);
358 let body = config_xml("PublicKeyConfig", &p.config)?;
359 Ok(xml_with_etag(StatusCode::OK, body, &p.etag, None))
360 }
361
362 pub(crate) fn update_public_key(
363 &self,
364 req: &AwsRequest,
365 route: &Route,
366 ) -> Result<AwsResponse, AwsServiceError> {
367 let id = route_id(route, "PublicKey")?;
368 let if_match = require_if_match(req)?;
369 let cfg: PublicKeyConfig = xml_io::from_xml_root(&req.body)
370 .map_err(|e| invalid_argument(format!("invalid PublicKeyConfig XML: {e}")))?;
371 if cfg.name.is_empty() {
372 return Err(invalid_argument("PublicKeyConfig.Name is required"));
373 }
374 let mut state = self.state.write();
375 let account = state
376 .accounts
377 .get_mut(DEFAULT_ACCOUNT)
378 .ok_or_else(|| not_found("PublicKey", &id))?;
379 let p = account
380 .public_keys
381 .get_mut(&id)
382 .ok_or_else(|| not_found("PublicKey", &id))?;
383 if p.etag != if_match {
384 return Err(precondition_failed());
385 }
386 if p.config.caller_reference != cfg.caller_reference {
388 return Err(invalid_argument(
389 "CallerReference cannot change on UpdatePublicKey",
390 ));
391 }
392 p.config = cfg;
393 p.etag = generate_id_with_prefix("E");
394 let snap = p.clone();
395 drop(state);
396 let body = render_public_key(&snap, "PublicKey");
397 Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
398 }
399
400 pub(crate) fn delete_public_key(
401 &self,
402 req: &AwsRequest,
403 route: &Route,
404 ) -> Result<AwsResponse, AwsServiceError> {
405 let id = route_id(route, "PublicKey")?;
406 let if_match = require_if_match(req)?;
407 let mut state = self.state.write();
408 let account = state
409 .accounts
410 .get_mut(DEFAULT_ACCOUNT)
411 .ok_or_else(|| not_found("PublicKey", &id))?;
412 let p = account
413 .public_keys
414 .get(&id)
415 .ok_or_else(|| not_found("PublicKey", &id))?;
416 if p.etag != if_match {
417 return Err(precondition_failed());
418 }
419 account.public_keys.remove(&id);
420 drop(state);
421 Ok(crate::policies::empty(StatusCode::NO_CONTENT))
422 }
423
424 pub(crate) fn list_public_keys(
425 &self,
426 _req: &AwsRequest,
427 ) -> Result<AwsResponse, AwsServiceError> {
428 let state = self.state.read();
429 let mut items: Vec<StoredPublicKey> = state
430 .accounts
431 .get(DEFAULT_ACCOUNT)
432 .map(|a| a.public_keys.values().cloned().collect())
433 .unwrap_or_default();
434 drop(state);
435 items.sort_by(|a, b| a.id.cmp(&b.id));
436
437 let mut body = String::with_capacity(512);
438 body.push_str(XML_DECL);
439 body.push_str(&format!("<PublicKeyList xmlns=\"{NS}\">"));
440 body.push_str("<Marker></Marker>");
441 body.push_str("<MaxItems>100</MaxItems>");
442 body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
443 body.push_str("<Items>");
444 for p in &items {
445 body.push_str("<PublicKeySummary>");
446 body.push_str(&format!("<Id>{}</Id>", esc(&p.id)));
447 body.push_str(&format!("<Name>{}</Name>", esc(&p.config.name)));
448 body.push_str(&format!(
449 "<CreatedTime>{}</CreatedTime>",
450 rfc3339(&p.created_time)
451 ));
452 body.push_str(&format!(
453 "<EncodedKey>{}</EncodedKey>",
454 esc(&p.config.encoded_key)
455 ));
456 if let Some(c) = &p.config.comment {
457 body.push_str(&format!("<Comment>{}</Comment>", esc(c)));
458 }
459 body.push_str("</PublicKeySummary>");
460 }
461 body.push_str("</Items>");
462 body.push_str("</PublicKeyList>");
463 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
464 }
465}
466
467impl CloudFrontService {
470 pub(crate) fn create_key_group(
471 &self,
472 req: &AwsRequest,
473 ) -> Result<AwsResponse, AwsServiceError> {
474 let cfg: KeyGroupConfig = xml_io::from_xml_root(&req.body)
475 .map_err(|e| invalid_argument(format!("invalid KeyGroupConfig XML: {e}")))?;
476 if cfg.name.is_empty() {
477 return Err(invalid_argument("KeyGroupConfig.Name is required"));
478 }
479 let mut state = self.state.write();
480 let account = state
481 .accounts
482 .entry(DEFAULT_ACCOUNT.to_string())
483 .or_default();
484 let id = generate_id_with_prefix("K");
485 let etag = generate_id_with_prefix("E");
486 let stored = StoredKeyGroup {
487 id: id.clone(),
488 etag: etag.clone(),
489 last_modified_time: Utc::now(),
490 config: cfg,
491 };
492 account.key_groups.insert(id.clone(), stored.clone());
493 drop(state);
494 let body = render_key_group(&stored, "KeyGroup");
495 Ok(xml_with_etag(StatusCode::CREATED, body, &etag, Some(&id)))
496 }
497
498 pub(crate) fn get_key_group(&self, route: &Route) -> Result<AwsResponse, AwsServiceError> {
499 let id = route_id(route, "KeyGroup")?;
500 let state = self.state.read();
501 let g = state
502 .accounts
503 .get(DEFAULT_ACCOUNT)
504 .and_then(|a| a.key_groups.get(&id).cloned())
505 .ok_or_else(|| not_found("KeyGroup", &id))?;
506 drop(state);
507 let body = render_key_group(&g, "KeyGroup");
508 Ok(xml_with_etag(StatusCode::OK, body, &g.etag, None))
509 }
510
511 pub(crate) fn get_key_group_config(
512 &self,
513 route: &Route,
514 ) -> Result<AwsResponse, AwsServiceError> {
515 let id = route_id(route, "KeyGroup")?;
516 let state = self.state.read();
517 let g = state
518 .accounts
519 .get(DEFAULT_ACCOUNT)
520 .and_then(|a| a.key_groups.get(&id).cloned())
521 .ok_or_else(|| not_found("KeyGroup", &id))?;
522 drop(state);
523 let body = config_xml("KeyGroupConfig", &g.config)?;
524 Ok(xml_with_etag(StatusCode::OK, body, &g.etag, None))
525 }
526
527 pub(crate) fn update_key_group(
528 &self,
529 req: &AwsRequest,
530 route: &Route,
531 ) -> Result<AwsResponse, AwsServiceError> {
532 let id = route_id(route, "KeyGroup")?;
533 let if_match = require_if_match(req)?;
534 let cfg: KeyGroupConfig = xml_io::from_xml_root(&req.body)
535 .map_err(|e| invalid_argument(format!("invalid KeyGroupConfig XML: {e}")))?;
536 if cfg.name.is_empty() {
537 return Err(invalid_argument("KeyGroupConfig.Name is required"));
538 }
539 let mut state = self.state.write();
540 let account = state
541 .accounts
542 .get_mut(DEFAULT_ACCOUNT)
543 .ok_or_else(|| not_found("KeyGroup", &id))?;
544 let g = account
545 .key_groups
546 .get_mut(&id)
547 .ok_or_else(|| not_found("KeyGroup", &id))?;
548 if g.etag != if_match {
549 return Err(precondition_failed());
550 }
551 g.config = cfg;
552 g.etag = generate_id_with_prefix("E");
553 g.last_modified_time = Utc::now();
554 let snap = g.clone();
555 drop(state);
556 let body = render_key_group(&snap, "KeyGroup");
557 Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
558 }
559
560 pub(crate) fn delete_key_group(
561 &self,
562 req: &AwsRequest,
563 route: &Route,
564 ) -> Result<AwsResponse, AwsServiceError> {
565 let id = route_id(route, "KeyGroup")?;
566 let if_match = require_if_match(req)?;
567 let mut state = self.state.write();
568 let account = state
569 .accounts
570 .get_mut(DEFAULT_ACCOUNT)
571 .ok_or_else(|| not_found("KeyGroup", &id))?;
572 let g = account
573 .key_groups
574 .get(&id)
575 .ok_or_else(|| not_found("KeyGroup", &id))?;
576 if g.etag != if_match {
577 return Err(precondition_failed());
578 }
579 account.key_groups.remove(&id);
580 drop(state);
581 Ok(crate::policies::empty(StatusCode::NO_CONTENT))
582 }
583
584 pub(crate) fn list_key_groups(
585 &self,
586 _req: &AwsRequest,
587 ) -> Result<AwsResponse, AwsServiceError> {
588 let state = self.state.read();
589 let mut items: Vec<StoredKeyGroup> = state
590 .accounts
591 .get(DEFAULT_ACCOUNT)
592 .map(|a| a.key_groups.values().cloned().collect())
593 .unwrap_or_default();
594 drop(state);
595 items.sort_by(|a, b| a.config.name.cmp(&b.config.name));
596
597 let mut body = String::with_capacity(512);
598 body.push_str(XML_DECL);
599 body.push_str(&format!("<KeyGroupList xmlns=\"{NS}\">"));
600 body.push_str("<Marker></Marker>");
601 body.push_str("<MaxItems>100</MaxItems>");
602 body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
603 body.push_str("<Items>");
604 for g in &items {
605 body.push_str("<KeyGroupSummary>");
606 body.push_str("<KeyGroup>");
607 push_key_group_inner(&mut body, g);
608 body.push_str("</KeyGroup>");
609 body.push_str("</KeyGroupSummary>");
610 }
611 body.push_str("</Items>");
612 body.push_str("</KeyGroupList>");
613 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
614 }
615}
616
617impl CloudFrontService {
620 pub(crate) fn create_key_value_store(
621 &self,
622 req: &AwsRequest,
623 ) -> Result<AwsResponse, AwsServiceError> {
624 let parsed: CreateKeyValueStoreRequest = xml_io::from_xml_root(&req.body)
625 .map_err(|e| invalid_argument(format!("invalid CreateKeyValueStore XML: {e}")))?;
626 if parsed.name.is_empty() {
627 return Err(invalid_argument("Name is required"));
628 }
629 let mut state = self.state.write();
630 let account = state
631 .accounts
632 .entry(DEFAULT_ACCOUNT.to_string())
633 .or_default();
634 if account.key_value_stores.contains_key(&parsed.name) {
635 return Err(aws_error(
636 StatusCode::CONFLICT,
637 "EntityAlreadyExists",
638 format!("KeyValueStore {} already exists", parsed.name),
639 ));
640 }
641 let now = Utc::now();
642 let id = Uuid::new_v4().to_string();
643 let etag = generate_id_with_prefix("E");
644 let arn = format!(
645 "arn:aws:cloudfront::{}:key-value-store/{}",
646 DEFAULT_ACCOUNT, id
647 );
648 let stored = StoredKeyValueStore {
649 name: parsed.name.clone(),
650 id,
651 etag: etag.clone(),
652 arn,
653 status: "READY".to_string(),
654 created_time: now,
655 last_modified_time: now,
656 comment: parsed.comment,
657 import_source: parsed.import_source,
658 };
659 account
660 .key_value_stores
661 .insert(parsed.name.clone(), stored.clone());
662 drop(state);
663 let body = render_key_value_store(&stored, "CreateKeyValueStoreResult");
664 Ok(xml_with_etag(StatusCode::CREATED, body, &etag, None))
665 }
666
667 pub(crate) fn describe_key_value_store(
668 &self,
669 route: &Route,
670 ) -> Result<AwsResponse, AwsServiceError> {
671 let name = route_id(route, "KeyValueStore")?;
672 let state = self.state.read();
673 let kvs = state
674 .accounts
675 .get(DEFAULT_ACCOUNT)
676 .and_then(|a| a.key_value_stores.get(&name).cloned())
677 .ok_or_else(|| not_found("KeyValueStore", &name))?;
678 drop(state);
679 let body = render_key_value_store(&kvs, "DescribeKeyValueStoreResult");
680 Ok(xml_with_etag(StatusCode::OK, body, &kvs.etag, None))
681 }
682
683 pub(crate) fn update_key_value_store(
684 &self,
685 req: &AwsRequest,
686 route: &Route,
687 ) -> Result<AwsResponse, AwsServiceError> {
688 let name = route_id(route, "KeyValueStore")?;
689 let if_match = require_if_match(req)?;
690 let parsed: UpdateKeyValueStoreRequest = xml_io::from_xml_root(&req.body)
691 .map_err(|e| invalid_argument(format!("invalid UpdateKeyValueStore XML: {e}")))?;
692 let mut state = self.state.write();
693 let account = state
694 .accounts
695 .get_mut(DEFAULT_ACCOUNT)
696 .ok_or_else(|| not_found("KeyValueStore", &name))?;
697 let kvs = account
698 .key_value_stores
699 .get_mut(&name)
700 .ok_or_else(|| not_found("KeyValueStore", &name))?;
701 if kvs.etag != if_match {
702 return Err(precondition_failed());
703 }
704 kvs.comment = Some(parsed.comment);
705 kvs.etag = generate_id_with_prefix("E");
706 kvs.last_modified_time = Utc::now();
707 let snap = kvs.clone();
708 drop(state);
709 let body = render_key_value_store(&snap, "UpdateKeyValueStoreResult");
710 Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
711 }
712
713 pub(crate) fn delete_key_value_store(
714 &self,
715 req: &AwsRequest,
716 route: &Route,
717 ) -> Result<AwsResponse, AwsServiceError> {
718 let name = route_id(route, "KeyValueStore")?;
719 let if_match = require_if_match(req)?;
720 let mut state = self.state.write();
721 let account = state
722 .accounts
723 .get_mut(DEFAULT_ACCOUNT)
724 .ok_or_else(|| not_found("KeyValueStore", &name))?;
725 let kvs = account
726 .key_value_stores
727 .get(&name)
728 .ok_or_else(|| not_found("KeyValueStore", &name))?;
729 if kvs.etag != if_match {
730 return Err(precondition_failed());
731 }
732 account.key_value_stores.remove(&name);
733 drop(state);
734 Ok(crate::policies::empty(StatusCode::NO_CONTENT))
735 }
736
737 pub(crate) fn list_key_value_stores(
738 &self,
739 _req: &AwsRequest,
740 ) -> Result<AwsResponse, AwsServiceError> {
741 let state = self.state.read();
742 let mut items: Vec<StoredKeyValueStore> = state
743 .accounts
744 .get(DEFAULT_ACCOUNT)
745 .map(|a| a.key_value_stores.values().cloned().collect())
746 .unwrap_or_default();
747 drop(state);
748 items.sort_by(|a, b| a.name.cmp(&b.name));
749
750 let mut body = String::with_capacity(512);
751 body.push_str(XML_DECL);
752 body.push_str(&format!("<KeyValueStoreList xmlns=\"{NS}\">"));
753 body.push_str("<NextMarker></NextMarker>");
754 body.push_str("<MaxItems>100</MaxItems>");
755 body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
756 body.push_str("<Items>");
757 for kvs in &items {
758 body.push_str("<KeyValueStore>");
759 push_kvs_inner(&mut body, kvs);
760 body.push_str("</KeyValueStore>");
761 }
762 body.push_str("</Items>");
763 body.push_str("</KeyValueStoreList>");
764 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
765 }
766}
767
768impl CloudFrontService {
771 pub(crate) fn create_oai(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
772 let cfg: CloudFrontOriginAccessIdentityConfig = xml_io::from_xml_root(&req.body)
773 .map_err(|e| invalid_argument(format!("invalid OAI config XML: {e}")))?;
774 if cfg.caller_reference.is_empty() {
775 return Err(invalid_argument("CallerReference is required"));
776 }
777 let mut state = self.state.write();
778 let account = state
779 .accounts
780 .entry(DEFAULT_ACCOUNT.to_string())
781 .or_default();
782 if let Some(existing) = account
783 .origin_access_identities
784 .values()
785 .find(|o| o.config.caller_reference == cfg.caller_reference)
786 {
787 return Err(aws_error(
788 StatusCode::CONFLICT,
789 "CloudFrontOriginAccessIdentityAlreadyExists",
790 format!("OAI with same CallerReference exists: {}", existing.id),
791 ));
792 }
793 let id = format!(
794 "E{}",
795 Uuid::new_v4()
796 .simple()
797 .to_string()
798 .to_uppercase()
799 .chars()
800 .take(13)
801 .collect::<String>()
802 );
803 let etag = generate_id_with_prefix("E");
804 let canonical = Uuid::new_v4().simple().to_string();
805 let stored = StoredOriginAccessIdentity {
806 id: id.clone(),
807 etag: etag.clone(),
808 s3_canonical_user_id: canonical,
809 config: cfg,
810 };
811 account
812 .origin_access_identities
813 .insert(id.clone(), stored.clone());
814 drop(state);
815 let body = render_oai(&stored, "CloudFrontOriginAccessIdentity");
816 Ok(xml_with_etag(StatusCode::CREATED, body, &etag, Some(&id)))
817 }
818
819 pub(crate) fn get_oai(&self, route: &Route) -> Result<AwsResponse, AwsServiceError> {
820 let id = route_id(route, "CloudFrontOriginAccessIdentity")?;
821 let state = self.state.read();
822 let oai = state
823 .accounts
824 .get(DEFAULT_ACCOUNT)
825 .and_then(|a| a.origin_access_identities.get(&id).cloned())
826 .ok_or_else(|| not_found("CloudFrontOriginAccessIdentity", &id))?;
827 drop(state);
828 let body = render_oai(&oai, "CloudFrontOriginAccessIdentity");
829 Ok(xml_with_etag(StatusCode::OK, body, &oai.etag, None))
830 }
831
832 pub(crate) fn get_oai_config(&self, route: &Route) -> Result<AwsResponse, AwsServiceError> {
833 let id = route_id(route, "CloudFrontOriginAccessIdentity")?;
834 let state = self.state.read();
835 let oai = state
836 .accounts
837 .get(DEFAULT_ACCOUNT)
838 .and_then(|a| a.origin_access_identities.get(&id).cloned())
839 .ok_or_else(|| not_found("CloudFrontOriginAccessIdentity", &id))?;
840 drop(state);
841 let body = config_xml("CloudFrontOriginAccessIdentityConfig", &oai.config)?;
842 Ok(xml_with_etag(StatusCode::OK, body, &oai.etag, None))
843 }
844
845 pub(crate) fn update_oai(
846 &self,
847 req: &AwsRequest,
848 route: &Route,
849 ) -> Result<AwsResponse, AwsServiceError> {
850 let id = route_id(route, "CloudFrontOriginAccessIdentity")?;
851 let if_match = require_if_match(req)?;
852 let cfg: CloudFrontOriginAccessIdentityConfig = xml_io::from_xml_root(&req.body)
853 .map_err(|e| invalid_argument(format!("invalid OAI config XML: {e}")))?;
854 let mut state = self.state.write();
855 let account = state
856 .accounts
857 .get_mut(DEFAULT_ACCOUNT)
858 .ok_or_else(|| not_found("CloudFrontOriginAccessIdentity", &id))?;
859 let oai = account
860 .origin_access_identities
861 .get_mut(&id)
862 .ok_or_else(|| not_found("CloudFrontOriginAccessIdentity", &id))?;
863 if oai.etag != if_match {
864 return Err(precondition_failed());
865 }
866 if oai.config.caller_reference != cfg.caller_reference {
867 return Err(invalid_argument(
868 "CallerReference cannot change on UpdateCloudFrontOriginAccessIdentity",
869 ));
870 }
871 oai.config = cfg;
872 oai.etag = generate_id_with_prefix("E");
873 let snap = oai.clone();
874 drop(state);
875 let body = render_oai(&snap, "CloudFrontOriginAccessIdentity");
876 Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
877 }
878
879 pub(crate) fn delete_oai(
880 &self,
881 req: &AwsRequest,
882 route: &Route,
883 ) -> Result<AwsResponse, AwsServiceError> {
884 let id = route_id(route, "CloudFrontOriginAccessIdentity")?;
885 let if_match = require_if_match(req)?;
886 let mut state = self.state.write();
887 let account = state
888 .accounts
889 .get_mut(DEFAULT_ACCOUNT)
890 .ok_or_else(|| not_found("CloudFrontOriginAccessIdentity", &id))?;
891 let oai = account
892 .origin_access_identities
893 .get(&id)
894 .ok_or_else(|| not_found("CloudFrontOriginAccessIdentity", &id))?;
895 if oai.etag != if_match {
896 return Err(precondition_failed());
897 }
898 account.origin_access_identities.remove(&id);
899 drop(state);
900 Ok(crate::policies::empty(StatusCode::NO_CONTENT))
901 }
902
903 pub(crate) fn list_oai(&self, _req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
904 let state = self.state.read();
905 let mut items: Vec<StoredOriginAccessIdentity> = state
906 .accounts
907 .get(DEFAULT_ACCOUNT)
908 .map(|a| a.origin_access_identities.values().cloned().collect())
909 .unwrap_or_default();
910 drop(state);
911 items.sort_by(|a, b| a.id.cmp(&b.id));
912
913 let mut body = String::with_capacity(512);
914 body.push_str(XML_DECL);
915 body.push_str(&format!(
916 "<CloudFrontOriginAccessIdentityList xmlns=\"{NS}\">"
917 ));
918 body.push_str("<Marker></Marker>");
919 body.push_str("<MaxItems>100</MaxItems>");
920 body.push_str("<IsTruncated>false</IsTruncated>");
921 body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
922 body.push_str("<Items>");
923 for oai in &items {
924 body.push_str("<CloudFrontOriginAccessIdentitySummary>");
925 body.push_str(&format!("<Id>{}</Id>", esc(&oai.id)));
926 body.push_str(&format!(
927 "<S3CanonicalUserId>{}</S3CanonicalUserId>",
928 esc(&oai.s3_canonical_user_id)
929 ));
930 body.push_str(&format!("<Comment>{}</Comment>", esc(&oai.config.comment)));
931 body.push_str("</CloudFrontOriginAccessIdentitySummary>");
932 }
933 body.push_str("</Items>");
934 body.push_str("</CloudFrontOriginAccessIdentityList>");
935 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
936 }
937}
938
939impl CloudFrontService {
942 pub(crate) fn create_monitoring_subscription(
943 &self,
944 req: &AwsRequest,
945 route: &Route,
946 ) -> Result<AwsResponse, AwsServiceError> {
947 let dist_id = route_id(route, "Distribution")?;
948 let parsed: MonitoringSubscriptionBody = xml_io::from_xml_root(&req.body)
949 .map_err(|e| invalid_argument(format!("invalid MonitoringSubscription XML: {e}")))?;
950 let mut state = self.state.write();
951 let account = state
952 .accounts
953 .entry(DEFAULT_ACCOUNT.to_string())
954 .or_default();
955 if !account.distributions.contains_key(&dist_id) {
956 return Err(not_found("Distribution", &dist_id));
957 }
958 let stored = StoredMonitoringSubscription {
959 distribution_id: dist_id.clone(),
960 config: parsed.realtime_metrics_subscription_config,
961 };
962 account
963 .monitoring_subscriptions
964 .insert(dist_id.clone(), stored.clone());
965 drop(state);
966 let body = render_monitoring(&stored);
967 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
968 }
969
970 pub(crate) fn get_monitoring_subscription(
971 &self,
972 route: &Route,
973 ) -> Result<AwsResponse, AwsServiceError> {
974 let dist_id = route_id(route, "Distribution")?;
975 let state = self.state.read();
976 let m = state
977 .accounts
978 .get(DEFAULT_ACCOUNT)
979 .and_then(|a| a.monitoring_subscriptions.get(&dist_id).cloned())
980 .ok_or_else(|| {
981 aws_error(
982 StatusCode::NOT_FOUND,
983 "NoSuchMonitoringSubscription",
984 format!("No monitoring subscription for distribution {dist_id}"),
985 )
986 })?;
987 drop(state);
988 let body = render_monitoring(&m);
989 Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
990 }
991
992 pub(crate) fn delete_monitoring_subscription(
993 &self,
994 route: &Route,
995 ) -> Result<AwsResponse, AwsServiceError> {
996 let dist_id = route_id(route, "Distribution")?;
997 let mut state = self.state.write();
998 let account = state
999 .accounts
1000 .get_mut(DEFAULT_ACCOUNT)
1001 .ok_or_else(|| not_found("Distribution", &dist_id))?;
1002 if account.monitoring_subscriptions.remove(&dist_id).is_none() {
1003 return Err(aws_error(
1004 StatusCode::NOT_FOUND,
1005 "NoSuchMonitoringSubscription",
1006 format!("No monitoring subscription for distribution {dist_id}"),
1007 ));
1008 }
1009 drop(state);
1010 Ok(crate::policies::empty(StatusCode::NO_CONTENT))
1011 }
1012}
1013
1014#[derive(Debug, serde::Deserialize)]
1017#[serde(rename_all = "PascalCase")]
1018struct CreateFunctionRequest {
1019 name: String,
1020 function_config: FunctionConfig,
1021 function_code: String,
1023}
1024
1025#[derive(Debug, serde::Deserialize)]
1026#[serde(rename_all = "PascalCase")]
1027struct UpdateFunctionRequest {
1028 function_config: FunctionConfig,
1029 function_code: String,
1030}
1031
1032#[derive(Debug, serde::Deserialize)]
1033#[serde(rename_all = "PascalCase")]
1034struct CreateKeyValueStoreRequest {
1035 name: String,
1036 #[serde(default)]
1037 comment: Option<String>,
1038 #[serde(default)]
1039 import_source: Option<ImportSource>,
1040}
1041
1042#[derive(Debug, serde::Deserialize)]
1043#[serde(rename_all = "PascalCase")]
1044struct UpdateKeyValueStoreRequest {
1045 comment: String,
1046}
1047
1048fn config_xml<T: serde::Serialize>(root: &str, cfg: &T) -> Result<String, AwsServiceError> {
1049 let inner = quick_xml::se::to_string_with_root(root, cfg).map_err(|e| {
1050 aws_error(
1051 StatusCode::INTERNAL_SERVER_ERROR,
1052 "InternalError",
1053 format!("xml encode failed: {e}"),
1054 )
1055 })?;
1056 let stamped = inner.replacen(
1057 &format!("<{root}>"),
1058 &format!("<{root} xmlns=\"{NS}\">", NS = crate::NAMESPACE),
1059 1,
1060 );
1061 Ok(format!("{XML_DECL}{stamped}"))
1062}
1063
1064fn parse_stage_query(query: &str) -> Option<String> {
1065 use std::collections::HashMap;
1066 let pairs: HashMap<&str, &str> = query.split('&').filter_map(|p| p.split_once('=')).collect();
1067 pairs.get("Stage").map(|s| s.to_string())
1068}
1069
1070fn stage_view(f: &StoredFunction, stage: &Option<String>) -> StoredFunction {
1071 let mut clone = f.clone();
1072 if stage.as_deref() == Some("LIVE") {
1073 clone.stage = "LIVE".into();
1074 }
1075 clone
1076}
1077
1078fn render_function_summary(f: &StoredFunction, _root: &str) -> String {
1079 let mut out = String::with_capacity(512);
1082 out.push_str(XML_DECL);
1083 out.push_str(&render_function_summary_inner_with_ns(f));
1084 out
1085}
1086
1087fn render_function_summary_inner_with_ns(f: &StoredFunction) -> String {
1088 let mut out = String::with_capacity(512);
1089 out.push_str(&format!("<FunctionSummary xmlns=\"{NS}\">"));
1090 out.push_str(&render_function_summary_body(f));
1091 out.push_str("</FunctionSummary>");
1092 out
1093}
1094
1095fn render_function_summary_inner(f: &StoredFunction) -> String {
1096 let mut out = String::with_capacity(512);
1097 out.push_str("<FunctionSummary>");
1098 out.push_str(&render_function_summary_body(f));
1099 out.push_str("</FunctionSummary>");
1100 out
1101}
1102
1103fn render_function_summary_body(f: &StoredFunction) -> String {
1104 let mut out = String::with_capacity(512);
1105 out.push_str(&format!("<Name>{}</Name>", esc(&f.name)));
1106 out.push_str(&format!("<Status>{}</Status>", esc(&f.status)));
1107 out.push_str("<FunctionConfig>");
1108 if let Some(c) = &f.config.comment {
1109 out.push_str(&format!("<Comment>{}</Comment>", esc(c)));
1110 } else {
1111 out.push_str("<Comment></Comment>");
1112 }
1113 out.push_str(&format!("<Runtime>{}</Runtime>", esc(&f.config.runtime)));
1114 out.push_str("</FunctionConfig>");
1115 out.push_str("<FunctionMetadata>");
1116 out.push_str(&format!(
1117 "<FunctionARN>{}</FunctionARN>",
1118 esc(&f.function_arn)
1119 ));
1120 out.push_str(&format!("<Stage>{}</Stage>", esc(&f.stage)));
1121 out.push_str(&format!(
1122 "<CreatedTime>{}</CreatedTime>",
1123 rfc3339(&f.created_time)
1124 ));
1125 out.push_str(&format!(
1126 "<LastModifiedTime>{}</LastModifiedTime>",
1127 rfc3339(&f.last_modified_time)
1128 ));
1129 out.push_str("</FunctionMetadata>");
1130 out
1131}
1132
1133fn render_public_key(p: &StoredPublicKey, root: &str) -> String {
1134 let mut out = String::with_capacity(512);
1135 out.push_str(XML_DECL);
1136 out.push_str(&format!("<{root} xmlns=\"{NS}\">"));
1137 out.push_str(&format!("<Id>{}</Id>", esc(&p.id)));
1138 out.push_str(&format!(
1139 "<CreatedTime>{}</CreatedTime>",
1140 rfc3339(&p.created_time)
1141 ));
1142 out.push_str("<PublicKeyConfig>");
1143 out.push_str(&format!(
1144 "<CallerReference>{}</CallerReference>",
1145 esc(&p.config.caller_reference)
1146 ));
1147 out.push_str(&format!("<Name>{}</Name>", esc(&p.config.name)));
1148 out.push_str(&format!(
1149 "<EncodedKey>{}</EncodedKey>",
1150 esc(&p.config.encoded_key)
1151 ));
1152 if let Some(c) = &p.config.comment {
1153 out.push_str(&format!("<Comment>{}</Comment>", esc(c)));
1154 }
1155 out.push_str("</PublicKeyConfig>");
1156 out.push_str(&format!("</{root}>"));
1157 out
1158}
1159
1160fn push_key_group_inner(out: &mut String, g: &StoredKeyGroup) {
1161 out.push_str(&format!("<Id>{}</Id>", esc(&g.id)));
1162 out.push_str(&format!(
1163 "<LastModifiedTime>{}</LastModifiedTime>",
1164 rfc3339(&g.last_modified_time)
1165 ));
1166 out.push_str("<KeyGroupConfig>");
1167 out.push_str(&format!("<Name>{}</Name>", esc(&g.config.name)));
1168 out.push_str("<Items>");
1169 for k in &g.config.items.public_key {
1170 out.push_str(&format!("<PublicKey>{}</PublicKey>", esc(k)));
1171 }
1172 out.push_str("</Items>");
1173 if let Some(c) = &g.config.comment {
1174 out.push_str(&format!("<Comment>{}</Comment>", esc(c)));
1175 }
1176 out.push_str("</KeyGroupConfig>");
1177}
1178
1179fn render_key_group(g: &StoredKeyGroup, root: &str) -> String {
1180 let mut out = String::with_capacity(512);
1181 out.push_str(XML_DECL);
1182 out.push_str(&format!("<{root} xmlns=\"{NS}\">"));
1183 push_key_group_inner(&mut out, g);
1184 out.push_str(&format!("</{root}>"));
1185 out
1186}
1187
1188fn push_kvs_inner(out: &mut String, kvs: &StoredKeyValueStore) {
1189 out.push_str(&format!("<Name>{}</Name>", esc(&kvs.name)));
1190 out.push_str(&format!("<Id>{}</Id>", esc(&kvs.id)));
1191 out.push_str(&format!(
1192 "<Comment>{}</Comment>",
1193 esc(kvs.comment.as_deref().unwrap_or(""))
1194 ));
1195 out.push_str(&format!("<ARN>{}</ARN>", esc(&kvs.arn)));
1196 out.push_str(&format!("<Status>{}</Status>", esc(&kvs.status)));
1197 out.push_str(&format!(
1198 "<LastModifiedTime>{}</LastModifiedTime>",
1199 rfc3339(&kvs.last_modified_time)
1200 ));
1201}
1202
1203fn render_key_value_store(kvs: &StoredKeyValueStore, _root: &str) -> String {
1204 let mut out = String::with_capacity(512);
1206 out.push_str(XML_DECL);
1207 out.push_str(&format!("<KeyValueStore xmlns=\"{NS}\">"));
1208 push_kvs_inner(&mut out, kvs);
1209 out.push_str("</KeyValueStore>");
1210 out
1211}
1212
1213fn render_oai(oai: &StoredOriginAccessIdentity, root: &str) -> String {
1214 let mut out = String::with_capacity(512);
1215 out.push_str(XML_DECL);
1216 out.push_str(&format!("<{root} xmlns=\"{NS}\">"));
1217 out.push_str(&format!("<Id>{}</Id>", esc(&oai.id)));
1218 out.push_str(&format!(
1219 "<S3CanonicalUserId>{}</S3CanonicalUserId>",
1220 esc(&oai.s3_canonical_user_id)
1221 ));
1222 out.push_str("<CloudFrontOriginAccessIdentityConfig>");
1223 out.push_str(&format!(
1224 "<CallerReference>{}</CallerReference>",
1225 esc(&oai.config.caller_reference)
1226 ));
1227 out.push_str(&format!("<Comment>{}</Comment>", esc(&oai.config.comment)));
1228 out.push_str("</CloudFrontOriginAccessIdentityConfig>");
1229 out.push_str(&format!("</{root}>"));
1230 out
1231}
1232
1233fn render_monitoring(m: &StoredMonitoringSubscription) -> String {
1234 let mut out = String::with_capacity(256);
1235 out.push_str(XML_DECL);
1236 out.push_str(&format!("<MonitoringSubscription xmlns=\"{NS}\">"));
1237 out.push_str("<RealtimeMetricsSubscriptionConfig>");
1238 out.push_str(&format!(
1239 "<RealtimeMetricsSubscriptionStatus>{}</RealtimeMetricsSubscriptionStatus>",
1240 esc(&m.config.realtime_metrics_subscription_status)
1241 ));
1242 out.push_str("</RealtimeMetricsSubscriptionConfig>");
1243 out.push_str("</MonitoringSubscription>");
1244 out
1245}