Skip to content

Commit 4aa9dd7

Browse files
authored
BB2-3889: Consolidate scopes during auth flow, set v2 scopes to default. (#1327)
* Consolidate scopes during auth flow, set v2 scopes to default. * Fix failing test
1 parent 7f24088 commit 4aa9dd7

File tree

7 files changed

+100
-43
lines changed

7 files changed

+100
-43
lines changed

apps/accounts/fixtures/scopes.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
"group": 5,
101101
"description": "Patient FHIR Resource",
102102
"protected_resources": "[\n \n [\n \"GET\",\n \"/v[12]/fhir/Patient[/?].*$\"\n ]\n]",
103-
"default": "False"
103+
"default": "True"
104104
}
105105
},
106106
{
@@ -112,7 +112,7 @@
112112
"group": 5,
113113
"description": "Patient FHIR Resource",
114114
"protected_resources": "[\n [\n \"GET\",\n \"/v[12]/fhir/Patient[/]?$\"\n ]\n]",
115-
"default": "False"
115+
"default": "True"
116116
}
117117
},
118118
{
@@ -124,7 +124,7 @@
124124
"group": 5,
125125
"description": "Patient FHIR Resource",
126126
"protected_resources": "[\n [\n \"GET\",\n \"/v[12]/fhir/Patient[/]?$\"\n ],\n [\n \"GET\",\n \"/v[12]/fhir/Patient[/?].*$\"\n ]\n]",
127-
"default": "False"
127+
"default": "True"
128128
}
129129
},
130130
{
@@ -136,7 +136,7 @@
136136
"group": 5,
137137
"description": "ExplanationOfBenefit FHIR Resource",
138138
"protected_resources": "[\n \n [\n \"GET\",\n \"/v[12]/fhir/ExplanationOfBenefit[/?].*$\"\n ]\n]",
139-
"default": "False"
139+
"default": "True"
140140
}
141141
},
142142
{
@@ -148,7 +148,7 @@
148148
"group": 5,
149149
"description": "ExplanationOfBenefit FHIR Resource",
150150
"protected_resources": "[\n [\n \"GET\",\n \"/v[12]/fhir/ExplanationOfBenefit[/]?$\"\n ]\n]",
151-
"default": "False"
151+
"default": "True"
152152
}
153153
},
154154
{
@@ -160,7 +160,7 @@
160160
"group": 5,
161161
"description": "ExplanationOfBenefit FHIR Resource",
162162
"protected_resources": "[\n [\n \"GET\",\n \"/v[12]/fhir/ExplanationOfBenefit[/]?$\"\n ],\n [\n \"GET\",\n \"/v[12]/fhir/ExplanationOfBenefit[/?].*$\"\n ]\n]",
163-
"default": "False"
163+
"default": "True"
164164
}
165165
},
166166
{
@@ -172,7 +172,7 @@
172172
"group": 5,
173173
"description": "Coverage FHIR Resource",
174174
"protected_resources": "[\n \n [\n \"GET\",\n \"/v[12]/fhir/Coverage[/?].*$\"\n ]\n]",
175-
"default": "False"
175+
"default": "True"
176176
}
177177
},
178178
{
@@ -184,7 +184,7 @@
184184
"group": 5,
185185
"description": "Coverage FHIR Resource",
186186
"protected_resources": "[\n [\n \"GET\",\n \"/v[12]/fhir/Coverage[/]?$\"\n ]\n]",
187-
"default": "False"
187+
"default": "True"
188188
}
189189
},
190190
{
@@ -196,7 +196,7 @@
196196
"group": 5,
197197
"description": "Coverage FHIR Resource",
198198
"protected_resources": "[\n [\n \"GET\",\n \"/v[12]/fhir/Coverage[/]?$\"\n ],\n [\n \"GET\",\n \"/v[12]/fhir/Coverage[/?].*$\"\n ]\n]",
199-
"default": "False"
199+
"default": "True"
200200
}
201201
},
202202
{

apps/capabilities/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55

66
@admin.register(ProtectedCapability)
77
class ProtectedCapabilityAdmin(admin.ModelAdmin):
8-
list_display = ("title", "slug", "group")
8+
list_display = ("title", "slug", "group", "default")
99
search_fields = ("title", "slug", "group")

apps/capabilities/management/commands/create_blue_button_scopes.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ def create_patient_read_capability(group, fhir_prefix, title="Read my general pa
8989
title=title,
9090
description=description,
9191
slug=smart_scope_string,
92-
default=False,
9392
protected_resources=json.dumps(pr, indent=4))
9493
return c
9594

@@ -108,7 +107,6 @@ def create_patient_search_capability(group,
108107
title=title,
109108
description=description,
110109
slug=smart_scope_string,
111-
default=False,
112110
protected_resources=json.dumps(pr, indent=4))
113111
return c
114112

@@ -129,7 +127,6 @@ def create_patient_read_search_capability(group,
129127
title=title,
130128
description=description,
131129
slug=smart_scope_string,
132-
default=False,
133130
protected_resources=json.dumps(pr, indent=4))
134131
return c
135132

@@ -161,7 +158,6 @@ def create_eob_read_capability(group, fhir_prefix, title="Read my Medicare claim
161158
title=title,
162159
description=description,
163160
slug=smart_scope_string,
164-
default=False,
165161
protected_resources=json.dumps(pr, indent=4))
166162
return c
167163

@@ -176,7 +172,6 @@ def create_eob_search_capability(group, fhir_prefix, title="Search my Medicare c
176172
title=title,
177173
description=description,
178174
slug=smart_scope_string,
179-
default=False,
180175
protected_resources=json.dumps(pr, indent=4))
181176
return c
182177

@@ -194,7 +189,6 @@ def create_eob_read_search_capability(group, fhir_prefix, title="Read and search
194189
title=title,
195190
description=description,
196191
slug=smart_scope_string,
197-
default=False,
198192
protected_resources=json.dumps(pr, indent=4))
199193
return c
200194

@@ -228,7 +222,6 @@ def create_coverage_read_capability(group,
228222
title=title,
229223
description=description,
230224
slug=smart_scope_string,
231-
default=False,
232225
protected_resources=json.dumps(pr, indent=4))
233226
return c
234227

@@ -246,7 +239,6 @@ def create_coverage_search_capability(group,
246239
title=title,
247240
description=description,
248241
slug=smart_scope_string,
249-
default=False,
250242
protected_resources=json.dumps(pr, indent=4))
251243
return c
252244

@@ -265,7 +257,6 @@ def create_coverage_read_search_capability(group,
265257
title=title,
266258
description=description,
267259
slug=smart_scope_string,
268-
default=False,
269260
protected_resources=json.dumps(pr, indent=4))
270261
return c
271262

apps/dot_ext/forms.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from oauth2_provider.models import get_application_model
1010
from apps.accounts.models import UserProfile
1111
from apps.capabilities.models import ProtectedCapability
12+
from apps.dot_ext.scopes import CapabilitiesScopes
1213
from apps.dot_ext.models import Application, InternalApplicationLabels
1314
from apps.dot_ext.validators import validate_logo_image, validate_notags
1415
from django.contrib.auth.models import Group, User
@@ -339,15 +340,11 @@ def clean(self):
339340
if scope is None:
340341
cleaned_data["scope"] = ""
341342
scope = ""
342-
343-
# Remove demographic information scopes, if beneficiary is not sharing
344-
if cleaned_data.get("share_demographic_scopes") != "True":
345-
cleaned_data["scope"] = " ".join(
346-
[
347-
s
348-
for s in scope.split(" ")
349-
if s not in settings.BENE_PERSONAL_INFO_SCOPES
350-
]
351-
)
343+
else:
344+
cleaned_scope_list = CapabilitiesScopes().condense_scopes(scope.split(" "))
345+
# Remove demographic information scopes, if beneficiary is not sharing
346+
if cleaned_data.get("share_demographic_scopes") != "True":
347+
cleaned_scope_list = CapabilitiesScopes().remove_demographic_scopes(cleaned_scope_list)
348+
cleaned_data["scope"] = " ".join(cleaned_scope_list)
352349

353350
return cleaned_data

apps/dot_ext/scopes.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,7 @@ def get_available_scopes(self, application=None, request=None, *args, **kwargs):
3434
# Return all scopes
3535
return app_scopes_avail
3636
else:
37-
# Remove personal information scopes
38-
app_scopes = []
39-
for s in app_scopes_avail:
40-
if s not in settings.BENE_PERSONAL_INFO_SCOPES:
41-
app_scopes.append(s)
42-
return app_scopes
37+
return self.remove_demographic_scopes(app_scopes_avail)
4338

4439
def get_default_scopes(self, application=None, request=None, *args, **kwargs):
4540
"""
@@ -59,9 +54,38 @@ def get_default_scopes(self, application=None, request=None, *args, **kwargs):
5954
# Return all scopes
6055
return app_scopes_default
6156
else:
62-
# Remove personal information scopes
63-
app_scopes = []
64-
for s in app_scopes_default:
65-
if s not in settings.BENE_PERSONAL_INFO_SCOPES:
66-
app_scopes.append(s)
67-
return app_scopes
57+
return self.remove_demographic_scopes(app_scopes_default)
58+
59+
def condense_scopes(self, scopes):
60+
"""
61+
Returns a list based on the provided list of scopes with redundant scopes consolidated
62+
"""
63+
out_scopes = set(scopes)
64+
65+
# Consolidate v2 resource scopes
66+
if "patient/Patient.rs" in scopes or ("patient/Patient.r" in scopes and "patient/Patient.s" in scopes):
67+
out_scopes.add("patient/Patient.rs")
68+
out_scopes.discard("patient/Patient.r")
69+
out_scopes.discard("patient/Patient.s")
70+
if "patient/Coverage.rs" in scopes or ("patient/Coverage.r" in scopes and "patient/Coverage.s" in scopes):
71+
out_scopes.add("patient/Coverage.rs")
72+
out_scopes.discard("patient/Coverage.r")
73+
out_scopes.discard("patient/Coverage.s")
74+
if "patient/ExplanationOfBenefit.rs" in scopes or \
75+
("patient/ExplanationOfBenefit.r" in scopes and "patient/ExplanationOfBenefit.s" in scopes):
76+
out_scopes.add("patient/ExplanationOfBenefit.rs")
77+
out_scopes.discard("patient/ExplanationOfBenefit.r")
78+
out_scopes.discard("patient/ExplanationOfBenefit.s")
79+
80+
return list(out_scopes)
81+
82+
def remove_demographic_scopes(self, scopes):
83+
"""
84+
Returns a list of all of the provided list except with personal info scopes removed
85+
"""
86+
# Remove personal information scopes
87+
out_scopes = []
88+
for s in scopes:
89+
if s not in settings.BENE_PERSONAL_INFO_SCOPES:
90+
out_scopes.append(s)
91+
return out_scopes

apps/dot_ext/tests/demographic_scopes_test_cases.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@
3232
'patient/launch',
3333
'openid'
3434
]
35+
V2_SCOPES_ALL_CONDENSED = [
36+
'patient/Patient.rs',
37+
'patient/ExplanationOfBenefit.rs',
38+
'patient/Coverage.rs',
39+
'patient/launch',
40+
'openid'
41+
]
3542
V2_SCOPES_NON_DEMOGRAPHIC = [
3643
'patient/ExplanationOfBenefit.r',
3744
'patient/ExplanationOfBenefit.s',
@@ -42,6 +49,12 @@
4249
'patient/launch',
4350
'openid'
4451
]
52+
V2_SCOPES_NON_DEMOGRAPHIC_CONDENSED = [
53+
'patient/ExplanationOfBenefit.rs',
54+
'patient/Coverage.rs',
55+
'patient/launch',
56+
'openid'
57+
]
4558

4659
# Scope to base URL PATH mapping.
4760
SCOPES_TO_URL_BASE_PATH = {
@@ -171,15 +184,15 @@
171184
"request_scopes": V2_SCOPES_ALL,
172185
# Result:
173186
"result_form_is_valid": True,
174-
"result_token_scopes_granted": V2_SCOPES_ALL,
187+
"result_token_scopes_granted": V2_SCOPES_ALL_CONDENSED,
175188
},
176189
"test 11: share = False v2 Scopes": {
177190
# Request:
178191
"request_bene_share_demographic_scopes": False,
179192
"request_scopes": V2_SCOPES_ALL,
180193
# Result:
181194
"result_form_is_valid": True,
182-
"result_token_scopes_granted": V2_SCOPES_NON_DEMOGRAPHIC,
195+
"result_token_scopes_granted": V2_SCOPES_NON_DEMOGRAPHIC_CONDENSED,
183196
},
184197
}
185198

apps/dot_ext/tests/test_scopes.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,35 @@ def test_backend_works_with_no_capabilities(self):
7878
# retrieve the list of the scopes available for the application
7979
default_scopes = CapabilitiesScopes().get_default_scopes(application=application)
8080
assert default_scopes == []
81+
82+
def test_condense_scopes(self):
83+
"""
84+
Test that v2 scopes get properly condensed
85+
"""
86+
scopes = [
87+
'patient/Patient.r',
88+
'patient/Patient.s',
89+
'patient/Patient.rs',
90+
'patient/Coverage.s',
91+
'patient/Coverage.rs',
92+
'patient/ExplanationOfBenefit.r',
93+
'patient/ExplanationOfBenefit.s'
94+
]
95+
condensed_scopes = CapabilitiesScopes().condense_scopes(scopes)
96+
assert len(condensed_scopes) == 3
97+
assert 'patient/Patient.rs' in condensed_scopes
98+
assert 'patient/Coverage.rs' in condensed_scopes
99+
assert 'patient/ExplanationOfBenefit.rs' in condensed_scopes
100+
scopes = [
101+
'patient/Patient.r',
102+
'patient/Coverage.s',
103+
'patient/Coverage.rs',
104+
'patient/ExplanationOfBenefit.s',
105+
'profile'
106+
]
107+
condensed_scopes = CapabilitiesScopes().condense_scopes(scopes)
108+
assert len(condensed_scopes) == 4
109+
assert 'patient/Patient.r' in condensed_scopes
110+
assert 'patient/Coverage.rs' in condensed_scopes
111+
assert 'patient/ExplanationOfBenefit.s' in condensed_scopes
112+
assert 'profile' in condensed_scopes

0 commit comments

Comments
 (0)