Skip to content

Commit 7b6d4e6

Browse files
Merge pull request #1071 from NHSDigital/feature/made14-NRL-1700-log-clientcert-info
[NRL-1700] Log info about which clientcert is being used for each req…
2 parents 8c0ead3 + 4dc6457 commit 7b6d4e6

File tree

3 files changed

+87
-3
lines changed

3 files changed

+87
-3
lines changed

layer/nrlf/core/decorators.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,23 @@ def request_handler(
236236
"""
237237

238238
def wrapped_func(func: RequestHandler):
239-
def wrapper(*args, **kwargs):
240-
event: APIGatewayProxyEvent = args[0]
241-
context: LambdaContext = args[1]
239+
def wrapper(event: APIGatewayProxyEvent, context: LambdaContext, **kwargs):
240+
client_cert = event.request_context.identity.client_cert
241+
if client_cert:
242+
client_cert_info = {
243+
"subject_dn": client_cert.subject_dn,
244+
"issuer_dn": client_cert.issuer_dn,
245+
"serial_number": client_cert.serial_number,
246+
}
247+
else:
248+
client_cert_info = "No client certificate provided"
242249

243250
logger.log(
244251
code=LogReference.HANDLER000,
245252
method=event.http_method,
246253
path=event.path,
247254
headers=event.headers,
255+
client_cert_info=client_cert_info,
248256
)
249257

250258
if skip_request_verification:

layer/nrlf/core/tests/test_decorators.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import warnings
3+
from typing import Any
34

45
import pytest
56
from aws_lambda_powertools.utilities.data_classes import APIGatewayProxyEvent
@@ -288,6 +289,74 @@ def decorated_function(event):
288289
)
289290

290291

292+
def test_log_includes_client_cert_details(mocker: MockerFixture):
293+
@request_handler()
294+
def decorated_function() -> Response:
295+
return Response(
296+
statusCode="200",
297+
body=json.dumps({"message": "Hello, World!"}),
298+
headers={"Content-Type": "application/json"},
299+
)
300+
301+
test_event = create_test_api_gateway_event()
302+
event = APIGatewayProxyEvent(test_event)
303+
304+
mock_logger = mocker.patch("nrlf.core.decorators.logger")
305+
306+
decorated_function(event, create_mock_context())
307+
308+
assert any(
309+
call[1]["code"].name == "HANDLER000"
310+
for call in mock_logger.log.call_args_list
311+
if call[1]
312+
)
313+
314+
logged_cert_info: dict[str, Any] = [
315+
call[1:][0]
316+
for call in mock_logger.log.call_args_list
317+
if call[1] and "code" in call[1] and call[1]["code"].name == "HANDLER000"
318+
][0]["client_cert_info"]
319+
320+
client_cert = event.request_context.identity.client_cert
321+
assert logged_cert_info == {
322+
"subject_dn": client_cert.subject_dn,
323+
"issuer_dn": client_cert.issuer_dn,
324+
"serial_number": client_cert.serial_number,
325+
}
326+
327+
328+
def test_log_includes_client_cert_details_when_no_cert(mocker: MockerFixture):
329+
@request_handler()
330+
def decorated_function() -> Response:
331+
return Response(
332+
statusCode="200",
333+
body=json.dumps({"message": "Hello, World!"}),
334+
headers={"Content-Type": "application/json"},
335+
)
336+
337+
test_event = create_test_api_gateway_event()
338+
test_event["requestContext"]["identity"]["clientCert"] = None
339+
event = APIGatewayProxyEvent(test_event)
340+
341+
mock_logger = mocker.patch("nrlf.core.decorators.logger")
342+
343+
decorated_function(event, create_mock_context())
344+
345+
assert any(
346+
call[1]["code"].name == "HANDLER000"
347+
for call in mock_logger.log.call_args_list
348+
if call[1]
349+
)
350+
351+
logged_cert_info: dict[str, Any] = [
352+
call[1:][0]
353+
for call in mock_logger.log.call_args_list
354+
if call[1] and "code" in call[1] and call[1]["code"].name == "HANDLER000"
355+
][0]["client_cert_info"]
356+
357+
assert logged_cert_info == "No client certificate provided"
358+
359+
291360
def test_verify_request_id_happy_path():
292361
test_event = create_test_api_gateway_event()
293362

layer/nrlf/tests/events.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ def create_test_api_gateway_event(
5555
"resourcePath": "/",
5656
"httpMethod": "GET",
5757
"path": "/Prod/",
58+
"identity": {
59+
"clientCert": {
60+
"subjectDN": "CN=TEST SUBJECT",
61+
"issuerDN": "CN=TEST ISSUER",
62+
"serialNumber": "0000001",
63+
}
64+
},
5865
},
5966
"headers": headers or create_headers(),
6067
"multiValueHeaders": {},

0 commit comments

Comments
 (0)