Skip to content

Commit 7667530

Browse files
Merge pull request #205 from NHSDigital/dev/NPA-4284/Allow-API-Headers-to-be-Case-Insensitive
NPA-4284: Allow api headers to be case insensitive
2 parents 9ef4add + 7b1d293 commit 7667530

File tree

6 files changed

+74
-3
lines changed

6 files changed

+74
-3
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<Script async="false" continueOnError="true" enabled="true" name="MirrorRequestHeaders">
3+
<DisplayName>Mirror Request Headers</DisplayName>
4+
<Properties/>
5+
<ResourceURL>py://mirror-request-headers.py</ResourceURL>
6+
</Script>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<Script async="false" continueOnError="true" enabled="true" name="TranslateRequestHeaders">
3+
<DisplayName>Translate Request Headers</DisplayName>
4+
<Properties/>
5+
<ResourceURL>py://translate-request-headers.py</ResourceURL>
6+
</Script>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
translated_header_names = ["X-Request-ID", "X-Correlation-ID"]
2+
for translated_header_name in translated_header_names:
3+
# Access original request headers dictionary
4+
original_request_header = flow.getVariable("original_request_header." + translated_header_name)
5+
if original_request_header:
6+
original_request_header = original_request_header.split("=")
7+
# Remove original response header
8+
flow.removeVariable("response.header." + translated_header_name)
9+
# Mirror original request header
10+
flow.setVariable("response.header." + original_request_header[0], original_request_header[1])
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
def get_request_headers():
2+
request_header_names = flow.getVariable("request.headers.names")
3+
request_headers = {}
4+
for name in request_header_names:
5+
request_headers[name] = flow.getVariable("request.header." + name)
6+
7+
return request_headers
8+
9+
10+
# Access request headers dictionary
11+
request_headers = get_request_headers()
12+
13+
# Map of lowercase header name to desired parcel case header name
14+
request_header_translation = {"x-request-id": "X-Request-ID", "x-correlation-id": "X-Correlation-ID"}
15+
16+
# Loop through request headers
17+
for original_header_name, header_value in request_headers.items():
18+
desired_header_name = request_header_translation.get(original_header_name.lower())
19+
if desired_header_name:
20+
# Store original header against the desired name for mirroring in response
21+
flow.setVariable(
22+
"original_request_header." + desired_header_name, "%s=%s" % (original_header_name, header_value)
23+
)
24+
# Remove original request header
25+
flow.removeVariable("request.header." + original_header_name)
26+
# Set header with correct casing
27+
flow.setVariable("request.header." + desired_header_name, header_value)

proxies/live/apiproxy/targets/target.xml

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
<Step>
99
<Name>FlowCallout.ApplyRateLimiting</Name>
1010
</Step>
11+
<Step>
12+
<Name>TranslateRequestHeaders</Name>
13+
</Step>
1114
<Step>
1215
<Name>AddDeveloperAppData</Name>
1316
</Step>
@@ -45,7 +48,7 @@
4548
</Step>
4649
</Request>
4750
</Flow>
48-
<Flow name ="User Restricted">
51+
<Flow name="User Restricted">
4952
<Condition>accesstoken.auth_type = "user"</Condition>
5053
<Request>
5154
<Step>
@@ -64,28 +67,44 @@
6467
</Request>
6568
</Flow>
6669
</Flows>
67-
6870
<PostFlow>
6971
<Response>
7072
<Step>
7173
<Name>SetMimeType</Name>
7274
</Step>
75+
<Step>
76+
<Name>MirrorRequestHeaders</Name>
77+
</Step>
7378
</Response>
7479
</PostFlow>
7580
<FaultRules>
81+
<DefaultFaultRule name="default">
82+
<Step>
83+
<Name>MirrorRequestHeaders</Name>
84+
</Step>
85+
</DefaultFaultRule>
7686
<FaultRule name="401_Unauthorized">
87+
<Step>
88+
<Name>MirrorRequestHeaders</Name>
89+
</Step>
7790
<Step>
7891
<Name>RaiseFault.401Unauthorized</Name>
7992
</Step>
8093
<Condition>oauthV2.OauthV2.VerifyAccessToken.failed = true or fault.name = "invalid_access_token" or fault.name = "InvalidAccessToken" or fault.name = "access_token_not_approved" or fault.name = "apiresource_doesnot_exist" or fault.name = "InvalidAPICallAsNo" or fault.name = "ApiProductMatchFound" or fault.name = "access_token_expired"</Condition>
8194
</FaultRule>
8295
<FaultRule name="404_Not_Found">
96+
<Step>
97+
<Name>MirrorRequestHeaders</Name>
98+
</Step>
8399
<Step>
84100
<Name>RaiseFault.404NotFound</Name>
85101
</Step>
86102
<Condition>response.header.x-amzn-ErrorType = "IncompleteSignatureException"</Condition>
87103
</FaultRule>
88104
<FaultRule name="ApplicationOperationOutcome">
105+
<Step>
106+
<Name>MirrorRequestHeaders</Name>
107+
</Step>
89108
<Step>
90109
<Name>RaiseFault.ApplicationOperationOutcome</Name>
91110
<Condition>response.header.x-amzn-RequestId != null</Condition>
@@ -101,4 +120,4 @@
101120
<Property name="request.retain.headers">User-Agent,Referer,Accept-Language</Property>
102121
</Properties>
103122
</HTTPTargetConnection>
104-
</TargetEndpoint>
123+
</TargetEndpoint>

specification/validated-relationships-service-api.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ info:
8787
|[NHS login - combined authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-login-combined-authentication-and-authorisation) |OAuth 2.0 authorisation code with API key and secret |No need to integrate and onboard separately with NHS login. |No access to user information. |
8888
|[NHS login - separate authentication and authorisation](https://digital.nhs.uk/developer/guides-and-documentation/security-and-authorisation/user-restricted-restful-apis-nhs-login-separate-authentication-and-authorisation) |OAuth 2.0 token exchange with signed JWT |Gives access to user information. |Need to integrate and onboard separately with NHS login. |
8989
90+
## Headers
91+
This API is case-insensitive when processing request headers, meaning it will accept headers regardless of the letter casing used. (e.g. X-Request-Id, x-request-id are treated the same). When sending headers back in the response, we preserve the exact casing as received in the original request.
92+
9093
## Errors
9194
We use standard HTTP status codes to show whether an API request succeeded or not. They are usually in the range:
9295

0 commit comments

Comments
 (0)