Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions layer/nrlf/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,23 @@ def request_handler(
"""

def wrapped_func(func: RequestHandler):
def wrapper(*args, **kwargs):
event: APIGatewayProxyEvent = args[0]
context: LambdaContext = args[1]
def wrapper(event: APIGatewayProxyEvent, context: LambdaContext, **kwargs):
client_cert = event.request_context.identity.client_cert
if client_cert:
client_cert_info = {
"subject_dn": client_cert.subject_dn,
"issuer_dn": client_cert.issuer_dn,
"serial_number": client_cert.serial_number,
}
else:
client_cert_info = "No client certificate provided"

logger.log(
code=LogReference.HANDLER000,
method=event.http_method,
path=event.path,
headers=event.headers,
client_cert_info=client_cert_info,
)

if skip_request_verification:
Expand Down
69 changes: 69 additions & 0 deletions layer/nrlf/core/tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import warnings
from typing import Any

import pytest
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent
Expand Down Expand Up @@ -288,6 +289,74 @@ def decorated_function(event):
)


def test_log_includes_client_cert_details(mocker: MockerFixture):
@request_handler()
def decorated_function() -> Response:
return Response(
statusCode="200",
body=json.dumps({"message": "Hello, World!"}),
headers={"Content-Type": "application/json"},
)

test_event = create_test_api_gateway_event()
event = APIGatewayProxyEvent(test_event)

mock_logger = mocker.patch("nrlf.core.decorators.logger")

decorated_function(event, create_mock_context())

assert any(
call[1]["code"].name == "HANDLER000"
for call in mock_logger.log.call_args_list
if call[1]
)

logged_cert_info: dict[str, Any] = [
call[1:][0]
for call in mock_logger.log.call_args_list
if call[1] and "code" in call[1] and call[1]["code"].name == "HANDLER000"
][0]["client_cert_info"]

client_cert = event.request_context.identity.client_cert
assert logged_cert_info == {
"subject_dn": client_cert.subject_dn,
"issuer_dn": client_cert.issuer_dn,
"serial_number": client_cert.serial_number,
}


def test_log_includes_client_cert_details_when_no_cert(mocker: MockerFixture):
@request_handler()
def decorated_function() -> Response:
return Response(
statusCode="200",
body=json.dumps({"message": "Hello, World!"}),
headers={"Content-Type": "application/json"},
)

test_event = create_test_api_gateway_event()
test_event["requestContext"]["identity"]["clientCert"] = None
event = APIGatewayProxyEvent(test_event)

mock_logger = mocker.patch("nrlf.core.decorators.logger")

decorated_function(event, create_mock_context())

assert any(
call[1]["code"].name == "HANDLER000"
for call in mock_logger.log.call_args_list
if call[1]
)

logged_cert_info: dict[str, Any] = [
call[1:][0]
for call in mock_logger.log.call_args_list
if call[1] and "code" in call[1] and call[1]["code"].name == "HANDLER000"
][0]["client_cert_info"]

assert logged_cert_info == "No client certificate provided"


def test_verify_request_id_happy_path():
test_event = create_test_api_gateway_event()

Expand Down
7 changes: 7 additions & 0 deletions layer/nrlf/tests/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ def create_test_api_gateway_event(
"resourcePath": "/",
"httpMethod": "GET",
"path": "/Prod/",
"identity": {
"clientCert": {
"subjectDN": "CN=TEST SUBJECT",
"issuerDN": "CN=TEST ISSUER",
"serialNumber": "0000001",
}
},
},
"headers": headers or create_headers(),
"multiValueHeaders": {},
Expand Down