Skip to content

APIGW NG implement api key validation handler #11114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 2, 2024

Conversation

cloutierMat
Copy link
Contributor

@cloutierMat cloutierMat commented Jun 28, 2024

Motivation

Implementation of a new api key validation handler. There is a mix of moto helpers in the implementation as not all relevant information live in the deployment.

Important configuration in the frozen deployment:

  • The resource method describes apiKeyRequired, which determines if validation is required or skipped.
  • The rest api describes apiKeySource, which determines if the key is to be retrieved from the headers or from an identity context populated by an Lambda Authorizer

Important configuration in apigateway store.

Those items, when created, modified or deleted, will affect a deployment without the need to redeploy. see https://docs.aws.amazon.com/apigateway/latest/developerguide/updating-api.html

  • Api keys and their properties are stored in moto (contradicts the documentation, but manually tested that updating enabled from the key will affect without redeploying)
  • Usage plans are stored in moto
  • Usage plan key (association of a key to a usage plan) are also stored in moto

Changes

  • Finds and validates api key based on deployment configuration and keys/usage plans stored on the backend
  • Updates the $context variables with relevant data.
  • Added moto_helpers to keep moto backend isolated and ease technical debt.
  • Added tests to validate functionality

TODO

What's left to do:

@cloutierMat cloutierMat added aws:apigateway Amazon API Gateway semver: patch Non-breaking changes which can be included in patch releases labels Jun 28, 2024
@cloutierMat cloutierMat added this to the 3.6 milestone Jun 28, 2024
@cloutierMat cloutierMat self-assigned this Jun 28, 2024
@cloutierMat cloutierMat requested a review from bentsku as a code owner June 28, 2024 21:23
@cloutierMat cloutierMat changed the title Apigw ng implement api key validation APIGW NG implement api key validation Jun 28, 2024
@cloutierMat cloutierMat changed the title APIGW NG implement api key validation APIGW NG implement api key validation handler Jun 28, 2024
Copy link
Contributor

@bentsku bentsku left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🚀 extremely well covered PR, the tests are really great, love the abstractions and usage of fixtures.

The handler is looking extremely good, well documented, nicely encapsulated functions and helpers, this is awesome 💯

edit: I've merged #11111 so I've took the liberty to rebase this PR on master and push it so I can create branches based on top of #11114 😄

Will return an api key from the moto store.
"""
apigateway_backend: APIGatewayBackend = apigateway_backends[account_id][region_name]
return ApiKey(**apigateway_backend.keys[api_key_id].to_json())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's not supposed to happen, but should we protect against KeyError here?
Also, I believe we could avoid the ** destructing into typed dict by doing the following:

Suggested change
return ApiKey(**apigateway_backend.keys[api_key_id].to_json())
api_key: ApiKey = apigateway_backend.keys[api_key_id].to_json()
return api_key

Not sure how to do it with the list comprehension, but the creation of a typed dict by de-structuring a dict is not really required, as we're just passing dict around.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually realised I don't even need to go that far and and with simply return apigateway_backend.keys[api_key_id].to_json() all the typing works fine. Which I can also use to simplify the code of get_usage_plans and get_usage_plan_keys and remove the destructing while keeping typing!
👍

Comment on lines 40 to 41
if not (identity := context.context_variables.get("identity", ContextVarsIdentity())):
context.context_variables["identity"] = identity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simplify with setdefault(key, default_value) which does the same thing under the hood:

Suggested change
if not (identity := context.context_variables.get("identity", ContextVarsIdentity())):
context.context_variables["identity"] = identity
identity = context.context_variables.setdefault("identity", ContextVarsIdentity())

LOG.debug("Updating $context.identity.apiKeyId='%s'", validated_key["id"])
identity["apiKeyId"] = validated_key["id"]

def validate_api_key(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe once we migrate UsagePlan and ApiKey to our store, seeing this usage, we should keep a map of API Key values (+ maybe stage?) directly in order to quickly validate them? We could maybe then simplify this to not have to iterate over all usage plans and usage plan keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I believe we can keep the item in the store in a way that will be more favourable to this specific access pattern. I don't think we will be able to avoid all looping, without creating some data duplication but we should be able to reduce the amount of work being done here.

Base automatically changed from apigw-add-contexts to master July 1, 2024 14:47
@bentsku bentsku force-pushed the apigw-ng-implement-api-key-validation branch from 8288e20 to a324a08 Compare July 1, 2024 15:54
Copy link

github-actions bot commented Jul 1, 2024

LocalStack Community integration with Pro

  2 files  ±    0    2 suites  ±0   23m 29s ⏱️ - 1h 11m 46s
643 tests  - 2 518  598 ✅  - 2 167  45 💤  - 351  0 ❌ ±0 
645 runs   - 2 518  598 ✅  - 2 167  47 💤  - 351  0 ❌ ±0 

Results for commit 0149699. ± Comparison against base commit be9149c.

This pull request removes 2518 tests.
tests.aws.scenario.bookstore.test_bookstore.TestBookstoreApplication ‑ test_lambda_dynamodb
tests.aws.scenario.bookstore.test_bookstore.TestBookstoreApplication ‑ test_opensearch_crud
tests.aws.scenario.bookstore.test_bookstore.TestBookstoreApplication ‑ test_search_books
tests.aws.scenario.bookstore.test_bookstore.TestBookstoreApplication ‑ test_setup
tests.aws.scenario.kinesis_firehose.test_kinesis_firehose.TestKinesisFirehoseScenario ‑ test_kinesis_firehose_s3
tests.aws.scenario.lambda_destination.test_lambda_destination_scenario.TestLambdaDestinationScenario ‑ test_destination_sns
tests.aws.scenario.lambda_destination.test_lambda_destination_scenario.TestLambdaDestinationScenario ‑ test_infra
tests.aws.scenario.loan_broker.test_loan_broker.TestLoanBrokerScenario ‑ test_prefill_dynamodb_table
tests.aws.scenario.loan_broker.test_loan_broker.TestLoanBrokerScenario ‑ test_stepfunctions_input_recipient_list[step_function_input0-SUCCEEDED]
tests.aws.scenario.loan_broker.test_loan_broker.TestLoanBrokerScenario ‑ test_stepfunctions_input_recipient_list[step_function_input1-SUCCEEDED]
…

♻️ This comment has been updated with latest results.

@cloutierMat cloutierMat merged commit faf13f7 into master Jul 2, 2024
32 checks passed
@cloutierMat cloutierMat deleted the apigw-ng-implement-api-key-validation branch July 2, 2024 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
aws:apigateway Amazon API Gateway semver: patch Non-breaking changes which can be included in patch releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants