feat: add post confirmation lambda hook
This commit is contained in:
1 parent
8d9faf126d
commit
af1250350a
5 files changed
+152
No files matched your search
@@ -0,0 +1 @@
|
||||
target
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "signup-lambda"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
lambda_runtime = "1.0.1"
|
||||
aws-sdk-cognitoidentityprovider = "1.105.0"
|
||||
serde = "1"
|
||||
tokio = { version = "1", features = ["macros"] }
|
||||
aws-config = "1.8.12"
|
||||
@@ -0,0 +1,52 @@
|
||||
# Introduction
|
||||
|
||||
signup-lambda is a Rust project that implements an AWS Lambda function in Rust.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Rust](https://www.rust-lang.org/tools/install)
|
||||
- [Cargo Lambda](https://www.cargo-lambda.info/guide/installation.html)
|
||||
|
||||
## Building
|
||||
|
||||
To build the project for production, run `cargo lambda build --release`. Remove the `--release` flag to build for development.
|
||||
|
||||
Read more about building your lambda function in [the Cargo Lambda documentation](https://www.cargo-lambda.info/commands/build.html).
|
||||
|
||||
## Testing
|
||||
|
||||
You can run regular Rust unit tests with `cargo test`.
|
||||
|
||||
If you want to run integration tests locally, you can use the `cargo lambda watch` and `cargo lambda invoke` commands to do it.
|
||||
|
||||
First, run `cargo lambda watch` to start a local server. When you make changes to the code, the server will automatically restart.
|
||||
|
||||
Second, you'll need a way to pass the event data to the lambda function.
|
||||
|
||||
You can use the existent [event payloads](https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/lambda-events/src/fixtures) in the Rust Runtime repository if your lambda function is using one of the supported event types.
|
||||
|
||||
You can use those examples directly with the `--data-example` flag, where the value is the name of the file in the [lambda-events](https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/lambda-events/src/fixtures) repository without the `example_` prefix and the `.json` extension.
|
||||
|
||||
```bash
|
||||
cargo lambda invoke --data-example apigw-request
|
||||
```
|
||||
|
||||
For generic events, where you define the event data structure, you can create a JSON file with the data you want to test with. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"command": "test"
|
||||
}
|
||||
```
|
||||
|
||||
Then, run `cargo lambda invoke --data-file ./data.json` to invoke the function with the data in `data.json`.
|
||||
|
||||
|
||||
Read more about running the local server in [the Cargo Lambda documentation for the `watch` command](https://www.cargo-lambda.info/commands/watch.html).
|
||||
Read more about invoking the function in [the Cargo Lambda documentation for the `invoke` command](https://www.cargo-lambda.info/commands/invoke.html).
|
||||
|
||||
## Deploying
|
||||
|
||||
To deploy the project, run `cargo lambda deploy`. This will create an IAM role and a Lambda function in your AWS account.
|
||||
|
||||
Read more about deploying your lambda function in [the Cargo Lambda documentation](https://www.cargo-lambda.info/commands/deploy.html).
|
||||
@@ -0,0 +1,76 @@
|
||||
use lambda_runtime::LambdaEvent;
|
||||
use aws_sdk_cognitoidentityprovider::Client;
|
||||
use aws_config::BehaviorVersion;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::collections::HashMap;
|
||||
use aws_sdk_cognitoidentityprovider::error::ProvideErrorMetadata;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct PostConfirmationEvent {
|
||||
version: String,
|
||||
trigger_source: String,
|
||||
region: String,
|
||||
user_pool_id: String,
|
||||
user_name: String,
|
||||
caller_context: CallerContext,
|
||||
request: Request,
|
||||
response: Response
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct CallerContext {
|
||||
client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Request {
|
||||
user_attributes: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct Response {}
|
||||
|
||||
fn get_error_message(err: impl ProvideErrorMetadata) -> String {
|
||||
err.message().unwrap_or("An unknown error occurred").to_string()
|
||||
}
|
||||
|
||||
async fn add_user_to_group(provider: Client, user_id: String, group_name: impl Into<String>) -> Result<(), String> {
|
||||
let group_name = group_name.into();
|
||||
println!("Adding user {} to group {}", user_id, group_name);
|
||||
match provider
|
||||
.admin_add_user_to_group()
|
||||
.user_pool_id("us-east-1_Lfjuy5zaM")
|
||||
.username(user_id)
|
||||
.group_name(group_name)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => Err(get_error_message(err)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn handler(event: LambdaEvent<PostConfirmationEvent>) -> Result<PostConfirmationEvent, String> {
|
||||
println!("Found event: {:?}", event.payload);
|
||||
|
||||
let user_id = event.payload.request.user_attributes.get("sub").unwrap().to_string();
|
||||
let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
|
||||
let client = Client::new(&config);
|
||||
|
||||
// Check the client ID of the request to assign the user to the correct location.
|
||||
let res = match event.payload.caller_context.client_id.as_str() {
|
||||
"10jr2h3vtpu9n7gj46pvg5qo2q" => add_user_to_group(client, user_id, "ScholarshipStudents").await,
|
||||
"56c2bqvl021rv8d5mq36blt7jv" => add_user_to_group(client, user_id, "ScholarshipProviders").await,
|
||||
_ => Err("Invalid client ID, denying all user access.".to_string()),
|
||||
};
|
||||
|
||||
if res.is_ok() {
|
||||
Ok(event.payload)
|
||||
} else {
|
||||
Err(res.unwrap_err())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use lambda_runtime::{run, service_fn, tracing, Error};
|
||||
mod handler;
|
||||
|
||||
use handler::handler as function_handler;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
tracing::init_default_subscriber();
|
||||
|
||||
run(service_fn(function_handler)).await
|
||||
}
|
||||
Reference in new issue
Block a user