Skip to content

Commit

Permalink
feat: add api keys authorizer
Browse files Browse the repository at this point in the history
  • Loading branch information
marc-gavanier committed Sep 29, 2023
1 parent fb7d58c commit 372f13b
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 46 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ jobs:
mv "import-from-s3/index.mjs" "dist/index.mjs"
(cd dist && zip "import-from-s3.zip" "index.mjs")
rm "dist/index.mjs"
mv "api-keys-authorizer/index.mjs" "dist/index.mjs"
(cd dist && zip "api-keys-authorizer.zip" "index.mjs")
rm "dist/index.mjs"
- name: Publish on S3
uses: jakejarvis/s3-sync-action@master
with:
Expand Down
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,6 @@ terraform.rc
node_modules/

# Build
dist
import-from-s3
dist/
api-keys-authorizer/
import-from-s3/
18 changes: 14 additions & 4 deletions api-gateway.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
resource "aws_apigatewayv2_api" "cartographie_nationale" {
name = "${local.product_information.context.project}-${local.product_information.context.service}"
name = local.name_prefix
tags = local.tags
protocol_type = "HTTP"

Expand Down Expand Up @@ -35,6 +35,15 @@ resource "aws_apigatewayv2_stage" "cartographie_nationale" {
}
}

resource "aws_apigatewayv2_authorizer" "api_key_authorizer" {
api_id = aws_apigatewayv2_api.cartographie_nationale.id
authorizer_type = "REQUEST"
authorizer_uri = aws_lambda_function.import_from_s3.invoke_arn
identity_sources = ["$request.header.Authorization"]
name = "${local.name_prefix}.key-authorizer"
authorizer_payload_format_version = "2.0"
}

resource "aws_apigatewayv2_integration" "api_integrations" {
for_each = {
for object in data.aws_s3_object.s3_objects :
Expand All @@ -55,9 +64,10 @@ resource "aws_apigatewayv2_route" "api_route" {
if object.content_type == "application/zip"
}

api_id = aws_apigatewayv2_api.cartographie_nationale.id
route_key = "GET /${aws_lambda_function.api_routes[each.key].function_name}"
target = "integrations/${aws_apigatewayv2_integration.api_integrations[each.key].id}"
api_id = aws_apigatewayv2_api.cartographie_nationale.id
route_key = "${upper(element(split(".", each.key), length(split(".", each.key)) - 2))} /${aws_lambda_function.api_routes[each.key].function_name}"
target = "integrations/${aws_apigatewayv2_integration.api_integrations[each.key].id}"
authorizer_id = element(split(".", each.key), length(split(".", each.key)) - 2) != "get" ? aws_apigatewayv2_authorizer.api_key_authorizer.id : null
}

resource "aws_cloudwatch_log_group" "api_cartographie_nationale" {
Expand Down
84 changes: 84 additions & 0 deletions api-keys.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
resource "aws_dynamodb_table" "api_keys_table" {
name = "${local.product_information.context.project}.api-keys"
billing_mode = "PAY_PER_REQUEST"
hash_key = "name"

attribute {
name = "name"
type = "S"
}

tags = local.tags
}

data "aws_iam_policy_document" "api_keys_table_policy_document" {
statement {
effect = "Allow"
actions = [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
]
resources = [aws_dynamodb_table.api_keys_table.arn]
}
}

resource "aws_iam_policy" "api_keys_table_policy" {
name = "${local.name_prefix}.keys-table-policy"
description = "IAM policy to allow DynamoDB Table api-keys access"
policy = data.aws_iam_policy_document.api_keys_table_policy_document.json
}

data "aws_s3_object" "api_keys_authorizer_archive" {
count = fileexists("${path.module}/api-keys-authorizer/index.mjs") ? 0 : 1
bucket = aws_s3_bucket.api.id
key = "utilities/api-keys-authorizer.zip"
}

data "archive_file" "api_keys_authorizer_archive" {
count = fileexists("${path.module}/api-keys-authorizer/index.mjs") ? 1 : 0
type = "zip"
source_dir = "${path.module}/api-keys-authorizer"
output_path = "${path.module}/api-keys-authorizer.zip"
}

resource "aws_lambda_function" "api_keys_authorizer" {
filename = fileexists("${path.module}/api-keys-authorizer/index.mjs") ? "api-keys-authorizer.zip" : null
s3_bucket = fileexists("${path.module}/api-keys-authorizer/index.mjs") ? null : aws_s3_bucket.api.id
s3_key = fileexists("${path.module}/api-keys-authorizer/index.mjs") ? null : "utilities/api-keys-authorizer.zip"
function_name = "api-keys-authorizer"
role = aws_iam_role.api_keys_authorizer_execution_role.arn
handler = "index.handler"
runtime = "nodejs18.x"
source_code_hash = fileexists("${path.module}/api-keys-authorizer/index.mjs") ? data.archive_file.import_from_s3_archive[0].output_base64sha256 : null
}

resource "aws_cloudwatch_log_group" "api_keys_authorizer_cloudwatch_log_group" {
name = "/aws/lambda/${aws_lambda_function.api_keys_authorizer.function_name}"
retention_in_days = 14
}

data "aws_iam_policy_document" "api_keys_authorizer_execution_policy" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]

principals {
identifiers = ["lambda.amazonaws.com"]
type = "Service"
}
}
}

resource "aws_iam_role" "api_keys_authorizer_execution_role" {
name = "${local.name_prefix}.keys-authorizer-execution-role"
description = "Authentication iam role references a policy document that can assume role for api keys authorizer execution"
tags = local.tags
assume_role_policy = data.aws_iam_policy_document.api_keys_authorizer_execution_policy.json
}

resource "aws_iam_role_policy" "api_keys_table_role_policy" {
name = "${local.name_prefix}.keys-table-role-policy"
role = aws_iam_role.api_keys_authorizer_execution_role.id
policy = data.aws_iam_policy_document.api_keys_table_policy_document.json
}
2 changes: 1 addition & 1 deletion api-lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ resource "aws_iam_role" "api_routes_roles" {

resource "aws_iam_policy_attachment" "sources_table_policy_attachment" {
name = "sources_table_policy_attachment"
policy_arn = aws_iam_policy.sources_table_table_policy.arn
policy_arn = aws_iam_policy.sources_table_policy.arn
roles = [aws_iam_role.api_routes_roles.name]
}

Expand Down
17 changes: 17 additions & 0 deletions cloud-watch.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
data "aws_iam_policy_document" "cloud_watch_role_policy_document" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = ["arn:aws:logs:*:*:*"]
}
}

resource "aws_iam_role_policy" "cloud_watch_role_policy" {
name = "${local.name_prefix}.cloud-watch-role-policy"
role = aws_iam_role.api_keys_authorizer_execution_role.id
policy = data.aws_iam_policy_document.cloud_watch_role_policy_document.json
}
21 changes: 9 additions & 12 deletions database.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
resource "aws_dynamodb_table" "sources_table" {
name = "Sources"
name = "${local.product_information.context.project}.sources"
billing_mode = "PAY_PER_REQUEST"
hash_key = "name"

Expand All @@ -11,7 +11,7 @@ resource "aws_dynamodb_table" "sources_table" {
tags = local.tags
}

data "aws_iam_policy_document" "sources_table_table_policy_document" {
data "aws_iam_policy_document" "sources_table_policy_document" {
statement {
effect = "Allow"
actions = [
Expand All @@ -26,14 +26,14 @@ data "aws_iam_policy_document" "sources_table_table_policy_document" {
}
}

resource "aws_iam_policy" "sources_table_table_policy" {
name = "sources_table_table_policy"
resource "aws_iam_policy" "sources_table_policy" {
name = "${local.product_information.context.project}.sources-table-policy"
description = "IAM policy to allow DynamoDB Table sources access"
policy = data.aws_iam_policy_document.sources_table_table_policy_document.json
policy = data.aws_iam_policy_document.sources_table_policy_document.json
}

resource "aws_dynamodb_table" "lieux_inclusion_numerique_table" {
name = "LieuxInclusionNumerique"
name = "${local.product_information.context.project}.lieux-inclusion-numerique"
billing_mode = "PAY_PER_REQUEST"
hash_key = "id"

Expand Down Expand Up @@ -61,12 +61,11 @@ data "aws_iam_policy_document" "lieux_inclusion_numerique_table_policy_document"
}

resource "aws_iam_policy" "lieux_inclusion_numerique_table_policy" {
name = "lieux_inclusion_numerique_table_policy"
name = "${local.name_prefix}.lieux-inclusion-numerique-table-policy"
description = "IAM policy to allow DynamoDB Table Lieux d'inclusion numérique access"
policy = data.aws_iam_policy_document.lieux_inclusion_numerique_table_policy_document.json
}


resource "aws_s3_bucket" "dynamo_table_import" {
bucket = "${replace(local.product_information.context.project, "_", "-")}-dynamo-table-import"
force_destroy = true
Expand Down Expand Up @@ -101,15 +100,13 @@ data "aws_iam_policy_document" "read_from_s3_policy_document" {
}

resource "aws_iam_role_policy" "read_from_s3_role_policy" {
name = "read-from-s3-role-policy"
name = "${local.name_prefix}.read-from-s3-role-policy"
role = aws_iam_role.import_from_s3_role.id
policy = data.aws_iam_policy_document.read_from_s3_policy_document.json
}



resource "aws_iam_role_policy" "import_in_dynamo_table_role_policy" {
name = "import-in-dynamo-table-role-policy"
name = "${local.name_prefix}.import-in-dynamo-table-role-policy"
role = aws_iam_role.import_from_s3_role.id
policy = data.aws_iam_policy_document.lieux_inclusion_numerique_table_policy_document.json
}
7 changes: 4 additions & 3 deletions global.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ locals {
}

locals {
projectTitle = title(replace(local.product_information.context.project, "-", " "))
layerTitle = title(replace(local.product_information.context.layer, "-", " "))
serviceTitle = title(replace(local.product_information.context.service, "-", " "))
name_prefix = "${local.product_information.context.project}-${local.product_information.context.service}"
project_title = title(replace(local.product_information.context.project, "-", " "))
layer_title = title(replace(local.product_information.context.layer, "-", " "))
service_title = title(replace(local.product_information.context.service, "-", " "))
}
21 changes: 1 addition & 20 deletions import-from-s3-lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ data "aws_s3_object" "import_from_s3_archive" {
key = "utilities/import-from-s3.zip"
}


data "archive_file" "import_from_s3_archive" {
count = fileexists("${path.module}/import-from-s3/index.mjs") ? 1 : 0
type = "zip"
Expand All @@ -25,7 +24,7 @@ resource "aws_lambda_function" "import_from_s3" {
source_code_hash = fileexists("${path.module}/import-from-s3/index.mjs") ? data.archive_file.import_from_s3_archive[0].output_base64sha256 : null
}

resource "aws_cloudwatch_log_group" "example" {
resource "aws_cloudwatch_log_group" "import_from_s3_cloudwatch_log_group" {
name = "/aws/lambda/${aws_lambda_function.import_from_s3.function_name}"
retention_in_days = 14
}
Expand All @@ -48,21 +47,3 @@ resource "aws_iam_role" "import_from_s3_role" {
tags = local.tags
assume_role_policy = data.aws_iam_policy_document.lambda_execution_policy.json
}

data "aws_iam_policy_document" "cloud_watch_role_policy_document" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = ["arn:aws:logs:*:*:*"]
}
}

resource "aws_iam_role_policy" "cloud_watch_role_policy" {
name = "cloud-watch-role-policy"
role = aws_iam_role.import_from_s3_role.id
policy = data.aws_iam_policy_document.cloud_watch_role_policy_document.json
}
9 changes: 9 additions & 0 deletions src/api-keys-authorizer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { APIGatewayProxyResultV2 } from 'aws-lambda';

export const handler = async (): Promise<APIGatewayProxyResultV2<boolean>> => {
try {
return false;
} catch (err) {
console.log(err);
}
};
16 changes: 12 additions & 4 deletions vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import * as path from 'path';
import { defineConfig } from 'vite';

const inputFor = (folder: string) => ({
[`${folder}/index`]: path.resolve(__dirname, `src/${folder}/index.ts`),
});
const inputFor = (folders: string[]) =>
folders.reduce(
(
inputs: { [key: string]: string },
folder: string,
): { [key: string]: string } => ({
...inputs,
[`${folder}/index`]: path.resolve(__dirname, `src/${folder}/index.ts`),
}),
{},
);

export default defineConfig({
build: {
Expand All @@ -15,7 +23,7 @@ export default defineConfig({
rollupOptions: {
external: /^@aws-sdk/,
input: {
...inputFor('import-from-s3'),
...inputFor(['api-keys-authorizer', 'import-from-s3']),
},
output: {
entryFileNames: '[name].mjs',
Expand Down

0 comments on commit 372f13b

Please sign in to comment.