Skip to main content

awsim_lambda/
lib.rs

1pub mod authz;
2mod error;
3mod executor;
4mod operations;
5pub mod state;
6mod util;
7
8pub use authz::LambdaResourcePolicyLookup;
9
10use std::sync::Arc;
11
12use async_trait::async_trait;
13use awsim_core::{
14    AccountRegionStore, AwsError, Protocol, RequestContext, RouteDefinition, ServiceHandler,
15};
16use serde_json::Value;
17use tracing::debug;
18
19use state::LambdaState;
20
21pub struct LambdaService {
22    store: AccountRegionStore<LambdaState>,
23}
24
25impl LambdaService {
26    pub fn new() -> Self {
27        Self {
28            store: AccountRegionStore::new(),
29        }
30    }
31
32    fn get_state(&self, ctx: &RequestContext) -> Arc<LambdaState> {
33        self.store.get(&ctx.account_id, &ctx.region)
34    }
35
36    pub fn store(&self) -> AccountRegionStore<LambdaState> {
37        self.store.clone()
38    }
39}
40
41impl Default for LambdaService {
42    fn default() -> Self {
43        Self::new()
44    }
45}
46
47#[async_trait]
48impl ServiceHandler for LambdaService {
49    fn service_name(&self) -> &str {
50        "lambda"
51    }
52
53    fn signing_name(&self) -> &str {
54        "lambda"
55    }
56
57    fn protocol(&self) -> Protocol {
58        Protocol::RestJson1
59    }
60
61    fn routes(&self) -> Vec<RouteDefinition> {
62        vec![
63            // Functions
64            RouteDefinition {
65                method: "POST",
66                path_pattern: "/2015-03-31/functions",
67                operation: "CreateFunction",
68                required_query_param: None,
69            },
70            RouteDefinition {
71                method: "GET",
72                path_pattern: "/2015-03-31/functions",
73                operation: "ListFunctions",
74                required_query_param: None,
75            },
76            RouteDefinition {
77                method: "GET",
78                path_pattern: "/2015-03-31/functions/{FunctionName}",
79                operation: "GetFunction",
80                required_query_param: None,
81            },
82            RouteDefinition {
83                method: "DELETE",
84                path_pattern: "/2015-03-31/functions/{FunctionName}",
85                operation: "DeleteFunction",
86                required_query_param: None,
87            },
88            RouteDefinition {
89                method: "GET",
90                path_pattern: "/2015-03-31/functions/{FunctionName}/configuration",
91                operation: "GetFunctionConfiguration",
92                required_query_param: None,
93            },
94            RouteDefinition {
95                method: "PUT",
96                path_pattern: "/2015-03-31/functions/{FunctionName}/code",
97                operation: "UpdateFunctionCode",
98                required_query_param: None,
99            },
100            RouteDefinition {
101                method: "PUT",
102                path_pattern: "/2015-03-31/functions/{FunctionName}/configuration",
103                operation: "UpdateFunctionConfiguration",
104                required_query_param: None,
105            },
106            RouteDefinition {
107                method: "POST",
108                path_pattern: "/2015-03-31/functions/{FunctionName}/invocations",
109                operation: "Invoke",
110                required_query_param: None,
111            },
112            // Versions
113            RouteDefinition {
114                method: "POST",
115                path_pattern: "/2015-03-31/functions/{FunctionName}/versions",
116                operation: "PublishVersion",
117                required_query_param: None,
118            },
119            RouteDefinition {
120                method: "GET",
121                path_pattern: "/2015-03-31/functions/{FunctionName}/versions",
122                operation: "ListVersionsByFunction",
123                required_query_param: None,
124            },
125            // Aliases
126            RouteDefinition {
127                method: "POST",
128                path_pattern: "/2015-03-31/functions/{FunctionName}/aliases",
129                operation: "CreateAlias",
130                required_query_param: None,
131            },
132            RouteDefinition {
133                method: "GET",
134                path_pattern: "/2015-03-31/functions/{FunctionName}/aliases",
135                operation: "ListAliases",
136                required_query_param: None,
137            },
138            RouteDefinition {
139                method: "GET",
140                path_pattern: "/2015-03-31/functions/{FunctionName}/aliases/{Name}",
141                operation: "GetAlias",
142                required_query_param: None,
143            },
144            RouteDefinition {
145                method: "DELETE",
146                path_pattern: "/2015-03-31/functions/{FunctionName}/aliases/{Name}",
147                operation: "DeleteAlias",
148                required_query_param: None,
149            },
150            // Event Source Mappings
151            RouteDefinition {
152                method: "POST",
153                path_pattern: "/2015-03-31/event-source-mappings",
154                operation: "CreateEventSourceMapping",
155                required_query_param: None,
156            },
157            RouteDefinition {
158                method: "GET",
159                path_pattern: "/2015-03-31/event-source-mappings",
160                operation: "ListEventSourceMappings",
161                required_query_param: None,
162            },
163            RouteDefinition {
164                method: "GET",
165                path_pattern: "/2015-03-31/event-source-mappings/{UUID}",
166                operation: "GetEventSourceMapping",
167                required_query_param: None,
168            },
169            RouteDefinition {
170                method: "DELETE",
171                path_pattern: "/2015-03-31/event-source-mappings/{UUID}",
172                operation: "DeleteEventSourceMapping",
173                required_query_param: None,
174            },
175            // Layers
176            RouteDefinition {
177                method: "POST",
178                path_pattern: "/2018-10-31/layers/{LayerName}/versions",
179                operation: "PublishLayerVersion",
180                required_query_param: None,
181            },
182            RouteDefinition {
183                method: "GET",
184                path_pattern: "/2018-10-31/layers",
185                operation: "ListLayers",
186                required_query_param: None,
187            },
188            RouteDefinition {
189                method: "GET",
190                path_pattern: "/2018-10-31/layers/{LayerName}/versions",
191                operation: "ListLayerVersions",
192                required_query_param: None,
193            },
194            RouteDefinition {
195                method: "DELETE",
196                path_pattern: "/2018-10-31/layers/{LayerName}/versions/{VersionNumber}",
197                operation: "DeleteLayerVersion",
198                required_query_param: None,
199            },
200            // Function URL Configs
201            RouteDefinition {
202                method: "POST",
203                path_pattern: "/2021-10-31/functions/{FunctionName}/url",
204                operation: "CreateFunctionUrlConfig",
205                required_query_param: None,
206            },
207            RouteDefinition {
208                method: "GET",
209                path_pattern: "/2021-10-31/functions/{FunctionName}/url",
210                operation: "GetFunctionUrlConfig",
211                required_query_param: None,
212            },
213            RouteDefinition {
214                method: "DELETE",
215                path_pattern: "/2021-10-31/functions/{FunctionName}/url",
216                operation: "DeleteFunctionUrlConfig",
217                required_query_param: None,
218            },
219            RouteDefinition {
220                method: "GET",
221                path_pattern: "/2021-10-31/functions/{FunctionName}/urls",
222                operation: "ListFunctionUrlConfigs",
223                required_query_param: None,
224            },
225            // Tags
226            RouteDefinition {
227                method: "GET",
228                path_pattern: "/2017-03-31/tags/{Resource}",
229                operation: "ListTags",
230                required_query_param: None,
231            },
232            RouteDefinition {
233                method: "POST",
234                path_pattern: "/2017-03-31/tags/{Resource}",
235                operation: "TagResource",
236                required_query_param: None,
237            },
238            RouteDefinition {
239                method: "DELETE",
240                path_pattern: "/2017-03-31/tags/{Resource}",
241                operation: "UntagResource",
242                required_query_param: None,
243            },
244            // Policy / Permissions
245            RouteDefinition {
246                method: "GET",
247                path_pattern: "/2015-03-31/functions/{FunctionName}/policy",
248                operation: "GetPolicy",
249                required_query_param: None,
250            },
251            RouteDefinition {
252                method: "POST",
253                path_pattern: "/2015-03-31/functions/{FunctionName}/policy",
254                operation: "AddPermission",
255                required_query_param: None,
256            },
257            RouteDefinition {
258                method: "DELETE",
259                path_pattern: "/2015-03-31/functions/{FunctionName}/policy/{StatementId}",
260                operation: "RemovePermission",
261                required_query_param: None,
262            },
263            // Account Settings
264            RouteDefinition {
265                method: "GET",
266                path_pattern: "/2016-08-19/account-settings",
267                operation: "GetAccountSettings",
268                required_query_param: None,
269            },
270            // Event Invoke Configs
271            RouteDefinition {
272                method: "PUT",
273                path_pattern: "/2019-09-25/functions/{FunctionName}/event-invoke-config",
274                operation: "PutFunctionEventInvokeConfig",
275                required_query_param: None,
276            },
277            RouteDefinition {
278                method: "GET",
279                path_pattern: "/2019-09-25/functions/{FunctionName}/event-invoke-config",
280                operation: "GetFunctionEventInvokeConfig",
281                required_query_param: None,
282            },
283            RouteDefinition {
284                method: "DELETE",
285                path_pattern: "/2019-09-25/functions/{FunctionName}/event-invoke-config",
286                operation: "DeleteFunctionEventInvokeConfig",
287                required_query_param: None,
288            },
289            RouteDefinition {
290                method: "POST",
291                path_pattern: "/2019-09-25/functions/{FunctionName}/event-invoke-config",
292                operation: "UpdateFunctionEventInvokeConfig",
293                required_query_param: None,
294            },
295            RouteDefinition {
296                method: "GET",
297                path_pattern: "/2019-09-25/functions/{FunctionName}/event-invoke-config/list",
298                operation: "ListFunctionEventInvokeConfigs",
299                required_query_param: None,
300            },
301        ]
302    }
303
304    async fn handle(
305        &self,
306        operation: &str,
307        input: Value,
308        ctx: &RequestContext,
309    ) -> Result<Value, AwsError> {
310        debug!(operation, "Lambda request");
311        let state = self.get_state(ctx);
312
313        match operation {
314            // Functions
315            "CreateFunction" => operations::functions::create_function(&state, &input, ctx),
316            "GetFunction" => operations::functions::get_function(&state, &input, ctx),
317            "GetFunctionConfiguration" => {
318                operations::functions::get_function_configuration(&state, &input, ctx)
319            }
320            "DeleteFunction" => operations::functions::delete_function(&state, &input),
321            "ListFunctions" => operations::functions::list_functions(&state, &input, ctx),
322            "UpdateFunctionCode" => {
323                operations::functions::update_function_code(&state, &input, ctx)
324            }
325            "UpdateFunctionConfiguration" => {
326                operations::functions::update_function_configuration(&state, &input, ctx)
327            }
328
329            // Invocations
330            "Invoke" => operations::invocations::invoke(&state, &input, ctx),
331
332            // Versions
333            "PublishVersion" => operations::versions::publish_version(&state, &input, ctx),
334            "ListVersionsByFunction" => {
335                operations::versions::list_versions_by_function(&state, &input, ctx)
336            }
337
338            // Aliases
339            "CreateAlias" => operations::aliases::create_alias(&state, &input, ctx),
340            "GetAlias" => operations::aliases::get_alias(&state, &input, ctx),
341            "DeleteAlias" => operations::aliases::delete_alias(&state, &input, ctx),
342            "ListAliases" => operations::aliases::list_aliases(&state, &input, ctx),
343
344            // Event Source Mappings
345            "CreateEventSourceMapping" => {
346                operations::event_source_mappings::create_event_source_mapping(&state, &input, ctx)
347            }
348            "GetEventSourceMapping" => {
349                operations::event_source_mappings::get_event_source_mapping(&state, &input, ctx)
350            }
351            "DeleteEventSourceMapping" => {
352                operations::event_source_mappings::delete_event_source_mapping(&state, &input, ctx)
353            }
354            "ListEventSourceMappings" => {
355                operations::event_source_mappings::list_event_source_mappings(&state, &input, ctx)
356            }
357
358            // Layers
359            "PublishLayerVersion" => operations::layers::publish_layer_version(&state, &input, ctx),
360            "ListLayers" => operations::layers::list_layers(&state, &input, ctx),
361            "ListLayerVersions" => operations::layers::list_layer_versions(&state, &input, ctx),
362            "DeleteLayerVersion" => operations::layers::delete_layer_version(&state, &input, ctx),
363
364            // Function URL Configs
365            "CreateFunctionUrlConfig" => {
366                operations::url_configs::create_function_url_config(&state, &input, ctx)
367            }
368            "GetFunctionUrlConfig" => {
369                operations::url_configs::get_function_url_config(&state, &input, ctx)
370            }
371            "DeleteFunctionUrlConfig" => {
372                operations::url_configs::delete_function_url_config(&state, &input, ctx)
373            }
374            "ListFunctionUrlConfigs" => {
375                operations::url_configs::list_function_url_configs(&state, &input, ctx)
376            }
377
378            // Tags
379            "TagResource" => operations::tags::tag_resource(&state, &input, ctx),
380            "UntagResource" => operations::tags::untag_resource(&state, &input, ctx),
381            "ListTags" => operations::tags::list_tags(&state, &input, ctx),
382
383            // Policy / Permissions
384            "GetPolicy" => operations::permissions::get_policy(&state, &input, ctx),
385            "AddPermission" => operations::permissions::add_permission(&state, &input, ctx),
386            "RemovePermission" => operations::permissions::remove_permission(&state, &input, ctx),
387
388            // Account Settings
389            "GetAccountSettings" => {
390                operations::permissions::get_account_settings(&state, &input, ctx)
391            }
392
393            // Event Invoke Configs
394            "PutFunctionEventInvokeConfig" => {
395                operations::event_invoke_configs::put_function_event_invoke_config(
396                    &state, &input, ctx,
397                )
398            }
399            "GetFunctionEventInvokeConfig" => {
400                operations::event_invoke_configs::get_function_event_invoke_config(
401                    &state, &input, ctx,
402                )
403            }
404            "UpdateFunctionEventInvokeConfig" => {
405                operations::event_invoke_configs::update_function_event_invoke_config(
406                    &state, &input, ctx,
407                )
408            }
409            "DeleteFunctionEventInvokeConfig" => {
410                operations::event_invoke_configs::delete_function_event_invoke_config(
411                    &state, &input, ctx,
412                )
413            }
414            "ListFunctionEventInvokeConfigs" => {
415                operations::event_invoke_configs::list_function_event_invoke_configs(
416                    &state, &input, ctx,
417                )
418            }
419
420            _ => Err(AwsError::unknown_operation(operation)),
421        }
422    }
423
424    fn iam_action(&self, operation: &str) -> Option<String> {
425        match operation {
426            "CreateFunction"
427            | "GetFunction"
428            | "GetFunctionConfiguration"
429            | "DeleteFunction"
430            | "ListFunctions"
431            | "UpdateFunctionCode"
432            | "UpdateFunctionConfiguration"
433            | "Invoke"
434            | "InvokeFunction"
435            | "InvokeAsync"
436            | "PublishVersion"
437            | "ListVersionsByFunction"
438            | "CreateAlias"
439            | "GetAlias"
440            | "DeleteAlias"
441            | "ListAliases"
442            | "UpdateAlias"
443            | "CreateEventSourceMapping"
444            | "GetEventSourceMapping"
445            | "DeleteEventSourceMapping"
446            | "ListEventSourceMappings"
447            | "UpdateEventSourceMapping"
448            | "PublishLayerVersion"
449            | "ListLayers"
450            | "ListLayerVersions"
451            | "DeleteLayerVersion"
452            | "GetLayerVersion"
453            | "CreateFunctionUrlConfig"
454            | "GetFunctionUrlConfig"
455            | "DeleteFunctionUrlConfig"
456            | "ListFunctionUrlConfigs"
457            | "UpdateFunctionUrlConfig"
458            | "TagResource"
459            | "UntagResource"
460            | "ListTags"
461            | "GetPolicy"
462            | "AddPermission"
463            | "RemovePermission"
464            | "GetAccountSettings"
465            | "PutFunctionEventInvokeConfig"
466            | "GetFunctionEventInvokeConfig"
467            | "UpdateFunctionEventInvokeConfig"
468            | "DeleteFunctionEventInvokeConfig"
469            | "ListFunctionEventInvokeConfigs" => Some(format!("lambda:{operation}")),
470            _ => None,
471        }
472    }
473
474    fn iam_resource(&self, operation: &str, input: &Value, ctx: &RequestContext) -> Option<String> {
475        let prefix = format!("arn:aws:lambda:{}:{}", ctx.region, ctx.account_id);
476        match operation {
477            "ListFunctions"
478            | "ListEventSourceMappings"
479            | "ListLayers"
480            | "GetAccountSettings"
481            | "CreateFunction"
482            | "CreateEventSourceMapping" => Some("*".to_string()),
483            "TagResource" | "UntagResource" | "ListTags" => input
484                .get("Resource")
485                .and_then(|v| v.as_str())
486                .map(|s| s.to_string()),
487            "GetEventSourceMapping" | "DeleteEventSourceMapping" | "UpdateEventSourceMapping" => {
488                input
489                    .get("UUID")
490                    .and_then(|v| v.as_str())
491                    .map(|uuid| format!("{prefix}:event-source-mapping:{uuid}"))
492            }
493            "PublishLayerVersion" | "ListLayerVersions" => input
494                .get("LayerName")
495                .and_then(|v| v.as_str())
496                .map(|name| format!("{prefix}:layer:{name}")),
497            "GetLayerVersion" | "DeleteLayerVersion" => {
498                let name = input.get("LayerName").and_then(|v| v.as_str())?;
499                let version = input
500                    .get("VersionNumber")
501                    .and_then(|v| v.as_i64())
502                    .unwrap_or(0);
503                Some(format!("{prefix}:layer:{name}:{version}"))
504            }
505            _ => {
506                let name = input.get("FunctionName").and_then(|v| v.as_str())?;
507                if name.starts_with("arn:") {
508                    Some(name.to_string())
509                } else {
510                    Some(format!("{prefix}:function:{name}"))
511                }
512            }
513        }
514    }
515}