From 1779e0378003e4d9de444e72b603f5694ca7c08d Mon Sep 17 00:00:00 2001 From: NautiyalVikas1 <157499123+NautiyalVikas1@users.noreply.github.com> Date: Fri, 30 May 2025 11:03:48 +0100 Subject: [PATCH 1/4] APM-6193-TestCaseUpdateSingleASID --- .../policies/FlowCalloutSingleASIDApply.xml | 7 + proxies/live/apiproxy/proxies/default.xml | 356 ++++++++++-------- tests/test_single_asid_apply.py | 188 +++++++++ 3 files changed, 386 insertions(+), 165 deletions(-) create mode 100644 proxies/live/apiproxy/policies/FlowCalloutSingleASIDApply.xml create mode 100644 tests/test_single_asid_apply.py diff --git a/proxies/live/apiproxy/policies/FlowCalloutSingleASIDApply.xml b/proxies/live/apiproxy/policies/FlowCalloutSingleASIDApply.xml new file mode 100644 index 0000000..7041fef --- /dev/null +++ b/proxies/live/apiproxy/policies/FlowCalloutSingleASIDApply.xml @@ -0,0 +1,7 @@ + + + FlowCallout.SingleASIDApply + + + SingleASIDApply + diff --git a/proxies/live/apiproxy/proxies/default.xml b/proxies/live/apiproxy/proxies/default.xml index 876f6d9..af0a813 100644 --- a/proxies/live/apiproxy/proxies/default.xml +++ b/proxies/live/apiproxy/proxies/default.xml @@ -1,167 +1,193 @@ + - - - - - FlowCallout.UserRoleService - - - - - AssignMessage.AddPayloadToPing - - - proxy.pathsuffix MatchesPath "/user-role-service" - - - - - FlowCallout.UserRoleServiceV2.CustomHeader - - - - - AssignMessage.AddPayloadToPing - - - proxy.pathsuffix MatchesPath "/user-role-service-v2-custom-header" - - - - - FlowCallout.UserRoleServiceV2.DefaultHeader - - - - - AssignMessage.AddPayloadToPing - - - proxy.pathsuffix MatchesPath "/user-role-service-v2-default-header" - - - - - - AssignMessage.AddCors - - - (request.verb = "OPTIONS") and (request.header.origin != null) and (request.header.Access-Control-Request-Method != null) - - - - - - - AssignMessage.AddPayloadToPing - - - (proxy.pathsuffix MatchesPath "/_ping") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - - - request.header.apikey = null or private.common.status-endpoint-api-key != request.header.apikey - RaiseFault.401Unauthorized - - - ServiceCallout.CallHealthcheckEndpoint - - - - - javascript.SetStatusResponse - - - (proxy.pathsuffix MatchesPath "/_status") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + FlowCallout.UserRoleService + + + + + AssignMessage.AddPayloadToPing + + + proxy.pathsuffix MatchesPath "/user-role-service" + + + + + FlowCallout.UserRoleServiceV2.CustomHeader + + + + + AssignMessage.AddPayloadToPing + + + proxy.pathsuffix MatchesPath "/user-role-service-v2-custom-header" + + + + + FlowCallout.UserRoleServiceV2.DefaultHeader + + + + + AssignMessage.AddPayloadToPing + + + proxy.pathsuffix MatchesPath "/user-role-service-v2-default-header" + + + + + + AssignMessage.AddCors + + + (request.verb = "OPTIONS") and (request.header.origin != null) and (request.header.Access-Control-Request-Method != null) + + + + + + + AssignMessage.AddPayloadToPing + + + (proxy.pathsuffix MatchesPath "/_ping") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + request.header.apikey = null or private.common.status-endpoint-api-key != request.header.apikey + RaiseFault.401Unauthorized + + + ServiceCallout.CallHealthcheckEndpoint + + + + + javascript.SetStatusResponse + + + (proxy.pathsuffix MatchesPath "/_status") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - - - OauthV2.VerifyAccessToken - - - AssignMessage.Swap.RequestHeaders - - - (proxy.pathsuffix MatchesPath "/splunk-test") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - - - VerifyApiKey.Apikey - - - (proxy.pathsuffix MatchesPath "/apikey-protected") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - (proxy.pathsuffix MatchesPath "/open-access") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - - - OauthV2.VerifyAccessToken - - - FlowCallout.ExtendedAttributes - - - - - AssignMessage.AddPayloadToPing - - - (proxy.pathsuffix MatchesPath "/extended-attributes") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - - - FlowCallout.EnhancedVerifyApiKey - - - (proxy.pathsuffix MatchesPath "/enhanced-verify-api-key") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - - - - - FlowCallout.LogToSplunk - - - - - {{ SERVICE_BASE_PATH }} - secure - - - (request.verb = "OPTIONS") and (request.header.origin != null) and (request.header.Access-Control-Request-Method != null) - - - (proxy.pathsuffix MatchesPath "/_ping") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - (proxy.pathsuffix MatchesPath "/_status") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - (proxy.pathsuffix MatchesPath "/user-role-service") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - (proxy.pathsuffix MatchesPath "/user-role-service-v2-custom-header") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - (proxy.pathsuffix MatchesPath "/user-role-service-v2-default-header") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - (proxy.pathsuffix MatchesPath "/extended-attributes") and ((request.verb = "GET") or (request.verb = "HEAD")) - - - shared-flow-testing-target - - - - AssignMessage.Errors.CatchAllMessage - - - + + + + + OauthV2.VerifyAccessToken + + + AssignMessage.Swap.RequestHeaders + + + (proxy.pathsuffix MatchesPath "/splunk-test") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + VerifyApiKey.Apikey + + + (proxy.pathsuffix MatchesPath "/apikey-protected") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/open-access") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + OauthV2.VerifyAccessToken + + + FlowCallout.ExtendedAttributes + + + + + AssignMessage.AddPayloadToPing + + + (proxy.pathsuffix MatchesPath "/extended-attributes") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + FlowCallout.EnhancedVerifyApiKey + + + (proxy.pathsuffix MatchesPath "/enhanced-verify-api-key") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + + OauthV2.VerifyAccessToken + + + FlowCalloutExtendedAttributes + + + FlowCalloutSingleASIDApply + + + + (proxy.pathsuffix MatchesPath "/single-asid") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + + + + AssignMessage.SetApimGuids + + + + + + + FlowCallout.LogToSplunk + + + + + shared-flow-testing + secure + + + (request.verb = "OPTIONS") and (request.header.origin != null) and (request.header.Access-Control-Request-Method != null) + + + (proxy.pathsuffix MatchesPath "/_ping") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/_status") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/user-role-service") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/user-role-service-v2-custom-header") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/user-role-service-v2-default-header") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/extended-attributes") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + (proxy.pathsuffix MatchesPath "/single-asid") and ((request.verb = "GET") or (request.verb = "HEAD")) + + + shared-flow-testing-target + + + + AssignMessage.Errors.CatchAllMessage + + + \ No newline at end of file diff --git a/tests/test_single_asid_apply.py b/tests/test_single_asid_apply.py new file mode 100644 index 0000000..8474ffe --- /dev/null +++ b/tests/test_single_asid_apply.py @@ -0,0 +1,188 @@ +import pytest +import requests +import jwt + +from uuid import uuid4 +from time import time + +from tests.utils.config import ENV +from tests.utils.helpers import get_variable_from_trace + + +class TestSingleAsidApply: + """Test Single Asid Apply are available""" + + @staticmethod + def _get_token_client_credentials(client_id, private_key): + claims = { + "sub": client_id, + "iss": client_id, + "jti": str(uuid4()), + "aud": ENV["oauth_base_uri"] + "/token", + "exp": int(time()) + 300, # 5 minutes in the future + } + + client_assertion = jwt.encode( + claims, private_key, algorithm="RS512", headers={"kid": "test-1"} + ) + token_data = { + "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + "client_assertion": client_assertion, + "grant_type": "client_credentials", + } + + token_resp = requests.post( + ENV["oauth_base_uri"] + "/token", + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data=token_data, + ) + + assert token_resp.status_code == 200 + + return token_resp.json()["access_token"] + + @pytest.mark.parametrize( + "new_attribute,flow_vars_to_check", + [ + pytest.param( + { + "name": "apim-app-flow-vars", + "value": '{"proxy":{"allowed":{"update":true}}}', + }, + [{"name": "apim-app-flow-vars.proxy.allowed.update", "value": "true"}], + id="Single attribute", + ), + pytest.param( + { + "name": "apim-app-flow-vars", + "value": '{"attr_a": "value_a", "attr_b": "value_b"}', + }, + [ + {"name": "apim-app-flow-vars.attr_a", "value": "value_a"}, + {"name": "apim-app-flow-vars.attr_b", "value": "value_b"}, + ], + id="Multiple attributes", + ), + ], + ) + def test_single_asid_apply( + self, + nhsd_apim_proxy_url, + _create_function_scoped_test_app, + _proxy_product_with_scope, + _jwt_keys, + developer_apps_api, + nhsd_apim_config, + trace, + new_attribute, + flow_vars_to_check, + ): + # Update test app with proxy product and the single asid + app = _create_function_scoped_test_app + app["apiProducts"] = [_proxy_product_with_scope["name"]] + app["attributes"].append(new_attribute) + app = developer_apps_api.put_app_by_name( + email=nhsd_apim_config["APIGEE_DEVELOPER"], app_name=app["name"], body=app + ) + + # Trace call to shared flow proxy single asid + session_name = str(uuid4()) + header_filters = {"trace_id": session_name} + trace.post_debugsession(session=session_name, header_filters=header_filters) + + access_token = self._get_token_client_credentials( + client_id=app["credentials"][0]["consumerKey"], + private_key=_jwt_keys["private_key_pem"], + ) + proxy_resp = requests.get( + url=f"{nhsd_apim_proxy_url}/single-asid", + headers={"Authorization": f"Bearer {access_token}","NHSD-End-User-Organisation-ODS": "X09001", **header_filters}, + ) + assert proxy_resp.status_code == 200 + + # Extract variables from trace and assert + extended_attributes = get_variable_from_trace( + trace, session_name, "app." + new_attribute["name"] + ) + assert extended_attributes == new_attribute["value"] + + for flow_var in flow_vars_to_check: + assert flow_var["value"] == get_variable_from_trace( + trace, session_name, flow_var["name"] + ) + + trace.delete_debugsession_by_name(session_name) + + # @pytest.mark.nhsd_apim_authorization(access="application", level="level3") + # def test_no_attribute( + # self, + # nhsd_apim_proxy_url, + # nhsd_apim_auth_headers, + # trace, + # ): + # # Trace call to shared flow proxy extended attributes endpoint + # session_name = str(uuid4()) + # header_filters = {"trace_id": session_name} + # trace.post_debugsession(session=session_name, header_filters=header_filters) + + # proxy_resp = requests.get( + # url=f"{nhsd_apim_proxy_url}/single-asid", + # headers={**nhsd_apim_auth_headers, **header_filters}, + # ) + # assert proxy_resp.status_code == 200 + + # # Extract variable from trace and assert + # extended_attributes = get_variable_from_trace( + # trace, session_name, "app.apim-app-flow-vars" + # ) + + # trace.delete_debugsession_by_name(session_name) + + # assert not extended_attributes + + # def test_invalid_json( + # self, + # nhsd_apim_proxy_url, + # _create_function_scoped_test_app, + # _proxy_product_with_scope, + # _jwt_keys, + # developer_apps_api, + # nhsd_apim_config, + # trace, + # ): + # new_attribute = { + # "name": "apim-app-flow-vars", + # "value": '{"proxy":{{"allowed":{"update":true}}}', + # } + + # # Update test app with proxy product and the extended attribute + # app = _create_function_scoped_test_app + # app["apiProducts"] = [_proxy_product_with_scope["name"]] + # app["attributes"].append(new_attribute) + # app = developer_apps_api.put_app_by_name( + # email=nhsd_apim_config["APIGEE_DEVELOPER"], app_name=app["name"], body=app + # ) + + # # Trace call to shared flow proxy extended attributes endpoint + # session_name = str(uuid4()) + # header_filters = {"trace_id": session_name} + # trace.post_debugsession(session=session_name, header_filters=header_filters) + + # access_token = self._get_token_client_credentials( + # client_id=app["credentials"][0]["consumerKey"], + # private_key=_jwt_keys["private_key_pem"], + # ) + # proxy_resp = requests.get( + # url=f"{nhsd_apim_proxy_url}/single-asid", + # headers={"Authorization": f"Bearer {access_token}", **header_filters}, + # ) + # assert proxy_resp.status_code == 500 + + # # Var is None unless the InvalidJson RaiseFault error has been thrown + # raise_fault_var = get_variable_from_trace( + # trace, session_name, "raisefault.RaiseFault.InvalidJson" + # ) + + # trace.delete_debugsession_by_name(session_name) + + # assert raise_fault_var is not None From 8def126f8600f699abff373a187761cbb9f7765b Mon Sep 17 00:00:00 2001 From: NautiyalVikas1 <157499123+NautiyalVikas1@users.noreply.github.com> Date: Fri, 30 May 2025 11:30:56 +0100 Subject: [PATCH 2/4] APM-6193-TestCaseUpdateSingleASID --- tests/test_single_asid_apply.py | 99 +-------------------------------- 1 file changed, 1 insertion(+), 98 deletions(-) diff --git a/tests/test_single_asid_apply.py b/tests/test_single_asid_apply.py index 8474ffe..630a953 100644 --- a/tests/test_single_asid_apply.py +++ b/tests/test_single_asid_apply.py @@ -41,30 +41,7 @@ def _get_token_client_credentials(client_id, private_key): return token_resp.json()["access_token"] - @pytest.mark.parametrize( - "new_attribute,flow_vars_to_check", - [ - pytest.param( - { - "name": "apim-app-flow-vars", - "value": '{"proxy":{"allowed":{"update":true}}}', - }, - [{"name": "apim-app-flow-vars.proxy.allowed.update", "value": "true"}], - id="Single attribute", - ), - pytest.param( - { - "name": "apim-app-flow-vars", - "value": '{"attr_a": "value_a", "attr_b": "value_b"}', - }, - [ - {"name": "apim-app-flow-vars.attr_a", "value": "value_a"}, - {"name": "apim-app-flow-vars.attr_b", "value": "value_b"}, - ], - id="Multiple attributes", - ), - ], - ) + def test_single_asid_apply( self, nhsd_apim_proxy_url, @@ -112,77 +89,3 @@ def test_single_asid_apply( ) trace.delete_debugsession_by_name(session_name) - - # @pytest.mark.nhsd_apim_authorization(access="application", level="level3") - # def test_no_attribute( - # self, - # nhsd_apim_proxy_url, - # nhsd_apim_auth_headers, - # trace, - # ): - # # Trace call to shared flow proxy extended attributes endpoint - # session_name = str(uuid4()) - # header_filters = {"trace_id": session_name} - # trace.post_debugsession(session=session_name, header_filters=header_filters) - - # proxy_resp = requests.get( - # url=f"{nhsd_apim_proxy_url}/single-asid", - # headers={**nhsd_apim_auth_headers, **header_filters}, - # ) - # assert proxy_resp.status_code == 200 - - # # Extract variable from trace and assert - # extended_attributes = get_variable_from_trace( - # trace, session_name, "app.apim-app-flow-vars" - # ) - - # trace.delete_debugsession_by_name(session_name) - - # assert not extended_attributes - - # def test_invalid_json( - # self, - # nhsd_apim_proxy_url, - # _create_function_scoped_test_app, - # _proxy_product_with_scope, - # _jwt_keys, - # developer_apps_api, - # nhsd_apim_config, - # trace, - # ): - # new_attribute = { - # "name": "apim-app-flow-vars", - # "value": '{"proxy":{{"allowed":{"update":true}}}', - # } - - # # Update test app with proxy product and the extended attribute - # app = _create_function_scoped_test_app - # app["apiProducts"] = [_proxy_product_with_scope["name"]] - # app["attributes"].append(new_attribute) - # app = developer_apps_api.put_app_by_name( - # email=nhsd_apim_config["APIGEE_DEVELOPER"], app_name=app["name"], body=app - # ) - - # # Trace call to shared flow proxy extended attributes endpoint - # session_name = str(uuid4()) - # header_filters = {"trace_id": session_name} - # trace.post_debugsession(session=session_name, header_filters=header_filters) - - # access_token = self._get_token_client_credentials( - # client_id=app["credentials"][0]["consumerKey"], - # private_key=_jwt_keys["private_key_pem"], - # ) - # proxy_resp = requests.get( - # url=f"{nhsd_apim_proxy_url}/single-asid", - # headers={"Authorization": f"Bearer {access_token}", **header_filters}, - # ) - # assert proxy_resp.status_code == 500 - - # # Var is None unless the InvalidJson RaiseFault error has been thrown - # raise_fault_var = get_variable_from_trace( - # trace, session_name, "raisefault.RaiseFault.InvalidJson" - # ) - - # trace.delete_debugsession_by_name(session_name) - - # assert raise_fault_var is not None From d13b24e36875062f9cc7a47a682fb6d8cc16b7ab Mon Sep 17 00:00:00 2001 From: NautiyalVikas1 <157499123+NautiyalVikas1@users.noreply.github.com> Date: Tue, 3 Jun 2025 10:33:57 +0100 Subject: [PATCH 3/4] APM-6193-Test Case --- tests/test_single_asid_apply.py | 49 ++++++--------------------------- 1 file changed, 9 insertions(+), 40 deletions(-) diff --git a/tests/test_single_asid_apply.py b/tests/test_single_asid_apply.py index 630a953..f283f06 100644 --- a/tests/test_single_asid_apply.py +++ b/tests/test_single_asid_apply.py @@ -40,52 +40,21 @@ def _get_token_client_credentials(client_id, private_key): assert token_resp.status_code == 200 return token_resp.json()["access_token"] - - - def test_single_asid_apply( + @pytest.mark.parametrize("expected_status_code, expected_message", [(400, "no_headers")]) + def test_no_header_present( self, nhsd_apim_proxy_url, - _create_function_scoped_test_app, - _proxy_product_with_scope, - _jwt_keys, - developer_apps_api, - nhsd_apim_config, - trace, - new_attribute, - flow_vars_to_check, + expected_status_code, + expected_message ): - # Update test app with proxy product and the single asid - app = _create_function_scoped_test_app - app["apiProducts"] = [_proxy_product_with_scope["name"]] - app["attributes"].append(new_attribute) - app = developer_apps_api.put_app_by_name( - email=nhsd_apim_config["APIGEE_DEVELOPER"], app_name=app["name"], body=app - ) - - # Trace call to shared flow proxy single asid - session_name = str(uuid4()) - header_filters = {"trace_id": session_name} - trace.post_debugsession(session=session_name, header_filters=header_filters) + # Create test app and don't define any apiProducts - access_token = self._get_token_client_credentials( - client_id=app["credentials"][0]["consumerKey"], - private_key=_jwt_keys["private_key_pem"], - ) proxy_resp = requests.get( url=f"{nhsd_apim_proxy_url}/single-asid", - headers={"Authorization": f"Bearer {access_token}","NHSD-End-User-Organisation-ODS": "X09001", **header_filters}, - ) - assert proxy_resp.status_code == 200 - - # Extract variables from trace and assert - extended_attributes = get_variable_from_trace( - trace, session_name, "app." + new_attribute["name"] + timeout=60 ) - assert extended_attributes == new_attribute["value"] - for flow_var in flow_vars_to_check: - assert flow_var["value"] == get_variable_from_trace( - trace, session_name, flow_var["name"] - ) + assert proxy_resp.status_code == expected_status_code + assert proxy_resp.json().get("error") == expected_message + - trace.delete_debugsession_by_name(session_name) From 334996a2b66775041bcc56fe073ddbd14b33edfd Mon Sep 17 00:00:00 2001 From: NautiyalVikas1 <157499123+NautiyalVikas1@users.noreply.github.com> Date: Tue, 3 Jun 2025 10:36:20 +0100 Subject: [PATCH 4/4] APM-6193-Test Case --- tests/test_single_asid_apply.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/tests/test_single_asid_apply.py b/tests/test_single_asid_apply.py index f283f06..d3fdcf1 100644 --- a/tests/test_single_asid_apply.py +++ b/tests/test_single_asid_apply.py @@ -11,35 +11,6 @@ class TestSingleAsidApply: """Test Single Asid Apply are available""" - - @staticmethod - def _get_token_client_credentials(client_id, private_key): - claims = { - "sub": client_id, - "iss": client_id, - "jti": str(uuid4()), - "aud": ENV["oauth_base_uri"] + "/token", - "exp": int(time()) + 300, # 5 minutes in the future - } - - client_assertion = jwt.encode( - claims, private_key, algorithm="RS512", headers={"kid": "test-1"} - ) - token_data = { - "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", - "client_assertion": client_assertion, - "grant_type": "client_credentials", - } - - token_resp = requests.post( - ENV["oauth_base_uri"] + "/token", - headers={"Content-Type": "application/x-www-form-urlencoded"}, - data=token_data, - ) - - assert token_resp.status_code == 200 - - return token_resp.json()["access_token"] @pytest.mark.parametrize("expected_status_code, expected_message", [(400, "no_headers")]) def test_no_header_present( self,