Skip to content

fix(event_handler): security scheme unhashable list when working with router #4421

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
12 changes: 7 additions & 5 deletions aws_lambda_powertools/event_handler/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
from aws_lambda_powertools.event_handler.util import (
_FrozenDict,
_FrozenListDict,
_validate_openapi_security_parameters,
extract_origin_header,
validate_openapi_security_parameters,
)
from aws_lambda_powertools.shared.cookies import Cookie
from aws_lambda_powertools.shared.functions import powertools_dev_is_set
Expand Down Expand Up @@ -1595,12 +1595,13 @@ def get_openapi_schema(
# Add routes to the OpenAPI schema
for route in all_routes:

if route.security and not validate_openapi_security_parameters(
if route.security and not _validate_openapi_security_parameters(
security=route.security,
security_schemes=security_schemes,
):
raise SchemaValidationError(
"Security configuration was not found in security_schemas or security_schema was not defined.",
"Security configuration was not found in security_schemas or security_schema was not defined. "
"See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#security-schemes",
)

if not route.include_in_schema:
Expand Down Expand Up @@ -1649,9 +1650,10 @@ def _get_openapi_security(
if not security:
return None

if not validate_openapi_security_parameters(security=security, security_schemes=security_schemes):
if not _validate_openapi_security_parameters(security=security, security_schemes=security_schemes):
raise SchemaValidationError(
"Security configuration was not found in security_schemas or security_schema was not defined.",
"Security configuration was not found in security_schemas or security_schema was not defined. "
"See: https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#security-schemes",
)

return security
Expand Down
20 changes: 11 additions & 9 deletions aws_lambda_powertools/event_handler/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ def extract_origin_header(resolver_headers: Dict[str, Any]):
return resolved_header


def validate_openapi_security_parameters(
def _validate_openapi_security_parameters(
security: List[Dict[str, List[str]]],
security_schemes: Optional[Dict[str, "SecurityScheme"]],
security_schemes: Optional[Dict[str, "SecurityScheme"]] = None,
) -> bool:
"""
Validates the security parameters based on the provided security schemes.
This function checks if all security requirements listed in the 'security'
parameter are defined in the 'security_schemes' dictionary, as specified
in the OpenAPI schema.

Parameters
----------
Expand All @@ -88,11 +90,11 @@ def validate_openapi_security_parameters(
Returns
-------
bool
True if all security scheme names in the `security` parameter are present in the `security_schemes` parameter,
False otherwise.

Whether list of security schemes match allowed security_schemes.
"""

return bool(
security_schemes and all(key in security_schemes for sec in security for key in sec),
)
security_schemes = security_schemes or {}

security_schema_match = all(key in security_schemes for sec in security for key in sec)

return bool(security_schema_match and security_schemes)
2 changes: 1 addition & 1 deletion docs/core/event_handler/api_gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ Below is an example configuration for serving Swagger UI from a custom path or C
No. Powertools adds support for generating OpenAPI documentation with [security schemes](https://swagger.io/docs/specification/authentication/), but it doesn't implement any of the security schemes itself, so you must implement the security mechanisms separately.

OpenAPI uses the term security scheme for [authentication and authorization schemes](https://swagger.io/docs/specification/authentication/){target="_blank"}.
When you're describing your API, declare security schemes at the top level, and reference them globally or per operation.
When you're describing your API, declare security schemes at the top level, and reference them globally or per operation. Remember to have the security configurations defined in the `security_schemes`, otherwise a `SchemaValidationError` will be raised.

=== "Global OpenAPI security schemes"

Expand Down
6 changes: 3 additions & 3 deletions tests/functional/event_handler/test_openapi_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def handler():
raise NotImplementedError()

# WHEN the get_openapi_schema method is called with security defined security schemes as APIKey
# WHEN top level security is defined as HTTPBearer
# AND top level security is defined as HTTPBearer
# THEN a SchemaValidationError should be raised
with pytest.raises(SchemaValidationError):
app.get_openapi_schema(
Expand Down Expand Up @@ -80,7 +80,7 @@ def test_openapi_operation_level_security_missing():
# GIVEN an APIGatewayRestResolver instance
app = APIGatewayRestResolver()

# WHEN we define a security in operation
# AND a route with a security scheme defined
@app.get("/", security=[{"apiKey": []}])
def handler():
raise NotImplementedError()
Expand All @@ -95,7 +95,7 @@ def test_openapi_operation_level_security_mismatch(security_scheme):
# GIVEN an APIGatewayRestResolver instance
app = APIGatewayRestResolver()

# WHEN we define a security in operation with value HTTPBearer
# AND a route with a security scheme using HTTPBearer
@app.get("/", security=[{"HTTPBearer": []}])
def handler():
raise NotImplementedError()
Expand Down