async def test_nhs_login_user_restricted_scope_combination( self, product_1_scopes, product_2_scopes, expected_filtered_scopes, test_app_and_product, helper, auth_code_nhs_login, ): test_product, test_product2, test_app = test_app_and_product await test_product.update_scopes(product_1_scopes) await test_product2.update_scopes(product_2_scopes) apigee_trace = ApigeeApiTraceDebug(proxy=config.SERVICE_NAME) callback_url = await test_app.get_callback_url() state = await auth_code_nhs_login.get_state(self.oauth, test_app) auth_code = await auth_code_nhs_login.make_auth_request( self.oauth, state) await apigee_trace.start_trace() await auth_code_nhs_login.make_callback_request( self.oauth, state, auth_code) user_restricted_scopes = await apigee_trace.get_apigee_variable_from_trace( name="apigee.user_restricted_scopes") assert ( user_restricted_scopes is not None ), "variable apigee.user_restricted_scopes not found in the trace" user_restricted_scopes = user_restricted_scopes.split(" ") assert expected_filtered_scopes.sort() == user_restricted_scopes.sort()
async def test_access_token_fields_for_logging_when_using_token_exchange_cis2(self): # Given apigee_trace = ApigeeApiTraceDebug(proxy=f"hello-world-{ENVIRONMENT}") id_token_jwt = self.oauth.create_id_token_jwt() client_assertion_jwt = self.oauth.create_jwt(kid='test-1') resp = await self.oauth.get_token_response(grant_type='token_exchange', _jwt=client_assertion_jwt, id_token_jwt=id_token_jwt) access_token = resp['body']['access_token'] # When await apigee_trace.start_trace() requests.get(f"{HELLO_WORLD_API_URL}", headers={"Authorization": f"Bearer {access_token}"}) # Then auth_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_type') auth_grant_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_grant_type') auth_provider = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_provider') auth_level = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_level') auth_user_id = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_user_id') assert auth_type == 'user' assert auth_grant_type == 'token_exchange' assert auth_provider == 'nhs-cis2' assert auth_level == 'aal3' assert auth_user_id == '787807429511'
async def test_access_token_fields_for_logging_when_using_client_credentials(self): # Given apigee_trace = ApigeeApiTraceDebug(proxy=f"hello-world-{ENVIRONMENT}") jwt_claims = { 'kid': 'test-1', 'claims': { "sub": self.oauth.client_id, "iss": self.oauth.client_id, "jti": str(uuid4()), "aud": f"{OAUTH_URL}/token", "exp": int(time()), } } jwt = self.oauth.create_jwt(**jwt_claims) resp = await self.oauth.get_token_response(grant_type='client_credentials', _jwt=jwt) access_token = resp['body']['access_token'] # When await apigee_trace.start_trace() requests.get(f"{HELLO_WORLD_API_URL}", headers={"Authorization": f"Bearer {access_token}"}) # Then auth_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_type') auth_grant_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_grant_type') auth_level = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_level') auth_provider = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_provider') auth_user_id = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_user_id') assert auth_type == 'app' assert auth_grant_type == 'client_credentials' assert auth_level == 'level3' assert auth_provider == 'apim' assert auth_user_id == ''
async def test_valid_application_restricted_scope_combination( self, product_1_scopes, product_2_scopes, expected_filtered_scopes, test_app_and_product, ): test_product, test_product2, test_app = test_app_and_product apigee_trace = ApigeeApiTraceDebug(proxy=config.SERVICE_NAME) await test_product.update_scopes(product_1_scopes) await test_product2.update_scopes(product_2_scopes) jwt = self.oauth.create_jwt(kid="test-1", client_id=test_app.client_id) await apigee_trace.start_trace() resp = await self.oauth.get_token_response( grant_type="client_credentials", _jwt=jwt) application_scope = await apigee_trace.get_apigee_variable_from_trace( name="apigee.application_restricted_scopes") assert ( application_scope is not None ), "variable apigee.user_restricted_scopes not found in the trace" application_scope = application_scope.split(" ") assert list(resp["body"].keys()) == [ "access_token", "expires_in", "token_type", "issued_at", ] assert resp["status_code"] == 200 assert application_scope.sort() == expected_filtered_scopes.sort()
async def test_splunk_fields_for_authorize_endpoint_for_cis2(self): debug = ApigeeApiTraceDebug(proxy=config.SERVICE_NAME) await debug.start_trace() await self.oauth.hit_oauth_endpoint( method="GET", endpoint="authorize", params={ "client_id": self.oauth.client_id, "redirect_uri": self.oauth.redirect_uri, "response_type": "code", "state": random.getrandbits(32), }, ) payload = await self._get_payload_from_splunk(debug) # Then auth = payload["auth"] auth_meta = auth["meta"] assert auth_meta["auth_type"] == "user" assert auth_meta["grant_type"] == "authorization_code" assert auth_meta[ "level"] == "" # level is unknown when hitting /authorize assert auth_meta["provider"] == "nhs-cis2" auth_user = auth["user"] assert auth_user[ "user_id"] == "" # user_id is unknown when hitting /authorize
async def test_apigee_stop_trace_without_starting_trace(): api = ApigeeApiTraceDebug(proxy="personal-demographics-pr-538") with pytest.raises(RuntimeError) as exec_info: await api.stop_trace() assert str( exec_info.value ) == 'You must run start_trace() before you can run stop_trace()'
async def test_apigee_trace_timeout(): api = ApigeeApiTraceDebug(proxy="personal-demographics-pr-538", timeout=2) resp = await api.start_trace() assert resp['status_code'] == 201 with pytest.raises(TimeoutError) as exec_info: sleep(2) # Wait for session to timeout await api.get_trace_data() assert str( exec_info.value ) == "Your session has timed out, please rerun the start_trace() method again"
async def test_cis2_user_restricted_scope_combination( self, product_1_scopes, product_2_scopes, expected_filtered_scopes, test_app_and_product, helper, ): test_product, test_product2, test_app = test_app_and_product await test_product.update_scopes(product_1_scopes) await test_product2.update_scopes(product_2_scopes) apigee_trace = ApigeeApiTraceDebug(proxy=config.SERVICE_NAME) callback_url = await test_app.get_callback_url() oauth = OauthHelper(test_app.client_id, test_app.client_secret, callback_url) apigee_trace.add_trace_filter(header_name="Auto-Test-Header", header_value="flow-callback") await apigee_trace.start_trace() assert helper.check_endpoint( verb="POST", endpoint=f"{config.OAUTH_URL}/token", expected_status_code=200, expected_response=[ "access_token", "expires_in", "refresh_count", "refresh_token", "refresh_token_expires_in", "sid", "token_type", ], data={ "client_id": test_app.get_client_id(), "client_secret": test_app.get_client_secret(), "redirect_uri": callback_url, "grant_type": "authorization_code", "code": await oauth.get_authenticated_with_simulated_auth(), }, ) user_restricted_scopes = await apigee_trace.get_apigee_variable_from_trace( name="apigee.user_restricted_scopes") assert ( user_restricted_scopes is not None ), "variable apigee.user_restricted_scopes not found in the trace" user_restricted_scopes = user_restricted_scopes.split(" ") assert expected_filtered_scopes.sort() == user_restricted_scopes.sort()
async def _api(): """ Setup and Teardown """ api = ApigeeApiTraceDebug(proxy="personal-demographics-pr-538") print(f"\nStarting Trace on proxy {api.proxy}...") await api.start_trace() yield api # teardown try: print(f"\nStopping Trace on proxy {api.proxy}...") await api.stop_trace() except RuntimeError: # Session already stopped or has timed out pass
async def test_access_token_fields_for_logging_when_using_authorization_code_cis2(self, helper): # Given apigee_trace = ApigeeApiTraceDebug(proxy=f"hello-world-{ENVIRONMENT}") # When await apigee_trace.start_trace() requests.get(f"{HELLO_WORLD_API_URL}", headers={"Authorization": f"Bearer {self.oauth.access_token}"}) # Then auth_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_type') auth_grant_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_grant_type') auth_level = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_level') auth_provider = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_provider') auth_user_id = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_user_id') assert auth_type == 'user' assert auth_grant_type == 'authorization_code' assert auth_level == 'aal3' assert auth_provider == 'nhs-cis2' assert auth_user_id == '787807429511'
async def test_access_token_fields_for_logging_when_using_authorization_code_nhs_login(self, scope, helper): # Given apigee_trace = ApigeeApiTraceDebug(proxy=f"hello-world-{ENVIRONMENT}") # Make authorize request to retrieve state2 response = await self.oauth.hit_oauth_endpoint( method="GET", endpoint="authorize", params={ "client_id": self.oauth.client_id, "redirect_uri": self.oauth.redirect_uri, "response_type": "code", "state": "1234567890", "scope": "nhs-login" }, allow_redirects=False, ) state = helper.get_param_from_url( url=response["headers"]["Location"], param="state" ) # Make simulated auth request to authenticate response = await self.oauth.hit_oauth_endpoint( base_uri=MOCK_IDP_BASE_URL, method="POST", endpoint="nhs_login_simulated_auth", params={ "response_type": "code", "client_id": self.oauth.client_id, "redirect_uri": self.oauth.redirect_uri, "scope": "openid", "state": state, }, headers={"Content-Type": "application/x-www-form-urlencoded"}, data={ "state": state, "auth_method": scope }, allow_redirects=False, ) # Make initial callback request auth_code = helper.get_param_from_url( url=response["headers"]["Location"], param="code" ) response = await self.oauth.hit_oauth_endpoint( method="GET", endpoint="callback", params={"code": auth_code, "client_id": "some-client-id", "state": state}, allow_redirects=False, ) auth_code = helper.get_param_from_url( url=response["headers"]["Location"], param="code" ) response = await self.oauth.hit_oauth_endpoint( method="POST", endpoint="token", data={ "grant_type": "authorization_code", "state": state, "code": auth_code, "redirect_uri": self.oauth.redirect_uri, "client_id": self.oauth.client_id, "client_secret": self.oauth.client_secret }, allow_redirects=False, ) access_token = response['body']['access_token'] # When await apigee_trace.start_trace() requests.get(f"{HELLO_WORLD_API_URL}", headers={"Authorization": f"Bearer {access_token}"}) # Then auth_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_type') auth_grant_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_grant_type') auth_level = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_level') auth_provider = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_provider') auth_user_id = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_user_id') assert auth_type == 'user' assert auth_grant_type == 'authorization_code' assert auth_level == scope.lower() assert auth_provider == 'apim-mock-nhs-login' assert auth_user_id == '9912003071'
async def test_access_token_fields_for_logging_when_using_token_exchange_nhs_login(self, scope): # Given apigee_trace = ApigeeApiTraceDebug(proxy=f"hello-world-{ENVIRONMENT}") id_token_claims = { "aud": "tf_-APIM-1", "id_status": "verified", "token_use": "id", "auth_time": 1616600683, "iss": "https://internal-dev.api.service.nhs.uk", "vot": "P9.Cp.Cd", "exp": int(time()) + 600, "iat": int(time()) - 10, "vtm": "https://auth.sandpit.signin.nhs.uk/trustmark/auth.sandpit.signin.nhs.uk", "jti": str(uuid4()), "identity_proofing_level": scope, "nhs_number": "900000000001" } id_token_headers = { "sub": "49f470a1-cc52-49b7-beba-0f9cec937c46", "aud": "APIM-1", "kid": "nhs-login", "iss": "https://internal-dev.api.service.nhs.uk", "typ": "JWT", "exp": 1616604574, "iat": 1616600974, "alg": "RS512", "jti": str(uuid4()), } with open(ID_TOKEN_NHS_LOGIN_PRIVATE_KEY_ABSOLUTE_PATH, "r") as f: contents = f.read() client_assertion_jwt = self.oauth.create_jwt(kid="test-1") id_token_jwt = self.oauth.create_id_token_jwt( algorithm="RS512", claims=id_token_claims, headers=id_token_headers, signing_key=contents, ) resp = await self.oauth.get_token_response( grant_type="token_exchange", _jwt=client_assertion_jwt, id_token_jwt=id_token_jwt, ) access_token = resp['body']['access_token'] # When await apigee_trace.start_trace() requests.get(f"{HELLO_WORLD_API_URL}", headers={"Authorization": f"Bearer {access_token}"}) # Then auth_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_type') auth_grant_type = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_grant_type') auth_provider = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_provider') auth_level = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_level') auth_user_id = await apigee_trace.get_apigee_variable_from_trace(name='accesstoken.auth_user_id') assert auth_type == 'user' assert auth_grant_type == 'token_exchange' assert auth_provider == 'apim-mock-nhs-login' assert auth_level == scope.lower() assert auth_user_id == '900000000001'
async def apigee_start_trace(expected_filtered_scopes): apigee_trace = ApigeeApiTraceDebug(proxy=config.SERVICE_NAME) await apigee_trace.start_trace() return apigee_trace
async def test_splunk_fields_for_callback_endpoint_for_cis2(self, helper): # Given response = await self.oauth.hit_oauth_endpoint( method="GET", endpoint="authorize", params={ "client_id": self.oauth.client_id, "redirect_uri": self.oauth.redirect_uri, "response_type": "code", "state": "1234567890", }, allow_redirects=False, ) state = helper.get_param_from_url(url=response["headers"]["Location"], param="state") # Make simulated auth request to authenticate response = await self.oauth.hit_oauth_endpoint( base_uri=config.MOCK_IDP_BASE_URL, method="POST", endpoint="simulated_auth", params={ "response_type": "code", "client_id": self.oauth.client_id, "redirect_uri": self.oauth.redirect_uri, "scope": "openid", "state": state, }, headers={"Content-Type": "application/x-www-form-urlencoded"}, data={"state": state}, allow_redirects=False, ) # Make initial callback request auth_code = helper.get_param_from_url( url=response["headers"]["Location"], param="code") # When debug = ApigeeApiTraceDebug(proxy=config.SERVICE_NAME) await debug.start_trace() await self.oauth.hit_oauth_endpoint( method="GET", endpoint="callback", params={ "code": auth_code, "client_id": "some-client-id", "state": state }, allow_redirects=False, ) payload = await self._get_payload_from_splunk(debug) # Then auth = payload["auth"] auth_meta = auth["meta"] assert auth_meta["auth_type"] == "user" assert auth_meta["grant_type"] == "authorization_code" assert auth_meta["level"] == "aal3" assert auth_meta["provider"] == "nhs-cis2" auth_user = auth["user"] assert auth_user["user_id"] == "787807429511"
async def test_apigee_get_trace_data(): api = ApigeeApiTraceDebug(proxy='apim-test') await api.start_trace() resp = await api.get_trace_data() assert resp == {}