diff --git a/Makefile b/Makefile index 8290ddf1..8395618c 100644 --- a/Makefile +++ b/Makefile @@ -68,4 +68,4 @@ smoketest: pytest-guards poetry run pytest -v --junitxml=smoketest-report.xml -s -m smoketest e2etest: pytest-guards - poetry run pytest -v --junitxml=e2e-report.xml -s -m e2e + poetry run pytest -v --junitxml=e2e-report.xml -s -m e2e --api-name=$$API_NAME --proxy-name=$$PROXY_NAME diff --git a/azure/components/run-e2e-tests.yml b/azure/components/run-e2e-tests.yml index f39b3871..7406c2f2 100644 --- a/azure/components/run-e2e-tests.yml +++ b/azure/components/run-e2e-tests.yml @@ -6,7 +6,7 @@ steps: export SERVICE_BASE_PATH="$(SERVICE_BASE_PATH)" export STATUS_ENDPOINT_API_KEY="$(status-endpoint-api-key)" export APIGEE_PRODUCT="$(FULLY_QUALIFIED_SERVICE_NAME)" - export OAUTH_PROXY="oauth2" + export OAUTH_PROXY="oauth2-mock" export OAUTH_BASE_URI="https://$(ENVIRONMENT).api.service.nhs.uk" export JWT_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(JWT_TESTING_PRIVATE_KEY)" export ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH="$(Pipeline.Workspace)/secrets/$(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY)" diff --git a/manifest_template.yml b/manifest_template.yml index 3cd4464d..28bfd146 100644 --- a/manifest_template.yml +++ b/manifest_template.yml @@ -4,6 +4,7 @@ DESCRIPTION: Immunisation history API APIGEE_ENVIRONMENTS: - name: internal-dev display_name: Internal Development + has_mock_auth: true - name: internal-dev-sandbox display_name: Internal Development Sandbox - name: internal-qa @@ -71,6 +72,9 @@ apigee: proxies: - immunisation-history-{{ ENV.name }} - identity-service-{{ ENV.name }} +{% if ENV.has_mock_auth | default(false) %} + - identity-service-mock-{{ ENV.name }} +{% endif %} scopes: {{ MODE.scopes }} quota: {{ ENV.quota | default('300') }} quotaInterval: '1' @@ -89,4 +93,4 @@ apigee: visibility: true specId: immunisation-history-{{ ENV.name }} {% endfor %} -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/tests/api_tests.py b/tests/api_tests.py index b5f4fa7d..df2ee5d8 100644 --- a/tests/api_tests.py +++ b/tests/api_tests.py @@ -10,6 +10,8 @@ TARGET_COMBINATIONS = [["COVID19"], ["HPV", "COVID19"]] +NHS_LOGIN_ID_BY_IDENTITY_PROOFING_LEVEL = {"P0": "9912003073", "P5": "9912003072", "P9": "9912003071"} + def dict_path(raw, path: List[str]): if not raw: @@ -79,13 +81,6 @@ def test_check_immunization_is_secured(service_url): assert resp.status_code == 401 -@pytest.mark.e2e -def test_check_proxy_name(): - proxy_name = conftest.get_env("PROXY_NAME") - print(f"PROXY {proxy_name}") - print(f'PRODUCT_NAME {conftest.get_env("APIGEE_PRODUCT")}') - - @pytest.mark.e2e @pytest.mark.parametrize( "immunisation_history_app", @@ -151,21 +146,34 @@ async def test_immunization_no_auth_bearer_token_provided( @pytest.mark.e2e @pytest.mark.asyncio -@pytest.mark.nhsd_apim_authorization( - access="patient", - level="P9", - login_form={"username": "9912003071"} +@pytest.mark.parametrize( + "immunisation_history_app", + _add_authorised_targets_to_request_params( + [ + { + "suffixes": ["-user-restricted"], + "requested_proofing_level": "P9", + "identity_proofing_level": "P9", + } + ] + ), + indirect=True, ) -async def test_bad_nhs_number(service_url, _nhsd_apim_auth_token_data): +async def test_bad_nhs_number(immunisation_history_app, service_url, environment): await asyncio.sleep(1) # Add delay to tests to avoid 429 on service callout - token = _nhsd_apim_auth_token_data["access_token"] + nhs_login_id = NHS_LOGIN_ID_BY_IDENTITY_PROOFING_LEVEL.get( + immunisation_history_app["request_params"]["identity_proofing_level"]) + token = conftest.get_nhs_login_token(immunisation_history_app, environment, nhs_login_id) correlation_id = _generate_correlation_id('test_bad_nhs_number') - headers = {"Authorization": f"Bearer {token}", "X-Correlation-ID": correlation_id} resp = requests.get( - f'{service_url}/{_valid_uri_procedure_below("90000000009", "90640007")}', headers=headers + f'{service_url}/{_valid_uri_procedure_below("90000000009", "90640007")}', + headers={ + "Authorization": f'Bearer {token["access_token"]}', + "X-Correlation-ID": correlation_id, + }, ) assert resp.status_code == 400 body = resp.json() @@ -238,7 +246,8 @@ def test_correlation_id_mirrored_in_resp_when_error(service_url): ) def test_token_exchange_happy_path(immunisation_history_app, service_url, environment, _keycloak_client_credentials, _jwt_keys): - nhs_login_id = "9912003071" + nhs_login_id = NHS_LOGIN_ID_BY_IDENTITY_PROOFING_LEVEL.get( + immunisation_history_app["request_params"]["identity_proofing_level"]) token_response = conftest.get_token_nhs_login_token_exchange( immunisation_history_app, environment=environment, _keycloak_client_credentials=_keycloak_client_credentials, _jwt_keys=_jwt_keys, nhs_login_id=nhs_login_id @@ -249,7 +258,7 @@ def test_token_exchange_happy_path(immunisation_history_app, service_url, enviro headers = {"Authorization": f"Bearer {token}", "X-Correlation-ID": correlation_id} resp = requests.get( - f'{service_url}/{_valid_uri_procedure_below("9912003888", "90640007")}', headers=headers, + f'{service_url}/{_valid_uri_procedure_below(nhs_login_id, "90640007")}', headers=headers, ) assert resp.status_code == 200, "failed getting backend data" body = resp.json() @@ -259,17 +268,35 @@ def test_token_exchange_happy_path(immunisation_history_app, service_url, enviro assert len(body["entry"]) == 3, body -@pytest.mark.nhsd_apim_authorization( - access="application", - level="level3", -) +@pytest.mark.e2e @pytest.mark.parametrize( - "nhs_login_id", ["9912003072", "9912003071"] + "immunisation_history_app", + _add_authorised_targets_to_request_params( + [ + { + "suffixes": ["-application-restricted"], + "requested_proofing_level": "P9", + "identity_proofing_level": "P9", + }, + { + "suffixes": ["-application-restricted"], + "requested_proofing_level": "P5", + "identity_proofing_level": "P9", + }, + { + "suffixes": ["-application-restricted"], + "requested_proofing_level": "P5", + "identity_proofing_level": "P5", + }, + ] + ), + indirect=True, ) -def test_token_exchange_sad_path(_test_app_credentials, environment, _jwt_keys, _keycloak_client_credentials, - nhs_login_id): +def test_token_exchange_sad_path(immunisation_history_app, environment, _jwt_keys, _keycloak_client_credentials): + nhs_login_id = NHS_LOGIN_ID_BY_IDENTITY_PROOFING_LEVEL.get( + immunisation_history_app["request_params"]["identity_proofing_level"]) conftest.check_for_unauthorised_token_exchange( - _test_app_credentials, environment, _jwt_keys, _keycloak_client_credentials, nhs_login_id) + immunisation_history_app, environment, _jwt_keys, _keycloak_client_credentials, nhs_login_id) @pytest.mark.e2e @@ -294,9 +321,11 @@ async def test_user_restricted_access_not_permitted(test_product_and_app, servic _keycloak_client_credentials, _jwt_keys): await asyncio.sleep(1) # Add delay to tests to avoid 429 on service callout - nhs_login_id = "9912003071" test_product, test_app = test_product_and_app + nhs_login_id = NHS_LOGIN_ID_BY_IDENTITY_PROOFING_LEVEL.get( + test_app["request_params"]["identity_proofing_level"]) + token_response = conftest.get_token(test_app, environment, _jwt_keys) correlation_id = _generate_correlation_id('test_user_restricted_access_not_permitted') @@ -343,9 +372,10 @@ async def test_user_restricted_access_not_permitted(test_product_and_app, servic indirect=True, ) def test_token_exchange_invalid_identity_proofing_level_scope(test_product_and_app, service_url, environment, - _keycloak_client_credentials, _jwt_keys, nhs_login_id): + _keycloak_client_credentials, _jwt_keys): test_product, test_app = test_product_and_app + nhs_login_id = NHS_LOGIN_ID_BY_IDENTITY_PROOFING_LEVEL.get(test_app["request_params"]["identity_proofing_level"]) token_response = conftest.get_token_nhs_login_token_exchange( app=test_app, environment=environment, _keycloak_client_credentials=_keycloak_client_credentials, _jwt_keys=_jwt_keys, nhs_login_id=nhs_login_id diff --git a/tests/conftest.py b/tests/conftest.py index 1d9ef2c7..8df474f2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,6 +14,8 @@ ) from pytest_nhsd_apim.identity_service import ( + AuthorizationCodeConfig, + AuthorizationCodeAuthenticator, ClientCredentialsConfig, ClientCredentialsAuthenticator, KeycloakUserConfig, @@ -56,12 +58,10 @@ def nhs_login_id_token(environment: str, _keycloak_client_credentials, nhs_login def get_token( app, environment: str, _jwt_keys ): - credentials = app["credentials"][0] - client_credentials_config = ClientCredentialsConfig( environment=environment, identity_service_base_url=f"https://{environment}.api.service.nhs.uk/oauth2-mock", - client_id=credentials["consumerKey"], + client_id=app["credentials"][0]["consumerKey"], jwt_private_key=_jwt_keys["private_key_pem"], jwt_kid="test-1", ) @@ -72,6 +72,25 @@ def get_token( return token_response +def get_nhs_login_token(app, environment: str, nhs_login_id): + credentials = app["credentials"][0] + authorization_config = AuthorizationCodeConfig( + environment=environment, + identity_service_base_url=f"https://{environment}.api.service.nhs.uk/oauth2-mock", + client_id=credentials["consumerKey"], + client_secret=credentials["consumerSecret"], + scope="nhs-login", + login_form={"username": nhs_login_id}, + callback_url=app["callbackUrl"], + ) + + authenticator = AuthorizationCodeAuthenticator(config=authorization_config) + + token_response = authenticator.get_token() + assert "access_token" in token_response + return token_response + + def get_authorised_headers(app, environment: str, _jwt_keys): token = get_token(app, environment, _jwt_keys=_jwt_keys) @@ -93,7 +112,7 @@ def get_token_nhs_login_token_exchange( id_token=id_token) token_response = authenticator.get_token() - assert set(token_response["body"].keys()).issuperset( + assert set(token_response.keys()).issuperset( {"access_token", "expires_in", "token_type", "issued_token_type"} ) return token_response @@ -180,12 +199,12 @@ def service_url(environment): @pytest.fixture() -def immunisation_history_app(client: ApigeeClient, request): +def immunisation_history_app(client: ApigeeClient, jwt_public_key_url, request): """Setup & Teardown an app-restricted app for this api""" request_params = request.param custom_attributes = { - "jwks-resource-url": "https://raw.githubusercontent.com/NHSDigital/identity-service-jwks/main/jwks/internal-dev/9baed6f4-1361-4a8e-8531-1f8426e3aba8.json", + "jwks-resource-url": jwt_public_key_url, "nhs-login-allowed-proofing-level": request_params.get( "requested_proofing_level", "" ), @@ -202,12 +221,12 @@ def immunisation_history_app(client: ApigeeClient, request): custom_attributes["use_strict_authorised_targets"] = strict_mode api_products = get_product_names(request_params["suffixes"]) + app_name = f"apim-auto-{uuid4()}" developer_apps_api = DeveloperAppsAPI(client=client) app = developer_apps_api.create_app(email=APP_EMAIL, body=_create_app_body(app_name=app_name, products=api_products, custom_attributes=custom_attributes)) - print(f"CREATED APP NAME: {app_name}") app["request_params"] = request_params yield app @@ -256,7 +275,6 @@ def test_product_and_app(client: ApigeeClient, environment: str, service_name: s app = developer_apps_api.create_app(email=APP_EMAIL, body=_create_app_body(app_name=app_name, products=[product_name], custom_attributes=custom_attributes)) - print(f"CREATED APP NAME: {app_name}") app["request_params"] = request_params yield product, app