Skip to content

Commit b9f6a94

Browse files
[ERSSUP-89806]-[MW]-[Updated app-restricted business function validation to use new business functions]-[JW]
1 parent f6d78d7 commit b9f6a94

File tree

5 files changed

+151
-5
lines changed

5 files changed

+151
-5
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<AssignMessage enabled="true" name="AssignMessage.SetOperationOutcomeInvalidBusinessFunction">
2+
<AssignVariable>
3+
<Name>op_outcome_issue_code</Name>
4+
<Value>forbidden</Value>
5+
</AssignVariable>
6+
<AssignVariable>
7+
<Name>faultstring</Name>
8+
<Value>User does not have the required Business Function and the specified Organisation.</Value>
9+
</AssignVariable>
10+
<AssignVariable>
11+
<Name>status_code</Name>
12+
<Value>403</Value>
13+
</AssignVariable>
14+
</AssignMessage>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<RaiseFault async="false" continueOnError="false" enabled="true" name="RaiseFault.InvalidBusinessFunction">
2+
<FaultResponse>
3+
<Set>
4+
<Payload contentType="text/plain"/>
5+
<StatusCode>403</StatusCode>
6+
<ReasonPhrase>Forbidden</ReasonPhrase>
7+
</Set>
8+
</FaultResponse>
9+
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
10+
</RaiseFault>

proxies/live/apiproxy/targets/ers-target.xml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,23 @@
106106
</Step>
107107
<Condition>(raisefault.RaiseFault.MissingAsid.failed = true)</Condition>
108108
</FaultRule>
109+
<FaultRule name="invalid_business_function">
110+
<Step>
111+
<Condition>(isFhirR4Path = true)</Condition>
112+
<Name>AssignMessage.SetOperationOutcomeVariablesR4</Name>
113+
</Step>
114+
<Step>
115+
<Condition>(isFhirR4Path = false)</Condition>
116+
<Name>AssignMessage.SetOperationOutcomeVariablesPreR4</Name>
117+
</Step>
118+
<Step>
119+
<Name>AssignMessage.SetOperationOutcomeInvalidBusinessFunction</Name>
120+
</Step>
121+
<Step>
122+
<Name>AssignMessage.OperationOutcomeErrorResponse</Name>
123+
</Step>
124+
<Condition>(raisefault.RaiseFault.InvalidBusinessFunction.failed = true)</Condition>
125+
</FaultRule>
109126
</FaultRules>
110127
<PreFlow>
111128
<Request>
@@ -154,9 +171,9 @@
154171
<Flows>
155172
<Flow name="user-restricted-flow">
156173
<Condition>(accesstoken.auth_type == "user")</Condition>
157-
<Request><!--AUTHORISED_APPLICATION business function is not supported in user restricted flow --><Step>
158-
<Name>RaiseFault.403Forbidden</Name>
159-
<Condition>(request.header.nhsd-ers-business-function == "AUTHORISED_APPLICATION")</Condition>
174+
<Request><!--AUTHORISED_APPLICATION business functions are not supported in user restricted flow --><Step>
175+
<Name>RaiseFault.InvalidBusinessFunction</Name>
176+
<Condition>(request.header.nhsd-ers-business-function == "PROVIDER_AUTHORISED_APPLICATION") or (request.header.nhsd-ers-business-function == "REFERRER_AUTHORISED_APPLICATION") or (request.header.nhsd-ers-business-function == "AUTHORISED_APPLICATION")</Condition>
160177
</Step> <Step>
161178
<Name>AssignMessage.Set.x-ers-access-mode-header-user-restricted</Name>
162179
</Step> <Step>

tests/integration/test_app_restricted.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,42 @@ async def test_authorised_application_not_supported_for_user_restricted(
4646
f"{service_url}{_SPECIALTY_REF_DATA_URL}", headers=client_request_headers
4747
)
4848

49-
assert_error_response_with_body(response, _EXPECTED_CORRELATION_ID, 403)
49+
assert response.status_code == 403, (
50+
"Expected 403 response when calling the endpoint. But instead received a "
51+
+ response.status_code
52+
)
53+
54+
assert (
55+
response.headers[RenamedHeader.CORRELATION_ID.original]
56+
== _EXPECTED_CORRELATION_ID
57+
)
58+
59+
for renamed_header in RenamedHeader:
60+
assert renamed_header.renamed not in response.headers
61+
62+
# Verify the OperationOutcome payload
63+
response_data = response.json()
64+
assert response_data["resourceType"] == "OperationOutcome"
65+
assert response_data["meta"]["lastUpdated"] is not None
66+
assert len(response_data["meta"]["profile"]) == 1
67+
assert (
68+
response_data["meta"]["profile"][0]
69+
== "https://fhir.nhs.uk/STU3/StructureDefinition/eRS-OperationOutcome-1"
70+
)
71+
assert len(response_data["issue"]) == 1
72+
issue = response_data["issue"][0]
73+
assert issue["severity"] == "error"
74+
assert issue["code"] == "forbidden"
75+
assert issue["diagnostics"] == (
76+
"User does not have the required Business Function and the specified Organisation."
77+
)
78+
assert len(issue["details"]["coding"]) == 1
79+
issue_details = issue["details"]["coding"][0]
80+
assert (
81+
issue_details["system"]
82+
== "https://fhir.nhs.uk/STU3/CodeSystem/eRS-APIErrorCode-1"
83+
)
84+
assert issue_details["code"] == "NO_ACCESS"
5085

5186
def test_authorised_application_supported_for_app_restricted(
5287
self, app_restricted_access_code, service_url

tests/integration/test_headers.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,76 @@ async def test_headers_on_echo_target(
7373
expected_amr,
7474
)
7575

76+
@pytest.mark.asyncio
77+
@pytest.mark.parametrize(
78+
"business_function,endpoint_url,is_r4",
79+
[
80+
("PROVIDER_AUTHORISED_APPLICATION", "", False),
81+
("REFERRER_AUTHORISED_APPLICATION", "", False),
82+
("AUTHORISED_APPLICATION", "", False),
83+
("PROVIDER_AUTHORISED_APPLICATION", "/FHIR/STU3/", False),
84+
("REFERRER_AUTHORISED_APPLICATION", "/FHIR/STU3/", False),
85+
("AUTHORISED_APPLICATION", "/FHIR/STU3/", False),
86+
("PROVIDER_AUTHORISED_APPLICATION", "/FHIR/R4/", True),
87+
("REFERRER_AUTHORISED_APPLICATION", "/FHIR/R4/", True),
88+
("AUTHORISED_APPLICATION", "/FHIR/R4/", True),
89+
],
90+
)
91+
async def test_headers_on_echo_target_with_app_restricted_business_function(
92+
self, business_function, endpoint_url, is_r4, authenticate_user, service_url
93+
):
94+
user = Actor.RC
95+
access_code = await authenticate_user(user)
96+
client_request_headers = {
97+
_HEADER_ECHO: "", # enable echo target
98+
_HEADER_AUTHORIZATION: "Bearer " + access_code,
99+
_HEADER_REQUEST_ID: "DUMMY-VALUE",
100+
RenamedHeader.REFERRAL_ID.original: _EXPECTED_REFERRAL_ID,
101+
RenamedHeader.CORRELATION_ID.original: _EXPECTED_CORRELATION_ID,
102+
RenamedHeader.BUSINESS_FUNCTION.original: business_function,
103+
RenamedHeader.ODS_CODE.original: user.org_code,
104+
RenamedHeader.FILENAME.original: _EXPECTED_FILENAME,
105+
RenamedHeader.COMM_RULE_ORG.original: _EXPECTED_COMM_RULE_ORG,
106+
RenamedHeader.OBO_USER_ID.original: _EXPECTED_OBO_USER_ID,
107+
}
108+
109+
# Make the API call
110+
response = requests.get(
111+
service_url + endpoint_url, headers=client_request_headers
112+
)
113+
114+
assert response.status_code == 403, (
115+
"Expected a 403 response when attempting to call the endpoint, but instead received a "
116+
+ str(response.status_code)
117+
)
118+
119+
# Verify the OperationOutcome payload
120+
response_data = response.json()
121+
assert response_data["resourceType"] == "OperationOutcome"
122+
assert response_data["meta"]["lastUpdated"] is not None
123+
assert len(response_data["meta"]["profile"]) == 1
124+
assert response_data["meta"]["profile"][0] == (
125+
"https://www.hl7.org/fhir/R4/operationoutcome.html"
126+
if is_r4
127+
else "https://fhir.nhs.uk/STU3/StructureDefinition/eRS-OperationOutcome-1"
128+
)
129+
assert len(response_data["issue"]) == 1
130+
issue = response_data["issue"][0]
131+
assert issue["severity"] == "error"
132+
assert issue["code"] == "forbidden"
133+
assert issue["diagnostics"] == (
134+
"User does not have the required Business Function and the specified Organisation."
135+
)
136+
assert len(issue["details"]["coding"]) == 1
137+
issue_details = issue["details"]["coding"][0]
138+
assert (
139+
issue_details["system"]
140+
== "https://fhir.nhs.uk/CodeSystem/NHSD-API-ErrorOrWarningCode"
141+
if is_r4
142+
else "https://fhir.nhs.uk/STU3/CodeSystem/eRS-APIErrorCode-1"
143+
)
144+
assert issue_details["code"] == "ACCESS_DENIED" if is_r4 else "NO_ACCESS"
145+
76146
@pytest.mark.asyncio
77147
@pytest.mark.parametrize(
78148
"endpoint_url,is_r4",
@@ -475,7 +545,7 @@ def test_unknown_access_code(
475545
assert renamed_header.renamed not in client_response_headers
476546

477547
@pytest.mark.asyncio
478-
@pytest.mark.parametrize("service_name", [(None)])
548+
@pytest.mark.parametrize("service_name", [None])
479549
async def test_access_code_not_supported(
480550
self, referring_clinician, authenticate_user, service_url
481551
):

0 commit comments

Comments
 (0)