Skip to content

Commit

Permalink
Refactored project structure and enabled Secrets Manager token for We…
Browse files Browse the repository at this point in the history
…bHook
  • Loading branch information
san99tiago committed Jul 8, 2024
1 parent f987f44 commit 598505e
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 232 deletions.
File renamed without changes.
File renamed without changes.
40 changes: 40 additions & 0 deletions backend/common/helpers/secrets_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Built-in imports
import json
import boto3
from typing import Union

# External imports
from botocore.exceptions import ClientError

# Own imports
from common.logger import custom_logger

logger = custom_logger()


class SecretsHelper:
"""Custom Secrets Manager Helper for simplifying secret's retrieval."""

def __init__(self, secret_name: str) -> None:
"""
:param secret_name (str): Name of the secret to fetch.
"""
self.secret_name = secret_name
self.client_sm = boto3.client("secretsmanager")

def get_secret_value(self, key_name: str) -> Union[str, None]:
"""
Obtain the AWS Secret value based on a given key.
:param key_name (str): Key name to fetch from the JSON secret.
"""
try:
secret_value = self.client_sm.get_secret_value(SecretId=self.secret_name)
logger.info(f"Successfully retrieved the AWS Secret: {self.secret_name}")
self.secret_string = json.loads(secret_value["SecretString"])
logger.debug("Successfully obtained the SecretString value.")
# Return value or intentional KeyError if the key is not present
return self.secret_string[key_name]
except ClientError as e:
logger.exception(f"Error in pulling the AWS Secret: {self.secret_name}")
logger.exception(f"Error details: {str(e)}")
raise e
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from common.message_base_model import MessageBaseModel
from common.models.message_base_model import MessageBaseModel


class TextMessageModel(MessageBaseModel):
Expand Down
13 changes: 8 additions & 5 deletions backend/whatsapp_webhook/api/v1/routers/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
from fastapi import APIRouter, Header, Query, Request, Response, status

# Own imports
from common.text_message_model import TextMessageModel
from common.models.text_message_model import TextMessageModel
from common.logger import custom_logger
from whatsapp_webhook.helpers.dynamodb_helper import DynamoDBHelper
from common.helpers.dynamodb_helper import DynamoDBHelper
from common.helpers.secrets_helper import SecretsHelper

# Initialize the META_API_CALLBACK_TOKEN (pending from Secrets Manager)
META_API_CALLBACK_TOKEN = "PENDING_ADD_TOKEN_FROM_SECRETS_MANAGER" # TODO (pending)
# Initialize Secrets Manager Helper
SECRET_NAME = os.environ["SECRET_NAME"]
secrets_helper = SecretsHelper(SECRET_NAME)

# Initialize DynamoDB Helper
DYNAMODB_TABLE = os.environ["DYNAMODB_TABLE"]
Expand Down Expand Up @@ -41,7 +43,8 @@ async def get_chatbot_webhook(
logger.debug(f"hub_verify_token_query_param: {hub_verify_token_query_param}")

# TODO: MIGRATE TOKEN VALIDATION TO DEDICATED AUTHORIZER!!!
if hub_verify_token_query_param == META_API_CALLBACK_TOKEN:
AWS_API_KEY_TOKEN = secrets_helper.get_secret_value("AWS_API_KEY_TOKEN")
if hub_verify_token_query_param == AWS_API_KEY_TOKEN:
return Response(
content=hub_challenge_query_param,
status_code=status.HTTP_200_OK,
Expand Down
3 changes: 2 additions & 1 deletion cdk.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"deployment_environment": "dev",
"log_level": "DEBUG",
"table_name": "aws-whatsapp-poc-dev",
"api_gw_name": "wpp-poc"
"api_gw_name": "wpp-poc",
"secret_name": "/dev/aws-whatsapp-chatbot"
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions cdk/stacks/cdk_chatbot_api_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
aws_lambda,
aws_lambda_event_sources,
aws_logs,
aws_secretsmanager,
aws_stepfunctions as aws_sfn,
aws_stepfunctions_tasks as aws_sfn_tasks,
aws_apigateway as aws_apigw,
Expand Down Expand Up @@ -48,7 +49,7 @@ def __init__(
self.deployment_environment = self.app_config["deployment_environment"]

# Main methods for the deployment
# TODO: Add "import_secret" method to import from secrets manager the tokens
self.import_secrets()
self.create_dynamodb_table()
self.create_lambda_layers()
self.create_lambda_functions()
Expand All @@ -62,6 +63,16 @@ def __init__(
# Generate CloudFormation outputs
self.generate_cloudformation_outputs()

def import_secrets(self) -> None:
"""
Method to import the AWS Secrets for the Lambda Functions.
"""
self.secret_chatbot = aws_secretsmanager.Secret.from_secret_name_v2(
self,
"Secret-Chatbot",
secret_name=self.app_config["secret_name"],
)

def create_dynamodb_table(self):
"""
Create DynamoDB table for storing the conversations.
Expand Down Expand Up @@ -133,13 +144,15 @@ def create_lambda_functions(self) -> None:
"ENVIRONMENT": self.app_config["deployment_environment"],
"LOG_LEVEL": self.app_config["log_level"],
"DYNAMODB_TABLE": self.dynamodb_table.table_name,
"SECRET_NAME": self.app_config["secret_name"],
},
layers=[
self.lambda_layer_powertools,
self.lambda_layer_common,
],
)
self.dynamodb_table.grant_read_write_data(self.lambda_whatsapp_webhook)
self.secret_chatbot.grant_read(self.lambda_whatsapp_webhook)

# Lambda Function for receiving the messages from DynamoDB Streams
# ... and triggering the State Machine for processing the messages
Expand Down Expand Up @@ -176,13 +189,14 @@ def create_lambda_functions(self) -> None:
environment={
"ENVIRONMENT": self.app_config["deployment_environment"],
"LOG_LEVEL": self.app_config["log_level"],
"STATE_MACHINE_ARN": "TBD",
"SECRET_NAME": self.app_config["secret_name"],
},
layers=[
self.lambda_layer_powertools,
self.lambda_layer_common,
],
)
self.secret_chatbot.grant_read(self.lambda_state_machine_process_message)

def create_dynamodb_streams(self) -> None:
"""
Expand Down
Loading

0 comments on commit 598505e

Please sign in to comment.