Skip to content

Commit

Permalink
Feature/issue 205 - Add Confluence API key (#221)
Browse files Browse the repository at this point in the history
* Fix possible variable references before value is assigned

* Define Confluence API key and trusted partner plan limits

* Define a list of trusted partner keys and store under single parameter

* Define API keys as encrypted envrionment variables for Lambda authorizer

* Update authorizer and connection class to use KMS to retrieve API keys

* Hack to force lambda deployment when ssm value changes (#218)

* Add replace_triggered_by to hydrocron_lambda_authorizer

* Introduce environment variable that contains random id which will change whenever an API key value changes. This will force lambda to publish new version of the function.

* Remove unnecessary hash function

* Update to SSM parameter API key storage and null_resource enviroment variable

* Update Terraform and AWS provider

* Update API key documentation

* Set source_code_hash to force deployment of new image

* Downgrade AWS provider to 4.0 to remove inline policy errors

* Update docs/timeseries.md

---------

Co-authored-by: Frank Greguska <[email protected]>
  • Loading branch information
nikki-t and frankinspace authored Aug 22, 2024
1 parent 6b31e36 commit 865809e
Show file tree
Hide file tree
Showing 12 changed files with 49 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ concurrency:
env:
POETRY_VERSION: "1.7.1"
PYTHON_VERSION: "3.10"
TERRAFORM_VERSION: "1.7.3"
TERRAFORM_VERSION: "1.9.3"
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Issue 205 - Define a an API key for the Confluence workflow and usage plan limits
- Issue 201- Create table for tracking granule ingest status
- Issue 225 - Create one track ingest table per feature type
- Issue 222 - Add operations to load granule Lambda to write granule record to track ingest database
- Issue 201 - Create table for tracking granule ingest status
- Issue 198 - Implement track ingest lambda function CMR and Hydrocron queries
- Issue 193 - Add new Dynamo table for prior lake data
- Issue 196 - Add new feature type to query the API for lake data
Expand Down
7 changes: 2 additions & 5 deletions docs/timeseries.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,9 @@ Example CSV response:

*The 400 code is also currently returned for queries where no time series data could be located for the request specified feature ID. The message returned with the response indicates this and it can be helpful to adjust the date ranges you are searching.

## API Keys [DRAFT]
## API Keys

> ⚠️
>API keys not yet implemented but coming soon! Content below is not finalized. More details to follow...
Users may request a special API key for cases where their intended usage of the API may be considered heavy or more complex. Heavy usage can be defined as continued used with over x requests per day or continue use which require many requests per second or concurrent requests. To request an API key or to discuss your use case, please contact us at x.
Users may request a special API key for cases where their intended usage of the API may be considered heavy or more complex. Heavy usage can be defined as continued use with many requests per hour or day or continued use which may require many requests per second or concurrent requests. To request an API key or to discuss your use case, please submit a [GitHub issue](https://github.com/podaac/hydrocron/issues).

**Note: Users do *not* have to send an API key in their request to use the Hydrocron API. The API key is optional.**

Expand Down
5 changes: 3 additions & 2 deletions hydrocron/api/controllers/authorizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ def authorization_handler(event, context):
logging.info("Context: %s", context)

api_key_trusted = "" if "x-hydrocron-key" not in event["headers"].keys() else event["headers"]["x-hydrocron-key"]
trusted_key_list = json.loads(STORED_API_KEY_TRUSTED)

if api_key_trusted and api_key_trusted == STORED_API_KEY_TRUSTED:
response_policy = create_policy("trusted_partner", "Allow", event["methodArn"], STORED_API_KEY_TRUSTED)
if api_key_trusted and api_key_trusted in trusted_key_list:
response_policy = create_policy("trusted_partner", "Allow", event["methodArn"], api_key_trusted)
logging.info("Created policy for truster partner.")

else:
Expand Down
1 change: 0 additions & 1 deletion terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 15 additions & 10 deletions terraform/hydrocron-apigw.tf
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ resource "aws_api_gateway_api_key" "default-user-key" {
}


resource "aws_api_gateway_api_key" "trusted-user-key" {
name = "${local.aws_resource_prefix}-api-key-trusted"
resource "aws_api_gateway_api_key" "confluence-user-key" {
name = "${local.aws_resource_prefix}-api-key-confluence"
}


Expand All @@ -124,7 +124,11 @@ resource "aws_ssm_parameter" "trusted-user-parameter" {
name = "/service/${var.app_name}/api-key-trusted"
description = "Hydrocron trusted user API key"
type = "SecureString"
value = aws_api_gateway_api_key.trusted-user-key.value
value = jsonencode(
[
"${aws_api_gateway_api_key.confluence-user-key.value}"
]
)
}


Expand All @@ -146,14 +150,14 @@ resource "aws_api_gateway_usage_plan" "default-user-usage-plan" {
}
}


resource "aws_api_gateway_usage_plan_key" "default-user-usage-key" {
key_id = aws_api_gateway_api_key.default-user-key.id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.default-user-usage-plan.id
}



resource "aws_api_gateway_usage_plan" "trusted-user-usage-plan" {
name = "${local.aws_resource_prefix}-usage-plan-trusted"
description = "Hydrocron trusted user usage plan"
Expand All @@ -162,17 +166,18 @@ resource "aws_api_gateway_usage_plan" "trusted-user-usage-plan" {
stage = aws_api_gateway_stage.hydrocron-api-gateway-stage.stage_name
}
quota_settings {
limit = 5
limit = 12107815
period = "MONTH"
}
throttle_settings {
burst_limit = 1
rate_limit = 1
burst_limit = 3000
rate_limit = 6000
}
}

resource "aws_api_gateway_usage_plan_key" "trusted-user-usage-key" {
key_id = aws_api_gateway_api_key.trusted-user-key.id

resource "aws_api_gateway_usage_plan_key" "confluence-user-usage-key" {
key_id = aws_api_gateway_api_key.confluence-user-key.id
key_type = "API_KEY"
usage_plan_id = aws_api_gateway_usage_plan.trusted-user-usage-plan.id
}
}
6 changes: 3 additions & 3 deletions terraform/hydrocron-dynamo.tf
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ resource "aws_dynamodb_table" "hydrocron-priorlake-track-ingest-table" {
type = "S"
}
global_secondary_index {
name = "statusIndex"
hash_key = "status"
projection_type = "ALL"
name = "statusIndex"
hash_key = "status"
projection_type = "ALL"
}
point_in_time_recovery {
enabled = var.stage == "ops" ? true : false
Expand Down
19 changes: 18 additions & 1 deletion terraform/hydrocron-lambda.tf
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ resource "aws_lambda_permission" "allow_hydrocron-timeseries" {
source_arn = "${aws_api_gateway_rest_api.hydrocron-api-gateway.execution_arn}/*"
}

resource "null_resource" "api_key_hash" {
/**
This resource is needed because of https://github.com/podaac/hydrocron/issues/205#issuecomment-2250982988
*/
triggers = {
default_key = aws_ssm_parameter.default-user-parameter.value
trusted_key_list = aws_ssm_parameter.trusted-user-parameter.value
}
}

resource "aws_lambda_function" "hydrocron_lambda_authorizer" {
package_type = "Image"
Expand All @@ -98,6 +107,14 @@ resource "aws_lambda_function" "hydrocron_lambda_authorizer" {
security_group_ids = data.aws_security_groups.vpc_default_sg.ids
}
tags = var.default_tags

/**
This is the preferred solution in lieu of the nonsense below but when using replace_triggered_by, terraform plan fails
to replace the lambda correctly and results in an error "ResourceConflictException: Function already exist"
lifecycle { replace_triggered_by = [aws_ssm_parameter.default-user-parameter.value, aws_ssm_parameter.trusted-user-parameter.value]}
*/
source_code_hash = null_resource.api_key_hash.id
}


Expand Down Expand Up @@ -201,4 +218,4 @@ resource "aws_lambda_function" "hydrocron_lambda_track_ingest" {
GRANULE_LAMBDA_FUNCTION_NAME = aws_lambda_function.hydrocron_lambda_load_granule.function_name
}
}
}
}
2 changes: 1 addition & 1 deletion terraform/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ terraform {
required_providers {
aws = "~> 4.0"
}
required_version = ">= 1.7.3"
required_version = ">= 1.9.3"
}
3 changes: 1 addition & 2 deletions tests/test_authorizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@ def setUp(self):
# Create SSM client and put API keys
ssm = boto3.client("ssm")
ssm.put_parameter(Name="/service/hydrocron/api-key-default", Value="abc123", Type="SecureString")
ssm.put_parameter(Name="/service/hydrocron/api-key-trusted", Value="def456", Type="SecureString")
ssm.put_parameter(Name="/service/hydrocron/api-key-trusted", Value='["def456", "qrs789"]', Type="SecureString")

def tearDown(self):

self.mock_aws.stop()


def test_authorizer_lambda_handler_default(self):
"""
Expand Down
5 changes: 2 additions & 3 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ def test_s3_resource(s3_connection):
# Import module
from hydrocron.utils import connection
assert type(connection.s3_resource).__name__ == "s3.ServiceResource"



def test_ssm_client():
"""Test retrieval of DynamoDB resource."""

# Import module
from hydrocron.utils import connection
assert type(connection.ssm_client).__name__ == "SSM"
assert type(connection.ssm_client).__name__ == "SSM"
4 changes: 1 addition & 3 deletions tests/test_data/api_authorizer_default.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
"resource": "/timeseries",
"path": "/timeseries",
"httpMethod": "GET",
"headers": {
"x-api-key": "abc123"
},
"headers": {},
"pathParameters": {},
"stageVariables": {},
"requestContext": {
Expand Down

0 comments on commit 865809e

Please sign in to comment.