Skip to content

Commit 801d5ca

Browse files
Fix some failing unit tests, add integration tests for 403s
1 parent 423bb30 commit 801d5ca

File tree

6 files changed

+151
-24
lines changed

6 files changed

+151
-24
lines changed

apps/authorization/tests/test_data_access_grant_permissions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from apps.authorization.models import (
1414
DataAccessGrant,
1515
)
16-
from waffle.testutils import override_switch
16+
from waffle.testutils import override_switch, override_flag
1717
from apps.fhir.bluebutton.tests.test_fhir_resources_read_search_w_validation import (
1818
get_response_json,
1919
)
@@ -145,6 +145,7 @@ def setUp(self):
145145
self.client = Client()
146146

147147
@override_switch('v3_endpoints', active=True)
148+
@override_flag('v3_early_adopter', active=True)
148149
def _assert_call_all_fhir_endpoints(
149150
self,
150151
access_token=None,

apps/core/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from waffle.models import AbstractUserFlag
2+
from django.db import models
23

34

45
class Flag(AbstractUserFlag):
56
""" Custom version of waffle feature Flag model """
67
""" This makes future extensions nicer """
8+
# Added as part of BB2-4250 testing
9+
objects = models.Manager()
710

811
def is_active_for_user(self, user):
912
# use app_user which is the owner of the current app

apps/dot_ext/tests/test_authorization.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
from django.http import HttpRequest
1212
from django.urls import reverse
1313
from django.test import Client
14-
from waffle.testutils import override_switch
14+
from apps.core.models import Flag
15+
from waffle.testutils import override_switch, override_flag
1516

1617
from apps.test import BaseApiTest
1718
from ..models import Application, ArchivedToken
@@ -1020,3 +1021,66 @@ def _execute_token_endpoint(self, token_path):
10201021
c = Client()
10211022
response = c.post(token_path, data=token_request_data)
10221023
self.assertEqual(response.status_code, 200)
1024+
1025+
@override_switch('v3_endpoints', active=True)
1026+
@override_flag('v3_early_adopter', active=True)
1027+
def test_v3_token_endpoint_with_early_adopter_flag_enabled(self):
1028+
self._execute_token_endpoint('/v3/o/token/')
1029+
1030+
@override_switch('v3_endpoints', active=True)
1031+
# @override_flag('v3_early_adopter', active=False)
1032+
def test_v3_token_endpoint_with_early_adopter_flag_disabled(self):
1033+
self._execute_token_endpoint_for_flag_test('/v3/o/token/')
1034+
1035+
# @override_switch('v3_endpoints', active=True)
1036+
# @override_flag('v3_early_adopter', active=True)
1037+
# def test_v3_token_endpoint_without_trailling_slash(self):
1038+
# self._execute_token_endpoint('/v3/o/token')
1039+
1040+
def _execute_token_endpoint_for_flag_test(self, token_path):
1041+
Flag.objects.create(name='v3_early_adopter', everyone=None)
1042+
redirect_uri = 'http://localhost'
1043+
# create a user
1044+
user = self._create_user('anna', '123456')
1045+
capability_a = self._create_capability('Capability A', [])
1046+
capability_b = self._create_capability('Capability B', [])
1047+
print("USER ID: ", user.id)
1048+
# create an application and add capabilities
1049+
application = self._create_application(
1050+
'an app',
1051+
grant_type=Application.GRANT_AUTHORIZATION_CODE,
1052+
client_type=Application.CLIENT_CONFIDENTIAL,
1053+
redirect_uris=redirect_uri,
1054+
user_id=user.id)
1055+
application.scope.add(capability_a, capability_b)
1056+
# user logs in
1057+
request = HttpRequest()
1058+
self.client.login(request=request, username='anna', password='123456')
1059+
# post the authorization form with only one scope selected
1060+
payload = {
1061+
'client_id': application.client_id,
1062+
'response_type': 'code',
1063+
'redirect_uri': redirect_uri,
1064+
'scope': ['capability-a'],
1065+
'expires_in': 86400,
1066+
'allow': True,
1067+
"state": "0123456789abcdef",
1068+
'refresh_token': 'asdfj23h4q98wuafidj'
1069+
}
1070+
response = self.client.post(reverse('oauth2_provider:authorize'), data=payload)
1071+
self.client.logout()
1072+
self.assertEqual(response.status_code, 302)
1073+
# now extract the authorization code and use it to request an access_token
1074+
query_dict = parse_qs(urlparse(response['Location']).query)
1075+
authorization_code = query_dict.pop('code')
1076+
token_request_data = {
1077+
'grant_type': 'authorization_code',
1078+
'code': authorization_code,
1079+
'redirect_uri': redirect_uri,
1080+
'client_id': application.client_id,
1081+
'client_secret': application.client_secret_plain,
1082+
}
1083+
c = Client()
1084+
print("token path: ", token_path)
1085+
response = c.post(token_path, data=token_request_data)
1086+
self.assertEqual(response.status_code, 403)

apps/dot_ext/views/authorization.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,7 @@ def validate_token_endpoint_request_body(self, request):
448448

449449
def validate_v3_token_call(self, request) -> None:
450450
flag = get_waffle_flag_model().get('v3_early_adopter')
451-
req_meta = request.META
452-
url_query = parse_qs(req_meta.get('QUERY_STRING'))
451+
453452
try:
454453
url_query = parse_qs(request._body.decode('utf-8'))
455454
refresh_token_from_request = url_query.get('refresh_token', [None])

apps/fhir/bluebutton/tests/test_wellknown_endpoints.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
from collections import namedtuple as NT
44
from django.conf import settings
55
from django.test.client import Client
6-
from httmock import all_requests, HTTMock
76
from oauth2_provider.models import get_access_token_model
87
from unittest import skipIf
9-
from waffle.testutils import override_switch
8+
from waffle.testutils import override_switch, override_flag
109

1110
# Introduced in bb2-4184
1211
# Rudimentary tests to make sure endpoints exist and are returning
@@ -52,29 +51,29 @@ def setUp(self):
5251

5352
@skipIf((not settings.RUN_ONLINE_TESTS), 'Can\'t reach external sites.')
5453
@override_switch('v3_endpoints', active=True)
54+
def test_userinfo_returns_403(self):
55+
first_access_token = self.create_token('John', 'Smith', fhir_id_v2=FHIR_ID_V2)
56+
ac = AccessToken.objects.get(token=first_access_token)
57+
ac.save()
58+
59+
response = self.client.get(
60+
f'{BASEURL}/v3/connect/userinfo',
61+
Authorization='Bearer %s' % (first_access_token))
62+
self.assertEqual(response.status_code, 403)
63+
self.assertEqual(response.json()['detail'], settings.APPLICATION_DOES_NOT_HAVE_V3_ENABLED_YET.format('John_Smith_test'))
64+
65+
@skipIf((not settings.RUN_ONLINE_TESTS), 'Can\'t reach external sites.')
66+
@override_switch('v3_endpoints', active=True)
67+
@override_flag('v3_early_adopter', active=True)
5568
def test_userinfo_returns_200(self):
5669
first_access_token = self.create_token('John', 'Smith', fhir_id_v2=FHIR_ID_V2)
5770
ac = AccessToken.objects.get(token=first_access_token)
5871
ac.save()
5972

60-
@all_requests
61-
def catchall(url, req):
62-
return {'status_code': 200,
63-
'content': {
64-
'sub': FHIR_ID_V2,
65-
'name': ' ',
66-
'given_name': '',
67-
'family_name': '',
68-
'email': '',
69-
'iat': '2025-10-14T18:01:01.660Z',
70-
'patient': FHIR_ID_V2
71-
}}
72-
73-
with HTTMock(catchall):
74-
response = self.client.get(
75-
f'{BASEURL}/v3/connect/userinfo',
76-
Authorization='Bearer %s' % (first_access_token))
77-
self.assertEqual(response.status_code, 200)
73+
response = self.client.get(
74+
f'{BASEURL}/v3/connect/userinfo',
75+
Authorization='Bearer %s' % (first_access_token))
76+
self.assertEqual(response.status_code, 200)
7877

7978
# This makes sure URLs return 200s.
8079
@skipIf((not settings.RUN_ONLINE_TESTS), "Can't reach external sites.")

apps/integration_tests/integration_test_fhir_resources.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
from django.conf import settings
44
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
5+
from http import HTTPStatus
56
from oauth2_provider.models import AccessToken
67
from rest_framework.test import APIClient
78
from waffle.testutils import override_switch
89

10+
from apps.core.models import Flag
911
from apps.test import BaseApiTest
1012
from apps.testclient.utils import extract_last_page_index
1113
from .common_utils import validate_json_schema
@@ -36,6 +38,9 @@
3638
FHIR_RES_TYPE_EOB = "ExplanationOfBenefit"
3739
FHIR_RES_TYPE_PATIENT = "Patient"
3840
FHIR_RES_TYPE_COVERAGE = "Coverage"
41+
V3_403_DETAIL = 'This application, John_Doe_test, does not yet have access to v3 endpoints. ' \
42+
'If you are the app maintainer, please contact the Blue Button API team. If you are a Medicare Beneficiary ' \
43+
'and need assistance, please contact the support team for the application you are trying to access.'
3944

4045

4146
def dump_content(json_str, file_name):
@@ -759,3 +764,59 @@ def _err_response_caused_by_illegalarguments(self, v2=False):
759764
# This now returns 400 after BB2-2063 work.
760765
# for both v1 and v2
761766
self.assertEqual(response.status_code, 400)
767+
768+
@override_switch('v3_endpoints', active=True)
769+
def test_patient_read_endpoint_v3_403(self):
770+
'''
771+
test patient read and search v2
772+
'''
773+
self._call_v3_endpoint_to_assert_403(FHIR_RES_TYPE_PATIENT, settings.DEFAULT_SAMPLE_FHIR_ID_V3, False, None)
774+
775+
@override_switch('v3_endpoints', active=True)
776+
def test_coverage_read_endpoint_v3_403(self):
777+
'''
778+
test patient read and search v2
779+
'''
780+
self._call_v3_endpoint_to_assert_403(FHIR_RES_TYPE_COVERAGE, 'part-a-99999999999999', False, None)
781+
782+
@override_switch('v3_endpoints', active=True)
783+
def test_eob_read_endpoint_v3_403(self):
784+
'''
785+
test patient read and search v2
786+
'''
787+
self._call_v3_endpoint_to_assert_403(FHIR_RES_TYPE_EOB, 'outpatient--9999999999999', False, None)
788+
789+
@override_switch('v3_endpoints', active=True)
790+
def test_patient_search_endpoint_v3_403(self):
791+
'''
792+
test patient read and search v2
793+
'''
794+
self._call_v3_endpoint_to_assert_403(FHIR_RES_TYPE_PATIENT, settings.DEFAULT_SAMPLE_FHIR_ID_V3, True, '_id=')
795+
796+
@override_switch('v3_endpoints', active=True)
797+
def test_coverage_search_endpoint_v3_403(self):
798+
'''
799+
test patient read and search v2
800+
'''
801+
self._call_v3_endpoint_to_assert_403(FHIR_RES_TYPE_COVERAGE, settings.DEFAULT_SAMPLE_FHIR_ID_V3, True, 'beneficiary=')
802+
803+
@override_switch('v3_endpoints', active=True)
804+
def test_eob_search_endpoint_v3_403(self):
805+
'''
806+
test patient read and search v2
807+
'''
808+
self._call_v3_endpoint_to_assert_403(FHIR_RES_TYPE_EOB, settings.DEFAULT_SAMPLE_FHIR_ID_V3, True, 'patient=')
809+
810+
def _call_v3_endpoint_to_assert_403(self, resource_type: str, resource_value: str, search: bool, search_param: str):
811+
client = APIClient()
812+
813+
# Authenticate
814+
self._setup_apiclient(client)
815+
Flag.objects.create(name='v3_early_adopter', everyone=None)
816+
if search:
817+
endpoint_url = "{}/v3/fhir/{}/?{}{}".format(self.live_server_url, resource_type, search_param, resource_value)
818+
else:
819+
endpoint_url = "{}/v3/fhir/{}/{}".format(self.live_server_url, resource_type, resource_value)
820+
response = client.get(endpoint_url)
821+
self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN)
822+
self.assertEqual(response.json()['detail'], V3_403_DETAIL)

0 commit comments

Comments
 (0)