Skip to content

Commit 499d00a

Browse files
Ensure 403s are thrown if an application is not in the v3_early_adopter waffle_flag. Working for read/search v3 calls, v3 auth/token flows (still need to add to some other auth views)
1 parent eb64ae9 commit 499d00a

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

apps/dot_ext/views/authorization.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from functools import wraps
55
from time import strftime
66

7+
from django.conf import settings
78
from django.contrib.auth import get_user_model
89
from django.contrib.auth.views import redirect_to_login
910
from django.http import JsonResponse
@@ -15,7 +16,8 @@
1516
from django.views.decorators.debug import sensitive_post_parameters
1617
from apps.dot_ext.constants import TOKEN_ENDPOINT_V3_KEY
1718
from oauth2_provider.exceptions import OAuthToolkitError
18-
from oauth2_provider.views.base import app_authorized, get_access_token_model
19+
from oauth2_provider.views.base import app_authorized
20+
from oauth2_provider.models import get_refresh_token_model, get_access_token_model
1921
from oauth2_provider.views.base import AuthorizationView as DotAuthorizationView
2022
from oauth2_provider.views.base import TokenView as DotTokenView
2123
from oauth2_provider.views.base import RevokeTokenView as DotRevokeTokenView
@@ -30,6 +32,7 @@
3032
import html
3133
from apps.dot_ext.scopes import CapabilitiesScopes
3234
import apps.logging.request_logger as bb2logging
35+
from apps.versions import Versions
3336

3437
from ..signals import beneficiary_authorized_application
3538
from ..forms import SimpleAllowForm
@@ -43,6 +46,7 @@
4346
)
4447
from ..models import Approval
4548
from ..utils import (
49+
get_api_version_number_from_url,
4650
remove_application_user_pair_tokens_data_access,
4751
validate_app_is_active,
4852
json_response_from_oauth2_error,
@@ -75,8 +79,50 @@ def _wrapped(request, *args, **kwargs):
7579
return _wrapped
7680

7781

82+
def check_v3_endpoint_access(view_func):
83+
@wraps(view_func)
84+
def _wrapped(request, *args, **kwargs):
85+
# 4250-TODO how do we not call this so many times?
86+
path_info = request.__dict__.get('path_info')
87+
version = get_api_version_number_from_url(path_info)
88+
if version != Versions.V3:
89+
return view_func(request, *args, **kwargs)
90+
91+
flag = get_waffle_flag_model().get('v3_early_adopter')
92+
req_meta = request.META
93+
url_query = parse_qs(req_meta.get('QUERY_STRING'))
94+
client_id = url_query.get('client_id', [None])
95+
try:
96+
if client_id[0]:
97+
application = get_application_model().objects.get(client_id=client_id[0])
98+
else:
99+
url_query = parse_qs(request._body.decode('utf-8'))
100+
refresh_token_from_request = url_query.get('refresh_token', [None])
101+
refresh_token = get_refresh_token_model().objects.get(token=refresh_token_from_request[0])
102+
application = get_application_model().objects.get(id=refresh_token.application_id)
103+
104+
application_user = get_user_model().objects.get(id=application.user_id)
105+
106+
if flag.id is not None and flag.is_active_for_user(application_user):
107+
return view_func(request, *args, **kwargs)
108+
else:
109+
return JsonResponse(
110+
{'status_code': 403, 'message': settings.APPLICATION_DOES_NOT_HAVE_V3_ENABLED_YET.format(application.name)},
111+
status=403,
112+
)
113+
except ObjectDoesNotExist:
114+
# 4250-TODO Do we need this?
115+
return JsonResponse(
116+
{'status_code': 500, 'message': 'Error retrieving data'},
117+
status=500,
118+
)
119+
120+
return _wrapped
121+
122+
78123
@method_decorator(csrf_exempt, name="dispatch")
79124
@method_decorator(require_post_state_decorator, name="dispatch")
125+
@method_decorator(check_v3_endpoint_access, name="dispatch")
80126
class AuthorizationView(DotAuthorizationView):
81127
"""
82128
Override the base authorization view from dot to
@@ -407,6 +453,7 @@ def dispatch(self, request, uuid, *args, **kwargs):
407453

408454

409455
@method_decorator(csrf_exempt, name="dispatch")
456+
@method_decorator(check_v3_endpoint_access, name="dispatch")
410457
class TokenView(DotTokenView):
411458

412459
def validate_token_endpoint_request_body(self, request):

apps/fhir/bluebutton/permissions.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
from django.conf import settings
44
from django.contrib.auth import get_user_model
5+
from oauth2_provider.views.base import get_access_token_model
6+
from oauth2_provider.models import get_application_model
57
from rest_framework import permissions, exceptions
6-
from rest_framework.exceptions import AuthenticationFailed
8+
from rest_framework.exceptions import AuthenticationFailed, PermissionDenied
9+
from waffle import get_waffle_flag_model
710
from .constants import ALLOWED_RESOURCE_TYPES
811
from apps.versions import Versions, VersionNotMatched
912

@@ -106,3 +109,25 @@ def has_permission(self, request, view):
106109
)
107110

108111
return True
112+
113+
114+
class V3EarlyAdopterPermission(permissions.BasePermission):
115+
def has_permission(self, request, view):
116+
print("IN HAS_PERMISSION OF V3EarlyAdopterPermission")
117+
print("V3EarlyAdopterPermission request: ", request.__dict__)
118+
print("V3EarlyAdopterPermission view: ", view.__dict__)
119+
# if it is not version 3, we do not need to check the waffle switch or flag
120+
if view.version < Versions.V3:
121+
return True
122+
123+
token = get_access_token_model().objects.get(token=request._auth)
124+
application = get_application_model().objects.get(id=token.application_id)
125+
application_user = get_user_model().objects.get(id=application.user_id)
126+
flag = get_waffle_flag_model().get('v3_early_adopter')
127+
128+
if flag.id is not None and flag.is_active_for_user(application_user):
129+
return True
130+
else:
131+
raise PermissionDenied(
132+
settings.APPLICATION_DOES_NOT_HAVE_V3_ENABLED_YET.format(application.name)
133+
)

apps/fhir/bluebutton/views/read.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
from apps.authorization.permissions import DataAccessGrantPermission
44
from apps.capabilities.permissions import TokenHasProtectedCapability
5-
from ..permissions import (ReadCrosswalkPermission, ResourcePermission, ApplicationActivePermission)
5+
from ..permissions import (
6+
ReadCrosswalkPermission,
7+
ResourcePermission,
8+
ApplicationActivePermission,
9+
V3EarlyAdopterPermission
10+
)
611
from apps.fhir.bluebutton.views.generic import FhirDataView
712

813

@@ -21,6 +26,7 @@ class ReadView(FhirDataView):
2126
ReadCrosswalkPermission,
2227
DataAccessGrantPermission,
2328
TokenHasProtectedCapability,
29+
V3EarlyAdopterPermission,
2430
]
2531

2632
def __init__(self, version=1):

apps/fhir/bluebutton/views/search.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
from apps.fhir.bluebutton.views.generic import FhirDataView
1717
from apps.authorization.permissions import DataAccessGrantPermission
1818
from apps.capabilities.permissions import TokenHasProtectedCapability
19-
from ..permissions import (SearchCrosswalkPermission, ResourcePermission, ApplicationActivePermission)
19+
from ..permissions import (
20+
SearchCrosswalkPermission,
21+
ResourcePermission,
22+
ApplicationActivePermission,
23+
V3EarlyAdopterPermission
24+
)
2025

2126

2227
class HasSearchScope(permissions.BasePermission):
@@ -42,7 +47,8 @@ class SearchView(FhirDataView):
4247
SearchCrosswalkPermission,
4348
DataAccessGrantPermission,
4449
TokenHasProtectedCapability,
45-
HasSearchScope
50+
HasSearchScope,
51+
V3EarlyAdopterPermission,
4652
]
4753

4854
# Regex to match a valid _lastUpdated value that can begin with lt, le, gt and ge operators

hhs_oauth_server/settings/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,13 @@
610610
'and consent to share their data.'
611611
)
612612

613+
APPLICATION_DOES_NOT_HAVE_V3_ENABLED_YET = (
614+
'This application, {}, does not yet have access to v3 endpoints.'
615+
' If you are the app maintainer, please contact the Blue Button API team.'
616+
' If you are a Medicare Beneficiary and need assistance, please contact'
617+
' the support team for the application you are trying to access.'
618+
)
619+
613620
FHIR_CLIENT_CERTSTORE = env(
614621
"DJANGO_FHIR_CERTSTORE",
615622
os.path.join(BASE_DIR, os.environ.get("DJANGO_FHIR_CERTSTORE_REL", "../certstore")),

0 commit comments

Comments
 (0)